3 #include "common/llist.h"
6 // deletes the specified object, or well, marks it to be deleted.
7 // it's not actually freed till the end of the tick.
10 Object
* const &o
= this;
15 // make sure no pointers are pointing at us
16 DisconnectGamePointers();
18 // show any damage waiting to be added NOW instead of later
19 if (o
->DamageWaiting
> 0)
21 DamageText
->AddQty(o
->DamageWaiting
);
25 // set it's id1 flag, required for some scripts
26 game
.flags
[o
->id1
] = true;
28 // mark it for deletion at end of loop
29 // (can't delete now as it may invalidate pointers--we don't know where we were called from)
33 void Object::Destroy()
35 Object
* const &o
= this;
37 // make sure no pointers are pointing at us
38 DisconnectGamePointers();
39 // delete associated floaty text as soon as it's animation is done
40 DamageText
->ObjectDestroyed
= true;
42 // if any objects are linked to this obj then unlink them
44 for(link
= firstobject
; link
; link
= link
->next
)
46 if (link
->linkedobject
== o
)
47 link
->linkedobject
= NULL
;
50 // remove from list and free
51 LL_REMOVE(o
, prev
, next
, firstobject
, lastobject
);
52 LL_REMOVE(o
, lower
, higher
, lowestobject
, highestobject
);
53 if (o
== player
) player
= NULL
;
58 // checks all the games pointers that point to an object
59 // record and disconnects them if they are pointing at object o.
60 // used in preparation to delete the object.
61 // protects against dangling pointers.
62 void Object::DisconnectGamePointers()
64 Object
* const &o
= this;
66 if (o
== player
->riding
) player
->riding
= NULL
;
67 if (o
== player
->lastriding
) player
->lastriding
= NULL
;
68 if (o
== player
->cannotride
) player
->cannotride
= NULL
;
69 if (o
== game
.bossbar
.object
) game
.bossbar
.object
= NULL
; // any enemy with a boss bar
70 if (o
== game
.stageboss
.object
) game
.stageboss
.object
= NULL
; // the stage boss
71 if (o
== map
.focus
.target
) map
.focus
.target
= NULL
;
72 if (o
== ID2Lookup
[this->id2
]) ID2Lookup
[this->id2
] = NULL
;
73 if (o
== map
.waterlevelobject
) map
.waterlevelobject
= NULL
;
77 void c------------------------------() {}
80 void Object::SetType(int type
)
82 Object
* const &o
= this;
85 o
->sprite
= objprop
[type
].sprite
;
86 o
->hp
= objprop
[type
].initial_hp
;
87 o
->damage
= objprop
[o
->type
].damage
;
90 // apply nxflags to new object type!
91 // (did this so toroko would handle slopes properly in Gard cutscene)
92 o
->nxflags
= objprop
[type
].defaultnxflags
;
94 // apply defaultflags to new object type, but NOT ALL defaultflags.
95 // otherwise <CNP's _WILL_ get messed up.
96 const static int flags_to_keep
= \
97 (FLAG_SCRIPTONTOUCH
| FLAG_SCRIPTONDEATH
| FLAG_SCRIPTONACTIVATE
| \
98 FLAG_APPEAR_ON_FLAGID
| FLAG_DISAPPEAR_ON_FLAGID
| \
101 uint32_t keep
= (o
->flags
& flags_to_keep
);
102 o
->flags
= (objprop
[type
].defaultflags
& ~flags_to_keep
) | keep
;
104 //stat("new flags: %04x", o->flags);
106 // setup default clipping extents, in case object turns on clip_enable
111 void Object::ChangeType(int type
)
113 Object
* const &o
= this;
115 int oldsprite
= o
->sprite
;
126 // adjust position so spawn points of old object and new object line up
127 o
->x
>>= CSF
; o
->x
<<= CSF
;
128 o
->y
>>= CSF
; o
->y
<<= CSF
;
129 o
->x
+= (sprites
[oldsprite
].spawn_point
.x
<< CSF
);
130 o
->y
+= (sprites
[oldsprite
].spawn_point
.y
<< CSF
);
131 o
->x
-= (sprites
[this->sprite
].spawn_point
.x
<< CSF
);
132 o
->y
-= (sprites
[this->sprite
].spawn_point
.y
<< CSF
);
134 // added this for when you pick up the puppy in the Deserted House in SZ--
135 // makes objects <CNPed during a <PRI initialize immediately instead of waiting
136 // for <PRI to be released.
143 // Sprites appearing out of an OBJ_NULL should generally go to the top of the z-order.
144 // this was originally added so that the Doctor would appear in front of the core
145 // when he teleports in at end of Almond battle (it's since been used in a lot of
146 // other places though).
147 if (oldsprite
== SPR_NULL
)
155 // moves an object to the top of the Z-order,
156 // so that it is drawn in front of all other objects.
157 void Object::BringToFront()
159 LL_REMOVE(this, lower
, higher
, lowestobject
, highestobject
);
160 LL_ADD_END(this, lower
, higher
, lowestobject
, highestobject
);
163 // move an object in the z-order to just below object "behind".
164 void Object::PushBehind(Object
*behind
)
169 LL_REMOVE(this, lower
, higher
, lowestobject
, highestobject
);
170 LL_INSERT_BEFORE(this, behind
, lower
, higher
, lowestobject
, highestobject
);
173 void Object::PushBehind(int objtype
)
175 Object
*target
= Objects::FindByType(objtype
);
179 staterr("PushBehind: could not find any objects of type %s", DescribeObjectType(objtype
));
183 void c------------------------------() {}
186 // for each point in pointlist, treats the point as a CSF'ed offset
187 // within the object's sprite. Then checks the attributes of the tile
188 // under each point. Returns an attribute mask containing the cumulative
189 // attributes of all the tiles under each point in the list.
191 // if tile is non-null, it is set to the tile type of the last tile checked.
192 uint32_t Object::GetAttributes(const Point
*pointlist
, int npoints
, int *tile
)
197 int xoff
= (this->x
>> CSF
);
198 int yoff
= (this->y
>> CSF
);
200 for(int i
=0;i
<npoints
;i
++)
202 int x
= (xoff
+ pointlist
[i
].x
) / TILE_W
;
203 int y
= (yoff
+ pointlist
[i
].y
) / TILE_H
;
205 if (x
>= 0 && y
>= 0 && x
< map
.xsize
&& y
< map
.ysize
)
207 tileno
= map
.tiles
[x
][y
];
208 attr
|= tileattr
[tileno
];
212 // also go underwater if we go under the variable waterlevel in Almond
213 if (map
.waterlevelobject
&& (this->y
+ (2<<CSF
)) > map
.waterlevelobject
->y
)
218 if (tile
) *tile
= tileno
;
222 // for each point in pointlist, treats the point as a CSF'ed offset
223 // within the object's sprite. The tile under each position is checked
224 // to see if it's attributes contain one or more of the attributes
225 // specified in attrmask.
227 // If any of the points match, returns 1, and optionally returns
228 // the map coordinates of the first matched tile in tile_x/y.
229 bool Object::CheckAttribute(const Point
*pointlist
, int npoints
, uint32_t attrmask
,
230 int *tile_x
, int *tile_y
)
232 int x
, y
, xoff
, yoff
;
234 xoff
= (this->x
>> CSF
);
235 yoff
= (this->y
>> CSF
);
237 for(int i
=0;i
<npoints
;i
++)
239 x
= (xoff
+ pointlist
[i
].x
) / TILE_W
;
240 y
= (yoff
+ pointlist
[i
].y
) / TILE_H
;
242 if (x
>= 0 && y
>= 0 && x
< map
.xsize
&& y
< map
.ysize
)
244 if ((tileattr
[map
.tiles
[x
][y
]] & attrmask
) != 0)
246 if (tile_x
) *tile_x
= x
;
247 if (tile_y
) *tile_y
= y
;
256 // treats each point in pointlist as an offset within the object, and returns
257 // true if any of the points intersect with object o2's solidbox.
258 bool Object::CheckSolidIntersect(Object
*other
, const Point
*pointlist
, int npoints
)
261 int ox
, oy
, o2x
, o2y
;
262 SIFSprite
*s2
= other
->Sprite();
264 ox
= (this->x
>> CSF
);
265 oy
= (this->y
>> CSF
);
266 o2x
= (other
->x
>> CSF
);
267 o2y
= (other
->y
>> CSF
);
269 for(int i
=0;i
<npoints
;i
++)
271 x
= ox
+ pointlist
[i
].x
;
272 y
= oy
+ pointlist
[i
].y
;
274 if (x
>= (o2x
+ s2
->solidbox
.x1
) && x
<= (o2x
+ s2
->solidbox
.x2
))
276 if (y
>= (o2y
+ s2
->solidbox
.y1
) && y
<= (o2y
+ s2
->solidbox
.y2
))
287 // update the blocked states of object o.
288 // updatemask specifies which states are in need of updating.
289 void Object::UpdateBlockStates(uint8_t updatemask
)
291 Object
* const &o
= this;
292 SIFSprite
*sprite
= Sprite();
293 int mask
= GetBlockingType();
295 if (updatemask
& LEFTMASK
)
297 o
->blockl
= CheckAttribute(&sprite
->block_l
, mask
);
299 // for objects which don't follow slope, have them see the slope as a wall so they
300 // won't just go right through it (looks really weird)
301 if (!(o
->nxflags
& NXFLAG_FOLLOW_SLOPE
))
304 o
->blockl
= IsSlopeAtPointList(o
, &sprite
->block_l
);
308 if (updatemask
& RIGHTMASK
)
310 o
->blockr
= CheckAttribute(&sprite
->block_r
, mask
);
312 // for objects which don't follow slope, have them see the slope as a wall so they
313 // won't just go right through it (looks really weird).
314 if (!(o
->nxflags
& NXFLAG_FOLLOW_SLOPE
))
317 o
->blockr
= IsSlopeAtPointList(o
, &sprite
->block_r
);
321 if (updatemask
& UPMASK
)
323 o
->blocku
= CheckAttribute(&sprite
->block_u
, mask
);
324 if (!o
->blocku
) o
->blocku
= CheckBoppedHeadOnSlope(o
) ? 1 : 0;
327 if (updatemask
& DOWNMASK
)
329 o
->blockd
= CheckAttribute(&sprite
->block_d
, mask
);
330 if (!o
->blockd
) o
->blockd
= CheckStandOnSlope(o
) ? 1 : 0;
333 // have player be blocked by objects with FLAG_SOLID_BRICK set
335 o
->SetBlockForSolidBrick(updatemask
);
338 // called from UpdateBlockedStates used w/ player.
339 // sets the object's block/l/r/u/d flags if it is in contact with a SOLID_BRICK object.
340 void Object::SetBlockForSolidBrick(uint8_t updatemask
)
342 SIFSprite
*thissprite
= this->Sprite();
345 // no need to check blockpoints that are already set
346 if (this->blockl
) updatemask
&= ~LEFTMASK
;
347 if (this->blockr
) updatemask
&= ~RIGHTMASK
;
348 if (this->blocku
) updatemask
&= ~UPMASK
;
349 if (this->blockd
) updatemask
&= ~DOWNMASK
;
353 if (!(o
->flags
& FLAG_SOLID_BRICK
)) continue;
355 if (updatemask
& LEFTMASK
)
357 if (this->CheckSolidIntersect(o
, &thissprite
->block_l
))
359 this->blockl
= BLOCKED_OBJECT
; // value of 2 instead of 1
360 updatemask
&= ~LEFTMASK
; // no need to keep checking
364 if (updatemask
& RIGHTMASK
)
366 if (this->CheckSolidIntersect(o
, &thissprite
->block_r
))
368 this->blockr
= BLOCKED_OBJECT
;
369 updatemask
&= ~RIGHTMASK
;
373 if (updatemask
& UPMASK
)
375 if (this->CheckSolidIntersect(o
, &thissprite
->block_u
))
377 this->blocku
= BLOCKED_OBJECT
;
378 updatemask
&= ~UPMASK
;
381 player
->bopped_object
= o
;
385 if (updatemask
& DOWNMASK
)
387 if (this->CheckSolidIntersect(o
, &thissprite
->block_d
))
389 this->blockd
= BLOCKED_OBJECT
;
390 updatemask
&= ~DOWNMASK
;
400 void c------------------------------() {}
403 // given an object, returns which tile attribute affects it's blocked state.
404 int Object::GetBlockingType()
406 Object
* const &o
= this;
409 return TA_SOLID_PLAYER
;
411 if (o
->type
>= OBJ_SHOTS_START
&& \
412 o
->type
<= OBJ_SHOTS_END
)
414 // Bubbler L1 can't pass tile 44.
415 if (o
->type
== OBJ_BUBBLER12_SHOT
&& o
->shot
.level
== 0)
416 return (TA_SOLID_SHOT
| TA_SOLID_NPC
);
418 return TA_SOLID_SHOT
;
421 if (o
->flags
& FLAG_IGNORETILE44
)
422 return TA_SOLID_PLAYER
;
428 void c------------------------------() {}
431 // tries to move the object in the X direction by the given amount.
432 // returns nonzero if the object was blocked.
433 bool Object::apply_xinertia(int inertia
)
435 Object
* const &o
= this;
440 if (o
->flags
& FLAG_IGNORE_SOLID
)
446 // only apply inertia one pixel at a time so we have
447 // proper hit detection--prevents objects traveling at
448 // high speed from becoming embedded in walls
451 while(inertia
> (1<<CSF
))
453 if (movehandleslope(o
, (1<<CSF
))) return 1;
456 o
->UpdateBlockStates(RIGHTMASK
);
459 else if (inertia
< 0)
461 while(inertia
< -(1<<CSF
))
463 if (movehandleslope(o
, -(1<<CSF
))) return 1;
466 o
->UpdateBlockStates(LEFTMASK
);
470 // apply any remaining inertia
472 movehandleslope(o
, inertia
);
477 // tries to move the object in the Y direction by the given amount.
478 // returns nonzero if the object was blocked.
479 bool Object::apply_yinertia(int inertia
)
481 Object
* const &o
= this;
486 if (o
->flags
& FLAG_IGNORE_SOLID
)
492 // only apply inertia one pixel at a time so we have
493 // proper hit detection--prevents objects traveling at
494 // high speed from becoming embedded in walls
497 if (o
->blockd
) return 1;
499 while(inertia
> (1<<CSF
))
504 o
->UpdateBlockStates(DOWNMASK
);
505 if (o
->blockd
) return 1;
508 else if (inertia
< 0)
510 if (o
->blocku
) return 1;
512 while(inertia
< -(1<<CSF
))
517 o
->UpdateBlockStates(UPMASK
);
518 if (o
->blocku
) return 1;
522 // apply any remaining inertia
530 // handles a moving object with "FLAG_SOLID_BRICK" set
531 // pushing the player as it moves.
532 void Object::PushPlayerOutOfWay(int xinertia
, int yinertia
)
534 Object
* const &o
= this;
538 // give a bit of a gap where they must be--i.e. don't push them if they're right
539 // at the top or the bottom of the brick: needed when he rides it and falls off, then it
540 // turns around and touches him again. in that case what we actually want to do is push him
541 // to the top, not push him side-to-side.
542 if ((player
->SolidBottom() - (2<<CSF
)) > o
->SolidTop() &&\
543 (player
->SolidTop() + (2<<CSF
)) < o
->SolidBottom())
545 if (xinertia
> 0 && player
->SolidRight() > o
->SolidRight() && solidhitdetect(o
, player
))
546 { // pushing player right
549 hurtplayer(o
->smushdamage
);
553 // align player's blockl grid with our right side
554 player
->x
= o
->SolidRight() - (sprites
[player
->sprite
].block_l
[0].x
<< CSF
);
556 // get player a xinertia equal to our own. You can see this
557 // with the moving blocks in Labyrinth H.
558 player
->xinertia
= xinertia
;
559 player
->x
+= -player
->xinertia
;
562 else if (xinertia
< 0 && player
->SolidLeft() < o
->SolidLeft() && solidhitdetect(o
, player
))
563 { // pushing player left
566 hurtplayer(o
->smushdamage
);
570 // align player's blockr grid with our left side
571 player
->x
= o
->SolidLeft() - (sprites
[player
->sprite
].block_r
[0].x
<< CSF
);
573 // get player a xinertia equal to our own. You can see this
574 // with the moving blocks in Labyrinth H.
575 player
->xinertia
= xinertia
;
576 player
->x
+= -player
->xinertia
;
584 if (player
->blocku
&& player
->riding
== o
) // smushed into ceiling!
585 hurtplayer(o
->smushdamage
);
587 else if (yinertia
> 0) // object heading downwards?
589 // player riding object down
590 if (player
->riding
== o
)
592 if (player
->yinertia
>= 0) // don't do this if he's trying to jump away
594 // align player's blockd grid with our top side so player
595 // doesn't perpetually fall.
596 player
->y
= o
->SolidTop() - (sprites
[player
->sprite
].block_d
[0].y
<< CSF
);
599 else if (player
->Top() >= o
->CenterY() && solidhitdetect(o
, player
)) // underneath object
601 // push him down if he's underneath us and we're going faster than he is.
602 if (yinertia
>= player
->yinertia
)
604 if (player
->blockd
) // squished into floor!
605 hurtplayer(o
->smushdamage
);
607 // align his blocku grid with our bottom side
608 player
->y
= o
->SolidBottom() - (sprites
[player
->sprite
].block_u
[0].y
<< CSF
);
614 // snap the object down to the nearest solid tile.
615 // the object must have at least one blockd point for this to work.
616 void Object::SnapToGround()
618 Object
* const &o
= this;
620 uint32_t flags
= o
->flags
;
621 o
->flags
&= ~FLAG_IGNORE_SOLID
;
623 UpdateBlockStates(DOWNMASK
);
624 apply_yinertia(SCREEN_HEIGHT
<< CSF
);
631 void c------------------------------() {}
634 // deals the specified amount of damage to the object,
635 // and kills it if it's hitpoints reach 0.
637 // It is valid to deal 0 damage. The trails of the Spur do this
638 // to keep the enemy shaking and making noise for as long as
641 // shot is an optional parameter specifying a pointer to
642 // the shot that hit the object, and is used to spawn
643 // blood spatter at the correct location.
644 void Object::DealDamage(int dmg
, Object
*shot
)
646 Object
* const &o
= this;
648 if (o
->flags
& FLAG_INVULNERABLE
)
653 if (o
->flags
& FLAG_SHOW_FLOATTEXT
)
654 o
->DamageWaiting
+= dmg
;
658 if (o
->shaketime
< objprop
[o
->type
].shaketime
- 2)
660 o
->shaketime
= objprop
[o
->type
].shaketime
;
662 if (objprop
[o
->type
].hurt_sound
)
663 sound(objprop
[o
->type
].hurt_sound
);
666 effect(shot
->CenterX(), shot
->CenterY(), EFFECT_BLOODSPLATTER
);
675 // kills the specified object, performing whatever action is
676 // applicable to that, such as spawning powerups or running scripts.
679 Object
* const &o
= this;
682 o
->flags
&= ~FLAG_SHOOTABLE
;
684 // auto disappear the bossbar if we have just killed a boss
685 if (o
== game
.bossbar
.object
)
686 game
.bossbar
.defeated
= true;
688 // if a script is set to run on death, run it instead of the usual explosion
689 if (o
->flags
& FLAG_SCRIPTONDEATH
)
696 // should spawn the smokeclouds first, for z-order reasons
697 SmokeClouds(o
, objprop
[o
->type
].death_smoke_amt
, 8, 8);
698 effect(o
->CenterX(), o
->CenterY(), EFFECT_BOOMFLASH
);
700 if (objprop
[o
->type
].death_sound
)
701 sound(objprop
[o
->type
].death_sound
);
703 if (objprop
[o
->type
].ai_routines
.ondeath
)
716 // spawn the powerups you get when you kill an enemy
717 void Object::SpawnPowerups()
719 Object
* const &o
= this;
720 int objectType
, bonusType
;
722 if (!objprop
[o
->type
].xponkill
)
725 bonusType
= random(1, 5);
728 SpawnXP(objprop
[o
->type
].xponkill
);
732 if (bonusType
== 2 && \
733 (player
->weapons
[WPN_MISSILE
].hasWeapon
|| \
734 player
->weapons
[WPN_SUPER_MISSILE
].hasWeapon
))
736 objectType
= OBJ_MISSILE
;
740 objectType
= OBJ_HEART
;
743 // upgrade to big 3-cluster versions of powerups
745 if (objprop
[o
->type
].xponkill
> 6)
747 if (objectType
== OBJ_HEART
)
749 objectType
= OBJ_HEART3
;
753 objectType
= OBJ_MISSILE3
;
757 // create the powerup
758 Object
*powerup
= CreateObject(o
->CenterX(), o
->CenterY(), objectType
);
759 powerup
->x
-= (powerup
->Width() / 2);
760 powerup
->y
-= (powerup
->Height() / 2);
762 powerup
->state
= 1; // make it animate
766 // spawn the given quantity of XP at the center of the object.
767 // amt indicates the total number of XP points to spawn.
768 // these will be collated into the appropriate sizes of XP triangles.
769 void Object::SpawnXP(int amt
)
771 Object
* const &o
= this;
773 int x
= o
->CenterX();
774 int y
= o
->CenterY();
778 Object
*xp
= CreateObject(x
, y
, OBJ_XP
);
779 xp
->xinertia
= random(-0x200, 0x200);
781 if (amt
>= XP_LARGE_AMT
)
783 xp
->sprite
= SPR_XP_LARGE
;
786 else if (amt
>= XP_MED_AMT
)
788 xp
->sprite
= SPR_XP_MED
;
793 xp
->sprite
= SPR_XP_SMALL
;
797 // center the sprite at the center of the object
798 xp
->x
-= (xp
->Width() / 2);
799 xp
->y
-= (xp
->Height() / 2);
801 xp
->UpdateBlockStates(ALLDIRMASK
);
806 void c------------------------------() {}
811 Object
* const &o
= this;
815 // trigger touch-activated scripts.
816 // it actually only triggers once his centerline touches the object.
817 // see the passageway between the Throne Room and Kings Table for a
818 // clear example of the correct coordinates.
819 if (o
->flags
& FLAG_SCRIPTONTOUCH
)
823 int y
= player
->y
+ (6 << CSF
);
825 // player->riding check is for fans in Final Cave
826 if ((y
> o
->Top() && y
< o
->Bottom()) || player
->riding
== o
)
828 if (GetCurrentScript() == -1 && // no override other scripts
829 game
.switchstage
.mapno
== -1) // no repeat exec after <TRA
831 stat("On-touch script %d triggered", o
->id2
);
839 // deals contact damage to player of o->damage, if applicable.
840 void Object::DealContactDamage()
842 Object
* const &o
= this;
844 // no contact damage to player while scripts running
845 if (GetCurrentScript() != -1 || player
->inputs_locked
)
848 if (!(o
->flags
& FLAG_NOREARTOPATTACK
))
850 hurtplayer(o
->damage
);
854 // else, the no rear/top attack flag is set, so only
855 // frontal or bottom contact are harmful to the player
856 switch(o
->GetAttackDirection())
859 hurtplayer(o
->damage
);
862 case LEFT
: // rear attack, p to left
863 if (player
->xinertia
> -0x100)
864 player
->xinertia
= -0x100;
867 case RIGHT
: // rear attack, p to right
868 if (player
->xinertia
< 0x100)
869 player
->xinertia
= 0x100;
874 // subfunction of HandleContactDamage. On entry, we assume that the player
875 // is in contact with this object, and that the object is trying to deal
877 // returns the type of attack:
878 // - UP a top attack (player hit top of object)
879 // - LEFT rear attack, player to left
880 // - RIGHT rear attack, player to right
881 // - -1 head-on or bottom attack
882 int Object::GetAttackDirection()
884 Object
* const &o
= this;
885 const int VARIANCE
= (5 << CSF
);
887 if (player
->riding
== o
)
890 if (player
->Bottom() <= (o
->Top() + VARIANCE
))
893 // (added for X treads) if the object is moving, then the "front"
894 // for purposes of this flag is the direction it's moving in.
895 // if it's still, the "front" is the actual direction it's facing.
897 if (o
->xinertia
> 0) rtdir
= RIGHT
;
898 if (o
->xinertia
< 0) rtdir
= LEFT
;
902 if (player
->Right() <= (o
->Left() + VARIANCE
))
905 else if (rtdir
== LEFT
) // the double check makes sense, what if o->dir was UP or DOWN
907 if (player
->Left() >= (o
->Right() - VARIANCE
))
914 void Object::MoveAtDir(int dir
, int speed
)
921 case LEFT
: this->xinertia
= -speed
; break;
922 case RIGHT
: this->xinertia
= speed
; break;
923 case UP
: this->yinertia
= -speed
; break;
924 case DOWN
: this->yinertia
= speed
; break;
929 void c------------------------------() {}
932 // animate over a list of frames, where the frames need not be consecutive.
933 // every speed ticks we will display a new frame from framelist.
934 // this function requires initilization of animframe and animtimer.
935 void Object::animate_seq(int speed
, const int *framelist
, int nframes
)
937 Object
* const &o
= this;
939 if (++o
->animtimer
> speed
)
945 if (o
->animframe
>= nframes
)
948 o
->frame
= framelist
[o
->animframe
];
951 // used by objects in Maze M, this hints to curly's AI that the object is attacking.
952 void Object::CurlyTargetHere(int mintime
, int maxtime
)
954 Object
* const &o
= this;
956 game
.curlytarget
.x
= o
->CenterX();
957 game
.curlytarget
.y
= o
->CenterY();
958 game
.curlytarget
.timeleft
= random(mintime
, maxtime
);
961 // reset the objects clip-extent fields (tp effects etc) to their defaults.
962 // i.e. such that if clip_enable were to be turned on it would have no immediate effect.
963 void Object::ResetClip()
965 Object
* const &o
= this;
967 o
->clipx1
= o
->clipy1
= 0;
968 o
->clipx2
= sprites
[o
->sprite
].w
;
969 o
->clipy2
= sprites
[o
->sprite
].h
;
972 void c------------------------------() {}
975 void Object::OnTick()
977 if (objprop
[this->type
].ai_routines
.ontick
)
978 (*objprop
[this->type
].ai_routines
.ontick
)(this);
981 void Object::OnAftermove()
983 if (objprop
[this->type
].ai_routines
.aftermove
)
984 (*objprop
[this->type
].ai_routines
.aftermove
)(this);
987 void Object::OnSpawn()
989 if (objprop
[this->type
].ai_routines
.onspawn
)
990 (*objprop
[this->type
].ai_routines
.onspawn
)(this);
993 void Object::OnDeath()
995 if (objprop
[this->type
].ai_routines
.ondeath
)
996 (*objprop
[this->type
].ai_routines
.ondeath
)(this);