1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/effect.c $
21  * $Revision: 1.111 $
22  * $Author: xemu $
23  * $Date: 1994/11/21 21:06:39 $
24  *
25  */
26 
27 #define _EFFECT_SRC
28 
29 #include <string.h>
30 
31 #include "objgame.h"
32 #include "effect.h"
33 #include "weapons.h" // for handart stuff
34 #include "objprop.h"
35 #include "objwpn.h"
36 #include "objsim.h"
37 #include "faketime.h"
38 #include "gametime.h"
39 #include "damage.h"
40 #include "player.h"
41 #include "mainloop.h"
42 #include "ai.h"
43 #include "objsim.h"
44 #include "objbit.h"
45 #include "otrip.h"
46 #include "cybrnd.h"
47 #include "schedule.h"
48 #include "wares.h"
49 #include "textmaps.h"
50 #include "frparams.h"
51 #include "gamesys.h"
52 #include "hudobj.h"
53 #include "aiflags.h"
54 #include "mapflags.h"
55 #include "doorparm.h"
56 
57 #define HANDART_SPEED 85
58 
59 #define MAX_ANIMLIST_CALLBACKS 10
60 AnimlistCB animlist_callbacks[MAX_ANIMLIST_CALLBACKS];
61 
62 ubyte effect_matrix[CRIT_HIT_NUM][AMMO_TYPES][SEVERITIES] = {
63     {
64          // soft mutants - blood
65          {BLOOD_LIGHT, BLOOD_LIGHT},      // projectiles
66          {BLOOD_LIGHT, BLOOD_LIGHT},      // beam
67          {BLOOD_LIGHT, BLOOD_LIGHT},      // hand-to-hand
68          {M_EXPL2, M_EXPL2}               // grenades
69      }, {
70          // plant mutant
71          {PLNT_EXPL, PLNT_EXPL},          // projectiles
72          {PLNT_EXPL, PLNT_EXPL},          // beam
73          {PLNT_EXPL, PLNT_EXPL},          // hand-to-hand
74          {PLNT_EXPL, PLNT_EXPL}           // grenades
75      }, {
76          // robot
77          {BULLET_ROBOT, BULLET_ROBOT},    // projectile
78          {BEAM_ROBOT_LT, BEAM_ROBOT_HVY}, // beam guns
79          {IMPACT, IMPACT},                // hand-to-hand
80          {M_EXPL2, M_EXPL2}               // grenades
81      }, {
82          // cyborgs
83         {BLOOD_LIGHT, BLOOD_LIGHT},       // flechette bullets
84         {BEAM_ROBOT_HVY, BEAM_ROBOT_HVY}, // beam guns
85         {IMPACT, IMPACT},                 // hand-to-hand
86         {M_EXPL2, M_EXPL2}
87      }, {
88         // all other objects
89         {BULL_HIT_WALL, BULL_HIT_WALL},
90         {BEAM_HIT_WALL, BEAM_HIT_WALL},
91         {IMPACT, IMPACT},
92         {M_EXPL2, M_EXPL2}
93      }
94 };
95 
96 // External Prototypes
97 extern void do_object_explosion(ObjID id);
98 
99 // Internal Prototypes
100 void critter_light_world(ObjID id);
101 void critter_unlight_world(ObjID id);
102 int anim_frames(ObjID id);
103 errtype increment_anim(ulong num_units);
104 uchar anim_data_from_id(ObjID id, bool *reverse, bool *cycle);
105 void init_animlist(void);
106 
107 // -----------------------------------------------------------------
108 // do_special_effect_location
109 //
110 
111 ObjID beam_effect_id = OBJ_NULL;
112 
do_special_effect_location(ObjID owner,ubyte effect,ubyte start,ObjLoc * loc,short s)113 ObjID do_special_effect_location(ObjID owner, ubyte effect, ubyte start, ObjLoc *loc, short s) {
114     ObjID new_id = OBJ_NULL;
115     ObjSpecID osid;
116     int triple = 0;
117 
118     // are we suppose to destroy the attached object?
119     uchar special = DESTROY_OBJ_EFFECT(effect);
120 
121     // strip out the extra stuff
122     effect = EFFECT_VAL(effect);
123 
124     if ((effect < 1) || (effect > EFFECT_NUMS))
125         return (OBJ_NULL);
126 
127     triple = EFFT2TRIP(effect);
128     if (triple) {
129         new_id = obj_create_base(triple);
130         if (new_id == OBJ_NULL) {
131             // make sure that if we're suppose to do an explosion - we do it!
132             if (ExplosionAnimatingProps[SCTRIP(triple)].frame_explode && special)
133                 do_object_explosion(owner);
134             return (OBJ_NULL);
135         }
136         osid = objs[new_id].specID;
137         obj_move_to(new_id, loc, TRUE);
138         if (start == 0xFF)
139             start = START_FRAME(objAnimatings[osid].start_frame);
140         objAnimatings[osid].owner = owner;
141         objs[new_id].info.current_frame = start;
142         if (TRIP2SC(triple) == ANIMATING_SUBCLASS_EXPLOSION) {
143             if (ExplosionAnimatingProps[SCTRIP(triple)].frame_explode && special)
144                 SET_EFFECT_DESTROY_OBJ(objAnimatings[osid].start_frame);
145         }
146         if (AnimatingProps[CPNUM(new_id)].flags & EFFECT_LIGHT_FLAG) {
147             MapElem *mmp;
148             ubyte i;
149             ubyte x = OBJ_LOC_BIN_X(*loc);
150             ubyte y = OBJ_LOC_BIN_Y(*loc);
151             ubyte light_bits = 0;
152 
153             for (i = 0; i < 4; i++) {
154                 mmp = MAP_GET_XY(x + (i / 2), y + (i % 2));
155                 if (!me_bits_rend3(mmp)) {
156                     me_rend3_set(mmp, me_bits_rend3(mmp) + 1);
157                     light_bits |= (1 << i);
158                 }
159             }
160             SET_EFFECT_LIGHT_MAP(objAnimatings[osid].start_frame, light_bits);
161         }
162     }
163     return (new_id);
164 }
165 
166 // --------------------------------------------------------------
167 // do_special_effect()
168 //
169 
do_special_effect(ObjID owner,ubyte effect,ubyte start,ObjID target_id,short location)170 ObjID do_special_effect(ObjID owner, ubyte effect, ubyte start, ObjID target_id, short location) {
171     ObjLoc loc = objs[target_id].loc;
172 
173     return (do_special_effect_location(owner, effect, start, &loc, location));
174 }
175 
critter_light_world(ObjID id)176 void critter_light_world(ObjID id) {
177     int j;
178     ubyte light_bits = 0;
179     ubyte x = OBJ_LOC_BIN_X(objs[id].loc);
180     ubyte y = OBJ_LOC_BIN_Y(objs[id].loc);
181     MapElem *mmp;
182 
183     for (j = 0; j < 4; j++) {
184         mmp = MAP_GET_XY(x + (j / 2), y + (j % 2));
185         if (!me_bits_rend3(mmp)) {
186             me_rend3_set(mmp, me_bits_rend3(mmp) + 1);
187             light_bits |= (1 << j);
188         }
189     }
190 
191     if (light_bits) {
192         SET_CRITLOCX(id, x);
193         SET_CRITLOCY(id, y);
194         SET_CRITTER_LAMP(id, light_bits);
195     }
196 }
197 
critter_unlight_world(ObjID id)198 void critter_unlight_world(ObjID id) {
199     ubyte light_bits = CRITTER_LAMP(id);
200     ubyte x = CRITLOCX(id);
201     ubyte y = CRITLOCY(id);
202     ubyte j;
203     MapElem *mmp;
204 
205     if (light_bits) {
206         for (j = 0; j < 4; j++) {
207             if (light_bits & (1 << j)) {
208                 mmp = MAP_GET_XY(x + (j / 2), y + (j % 2));
209                 me_rend3_set(mmp, me_bits_rend3(mmp) - 1);
210             }
211         }
212         CLEAR_CRITTER_LAMP(id);
213     }
214 }
215 
216 #define DEFAULT_ANIMLIST_SPEED 128
217 // ---------------------------------------------------------
218 // anim_frames()
219 //
220 
221 // in gameobj.c also
222 #define MAX_TELEPORT_FRAME 10
223 #define DIEGO_DEATH_BATTLE_LEVEL 8
224 
anim_frames(ObjID id)225 int anim_frames(ObjID id) {
226     int retval = 1;
227     RefTable *prt;
228 
229     switch (objs[id].obclass) {
230     case CLASS_DOOR:
231         //		prt = ResReadRefTable(door_id(id));
232         prt = (RefTable *)ResLock(door_id(id));
233         retval = prt->numRefs;
234         //		ResFreeRefTable(prt);
235         ResUnlock(door_id(id));
236         break;
237     default:
238         switch (objs[id].obclass) {
239         case CLASS_BIGSTUFF:
240             retval = objBigstuffs[objs[id].specID].cosmetic_value;
241             if (retval == 0)
242                 retval = 1;
243             break;
244         case CLASS_SMALLSTUFF:
245             retval = objSmallstuffs[objs[id].specID].cosmetic_value;
246             if (retval == 0)
247                 retval = 4;
248             break;
249         case CLASS_CRITTER:
250             if ((ID2TRIP(id) == DIEGO_TRIPLE) && (get_crit_posture(objs[id].specID) == DEATH_CRITTER_POSTURE) &&
251                 (player_struct.level != DIEGO_DEATH_BATTLE_LEVEL))
252                 retval = MAX_TELEPORT_FRAME;
253             break;
254         default:
255             retval = FRAME_NUM_3D(ObjProps[OPNUM(id)].bitmap_3d);
256             break;
257         }
258         break;
259     }
260     return (retval);
261 }
262 
263 // Light #defines
264 #define BRIGHT_LIGHT_FLASH 60L // brightness of flash
265 #define LIGHT_DELTA 4          // time length of flash
266 
267 extern void lamp_change_setting(byte offset);
268 extern ubyte energy_expulsion;
269 extern ubyte handart_count;
270 extern uchar handart_flash;
271 
272 #define DEFAULT_ANIMATION_SPEED 32
273 
274 #ifdef USE_ANIMCRIT_DEFS
275 #define STANDARD_CRITTER_SPEED fix_make(0, 0x4000)
276 #define MIN_CRITTER_ANIM_SPEED 25
277 #define MAX_CRITTER_ANIM_SPEED 200
278 #define MIN_MOJO fix_make(0, 0x1A00)
279 #else
280 fix standard_critter_speed = fix_make(0, 0x5800);
281 fix min_mojo = fix_make(0, 0x1a00);
282 int min_critter_anim_speed = 35;
283 int max_critter_anim_speed = 170;
284 int attacking_anim_speed = 45;
285 #endif
286 
increment_anim(ulong num_units)287 errtype increment_anim(ulong num_units) {
288     ObjSpecID osid;
289     ObjID id;
290     uchar anim_rem[MAX_ANIMLIST_SIZE];
291     uchar cb_list[MAX_ANIMLIST_SIZE];
292     int triple, num_frames;
293     char curr_frames;
294     int post, i;
295     short rem_num = 0, cb_num = 0;
296     int interval;
297     ulong new_units;
298     ulong hand_speed = HANDART_SPEED;
299     ubyte old_handart;
300     LightSchedEvent new_event;
301     extern uchar anim_on;
302 #ifdef SPEW_ON
303     char ft1[30];
304 #endif
305 
306     // *************************************************************
307     //                   HAND ART
308     // *************************************************************
309 
310     if (handart_show > 1) {
311         old_handart = handart_show;
312         new_units = num_units + handart_remainder;
313         if (handart_count != 2) {
314             hand_speed = hand_speed / 2;
315         }
316         handart_show += (new_units / hand_speed);
317         handart_remainder = new_units % hand_speed;
318         if (old_handart != handart_show)
319             chg_set_flg(_current_3d_flag);
320 
321         // check to see if we're going to skip the fire frame, if so, don't skip it!
322         // - this is so we definitely show the fire frame, otherwise it would look very goooooofy - minman
323 
324         if (handart_show > (handart_count & 0x7F))
325             handart_show = (handart_fire) ? 1 : 2;
326     }
327     if ((handart_show != 1) && handart_flash) {
328         byte light_val;
329         ubyte slot = player_struct.actives[ACTIVE_WEAPON];
330         extern byte gun_fire_offset;
331 
332         switch (player_struct.weapons[slot].type) {
333         case (GUN_SUBCLASS_BEAM):
334             if (energy_expulsion)
335                 light_val = (ubyte)((BRIGHT_LIGHT_FLASH * energy_expulsion) / 100);
336             break;
337         case (GUN_SUBCLASS_HANDTOHAND):
338             light_val = 0;
339             break;
340         case (GUN_SUBCLASS_PISTOL):
341             if (player_struct.weapons[slot].subtype == 1) {
342                 light_val = 0;
343                 break;
344             }
345         default:
346             light_val = BRIGHT_LIGHT_FLASH;
347             break;
348         }
349 
350         if (light_val) {
351             light_val += gun_fire_offset;
352 
353             new_event.timestamp = TICKS2TSTAMP(player_struct.game_time) + LIGHT_DELTA;
354             new_event.type = LIGHT_SCHED_EVENT;
355             new_event.light_value = light_val;
356             new_event.previous = (player_struct.hardwarez_status[CPTRIP(LANTERN_HARD_TRIPLE)] & WARE_ON);
357             { errtype err = schedule_event(&game_seconds_schedule, (SchedEvent *)&new_event); }
358             lamp_change_setting(light_val);
359             _frp_light_bits_set(LIGHT_BITS_CAM);
360             handart_flash = FALSE;
361         }
362     }
363 
364     if (!anim_on)
365         return (OK);
366 
367     // ****************************************************
368     // Class Animating
369     // ****************************************************
370 
371     osid = objAnimatings[0].id;
372     while (osid != OBJ_SPEC_NULL) {
373         short dest_frame;
374 
375         id = objAnimatings[osid].id;
376         if (id == OBJ_NULL) {
377             osid = OBJ_SPEC_NULL;
378         } else {
379             ubyte spd;
380             int cptrip;
381 
382             triple = MAKETRIP(objs[id].obclass, objs[id].subclass, objs[id].info.type);
383             new_units = num_units + objs[id].info.time_remainder;
384             cptrip = CPTRIP(triple);
385             spd = AnimatingProps[cptrip].speed;
386             if (spd == 0) {
387                 spd = DEFAULT_ANIMATION_SPEED;
388             }
389             interval = new_units / spd;
390             objs[id].info.time_remainder = new_units % spd;
391             objs[id].info.current_frame += interval;
392             if (objs[id].subclass == ANIMATING_SUBCLASS_EXPLOSION) {
393                 if (EFFECT_DESTROY_OBJ(objAnimatings[osid].start_frame) &&
394                     (objs[id].info.current_frame >= ExplosionAnimatingProps[SCNUM(id)].frame_explode)) {
395                     do_object_explosion(objAnimatings[osid].owner);
396                     CLEAR_EFFECT_DESTROY_OBJ(objAnimatings[osid].start_frame);
397                 }
398             }
399             //         dest_frame = objAnimatings[osid].end_frame;
400             //         if (dest_frame==0)
401             dest_frame = FRAME_NUM_3D(ObjProps[OPTRIP(triple)].bitmap_3d);
402             if ((objs[id].info.current_frame != 255) && (objs[id].info.current_frame > dest_frame)) {
403                 if (AnimatingProps[CPNUM(id)].flags & EFFECT_LIGHT_FLAG) {
404                     ubyte i;
405                     ubyte light_bits = EFFECT_LIGHT_MAP(objAnimatings[objs[id].specID].start_frame);
406                     ubyte x = OBJ_LOC_BIN_X(objs[id].loc);
407                     ubyte y = OBJ_LOC_BIN_Y(objs[id].loc);
408                     MapElem *mmp;
409 
410                     for (i = 0; i < 4; i++) {
411                         if (light_bits & (1 << i)) {
412                             mmp = MAP_GET_XY(x + (i / 2), y + (i % 2));
413                             me_rend3_set(mmp, me_bits_rend3(mmp) - 1);
414                         }
415                     }
416                     CLEAR_EFFECT_LIGHT_MAP(objAnimatings[objs[id].specID].start_frame);
417                 }
418 
419                 switch (objs[id].subclass) {
420                 case ANIMATING_SUBCLASS_TRANSITORY:
421                 case ANIMATING_SUBCLASS_EXPLOSION:
422                     ADD_DESTROYED_OBJECT(id);
423                     if (id == beam_effect_id) {
424                         hudobj_set_id(id, FALSE);
425                         beam_effect_id = OBJ_NULL;
426                     }
427                     break;
428                 default:
429                     objs[id].info.current_frame = START_FRAME(objAnimatings[osid].start_frame);
430                     break;
431                 }
432             }
433             osid = objAnimatings[osid].next;
434         }
435     }
436 
437     // Objects on the animation list
438     LG_memset(anim_rem, 0, MAX_ANIMLIST_SIZE);
439     LG_memset(cb_list, 0, MAX_ANIMLIST_SIZE);
440     for (i = 0; i < anim_counter; i++) {
441         id = animlist[i].id;
442         num_frames = anim_frames(id);
443         new_units = num_units + objs[id].info.time_remainder;
444         interval = new_units / animlist[i].speed;
445         objs[id].info.time_remainder = new_units % animlist[i].speed;
446         if (animlist[i].flags & ANIMFLAG_REVERSE) {
447             switch (objs[id].obclass) {
448             case CLASS_DOOR:
449                 if ((objs[id].info.current_frame - interval < DOOR_OPEN_FRAME) &&
450                     (objs[id].info.current_frame >= DOOR_OPEN_FRAME)) {
451                     obj_physics_refresh_area(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc), TRUE);
452                 }
453                 break;
454             }
455             if (objs[id].info.current_frame < interval) {
456                 if (animlist[i].flags & ANIMFLAG_CYCLE) {
457                     if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_CYCLE))
458                         cb_list[cb_num++] = i;
459                     objs[id].info.current_frame = 0;
460                     // turn around
461                     animlist[i].flags &= ~ANIMFLAG_REVERSE;
462                 } else if (animlist[i].flags & ANIMFLAG_REPEAT) {
463                     if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_REPEAT))
464                         cb_list[cb_num++] = i;
465                     objs[id].info.current_frame = num_frames - 1;
466                 } else {
467                     objs[id].info.current_frame = 0;
468                     anim_rem[rem_num++] = i;
469                     switch (objs[id].obclass) {
470                     case CLASS_DOOR:
471                         // if we are a kind of door that blocks the renderer, and we are closed, then
472                         // set our instance bit so that the renderer can know about it.
473                         if (RENDER_BLOCK & ObjProps[OPNUM(id)].flags)
474                             objs[id].info.inst_flags |= RENDER_BLOCK_FLAG;
475                         break;
476                     }
477                 }
478             } else
479                 objs[id].info.current_frame -= interval;
480         } else {
481             switch (objs[id].obclass) {
482             case CLASS_DOOR:
483                 if (((objs[id].loc.p != 0) || (objs[id].loc.b != 0)) &&
484                     (objs[id].info.current_frame < DOOR_OPEN_FRAME) &&
485                     (objs[id].info.current_frame + interval >= DOOR_OPEN_FRAME)) {
486                     obj_physics_refresh_area(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc), TRUE);
487                 }
488                 break;
489             }
490             objs[id].info.current_frame += interval;
491 
492             if (objs[id].info.current_frame >= num_frames) {
493                 if (animlist[i].flags & ANIMFLAG_CYCLE) {
494                     if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_CYCLE))
495                         cb_list[cb_num++] = i;
496                     objs[id].info.current_frame = num_frames - 1;
497                     // turn around
498                     animlist[i].flags |= ANIMFLAG_REVERSE;
499                 } else if (animlist[i].flags & ANIMFLAG_REPEAT) {
500                     if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_REPEAT))
501                         cb_list[cb_num++] = i;
502                     objs[id].info.current_frame = 0;
503                 } else {
504                     objs[id].info.current_frame = num_frames - 1;
505                     anim_rem[rem_num++] = i;
506                 }
507             }
508         }
509     }
510 
511     for (i = 0; i < cb_num; i++)
512         animlist_callbacks[animlist[cb_list[i]].callback](animlist[cb_list[i]].id, animlist[cb_list[i]].user_data);
513     for (i = 0; i < rem_num; i++)
514         remove_obj_from_animlist(animlist[anim_rem[i]].id);
515 
516     // Class Critter
517     // iterate through all the critters, and update their frames...
518     osid = objCritters[0].id;
519     while (osid != OBJ_SPEC_NULL) {
520         int cptripnum, asp;
521         id = objCritters[osid].id;
522         triple = MAKETRIP(objs[id].obclass, objs[id].subclass, objs[id].info.type);
523         if ((triple == DIEGO_TRIPLE) && (get_crit_posture(objs[id].specID) == DEATH_CRITTER_POSTURE) &&
524             (player_struct.level != DIEGO_DEATH_BATTLE_LEVEL)) {
525             osid = objCritters[osid].next;
526             continue;
527         }
528         if (EFFECT_LOC(id)) {
529             //         ulong time = (player_struct.game_time & 0x03);
530             //         if ((time/32) != EFFECT_EIGHTH(id))
531             //         {
532             //            SET_EFFECT_FRAME(id,EFFECT_FRAME(id)+(((time/32)+8-EFFECT_EIGHTH(id))%8));
533             //          if (EFFECT_FRAME(id) > FRAME_NUM_3D(ObjProps[OPTRIP(EFFT2TRIP(EFFECT_NUM(id)))].bitmap_3d))
534             //              SET_EFFECT_LOC(id,0);
535             //         SET_EFFECT_EIGHTH(id, (time/32));
536             //     }
537         }
538         if ((id != PLAYER_OBJ) && (!ai_critter_sleeping(osid) || (get_crit_posture(osid) == DEATH_CRITTER_POSTURE))) {
539             new_units = num_units + objs[id].info.time_remainder;
540             cptripnum = CPTRIP(triple);
541             post = get_crit_posture(osid);
542             asp = CritterProps[cptripnum].anim_speed;
543             if (asp == 0) {
544                 asp = 1;
545             } else {
546                 if ((post == MOVING_CRITTER_POSTURE) && (objs[id].info.ph != -1)) {
547                     State s;
548                     fix pd;
549 #ifdef USE_PHYS_STATE
550                     extern void get_phys_state(int ph, State *new_state, ObjID id);
551                     get_phys_state(objs[id].info.ph, &s, id);
552 #else
553                     EDMS_get_state(objs[id].info.ph, &s);
554 #endif
555                     pd = fix_fast_pyth_dist(s.X_dot, s.Y_dot);
556                     if (pd > min_mojo) {
557                         asp = fix_int(fix_mul_div(fix_make(asp, 0), standard_critter_speed, pd));
558                         if (asp < min_critter_anim_speed)
559                             asp = min_critter_anim_speed;
560                         else if (asp > max_critter_anim_speed)
561                             asp = max_critter_anim_speed;
562                     }
563                 } else if ((post == ATTACKING_CRITTER_POSTURE) || (post == ATTACKING2_CRITTER_POSTURE)) {
564                     asp = attacking_anim_speed;
565                 }
566             }
567             interval = new_units / asp;
568             objs[id].info.time_remainder = new_units % asp;
569             objs[id].info.inst_flags &= ~(UNLIT_FLAG);
570 
571             if (CRITTER_LAMP(id) && (ID2TRIP(id) != AUTOBOMB_TRIPLE))
572                 critter_unlight_world(id);
573 
574             // Do attack if at right point in anim
575             curr_frames = (objs[id].subclass == CRITTER_SUBCLASS_CYBER) ? 4 : CritterProps[CPTRIP(triple)].frames[post];
576             if ((post == ATTACKING_CRITTER_POSTURE) || (post == ATTACKING2_CRITTER_POSTURE)) {
577                 short att_frame;
578 
579                 att_frame = (CritterProps[CPTRIP(triple)].fire_frame == 0) ? (curr_frames * 3) / 4
580                                                                            : CritterProps[CPTRIP(triple)].fire_frame;
581 
582                 // Only attack when first reaching or passing sancted attack frame
583                 if ((objs[id].info.current_frame < att_frame) &&
584                     (objs[id].info.current_frame + interval >= att_frame)) {
585                     ai_attack_player(osid, (post == ATTACKING2_CRITTER_POSTURE));
586                     // make sure we see the right attack frame
587                     objs[id].info.current_frame = att_frame;
588                     if (((ObjProps[OPTRIP(triple)].flags & LIGHT_TYPE) >> LIGHT_TYPE_SHF) == 3) {
589                         objs[id].info.inst_flags |= UNLIT_FLAG;
590                         if (!CRITTER_LAMP(id) && (ID2TRIP(id) != AUTOBOMB_TRIPLE))
591                             critter_light_world(id);
592                     }
593                 } else
594                     objs[id].info.current_frame += interval;
595             } else
596                 objs[id].info.current_frame += interval;
597 
598             // If past end of cycle, wrap around
599             if (objs[id].info.current_frame >= curr_frames) {
600                 // If dying, remove at end of cycle
601                 if (post == DEATH_CRITTER_POSTURE) {
602                     objs[id].info.current_frame = curr_frames - 1;
603                     ai_critter_really_dead(objs[id].specID);
604 
605                     // turn off the darned autobomb before we kill it
606                     if (CRITTER_LAMP(id) && (ID2TRIP(id) == AUTOBOMB_TRIPLE))
607                         critter_unlight_world(id);
608 
609                     ADD_DESTROYED_OBJECT(id);
610                 } else if ((post == ATTACKING_CRITTER_POSTURE) || (post == ATTACKING2_CRITTER_POSTURE) ||
611                            (post == DISRUPT_CRITTER_POSTURE) || (post == KNOCKBACK_CRITTER_POSTURE)) {
612                     set_posture(objs[id].specID, ATTACK_REST_CRITTER_POSTURE);
613                 } else {
614                     // Otherwise, loop around again
615                     objs[id].info.current_frame = 0;
616                     objs[id].info.time_remainder = 0;
617                 }
618             }
619         }
620 
621         // Go to next critter
622         osid = objCritters[osid].next;
623     }
624 
625     // Animating Textures
626     // Start at 1, since 0 is the normal texture group
627     for (i = 1; i < NUM_ANIM_TEXTURE_GROUPS; i++) {
628         if (animtextures[i].num_frames > 0) {
629             new_units = num_units + animtextures[i].time_remainder;
630             interval = new_units / animtextures[i].anim_speed;
631             animtextures[i].time_remainder = new_units % animtextures[i].anim_speed;
632             while (interval > 0) {
633                 if (animtextures[i].flags & ANIMTEXTURE_REVERSED) {
634                     // Currently, REVERSED implies CYCLE.  Maybe this should change
635                     // in the future.
636                     animtextures[i].current_frame--;
637                     if (animtextures[i].current_frame < 0) {
638                         animtextures[i].flags &= ~(ANIMTEXTURE_REVERSED);
639                         animtextures[i].current_frame = 0;
640                     }
641                 } else {
642                     animtextures[i].current_frame++;
643                     if (animtextures[i].current_frame >= animtextures[i].num_frames) {
644                         if (animtextures[i].flags & ANIMTEXTURE_CYCLE) {
645                             animtextures[i].flags |= ANIMTEXTURE_REVERSED;
646                             animtextures[i].current_frame = animtextures[i].num_frames - 1;
647                         } else {
648                             animtextures[i].current_frame = 0;
649                         }
650                     }
651                 }
652                 interval--;
653             }
654         }
655     }
656     destroy_destroyed_objects();
657     return (OK);
658 }
659 
advance_animations(void)660 void advance_animations(void) {
661     ulong time_diff;
662 
663     time_diff = (player_struct.game_time - player_struct.last_anim_check);
664     increment_anim(time_diff);
665     player_struct.last_anim_check = player_struct.game_time;
666 }
667 
668 // fills in requested types of information about an animating object,
669 // returning FALSE iff that object is not in the anim list.  Pass a
670 // NULL pointer about a piece of data if you don't want it.
anim_data_from_id(ObjID id,bool * reverse,bool * cycle)671 uchar anim_data_from_id(ObjID id, bool *reverse, bool *cycle) {
672     int i;
673 
674     for (i = 0; i < anim_counter; i++) {
675         if (animlist[i].id == id) {
676             if (reverse)
677                 *reverse = (animlist[i].flags & ANIMFLAG_REVERSE) != 0;
678             if (cycle)
679                 *cycle = (animlist[i].flags & ANIMFLAG_CYCLE) != 0;
680             return TRUE;
681         }
682     }
683     return FALSE;
684 }
685 
686 #define CHECK_ANIM_SPEED
add_obj_to_animlist(ObjID id,uchar repeat,uchar reverse,uchar cycle,short speed,int cb_id,intptr_t user_data,short cbtype)687 errtype add_obj_to_animlist(ObjID id, uchar repeat, uchar reverse, uchar cycle, short speed, int cb_id, intptr_t user_data,
688                             short cbtype) {
689     int i = 0;
690     uchar replace_me = FALSE;
691     int use_counter = anim_counter;
692 #ifdef CHECK_ANIM_SPEED
693     char count = 0;
694 #endif
695 
696     if (anim_counter == MAX_ANIMLIST_SIZE) {
697         return (ERR_NOMEM);
698     }
699 
700     for (i = 0; i < anim_counter; i++) {
701         if (animlist[i].id == id) {
702             replace_me = TRUE;
703             use_counter = i;
704         }
705     }
706     animlist[use_counter].id = id;
707 
708     // Set flags
709     animlist[use_counter].flags = 0;
710     if (repeat)
711         animlist[use_counter].flags |= ANIMFLAG_REPEAT;
712     if (reverse)
713         animlist[use_counter].flags |= ANIMFLAG_REVERSE;
714     if (cycle)
715         animlist[use_counter].flags |= ANIMFLAG_CYCLE;
716     if (speed)
717         animlist[use_counter].speed = speed;
718     else
719         animlist[use_counter].speed = DEFAULT_ANIMLIST_SPEED;
720 #ifdef CHECK_ANIM_SPEED
721     // Hmm, there's probably a better way to check for power-of-2-ness
722     for (i = 0; i < 16; i++) {
723         if (animlist[use_counter].speed & (1 << i))
724             count++;
725         if (count > 1) {
726             break;
727         }
728     }
729 #endif
730 
731     animlist[use_counter].cbtype = cbtype;
732     animlist[use_counter].callback = cb_id;
733     animlist[use_counter].user_data = user_data;
734     if (!replace_me)
735         anim_counter++;
736 
737     objs[id].info.time_remainder = 0;
738     return (OK);
739 }
740 
remove_obj_from_animlist(ObjID id)741 errtype remove_obj_from_animlist(ObjID id) {
742     int i = 0;
743     AnimlistCB cb = NULL;
744     intptr_t ud;
745 
746     for (i = 0; i < anim_counter; i++) {
747         if (animlist[i].id == id) {
748             if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_REMOVE)) {
749                 cb = animlist_callbacks[animlist[i].callback];
750                 ud = animlist[i].user_data;
751             }
752             anim_counter--;
753             animlist[i] = animlist[anim_counter];
754             if (cb != NULL)
755                 cb(id, ud);
756             return (OK);
757         }
758     }
759     return (ERR_NOEFFECT);
760 }
761 
animlist_clear()762 errtype animlist_clear() {
763     LG_memset(animlist, 0, sizeof(AnimListing) * MAX_ANIMLIST_SIZE);
764     anim_counter = 0;
765     return (OK);
766 }
767 
init_animlist(void)768 void init_animlist(void) {
769     extern void diego_teleport_callback(ObjID id, intptr_t user_data);
770     extern void destroy_screen_callback_func(ObjID id, intptr_t user_data);
771     extern void unshodanizing_callback(ObjID id, intptr_t user_data);
772     extern void unmulti_anim_callback(ObjID id, intptr_t user_data);
773     extern void multi_anim_callback(ObjID id, intptr_t user_data);
774     extern void animate_callback_func(ObjID id, intptr_t user_data);
775     animlist_callbacks[1] = diego_teleport_callback;
776     animlist_callbacks[2] = destroy_screen_callback_func;
777     animlist_callbacks[3] = unshodanizing_callback;
778     animlist_callbacks[4] = unmulti_anim_callback;
779     animlist_callbacks[5] = multi_anim_callback;
780     animlist_callbacks[6] = animate_callback_func;
781 }
782