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/objuse.c $
21  * $Revision: 1.194 $
22  * $Author: dc $
23  * $Date: 1994/11/28 06:38:32 $
24  */
25 
26 #include <stdio.h>
27 #include <string.h>
28 
29 #include "amap.h"
30 #include "audiolog.h"
31 #include "criterr.h"
32 #include "cyber.h"
33 #include "cybstrng.h"
34 #include "damage.h"
35 #include "diffq.h"
36 #include "doorparm.h"
37 #include "effect.h"
38 #include "faketime.h"
39 #include "fullscrn.h"
40 #include "gamestrn.h"
41 #include "grenades.h"
42 #include "gr2ss.h"
43 #include "hud.h"
44 #include "ice.h"
45 #include "input.h"
46 #include "invent.h"
47 #include "mainloop.h"
48 #include "mapflags.h"
49 #include "mfdint.h"
50 #include "mfdpanel.h"
51 #include "musicai.h"
52 #include "newmfd.h"
53 #include "objbit.h"
54 #include "objprop.h"
55 #include "objsim.h"
56 #include "objstuff.h"
57 #include "objuse.h"
58 #include "otrip.h"
59 #include "player.h"
60 #include "physics.h"
61 #include "render.h"
62 #include "saveload.h"
63 #include "schedule.h"
64 #include "sfxlist.h"
65 #include "target.h"
66 #include "tilename.h"
67 #include "tools.h"
68 #include "trigger.h"
69 #include "mouselook.h"
70 
71 #define MFD_FIXTURE_FLAG 0x8 // class flag for mfd fixtures
72 
73 extern void mfd_setup_elevator(ushort levmask, ushort reachmask, ushort curlevel, uchar special);
74 extern void mfd_setup_keypad(char special);
75 extern void save_mfd_slot(int mfd_id);
76 extern void restore_mfd_slot(int mfd_id);
77 extern short qdata_get(short qdata);
78 errtype accesspanel_trigger(ObjID id);
79 
80 // -----------
81 //  PROTOTYPES
82 // -----------
83 void zoom_mfd(int mfd, bool shifted);
84 int grab_and_zoom_mfd(int mfd_func, int mfd_slot, bool shifted);
85 errtype obj_access_fail_message(int stringref, char access_level, char offset);
86 uchar really_really_locked(int qvar);
87 uchar use_door(ObjID id, uchar in_inv, ObjID cursor_obj);
88 uchar obj_too_smart(ObjID id);
89 void container_check(ObjID obj, char *count, ObjID *pidlist);
90 char container_extract(ObjID *pidlist, int d1, int d2);
91 void container_stuff(ObjID *pidlist, int numobjs, int *d1, int *d2);
92 uchar is_container(ObjID id, int **d1, int **d2);
93 uchar obj_fixture_zoom(ObjID id, uchar in_inv, uchar *messagep);
94 errtype obj_tractor_beam_func(ObjID id, uchar on);
95 errtype gear_power_outage();
96 void unmulti_anim_callback(ObjID id, intptr_t user_data);
97 errtype obj_screen_animate(ObjID id);
98 uchar obj_keypad_crunch(int p, uchar digits[]);
99 errtype keypad_trigger(ObjID id, uchar digits[]);
100 uchar try_use_epick(ObjID panel, ObjID cursor_obj);
101 ObjID door_in_square(ObjLoc *loc, uchar usable);
102 void regenetron_door_hack();
103 errtype elevator_janitor_run();
104 
105 // not in any way the right way to fix this bug, except
106 // that it is the fastest way.  Doug says go.  Ask TJS.
107 uchar shameful_obselete_flag;
108 
109 #define DOOR_TIME_UNIT 2 // how many time-setting units in a second
110 
zoom_mfd(int mfd,bool shifted)111 void zoom_mfd(int mfd, bool shifted) {
112     LGRect start = {{-5, -5}, {5, 5}};
113     extern LGPoint use_cursor_pos;
114     LGPoint ucp;
115 
116     extern void mfd_zoom_rect(LGRect *, int);
117     extern bool DoubleSize;
118 
119     if (!shifted)
120         mouse_look_off();
121 
122     ucp = use_cursor_pos;
123     if (!DoubleSize)
124         ss_point_convert(&ucp.x, &ucp.y, TRUE);
125     RECT_MOVE(&start, ucp);
126     mfd_zoom_rect(&start, mfd);
127 }
128 
grab_and_zoom_mfd(int mfd_func,int mfd_slot,bool shifted)129 int grab_and_zoom_mfd(int mfd_func, int mfd_slot, bool shifted) {
130     int mfd = mfd_grab_func(mfd_func, mfd_slot);
131     zoom_mfd(mfd, shifted);
132     return mfd;
133 }
134 
obj_access_fail_message(int stringref,char access_level,char offset)135 errtype obj_access_fail_message(int stringref, char access_level, char offset) {
136     char temp[40];
137     strcpy(temp, get_temp_string(MKREF(RES_accessCards, access_level << 1)));
138     strcat(temp, get_string(stringref + offset, NULL, 0));
139     message_info(temp);
140     return (OK);
141 }
142 
143 // dir true for door_closing
door_moving(ObjID door,uchar dir)144 uchar door_moving(ObjID door, uchar dir) {
145     uchar anim_data_from_id(ObjID id, bool *reverse, bool *cycle);
146 
147     bool moving, closing;
148 
149     moving = anim_data_from_id(door, &closing, NULL);
150     return (moving && (!closing == !dir));
151 }
152 
153 #define COMPAR_ESC_ACCESS 0xFF
154 extern uchar comparator_check(int comparator, ObjID obj, uchar *special_code);
155 
door_locked(ObjID obj)156 uchar door_locked(ObjID obj) {
157     ObjSpecID spec = objs[obj].specID;
158     if (!DOOR_CLOSED(obj))
159         return FALSE;
160     if (objDoors[spec].access_level != COMPAR_ESC_ACCESS && objDoors[spec].access_level != 0) {
161         int i;
162         ulong try_combo = 1 << objDoors[spec].access_level;
163         for (i = 0; i < NUM_GENERAL_SLOTS; i++) {
164             ObjID try_card = player_struct.inventory[i];
165             if (ID2TRIP(try_card) == GENCARDS_TRIPLE) {
166                 if (objSmallstuffs[objs[try_card].specID].data1 & try_combo) {
167                     return FALSE;
168                 } else
169                     return TRUE;
170             }
171         }
172         return TRUE;
173     }
174     if (objDoors[spec].locked == 0)
175         return FALSE;
176     if (objDoors[spec].access_level == COMPAR_ESC_ACCESS) {
177         uchar special;
178         int comp = objTraps[objs[objDoors[spec].locked].specID].comparator;
179         // zorch the failure message out of the comparator before
180         // checking.
181         comp = comp & (~(0xFF << 24));
182         return !comparator_check(comp, obj, &special);
183     }
184 
185     return (QUESTBIT_GET(objDoors[spec].locked));
186 }
187 
188 #ifdef DOOM_EMULATION_MODE
189 // questbits for doors that are "broken beyond repair" or such,
190 // and should not be openable even in doom emulation mode.
really_really_locked(int qvar)191 uchar really_really_locked(int qvar) { return (qvar == 0x5E || qvar == 0xE7); }
192 #endif
193 
194 // Okay, secretly we use our in_inv parameter not to really
195 // indicate that the door is in anybody's inventory, but as
196 // follows:
197 //   the 0x1 bit indicates not to use the door's other_half
198 //   the 0x2 bit indicates not to spew any relevant messages,
199 //      and should be used by anyone other than the player trying
200 //      to use the door.
201 //
use_door(ObjID id,uchar in_inv,ObjID cursorObj)202 uchar use_door(ObjID id, uchar in_inv, ObjID cursorObj) {
203     uchar retval = FALSE;
204     uchar play_fx = FALSE;
205     uchar use_card = FALSE;
206     int lqb;
207     DoorSchedEvent new_event;
208     ObjID try_card, other;
209     char i;
210 
211     if ((objDoors[objs[id].specID].access_level != 0)
212 #ifdef DOOM_EMULATION_MODE
213         && (QUESTVAR_GET(MISSION_DIFF_QVAR) >= 2))
214 #else
215     )
216 #endif
217     {
218         int try_combo;
219         uchar rv;
220         uchar special;
221         int comp;
222 
223         if (objDoors[objs[id].specID].access_level > NUM_ACCESS_CODES) {
224             if (objDoors[objs[id].specID].access_level == COMPAR_ESC_ACCESS) {
225                 comp = objTraps[objs[objDoors[objs[id].specID].locked].specID].comparator;
226                 rv = comparator_check(comp, id, &special);
227                 if (!rv) {
228                     return ((comp >> 24) && (special == 0));
229                 }
230                 objDoors[objs[id].specID].access_level = 0;
231                 objDoors[objs[id].specID].locked = 0;
232                 goto access_ok;
233             }
234         }
235 
236         try_combo = 1 << objDoors[objs[id].specID].access_level;
237         {
238             uchar some_card = FALSE;
239             try_card = OBJ_NULL;
240             for (i = 0; i < NUM_GENERAL_SLOTS; i++) {
241                 try_card = player_struct.inventory[i];
242                 if (ID2TRIP(try_card) == GENCARDS_TRIPLE) {
243                     some_card = TRUE;
244                     if (objSmallstuffs[objs[try_card].specID].data1 & try_combo) {
245                         use_card = TRUE;
246                         break;
247                     }
248                 }
249             }
250             if (!use_card) {
251                 if (!(in_inv & 0x2))
252                     obj_access_fail_message(REF_STR_DoorWrongAccess, objDoors[objs[id].specID].access_level,
253                                             objDoors[objs[id].specID].stringnum);
254                 retval = TRUE;
255                 goto out;
256             }
257         }
258     }
259 access_ok:
260     if (DOOR_CLOSED(id) || door_moving(id, TRUE)) {
261         if (((lqb = objDoors[objs[id].specID].locked) != 0) && QUESTBIT_GET(objDoors[objs[id].specID].locked)
262 #ifdef DOOM_EMULATION_MODE
263             && ((QUESTVAR_GET(MISSION_DIFF_QVAR) >= 1) || really_really_locked(lqb)))
264 #else
265         )
266 #endif
267         {
268             if (!(in_inv & 0x2)) {
269                 if (use_card)
270                     string_message_info(REF_STR_DoorCardGoodButLocked + objDoors[objs[id].specID].stringnum);
271                 else
272                     string_message_info(REF_STR_DoorLocked + objDoors[objs[id].specID].stringnum);
273             }
274             retval = TRUE;
275         } else {
276             int closetime;
277 
278             // This string is strangely here rather than above so as to
279             // insure that we do no collide with any other messages
280             if ((use_card) && (!(in_inv & 0x2))) {
281                 char tempbuf[30], tb2[30];
282                 get_object_short_name(ID2TRIP(try_card), tempbuf, 30);
283                 strcpy(tempbuf, get_temp_string(MKREF(RES_accessCards, (objDoors[objs[id].specID].access_level) << 1)));
284                 strcat(tempbuf, " ");
285                 strcat(tempbuf, get_string(REF_STR_DoorCardGood, tb2, 30));
286                 message_info(tempbuf);
287                 retval = TRUE;
288             }
289 
290             add_obj_to_animlist(id, FALSE, FALSE, FALSE, 32, 0, 0, 0); // play anim forwards
291             play_fx = TRUE;
292 
293             // remove render-blocking bit, since well, we're open.
294             objs[id].info.inst_flags &= ~(RENDER_BLOCK_FLAG);
295 
296             // If appropriate, have door automatically close again
297             if (((closetime = objDoors[objs[id].specID].autoclose_time) > 0) && (closetime != NEVER_AUTOCLOSE_COOKIE)) {
298                 ushort new_code;
299                 new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * closetime) / DOOR_TIME_UNIT);
300                 new_event.type = DOOR_SCHED_EVENT;
301                 new_event.door_id = id;
302 
303                 // Compute new secret code, poke it into event and door
304                 new_code = ((objs[id].info.inst_flags >> 6) + 1) & 0x3;
305                 new_event.secret_code = new_code;
306                 objs[id].info.inst_flags &= ~(0x3 << 6);     // clear out space for new code
307                 objs[id].info.inst_flags |= (new_code << 6); // set new code
308 
309                 // schedule us!
310                 schedule_event(&global_fullmap->sched[MAP_SCHEDULE_GAMETIME], (SchedEvent *)&new_event);
311             }
312         }
313     } else {
314         char real_frame = objs[id].info.current_frame;
315         add_obj_to_animlist(id, FALSE, TRUE, FALSE, 32, 0, 0, 0); // play anim backwards
316         play_fx = TRUE;
317     }
318     if (play_fx) {
319         int sfx_id;
320         switch (ID2TRIP(id)) {
321         case ACCESS_DOOR_TRIPLE:
322         case REACTR_DOOR_TRIPLE:
323         case BLAST_DOOR_TRIPLE:
324         case STOR_DOOR_TRIPLE:
325             sfx_id = SFX_DOOR_METAL;
326             break;
327         case EXEC_DOOR_TRIPLE:
328         case IRIS_TRIPLE:
329             sfx_id = SFX_DOOR_IRIS;
330             break;
331         case DOUB_LEFTDOOR_TRIPLE:
332         case DOUB_RITEDOOR_TRIPLE:
333             sfx_id = SFX_DOOR_BULKHEAD;
334             break;
335         case LABFORCE_TRIPLE:
336         case BROKLABFORCE_TRIPLE:
337         case RESFORCE_TRIPLE:
338         case BROKRESFORCE_TRIPLE:
339             sfx_id = -1;
340             break;
341         case GENFORCE_TRIPLE:
342         case CYBGENFORCE_TRIPLE:
343             sfx_id = SFX_DOOR_GRATING;
344             break;
345         default:
346             sfx_id = SFX_DOOR_NORMAL;
347             break;
348         }
349         if (sfx_id != -1)
350             play_digi_fx_obj(sfx_id, 1, id);
351     }
352     if (((other = objDoors[objs[id].specID].other_half) != OBJ_NULL) && !(in_inv & 0x1)) {
353         uchar otherdoor = objs[other].obclass == CLASS_DOOR;
354         // use other half if we don't have the same closed-ness, in order to
355         // cause us to have the same closed-ness.
356         if (!otherdoor || (door_moving(id, TRUE) != (DOOR_REALLY_CLOSED(other) || door_moving(other, TRUE))))
357             object_use(objDoors[objs[id].specID].other_half, otherdoor ? (in_inv | 0x1) : FALSE, OBJ_NULL);
358     }
359 out:
360     return retval;
361 }
362 
363 // Maximum number of objects within another object
364 #define MAX_CONTAINER_CONTENTS 4
365 
366 // If mission difficulty is low, returns TRUE on objects
367 // which might require literacy.
obj_too_smart(ObjID id)368 uchar obj_too_smart(ObjID id) {
369     switch (QUESTVAR_GET(MISSION_DIFF_QVAR)) {
370     case 0:
371         switch (ID2TRIP(id)) {
372         case PAPERS_TRIPLE:
373         case TEXT1_TRIPLE:
374         case EMAIL1_TRIPLE:
375         case MAP1_TRIPLE:
376         case VIDTEX_HARD_TRIPLE:
377             return (TRUE);
378             break;
379         }
380     case 1:
381         switch (ID2TRIP(id)) {
382         case GENCARDS_TRIPLE:
383         case STDCARD_TRIPLE:
384         case SCICARD_TRIPLE:
385         case STORECARD_TRIPLE:
386         case ENGCARD_TRIPLE:
387         case MEDCARD_TRIPLE:
388         case MAINTCARD_TRIPLE:
389         case ADMINCARD_TRIPLE:
390         case SECCARD_TRIPLE:
391         case COMCARD_TRIPLE:
392         case GROUPCARD_TRIPLE:
393         case PERSCARD_TRIPLE:
394         case CYBERCARD_TRIPLE:
395             return (TRUE);
396             break;
397         }
398         break;
399     default:
400         return (FALSE);
401         break;
402     }
403     return (FALSE);
404 }
405 
container_check(ObjID obj,char * count,ObjID * pidlist)406 void container_check(ObjID obj, char *count, ObjID *pidlist) {
407     if (objs[obj].active && !obj_too_smart(obj)) {
408         if (USE_MODE(obj) == PICKUP_USE_MODE)
409             pidlist[(*count)++] = obj;
410         //      else
411         //         Warning(("Non-pickup: trip %#x (%#x) in container!!\n",ID2TRIP(obj),obj));
412     }
413 }
414 
container_extract(ObjID * pidlist,int d1,int d2)415 char container_extract(ObjID *pidlist, int d1, int d2) {
416     char retval = 0;
417     container_check(d1 & 0xFFFF, &retval, pidlist);
418     container_check(d1 >> 16, &retval, pidlist);
419     container_check(d2 & 0xFFFF, &retval, pidlist);
420     container_check(d2 >> 16, &retval, pidlist);
421     return (retval);
422 }
423 
container_stuff(ObjID * pidlist,int numobjs,int * d1,int * d2)424 void container_stuff(ObjID *pidlist, int numobjs, int *d1, int *d2) {
425     int i;
426     for (i = numobjs; i < MAX_CONTAINER_CONTENTS; i++)
427         pidlist[i] = OBJ_NULL;
428     i = 0;
429     *d1 = pidlist[i++];
430     *d1 |= pidlist[i++] << 16;
431     if (d2 == NULL)
432         return;
433     *d2 = pidlist[i++];
434     *d2 |= pidlist[i++] << 16;
435 }
436 
437 // Determines whether or not an object is a "container" and then
438 // figures out what part of it's instance data is used for holding
439 // the objects.
440 // If we want to make more objects have these properties later, here is
441 // the place to add 'em.
is_container(ObjID id,int ** d1,int ** d2)442 uchar is_container(ObjID id, int **d1, int **d2) {
443     ObjSpecID specid = objs[id].specID;
444     uchar retval = FALSE;
445     if (objs[id].obclass == CLASS_CONTAINER) {
446         // Containers
447         *d1 = &objContainers[specid].contents1;
448         *d2 = &objContainers[specid].contents2;
449         retval = TRUE;
450     } else {
451         switch (ID2TRIP(id)) {
452         // Smallstuff
453         case CORPSE1_TRIPLE:
454         case CORPSE2_TRIPLE:
455         case CORPSE3_TRIPLE:
456         case CORPSE4_TRIPLE:
457         case CORPSE5_TRIPLE:
458         case CORPSE6_TRIPLE:
459         case CORPSE7_TRIPLE:
460         case CORPSE8_TRIPLE:
461         case BRIEFCASE_TRIPLE:
462             *d1 = &objSmallstuffs[specid].data1;
463             *d2 = &objSmallstuffs[specid].data2;
464             retval = TRUE;
465             break;
466         // Bigstuff
467         case CABINET_TRIPLE:
468             // Contents in data1, since cosmetic value and data2 taken for texturing
469             // Maybe we can use cosmetic_value too, although thats mighty non-intuitive
470             retval = TRUE;
471             *d1 = &objBigstuffs[specid].data1;
472             *d2 = NULL;
473             break;
474         }
475     }
476     return (retval);
477 }
478 
obj_fixture_zoom(ObjID id,uchar in_inv,uchar * messagep)479 uchar obj_fixture_zoom(ObjID id, uchar in_inv, uchar *messagep) {
480     uchar retval = FALSE;
481     uchar zoom = (objs[id].info.inst_flags & CLASS_INST_FLAG);
482     if (zoom && !in_inv) {
483         int mfd = grab_and_zoom_mfd(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, FALSE);
484         save_mfd_slot(mfd);
485         mfd_notify_func(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE);
486         player_struct.panel_ref = id;
487         mfd_change_slot(mfd, MFD_INFO_SLOT);
488     } else {
489         retval = trap_activate(id, messagep);
490         if (retval) {
491             if (objs[id].info.current_frame == 0)
492                 objs[id].info.current_frame = FRAME_NUM_3D(ObjProps[OPTRIP(ID2TRIP(id))].bitmap_3d);
493             else
494                 objs[id].info.current_frame = 0;
495         }
496     }
497     return (retval);
498 }
499 
500     // GEAR, GEAR, GEAR!
501 
502 #define BATTERY_ENERGY_BONUS 0x50
503 #define TRACBEAM_ENERGY_COST 0x20
504 #define TRACBEAM_DIST_MOD 0x20
505 extern ubyte pickup_distance_mod;
506 
obj_tractor_beam_func(ObjID id,uchar on)507 errtype obj_tractor_beam_func(ObjID id, uchar on) {
508     if (on) {
509         // Tractor beam turning on
510         if (player_struct.energy_spend + TRACBEAM_ENERGY_COST > MAX_ENERGY ||
511             player_struct.energy < TRACBEAM_ENERGY_COST)
512             string_message_info(REF_STR_WareNoPower);
513         else {
514             string_message_info(REF_STR_TractorActivate);
515             pickup_distance_mod += TRACBEAM_DIST_MOD;
516             objs[id].info.inst_flags |= CLASS_INST_FLAG;
517             set_player_energy_spend(player_struct.energy_spend + TRACBEAM_ENERGY_COST);
518         }
519     } else {
520         // Tractor beam turning off
521         set_player_energy_spend(player_struct.energy_spend - TRACBEAM_ENERGY_COST);
522         objs[id].info.inst_flags &= ~CLASS_INST_FLAG;
523         pickup_distance_mod -= TRACBEAM_DIST_MOD;
524         string_message_info(REF_STR_TractorDeactivate);
525     }
526     return (OK);
527 }
528 
gear_power_outage()529 errtype gear_power_outage() {
530     extern errtype obj_tractor_beam_func(ObjID id, uchar on);
531 
532     ObjID obj;
533     char i;
534     for (i = 0; i < NUM_GENERAL_SLOTS; i++) {
535         obj = player_struct.inventory[i];
536         if ((obj != OBJ_NULL) && (objs[obj].active)) {
537             // Turn off any active gear
538             switch (ID2TRIP(obj)) {
539             case TRACBEAM_TRIPLE:
540                 if (objs[obj].info.inst_flags & CLASS_INST_FLAG)
541                     obj_tractor_beam_func(obj, FALSE);
542                 break;
543             }
544         }
545     }
546     return (OK);
547 }
548 
549 // returns TRUE iff we tried to use an electronic pick on the panel.
550 //
try_use_epick(ObjID panel,ObjID cursor_obj)551 uchar try_use_epick(ObjID panel, ObjID cursor_obj) {
552     uchar sol;
553 
554     if (cursor_obj != OBJ_NULL) {
555         if (ID2TRIP(cursor_obj) == EPICK_TRIPLE) {
556             if ((sol = mfd_solve_accesspanel(panel)) == EPICK_SOLVED) {
557                 obj_destroy(cursor_obj);
558                 pop_cursor_object();
559             } else
560                 string_message_info(REF_STR_EPickFailure + sol - 1);
561         }
562         return TRUE;
563     }
564     return FALSE;
565 }
566 
567 #define PLASTIQUE_TIME 10
568 extern void remove_general_item(ObjID obj);
569 extern bool gKeypadOverride;
570 
571 bool ObjectUseShifted = FALSE; //set if shift key was held when using object
572 
573 // We return whether or not we used the message line.
object_use(ObjID id,uchar in_inv,ObjID cursor_obj)574 uchar object_use(ObjID id, uchar in_inv, ObjID cursor_obj) {
575     uchar retval = FALSE, rv;
576     ObjFixture *pfixt;
577     ObjBigstuff *pbigs;
578     char i;
579     ObjSpecID osid;
580     uchar special;
581     extern errtype do_random_loot(ObjID corpse);
582     extern char camera_map[NUM_HACK_CAMERAS];
583     extern ObjID hack_cam_objs[NUM_HACK_CAMERAS];
584     extern ubyte next_text_line;
585     extern errtype inventory_draw_new_page(int num);
586     extern void read_email(Id new_base, int num);
587     int *d1, *d2;
588     bool shifted;
589 
590     shifted = ObjectUseShifted;
591     ObjectUseShifted = FALSE;
592 
593     // First, the multi-class behavior objects
594     if (is_container(id, &d1, &d2)) {
595         int mfd;
596         extern uchar gump_get_useful(bool shifted);
597         extern void gump_clear(void);
598 
599         if (id == player_struct.panel_ref && object_on_cursor == 0) {
600             gump_get_useful(shifted);
601             return TRUE;
602         }
603         mfd = grab_and_zoom_mfd(MFD_GUMP_FUNC, MFD_INFO_SLOT, shifted);
604         do_random_loot(id);
605         save_mfd_slot(mfd);
606         mfd_notify_func(MFD_GUMP_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE);
607 
608         // oh, how we are filled with shame.  The gump_idlist doesn't
609         // get filled until the expose func, one frame from now, so for
610         // this one frame the idlist and the panel_ref will be out of
611         // synch.  During this frame it is possible to gump_get_useful
612         // from the wrong idlist for our current panel_ref!  A simple
613         // but inelegant fix to the problem is to null the idlist so that
614         // gump_pickups cannot work and we do not container_stuff the
615         // altered idlist into the wrong container.  Get it?  Good.
616         gump_clear();
617 
618         player_struct.panel_ref = id;
619         mfd_change_slot(mfd, MFD_INFO_SLOT);
620         retval = TRUE;
621         return retval;
622     }
623     osid = objs[id].specID;
624 
625     if (global_fullmap->cyber) {
626         uchar did_something = FALSE;
627         extern void long_bark(ObjID speaker_id, uchar mug_id, int string_id, ubyte color);
628         switch (ID2TRIP(id)) {
629         case CYBERHEAL_TRIPLE:
630             if (player_struct.cspace_hp != PLAYER_MAX_HP) {
631                 player_struct.cspace_hp =
632                     lg_min(player_struct.cspace_hp + CYBERHEAL_QUANTITY + objSmallstuffs[objs[id].specID].data1,
633                            PLAYER_MAX_HP);
634                 hud_unset(HUD_CYBERDANGER);
635                 string_message_info(REF_STR_CspaceHeal);
636                 ADD_DESTROYED_OBJECT(id);
637                 chg_set_flg(VITALS_UPDATE);
638             } else
639                 string_message_info(REF_STR_CspaceMaxHealth);
640             did_something = TRUE;
641             break;
642         case CYBERMINE_TRIPLE:
643             damage_object(PLAYER_OBJ, CYBERMINE_DAMAGE * (objSmallstuffs[objs[id].specID].data1 + 1), 1, 0);
644             did_something = TRUE;
645             break;
646         case DATALET_TRIPLE:
647             long_bark(OBJ_NULL, 0, REF_STR_DataletZero + objSmallstuffs[objs[id].specID].data1, 0x4c);
648             did_something = TRUE;
649             break;
650         case CSPACE_EXIT_TRIPLE:
651             player_struct.cspace_time_base =
652                 lg_max(CSPACE_MIN_TIME, player_struct.cspace_time_base - CSPACE_EXIT_PENALTY);
653             go_to_different_level(player_struct.realspace_level);
654             did_something = TRUE;
655             retval = obj_move_to(PLAYER_OBJ, &player_struct.realspace_loc, TRUE);
656             break;
657         case INFONODE_TRIPLE:
658             long_bark(OBJ_NULL, 0, REF_STR_CspaceInfoBase + objSmallstuffs[objs[id].specID].data1, 0x4c);
659             did_something = TRUE;
660             break;
661         case CYBERCARD_TRIPLE:
662             if (QUESTVAR_GET(MISSION_DIFF_QVAR) > 1) {
663                 ObjLocState del_loc_state;
664                 // yank the object out of the map.
665                 del_loc_state.obj = id;
666                 del_loc_state.loc = objs[id].loc;
667                 del_loc_state.loc.x = -1;
668                 ObjRefStateBinSetNull(del_loc_state.refs[0].bin);
669                 ObjUpdateLocs(&del_loc_state);
670                 inventory_add_object(id, FALSE);
671             }
672             break;
673         }
674         if (did_something)
675             return (TRUE);
676     }
677 
678     switch (objs[id].obclass) {
679         extern uchar comparator_check(int comparator, ObjID obj, uchar *special_code);
680     case CLASS_TRAP:
681         if (ID2TRIP(id) == MAPNOTE_TRIPLE) {
682             char buf[80];
683 
684             sprintf(buf, "\"%s\"", amap_note_string(id));
685             message_info(buf);
686             retval = TRUE;
687         }
688         break;
689     case CLASS_DOOR:
690         retval = use_door(id, in_inv, cursor_obj);
691         break;
692 
693     case CLASS_FIXTURE:
694         // Deal with some specifics....
695         switch (ID2TRIP(id)) {
696         case ELEPANEL1_TRIPLE:
697         case ELEPANEL2_TRIPLE:
698         case ELEPANEL3_TRIPLE: {
699             if (me_bits_music(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y)) != ELEVATOR_ZONE) {
700                 string_message_info(REF_STR_UseTooFar);
701                 retval = TRUE;
702             } else {
703                 pfixt = &objFixtures[objs[id].specID];
704 #ifdef DOOM_EMULATION_MODE
705                 if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 0) {
706                     rv = TRUE;
707                     special = FALSE;
708                 } else
709 #endif
710                     rv = comparator_check(pfixt->comparator, id, &special);
711                 if (rv || (special != 0)) {
712                     int mfd = grab_and_zoom_mfd(MFD_ELEV_FUNC, MFD_INFO_SLOT, FALSE);
713                     // Set our reference...
714                     save_mfd_slot(mfd);
715 
716                     // Call appropriate MFD function so that later, in turn, we get called
717                     // First force the slot...
718                     mfd_setup_elevator(pfixt->p4 >> 16, pfixt->p4 & 0xFFFF, player_struct.level, special);
719                     player_struct.panel_ref = id;
720                     mfd_change_slot(mfd, MFD_INFO_SLOT);
721                 }
722             }
723             retval = TRUE;
724             break;
725         }
726 
727         case KEYPAD1_TRIPLE:
728         case KEYPAD2_TRIPLE: {
729             pfixt = &objFixtures[objs[id].specID];
730             rv = comparator_check(pfixt->comparator, id, &special);
731 #ifdef DOOM_EMULATION_MODE
732             if (rv && (QUESTVAR_GET(MISSION_DIFF_QVAR) <= 1)) {
733                 do_multi_stuff(qdata_get(pfixt->p1 >> 16));
734                 do_multi_stuff(qdata_get(pfixt->p2 >> 16));
735                 do_multi_stuff(qdata_get(pfixt->p3 >> 16));
736                 play_digi_fx_obj(SFX_MFD_SUCCESS, 1, id);
737             }
738 #endif
739             else if (rv || (special != 0)) {
740                 int mfd = grab_and_zoom_mfd(MFD_KEYPAD_FUNC, MFD_INFO_SLOT, FALSE);
741                 // Set our reference...
742                 save_mfd_slot(mfd);
743 
744                 // Call appropriate MFD function so that later, in turn, we get called
745                 // First force the slot...
746                 objs[id].info.current_frame = 1;
747                 gKeypadOverride = TRUE;
748                 mfd_setup_keypad(special);
749                 player_struct.panel_ref = id;
750                 mfd_change_slot(mfd, MFD_INFO_SLOT);
751             }
752             retval = TRUE;
753             break;
754         }
755 
756         case ACCPANEL1_TRIPLE:
757         case ACCPANEL2_TRIPLE:
758         case ACCPANEL3_TRIPLE:
759         case ACCPANEL4_TRIPLE:
760         case ACCPANEL5_TRIPLE:
761         case ACCPANEL6_TRIPLE: {
762             pfixt = &objFixtures[objs[id].specID];
763             rv = comparator_check(pfixt->comparator, id, &special);
764             if (rv) {
765                 int accessmfd = NUM_MFDS;
766 
767                 if (player_struct.panel_ref != id) {
768                     accessmfd = grab_and_zoom_mfd(mfd_type_accesspanel(id), MFD_INFO_SLOT, FALSE);
769                     save_mfd_slot(accessmfd);
770 
771                     // Call appropriate MFD function so that later, in turn, we get called
772                     // First force the slot...
773                     objs[id].info.current_frame = 1;
774                     mfd_setup_accesspanel(special, id);
775                     player_struct.panel_ref = id;
776                 } else {
777                     int mfd_id = NUM_MFDS;
778                     if (mfd_yield_func(mfd_type_accesspanel(id), &mfd_id)) {
779                         zoom_mfd(mfd_id, FALSE);
780                     }
781                 }
782 
783                 // electronic picks work even at difficulty 0, though
784                 // you don't need them.  Only automatically solve the
785                 // puzzle if a pick doesn't try to do so for you.
786                 if (!try_use_epick(id, cursor_obj)) {
787                     int info;
788 
789                     if (player_struct.difficulty[PUZZLE_DIFF_INDEX] == 0)
790                         mfd_solve_accesspanel(id);
791                     else {
792 
793                         // give us another info MFD for help text.
794                         // note that this all works without assuming
795                         // that we have exactly 2 mfd's.  If you want
796                         // to pitch that, you can compact this code
797                         // a little bit just by setting mfd=0, info=1.
798 
799                         for (info = 0; info < NUM_MFDS; info++)
800                             if (info != accessmfd) {
801                                 save_mfd_slot(info);
802                                 mfd_change_slot(info, MFD_INFO_SLOT);
803                                 break;
804                             }
805                     }
806                 }
807                 if (accessmfd < NUM_MFDS)
808                     mfd_change_slot(accessmfd, MFD_INFO_SLOT);
809             }
810             retval = TRUE;
811             break;
812         }
813 
814         case ENRG_CHARGE_TRIPLE:
815             pfixt = &objFixtures[objs[id].specID];
816             rv = comparator_check(pfixt->comparator, id, &special);
817             if (rv) {
818                 if (player_struct.game_time >= objFixtures[osid].p4) {
819                     // give the player some juice!
820                     player_struct.energy = lg_min(255, player_struct.energy + objFixtures[osid].p1);
821 
822                     // update the vitals window
823                     chg_set_flg(VITALS_UPDATE);
824 
825                     // don't let us get energy until some time later
826                     objFixtures[osid].p4 = (int)(player_struct.game_time + (CIT_CYCLE * objFixtures[osid].p2));
827 
828                     string_message_info(REF_STR_Recharge);
829                     play_digi_fx_obj(SFX_ENERGY_RECHARGE, 1, id);
830 
831                     // Trigger any associated trap
832                     do_multi_stuff(objFixtures[osid].p3);
833                 } else
834                     string_message_info(REF_STR_NoRecharge);
835             }
836             retval = TRUE;
837             break;
838 
839         case ANTENNA_PAN_TRIPLE:
840             if (ID2TRIP(cursor_obj) == PLASTIQUE_TRIPLE) {
841                 DoorSchedEvent new_event;
842 
843                 if (objs[id].info.current_frame == 1) {
844                     // Putting plastique on the panel.
845                     string_message_info(REF_STR_PlastiqueOn);
846                     obj_destroy(cursor_obj);
847                     pop_cursor_object();
848 
849                     // Transmogrify der objectenhausen into a plastiqued panel
850                     objs[id].info.type = 4;
851                     objs[id].info.current_frame = 0;
852 
853                     // Set a timer to go boom
854                     new_event.type = DOOR_SCHED_EVENT;
855                     new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * PLASTIQUE_TIME));
856                     new_event.door_id = id;
857                     schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event);
858 
859                     // do any appropriate trap
860                     do_multi_stuff(objFixtures[objs[id].specID].p2);
861                 } else
862                     string_message_info(REF_STR_OpenPanelFirst);
863             } else {
864                 if (objs[id].info.current_frame)
865                     objs[id].info.current_frame = 0;
866                 else
867                     objs[id].info.current_frame = 1;
868             }
869             retval = TRUE;
870             break;
871 
872         default:
873             switch (objs[id].subclass) {
874             case FIXTURE_SUBCLASS_RECEPTACLE:
875             case FIXTURE_SUBCLASS_VENDING:
876                 if ((cursor_obj != OBJ_NULL) || (QUESTVAR_GET(MISSION_DIFF_QVAR) < 1)) {
877                     if (ID2TRIP(id) == RETSCANNER_TRIPLE) {
878                         int head_count = 0;
879                         if (ID2TRIP(cursor_obj) == HEAD_TRIPLE)
880                             head_count = objs[cursor_obj].info.current_frame + 1;
881                         else if (ID2TRIP(cursor_obj) == HEAD2_TRIPLE)
882                             head_count = objs[cursor_obj].info.current_frame + 11 + 1;
883                         if (head_count > 0) {
884                             if (((objFixtures[objs[id].specID].comparator & 0xFFFFFF) == head_count - 1) ||
885                                 (QUESTVAR_GET(MISSION_DIFF_QVAR) < 1)) {
886                                 objs[id].info.current_frame = 1;
887                                 obj_fixture_zoom(id, in_inv, &retval);
888                             } else {
889                                 objs[id].info.current_frame = 2;
890                                 string_message_info(REF_STR_WrongHead);
891                             }
892                         }
893                     } else if (ID2TRIP(cursor_obj) ==
894                                 (objFixtures[objs[id].specID].comparator & 0xFFFFFF) ||
895                                QUESTVAR_GET(MISSION_DIFF_QVAR) < 1) {
896                         obj_fixture_zoom(id, in_inv, &retval);
897                         obj_destroy(cursor_obj);
898                         pop_cursor_object();
899                     } else if (objFixtures[objs[id].specID].comparator >> 24) {
900                         string_message_info(REF_STR_TrapZeroMessage + (objFixtures[objs[id].specID].comparator >> 24));
901 #ifdef AUDIOLOGS
902                         audiolog_bark_play(objFixtures[objs[id].specID].comparator >> 24);
903 #endif
904                     }
905                 } else if (ID2TRIP(id) == RETSCANNER_TRIPLE)
906                     string_message_info(REF_STR_WrongHead);
907                 retval = TRUE;
908                 break;
909             default: {
910                 uchar access_okay = FALSE;
911                 int try_combo;
912                 if ((objFixtures[osid].access_level == 0)
913 #ifdef DOOM_EMULATION_MODE
914                     || (QUESTVAR_GET(MISSION_DIFF_QVAR) < 2)
915 #endif
916                 )
917                     access_okay = TRUE;
918                 else {
919                     ObjID try_card;
920                     uchar had_card = FALSE;
921                     try_combo = 1 << objFixtures[osid].access_level;
922                     for (i = 0; i < NUM_GENERAL_SLOTS; i++) {
923                         try_card = player_struct.inventory[i];
924                         if (ID2TRIP(try_card) == GENCARDS_TRIPLE) {
925                             had_card = TRUE;
926                             if (objSmallstuffs[objs[try_card].specID].data1 & try_combo)
927                                 access_okay = TRUE;
928                             else
929                                 obj_access_fail_message(REF_STR_FixtureAccessBad, objFixtures[osid].access_level, 0);
930                         }
931                     }
932                     if (!had_card)
933                         obj_access_fail_message(REF_STR_FixtureAccessBad, objFixtures[osid].access_level, 0);
934                 }
935                 if (access_okay) {
936                     if (comparator_check(objFixtures[objs[id].specID].comparator, id, &special)) {
937                         switch (ID2TRIP(id)) {
938                         case BUTTON1_TRIPLE:
939                         case BUTTON2_TRIPLE:
940                             play_digi_fx_obj(SFX_BUTTON, 1, id);
941                             break;
942                         case BIGRED_TRIPLE:
943                             play_digi_fx_obj(SFX_BIGBUTTON, 1, id);
944                             break;
945                         case BIGLEVER_TRIPLE:
946                             play_digi_fx_obj(SFX_BIGLEVER, 1, id);
947                             break;
948                         case LEVER1_TRIPLE:
949                         case LEVER2_TRIPLE:
950                             play_digi_fx_obj(SFX_NORMAL_LEVER, 1, id);
951                             break;
952                         case SWITCH1_TRIPLE:
953                         case SWITCH2_TRIPLE:
954                             play_digi_fx_obj(SFX_MECH_BUTTON, 1, id);
955                             break;
956                         }
957                         obj_fixture_zoom(id, in_inv, &retval);
958                         if (objs[id].subclass == FIXTURE_SUBCLASS_CYBER) {
959                             if (ID2TRIP(id) == CYBERTOG1_TRIPLE) {
960                                 objs[id].info.type = 1;
961                             }
962 #ifdef BROKEN_CYBERTOGS
963                             else if (ID2TRIP(id) == CYBERTOG2_TRIPLE)
964                                 objs[id].info.type = 0;
965 #endif
966                         }
967                     }
968                 }
969                 break;
970             }
971             }
972             retval = TRUE;
973             break;
974         }
975         break;
976     case CLASS_CRITTER: {
977         if (cursor_obj == OBJ_NULL) {
978             if (id != player_struct.curr_target)
979                 select_current_target(id, TRUE);
980             else
981                 select_current_target(OBJ_NULL, TRUE);
982         }
983     }
984         retval = TRUE;
985         break;
986 
987     case CLASS_SMALLSTUFF:
988         if (in_inv) {
989             switch (ID2TRIP(id)) {
990             case AIDKIT_TRIPLE:
991                 string_message_info(REF_STR_MedikitUse);
992                 player_struct.hit_points = PLAYER_MAX_HP;
993                 goto yankinv;
994             case BATTERY2_TRIPLE:
995                 player_struct.energy = 255;
996             case BATTERY_TRIPLE:
997                 player_struct.energy = lg_min(255, player_struct.energy + BATTERY_ENERGY_BONUS);
998                 play_digi_fx(SFX_BATTERY_USE, 1);
999             yankinv:
1000                 remove_general_item(id);
1001 
1002                 // Make appropriate UI parts redraw
1003                 chg_set_flg(VITALS_UPDATE);
1004                 chg_set_flg(INVENTORY_UPDATE);
1005                 retval = TRUE;
1006                 break;
1007             case TRACBEAM_TRIPLE:
1008                 obj_tractor_beam_func(id, (objs[id].info.inst_flags & CLASS_INST_FLAG) == 0);
1009                 retval = TRUE;
1010                 break;
1011             default:
1012 #ifdef SUPPORT_STUFF_OBJUSE
1013                 if (((ObjProps[OPNUM(id)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) == STUFF_OBJUSE_FLAG) {
1014                     do_multi_stuff(objSmallstuffs[objs[id].specID].data1 & 0xFFFF);
1015                     do_multi_stuff(objSmallstuffs[objs[id].specID].data1 >> 16);
1016                     retval = TRUE;
1017                 } else
1018 #endif
1019                     break;
1020             }
1021             mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1022         } else {
1023             switch (ID2TRIP(id)) {
1024             case PAPERS_TRIPLE:
1025                 // Note secret perversion of email system
1026                 read_email(RES_paper0, objSmallstuffs[objs[id].specID].data1);
1027 #ifdef OLD_WAY
1028                 next_text_line = 0;
1029                 current_email = 0;
1030                 inventory_draw_new_page(EMAILTEXT_INV_PAGE);
1031                 if (ResInUse(RES_paper0 + objSmallstuffs[objs[id].specID].data1))
1032                     email_draw_text(RES_paper0 + objSmallstuffs[objs[id].specID].data1, FALSE);
1033                 else
1034                     email_draw_text(RES_paper0, FALSE);
1035 #endif
1036                 retval = TRUE;
1037                 break;
1038             }
1039         }
1040         break;
1041 
1042     case CLASS_BIGSTUFF:
1043         switch (ID2TRIP(id)) {
1044         case SURG_MACH_TRIPLE:
1045             pbigs = &objBigstuffs[objs[id].specID];
1046             rv = comparator_check(pbigs->data1, id, &special);
1047             if (rv) {
1048                 if (pbigs->data2 & 0xFFFF)
1049                     player_struct.hit_points =
1050                         lg_min(PLAYER_MAX_HP, player_struct.hit_points + (pbigs->data2 & 0xFFFF));
1051                 else
1052                     player_struct.hit_points = PLAYER_MAX_HP;
1053                 chg_set_flg(VITALS_UPDATE);
1054                 string_message_info(REF_STR_SurgeryHeal);
1055                 trap_sfx_func(0, 0, 2, CIT_CYCLE << 1);
1056                 do_multi_stuff(pbigs->data2 >> 16);
1057                 play_digi_fx_obj(SFX_SURGERY_MACHINE, 1, id);
1058             }
1059             // return here to prevent 'data1' from being interpreted as object IDs below
1060             return TRUE;
1061 
1062         case CONTPAN_TRIPLE:
1063         case CONTPED_TRIPLE:
1064             if (objBigstuffs[osid].data1) {
1065                 break;
1066             }
1067         case TV_TRIPLE:
1068         case MONITOR2_TRIPLE:
1069         case SCREEN_TRIPLE:
1070         case BIGSCREEN_TRIPLE:
1071         case SUPERSCREEN_TRIPLE: {
1072             short v = objBigstuffs[osid].data2 & 0x7F;
1073             if ((v >= FIRST_CAMERA_TMAP) && (v < FIRST_CAMERA_TMAP + NUM_HACK_CAMERAS)) {
1074                 if (camera_map[v - FIRST_CAMERA_TMAP] && hack_cam_objs[v - FIRST_CAMERA_TMAP])
1075                     hack_camera_takeover(v - FIRST_CAMERA_TMAP);
1076             } else
1077                 string_message_info(REF_STR_NormalScreen);
1078             retval = TRUE;
1079             break;
1080         }
1081 
1082         default:
1083             break;
1084         }
1085 
1086         if (((ObjProps[OPNUM(id)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) == STUFF_OBJUSE_FLAG) {
1087             do_multi_stuff(objBigstuffs[objs[id].specID].data1 & 0xFFFF);
1088             do_multi_stuff(objBigstuffs[objs[id].specID].data1 >> 16);
1089             if (objBigstuffs[objs[id].specID].data1)
1090                 retval = TRUE;
1091         }
1092         break;
1093     default:
1094         break;
1095     }
1096     return (retval);
1097 }
1098 
door_in_square(ObjLoc * loc,uchar usable)1099 ObjID door_in_square(ObjLoc *loc, uchar usable) {
1100     ObjRefID oref;
1101     ObjID id;
1102 
1103     oref = me_objref(MAP_GET_XY(OBJ_LOC_BIN_X(*loc), OBJ_LOC_BIN_Y(*loc)));
1104     while (oref != OBJ_REF_NULL) {
1105         id = objRefs[oref].obj;
1106 
1107         if (objs[id].obclass == CLASS_DOOR) {
1108             if (!usable || USE_MODE(id) == USE_USE_MODE)
1109                 return id;
1110         }
1111 
1112         oref = objRefs[oref].next;
1113     }
1114     return (OBJ_NULL);
1115 }
1116 
regenetron_door_hack()1117 void regenetron_door_hack() {
1118     ObjID id;
1119 
1120     id = door_in_square(&(objs[PLAYER_OBJ].loc), TRUE);
1121 
1122     if (id && !door_moving(id, TRUE) && !door_moving(id, FALSE))
1123         objs[id].info.current_frame = 0;
1124 }
1125 
1126 // Collects all the objects already in the elevator that you are going to
1127 // and move them outside the door
1128 #define MAX_JANITOR_OBJS 32
elevator_janitor_run()1129 errtype elevator_janitor_run() {
1130     short x0, x1, y0, y1, x, y;
1131     int i, j, obj_count = 0;
1132     ObjLoc dump_loc = {0, 0, 0, 0, 0, 0}, newloc;
1133     ObjID objlist[MAX_JANITOR_OBJS], id;
1134     uchar dupe;
1135     ObjRefID orefid;
1136     extern uchar robot_antisocial;
1137 
1138     // clear out our movelist
1139     for (i = 0; i < MAX_JANITOR_OBJS; i++)
1140         objlist[i] = OBJ_NULL;
1141     dump_loc.x = 65535;
1142 
1143     // Compute bounding box of the elevator, see comments in compute_elev_objs
1144     x0 = PLAYER_BIN_X;
1145     y0 = PLAYER_BIN_Y;
1146     while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE)
1147         x0--;
1148     x0++;
1149     while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE)
1150         y0--;
1151     y0++;
1152     x1 = x0;
1153     y1 = y0;
1154     while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE)
1155         x1++;
1156     x1--;
1157     while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE)
1158         y1++;
1159     y1--;
1160 
1161     // Collect all the objects
1162     for (x = x0; x <= x1; x++) {
1163         for (y = y0; y <= y1; y++) {
1164             orefid = me_objref(MAP_GET_XY(x, y));
1165             while (orefid != OBJ_NULL) {
1166                 id = objRefs[orefid].obj;
1167                 if ((objs[id].obclass == CLASS_DOOR) && (dump_loc.x == 65535)) {
1168                     dump_loc.x = objs[id].loc.x & (~0xFF);
1169                     dump_loc.y = objs[id].loc.y & (~0xFF);
1170                     if ((objs[id].loc.y & 0xFF) <= 0x2)
1171                         dump_loc.y -= 0x100;
1172                     else if ((objs[id].loc.y & 0xFF) >= 0xFC)
1173                         dump_loc.y += 0x100;
1174                     else if ((objs[id].loc.x & 0xFF) <= 0x2)
1175                         dump_loc.x -= 0x100;
1176                     else
1177                         dump_loc.x += 0x100;
1178                 } else if ((id != OBJ_NULL) && (id != PLAYER_OBJ) && (ObjProps[OPNUM(id)].physics_model)) {
1179                     dupe = FALSE;
1180                     for (j = 0; j < obj_count; j++) {
1181                         if (objlist[j] == id) {
1182                             dupe = TRUE;
1183                             break;
1184                         }
1185                     }
1186                     if (!dupe)
1187                         objlist[obj_count++] = id;
1188                 }
1189                 orefid = objRefs[orefid].next;
1190             }
1191         }
1192     }
1193 
1194     // Move all the stuff there
1195     robot_antisocial = TRUE;
1196     for (i = 0; i < obj_count; i++) {
1197         newloc = objs[objlist[i]].loc;
1198         newloc.x = dump_loc.x + (rand() & 0xBF) + 0x20;
1199         newloc.y = dump_loc.y + (rand() & 0xBF) + 0x20;
1200         newloc.z =
1201             obj_floor_compute(objlist[i], me_height_flr(MAP_GET_XY(OBJ_LOC_BIN_X(dump_loc), OBJ_LOC_BIN_Y(dump_loc))));
1202         obj_move_to(objlist[i], &newloc, TRUE);
1203     }
1204     robot_antisocial = FALSE;
1205 
1206     return (OK);
1207 }
1208 
1209 #ifdef ELEVATOR_PACKRAT
1210 
1211 #define MAX_ELEV_OBJS 16
1212 
1213 // Goes through all the objects in the same elevator as the player, and fills objlist with them
compute_elev_objs(ObjID * objlist)1214 errtype compute_elev_objs(ObjID *objlist) {
1215     short i, j, x, y;
1216     short x0, x1, y0, y1;
1217     ObjRefID oref;
1218     ObjID id;
1219     MapElem *pme;
1220     uchar dupe;
1221 
1222     for (i = 0; i < MAX_ELEV_OBJS; i++)
1223         objlist[i] = OBJ_NULL;
1224     i = 0;
1225 
1226     // Wow, this is an wacky way of finding the bounding rectangle
1227     // of elevator music, but hey, it should work for any rectangle...
1228     x0 = PLAYER_BIN_X;
1229     y0 = PLAYER_BIN_Y;
1230     while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE)
1231         x0--;
1232     x0++;
1233     while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE)
1234         y0--;
1235     y0++;
1236     x1 = x0;
1237     y1 = y0;
1238     while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE)
1239         x1++;
1240     x1--;
1241     while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE)
1242         y1++;
1243     y1--;
1244 
1245     // Go through all the elevator squares, collecting objects
1246     for (x = x0; x <= x1; x++) {
1247         for (y = y0; y <= y1; y++) {
1248             pme = MAP_GET_XY(x, y);
1249             oref = me_objref(pme);
1250             while (oref != OBJ_REF_NULL) {
1251                 dupe = FALSE;
1252                 id = objRefs[oref].obj;
1253                 if ((id != OBJ_NULL) && (id != PLAYER_OBJ) && (ObjProps[OPNUM(id)].physics_model)) {
1254                     for (j = 0; j < i; j++) {
1255                         if (objlist[j] == id) {
1256                             dupe = TRUE;
1257                             break;
1258                         }
1259                     }
1260                     if (!dupe) {
1261                         ObjLoc newloc;
1262                         State st;
1263                         extern void state_to_objloc(State * s, ObjLoc * l);
1264                         int ph = objs[id].info.ph;
1265                         int *pd1, *pd2;
1266 
1267                         // Make sure it is okay to come with us.... first check physics then check
1268                         // for containerism, and grenadeliness.
1269                         if (ph != -1) {
1270                             // if we are in physics, force us to the floor, etc. before going
1271                             EDMS_settle_object(ph);
1272                             EDMS_get_state(ph, &st);
1273                             state_to_objloc(&st, &newloc);
1274                             obj_move_to(id, &newloc, FALSE);
1275                             EDMS_kill_object(ph);
1276                             objs[id].info.ph = -1;
1277                         }
1278                         // This will cruelly strand the container's contents to the
1279                         // eternal limbo of the unreferenced object.
1280                         // Life is a grim place sometimes.
1281                         if (is_container(id, &pd1, &pd2)) {
1282                             *pd1 = 0;
1283                             *pd2 = 0;
1284                         }
1285 
1286                         // Boom go the grenades
1287                         if ((objs[id].obclass == CLASS_GRENADE) &&
1288                             (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG))
1289                             ADD_DESTROYED_OBJECT(id);
1290                         //                     do_grenade_explosion(id,TRUE);
1291                         else
1292                             objlist[i++] = id;
1293                     }
1294                 }
1295                 oref = objRefs[oref].next;
1296             }
1297         }
1298     }
1299     return (OK);
1300 }
1301 
1302 #endif
1303 
1304 // uncomment the next 2 lines for playable demo only!!!
1305 //#define MAC_DEMO
1306 // extern Boolean	gPlayingGame;
1307 //
1308 
1309 // Eventually, this code will also go and do some nice level-changing
1310 // animation.  For now, we just telemaport you.
1311 // dest_level is the target level to be teleported to
1312 // which_panel is an index into the list of "equivalent" panels that each panel keeps around.
1313 // returns whether or not the elevator actually went anywhere
elevator_use(short dest_level,ubyte which_panel)1314 uchar elevator_use(short dest_level, ubyte which_panel) {
1315 #ifdef MAC_DEMO
1316     //   extern errtype trap_cutscene_func(int p1, int p2, int p3, int p4);
1317     //   trap_cutscene_func(2,TRUE,0,0);
1318     uiHideMouse(NULL);
1319     ShowCursor();
1320     Alert(1999, NULL);    // Show "thanks for playing demo" alert.
1321     gPlayingGame = FALSE; // Hop out of the game loop.
1322 #else
1323     errtype retval = TRUE;
1324     short xdiff, ydiff, zdiff;
1325     ObjLoc panel_loc, newloc;
1326     char old_zsh;
1327     int new_panel;
1328     ObjRefID oref;
1329     ObjID id;
1330     int nuframe;
1331 #ifdef ELEVATOR_PACKRAT
1332     char i;
1333     ObjLoc temploc;
1334     ObjID tempid;
1335     ObjID elev_obj_list[MAX_ELEV_OBJS];
1336     ObjLoc elev_obj_diffs[MAX_ELEV_OBJS];
1337     extern void store_objects(char **buf, ObjID *obj_array, char obj_count);
1338     extern void restore_objects(char *buf, ObjID *obj_array, char obj_count);
1339     extern errtype obj_load_art(uchar flush_all);
1340     extern uchar robot_antisocial;
1341     char *buf;
1342 #endif
1343     extern void check_panel_ref(uchar puntme);
1344 
1345     if (dest_level == player_struct.level) {
1346         string_message_info(REF_STR_ElevatorSameFloor);
1347         return (FALSE);
1348     } else
1349         nuframe = (dest_level > player_struct.level);
1350 
1351     panel_loc = objs[player_struct.panel_ref].loc;
1352 
1353     oref = me_objref(MAP_GET_XY(OBJ_LOC_BIN_X(panel_loc), OBJ_LOC_BIN_Y(panel_loc)));
1354 
1355     while (oref != OBJ_REF_NULL) {
1356         ObjID id = objRefs[oref].obj;
1357 
1358         if ((objs[id].obclass == CLASS_DOOR) && (!(DOOR_REALLY_CLOSED(id)))) {
1359             // there is an open elevator in the square, so don't let the elevator go
1360             string_message_info(REF_STR_ElevatorDoorOpen);
1361             return (FALSE);
1362         }
1363         oref = objRefs[oref].next;
1364     }
1365 
1366     string_message_info(REF_STR_ElevatorMove);
1367 
1368 #ifdef ELEVATOR_PACKRAT
1369     // Fill list of elevator-contained objects
1370     compute_elev_objs(elev_obj_list);
1371     store_objects(&buf, elev_obj_list, MAX_ELEV_OBJS);
1372 #endif
1373 
1374     // Compute our offset from the panel we actually frobbed with.
1375     xdiff = panel_loc.x - player_dos_obj->loc.x;
1376     ydiff = panel_loc.y - player_dos_obj->loc.y;
1377     zdiff = panel_loc.z - player_dos_obj->loc.z;
1378 #ifdef ELEVATOR_PACKRAT
1379     for (i = 0; i < MAX_ELEV_OBJS; i++) {
1380         if (elev_obj_list[i] != OBJ_NULL) {
1381             elev_obj_diffs[i].x = panel_loc.x - objs[elev_obj_list[i]].loc.x;
1382             elev_obj_diffs[i].y = panel_loc.y - objs[elev_obj_list[i]].loc.y;
1383             elev_obj_diffs[i].z = panel_loc.z - objs[elev_obj_list[i]].loc.z;
1384         }
1385     }
1386 #endif
1387     old_zsh = MAP_ZSHF;
1388 
1389     if (full_game_3d) {
1390         render_run();
1391     }
1392 
1393     // Find what the equivalent panel on the new level is
1394     // we do this by deparsing the data stuffed into the trap data
1395     // of the panel.
1396     switch (which_panel) {
1397     case 0:
1398     case 1:
1399         new_panel = objFixtures[objs[player_struct.panel_ref].specID].p1;
1400         break;
1401     case 2:
1402     case 3:
1403         new_panel = objFixtures[objs[player_struct.panel_ref].specID].p2;
1404         break;
1405     case 4:
1406     case 5:
1407         new_panel = objFixtures[objs[player_struct.panel_ref].specID].p3;
1408         break;
1409     }
1410     if ((which_panel % 2) == 0)
1411         new_panel = new_panel >> 16;
1412     else
1413         new_panel = new_panel & 0xFFFF;
1414 
1415     objs[player_struct.panel_ref].info.current_frame = nuframe;
1416     check_panel_ref(TRUE);
1417 
1418     // Teleport the player to that level, at the same relative distance
1419     // from the new panel as to the old.
1420     begin_wait();
1421     retval = trap_teleport_func(0xF000, 0xF000, 0xF000, dest_level); // no change in x, y, or z
1422 
1423     if (retval == OK) {
1424         panel_loc = objs[new_panel].loc;
1425         objs[new_panel].info.current_frame = nuframe;
1426         newloc = player_dos_obj->loc;
1427         newloc.x = panel_loc.x - xdiff;
1428         newloc.y = panel_loc.y - ydiff;
1429 #ifdef BROKEN_CODE
1430         newloc.z = panel_loc.z - zdiff;
1431         if (MAP_ZSHF > old_zsh)
1432             newloc.z = newloc.z << (MAP_ZSHF - old_zsh);
1433         else
1434             newloc.z = newloc.z << (old_zsh - MAP_ZSHF);
1435 #endif
1436         if (MAP_ZSHF == old_zsh)
1437             newloc.z = panel_loc.z - zdiff;
1438         else if (MAP_ZSHF > old_zsh)
1439             newloc.z = panel_loc.z - (zdiff << (MAP_ZSHF - old_zsh));
1440         else
1441             newloc.z = panel_loc.z - (zdiff >> (old_zsh - MAP_ZSHF));
1442 
1443         obj_move_to(PLAYER_OBJ, &newloc, TRUE);
1444 
1445         // Clear out old cruft in the new elevator squares
1446         elevator_janitor_run();
1447 
1448 #ifdef ELEVATOR_PACKRAT
1449         // Reconsitute elevator-objects
1450         restore_objects(buf, elev_obj_list, MAX_ELEV_OBJS);
1451 
1452         // Move 'em to the right place
1453         robot_antisocial = TRUE;
1454         for (i = 0; i < MAX_ELEV_OBJS; i++) {
1455             tempid = elev_obj_list[i];
1456             if (tempid != OBJ_NULL) {
1457                 temploc = objs[tempid].loc;
1458                 temploc.x = panel_loc.x - elev_obj_diffs[i].x;
1459                 temploc.y = panel_loc.y - elev_obj_diffs[i].y;
1460                 if (MAP_ZSHF == old_zsh)
1461                     temploc.z = panel_loc.z - elev_obj_diffs[i].z;
1462                 else if (MAP_ZSHF > old_zsh)
1463                     temploc.z = panel_loc.z - (elev_obj_diffs[i].z << (MAP_ZSHF - old_zsh));
1464                 else
1465                     temploc.z = panel_loc.z - (elev_obj_diffs[i].z >> (old_zsh - MAP_ZSHF));
1466                 obj_move_to(tempid, &temploc, TRUE);
1467             }
1468         }
1469         robot_antisocial = FALSE;
1470         obj_load_art(FALSE);
1471 #endif
1472         end_wait();
1473 
1474         stop_digi_fx(); // KLC - Moved this to before the door tries to open.
1475 
1476         // open the door, unless freight elevator
1477         id = door_in_square(&panel_loc, TRUE);
1478         if (DOOR_REALLY_CLOSED(id) && !door_locked(id) && objDoors[objs[id].specID].other_half == 0) {
1479             object_use(id, FALSE, OBJ_NULL);
1480         }
1481     } else
1482         critical_error(CRITERR_FILE);
1483 #endif
1484     return (TRUE);
1485 }
1486 
obj_door_lock(ObjID door_id,uchar new_lock)1487 errtype obj_door_lock(ObjID door_id, uchar new_lock) {
1488     if (new_lock)
1489         QUESTBIT_ON(objDoors[objs[door_id].specID].locked);
1490     else
1491         QUESTBIT_OFF(objDoors[objs[door_id].specID].locked);
1492     return (OK);
1493 }
1494 
1495 void multi_anim_callback(ObjID id, intptr_t user_data);
1496 uchar in_anim_callback = FALSE;
1497 
unmulti_anim_callback(ObjID id,intptr_t user_data)1498 void unmulti_anim_callback(ObjID id, intptr_t user_data) {
1499     int orig_parm = (int)user_data;
1500     int *pp2, *pp1;
1501 
1502     if (in_anim_callback || !time_passes)
1503         return;
1504     in_anim_callback = TRUE;
1505     switch (objs[id].obclass) {
1506     case CLASS_BIGSTUFF:
1507         pp1 = &objBigstuffs[objs[id].specID].data1;
1508         pp2 = &objBigstuffs[objs[id].specID].data2;
1509         break;
1510     case CLASS_SMALLSTUFF:
1511         pp1 = &objSmallstuffs[objs[id].specID].data1;
1512         pp2 = &objSmallstuffs[objs[id].specID].data2;
1513         break;
1514     }
1515     objs[id].info.current_frame = 0;
1516     add_obj_to_animlist(id, TRUE, *pp1 & 0x2, *pp1 & 0x1, 0, 5, 0, ANIMCB_REPEAT | ANIMCB_CYCLE);
1517     *pp2 = orig_parm;
1518     in_anim_callback = FALSE;
1519 }
1520 
multi_anim_callback(ObjID id,intptr_t data)1521 void multi_anim_callback(ObjID id, intptr_t data) {
1522     int *pp2, *pp1;
1523     uchar do_swap = FALSE;
1524 
1525     if (in_anim_callback || !time_passes)
1526         return;
1527     in_anim_callback = TRUE;
1528     switch (objs[id].obclass) {
1529     case CLASS_BIGSTUFF:
1530         pp1 = &objBigstuffs[objs[id].specID].data1;
1531         pp2 = &objBigstuffs[objs[id].specID].data2;
1532         break;
1533     case CLASS_SMALLSTUFF:
1534         pp1 = &objSmallstuffs[objs[id].specID].data1;
1535         pp2 = &objSmallstuffs[objs[id].specID].data2;
1536         break;
1537     }
1538 
1539     if ((*pp1) >> 16) {
1540         // As we have other ways of determining when to switch, they can just go
1541         // into this case statement.
1542         switch ((*pp1) >> 28) {
1543         case 0:
1544             if (rand() % ((*pp1 & 0xFFF0000) >> 16) == 1)
1545                 do_swap = TRUE;
1546             break;
1547         }
1548         if (do_swap) {
1549             remove_obj_from_animlist(id);
1550             objs[id].info.current_frame = 0;
1551             add_obj_to_animlist(id, FALSE, FALSE, FALSE, 0, 4, *pp2, ANIMCB_REMOVE);
1552             *pp2 = *pp2 >> 16;
1553         }
1554     }
1555     in_anim_callback = FALSE;
1556 }
1557 
obj_screen_animate(ObjID id)1558 errtype obj_screen_animate(ObjID id) {
1559     errtype retval = OK;
1560     remove_obj_from_animlist(id);
1561     switch (objs[id].obclass) {
1562     case CLASS_BIGSTUFF:
1563         if (objBigstuffs[objs[id].specID].data2 >> 16)
1564             retval = add_obj_to_animlist(id, TRUE, objBigstuffs[objs[id].specID].data1 & 0x2,
1565                                          objBigstuffs[objs[id].specID].data1 & 0x1, 0, 5, 0,
1566                                          ANIMCB_REPEAT | ANIMCB_CYCLE);
1567         else
1568             retval = add_obj_to_animlist(id, TRUE, objBigstuffs[objs[id].specID].data1 & 0x2,
1569                                          objBigstuffs[objs[id].specID].data1 & 0x1, 0, 0, 0, 0);
1570         break;
1571     case CLASS_SMALLSTUFF:
1572         if (objSmallstuffs[objs[id].specID].data2 >> 16)
1573             retval = add_obj_to_animlist(id, TRUE, objSmallstuffs[objs[id].specID].data1 & 0x2,
1574                                          objSmallstuffs[objs[id].specID].data1 & 0x1, 0, 5, 0,
1575                                          ANIMCB_REPEAT | ANIMCB_CYCLE);
1576         else
1577             retval = add_obj_to_animlist(id, TRUE, objBigstuffs[objs[id].specID].data1 & 0x2,
1578                                          objBigstuffs[objs[id].specID].data1 & 0x1, 0, 0, 0, 0);
1579         break;
1580     }
1581     return (OK);
1582 }
1583 
1584 #define MAX_KEYPAD_DIGITS 3
1585 
obj_keypad_crunch(int p,uchar digits[MAX_KEYPAD_DIGITS])1586 uchar obj_keypad_crunch(int p, uchar digits[MAX_KEYPAD_DIGITS]) {
1587     uchar retval = TRUE;
1588     int i;
1589     short combo = qdata_get(p & 0xFFFF);
1590     ObjID id = qdata_get(p >> 16);
1591 
1592     if (combo == 0)
1593         return (FALSE);
1594     for (i = 0; i < MAX_KEYPAD_DIGITS; i++) {
1595         if (((combo >> (4 * i)) & 0xF) != digits[MAX_KEYPAD_DIGITS - 1 - i])
1596             retval = FALSE;
1597     }
1598     if (retval) {
1599         gKeypadOverride = FALSE;
1600         play_digi_fx_obj(SFX_MFD_SUCCESS, 1, id);
1601         do_multi_stuff(id);
1602     }
1603     return (retval);
1604 }
1605 
keypad_trigger(ObjID id,uchar digits[MAX_KEYPAD_DIGITS])1606 errtype keypad_trigger(ObjID id, uchar digits[MAX_KEYPAD_DIGITS]) {
1607     ObjSpecID osid = objs[id].specID;
1608     char match = -1;
1609     if ((objs[id].obclass != CLASS_FIXTURE) || (!objs[id].active)) {
1610         return (ERR_NOEFFECT);
1611     }
1612     if (obj_keypad_crunch(objFixtures[osid].p1, digits))
1613         match = 1;
1614     if (obj_keypad_crunch(objFixtures[osid].p2, digits))
1615         match = 1;
1616     if (obj_keypad_crunch(objFixtures[osid].p3, digits))
1617         match = 1;
1618     if (match == -1) {
1619         if (qdata_get(objFixtures[osid].p4 >> 16) == 0)
1620             string_message_info(REF_STR_KeypadBad);
1621         else {
1622             string_message_info(REF_STR_TrapZeroMessage + qdata_get(objFixtures[osid].p4 >> 16));
1623 #ifdef AUDIOLOGS
1624             audiolog_bark_play(qdata_get(objFixtures[osid].p4 >> 16));
1625 #endif
1626         }
1627         play_digi_fx_obj(SFX_MFD_BUZZ, 1, id);
1628         do_multi_stuff(qdata_get(objFixtures[osid].p4 & 0xFFFF));
1629     } else {
1630         if (match == CLASS_DOOR)
1631             string_message_info(REF_STR_KeypadGood);
1632     }
1633     return (OK);
1634 }
1635 
1636 // Access panels stick their door to be opened in P1
accesspanel_trigger(ObjID id)1637 errtype accesspanel_trigger(ObjID id) {
1638     TrapSchedEvent new_ev;
1639     ObjID trap;
1640     errtype err;
1641 
1642     trap = objFixtures[objs[id].specID].p1;
1643 
1644     new_ev.timestamp = TICKS2TSTAMP(player_struct.game_time) + 1;
1645     new_ev.type = TRAP_SCHED_EVENT;
1646     new_ev.target_id = trap;
1647     new_ev.source_id = -1;
1648     err = schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_ev);
1649 
1650     if (err) {
1651         // failed to schedule!  Forge ahead anyway.  As far as I know, the
1652         // worst thing that happens is your MFD state gets screwed up, and
1653         // not having a puzzle fire at all can lose you the game.
1654 
1655         do_multi_stuff(trap);
1656     }
1657 
1658     play_digi_fx_obj(SFX_PANEL_SUCCESS, 1, id);
1659     return (OK);
1660 }
1661 
1662 // Hmm, I wonder whether this could be a problem after loading a game...
1663 #define CSPACE_OBJECT_DELAY_TIME CIT_CYCLE
1664 
1665 ObjID last_obj;
1666 ulong last_obj_time;
1667 
1668 // Collision with something while in cyberspace
obj_cspace_collide(ObjID id,ObjID collider)1669 errtype obj_cspace_collide(ObjID id, ObjID collider) {
1670     extern errtype collide_objects(ObjID collision, ObjID victim, int bad);
1671     char str_buf[60], temp[20];
1672     int bigstuff_fake = 0, trip;
1673     uchar select = FALSE;
1674 
1675     if (collider != PLAYER_OBJ) {
1676         // generate a fake physics-like collision callback
1677         collide_objects(collider, id, 0);
1678         return (OK);
1679     }
1680     if (objs[id].obclass == CLASS_TRAP)
1681         return (OK);
1682     if ((last_obj == id) && (player_struct.game_time < last_obj_time))
1683         return (OK);
1684     last_obj = id;
1685     last_obj_time = player_struct.game_time + CSPACE_OBJECT_DELAY_TIME;
1686     if (ICE_ICE_BABY(id)) {
1687         if (player_struct.hud_modes & HUD_FAKEID)
1688             hud_unset(HUD_FAKEID);
1689         else {
1690             string_message_info(REF_STR_IceEncrusted);
1691             return (OK);
1692         }
1693     }
1694 #ifdef MATCHBOX_SUPPORT
1695     switch (ID2TRIP(id)) {
1696     case ARROW_TRIPLE:
1697         cspace_effect_times[CS_MATCHBOX_EFF] = player_struct.game_time + cspace_effect_durations[CS_MATCHBOX_EFF];
1698         return (OK);
1699         break;
1700     }
1701 #endif
1702     switch (objs[id].obclass) {
1703     case CLASS_FIXTURE:
1704     case CLASS_SMALLSTUFF:
1705         do_multi_stuff(id);
1706         break;
1707     case CLASS_BIGSTUFF:
1708         bigstuff_fake =
1709             MAKETRIP(CLASS_SOFTWARE, objBigstuffs[objs[id].specID].data1, objBigstuffs[objs[id].specID].data2);
1710     case CLASS_SOFTWARE:
1711     default:
1712         shameful_obselete_flag = FALSE;
1713         // if player has a valid currently selected combat soft, do not select
1714         // a new one.
1715         select = FALSE;
1716         if (bigstuff_fake != 0)
1717             trip = bigstuff_fake;
1718         else
1719             trip = ID2TRIP(id);
1720         if (TRIP2CL(trip) == CLASS_SOFTWARE && TRIP2SC(trip) == SOFTWARE_SUBCLASS_OFFENSE &&
1721             !player_struct.softs.combat[player_struct.actives[ACTIVE_COMBAT_SOFT]]) {
1722             select = TRUE;
1723         }
1724         if ((bigstuff_fake != 0) || (USE_MODE(id) == PICKUP_USE_MODE && inventory_add_object(id, select))) {
1725             ObjLocState del_loc_state;
1726             int version = 0;
1727             // yank the object out of the map.
1728             del_loc_state.obj = id;
1729             del_loc_state.loc = objs[id].loc;
1730             del_loc_state.loc.x = -1;
1731             ObjRefStateBinSetNull(del_loc_state.refs[0].bin);
1732             ObjUpdateLocs(&del_loc_state);
1733 
1734             if (bigstuff_fake != 0) {
1735                 inventory_add_object(id, select);
1736 #ifdef SWITCH_BY_COLLIDE
1737                 switch (objBigstuffs[objs[id].specID].data1) {
1738                 case SOFTWARE_SUBCLASS_OFFENSE:
1739                     player_struct.actives[ACTIVE_COMBAT_SOFT] = objBigstuffs[objs[id].specID].data2;
1740                     break;
1741                 case SOFTWARE_SUBCLASS_DEFENSE:
1742                     player_struct.actives[ACTIVE_DEFENSE_SOFT] = objBigstuffs[objs[id].specID].data2;
1743                     break;
1744                 }
1745 #endif
1746                 get_object_short_name(bigstuff_fake, str_buf, 40);
1747                 version = objBigstuffs[objs[id].specID].cosmetic_value;
1748             } else {
1749                 // set it to be the "active" object under certain circumstances
1750                 if (objs[id].obclass == CLASS_SOFTWARE) {
1751                     switch (objs[id].subclass) {
1752 #ifdef SWITCH_BY_COLLIDE
1753                     case SOFTWARE_SUBCLASS_OFFENSE:
1754                         player_struct.actives[ACTIVE_COMBAT_SOFT] = objs[id].info.type;
1755                         break;
1756                     case SOFTWARE_SUBCLASS_DEFENSE:
1757                         player_struct.actives[ACTIVE_DEFENSE_SOFT] = objs[id].info.type;
1758                         break;
1759 #endif
1760                     case SOFTWARE_SUBCLASS_DATA:
1761                         string_message_info(REF_STR_CspaceData);
1762                         return (OK);
1763                     }
1764                 }
1765                 get_object_short_name(ID2TRIP(id), str_buf, 40);
1766                 version = objSoftwares[objs[id].specID].version;
1767             }
1768             if (version && trip == GAMES_TRIPLE) {
1769                 int game = 0;
1770                 while ((version & 1) == 0)
1771                     game++, version = version >> 1;
1772                 version = 0;
1773                 sprintf(str_buf, "\"%s\" %s", (char *)RefGet(REF_STR_GameName0 + game),
1774                         (char *)RefGet(MKREF(RES_objshortnames, OPTRIP(GAMES_TRIPLE))));
1775             }
1776             if (version) {
1777                 sprintf(str_buf + strlen(str_buf), " %s%d", (char *)RefGet(REF_STR_VersionPrefix), version);
1778             }
1779             strcat(str_buf, get_string(REF_STR_CspaceAcquire, temp, 20));
1780             if (shameful_obselete_flag)
1781                 string_message_info(REF_STR_AlreadyHaveOne);
1782             else
1783                 message_info(str_buf);
1784         }
1785         break;
1786     }
1787     return (OK);
1788 }
1789