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/trigger.c $
21  * $Revision: 1.161 $
22  * $Author: xemu $
23  * $Date: 1994/11/25 18:18:30 $
24  *
25  */
26 
27 #define __TRIGGER_SRC
28 
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "Prefs.h"
33 
34 #include "ai.h"
35 #include "audiolog.h"
36 #include "criterr.h"
37 #include "cybstrng.h"
38 #include "damage.h"
39 #include "diffq.h"
40 #include "doorparm.h"
41 #include "effect.h"
42 #include "faketime.h"
43 #include "frflags.h"
44 #include "frprotox.h"
45 #include "gameloop.h" // for VITALS_UPDATE
46 #include "gamerend.h"
47 #include "gamescr.h"
48 #include "gamestrn.h"
49 #include "invent.h"
50 #include "mainloop.h" // for flag setting stuff
51 #include "map.h"
52 #include "mapflags.h"
53 #include "mfdext.h"
54 #include "musicai.h"
55 #include "objbit.h"
56 #include "objcrit.h"
57 #include "objgame.h"
58 #include "objsim.h"
59 #include "objstuff.h"
60 #include "objuse.h"
61 #include "olhext.h"
62 #include "otrip.h"
63 #include "physics.h"
64 #include "player.h"
65 #include "saveload.h"
66 #include "schedule.h"
67 #include "sfxlist.h"
68 #include "shodan.h"
69 #include "tilename.h"
70 #include "tools.h"
71 #include "trigger.h"
72 #include "hkeyfunc.h"
73 #include "cyber.h"
74 #include "colors.h"
75 #include "grenades.h"
76 #include "bark.h"
77 #include "view360.h"
78 #include "objload.h"
79 #include "rendfx.h"
80 #include "statics.h"
81 #include "citres.h"
82 #include "setploop.h"
83 #include "cutsloop.h"
84 
85 #ifdef OLD_TELEPORT_BETWEEN_LEVELS
86 #include <gamewrap.h>
87 #include <gamerend.h>
88 #include <render.h>
89 #endif
90 
91 // As far as I can tell, these NEVER GET USED.  So I thought I'd move them out of the file
92 // to remove a dependency problem.
93 
94 #define TRAP_NULL_CODE 0
95 #define TRAP_TELEPORT_CODE 1
96 #define TRAP_DAMAGE_CODE 2
97 #define TRAP_CREATE_OBJ_CODE 3
98 #define TRAP_QUESTBIT_CODE 4
99 #define TRAP_ENDGAME_CODE 5
100 #define TRAP_MULTI_CODE 6
101 #define TRAP_LIGHT_CODE 7
102 #define TRAP_SFX_CODE 8
103 #define TRAP_HEIGHT_CODE 9
104 #define TRAP_TERRAIN_CODE 10
105 #define TRAP_SCHEDULER_CODE 11
106 #define TRAP_ALT_SPLIT_CODE 12
107 #define TRAP_DESTROY_OBJ_CODE 13
108 #define TRAP_PLOT_CLOCK_CODE 14
109 #define TRAP_EMAIL_CODE 15
110 #define TRAP_EXPOSE_CODE 16
111 #define TRAP_INSTANCE_CODE 17
112 #define TRAP_ANIMATE_CODE 18
113 #define TRAP_HACK_CODE 19
114 #define TRAP_TEXTURE_CODE 20
115 #define TRAP_AI_CODE 21
116 #define TRAP_BARK_CODE 22
117 #define TRAP_MONSTER_CODE 23
118 #define TRAP_TRANSMOGRIFY_CODE 24
119 
120 // Note that this will always give you boolean 0 or 1, as opposed to
121 // things like (p2 & 0x10000) that certain people once used which is
122 // always 0 when cast to a bool.
123 
124 #define BIT_SET(val, bit) (((val) & (1 << bit)) == (1 << bit))
125 
126 #define REACTOR_BOOM_QB 0x14
127 errtype do_special_reactor_hack();
128 
129 errtype do_destroy(int victim_data);
130 
131 ObjID current_trap;
132 uchar _tr_use_message;
133 #define trap_use_message (&_tr_use_message)
134 
135 short qdata_get(short qdata);
136 errtype qdata_set(short qdata, short new_val);
137 uchar comparator_check(int comparator, ObjID obj, uchar *special_code);
138 errtype set_trap_data(ObjID id, char num_param, int new_val);
139 errtype do_timed_multi_stuff(int p);
140 
141 errtype trap_null_func(int p1, int p2, int p3, int p4);
142 errtype trap_transmogrify_func(int p1, int p2, int p3, int p4);
143 uchar player_facing_square(LGPoint sq);
144 errtype trap_monster_func(int p1, int p2, int p3, int p4);
145 errtype do_ai_trap(ObjSpecID osid, int p1, int p3, int p4);
146 errtype trap_ai_func(int p1, int p2, int p3, int p4);
147 errtype trap_alternating_splitter_func(int p1, int p2, int p3, int p4);
148 errtype trap_main_light_func(int p1, int p2, int p3, int p4);
149 errtype trap_terrain_func(int p1, int p2, int p3, int p4);
150 errtype trap_height_func(int p1, int p2, int p3, int p4);
151 errtype real_instance_func(int p1, int p2, int p3, int p4);
152 errtype trap_instance_func(int p1, int p2, int p3, int p4);
153 void animate_callback_func(ObjID id, intptr_t user_data);
154 errtype real_animate_func(ObjID id, int p2, int p3, int p4);
155 errtype trap_animate_func(int p1, int p2, int p3, int p4);
156 void hack_shodan_conquer_func(char bonus_fun);
157 void hack_armageddon_func(int otrip, int x0, int y0, int r);
158 void hack_multi_trans(int trip, int newtype);
159 void hack_change_comparator(int p2, int p3);
160 void hack_taunt_diego(int p2, int p3);
161 errtype trap_hack_func(int p1, int p2, int p3, int p4);
162 errtype trap_multi_func(int p1, int p2, int p3, int p4);
163 errtype trap_destroy_object_func(int p1, int p2, int p3, int p4);
164 errtype trap_plot_clock_func(int p1, int p2, int p3, int p4);
165 errtype trap_email_func(int mung, int time, int p3, int p4);
166 errtype trap_texture_func(int p1, int p2, int p3, int p4);
167 errtype trap_expose_func(int dmg, int dtype, int tsecs, int dummy);
168 errtype trap_bark_func(int speaker, int strnum, int color, int hud_bark);
169 
170 errtype grind_trap(char type, int p1, int p2, int p3, int p4, ubyte *destroy_count_ptr, ObjID id);
171 errtype do_level_entry_triggers();
172 errtype do_shodan_triggers();
173 errtype do_ecology_triggers();
174 
175 #define SHODOMETER_QVAR_BASE 0x10
176 
qdata_get(short qdata)177 short qdata_get(short qdata) {
178     short contents = qdata & 0xFFF;
179     if (qdata & 0x1000) {
180         if ((contents >= FIRST_SHODAN_QV) && (contents <= FIRST_SHODAN_QV + MAX_SHODOMETER_LEVEL)) {
181             short retval;
182             if (QUESTVAR_GET(MISSION_DIFF_QVAR) <= 1)
183                 return (0);
184             retval = QUESTVAR_GET(contents) * 255 / player_struct.initial_shodan_vals[contents - FIRST_SHODAN_QV];
185             if (retval > 255)
186                 retval = 255;
187             return (retval);
188         } else
189             return (QUESTVAR_GET(contents));
190     } else if (qdata & 0x2000)
191         return (QUESTBIT_GET(contents));
192     else
193         return (contents);
194 }
195 
qdata_set(short qdata,short new_val)196 errtype qdata_set(short qdata, short new_val) {
197     if (qdata & 0x1000)
198         QUESTVAR_SET(qdata & 0xFFF, new_val);
199     else if (qdata & 0x2000) {
200         if ((new_val > 1) || (new_val < -1)) {
201             if (qdata_get(qdata))
202                 new_val = 0;
203             else
204                 new_val = 1;
205         }
206         if (new_val)
207             QUESTBIT_ON(qdata & 0xFFF);
208         else
209             QUESTBIT_OFF(qdata & 0xFFF);
210     }
211 
212     mfd_notify_func(MFD_PLOTWARE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE);
213 
214     return (OK);
215 }
216 
comparator_check(int comparator,ObjID obj,uchar * special_code)217 uchar comparator_check(int comparator, ObjID obj, uchar *special_code) {
218     short cval, compval;
219     uchar fail_code;
220     short fail_amt = 0;
221     uchar truthval = FALSE;
222     // shodo_qvar is the questvariable for the shodometer on the current levelle
223     char shodo_qvar = SHODOMETER_QVAR_BASE + player_struct.level;
224 
225     *special_code = 0;
226     fail_code = comparator >> 24;
227     comparator = comparator & 0xFFFFFF;
228     if (comparator == 0)
229         return (TRUE);
230     compval = comparator >> 16;
231     if (comparator & 0x1000) {
232         cval = qdata_get(0x1000 | (comparator & 0xFFF)); // QUESTVAR_GET(comparator & 0xFFF);
233     } else
234         cval = (QUESTBIT_GET(comparator & 0xFFF) != 0);
235     switch ((comparator & 0xE000) >> 13) {
236     case 0:
237         truthval = (cval == compval);
238         fail_amt = abs(cval - compval);
239         break;
240     case 1:
241         truthval = (cval < compval);
242         fail_amt = cval - compval;
243         break;
244     case 2:
245         truthval = (cval <= compval);
246         fail_amt = cval - compval;
247         break;
248     case 3:
249         truthval = (cval > compval);
250         fail_amt = compval - cval;
251         break;
252     case 4:
253         truthval = (cval >= compval);
254         fail_amt = compval - cval;
255         break;
256     case 5:
257         truthval = (cval != compval);
258         fail_amt = abs(cval - compval);
259         break;
260     case 6:
261         truthval = (compval > (rand() % 255));
262         break;
263     }
264     if (!truthval && fail_code) {
265         if ((comparator & 0xFFF) == shodo_qvar)
266             *special_code = fail_amt;
267         if ((*special_code != 0) && (fail_code == SPECIAL_SHODAN_FAIL_CODE)) {
268             short shodan_amt;
269             shodan_amt = lg_min(NUM_SHODAN_MUGS - 1, *special_code >> SHODAN_INTERVAL_SHIFT);
270             long_bark(obj, FIRST_SHODAN_MUG + shodan_amt, SHODAN_FAILURE_STRING, 0x4c);
271         } else {
272             string_message_info(REF_STR_TrapZeroMessage + fail_code);
273 #ifdef AUDIOLOGS
274             audiolog_bark_play(fail_code);
275 #endif
276         }
277     }
278 
279     return (truthval);
280 }
281 
set_trap_data(ObjID id,char num_param,int new_val)282 errtype set_trap_data(ObjID id, char num_param, int new_val) {
283     uint *pbase = NULL;
284     ObjSpecID osid = objs[id].specID;
285 
286     if (num_param < 1 || num_param > 4)
287         return (ERR_NOEFFECT);
288 
289     switch (objs[id].obclass) {
290     case CLASS_FIXTURE:
291         pbase = &(objFixtures[osid].p1);
292         break;
293     case CLASS_TRAP:
294         pbase = &(objTraps[osid].p1);
295         break;
296     }
297 
298     if (pbase == NULL)
299         return (ERR_NOEFFECT);
300 
301     *(pbase + (num_param - 1)) = new_val;
302 
303     return (OK);
304 }
305 
trigger_check_destroyed(ObjID id)306 errtype trigger_check_destroyed(ObjID id) {
307     errtype retval = OK;
308     if ((objs[id].obclass == CLASS_FIXTURE) && (objs[id].subclass == FIXTURE_SUBCLASS_CYBER))
309         retval = trap_activate(id, trap_use_message);
310     return (retval);
311 }
312 
location_trigger_activate(ObjID id)313 errtype location_trigger_activate(ObjID id) {
314     errtype retval = ERR_NOEFFECT;
315 
316     if (objs[id].info.type == ENTRY_TRIGGER_TYPE)
317         retval = trap_activate(id, trap_use_message);
318     if (objs[id].info.type == FLOOR_TRIGGER_TYPE) {
319         if (fix_from_obj_height(PLAYER_OBJ) <
320             (STANDARD_SIZE + (fix_from_map_height(me_height_flr(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y))))))
321             retval = trap_activate(id, trap_use_message);
322     }
323 
324     return (retval);
325 }
326 
trap_null_func(int p1,int p2,int p3,int p4)327 errtype trap_null_func(int p1, int p2, int p3, int p4) { return (OK); }
328 
329 #define MAX_BRIDGE_FRAME 32
330 
trap_transmogrify_func(int p1,int p2,int p3,int p4)331 errtype trap_transmogrify_func(int p1, int p2, int p3, int p4) {
332     extern void slam_posture_meter_state(void);
333     int source, dest, t1, t2;
334 
335     if (!objs[p1].active) {
336         return (ERR_NOEFFECT);
337     }
338     t1 = p2 & 0xFFFF;
339 
340     // okay, so this is kind of a dopey way to do things, but it's backwards-
341     // compatible with our initial transmog spec.  For FC, we should not do
342     // this ... hopefully following ifdef trick will make sure we change it.
343 
344 #ifdef FIRST_SHODAN_MUG
345     t2 = t1 ^ (p2 >> 16);
346 #else
347 #error "hey, look at me, I don't compile.  Look at me! t2 = (p2 >> 16);"
348 #endif
349 
350     if (((t1 > t2) ? t1 : t2) >= num_types(objs[p1].obclass, objs[p1].subclass)) {
351         return (ERR_NOEFFECT);
352     }
353     source = ID2TRIP(p1);
354     if (objs[p1].info.type == t1)
355         objs[p1].info.type = t2;
356     else
357         objs[p1].info.type = t1;
358     dest = ID2TRIP(p1);
359     //   if (p3 > 0)
360     {
361         switch (source) {
362         case NON_BRIDGE_TRIPLE:
363             if ((source == NON_BRIDGE_TRIPLE) && ((dest == FORCE_BRIJ_TRIPLE) || (dest == FORCE_BRIJ2_TRIPLE))) {
364                 remove_obj_from_animlist(p1);
365                 objs[p1].info.current_frame = MAX_BRIDGE_FRAME;
366                 objBigstuffs[objs[p1].specID].cosmetic_value = MAX_BRIDGE_FRAME;
367                 // counting backwards from zero
368                 add_obj_to_animlist(p1, FALSE, TRUE, FALSE, 16, 0, 0, 0);
369                 if (source == NON_BRIDGE_TRIPLE) {
370                     //hack: on level 4 only allow one simultaneously playing force bridge sound
371                     if (player_struct.level != 4 || !digi_fx_playing(SFX_FORCE_BRIDGE, NULL))
372                     play_digi_fx_obj(SFX_FORCE_BRIDGE, 1, p1);
373                 }
374             }
375             break;
376 #ifdef TWO_WAY_TRANSMOGGING
377         case FORCE_BRIJ_TRIPLE:
378         case FORCE_BRIJ2_TRIPLE:
379             if (dest == NON_BRIDGE_TRIPLE) {
380                 remove_obj_from_animlist(p1);
381                 objs[p1].info.current_frame = 0;
382                 objBigstuffs[objs[p1].specID].cosmetic_value = MAX_BRIDGE_FRAME;
383                 add_obj_to_animlist(p1, FALSE, FALSE, FALSE, 16, 0, 0, 0);
384             }
385 #endif
386         }
387     }
388     slam_posture_meter_state();
389     obj_physics_refresh_area(OBJ_LOC_BIN_X(objs[p1].loc), OBJ_LOC_BIN_Y(objs[p1].loc), FALSE);
390     return (OK);
391 }
392 
393 // Above this search size, just go through list of objCritters, otherwise
394 // look at all the objects
395 #define SEARCH_AREA_THRESHOLD 81
396 
397 #define MIN_MONSTER_DISTANCE 7
398 #define NOLOOK_MONSTER_DISTANCE 14
399 
400 #define CONSERVATIVE_OCTANT_ARC 0x2500
401 
402 // Okay, this is stupid and uses fix_atan but WTF, I wanted to
403 // write something really quickly for alpha.
404 
405 // cast to signed-ness before taking abs
406 #define fixang_abs(x) abs((short)(x))
407 
player_facing_square(LGPoint sq)408 uchar player_facing_square(LGPoint sq) {
409     fixang plrh, sqh, delta;
410     plrh = fixang_from_phys_angle(phys_angle_from_obj(objs[PLAYER_OBJ].loc.h));
411     // reflect x,y around the line x=y to sneakily convert fixang in regular-
412     // guy coordinates to fixang in north-is-zero-and-clockwise coordinates
413     // so, anyway, I'm swapping x and y for a reason here.
414     sqh = fix_atan2(fix_make(sq.x - PLAYER_BIN_X, 0), fix_make(sq.y - PLAYER_BIN_Y, 0));
415     delta = sqh - plrh;
416     if (fixang_abs(delta) < CONSERVATIVE_OCTANT_ARC)
417         return (TRUE);
418     // note that we're working in a left-handed, clockwise world
419     if (view360_active_contexts[LEFT_CONTEXT] && fixang_abs(delta + 0x4000) < CONSERVATIVE_OCTANT_ARC)
420         return (TRUE);
421     if (view360_active_contexts[RIGHT_CONTEXT] && fixang_abs(delta - 0x4000) < CONSERVATIVE_OCTANT_ARC)
422         return (TRUE);
423     if (view360_active_contexts[MID_CONTEXT] && fixang_abs(delta + 0x8000) < CONSERVATIVE_OCTANT_ARC)
424         return (TRUE);
425     return (FALSE);
426 }
427 
428 // p1 = triple
429 // p2 = area of effect
430 // p3 = quantity of object to create
trap_monster_func(int p1,int p2,int p3,int p4)431 errtype trap_monster_func(int p1, int p2, int p3, int p4) {
432     ObjID new_id, id1, id2;
433     ObjSpecID osid;
434     ObjRefID oref;
435     char minx, miny, sizex, sizey, quan, failures = 0, num_gen = 0;
436     LGPoint sq;
437     extern errtype obj_load_art(uchar flush_all);
438     uchar okay = FALSE;
439     char monster_count;
440     MapElem *pme;
441 
442     TRACE("%s: trigger", __FUNCTION__);
443 
444     quan = qdata_get(p3);
445     switch (player_struct.difficulty[COMBAT_DIFF_INDEX]) {
446     case 0:
447         quan = 0;
448         break;
449     case 3:
450         quan++;
451     }
452     while (quan > 0) {
453         id1 = p2 & 0xFFFF;
454         id2 = p2 >> 16;
455         if (id2 != OBJ_NULL) {
456             minx = lg_min(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc));
457             miny = lg_min(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc));
458             sizex = lg_max(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc)) - minx;
459             sizey = lg_max(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc)) - miny;
460         } else {
461             sizex = sizey = id1 * 2;
462             minx = OBJ_LOC_BIN_X(objs[current_trap].loc) - sizex;
463             miny = OBJ_LOC_BIN_Y(objs[current_trap].loc) - sizey;
464         }
465         okay = FALSE;
466         monster_count = 0;
467         while (!okay && (monster_count < 100)) {
468             int tiletype, room;
469 
470             okay = TRUE;
471             sq.x = minx + rand() % (sizex + 1);
472             sq.y = miny + rand() % (sizey + 1);
473             pme = MAP_GET_XY(sq.x, sq.y);
474 
475             tiletype = me_tiletype(pme);
476             room = MAP_HEIGHTS - me_height_ceil(pme) - me_height_flr(pme);
477 
478             if (tiletype >= TILE_SLOPEUP_N && tiletype <= TILE_SLOPECV_SW) {
479                 if (me_bits_mirror(pme) == MAP_FFLAT)
480                     room -= me_param(pme);
481                 else
482                     room = 0;
483             } else if (tiletype != TILE_OPEN)
484                 room = 0;
485             room = (room << MAP_ZSHF) / 8;
486             if (room >= 8) {
487                 oref = me_objref(pme);
488                 while (okay && (oref != OBJ_REF_NULL)) {
489                     if (objs[objRefs[oref].obj].obclass == CLASS_CRITTER) {
490                         okay = FALSE;
491                     }
492                     oref = objRefs[oref].next;
493                 }
494             } else {
495                 okay = FALSE;
496                 break;
497             }
498             if (p4 & 0x8)
499                 okay = TRUE;
500             if (okay) {
501                 char d;
502                 d = abs(PLAYER_BIN_X - sq.x) + abs(PLAYER_BIN_Y - sq.y);
503 
504                 // This should be in order of harshness
505                 if ((p4 & 0x2) && player_facing_square(sq) && (d < NOLOOK_MONSTER_DISTANCE)) {
506                     okay = FALSE;
507                 }
508                 if ((p4 & 0x1) && (d < MIN_MONSTER_DISTANCE)) {
509                     okay = FALSE;
510                 }
511                 if (!(p4 & 0x4) && (me_bits_music(pme) == ELEVATOR_ZONE)) {
512                     okay = FALSE;
513                 }
514             }
515             monster_count++;
516         }
517         if (okay) {
518             new_id = object_place(p1, sq);
519             if (new_id == OBJ_NULL) {
520                 quan = 0;
521             } else {
522                 num_gen++;
523                 // Set some default instance data
524                 osid = objs[new_id].specID;
525                 switch (objs[new_id].obclass) {
526                 case CLASS_CRITTER:
527                     objCritters[osid].mood = AI_MOOD_NEUTRAL;
528                     objCritters[osid].orders = AI_ORDERS_ROAM;
529                     break;
530                 }
531                 quan--;
532             }
533         } else {
534             failures++;
535             if (failures > quan)
536                 quan = 0;
537         }
538     }
539     if (num_gen > 0)
540         obj_load_art(FALSE);
541     return (OK);
542 }
543 
do_ai_trap(ObjSpecID osid,int p1,int p3,int p4)544 errtype do_ai_trap(ObjSpecID osid, int p1, int p3, int p4) {
545     ObjID oid;
546     oid = objCritters[osid].id;
547     if ((p1 < 0) || ((objs[oid].subclass == (p1 & 0xFF00) >> 8) && (objs[oid].info.type == (p1 & 0xFF)))) {
548         // Now look at the solo bit
549         if ((p1 == -1) ||     // we are already OK
550             (p1 & 0x20000) || // or we don't care
551             ((p1 & 0x10000) &&
552              (objs[oid].info.inst_flags & CLASS_INST_FLAG)) || // or if we want only loners, and we're a loner
553             ((!(p1 & 0x10000)) &&
554              (!(objs[oid].info.inst_flags & CLASS_INST_FLAG)))) // or we want only non-loners, and we're a non-loner
555         {
556             if ((p3 < 0x1000) && ((QUESTVAR_GET(COMBAT_DIFF_QVAR) > 0) || (qdata_get(p3) == AI_MOOD_FRIENDLY)))
557                 objCritters[osid].mood = qdata_get(p3);
558             if (p4 < 0x1000)
559                 objCritters[osid].orders = qdata_get(p4);
560         }
561     }
562     return (OK);
563 }
564 
trap_ai_func(int p1,int p2,int p3,int p4)565 errtype trap_ai_func(int p1, int p2, int p3, int p4) {
566     int x1, x2, y1, y2, i, j;
567     ObjSpecID osid;
568     ObjRefID oref;
569     ObjID oid, o1, o2;
570 
571     TRACE("%s: trigger", __FUNCTION__);
572 
573     if (p1 & 0x40000) {
574         if ((objs[p1 & 0xFFFF].active) && (objs[p1 & 0xFFFF].obclass == CLASS_CRITTER))
575             do_ai_trap(objs[p1 & 0xFFFF].specID, -1, p3, p4);
576         return (OK);
577     }
578     if ((p2 & 0xFFFF) == 0) {
579         // Radius or total AOE
580         if ((p2 >> 16) == 0) {
581             x1 = 0;
582             y1 = 0;
583             x2 = global_fullmap->x_size;
584             y2 = global_fullmap->y_size;
585         } else {
586             x1 = OBJ_LOC_BIN_X(objs[current_trap].loc) - (p2 >> 16);
587             y1 = OBJ_LOC_BIN_Y(objs[current_trap].loc) - (p2 >> 16);
588             x2 = x1 + (2 * (p2 >> 16));
589             y2 = y1 + (2 * (p2 >> 16));
590         }
591     } else {
592         // Object-demarked rectangle AOE
593         o1 = p2 & 0xFFFF;
594         o2 = p2 >> 16;
595         if ((o1 == OBJ_NULL) || (o2 == OBJ_NULL)) {
596             return (ERR_NOEFFECT);
597         } else {
598             x1 = lg_min(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc));
599             x2 = lg_max(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc));
600             y1 = lg_min(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc));
601             y2 = lg_max(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc));
602         }
603     }
604 
605     // If the area to search is greater than a certain threshold, use
606     // one search method, otherwise use another.
607     if ((x2 - x1) * (y2 - y1) > SEARCH_AREA_THRESHOLD) {
608         osid = objCritters[0].id;
609         while (osid != OBJ_SPEC_NULL) {
610             oid = objCritters[osid].id;
611             if ((OBJ_LOC_BIN_X(objs[oid].loc) >= x1) && (OBJ_LOC_BIN_X(objs[oid].loc) <= x2) &&
612                 (OBJ_LOC_BIN_Y(objs[oid].loc) >= y1) && (OBJ_LOC_BIN_Y(objs[oid].loc) <= y2)) {
613                 do_ai_trap(osid, p1, p3, p4);
614             }
615             osid = objCritters[osid].next;
616         }
617     } else {
618         for (j = y1; j <= y2; j++) {
619             for (i = x1; i <= x2; i++) {
620                 oref = me_objref(MAP_GET_XY(i, j));
621                 while (oref != OBJ_REF_NULL) {
622                     // Make sure we only do this to the "true" ref
623                     // on each obj
624                     oid = objRefs[oref].obj;
625                     if ((objs[oid].ref == oref) && (objs[oid].obclass == CLASS_CRITTER)) {
626                         if (p1 & 0x80000)
627                             do_ai_trap(objs[oid].specID, -2, p3, p4);
628                         else
629                             do_ai_trap(objs[oid].specID, p1, p3, p4);
630                     }
631                     oref = objRefs[oref].next;
632                 }
633             }
634         }
635     }
636     return (OK);
637 }
638 
639 #define TRAP_TIME_UNIT 10 // how many time-setting units in a second
640 
trap_scheduler_func(int p1,int p2,int p3,int p4)641 errtype trap_scheduler_func(int p1, int p2, int p3, int p4) {
642     TrapSchedEvent new_event;
643     uint *p;
644 
645     TRACE("%s: trigger", __FUNCTION__);
646 
647     if ((p3 >= 0xFFFF) || ((p3 > 0x1000) && (QUESTBIT_GET(p3 & 0xFFF))) || ((p3 < 0x1000) && (p3 > 0))) {
648         switch (objs[current_trap].obclass) {
649         case CLASS_TRAP:
650             p = &(objTraps[objs[current_trap].specID].p3);
651         case CLASS_FIXTURE:
652             p = &(objFixtures[objs[current_trap].specID].p3);
653         }
654         if ((p3 < 0x1000) && (p3 > 0))
655             (*p)--;
656         new_event.timestamp =
657             TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * (ushort)qdata_get(p2)) / TRAP_TIME_UNIT) + 1;
658         if (qdata_get(p4) != 0)
659             new_event.timestamp += rand() % qdata_get(p4);
660         new_event.type = TRAP_SCHED_EVENT;
661         new_event.target_id = qdata_get(p1);
662         new_event.source_id = current_trap;
663         return (schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event));
664     }
665     return (OK);
666 }
667 
trap_alternating_splitter_func(int p1,int p2,int p3,int p4)668 errtype trap_alternating_splitter_func(int p1, int p2, int p3, int p4) {
669     uchar found_new = FALSE;
670     char loop_count = 0;
671     int n = p4;
672     ObjID tr = current_trap;
673 
674     while (!found_new) {
675         switch (n) {
676         case 0:
677             do_timed_multi_stuff(qdata_get(p1));
678             found_new = TRUE;
679             break;
680         case 1:
681             do_timed_multi_stuff(qdata_get(p2));
682             found_new = TRUE;
683             break;
684         case 2:
685             do_timed_multi_stuff(qdata_get(p3));
686             found_new = TRUE;
687             break;
688         }
689         n += 1;
690         set_trap_data(tr, 4, n);
691         if ((n > 2) || ((n == 2) && (p3 == 0))) {
692             set_trap_data(tr, 4, 0);
693         }
694         loop_count++;
695         if (loop_count > 10) {
696             found_new = TRUE;
697         }
698     }
699     return (OK);
700 }
701 
702 errtype trap_lighting_func(uchar floor, int p1, int p2, int p3, int p4);
703 
704 // NUM_LIGHT_STEPS * LIGHT_TICKS should equal the total time of a transition, in this case .5 seconds
705 #define NUM_LIGHT_STEPS 8
706 #define LIGHT_TICKS (CIT_CYCLE >> 4)
707 
trap_main_light_func(int p1,int p2,int p3,int p4)708 errtype trap_main_light_func(int p1, int p2, int p3, int p4) {
709     uint *p;
710     if ((p3 & 0x10000) || (p3 & 0x20000))
711         trap_lighting_func(FALSE, p1, p2, p3 & 0xffff, p4);
712     if (!(p3 & 0x10000))
713         trap_lighting_func(TRUE, p1, p2, p3 & 0xffff, p4);
714 
715     TRACE("%s: trigger", __FUNCTION__);
716 
717     // Do transition rescheduling & incrementing
718     if (p2 & 0xFFFF) {
719         TrapSchedEvent new_event;
720         int num_steps = (p2 & 0xFFF0000) >> 16;
721         switch (objs[current_trap].obclass) {
722         case CLASS_TRAP:
723             p = &(objTraps[objs[current_trap].specID].p2);
724             break;
725         case CLASS_FIXTURE:
726             p = &(objFixtures[objs[current_trap].specID].p2);
727             break;
728         }
729         *p &= 0xF000FFFF;
730         if (num_steps < NUM_LIGHT_STEPS) {
731             // Now increment & re-schedule
732             num_steps++;
733             *p |= (num_steps << 16);
734             new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + LIGHT_TICKS) + 1;
735             new_event.type = TRAP_SCHED_EVENT;
736             new_event.target_id = OBJ_NULL;
737             new_event.source_id = current_trap;
738             schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event);
739         }
740     }
741     return (OK);
742 }
743 
744 #define MAX_LIGHT_VAL 15
745 
746 #define LIGHT_PLAIN_ALT 0
747 #define LIGHT_EW_SMOOTH 1
748 #define LIGHT_NS_SMOOTH 2
749 #define LIGHT_RADIAL 3
750 
trap_lighting_func(uchar floor,int p1,int p2,int p3,int p4)751 errtype trap_lighting_func(uchar floor, int p1, int p2, int p3, int p4) {
752     int i, j;
753     int targ1, targ2, setme, delta;
754     int otarg1, otarg2;
755     int x1, x2, y1, y2;
756     fix rad_delt, rdx, rdy;
757     short num_steps = -1;
758     char trans_type;
759     ObjID o1, o2;
760     MapElem *pme;
761     char v[4];
762     char light_state;
763     uint *p;
764 
765     TRACE("%s: trigger", __FUNCTION__);
766 
767     o1 = qdata_get(p1 & 0xFFFF);
768     o2 = qdata_get(p1 >> 16);
769 
770     // p2 will eventually be used for lighting transition types (interp or area staggered)
771     // although, the high bits of it are used for maintaining state.
772 
773     trans_type = p2 & 0xFFFF;
774     if (trans_type != 0) {
775         num_steps = (p2 & 0xFFF0000) >> 16;
776     }
777 
778     if (p3 == LIGHT_RADIAL) {
779         // compute the bounding box of the radius
780         x1 = OBJ_LOC_BIN_X(objs[current_trap].loc) - o1;
781         x2 = x1 + (2 * o1);
782         y1 = OBJ_LOC_BIN_Y(objs[current_trap].loc) - o1;
783         y2 = y1 + (2 * o1);
784     } else {
785         if ((o1 == OBJ_NULL) || (o2 == OBJ_NULL) || (!objs[o1].active) || (!objs[o2].active)) {
786             return (ERR_NOEFFECT);
787         }
788 
789         x1 = lg_min(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc));
790         x2 = lg_max(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc));
791         y1 = lg_min(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc));
792         y2 = lg_max(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc));
793     }
794 
795     v[0] = p4 & 0xFF;
796     v[1] = (p4 & 0xFF00) >> 8;
797     v[2] = (p4 & 0xFF0000) >> 16;
798     v[3] = p4 >> 24;
799     // radial light doesn't actually necessarily set any of its neighboring
800     // points to its lighting values ... lighting from trap may fall from
801     // an illegal value into the legal range due to distance from trap to
802     // floor vertex.
803     if (p3 != LIGHT_RADIAL) {
804         for (i = 0; i < 4; i++) {
805             if (v[i] > MAX_LIGHT_VAL) {
806                 return (ERR_RANGE);
807             }
808         }
809     }
810 
811     // Compare against it and figure out which set of lighting
812     // values to use.  Plain lighting cares about 0 vs 1, everyone
813     // else is 0 & 1 vs 2 & 3.
814     light_state = p2 >> 28;
815     if (p3 == LIGHT_PLAIN_ALT) {
816         if (light_state) {
817             targ1 = v[0];
818             otarg1 = v[1];
819         } else {
820             targ1 = v[1];
821             otarg1 = v[0];
822         }
823     } else {
824         if (light_state) {
825             targ1 = v[0];
826             otarg1 = v[2];
827             targ2 = v[1];
828             otarg2 = v[3];
829         } else {
830             targ1 = v[2];
831             otarg1 = v[0];
832             targ2 = v[3];
833             otarg2 = v[1];
834         }
835     }
836 
837     // Toggle state if done transiting (or not transiting)
838     switch (objs[current_trap].obclass) {
839     case CLASS_TRAP:
840         p = &(objTraps[objs[current_trap].specID].p2);
841         break;
842     case CLASS_FIXTURE:
843         p = &(objFixtures[objs[current_trap].specID].p2);
844         break;
845     }
846     if ((num_steps == -1) || (num_steps == NUM_LIGHT_STEPS)) {
847         if (!light_state)
848             *p |= 0x10000000; // turn on state
849         else
850             *p &= 0xFFFFFFF; // turn off highest bits
851     } else                   // otherwise, tone down the destination appropriately.
852     {
853         targ1 -= ((targ1 - otarg1) * (NUM_LIGHT_STEPS - num_steps) / NUM_LIGHT_STEPS);
854         if (p3 != LIGHT_PLAIN_ALT)
855             targ2 -= ((targ2 - otarg2) * (NUM_LIGHT_STEPS - num_steps) / NUM_LIGHT_STEPS);
856     }
857 
858     // Now go and crank through it...
859     switch (p3) {
860     case LIGHT_PLAIN_ALT:
861         delta = 0;
862         break;
863     case LIGHT_EW_SMOOTH:
864         delta = (targ2 - targ1) / (x2 - x1);
865         break;
866     case LIGHT_NS_SMOOTH:
867         delta = (targ2 - targ1) / (y2 - y1);
868         break;
869     }
870 #ifdef OLD_LIGHT
871     setme = targ1 - otarg1;
872 #endif
873     setme = targ1;
874     for (j = y1; j <= y2; j++) {
875         for (i = x1; i <= x2; i++) {
876             pme = MAP_GET_XY(i, j);
877             // This code now does lighting deltas
878             // Note, however, that it is still confused by light values getting "pegged"
879 
880 #define FIX_HALF (FIX_UNIT >> 1)
881             if (p3 == LIGHT_RADIAL) {
882                 rdx = fix_make(i, 0) - (objs[current_trap].loc.x << 8);
883                 rdy = fix_make(j, 0) - (objs[current_trap].loc.y << 8);
884                 rad_delt = fix_fast_pyth_dist(rdx, rdy);
885                 if (rad_delt <= fix_make(o1, 0))
886                     setme = targ1 + fix_int((rad_delt / o1) * (targ2 - targ1));
887                 else
888                     setme = me_templight_flr(pme);
889                 if (setme > MAX_LIGHT_VAL)
890                     setme = MAX_LIGHT_VAL;
891             }
892 
893             if (floor) {
894 #ifdef OLD_LIGHT
895                 //               if ((setme + me_templight_flr(pme) > 0xF) || (me_templight_flr(pme) - setme < 0))
896                 //                  Spew(DSRC_GAMESYS_Traps, ("pegged lights at 0x%x, 0x%x -- %d + %d = %d\n",
897                 //                     i,j,setme,me_templight_flr(pme),setme+me_templight_flr(pme)));
898                 new_val = me_templight_flr(pme) + setme;
899                 if (newval > 0xF)
900                     newval = 0xF;
901                 else if (newval < 0)
902                     newval = 0;
903                 me_templight_flr_set(pme, newval);
904 #endif
905                 me_templight_flr_set(pme, setme);
906             } else {
907 #ifdef OLD_LIGHT
908                 //               if ((setme + me_templight_ceil(pme) > 0xF) || (me_templight_ceil(pme) - setme < 0))
909                 //                  Spew(DSRC_GAMESYS_Traps, ("pegged lights at 0x%x, 0x%x -- %d + %d = %d\n",
910                 //                     i,j,setme,me_templight_ceil(pme),setme+me_templight_ceil(pme)));
911                 new_val = me_templight_ceil(pme) + setme;
912                 if (newval > 0xF)
913                     newval = 0xF;
914                 else if (newval < 0)
915                     newval = 0;
916                 me_templight_ceil_set(pme, newval);
917 #endif
918                 me_templight_ceil_set(pme, setme);
919             }
920             if (p3 == LIGHT_EW_SMOOTH) {
921                 if (i == x2 - 1)
922                     setme = targ2;
923                 else
924                     setme += delta;
925             }
926         }
927 
928         // Now set values for next time around
929         switch (p3) {
930         case LIGHT_EW_SMOOTH:
931             setme = targ1;
932             break;
933         case LIGHT_NS_SMOOTH:
934             if (j == y2 - 1)
935                 setme = targ2;
936             else
937                 setme += delta;
938             break;
939         }
940     }
941 
942     return (OK);
943 }
944 
trap_damage_func(int p1,int p2,int p3,int p4)945 errtype trap_damage_func(int p1, int p2, int p3, int p4) {
946     short dval;
947 
948     // Can't be inverted!
949     dval = qdata_get(p1);
950     if (dval > 0)
951         damage_object(PLAYER_OBJ, EXPLOSION_TYPE, dval, 0);
952 
953     dval = qdata_get(p2 & 0xFFFF);
954     if (!(p2 & 0x10000))
955         damage_object(PLAYER_OBJ, dval, p2 >> 24, 0x01);
956     else
957         player_struct.hit_points = lg_min((short)player_struct.hit_points + dval, PLAYER_MAX_HP);
958 
959     dval = qdata_get(p3 & 0xFFFF);
960     if (p3 < 0x10000)
961         player_struct.energy -= dval;
962     else
963         player_struct.energy += dval;
964 
965     dval = qdata_get(p4 & 0xFFFF);
966     if (p4 < 0x10000)
967         player_struct.fatigue += dval;
968     else
969         player_struct.fatigue -= dval;
970 
971     chg_set_flg(VITALS_UPDATE);
972     return (OK);
973 }
974 
975 uchar fake_endgame = FALSE;
976 #define ENDGAME_TICKS CIT_CYCLE * 2
977 
trap_sfx_func(int p1,int p2,int p3,int p4)978 errtype trap_sfx_func(int p1, int p2, int p3, int p4) {
979     extern short fr_solidfr_time;
980     extern short fr_sfx_time;
981     extern short fr_surge_time;
982     extern char surg_fx_frame;
983     short scr_fx, sfx_time, sound_fx;
984     short wacky, wacky_sev;
985     extern short surge_duration;
986     extern ulong player_death_time;
987 
988     sound_fx = qdata_get(p1 & 0xFFFF);
989     scr_fx = qdata_get(p3);
990     sfx_time = qdata_get(p4);
991     wacky = qdata_get(p2 & 0xFFFF);
992     wacky_sev = qdata_get(p2 >> 16);
993 
994     if (sound_fx)
995         play_digi_fx(sound_fx, qdata_get(p1 >> 16));
996 
997     switch (wacky) {
998     // Power Surge effect
999     case 1:
1000         if (fr_surge_time == 0) {
1001             fr_surge_time = surge_duration;
1002             surg_fx_frame = 0;
1003             play_digi_fx(SFX_SURGE, 1);
1004         }
1005         break;
1006 
1007     // Shake that booty (or head)
1008     case 2:
1009         //         if (!sound_fx)
1010         play_digi_fx(SFX_RUMBLE, 2);
1011         fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK);
1012         fr_sfx_time = CIT_CYCLE * 4; // 4 seconds of shake
1013         break;
1014 
1015     // Fake endgame
1016     case 3: {
1017         extern void physics_zero_all_controls();
1018         extern ulong secret_sfx_time;
1019         physics_zero_all_controls();
1020         secret_render_fx = FAKEWIN_REND_SFX;
1021         secret_sfx_time = *tmd_ticks;
1022         fr_surge_time = surge_duration;
1023         chg_set_sta(GL_CHG_2);
1024     } break;
1025 
1026     // Teleport special effect
1027     case 4:
1028         fr_global_mod_flag(FR_SFX_TELEPORT, FR_SFX_MASK);
1029         fr_sfx_time = CIT_CYCLE; // 1 second of teleport effect
1030         break;
1031 
1032     case 5: {
1033         // let's do some damage static!
1034         extern void set_dmg_percentage(int which, ubyte percent);
1035         set_dmg_percentage(DMG_BLOOD, 100); // 100 is the amount of static (100/255) is the percent of static
1036     } break;
1037     }
1038     switch (scr_fx) {
1039     case 1:
1040         fr_global_mod_flag(FR_SOLIDFR_SLDCLR, FR_SOLIDFR_MASK);
1041         fr_solidfr_color = GRENADE_COLOR;
1042         break;
1043     case 2:
1044         fr_global_mod_flag(FR_SOLIDFR_SLDCLR, FR_SOLIDFR_MASK);
1045         fr_solidfr_color = RED_BASE;
1046         break;
1047     case 3:
1048         fr_global_mod_flag(FR_SOLIDFR_STATIC, FR_SOLIDFR_MASK);
1049         break;
1050     case 4: {
1051         extern short vhold_shift;
1052         fr_global_mod_flag(FR_SFX_VHOLD, FR_SFX_MASK);
1053         vhold_shift = 0;
1054         break;
1055     }
1056     }
1057     if (scr_fx) {
1058         if (scr_fx >= 4)
1059             fr_sfx_time = sfx_time;
1060         else
1061             fr_solidfr_time = sfx_time;
1062     }
1063     return (OK);
1064 }
1065 
trap_create_obj_func(int p1,int p2,int p3,int p4)1066 errtype trap_create_obj_func(int p1, int p2, int p3, int p4) {
1067     ObjID new_id, oid;
1068     ObjLoc new_loc;
1069 
1070     TRACE("%s: trigger", __FUNCTION__);
1071 
1072     if ((p1 & 0xFFFF) == OBJ_NULL) {
1073         return (ERR_NOEFFECT);
1074     }
1075 
1076     // use questvar if asked for
1077     oid = qdata_get(p1 & 0xFFFF);
1078 
1079     if (!objs[oid].active) {
1080         return (ERR_NOEFFECT);
1081     }
1082 
1083     // We are okay to go, so clone the darned thing
1084     if ((p1 > 0xFFFF) && !(p1 & 0x10000000))
1085         new_id = oid;
1086     else {
1087         new_id = obj_create_clone(oid);
1088 
1089         // set the QV, if appropriate
1090         qdata_set(p1 >> 16, new_id);
1091     }
1092 
1093     // Now move it to where the trap says
1094     new_loc = objs[oid].loc;
1095     if (p2 < 0x4000) {
1096         p2 = qdata_get(p2);
1097         new_loc.x = (p2 << 8) | (new_loc.x & 0xFF);
1098     }
1099     if (p3 < 0x4000) {
1100         p3 = qdata_get(p3);
1101         new_loc.y = (p3 << 8) | (new_loc.y & 0xFF);
1102     }
1103     if (p4 < 0x4000) {
1104         new_loc.z = qdata_get(p4);
1105     }
1106     obj_move_to(new_id, &new_loc, TRUE);
1107     obj_physics_refresh_area(OBJ_LOC_BIN_X(new_loc), OBJ_LOC_BIN_Y(new_loc), TRUE);
1108     return (OK);
1109 }
1110 
trap_questbit_func(int p1,int p2,int p3,int p4)1111 errtype trap_questbit_func(int p1, int p2, int p3, int p4) {
1112     char message_buf[100];
1113     short qarg, mod;
1114     qarg = p2 & 0xFFFF;
1115     mod = p2 >> 16;
1116 
1117     TRACE("%s: trigger", __FUNCTION__);
1118 
1119     if ((p1 & 0xF000) == 0)
1120         p1 |= 0x2000;
1121 
1122     if (p1 == 0x2091) // KLC - special hack for auto shutoff of on-line help.
1123     {
1124         olh_active = FALSE; // KLC - this is kept in a global now.
1125 
1126         gShockPrefs.goOnScreenHelp = FALSE; // Yeah, got to update this one too and
1127         SavePrefs();                        // save the prefs out to disk.
1128         return (OK);
1129     }
1130 
1131     if (p1 & 0x2000) {
1132         switch (qarg) {
1133         case 0:
1134             qdata_set(p1, FALSE);
1135             break;
1136         case 1:
1137             qdata_set(p1, TRUE);
1138             break;
1139         default:
1140             if (qdata_get(p1))
1141                 qdata_set(p1, FALSE);
1142             else
1143                 qdata_set(p1, TRUE);
1144             break;
1145         }
1146         if (((p1 & 0xFFF) == REACTOR_BOOM_QB) && (qdata_get(p1)))
1147             do_special_reactor_hack();
1148     } else {
1149         switch (mod) {
1150         case 0:
1151             qdata_set(p1, qdata_get(qarg));
1152             break;
1153         case 1:
1154             qdata_set(p1, qdata_get(p1) + qdata_get(qarg));
1155             break;
1156         case 2:
1157             qdata_set(p1, qdata_get(p1) - qdata_get(qarg));
1158             break;
1159         case 3:
1160             qdata_set(p1, qdata_get(p1) * qdata_get(qarg));
1161             break;
1162         case 4:
1163             qdata_set(p1, qdata_get(p1) / qdata_get(qarg));
1164             break;
1165         case 5:
1166             qdata_set(p1, qdata_get(p1) % qdata_get(qarg));
1167             break;
1168         }
1169     }
1170     if (qdata_get(p1)) {
1171         if (qdata_get(p3)) {
1172             message_info(get_string(REF_STR_TrapZeroMessage + qdata_get(p3), message_buf, 80));
1173 #ifdef AUDIOLOGS
1174             audiolog_bark_play(qdata_get(p3));
1175 #endif
1176             *trap_use_message = TRUE;
1177         }
1178     } else {
1179         if (qdata_get(p4)) {
1180             message_info(get_string(REF_STR_TrapZeroMessage + qdata_get(p4), message_buf, 80));
1181 #ifdef AUDIOLOGS
1182             audiolog_bark_play(qdata_get(p4));
1183 #endif
1184             *trap_use_message = TRUE;
1185         }
1186     }
1187 
1188     return (OK);
1189 }
1190 
1191 extern uchar alternate_death;
1192 
1193 extern bool gPlayingGame;
1194 extern bool gDeadPlayerQuit;
1195 extern bool gGameCompletedQuit;
1196 
trap_cutscene_func(int p1,int p2,int p3,int p4)1197 errtype trap_cutscene_func(int p1, int p2, int p3, int p4) {
1198     short cs = qdata_get(p1);
1199 
1200     INFO("Playing cutscene %i %i\n", cs, qdata_get(p2));
1201 
1202     //if (qdata_get(p1) == 0)				// KLC - if we are to play the endgame cutscene
1203     //{
1204         //gGameCompletedQuit = TRUE;
1205 
1206         //gPlayingGame = FALSE; // Hop out of the game loop.
1207         // KLC   play_cutscene(qdata_get(p1), qdata_get(p2));
1208 
1209         play_cutscene(WIN_CUTSCENE, TRUE);
1210         setup_mode = SETUP_CREDITS;
1211         extern int WonGame_ShowStats;
1212         WonGame_ShowStats = 1;
1213     //}
1214 
1215     alternate_death = (qdata_get(p2) != 0);
1216     return (OK);
1217 }
1218 
trap_terrain_func(int p1,int p2,int p3,int p4)1219 errtype trap_terrain_func(int p1, int p2, int p3, int p4) {
1220     TRACE("%s: trigger", __FUNCTION__);
1221     MapElem *pme;
1222     uchar reprocess = FALSE;
1223     extern void rendedit_process_tilemap(FullMap * map, LGRect * r, uchar newMap);
1224     LGRect bounds;
1225 
1226     pme = MAP_GET_XY(qdata_get(p1), qdata_get(p2));
1227     if (pme == NULL)
1228         return (ERR_NOEFFECT);
1229     if (p3 < 0x4000) {
1230         me_tiletype_set(pme, qdata_get(p3));
1231         reprocess = TRUE;
1232     }
1233     if (p4 < 0x4000) {
1234         me_param_set(pme, qdata_get(p4));
1235         reprocess = TRUE;
1236     }
1237     if (reprocess) {
1238         bounds.ul.x = bounds.lr.x = qdata_get(p1);
1239         bounds.ul.y = bounds.lr.y = qdata_get(p2);
1240         rendedit_process_tilemap(global_fullmap, &bounds, FALSE);
1241     }
1242     obj_physics_refresh_area(qdata_get(p1), qdata_get(p2), TRUE);
1243     return (OK);
1244 }
1245 
trap_height_func(int p1,int p2,int p3,int p4)1246 errtype trap_height_func(int p1, int p2, int p3, int p4) {
1247     TRACE("%s: trigger", __FUNCTION__);
1248     MapElem *pme;
1249     HeightSchedEvent hse;
1250     ushort use_val;
1251     uchar x, y;
1252     char steps;
1253     char ht;
1254     uchar did_sfx = FALSE;
1255     extern uchar register_h_event(uchar x, uchar y, uchar floor, char *sem, char *key, uchar no_sfx);
1256 
1257     x = qdata_get(p1);
1258     y = qdata_get(p2);
1259     pme = MAP_GET_XY(x, y);
1260 
1261     hse.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * HEIGHT_STEP_TIME) / HEIGHT_TIME_UNIT) + 1;
1262     use_val = qdata_get(p3 & 0xFFFF);
1263     if (use_val < 0x100) {
1264         if (me_height_flr(pme) != use_val) {
1265             hse.type = FLOOR_SCHED_EVENT;
1266 
1267             steps = use_val - me_height_flr(pme);
1268             hse.steps_remaining = steps;
1269             hse.sfx_code = 0;
1270             if (p4 >> 16)
1271                 did_sfx = TRUE;
1272             if (register_h_event(x, y, TRUE, &hse.semaphor, &hse.key, p4 >> 16)) {
1273                 schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&hse);
1274             }
1275         }
1276     }
1277     use_val = qdata_get(p3 >> 16);
1278     if (use_val < 0x100) {
1279         // convert to make life easier on Erik
1280         use_val = 32 - use_val;
1281 
1282         if (me_height_ceil(pme) != use_val) {
1283             hse.type = CEIL_SCHED_EVENT;
1284             ht = me_height_ceil(pme);
1285             steps = use_val - ht;
1286             hse.steps_remaining = steps;
1287             hse.sfx_code = p4 >> 16;
1288             if (register_h_event(x, y, FALSE, &hse.semaphor, &hse.key, (did_sfx) ? FALSE : (p4 >> 16))) {
1289                 schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&hse);
1290             }
1291         }
1292     }
1293     obj_physics_refresh_area(x, y, TRUE);
1294     return (OK);
1295 }
1296 
real_instance_func(int p1,int p2,int p3,int p4)1297 errtype real_instance_func(int p1, int p2, int p3, int p4) {
1298     ObjSpecID osid;
1299     if (p1 == 0)
1300         return (OK);
1301     if (!objs[p1].active) {
1302         return (ERR_NOEFFECT);
1303     }
1304     osid = objs[p1].specID;
1305     switch (objs[p1].obclass) {
1306     case CLASS_BIGSTUFF:
1307         if (p2 != -1)
1308             objBigstuffs[osid].cosmetic_value = p2;
1309         if (p3 != -1)
1310             objBigstuffs[osid].data1 = p3;
1311         if (p4 != -1)
1312             objBigstuffs[osid].data2 = p4;
1313         break;
1314     case CLASS_DOOR:
1315         if (p2 != -1)
1316             objDoors[osid].locked = p2;
1317         if (p3 != -1) {
1318             objDoors[osid].stringnum = p3 >> 8;
1319             objDoors[osid].cosmetic_value = p3 & 0xFF;
1320         }
1321         if (p4 != -1) {
1322             objDoors[osid].access_level = p4 >> 8;
1323             objDoors[osid].autoclose_time = p4 & 0xFF;
1324         }
1325         break;
1326     case CLASS_FIXTURE:
1327     case CLASS_TRAP:
1328         set_trap_data(p1, p2, p3);
1329         break;
1330     }
1331     return (OK);
1332 }
1333 
trap_instance_func(int p1,int p2,int p3,int p4)1334 errtype trap_instance_func(int p1, int p2, int p3, int p4) {
1335     real_instance_func(qdata_get(p1 & 0xFFFF), p2, p3, p4);
1336     real_instance_func(qdata_get(p1 >> 16), p2, p3, p4);
1337     return (OK);
1338 }
1339 
animate_callback_func(ObjID id,intptr_t user_data)1340 void animate_callback_func(ObjID id, intptr_t user_data) {
1341     int p3;
1342 
1343     p3 = (int)user_data;
1344 
1345     if (BIT_SET(p3, 17))
1346         do_multi_stuff(p3 & 0x7FFF);
1347     else
1348         real_animate_func(id, 0, p3, 0);
1349 }
1350 
real_animate_func(ObjID id,int p2,int p3,int p4)1351 errtype real_animate_func(ObjID id, int p2, int p3, int p4) {
1352     errtype retval = OK;
1353     int frames;
1354     uchar reverse;
1355 
1356     if (id == 0)
1357         return (OK);
1358     if (!objs[id].active) {
1359         return (ERR_NOEFFECT);
1360     }
1361 
1362     // Don't allow animation updates of "destroyed" screens
1363     if (objs[id].info.current_hp == 0)
1364         return (OK);
1365 
1366     frames = objBigstuffs[objs[id].specID].cosmetic_value;
1367     if (frames == 0)
1368         frames = 4;
1369 
1370     if (p4 != 0)
1371         remove_obj_from_animlist(id);
1372     if (p2 == 0) {
1373         reverse = BIT_SET(p2, 15);
1374         if (p3 & 0xF0000000)
1375             frames = p3 >> 28;
1376         real_instance_func(id, frames, -1, p3 & 0x7FFF);
1377         remove_obj_from_animlist(id);
1378         objs[id].info.current_frame = reverse ? frames - 1 : 0;
1379         add_obj_to_animlist(id, TRUE, BIT_SET(p3, 15), BIT_SET(p3, 16), 0, 0, 0, 0);
1380     } else {
1381         reverse = BIT_SET(p2, 15);
1382         if (p2 & 0xF0000000)
1383             frames = p2 >> 28;
1384         real_instance_func(id, frames, -1, p2 & 0x7FFF);
1385         objs[id].info.current_frame = reverse ? frames - 1 : 0;
1386         if (p3 != 0)
1387             retval = add_obj_to_animlist(id, FALSE, BIT_SET(p2, 15), BIT_SET(p2, 16), 0, 6, p3, ANIMCB_REMOVE);
1388         else
1389             retval = add_obj_to_animlist(id, FALSE, BIT_SET(p2, 15), BIT_SET(p2, 16), 0, 0, 0, 0);
1390     }
1391     return (retval);
1392 }
1393 
trap_animate_func(int p1,int p2,int p3,int p4)1394 errtype trap_animate_func(int p1, int p2, int p3, int p4) {
1395     real_animate_func(qdata_get(p1 & 0xFFFF), p2, p3, p4);
1396     real_animate_func(qdata_get(p1 >> 16), p2, p3, p4);
1397     return (OK);
1398 }
1399 
1400 // Note that this blows away the usual shodan time countdown,
1401 // so can only be used in endgame!
1402 uchar *shodan_bitmask = NULL;
1403 grs_bitmap shodan_draw_fs;
1404 grs_bitmap shodan_draw_normal;
1405 
hack_shodan_conquer_func(char c)1406 void hack_shodan_conquer_func(char c) {
1407     extern void begin_shodan_conquer_fx(uchar begin);
1408     extern char thresh_fail;
1409     extern uchar shodan_phase_in(uchar * bitmask, short x, short y, short w, short h, short num, uchar dir);
1410     shodan_bitmask = tmap_static_mem;
1411     LG_memset(shodan_bitmask, 0, SHODAN_BITMASK_SIZE / 8);
1412     shodan_draw_fs.bits = tmap_static_mem + (SHODAN_BITMASK_SIZE / 8);
1413     shodan_draw_normal.bits = shodan_draw_fs.bits + (320 * 200);
1414     load_res_bitmap(&shodan_draw_fs, SHODAN_FULLSCRN_CONQUER_REF, FALSE);
1415     load_res_bitmap(&shodan_draw_normal, SHODAN_CONQUER_REF, FALSE);
1416     thresh_fail = 0;
1417     time_until_shodan_avatar = player_struct.game_time + SHODAN_INTERVAL;
1418 
1419     begin_shodan_conquer_fx(TRUE);
1420 }
1421 
hack_armageddon_func(int otrip,int x0,int y0,int r)1422 void hack_armageddon_func(int otrip, int x0, int y0, int r) {
1423     int ulx, uly, lrx, lry;
1424     int i, j;
1425     ObjRefID oref;
1426     ObjID oid;
1427 
1428     ulx = x0 - r;
1429     uly = y0 - r;
1430     lrx = x0 + r;
1431     lry = y0 + r;
1432 
1433     for (j = uly; j <= lry; j++) {
1434         for (i = ulx; i <= lrx; i++) {
1435             oref = me_objref(MAP_GET_XY(i, j));
1436             while (oref != OBJ_REF_NULL) {
1437                 // Make sure we only do this to the "true" ref
1438                 // on each obj
1439                 oid = objRefs[oref].obj;
1440                 if ((objs[oid].ref == oref) && (objs[oid].obclass == CLASS_CRITTER)) {
1441                     if (ID2TRIP(oid) == otrip)
1442                         ADD_DESTROYED_OBJECT(oid);
1443                 }
1444                 oref = objRefs[oref].next;
1445             }
1446         }
1447     }
1448 }
1449 
hack_area_spew(int p2,int p3,int p4)1450 void hack_area_spew(int p2, int p3, int p4) {
1451     int ulx, uly, lrx, lry;
1452     int x, y;
1453     MapElem *pme;
1454     uchar state;
1455     ObjID obj;
1456 
1457     ulx = lg_min(OBJ_LOC_BIN_X(objs[current_trap].loc), OBJ_LOC_BIN_X(objs[(ObjID)p2].loc));
1458     lrx = lg_max(OBJ_LOC_BIN_X(objs[current_trap].loc), OBJ_LOC_BIN_X(objs[(ObjID)p2].loc));
1459     uly = lg_min(OBJ_LOC_BIN_Y(objs[current_trap].loc), OBJ_LOC_BIN_Y(objs[(ObjID)p2].loc));
1460     lry = lg_max(OBJ_LOC_BIN_Y(objs[current_trap].loc), OBJ_LOC_BIN_Y(objs[(ObjID)p2].loc));
1461 
1462     obj = (ObjID)(p3 & 0xFFFF);
1463     if (obj == OBJ_NULL)
1464         state = 0;
1465     else
1466         state = (objs[obj].info.current_frame == 0);
1467     state = state ^ ((p3 >> 16) != 0);
1468 
1469     for (x = ulx; x <= lrx; x++) {
1470         for (y = uly; y <= lry; y++) {
1471             pme = MAP_GET_XY(x, y);
1472             switch (p4) {
1473             case 0:
1474                 me_hazard_rad_set(pme, state);
1475                 break;
1476             }
1477         }
1478     }
1479 }
1480 
hack_multi_trans(int trip,int newtype)1481 void hack_multi_trans(int trip, int newtype) {
1482     ObjSpecID osid;
1483     ObjID oid;
1484 
1485     osid = objCritters[0].id;
1486     while (osid != OBJ_SPEC_NULL) {
1487         oid = objCritters[osid].id;
1488         if (ID2TRIP(oid) == trip) {
1489             if (newtype > 0xF)
1490                 do_destroy(oid);
1491             else
1492                 trap_transmogrify_func(oid, newtype, 0, 0);
1493         }
1494         osid = objCritters[osid].next;
1495     }
1496 }
1497 
hack_change_comparator(int p2,int p3)1498 void hack_change_comparator(int p2, int p3) {
1499     ObjSpecID osid;
1500 
1501     osid = objs[p2].specID;
1502     switch (objs[p2].obclass) {
1503     case CLASS_TRAP:
1504         objTraps[osid].comparator = p3;
1505         return;
1506     case CLASS_FIXTURE:
1507         objFixtures[osid].comparator = p3;
1508         return;
1509     default:
1510         return;
1511     }
1512 }
1513 
1514 // Wow.  Pretty non-general here.
1515 // Diego taunts the player.
1516 #define FIXANG_OCT (FIXANG_PI / 4)
hack_taunt_diego(int p2,int p3)1517 void hack_taunt_diego(int p2, int p3) {
1518     fixang plrh;
1519 
1520     // triggers trap p3 if and only if player's facing is within
1521     // an octant either way of fixang p2.
1522     plrh = fixang_from_phys_angle(phys_angle_from_obj(objs[PLAYER_OBJ].loc.h));
1523     plrh -= p2;
1524     if ((plrh < FIXANG_OCT) || (plrh >= ((fixang)-FIXANG_OCT)))
1525         do_multi_stuff(p3);
1526 }
1527 
1528 #define REPULSOR_TOGGLE_HACK 0x1
1529 #define REPULSOR_UP 0
1530 #define REPULSOR_DOWN 1
1531 
1532 #define REACTOR_DIGIT_HACK 0x2
1533 #define REACTOR_COMBO_QVAR 0x1f
1534 #define SCREEN_DIGIT_0 (REFINDEX(REF_STR_ScreenZero + 0x34))
1535 
1536 #define REACTOR_KEYPAD_HACK 0x3
1537 
1538 #define FIXTURE_FRAME_HACK 0x4
1539 
1540 #define DOOR_HACK 0x5
1541 
1542 #define GAME_OVER_HACK 0x6
1543 
1544 #define TURN_OBJECT_HACK 0x7
1545 
1546 #define ARMAGEDDON_HACK 0x8
1547 
1548 #define SHODAN_CONQUER_HACK 0x9
1549 
1550 #define COMPARATOR_HACK 0xA
1551 
1552 #define PLOTWARE_HACK 0xB
1553 
1554 #define AREASPEW_HACK 0xC
1555 
1556 #define DIEGO_HACK 0xD
1557 
1558 #define PANEL_REF_HACK 0xE
1559 
1560 #define EARTH_DESTROYED_HACK 0xF
1561 
1562 #define MULTI_TRANSMOG_HACK 0x10
1563 
trap_hack_func(int p1,int p2,int p3,int p4)1564 errtype trap_hack_func(int p1, int p2, int p3, int p4) {
1565     uchar door_moving(ObjID door, uchar dir);
1566     void plotware_showpage(uchar page);
1567     void check_panel_ref(uchar puntme);
1568     void email_slam_hack(short which);
1569 
1570     TRACE("%s: trigger", __FUNCTION__);
1571 
1572     // As we need hacks in the game, just add new
1573     // cases here to do your particular hack.
1574     switch (p1) {
1575     case REPULSOR_TOGGLE_HACK:
1576         if (ID2TRIP(p2) == REPULSOR_TRIPLE) {
1577             Obj *repul = &objs[p2];
1578             ObjRefID oref;
1579             ObjID oid;
1580             uint *upness = &(objTraps[repul->specID].p4);
1581             int nu_tmap, old_tmap, nu_frame;
1582             MapElem *pme;
1583             ObjLoc where = repul->loc;
1584             extern errtype obj_physics_refresh(short x, short y, uchar use_floor);
1585 
1586             switch (p4) {
1587             case 0:
1588                 *upness = (*upness == REPULSOR_UP) ? REPULSOR_DOWN : REPULSOR_UP;
1589                 break;
1590             case 1:
1591                 *upness = REPULSOR_UP;
1592                 break;
1593             case 2:
1594                 *upness = REPULSOR_DOWN;
1595                 break;
1596             }
1597 
1598             if (*upness == REPULSOR_UP) {
1599                 nu_tmap = (p3 >> 8) & 0x1F;
1600                 old_tmap = p3 & 0x1F;
1601                 nu_frame = 0;
1602             } else {
1603                 old_tmap = (p3 >> 8) & 0x1F;
1604                 nu_tmap = p3 & 0x1F;
1605                 nu_frame = 1;
1606             }
1607 
1608             pme = MAP_GET_XY(OBJ_LOC_BIN_X(where), OBJ_LOC_BIN_Y(where));
1609             if (nu_tmap != old_tmap) {
1610                 if (me_tmap_flr(pme) == old_tmap)
1611                     me_tmap_flr_set(pme, nu_tmap);
1612                 if (me_tmap_ceil(pme) == old_tmap)
1613                     me_tmap_ceil_set(pme, nu_tmap);
1614             }
1615             oref = me_objref(pme);
1616             while (oref != OBJ_REF_NULL) {
1617                 oid = objRefs[oref].obj;
1618                 if ((objs[oid].ref == oref) && ID2TRIP(oid) == REPULSWALL_TRIPLE)
1619                     objs[oid].info.current_frame = nu_frame;
1620                 oref = objRefs[oref].next;
1621             }
1622             obj_physics_refresh(OBJ_LOC_BIN_X(where), OBJ_LOC_BIN_Y(where), FALSE);
1623         }
1624         break;
1625     case REACTOR_DIGIT_HACK:
1626         if (objs[p2].obclass == CLASS_BIGSTUFF) {
1627             uint combo;
1628 
1629             combo = (QUESTVAR_GET(REACTOR_COMBO_QVAR) << 12) | (QUESTVAR_GET(REACTOR_COMBO_QVAR + 1));
1630 
1631             objBigstuffs[ID2SPEC(p2)].data2 = 0x100 | (SCREEN_DIGIT_0 + (0xF & (combo >> (4 * (6 - p3)))));
1632         }
1633         break;
1634     case REACTOR_KEYPAD_HACK:
1635         if (objs[p2].obclass == CLASS_FIXTURE) {
1636             uint *field = &(objFixtures[ID2SPEC(p2)].p1);
1637 
1638             field += (p3 - 1);
1639             *field = (*field & ~0xFFFF) | QUESTVAR_GET(p4);
1640         }
1641         break;
1642     case FIXTURE_FRAME_HACK:
1643         objs[p2].info.current_frame = p3;
1644         if (p4)
1645             mfd_notify_func(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE);
1646         break;
1647     case DOOR_HACK:
1648         if (objs[p2].obclass == CLASS_DOOR) {
1649 
1650             uchar closed;
1651 
1652             // p3 indicates operation on door:
1653             // 0=null; 1=open; 2=close; 3=toggle; 4=disable autoclose
1654 
1655             if (p3 < 4) {
1656                 closed = door_moving(p2, TRUE) || DOOR_REALLY_CLOSED(p2);
1657 
1658                 if (p3 && ((p3 == 3) || ((p3 == 1) == closed)))
1659                     do_multi_stuff(p2);
1660             } else {
1661                 objDoors[objs[p2].specID].autoclose_time = NEVER_AUTOCLOSE_COOKIE;
1662                 remove_obj_from_animlist(p2);
1663             }
1664         }
1665         break;
1666     case GAME_OVER_HACK: {
1667         extern void player_dead(void);
1668         extern int curr_alog;
1669         extern char secret_pending_hack;
1670         if (curr_alog != -1)
1671             secret_pending_hack = 1;
1672         else {
1673             gDeadPlayerQuit = TRUE; // The player is dead.
1674             //gPlayingGame = FALSE;   // Hop out of the game loop.
1675 
1676             INFO("GAME OVER!\n");
1677             play_cutscene(DEATH_CUTSCENE, FALSE);
1678         }
1679         break;
1680     }
1681     case TURN_OBJECT_HACK: {
1682         short head, lo, hi, phb;
1683 
1684         phb = (p3 >> 24) & 0xF;
1685         switch (phb) {
1686         case 1:
1687             head = objs[p2].loc.p;
1688             break;
1689         case 2:
1690             head = objs[p2].loc.b;
1691             break;
1692         default:
1693             head = objs[p2].loc.h;
1694             break;
1695         }
1696         hi = p4 & 0xFF;
1697         if (hi == 0)
1698             hi = 255;
1699         lo = (p4 >> 8) & 0xFF;
1700         if (!(p3 & 0xFF))
1701             p3 |= 0x10;
1702         head += (p3 & 0xFF0000) ? -(p3 & 0xFF) : (p3 & 0xFF);
1703         if ((p3 & 0xFF00) == 0) {
1704             if (hi != lo)
1705             head = lo + (head - lo) % (hi - lo);
1706         } else {
1707             if (head > hi) {
1708                 head = hi;
1709                 p3 = p3 ^ 0x10000;
1710             } else if (head < lo) {
1711                 head = lo;
1712                 p3 = p3 ^ 0x10000;
1713             }
1714         }
1715         switch (phb) {
1716         case 1:
1717             objs[p2].loc.p = head;
1718             break;
1719         case 2:
1720             objs[p2].loc.b = head;
1721             break;
1722         default:
1723             objs[p2].loc.h = head;
1724             break;
1725         }
1726         objTraps[objs[current_trap].specID].p3 = p3;
1727         break;
1728     }
1729     case ARMAGEDDON_HACK:
1730         hack_armageddon_func(p2, OBJ_LOC_BIN_X(objs[current_trap].loc), OBJ_LOC_BIN_Y(objs[current_trap].loc), p3);
1731         break;
1732     case SHODAN_CONQUER_HACK:
1733         hack_shodan_conquer_func(p2);
1734         break;
1735     case COMPARATOR_HACK:
1736         hack_change_comparator(p2, p3);
1737         break;
1738     case PLOTWARE_HACK:
1739         // show page p2
1740         plotware_showpage(p2);
1741         break;
1742     case AREASPEW_HACK:
1743         hack_area_spew(p2, p3, p4);
1744         break;
1745     case DIEGO_HACK:
1746         hack_taunt_diego(p2, p3);
1747         break;
1748     case PANEL_REF_HACK:
1749         if (player_struct.panel_ref == p2)
1750             check_panel_ref(TRUE);
1751         break;
1752     case EARTH_DESTROYED_HACK:
1753         email_slam_hack(0x01D);
1754         break;
1755     case MULTI_TRANSMOG_HACK:
1756         hack_multi_trans(p2, p3);
1757         break;
1758     }
1759     return (OK);
1760 }
1761 
do_multi_stuff(ObjID id)1762 errtype do_multi_stuff(ObjID id) {
1763     ObjID other;
1764 
1765     if (id != OBJ_NULL) {
1766         if (objs[id].obclass == CLASS_TRAP)
1767             trap_activate(id, trap_use_message);
1768         else {
1769             switch (objs[id].obclass) {
1770             case CLASS_DOOR:
1771                 objDoors[objs[id].specID].locked = 0;
1772                 objDoors[objs[id].specID].access_level = 0;
1773                 other = objDoors[objs[id].specID].other_half;
1774                 if ((other) && objs[other].obclass == CLASS_DOOR) {
1775                     // door has a door other half.  Unlock it too.
1776                     objDoors[objs[other].specID].locked = 0;
1777                     objDoors[objs[other].specID].access_level = 0;
1778                 }
1779                 break;
1780             }
1781             object_use(id, FALSE, OBJ_NULL);
1782         }
1783     }
1784     return (OK);
1785 }
1786 
1787 // number of multi delay units in a second
1788 #define MULTI_TIME_UNIT 10
1789 
do_timed_multi_stuff(int p)1790 errtype do_timed_multi_stuff(int p) {
1791     TrapSchedEvent new_event;
1792 
1793     if (p >> 16) {
1794         // Time delay
1795         new_event.timestamp =
1796             TICKS2TSTAMP(player_struct.game_time + (ushort)(CIT_CYCLE * (p >> 16)) / MULTI_TIME_UNIT) + 1;
1797         new_event.type = TRAP_SCHED_EVENT;
1798         new_event.target_id = qdata_get(p & 0xFFFF);
1799         new_event.source_id = -1;
1800         schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event);
1801     } else {
1802         // Do immediately
1803         do_multi_stuff(qdata_get(p & 0xFFFF));
1804     }
1805     return (OK);
1806 }
1807 
trap_multi_func(int p1,int p2,int p3,int p4)1808 errtype trap_multi_func(int p1, int p2, int p3, int p4) {
1809     do_timed_multi_stuff(p1);
1810     do_timed_multi_stuff(p2);
1811     do_timed_multi_stuff(p3);
1812     do_timed_multi_stuff(p4);
1813     return (OK);
1814 }
1815 
do_destroy(int victim_data)1816 errtype do_destroy(int victim_data) {
1817     ObjID victim;
1818 
1819     victim = qdata_get(victim_data);
1820 
1821     if (victim != OBJ_NULL)
1822         ADD_DESTROYED_OBJECT(victim);
1823     return (OK);
1824 }
1825 
trap_destroy_object_func(int p1,int p2,int p3,int p4)1826 errtype trap_destroy_object_func(int p1, int p2, int p3, int p4) {
1827     char message_buf[100];
1828 
1829     do_destroy(p1);
1830     do_destroy(p2);
1831     do_destroy(p3);
1832     if (p4 > 0) {
1833         message_info(get_string(REF_STR_TrapZeroMessage + p4, message_buf, 80));
1834 #ifdef AUDIOLOGS
1835         audiolog_bark_play(p4);
1836 #endif
1837         *trap_use_message = TRUE;
1838     }
1839     return (OK);
1840 }
1841 
trap_plot_clock_func(int p1,int p2,int p3,int p4)1842 errtype trap_plot_clock_func(int p1, int p2, int p3, int p4) { return (OK); }
1843 
trap_email_func(int mung,int time,int p3,int p4)1844 errtype trap_email_func(int mung, int time, int p3, int p4) {
1845     void add_email_datamunge(short mung, uchar select);
1846 
1847 #ifdef DOOM_EMULATION_MODE
1848     if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 0)
1849         return (OK);
1850 #endif
1851     if (time == 0) {
1852         add_email_datamunge(mung, TRUE);
1853         *trap_use_message = TRUE;
1854     } else {
1855         EmailSchedEvent ev;
1856         ev.type = EMAIL_SCHED_EVENT;
1857         ev.timestamp = TICKS2TSTAMP((time << APPROX_CIT_CYCLE_SHFT) + player_struct.game_time);
1858         ev.datamunge = mung;
1859         schedule_event(&game_seconds_schedule, (SchedEvent *)&ev);
1860     }
1861     return (OK);
1862 }
1863 
trap_texture_func(int p1,int p2,int p3,int p4)1864 errtype trap_texture_func(int p1, int p2, int p3, int p4) {
1865     ObjID id1, id2;
1866     short cx, cy, minx, maxx, miny, maxy;
1867     int i, src[3], dest[3];
1868     MapElem *pme;
1869 
1870     id2 = qdata_get(p1 >> 16);
1871     if (id2 != OBJ_NULL) {
1872         id1 = qdata_get(p1 & 0xFFFF);
1873         if (!objs[id1].active || !objs[id2].active) {
1874             return (OK);
1875         }
1876         minx = lg_min(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc));
1877         maxx = lg_max(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc));
1878         miny = lg_min(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc));
1879         maxy = lg_max(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc));
1880     } else {
1881         minx = maxx = (p1 & 0xFF00) >> 8;
1882         miny = maxy = (p1 & 0xFF);
1883     }
1884 
1885     src[0] = p2 >> 16;
1886     src[1] = p3 >> 16;
1887     src[2] = p4 >> 16;
1888     dest[0] = p2 & 0xFFFF;
1889     dest[1] = p3 & 0xFFFF;
1890     dest[2] = p4 & 0xFFFF;
1891 
1892     for (cx = minx; cx <= maxx; cx++) {
1893         for (cy = miny; cy <= maxy; cy++) {
1894             pme = MAP_GET_XY(cx, cy);
1895             for (i = 0; i < 3; i++) {
1896                 if ((src[i] >= 0x1000) || (src[i] == me_tmap(pme, i))) {
1897                     if (dest[i] < 0x1000) {
1898                         me_tmap_set(pme, i, dest[i]);
1899                     }
1900                 }
1901             }
1902         }
1903     }
1904     return (OK);
1905 }
1906 
trap_teleport_func(int targ_x,int targ_y,int targ_z,int targlevel)1907 errtype trap_teleport_func(int targ_x, int targ_y, int targ_z, int targlevel) {
1908     ObjLoc newloc;
1909     errtype errcode = OK;
1910     uchar to_cyber = FALSE;
1911 
1912     if (targlevel >= 0x1000)
1913         targlevel = player_struct.level;
1914 
1915     // If going between cyber and real, static out the screen during the load
1916     if (targlevel != player_struct.level) {
1917         to_cyber = go_to_different_level(targlevel);
1918     }
1919     if (errcode == OK) {
1920         newloc = objs[PLAYER_OBJ].loc;
1921         if (targ_x < 0x4000) {
1922             targ_x = qdata_get(targ_x);
1923             newloc.x = (targ_x << 8) + 0x80;
1924         }
1925         if (targ_y < 0x4000) {
1926             targ_y = qdata_get(targ_y);
1927             newloc.y = (targ_y << 8) + 0x80;
1928         }
1929         if (targ_z < 0x4000) {
1930             targ_z = qdata_get(targ_z);
1931             newloc.z = targ_z;
1932         }
1933         obj_move_to(PLAYER_OBJ, &newloc, TRUE);
1934         if (to_cyber) {
1935             recall_objloc = newloc;
1936             // KLC         if (music_on)
1937             // KLC            start_music();
1938         }
1939     } else {
1940         return (ERR_FOPEN);
1941     }
1942     return (OK);
1943 }
1944 
trap_expose_func(int dmg,int dtype,int tsecs,int p4)1945 errtype trap_expose_func(int dmg, int dtype, int tsecs, int p4) {
1946     extern void expose_player(byte damage, ubyte type, ushort tsecs);
1947     short damage = qdata_get(dmg & 0xFFFF);
1948 
1949     if (dmg & 0x10000) {
1950         damage &= 0xFFFF;
1951         if (dtype == RADIATION_TYPE) {
1952             if (damage <= player_struct.rad_post_expose) {
1953                 player_struct.rad_post_expose -= damage;
1954                 return OK;
1955             }
1956             damage -= player_struct.rad_post_expose;
1957             player_struct.rad_post_expose = 0;
1958         }
1959         if (dtype == BIO_TYPE) {
1960             if (damage <= player_struct.bio_post_expose) {
1961                 player_struct.bio_post_expose -= damage;
1962                 return OK;
1963             }
1964             damage -= player_struct.bio_post_expose;
1965             player_struct.bio_post_expose = 0;
1966         }
1967         damage = lg_max(-damage, -128);
1968     }
1969     expose_player((byte)damage, (ubyte)qdata_get(dtype), (ushort)qdata_get(tsecs));
1970     return OK;
1971 }
1972 
trap_bark_func(int speaker,int strnum,int color,int hud_bark)1973 errtype trap_bark_func(int speaker, int strnum, int color, int hud_bark) {
1974     int string_id = REF_STR_TrapZeroMessage + qdata_get(strnum);
1975     SchedEvent new_event;
1976     ushort special;
1977     uint timeout = 0, len;
1978 
1979     special = (speaker < 0) ? -speaker : 0;
1980 
1981     if (hud_bark) {
1982         // just message_info for now
1983         string_message_info(string_id);
1984 #ifdef AUDIOLOGS
1985         audiolog_bark_play(string_id - REF_STR_TrapZeroMessage);
1986 #endif
1987     } else if (special) {
1988         int mug;
1989 
1990         mug = (-special == SHODAN_BARK_CODE) ? SHODAN_MUG : DIEGO_MUG;
1991         long_bark(PLAYER_OBJ, mug, string_id, (ubyte)color);
1992         timeout = SHODAN_BARK_TIMEOUT;
1993     } else if ((ObjID)speaker == OBJ_NULL) {
1994         long_bark(PLAYER_OBJ, 0, string_id, (ubyte)color);
1995         timeout = NULL_BARK_TIMEOUT;
1996     } else {
1997         //      long_bark(speaker,0,string_id,(ubyte)color);
1998         long_bark(PLAYER_OBJ, 0, string_id, (ubyte)color);
1999         timeout = NULL_BARK_TIMEOUT;
2000     }
2001     if (timeout > 0) {
2002         // convert to sixteenths of a second
2003         timeout = (timeout * 16) / 10;
2004 
2005         len = strlen(get_temp_string(string_id));
2006         timeout *= 10 + len;
2007         new_event.timestamp = TICKS2TSTAMP(player_struct.game_time) + timeout;
2008         new_event.type = BARK_SCHED_EVENT;
2009         schedule_event(&game_seconds_schedule, &new_event);
2010     }
2011     return OK;
2012 }
2013 
2014 errtype (*trap_functions[])(int, int, int, int) = {
2015     trap_null_func,
2016     trap_teleport_func,
2017     trap_damage_func,
2018     trap_create_obj_func,
2019     trap_questbit_func,
2020     trap_cutscene_func,
2021     trap_multi_func,
2022     trap_main_light_func,
2023     trap_sfx_func,
2024     trap_height_func,
2025     trap_terrain_func,
2026     trap_scheduler_func,
2027     trap_alternating_splitter_func,
2028     trap_destroy_object_func,
2029     trap_plot_clock_func,
2030     trap_email_func,
2031     trap_expose_func,
2032     trap_instance_func,
2033     trap_animate_func,
2034     trap_hack_func,
2035     trap_texture_func,
2036     trap_ai_func,
2037     trap_bark_func,
2038     trap_monster_func,
2039     trap_transmogrify_func
2040 };
2041 
2042 ubyte num_trap_types = (sizeof(trap_functions) / sizeof(trap_functions[0]));
2043 
grind_trap(char type,int p1,int p2,int p3,int p4,ubyte * destroy_count_ptr,ObjID id)2044 errtype grind_trap(char type, int p1, int p2, int p3, int p4, ubyte *destroy_count_ptr, ObjID id) {
2045     trap_functions[type](p1, p2, p3, p4);
2046     if (*destroy_count_ptr > 0) {
2047         (*destroy_count_ptr) = (*destroy_count_ptr) - 1;
2048         if (*destroy_count_ptr == 0)
2049             ADD_DESTROYED_OBJECT(id);
2050     }
2051     return (OK);
2052 }
2053 
trap_activate(ObjID id,uchar * use_message)2054 uchar trap_activate(ObjID id, uchar *use_message) {
2055     uchar retval = FALSE;
2056     ubyte traptype;
2057     int comparator;
2058     int p1, p2, p3, p4;
2059     ubyte *destroy_count_ptr;
2060     uchar special;
2061 
2062     *trap_use_message = *use_message;
2063     current_trap = id;
2064 
2065     switch (objs[id].obclass) {
2066     case CLASS_TRAP:
2067         traptype = objTraps[objs[id].specID].trap_type;
2068         destroy_count_ptr = &(objTraps[objs[id].specID].destroy_count);
2069         if (objs[id].subclass == TRAP_SUBCLASS_TRIGGER) {
2070             // Triggers that overwrite their comparator have their comparator
2071             // set to zero to avoid problems in interpretation.
2072             switch (objs[id].info.type) {
2073             case DEATHWATCH_TRIGGER_TYPE:
2074             case AREA_ENTRY_TRIGGER_TYPE:
2075             case AREA_CONTINUOUS_TRIGGER_TYPE:
2076                 comparator = 0;
2077                 break;
2078             default:
2079                 comparator = objTraps[objs[id].specID].comparator;
2080                 break;
2081             }
2082         }
2083         p1 = objTraps[objs[id].specID].p1;
2084         p2 = objTraps[objs[id].specID].p2;
2085         p3 = objTraps[objs[id].specID].p3;
2086         p4 = objTraps[objs[id].specID].p4;
2087         break;
2088     case CLASS_FIXTURE:
2089         traptype = objFixtures[objs[id].specID].trap_type;
2090         destroy_count_ptr = &(objFixtures[objs[id].specID].destroy_count);
2091         if ((objs[id].subclass == FIXTURE_SUBCLASS_RECEPTACLE) || (objs[id].subclass == FIXTURE_SUBCLASS_VENDING))
2092             comparator = 0;
2093         else
2094             comparator = objFixtures[objs[id].specID].comparator;
2095         p1 = objFixtures[objs[id].specID].p1;
2096         p2 = objFixtures[objs[id].specID].p2;
2097         p3 = objFixtures[objs[id].specID].p3;
2098         p4 = objFixtures[objs[id].specID].p4;
2099         break;
2100     default:
2101         retval = FALSE;
2102         goto out;
2103     }
2104 
2105     if (comparator_check(comparator, id, &special)) {
2106         grind_trap(traptype, p1, p2, p3, p4, destroy_count_ptr, id);
2107         retval = TRUE;
2108     }
2109 out:
2110     *use_message = *trap_use_message;
2111     return (retval);
2112 }
2113 
2114 // Look through all deathwatch triggers on the level
2115 // if lots of goofy things like explosions are being destroyed here, we may want to
2116 // use some sort of heuristic to speed things up (don't search if destroyed thing
2117 // was an anim, etc.)
2118 // also, we might want to be able to override this behavior if destroying objects
2119 // via the editor is causing unwanted trap problems....
2120 
check_deathwatch_triggers(ObjID id,uchar really_dead)2121 errtype check_deathwatch_triggers(ObjID id, uchar really_dead) {
2122     ObjSpecID osid, nextid;
2123     int comp;
2124     uchar dummy;
2125 
2126     if (id == OBJ_NULL)
2127         return (ERR_NOEFFECT);
2128 
2129     osid = objTraps[0].id;
2130 
2131     while (osid != OBJ_SPEC_NULL) {
2132         nextid = objTraps[osid].next;
2133         if (objs[objTraps[osid].id].info.type == DEATHWATCH_TRIGGER_TYPE) {
2134             comp = objTraps[osid].comparator;
2135 
2136             // Does this particular trigger care about us?
2137             if (really_dead == !(comp & 0x2000000)) {
2138                 if (((comp & 0x1000000) && ((comp & 0xFFFF) == id)) || (ID2TRIP(id) == (comp & 0xFFFFFF))) {
2139                     trap_activate(objTraps[osid].id, &dummy);
2140                 }
2141             }
2142         }
2143         osid = nextid;
2144     }
2145     return (OK);
2146 }
2147 
2148 #define SQ(x) (x * x)
2149 #define in_bin_radius(x1, y1, x2, y2, r) (SQ(r) > SQ((x1 - x2)) + SQ((y1 - y2)))
2150 
check_entrance_triggers(uchar old_x,uchar old_y,uchar new_x,uchar new_y)2151 errtype check_entrance_triggers(uchar old_x, uchar old_y, uchar new_x, uchar new_y) {
2152     ObjID id;
2153     ObjSpecID osid;
2154     uchar trap_x, trap_y;
2155     uchar rad;
2156     uchar invert, in_rad_before, in_rad_now;
2157 
2158     osid = objTraps[0].id;
2159     while (osid != OBJ_SPEC_NULL) {
2160         id = objTraps[osid].id;
2161 
2162         switch (objs[id].subclass) {
2163         case TRAP_SUBCLASS_TRIGGER:
2164             trap_x = OBJ_LOC_BIN_X(objs[id].loc);
2165             trap_y = OBJ_LOC_BIN_Y(objs[id].loc);
2166             switch (objs[id].info.type) {
2167             // Yer basic entry & floor trigger -- this will be used for tripbeams in the near future too
2168             case FLOOR_TRIGGER_TYPE:
2169             case ENTRY_TRIGGER_TYPE:
2170                 if ((trap_x == new_x) && (trap_y == new_y))
2171                     location_trigger_activate(id);
2172                 break;
2173             case AREA_ENTRY_TRIGGER_TYPE:
2174             case AREA_CONTINUOUS_TRIGGER_TYPE:
2175                 rad = abs(objTraps[osid].comparator);
2176                 invert = (objTraps[osid].comparator >= 0x1000);
2177                 in_rad_now = in_bin_radius(trap_x, trap_y, new_x, new_y, rad);
2178                 if (objs[id].info.type == AREA_CONTINUOUS_TRIGGER_TYPE) {
2179                     if ((in_rad_now && !invert) || (!in_rad_now && invert))
2180                         trap_activate(id, trap_use_message);
2181                 } else {
2182                     in_rad_before = in_bin_radius(trap_x, trap_y, old_x, old_y, rad);
2183                     if (in_rad_before != in_rad_now) {
2184                         if ((in_rad_now && !invert) || (!in_rad_now && invert))
2185                             trap_activate(id, trap_use_message);
2186                     }
2187                 }
2188                 break;
2189             }
2190             break;
2191         }
2192         osid = objTraps[osid].next;
2193     }
2194     return (OK);
2195 }
2196 
2197 #define REACTOR_BOOM_QB 0x14
2198 
do_special_reactor_hack()2199 errtype do_special_reactor_hack() {
2200     ObjSpecID osid;
2201 
2202     if (!qdata_get(REACTOR_BOOM_QB | 0x2000))
2203         return (OK);
2204 
2205     // Secret reactor alarm hack
2206     osid = objAnimatings[0].id;
2207     while (osid != OBJ_SPEC_NULL) {
2208         if ((ID2TRIP(objAnimatings[osid].id) == ALERT_PANEL_OFF_TRIPLE) ||
2209             (ID2TRIP(objAnimatings[osid].id) == HORZ_KLAXOFF_TRIPLE)) {
2210             objs[objAnimatings[osid].id].info.type++;
2211         }
2212         osid = objAnimatings[osid].next;
2213     }
2214     obj_load_art(FALSE);
2215     return (OK);
2216 }
2217 
do_level_entry_triggers()2218 errtype do_level_entry_triggers() {
2219     ObjSpecID osid;
2220     uchar special;
2221 
2222     osid = objTraps[0].id;
2223     while (osid != OBJ_SPEC_NULL) {
2224         if (ID2TRIP(objTraps[osid].id) == LEVEL_TRIG_TRIPLE) {
2225             if (comparator_check(objTraps[osid].comparator, objTraps[osid].id, &special)) {
2226                 grind_trap(objTraps[osid].trap_type, objTraps[osid].p1, objTraps[osid].p2, objTraps[osid].p3,
2227                            objTraps[osid].p4, &objTraps[osid].destroy_count, objTraps[osid].id);
2228             }
2229         }
2230         osid = objTraps[osid].next;
2231     }
2232 
2233     do_special_reactor_hack();
2234 
2235     return (OK);
2236 }
2237 
do_shodan_triggers()2238 errtype do_shodan_triggers() {
2239     ObjSpecID osid;
2240     uchar special;
2241 
2242     osid = objTraps[0].id;
2243     while (osid != OBJ_SPEC_NULL) {
2244         if (ID2TRIP(objTraps[osid].id) == SHODO_TRIG_TRIPLE) {
2245             if (comparator_check(objTraps[osid].comparator, objTraps[osid].id, &special)) {
2246                 grind_trap(objTraps[osid].trap_type, objTraps[osid].p1, objTraps[osid].p2, objTraps[osid].p3,
2247                            objTraps[osid].p4, &objTraps[osid].destroy_count, objTraps[osid].id);
2248             }
2249         }
2250         osid = objTraps[osid].next;
2251     }
2252     return (OK);
2253 }
2254 
do_ecology_triggers()2255 errtype do_ecology_triggers() {
2256     ObjSpecID osid, osid2;
2257     ObjClass cl;
2258     char counter = 0, quan;
2259     int trip;
2260     ObjSpec ospec;
2261     ObjID id;
2262     extern uchar trigger_check;
2263 
2264     osid = objTraps[0].id;
2265     while (osid != OBJ_SPEC_NULL) {
2266         id = objTraps[osid].id;
2267         if (ID2TRIP(id) == ECOLOGY_TRIG_TRIPLE) {
2268             quan = objTraps[osid].comparator >> 24;
2269             cl = (ObjClass)((objTraps[osid].comparator & 0xFF0000) >> 16);
2270             trip = objTraps[osid].comparator & 0xFFFFFF;
2271             osid2 = (*(ObjSpec *)objSpecHeaders[cl].data).bits.id;
2272             counter = 0;
2273             while (osid2 != OBJ_SPEC_NULL) {
2274                 ospec = *(ObjSpec *)(objSpecHeaders[cl].data + (osid2 * objSpecHeaders[cl].struct_size));
2275                 if (ID2TRIP(ospec.bits.id) == trip)
2276                     counter++;
2277                 if (counter > quan)
2278                     osid2 = OBJ_SPEC_NULL;
2279                 else
2280                     osid2 = ospec.next;
2281             }
2282             if (counter < quan) {
2283                 trigger_check = FALSE;
2284                 grind_trap(objTraps[osid].trap_type, objTraps[osid].p1, objTraps[osid].p2, objTraps[osid].p3,
2285                            objTraps[osid].p4, &objTraps[osid].destroy_count, objTraps[osid].id);
2286                 trigger_check = TRUE;
2287             }
2288         }
2289         osid = objTraps[osid].next;
2290     }
2291     return (OK);
2292 }
2293