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