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