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/wares.c $
21  * $Revision: 1.109 $
22  * $Author: dc $
23  * $Date: 1994/11/22 15:59:26 $
24  *
25  */
26 
27 #include <string.h>
28 
29 #include "wares.h"
30 #include "hud.h"
31 #include "player.h"
32 #include "gamesys.h"
33 #include "sideicon.h"
34 #include "newmfd.h"
35 #include "cybstrng.h"
36 #include "gamestrn.h"
37 #include "textmaps.h"
38 #include "render.h"
39 #include "frparams.h"
40 #include "FrUtils.h"
41 #include "objsim.h"
42 #include "otrip.h"
43 #include "mainloop.h"
44 #include "gameloop.h"
45 #include "musicai.h"
46 #include "sfxlist.h"
47 #include "objbit.h"
48 #include "objprop.h"
49 #include "tools.h"
50 #include "faketime.h"
51 #include "weapons.h"
52 #include "fullscrn.h"
53 #include "mainloop.h"
54 #include "map.h"
55 #include "physics.h"
56 #include "softdef.h"
57 #include "cyber.h"
58 #include "damage.h"
59 
60 //----------------
61 //  Internal Prototypes
62 //----------------
63 uchar is_passive_hardware(int n);
64 bool is_oneshot_misc_software(int n);
65 int energy_cost(int warenum);
66 void hardware_closedown(uchar visible);
67 void hardware_startup(uchar visible);
68 void hardware_power_outage(void);
69 bool check_game(void);
70 
71 // ------
72 // Globals
73 // -------
74 
75 // forward decls for arrays at end of file
76 extern WARE HardWare[NUM_HARDWAREZ];
77 extern WARE Combat_SoftWare[NUM_COMBAT_SOFTS];
78 extern WARE Defense_SoftWare[NUM_DEFENSE_SOFTS];
79 extern WARE Misc_SoftWare[NUM_MISC_SOFTS];
80 
81 #define MAX_VERSIONS 5
82 extern short energy_cost_vec[NUM_HARDWAREZ][MAX_VERSIONS];
83 
84 long ware_base_triples[NUM_WARE_TYPES] = {
85     MAKETRIP(CLASS_HARDWARE, 0, 0),
86     MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_OFFENSE, 0),
87     MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_DEFENSE, 0),
88     MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_ONESHOT, 0),
89 };
90 
91 // The existence of this array is a crime.  I should be shot.
92 ubyte waretype2invtype[] = {
93     MFD_INV_HARDWARE,
94     MFD_INV_SOFT_COMBAT,
95     MFD_INV_SOFT_DEFENSE,
96     MFD_INV_SOFT_MISC,
97 };
98 
99 #define IDX_OF_TYPE(type, trip) (OPTRIP(trip) - OPTRIP(ware_base_triples[type]))
100 
101 #define PASSIVE_WARE_FLAG 1
102 
103 // EXTERNALS
104 // =========
105 // -------------------------------------------------------
106 // get_ware_triple() converts our stupid representation for
107 // a ware triple into the standard one.
108 
get_ware_triple(int waretype,int num)109 int get_ware_triple(int waretype, int num) { return nth_after_triple(ware_base_triples[waretype], num); }
110 
111 // ---------------------------------------------------------------------------
112 // get_ware_name()
113 //
114 // Returns the name of a ware to the inventory system.
115 
get_ware_name(int waretype,int num,char * buf,int sz)116 char *get_ware_name(int waretype, int num, char *buf, int sz) {
117     get_object_short_name(nth_after_triple(ware_base_triples[waretype], num), buf, sz);
118     return buf;
119 }
120 
is_passive_hardware(int n)121 uchar is_passive_hardware(int n) {
122     ushort cflags = (ObjProps[OPTRIP(MAKETRIP(CLASS_HARDWARE, 0, 0)) + n].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF;
123     return (cflags & PASSIVE_WARE_FLAG);
124 }
125 
is_oneshot_misc_software(int n)126 bool is_oneshot_misc_software(int n) { return ((n < NUM_ONESHOT_SOFTWARE)); }
127 
128 // INTERNALS
129 // =========
130 
energy_cost(int warenum)131 int energy_cost(int warenum) {
132     uchar version = player_struct.hardwarez[warenum];
133     if (version == 0)
134         return 0;
135     if (warenum == CPTRIP(LANTERN_HARD_TRIPLE))
136         version = LAMP_SETTING(player_struct.hardwarez_status[warenum]) + 1;
137     if (warenum == CPTRIP(SHIELD_HARD_TRIPLE))
138         version = LAMP_SETTING(player_struct.hardwarez_status[warenum]) + 1;
139     if (warenum == CPTRIP(MOTION_HARD_TRIPLE) && motionware_mode == MOTION_SKATES)
140         version = MOTION_SKATES;
141     if (warenum == CPTRIP(JET_HARD_TRIPLE))
142         return 0;
143     return energy_cost_vec[warenum][version - 1];
144 }
145 
146 // ---------------------------------------------------------------------------
147 // use_ware()
148 //
149 // Called from the UI/Inventory, this routine figures out what is being used
150 // and what function to call.  Turns things on or off as appropriate.
151 
use_ware(int waretype,int num)152 void use_ware(int waretype, int num) {
153     ubyte *player_wares, *player_status;
154     WARE *wares;
155     int n, ecost;
156     int ware_sfx = SFX_NONE, hnd;
157     //   int   i;
158     //   ubyte invtype;
159     if ((!global_fullmap->cyber != (waretype == 0))  // boolean equality, yum.
160         && !(waretype == WARE_SOFT_MISC && num == 4) // special games ware hack
161         && !(waretype == WARE_HARD && num == HARDWARE_FULLSCREEN))
162         return;
163     get_ware_pointers(waretype, &player_wares, &player_status, &wares, &n);
164     if ((player_wares[num] == 0) && (!(WareActive(player_status[num])))) {
165         return; // don't turn on a ware we don't have, only turn off one we're discarding
166     }
167 
168     // Hey, can we even use this kind of ware right now?
169     if (wares[num].check != NULL)
170         if (!wares[num].check())
171             return;
172 
173     // check to see if we have enough power
174     if (waretype == WARE_HARD && !WareActive(player_status[num]) && player_struct.energy < (energy_cost(num) + 4) / 5) {
175         string_message_info(REF_STR_WareNoPower);
176         return;
177     }
178     player_status[num] ^= WARE_ON;
179 
180     if (wares[num].sideicon != SI_NONE)
181         side_icon_expose(wares[num].sideicon);
182 
183     if (!WareActive(player_status[num])) { // we're turning a ware off
184 
185         // note that the energy_cost function may use state which is
186         // dependent on the ware being on to figure out the correct
187         // cost (e.g., motionware).
188         if (waretype == WARE_HARD)
189             ecost = energy_cost(num);
190 
191         if (wares[num].turnoff)
192             wares[num].turnoff(TRUE, TRUE);
193         switch (num) {
194         case HARDWARE_360:
195         case HARDWARE_SHIELD:
196         case HARDWARE_EMAIL:
197             break;
198         case HARDWARE_GOGGLE_INFRARED:
199             ware_sfx = SFX_VIDEO_DOWN;
200             break;
201         default:
202             ware_sfx = SFX_HUDFROB;
203             break;
204         }
205     } else { // we're turning a ware on
206 
207         // Turn on the durned thing
208         if (wares[num].turnon)
209             wares[num].turnon(TRUE, TRUE);
210 
211         // note that the energy_cost function may use state which is
212         // dependent on the ware being on to figure out the correct
213         // cost (e.g., motionware).
214         if (waretype == WARE_HARD)
215             ecost = energy_cost(num);
216 
217         if ((waretype == WARE_HARD) && (num >= FIRST_GOGGLE_WARE) && (num <= LAST_GOGGLE_WARE)) {
218             // Play the goggle sound effect
219             ware_sfx = SFX_GOGGLE;
220         } else {
221             if (num != HARDWARE_SHIELD) {
222                 ware_sfx = SFX_HUDFROB;
223             }
224         }
225 
226         //      // keep the invtype around in case we want to let mfd know about it
227         //      if      (waretype == WARE_HARD)           invtype = MFD_INV_HARDWARE;
228         //      else if (waretype == WARE_SOFT_COMBAT)    invtype = MFD_INV_SOFT_COMBAT;
229         //      else if (waretype == WARE_SOFT_DEFENSE)   invtype = MFD_INV_SOFT_DEFENSE;
230         //      else                                      invtype = MFD_INV_SOFT_MISC;
231     }
232     if (ware_sfx != SFX_NONE) {
233         extern char secret_global_pan;
234         int ci_idx = wares[num].sideicon;
235         // secret_global_pan=(ci_idx==SI_NONE)?SND_DEF_PAN:(ci_idx<5)?5:122;
236         hnd = play_digi_fx(ware_sfx, 1);
237         // secret_global_pan=SND_DEF_PAN;
238     }
239     if (waretype == WARE_HARD) {
240         short newe = player_struct.energy_spend;
241         if (WareActive(player_status[num]))
242             newe = lg_min(newe + ecost, MAX_ENERGY);
243         else
244             newe = lg_max(newe - ecost, 0);
245         set_player_energy_spend((ubyte)newe);
246     }
247     if (_current_loop <= FULLSCREEN_LOOP)
248         chg_set_flg(INVENTORY_UPDATE);
249     mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE);
250 }
251 
252 // Hey, we're closing down a game. return to normalcy.
hardware_closedown(uchar visible)253 void hardware_closedown(uchar visible) {
254     for (uint16_t i = 0; i < NUM_HARDWAREZ; i++) {
255         // if (i != HARDWARE_FULLSCREEN)
256         if (WareActive(player_struct.hardwarez_status[i]))
257             if (HardWare[i].turnoff != NULL)
258                 HardWare[i].turnoff(visible, FALSE);
259     }
260 }
261 
hardware_startup(uchar visible)262 void hardware_startup(uchar visible) {
263     for (uint16_t i = 0; i < NUM_HARDWAREZ; i++) {
264         // if (i != HARDWARE_FULLSCREEN)
265         if (WareActive(player_struct.hardwarez_status[i]))
266             if (HardWare[i].turnon != NULL)
267                 HardWare[i].turnon(visible, FALSE);
268     }
269 }
270 
hardware_power_outage(void)271 void hardware_power_outage(void) {
272     for (uint16_t i = 0; i < NUM_HARDWAREZ; i++) {
273         if (energy_cost(i) > 0 && WareActive(player_struct.hardwarez_status[i]))
274             use_ware(WARE_HARD, i);
275     }
276     if (WareActive(player_struct.hardwarez_status[CPTRIP(JET_HARD_TRIPLE)]))
277         use_ware(WARE_HARD, CPTRIP(JET_HARD_TRIPLE));
278 }
279 
280 // ---------------------------------------------------------------------------
281 // get_ware_pointers()
282 //
283 // Sets a number of pointers to point at the appropriate ware structures
284 // for a given type.
285 
get_ware_pointers(int type,ubyte ** player_wares,ubyte ** player_status,WARE ** wares,int * n)286 void get_ware_pointers(int type, ubyte **player_wares, ubyte **player_status, WARE **wares, int *n) {
287     if (type == WARE_HARD) {
288         *n = NUM_HARDWAREZ;
289         *player_wares = player_struct.hardwarez;
290         *player_status = player_struct.hardwarez_status;
291         *wares = HardWare;
292     }
293 
294     else if (type == WARE_SOFT_COMBAT) {
295         *n = NUM_COMBAT_SOFTS;
296         *player_wares = player_struct.softs.combat;
297         *player_status = player_struct.softs_status.combat;
298         *wares = Combat_SoftWare;
299     } else if (type == WARE_SOFT_DEFENSE) {
300         *n = NUM_DEFENSE_SOFTS;
301         *player_wares = player_struct.softs.defense;
302         *player_status = player_struct.softs_status.defense;
303         *wares = Defense_SoftWare;
304     } else {
305         *n = NUM_MISC_SOFTS;
306         *player_wares = player_struct.softs.misc;
307         *player_status = player_struct.softs_status.misc;
308         *wares = Misc_SoftWare;
309     }
310 
311 }
312 
313 // --------------------------------------------------
314 //
315 // get_player_ware_version returns the version number
316 // of a ware in the player's inventory.  zero means
317 // the player doesn't have it.
318 
get_player_ware_version(int type,int n)319 int get_player_ware_version(int type, int n) {
320     WARE *Pwares;
321     ubyte *pver;
322     ubyte *pstat;
323     int num;
324     get_ware_pointers(type, &pver, &pstat, &Pwares, &num);
325     return pver[n];
326 }
327 
328 // ---------------------------------------------------------------------------
329 // wares_update()
330 //
331 // Called from the main loop, this routine cycles through all active wares
332 // and sees if any need attention.
333 
wares_update()334 void wares_update() {
335     ubyte *player_status, *player_wares;
336     WARE *wares;
337     int i, j, n;
338 
339     if ((player_struct.game_time - player_struct.last_ware_update) >= WARE_UPDATE_FREQ) {
340 
341         player_struct.last_ware_update = player_struct.game_time;
342 
343         // Iterate through all types of wares...
344 
345         // NOTE: At some point, we may want to differentiate here between
346         // wares that get updated in the cyberloop as opposed to the
347         // real world.  For now, we leave it all mashed together.
348 
349         for (i = 0; i < NUM_WARE_TYPES; i++) {
350 
351             get_ware_pointers(i, &player_wares, &player_status, &wares, &n);
352 
353             // Now we know what type of ware we're looking at.
354             // Look at all wares of this type.
355 
356             for (j = 0; j < n; j++) {
357 
358                 // Is it active? If so...
359                 if (WareActive(player_status[j])) {
360 
361                     // Does it have a continually active effect?
362                     if (wares[j].effect)
363                         wares[j].effect();
364                 }
365             }
366 
367             // We're ready to look at the next type of ware.
368         }
369     }
370     for (j = 0; j < NUM_HARDWAREZ; j++)
371         if (player_struct.hardwarez_status[j] & WARE_FLASH) {
372             side_icon_expose(HardWare[j].sideicon);
373         }
374 
375     }
376 
377 // ---------------------------------------------------------------------------
378 // wares_init()
379 //
380 // Sets the static values for all wares.
381 
wares_init()382 void wares_init() { }
383 
384 // CALLBACKS
385 // =========
386 
387 // ---------------------------------------------------------------------------
388 // wares_dummy_func()
389 //
390 // Temporary dummy function for all wares callbacks.
391 
392 // void wares_dummy_func()
393 //{
394 //   return;
395 //}
396 
397 // ----------
398 // * BIO WARE
399 // ----------
400 void bioware_turnon(uchar visible, uchar real_start);
401 void bioware_turnoff(uchar visible, uchar real_stop);
402 void bioware_effect(void);
403 
404 // ---------------------------------------------------------------------------
405 // bioware_turnon()
406 //
407 // Let the MFD system know that the bioware is active, and take over
408 // the appropriate info MFD
409 
bioware_turnon(uchar visible,uchar real_s)410 void bioware_turnon(uchar visible, uchar real_s) {
411     if (visible) {
412         mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, TRUE, MFD_FLASH, TRUE);
413         int32_t i = mfd_grab_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT);
414         mfd_change_slot(i, MFD_INFO_SLOT);
415     }
416 
417 }
418 
419 // ---------------------------------------------------------------------------
420 // bioware_turnoff()
421 //
422 // Let the MFD system know that the bioware is deactivated, and toss it off
423 // the info slot, replacing it with a blank.
424 
bioware_turnoff(uchar visible,uchar real_stop)425 void bioware_turnoff(uchar visible, uchar real_stop) {
426     if (real_stop && player_struct.mfd_all_slots[MFD_INFO_SLOT] == MFD_BIOWARE_FUNC)
427         mfd_notify_func(MFD_EMPTY_FUNC, MFD_INFO_SLOT, TRUE, MFD_EMPTY, TRUE);
428 }
429 
430 // ---------------------------------------------------
431 // bioware_effect()
432 //
433 // updates the mfd.
434 
bioware_effect(void)435 void bioware_effect(void) { mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); }
436 
437 // ---------------
438 // * INFRARED WARE
439 // ---------------
440 void infrared_turnon(uchar visible, uchar real_start);
441 void infrared_turnoff(uchar visible, uchar real_start);
442 
443 extern char curr_clut_table;
444 // ---------------------------------------------------------------------------
445 // infrared_turnon()
446 //
447 // Turns on the infrared ware
infrared_turnon(uchar visible,uchar real_s)448 void infrared_turnon(uchar visible, uchar real_s) {
449     if (visible) {
450         gr_set_light_tab(bw_shading_table);
451         curr_clut_table = 1;
452         chg_set_flg(_current_3d_flag);
453         hud_set(HUD_INFRARED);
454     }
455 }
456 
457 // ---------------------------------------------------------------------------
458 // infrared_turnoff()
459 //
460 // Turns off the infrared ware
461 
infrared_turnoff(uchar visible,uchar real_s)462 void infrared_turnoff(uchar visible, uchar real_s) {
463     if (visible) {
464         gr_set_light_tab(shading_table);
465         chg_set_flg(_current_3d_flag);
466         curr_clut_table = 0;
467 
468         hud_unset(HUD_INFRARED);
469     }
470 }
471 
472 // --------------------
473 // * TARGETING WARE
474 // --------------------
475 void targeting_turnon(uchar visible, uchar real_start);
476 void targeting_turnoff(uchar visible, uchar real_start);
477 
478 // ---------------------------------------------------------------------------
479 // targeting_turnon()
480 //
481 // Turn on the targeting ware
482 
targeting_turnon(uchar visible,uchar real_start)483 void targeting_turnon(uchar visible, uchar real_start) {
484     extern void select_closest_target();
485 
486     player_struct.hardwarez_status[CPTRIP(TARG_GOG_TRIPLE)] &= ~WARE_ON;
487     if (visible && real_start) {
488         if (player_struct.curr_target == OBJ_NULL)
489             select_closest_target();
490         mfd_change_slot(mfd_grab_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT), MFD_TARGET_SLOT);
491     }
492 }
493 
494 // ---------------------------------------------------------------------------
495 // targeting_turnoff()
496 //
497 // Turn off the targeting ware
498 
targeting_turnoff(uchar visible,uchar real_s)499 void targeting_turnoff(uchar visible, uchar real_s) { }
500 
501 // -------------------------------------------------------
502 //  LANTERN WARE
503 // ---------------
504 void lamp_set_vals(void);
505 void lamp_set_vals_with_offset(byte offset);
506 void lamp_turnon(uchar visible, uchar real_start);
507 void lamp_change_setting(byte offset);
508 void lamp_turnoff(uchar visible, uchar real_stop);
509 uchar lantern_change_setting_hkey(ushort keycode, uint32_t context, intptr_t data);
510 
511 struct _lampspec {
512     int rad1;
513     int base1;
514     int rad2;
515     int base2;
516     fix slope;
517     fix yint;
518 } lamp_specs[] = {
519     {0, 10, 5, 0, -2 * FIX_UNIT, 12 * FIX_UNIT},
520     {1, 20, 6, 0, -4 * FIX_UNIT, 24 * FIX_UNIT},
521     {1, 18, 7, 0, -3 * FIX_UNIT, 21 * FIX_UNIT},
522     {1, 14, 8, 0, -2 * FIX_UNIT, 16 * FIX_UNIT}
523 };
524 
525 // other oldest lowest value
526 //   {  0,15,5,0,-3*FIX_UNIT,18*FIX_UNIT},
527 // old lowest value (old 0)
528 //   {  0,8,4,0,-2*FIX_UNIT,8*FIX_UNIT},
529 // is level 3 above ever used?
530 
531 extern uchar muzzle_fire_light;
532 
lamp_set_vals(void)533 void lamp_set_vals(void) { lamp_set_vals_with_offset(0); }
534 
535 #define OFF_SHF 3
lamp_set_vals_with_offset(byte offset)536 void lamp_set_vals_with_offset(byte offset) {
537     int n = IDX_OF_TYPE(WARE_HARD, LANTERN_HARD_TRIPLE), s;
538     struct _lampspec *lspec;
539 
540     s = (muzzle_fire_light) ? LAMP_SETTING(player_struct.light_value) : LAMP_SETTING(player_struct.hardwarez_status[n]);
541     lspec = &lamp_specs[s];
542 
543     _frp.lighting.yint = lspec->yint + (offset << (16 - OFF_SHF));
544     _frp.lighting.slope = lspec->slope;
545     _frp.lighting.rad[0] = (uchar)lspec->rad1;
546     _frp.lighting.base[0] =
547         (uchar)((lspec->base1 + (offset >> OFF_SHF) > 0) ? (lspec->base1 + (offset >> OFF_SHF)) : 0);
548     if (offset != 0) {
549         fix slope_based_mod;
550         slope_based_mod = fix_div((offset << (16 - OFF_SHF)), -lspec->slope);
551         _frp.lighting.rad[1] = (uchar)(lspec->rad2 + (slope_based_mod >> 16));
552         //      slope_based_mod=fix_mul((offset<<(16-OFF_SHF)),-lspec->slope);
553         _frp.lighting.yint += -lspec->slope;
554         _frp.lighting.rad[0]++;
555     } else
556         _frp.lighting.rad[1] = (uchar)lspec->rad2;
557     _frp.lighting.base[1] = (uchar)lspec->base2;
558 
559     chg_set_flg(_current_3d_flag);
560     //   Warning(("New parms %x %x, %x %x, line %x %x from %x %x\n",
561     //      _frp.lighting.rad[0], _frp.lighting.base[0],
562     //      _frp.lighting.rad[1], _frp.lighting.base[1],
563     //      _frp.lighting.yint,   _frp.lighting.slope,offset,s));
564 }
565 
lamp_turnon(uchar visible,uchar real_s)566 void lamp_turnon(uchar visible, uchar real_s) {
567     lamp_set_vals();
568     if (visible) {
569         _frp_light_bits_set(LIGHT_BITS_CAM);
570         mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
571     }
572 }
573 
lamp_change_setting(byte offset)574 void lamp_change_setting(byte offset) {
575     lamp_set_vals_with_offset(offset);
576     _frp_light_bits_set(LIGHT_BITS_CAM);
577 }
578 
lamp_turnoff(uchar visible,uchar real_stop)579 void lamp_turnoff(uchar visible, uchar real_stop) {
580     if (visible) {
581         _frp_light_bits_clear(LIGHT_BITS_CAM);
582         chg_set_flg(_current_3d_flag);
583         if (real_stop)
584             mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
585     }
586 }
587 
lantern_change_setting_hkey(ushort key,uint32_t context,intptr_t data)588 uchar lantern_change_setting_hkey(ushort key, uint32_t context, intptr_t data) {
589     int n = CPTRIP(LANTERN_HARD_TRIPLE);
590     int v = player_struct.hardwarez[n];
591     uint32_t s = player_struct.hardwarez_status[n];
592     uchar on = s & WARE_ON;
593     void mfd_lantern_setting(int setting);
594 
595     s = LAMP_SETTING(s);
596     if (s == 0 && on) {
597         use_ware(WARE_HARD, n);
598         mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
599         return TRUE;
600     }
601 
602     s = (s + v - 1) % v; // decrement current setting
603     mfd_lantern_setting(s);
604 
605     if (!on)
606         use_ware(WARE_HARD, n);
607     mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
608 
609     return TRUE;
610 }
611 
612 //--------------------------
613 // SHIELD WARE
614 //--------------------------
615 void shield_set_absorb(void);
616 void shield_toggle(uchar visible, uchar real);
617 uchar shield_change_setting_hkey(ushort keycode, uint32_t context, intptr_t data);
618 
619 #define SHIELD_IDX (CPTRIP(SHIELD_HARD_TRIPLE))
620 
621 ubyte shield_absorb_rates[] = {20, 40, 75, 75};
622 ubyte shield_thresholds[] = {0, 10, 15, 30};
623 
624 extern void set_shield_raisage(uchar going_up);
625 
shield_set_absorb(void)626 void shield_set_absorb(void) {
627     ubyte s = player_struct.hardwarez_status[SHIELD_IDX];
628     if (s & WARE_ON) {
629         player_struct.shield_absorb_rate = shield_absorb_rates[LAMP_SETTING(s)];
630         player_struct.shield_threshold = shield_thresholds[LAMP_SETTING(s)];
631     } else {
632         player_struct.shield_absorb_rate = 0;
633         player_struct.shield_threshold = 0;
634     }
635 }
636 
shield_toggle(uchar visible,uchar real)637 void shield_toggle(uchar visible, uchar real) {
638     ubyte s = player_struct.hardwarez_status[SHIELD_IDX];
639     if (real) {
640         if (s & WARE_ON) {
641             set_shield_raisage(TRUE);
642             play_digi_fx(SFX_SHIELD_UP, 1);
643         } else {
644             set_shield_raisage(FALSE);
645             play_digi_fx(SFX_SHIELD_DOWN, 1);
646         }
647         mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
648     }
649     shield_set_absorb();
650 }
651 
shield_change_setting_hkey(ushort key,uint32_t context,intptr_t data)652 uchar shield_change_setting_hkey(ushort key, uint32_t context, intptr_t data) {
653     int n = CPTRIP(SHIELD_HARD_TRIPLE);
654     int v = player_struct.hardwarez[n];
655     uint32_t s = player_struct.hardwarez_status[n];
656     uchar on = s & WARE_ON;
657     void mfd_shield_setting(int setting);
658 
659     // version 4 has only one setting.
660     if (v == 4)
661         v = 1;
662 
663     s = LAMP_SETTING(s);
664     if (s == 0 && on) {
665         use_ware(WARE_HARD, n);
666         mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
667         return TRUE;
668     }
669 
670     s = (s + v - 1) % v; // decrement current setting
671     mfd_shield_setting(s);
672 
673     if (!on)
674         use_ware(WARE_HARD, n);
675     mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
676 
677     return TRUE;
678 }
679 
680 //----------------
681 // NAV WARE
682 //----------------
683 void nav_turnon(uchar visible, uchar real_start);
684 void nav_turnoff(uchar visible, uchar real_start);
685 
nav_turnon(uchar visible,uchar real_s)686 void nav_turnon(uchar visible, uchar real_s) {
687     if (visible)
688         hud_set(HUD_COMPASS);
689 }
690 
nav_turnoff(uchar visible,uchar real_s)691 void nav_turnoff(uchar visible, uchar real_s) {
692     if (visible) {
693         hud_unset(HUD_COMPASS);
694     }
695 }
696 
697 //----------------------
698 // MOTION WARE
699 //----------------------
700 void motionware_update(uchar visible, uchar real, uchar on);
701 void motionware_turnon(uchar visible, uchar real);
702 void motionware_turnoff(uchar visible, uchar real);
703 
704 ubyte motionware_mode = MOTION_INACTIVE;
705 
706 #define MOTION_SETTING LAMP_SETTING
707 
motionware_update(uchar visible,uchar real,uchar on)708 void motionware_update(uchar visible, uchar real, uchar on) {
709     ubyte s = player_struct.hardwarez_status[CPTRIP(MOTION_HARD_TRIPLE)];
710     if (on)
711         motionware_mode = MOTION_SETTING(s) + 1;
712     else
713         motionware_mode = MOTION_INACTIVE;
714     if (visible) {
715         Pelvis elvis;
716         mfd_notify_func(MFD_MOTION_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE);
717         EDMS_get_pelvis_parameters(PLAYER_PHYSICS, &elvis);
718         if (motionware_mode == MOTION_SKATES) {
719             if (!global_fullmap->cyber) {
720                 elvis.cyber_space = PELVIS_MODE_SKATES;
721             }
722         } else {
723             if (!global_fullmap->cyber) {
724                 elvis.cyber_space = PELVIS_MODE_NORMAL;
725             }
726         }
727         EDMS_set_pelvis_parameters(PLAYER_PHYSICS, &elvis);
728     }
729 }
730 
motionware_turnon(uchar visible,uchar real)731 void motionware_turnon(uchar visible, uchar real) { motionware_update(visible, real, TRUE); }
732 
motionware_turnoff(uchar visible,uchar real)733 void motionware_turnoff(uchar visible, uchar real) { motionware_update(visible, real, FALSE); }
734 
735 // ---------------------
736 //    JUMP JET WARE
737 // ---------------------
738 void activate_jumpjets(fix *xcntl, fix *ycntl, fix *zcntl);
739 
740 static short jumpjet_controls[] = {-25, -50, -75};
741 static fix jumpjet_thrust_scales[] = {FIX_UNIT / 64, FIX_UNIT / 32, FIX_UNIT / 16};
742 
743 uchar jumpjets_active = FALSE;
744 
745 // modifies z control based on jumpject ware.
activate_jumpjets(fix * xcntl,fix * ycntl,fix * zcntl)746 void activate_jumpjets(fix *xcntl, fix *ycntl, fix *zcntl) {
747     int ecost;
748     short edrain;
749 
750     ubyte n = CPTRIP(JET_HARD_TRIPLE);
751     ubyte v = player_struct.hardwarez[n];
752     ubyte s = player_struct.hardwarez_status[n];
753 
754     jumpjets_active = FALSE;
755     if ((s & WARE_ON) == 0 || player_struct.energy == 0)
756         return;
757     ecost = energy_cost_vec[n][v - 1] * player_struct.deltat + player_struct.jumpjet_energy_fraction;
758     player_struct.jumpjet_energy_fraction = ecost % APPROX_CIT_CYCLE_HZ;
759     ecost /= APPROX_CIT_CYCLE_HZ;
760     edrain = drain_energy(ecost);
761     *zcntl = fix_make(jumpjet_controls[v - 1], 0);
762     if (edrain < ecost)
763         *zcntl = (*zcntl) * edrain / ecost;
764     *ycntl = fix_mul(*ycntl, jumpjet_thrust_scales[v - 1]);
765     *xcntl = 0;
766     jumpjets_active = TRUE;
767 }
768 
769 //-----------------------
770 //   FULLSCREEN WARE
771 //-----------------------
772 void fullscreen_turnon(uchar visible, uchar real_start);
773 void fullscreen_turnoff(uchar visible, uchar real_start);
774 extern bool DoubleSize;
775 
fullscreen_turnon(uchar visible,uchar real_s)776 void fullscreen_turnon(uchar visible, uchar real_s) {
777     if (visible) {
778         _new_mode = FULLSCREEN_LOOP;
779         chg_set_flg(GL_CHG_LOOP);
780     }
781 }
782 
fullscreen_turnoff(uchar visible,uchar real_s)783 void fullscreen_turnoff(uchar visible, uchar real_s) {
784     if (visible) {
785         _new_mode = GAME_LOOP;
786         chg_set_flg(GL_CHG_LOOP);
787     }
788 }
789 
790 //-----------------------
791 //   CYBERSPACE ONESHOTS
792 //-----------------------
793 void do_turbo_stuff(uchar from_drug);
794 void turbo_turnon(uchar visible, uchar real_start);
795 void turbo_turnoff(uchar visible, uchar real_start);
796 void fakeid_turnon(uchar visible, uchar real_start);
797 void decoy_turnon(uchar visible, uchar real_start);
798 void decoy_turnoff(uchar visible, uchar real_stop);
799 void recall_turnon(uchar visible, uchar real_start);
800 
do_turbo_stuff(uchar from_drug)801 void do_turbo_stuff(uchar from_drug) {
802     if (cspace_effect_times[CS_TURBO_EFF] == 0) {
803         if (from_drug) {
804             hud_set(HUD_TURBO);
805             chg_set_flg(INVENTORY_UPDATE);
806         }
807     }
808 }
809 
turbo_turnon(uchar visible,uchar real_start)810 void turbo_turnon(uchar visible, uchar real_start) {
811     ulong hammer_time = cspace_effect_durations[CS_TURBO_EFF];
812     do_turbo_stuff(visible);
813     if (real_start) {
814         play_digi_fx(SFX_TURBO, 1);
815         player_struct.softs.misc[SOFTWARE_TURBO]--;
816         cspace_effect_times[CS_TURBO_EFF] = player_struct.game_time + hammer_time;
817     }
818 }
819 
turbo_turnoff(uchar visible,uchar real_s)820 void turbo_turnoff(uchar visible, uchar real_s) {
821     if (visible) {
822         hud_unset(HUD_TURBO);
823     }
824     cspace_effect_times[CS_TURBO_EFF] = 0;
825 }
826 
fakeid_turnon(uchar visible,uchar real_start)827 void fakeid_turnon(uchar visible, uchar real_start) {
828     if (!(player_struct.hud_modes & HUD_FAKEID) && visible) {
829         if (real_start) {
830             player_struct.softs.misc[SOFTWARE_FAKEID]--;
831             play_digi_fx(SFX_FAKEID, 1);
832         }
833         hud_set(HUD_FAKEID);
834         chg_set_flg(INVENTORY_UPDATE);
835     }
836 }
837 
decoy_turnon(uchar visible,uchar real_start)838 void decoy_turnon(uchar visible, uchar real_start) {
839     if (real_start) {
840         if (cspace_decoy_obj != OBJ_NULL)
841             decoy_turnoff(TRUE, TRUE);
842         cspace_decoy_obj = obj_create_base(TARGET_TRIPLE);
843         if (cspace_decoy_obj != OBJ_NULL) {
844             obj_move_to(cspace_decoy_obj, &objs[PLAYER_OBJ].loc, FALSE);
845             cspace_effect_times[CS_DECOY_EFF] = player_struct.game_time + cspace_effect_durations[CS_DECOY_EFF];
846             player_struct.softs.misc[SOFTWARE_DECOY]--;
847             play_digi_fx(SFX_DECOY, 1);
848             hud_set(HUD_DECOY);
849             chg_set_flg(INVENTORY_UPDATE);
850         }
851     }
852 }
853 
decoy_turnoff(uchar visible,uchar real_stop)854 void decoy_turnoff(uchar visible, uchar real_stop) {
855     if (visible) {
856         hud_unset(HUD_DECOY);
857     }
858     cspace_effect_times[CS_DECOY_EFF] = 0;
859     if (real_stop) {
860         if (cspace_decoy_obj != OBJ_NULL)
861             ADD_DESTROYED_OBJECT(cspace_decoy_obj);
862     }
863     cspace_decoy_obj = OBJ_NULL;
864 }
865 
recall_turnon(uchar visible,uchar real_start)866 void recall_turnon(uchar visible, uchar real_start) {
867     if (visible && real_start) {
868         player_struct.softs.misc[SOFTWARE_RECALL]--;
869         obj_move_to(PLAYER_OBJ, &recall_objloc, TRUE);
870         chg_set_flg(INVENTORY_UPDATE);
871         play_digi_fx(SFX_RECALL, 1);
872     }
873 }
874 
875 // =================
876 // THE STATIC ARRAYS
877 
878 extern void view360_turnon(uchar visible, uchar real_start), view360_turnoff(uchar visible, uchar real_start);
879 extern bool view360_check();
880 extern void email_turnon(uchar visible, uchar real_start);
881 extern void email_turnoff(uchar visible, uchar real_start);
882 extern void plotware_turnon(uchar visible, uchar real_start);
883 
884 WARE HardWare[NUM_HARDWAREZ] = {
885     //"infrared"
886     {WARE_FLAGS_NONE, SI_SIXTH, infrared_turnon, NULL, infrared_turnoff, NULL},
887     //"target info"
888     {WARE_FLAGS_NONE, SI_NONE, targeting_turnon, NULL, targeting_turnoff, NULL},
889     //"360 view"
890     {WARE_FLAGS_NONE, SI_THIRD, view360_turnon, NULL, view360_turnoff, view360_check},
891     //"aim"
892     {WARE_FLAGS_NONE, SI_NONE, NULL, NULL, NULL, NULL},
893     //"HUD"
894     {WARE_FLAGS_NONE, SI_NONE, NULL, NULL, NULL, NULL},
895     //"bioscan"
896     {WARE_FLAGS_NONE, SI_FIRST, bioware_turnon, bioware_effect, bioware_turnoff, NULL},
897     //"nav unit"
898     {WARE_FLAGS_NONE, SI_SEVENTH, nav_turnon, NULL, nav_turnoff, NULL},
899     //"shield"
900     {WARE_FLAGS_NONE, SI_FIFTH, shield_toggle, NULL, shield_toggle, NULL},
901     //"data reader"
902     {WARE_FLAGS_NONE, SI_EIGHTH, email_turnon, NULL, email_turnoff, NULL},
903     //"lantern"
904     {WARE_FLAGS_NONE, SI_FOURTH, lamp_turnon, NULL, lamp_turnoff, NULL},
905     //"fullscreen"
906     {WARE_FLAGS_NONE, SI_SECOND, fullscreen_turnon, NULL, fullscreen_turnoff, NULL},
907     //"enviro-suit"
908     {WARE_FLAGS_NONE, SI_NONE, NULL, NULL, NULL, NULL},
909     //"motion"
910     {WARE_FLAGS_NONE, SI_NINTH, motionware_turnon, NULL, motionware_turnoff, NULL},
911     //"skates"
912     {WARE_FLAGS_NONE, SI_TENTH, NULL, NULL, NULL, NULL},
913     //"status"
914     {WARE_FLAGS_NONE, SI_NONE, plotware_turnon, NULL, NULL, NULL},
915 };
916 
917 // Except for jumpjets, these costs are in points per
918 // minute of use.
919 
920 short energy_cost_vec[NUM_HARDWAREZ][MAX_VERSIONS] = {
921     //"infrared"
922     {50, 50, 50},
923     //"target info"
924     {0},
925     //"360 view"
926     {9, 9, 9},
927     //"aim"
928     {0},
929     //"HUD"
930     {0},
931     //"bioscan"
932     {1},
933     //"nav unit"
934     {0},
935     //"shield"
936     {24, 60, 105, 30},
937     //"data reader"
938     {0},
939     //"lantern"
940     //   { 15, 25, 30, },
941     {10, 25, 30},
942     //"robo comm"
943     {0},
944     //"enviro-suit"
945     {0},
946     //"motion"
947     {0, 40},
948     //"jumpjets"
949     // these are in points per second of thrust
950     {25, 30, 35},
951     //"status"
952     {0},
953 };
954 
check_game(void)955 bool check_game(void) { return (!global_fullmap->cyber); }
956 extern void mfd_games_turnon(uchar, uchar real_s), mfd_games_turnoff(uchar, uchar real_s);
957 
958 WARE Combat_SoftWare[NUM_COMBAT_SOFTS];
959 WARE Defense_SoftWare[NUM_DEFENSE_SOFTS];
960 WARE Misc_SoftWare[NUM_MISC_SOFTS] = {
961     {WARE_FLAGS_NONE, SI_NONE, turbo_turnon, NULL, NULL, NULL},
962     {WARE_FLAGS_NONE, SI_NONE, fakeid_turnon, NULL, NULL, NULL},
963     {WARE_FLAGS_NONE, SI_NONE, decoy_turnon, NULL, decoy_turnoff, NULL},
964     {WARE_FLAGS_NONE, SI_NONE, recall_turnon, NULL, NULL, NULL},
965     {WARE_FLAGS_NONE, SI_NONE, mfd_games_turnon, NULL, mfd_games_turnoff, check_game},
966 };
967