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/mfdfunc.c $
21  * $Revision: 1.237 $
22  * $Author: mahk $
23  * $Date: 1994/11/23 20:34:20 $
24  *
25  */
26 #define __MFDFUNC_SRC
27 
28 // Source code for all MFD Expose/Handler function pairs
29 // This file is for callbacks only, actual infrastructure belongs
30 // in newmfd.c
31 
32 #include <string.h>
33 
34 #include "objprop.h" // temp
35 #include "tools.h"
36 #include "colors.h"
37 #include "mainloop.h"
38 #include "gameloop.h"
39 #include "mfdart.h"
40 #include "gamescr.h"
41 //¥¥#include "anim.h"
42 //¥¥#include "animreg.h"
43 #include "objwarez.h"
44 #include "objsim.h"
45 #include "gamestrn.h"
46 #include "cybstrng.h"
47 #include "mfdint.h"
48 #include "mfdext.h"
49 #include "mfddims.h"
50 #include "player.h"
51 #include "wares.h"
52 #include "drugs.h"
53 #include "weapons.h"
54 #include "automap.h"
55 #include "target.h"
56 #include "criterr.h"
57 #include "mfdgadg.h"
58 #include "objclass.h"
59 #include "otrip.h"
60 #include "citres.h"
61 #include "objuse.h"
62 #include "sfxlist.h"
63 #include "musicai.h"
64 #include "sideicon.h"
65 #include "hud.h"
66 #include "textmaps.h"
67 #include "fullscrn.h"
68 #include "objbit.h"
69 #include "limits.h"
70 #include "mapflags.h"
71 #include "input.h"
72 #include "cit2d.h"
73 #include "gr2ss.h"
74 #include "mfdgames.h"
75 #include "shodan.h"
76 
77 #define MFD_SHIELD_FUNC 19
78 
79 // ------------
80 // Useful Defines
81 // ------------
82 
83 #define OVERLOAD_BUTTON_Y (MFD_VIEW_HGT - 24)
84 #define TEMPR_X 47
85 #define TEMPR_Y 27
86 #define TEMPR_WIDTH 13
87 #define TEMPR_HEIGHT 16
88 #define TEMPR_DIV 8
89 #define SETTING_TEXT 46
90 #define ENERGY_TEXT_LEN 40
91 
92 static uchar in_or_out = FALSE;
93 
94 extern void mouse_unconstrain(void);
95 extern void mfd_ammo_expose(ubyte control);
96 extern uchar mfd_ammo_handler(MFD *m, uiEvent *ev);
97 
98 #define LNAME_BUFSIZE 128
99 
100 #define GOOD_RED (RED_BASE + 5)
101 #define ITEM_COLOR (0x5A)
102 #define SELECTED_ITEM_COLOR (0x4C)
103 #define UNAVAILABLE_ITEM_COLOR (0x60)
104 #define X_MARGIN 1
105 #define Y_STEP 5
106 
107 extern void check_panel_ref(uchar punt);
108 
109 #define PUSH_CANVAS(x) gr_push_canvas(x)
110 #define POP_CANVAS() gr_pop_canvas()
111 
112 #define MFD_REGION(m) ((full_game_3d) ? &(m)->reg2 : &(m)->reg)
113 
114 // -------
115 // Globals
116 // -------
117 
118 // Forward declaration of array at bottom of file
119 
120 extern void lamp_set_vals(void);
121 extern uchar full_game_3d;
122 
123 LGRegion *mfd_regions[NUM_MFDS];
124 
125 // ----------------
126 // Local Prototypes
127 // ----------------
128 
129 void mfd_clear_view(void);
130 int mfd_bmap_id(int triple);
131 void draw_blank_mfd(void);
132 void draw_mfd_item_spew(Ref id, int n);
133 
134 errtype mfd_item_init(MFD_Func *mfd);
135 void mfd_expose_blank(MFD *m, ubyte control);
136 void mfd_item_expose(MFD *m, ubyte control);
137 uchar mfd_item_handler(MFD *m, uiEvent *e);
138 void mfd_item_micro_expose(uchar full, int triple);
139 void mfd_item_micro_hires_expose(uchar full, int triple);
140 
141 void mfd_general_inv_expose(MFD *m, ubyte control, ObjID id, uchar full);
142 uchar mfd_general_inv_handler(MFD *m, uiEvent *ev, int row);
143 
144 uchar mfd_lantern_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data);
145 void mfd_lantern_setting(int setting);
146 errtype mfd_lanternware_init(MFD_Func *f);
147 void mfd_lanternware_expose(MFD *mfd, ubyte control);
148 
149 void draw_ammo_button(int triple, short x, short y);
150 
151 errtype mfd_anim_init();
152 void mfd_anim_expose(MFD *m, ubyte control);
153 
154 errtype mfd_weapon_init(MFD_Func *mfd);
155 void weapon_mfd_for_reload(void);
156 void mfd_weapon_expose(MFD *m, ubyte control);
157 uchar mfd_weapon_handler(MFD *m, uiEvent *e);
158 uchar mfd_weapon_beam_handler(MFD *m, uiEvent *e);
159 uchar mfd_weapon_projectile_handler(MFD *m, uiEvent *e, weapon_slot *ws);
160 uchar mfd_weapon_expose_projectile(MFD *m, weapon_slot *ws, ubyte control);
161 void mfd_weapon_expose_beam(weapon_slot *ws, ubyte id, uchar Redraw);
162 void mfd_weapon_draw_temp(ubyte temp);
163 void mfd_weapon_draw_ammo_buttons(int num_ammo_buttons, int ammo_subclass, ubyte *ammo_types, ubyte curr_ammo_type,
164                                   int ammo_count);
165 void mfd_weapon_draw_beam_status_bar(int charge, int setting, uchar does_overload);
166 
167 void mfd_setup_keypad(char special);
168 
169 uchar weapon_mfd_temp;
170 
171 void mfd_bioware_expose(MFD *m, ubyte control);
172 
173 // ------------
174 // USEFUL STUFF
175 // ------------
176 
mfd_clear_view(void)177 void mfd_clear_view(void) {
178     if (full_game_3d)
179         return;
180     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
181     ss_bitmap(&mfd_background, 0, 0);
182     // gr_bitmap(&mfd_background, 0, 0);
183     mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
184 }
185 
mfd_bmap_id(int triple)186 int mfd_bmap_id(int triple) {
187     int obclass = TRIP2CL(triple);
188     int t = CPTRIP(triple);
189     return MKREF(RES_mfdClass_1 + obclass, t);
190 }
191 
draw_blank_mfd(void)192 void draw_blank_mfd(void) {
193     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
194     draw_res_bm(REF_IMG_bmBlankMFD, 0, 0);
195     // draw_hires_resource_bm(REF_IMG_bmBlankMFD, 0, 0);
196     draw_res_bm(MKREF(RES_mfdArtOverlays, MFD_ART_TRIOP), 0, 0);
197 }
198 
199 // --------------------
200 // FUNCTION INITIALIZER
201 // --------------------
202 
203 // ---------------------------------------------------------------------------
204 // mfd_init_funcs()
205 //
206 // Here is where you set the global MFD_Func structures to point at
207 // expose/handler pairs, and also where you set their flags.  This is also
208 // where MFD virtual slots are set to point at their functions.
209 
mfd_init_funcs()210 void mfd_init_funcs() {
211     int i;
212     // Define a couple of MFD functions.
213     for (i = 0; i < MFD_NUM_FUNCS; i++)
214         if (mfd_funcs[i].init != NULL) {
215             errtype err = mfd_funcs[i].init(&mfd_funcs[i]);
216             if (err != OK)
217                 critical_error(CRITERR_MISC | 1);
218         }
219 
220     // Set slots to point at functions
221     set_slot_to_func(MFD_WEAPON_SLOT, MFD_WEAPON_FUNC, MFD_ACTIVE);
222     set_slot_to_func(MFD_ITEM_SLOT, MFD_ITEM_FUNC, MFD_ACTIVE);
223     set_slot_to_func(MFD_MAP_SLOT, MFD_MAP_FUNC, MFD_ACTIVE);
224     set_slot_to_func(MFD_INFO_SLOT, MFD_EMPTY_FUNC, MFD_ACTIVE);
225     set_slot_to_func(MFD_TARGET_SLOT, MFD_TARGET_FUNC, MFD_ACTIVE);
226 
227     //   set_slot_to_func(MFD_SPECIAL_SLOT, MFD_ANIM_FUNC,   MFD_FLASH);
228     //   set_slot_to_func(MFD_SPECIAL_SLOT, MFD_EMPTY_FUNC, MFD_EMPTY);
229 
230     //   debug_mfd_func_table();
231     //   debug_mfd_slots_table();
232     return;
233 }
234 
235 // ===========================================================================
236 //
237 // ===========================================================================
238 
239 //                             --------------------
240 //                             ACTUAL MFD FUNCTIONS
241 //                             --------------------
242 
243 // ===========================================================================
244 //                             * THE WEAPON MFD *
245 // ===========================================================================
246 
247 // Hey, wow, floats and doubles are UNcool.
248 
249 #define mfd_pixels_per_charge_unit (FIX_UNIT * 69 / 100) // How many hor. pixels equals a % of charge?
250 #define mfd_charge_units_per_pixel (FIX_UNIT * 100 / 69)
251 
252 // note _LEFT is 0, _RIGHT is 1
253 #define MFDLeftOffs MFD_LEFT
254 #define MFDRightOffs MFD_RIGHT
255 #define MFDLastWeapon 0
256 #define MFDAmmo 2
257 #define MFDLastBeamHeat 4
258 
259 #define MFD_Access(which, lorr) mfd_fdata[MFD_WEAPON_FUNC][(which) + (lorr)]
260 
261 #define MFDGetLastLeftWeapon mfd_fdata[MFD_WEAPON_FUNC][0]
262 #define MFDGetLastRightWeapon mfd_fdata[MFD_WEAPON_FUNC][1]
263 #define MFDGetLeftAmmo mfd_fdata[MFD_WEAPON_FUNC][2]
264 #define MFDGetRightAmmo mfd_fdata[MFD_WEAPON_FUNC][3]
265 #define MFDGetLastLeftBeamHeat mfd_fdata[MFD_WEAPON_FUNC][4]
266 #define MFDGetLastRightBeamHeat mfd_fdata[MFD_WEAPON_FUNC][5]
267 #define MFDSetLastLeftWeapon(n) (mfd_fdata[MFD_WEAPON_FUNC][0] = (n))
268 #define MFDSetLastRightWeapon(n) (mfd_fdata[MFD_WEAPON_FUNC][1] = (n))
269 #define MFDSetLeftAmmo(n) (mfd_fdata[MFD_WEAPON_FUNC][2] = (n))
270 #define MFDSetRightAmmo(n) (mfd_fdata[MFD_WEAPON_FUNC][3] = (n))
271 #define MFDSetLastLeftBeamHeat(n) (mfd_fdata[MFD_WEAPON_FUNC][4] = (n))
272 #define MFDSetLastRightBeamHeat(n) (mfd_fdata[MFD_WEAPON_FUNC][5] = (n))
273 
274 #define MFD_BEAMWPN_STAT_BORDER GREEN_YELLOW_BASE
275 #define MFD_BEAMWPN_STAT_CHARGE WHITE
276 #define MFD_BEAMWPN_STAT_MAXCHARGE PURPLE_BASE
277 #define MFD_BEAMWPN_STAT_DEADSPACE BLACK
278 
279 #define WEAPON_ART_Y 7
280 
281 #define AMMO_BUTTON_H 24
282 #define AMMO_BUTTON_W 23
283 #define AMMO_BUTTON_Y (MFD_VIEW_HGT - AMMO_BUTTON_H)
284 #define AMMO_STRING_Y (AMMO_BUTTON_Y - 4)
285 #define AMMO_NAME_Y (MFD_VIEW_HGT - 6)
286 
287 #define AMMO_BUTTON_X1 29
288 #define AMMO_BUTTON_DX1 0
289 #define AMMO_BUTTON_X2 11
290 #define AMMO_BUTTON_DX2 31
291 #define AMMO_BUTTON_X3 1
292 #define AMMO_BUTTON_DX3 24
293 
294 #define MFD_BEAM_RECT_X1 1
295 #define MFD_BEAM_RECT_X2 71
296 #define MFD_BEAM_RECT_Y1 52
297 #define MFD_BEAM_RECT_Y2 56
298 
299 LGRect MfdAmmoRectZone = {{0, AMMO_BUTTON_Y}, {MFD_VIEW_WID, AMMO_BUTTON_Y + AMMO_BUTTON_H}};
300 LGRect MfdBeamStatusRect;
301 
302 #define NO_CONSTRAIN NUM_MFDS
303 static ubyte beam_constrain = NO_CONSTRAIN;
304 
305 LGCursor slider_cursor;
306 grs_bitmap slider_cursor_bmap;
307 
308 // ---------- WEAPON MFD FUNC ---------------
309 
310 // --------------------------------------------------------------------------
311 // mfd_weapon_init()
312 //
313 // Initializes the MFD weapons function.
314 
mfd_weapon_init(MFD_Func * mfd)315 errtype mfd_weapon_init(MFD_Func *mfd) {
316 #ifndef NO_DUMMIES
317     void *yum;
318     yum = mfd;
319 #endif // NO_DUMMIES
320 
321     MFDSetLastLeftWeapon(0);
322     MFDSetLastRightWeapon(0);
323     MFDSetLeftAmmo(0xFF);
324     MFDSetRightAmmo(0xFF);
325 
326     MfdBeamStatusRect.ul.x = MFD_BEAM_RECT_X1;
327     MfdBeamStatusRect.ul.y = MFD_BEAM_RECT_Y1;
328     MfdBeamStatusRect.lr.x = MFD_BEAM_RECT_X2;
329     MfdBeamStatusRect.lr.y = MFD_BEAM_RECT_Y2;
330 
331     return OK;
332 }
333 
334 // --------------------------------------------------------------------------
335 // mfd_weapon_expose()
336 //
337 // Draws an overlay of a weapon in the current mfd slot.
338 
mfd_weapon_expose(MFD * m,ubyte control)339 void mfd_weapon_expose(MFD *m, ubyte control) {
340     weapon_slot *ws;
341     char buf[50];
342     int triple;
343     uchar punt = player_struct.actives[ACTIVE_WEAPON] == EMPTY_WEAPON_SLOT;
344     uchar Redraw = FALSE;
345     uchar RedrawAmmoArea = TRUE;
346     extern uchar full_game_3d;
347 
348     if (control == 0) {
349         uiCursorStack *cs;
350         weapon_mfd_temp = FALSE;
351 
352         uiGetRegionCursorStack(MFD_REGION(m), &cs);
353         uiPopCursorEvery(cs, &slider_cursor);
354 
355         if (beam_constrain == m->id) {
356             beam_constrain = NO_CONSTRAIN;
357             // KLC         mouse_unconstrain();
358         }
359         return;
360     }
361 
362     // Get the triple for the current weapon
363 
364     if (!punt)
365         ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]];
366     if (ws->type == EMPTY_WEAPON_SLOT)
367         punt = TRUE;
368     if (punt) {
369         mfd_expose_blank(m, control);
370         return;
371     }
372 
373     triple = MAKETRIP(CLASS_GUN, ws->type, ws->subtype);
374 
375     if (control & MFD_EXPOSE) {
376 
377         PUSH_CANVAS(pmfd_canvas);
378         mfd_clear_rects();
379 
380         if (control & MFD_EXPOSE_FULL)
381             Redraw = TRUE;
382         if (MFD_Access(m->id, MFDLastWeapon) != player_struct.actives[ACTIVE_WEAPON]) {
383             Redraw = TRUE;
384             MFD_Access(m->id, MFDLastWeapon) = player_struct.actives[ACTIVE_WEAPON];
385             MFD_Access(m->id, MFDAmmo) = 0xFF;
386             weapon_mfd_temp = FALSE;
387         }
388 
389         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
390         if (!full_game_3d)
391             ss_bitmap(&mfd_background, 0, 0);
392         // gr_bitmap(&mfd_background, 0, 0);
393 
394         // Draw the appropriate weapon art.
395         // We draw it here because it is effectively "background"
396         // Hopefully update-rects will take care of us..
397         {
398             int id = mfd_bmap_id(triple);
399             draw_res_bm(id, (MFD_VIEW_WID - res_bm_width(id)) / 2, WEAPON_ART_Y);
400             // draw_hires_resource_bm(id,
401             //		(SCONV_X(MFD_VIEW_WID)-res_bm_width(id))/2, SCONV_Y(WEAPON_ART_Y));  // is this right?
402         }
403 
404         // This is all stuff that should be drawn for a full expose of
405         // a new weapon
406         if (Redraw) {
407             short y = 2;
408             short w, h;
409 
410             // Print name of gun in top line of mfd
411             get_weapon_long_name(ws->type, ws->subtype, buf);
412             mfd_draw_string(buf, X_MARGIN, y, GOOD_RED, TRUE);
413             gr_string_size(buf, &w, &h);
414             y += h;
415 
416             mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
417         }
418 
419         // Here is where the dynamic info for a given weapon is drawn; ie; data that
420         // requires a redraw even when the current weapon has not changed
421 
422         if ((ws->type == GUN_SUBCLASS_BEAM) || (ws->type == GUN_SUBCLASS_BEAMPROJ))
423             mfd_weapon_expose_beam(ws, m->id, Redraw);
424         else if (ws->type != GUN_SUBCLASS_HANDTOHAND)
425             RedrawAmmoArea = mfd_weapon_expose_projectile(m, ws, control);
426 
427         // Redraw everything
428         POP_CANVAS();
429         mfd_update_rects(m);
430     }
431 
432     return;
433 }
434 
435 // --------------------------------------------------------------------------
436 // mfd_weapon_expose_projectile()
437 //
438 // Exposes relevant weapons mfd info for a projectile weapon.
439 // returns whether or not it drew the ammo buttons
440 
mfd_weapon_expose_projectile(MFD * m,weapon_slot * ws,ubyte control)441 uchar mfd_weapon_expose_projectile(MFD *m, weapon_slot *ws, ubyte control) {
442     int num_ammo_buttons;
443     int ammo_subclass;
444     ubyte ammo_types[3];
445     uchar RedrawAmmoFlag = FALSE;
446 
447     // Get the ammo data for the current weapon
448     get_available_ammo_type(ws->type, ws->subtype, &num_ammo_buttons, ammo_types, &ammo_subclass);
449 
450     /*
451      * FIXME: shamaz 20.04.2020
452      *
453      * The following two lines were present in the code provided by
454      * Nightdive Studios, LLC along with the following comment:
455      *    > ammo_type and setting have same memory location in
456      *    > weapon_slot union. setting can have values greater than
457      *    > num ammo buttons so check for and fix oob
458      * It's not clear how oob access can happen because .setting and
459      * .ammo_type fields are used independently (based on a type of
460      * the weapon, e.g. beam or other range weapon) and never .setting
461      * value is treated as .ammo_type and vice versa.
462      *
463      * On the other hand they cause a problem when you reload your
464      * weapon with the last clip (of any suitable type). At first, if
465      * you unload this clip, it get lost (disappears from your
466      * inventory forever) even if not depleted completely. At second,
467      * this final clip is replaced with a clip of other type
468      * (sometimes even inappropriate for this particular weapon).
469      *
470      * So I find it better to comment these lines out. Seems to be
471      * perfectly safe, but some more testing is required.
472      */
473     /* if (ws->ammo_type >= num_ammo_buttons) */
474     /*     ws->ammo_type = (num_ammo_buttons ? ammo_types[0] : 0); */
475 
476     if ((control & MFD_EXPOSE_FULL) || (ws->ammo == 0))
477         RedrawAmmoFlag = TRUE;
478     else if (MFD_Access(m->id, MFDAmmo) != ws->ammo) {
479         RedrawAmmoFlag = TRUE;
480         MFD_Access(m->id, MFDAmmo) = ws->ammo;
481     }
482 
483     // Redraw ammo buttons if neccessary
484     if (RedrawAmmoFlag)
485         mfd_weapon_draw_ammo_buttons(num_ammo_buttons, ammo_subclass, ammo_types, ws->ammo_type, ws->ammo);
486 
487     return RedrawAmmoFlag;
488 }
489 
490 // ---------------------------------------------------------------------------
491 // mfd_weapon_draw_ammo_buttons()
492 //
493 // Draws the labelled buttons of ammo on a projectile gun's mfd
494 
495 #define MAX_CART_COLORS 3
496 #define CARTRIDGE_BRACKET 8 // cartridges per color
497 static uchar cart_colors[MAX_CART_COLORS] = {GOOD_RED, 0x4b, GREEN_BASE + 2};
498 
draw_ammo_button(int triple,short x,short y)499 void draw_ammo_button(int triple, short x, short y) {
500     short h;
501     int id;
502     int carts = player_struct.cartridges[CPTRIP(triple)];
503     ubyte cnum = lg_min(MAX_CART_COLORS - 1, (carts - 1) / CARTRIDGE_BRACKET);
504 
505     // Draw the outline of an ammo box
506     draw_res_bm(REF_IMG_BullFrame + lg_min(2, lg_max(3 - carts, 0)), x, y);
507     id = mfd_bmap_id(triple);
508     h = res_bm_height(id);
509     draw_res_bm(id, x + 4, y + AMMO_BUTTON_H - 4 - h);
510     // draw_hires_resource_bm(id, SCONV_X(x+4), SCONV_Y(y+AMMO_BUTTON_H-3)-h);
511     if (carts > 0) {
512         int cnt = carts % CARTRIDGE_BRACKET;
513         if (cnt == 0)
514             cnt = CARTRIDGE_BRACKET;
515         gr_set_fcolor(cart_colors[cnum]);
516         while (cnt-- > 0) {
517             short cy = y + AMMO_BUTTON_H - 5 - 2 * cnt;
518             ss_hline(x + AMMO_BUTTON_W - 6, cy, x + AMMO_BUTTON_W - 5);
519         }
520     }
521     if (carts > 0 || player_struct.partial_clip[CPTRIP(triple)] > 0)
522         mfd_add_rect(x, y, x + AMMO_BUTTON_W, y + AMMO_BUTTON_H);
523 }
524 
mfd_weapon_draw_ammo_buttons(int num_ammo_buttons,int ammo_subclass,ubyte * ammo_types,ubyte curr_ammo_type,int ammo_count)525 void mfd_weapon_draw_ammo_buttons(int num_ammo_buttons, int ammo_subclass, ubyte *ammo_types, ubyte curr_ammo_type,
526                                   int ammo_count) {
527     int i, triple;
528 
529     // Draw ammo boxes
530     if (ammo_count == 0) {
531         for (i = 0; i < num_ammo_buttons; i++) {
532             short x = (ammo_types[i]) * AMMO_BUTTON_W;
533             triple = MAKETRIP(CLASS_AMMO, ammo_subclass, ammo_types[i]);
534             // Get useful ammo information for box label
535             draw_ammo_button(triple, x, AMMO_BUTTON_Y);
536         }
537         if (num_ammo_buttons > 0)
538             mfd_draw_string(get_temp_string(REF_STR_ClickToLoad), 1, AMMO_STRING_Y, GREEN_YELLOW_BASE, TRUE);
539     } else {
540         char buf[4], buf2[50];
541         triple = MAKETRIP(CLASS_AMMO, ammo_subclass, curr_ammo_type);
542 
543         sprintf(buf, "%d", ammo_count);
544         gr_set_font(ResGet(RES_mediumLEDFont));
545         mfd_string_shadow = MFD_SHADOW_NEVER;
546         mfd_draw_font_string(buf, MFD_VIEW_WID - gr_string_width(buf) - 2, AMMO_BUTTON_Y + 2, GOOD_RED,
547                              RES_mediumLEDFont, TRUE);
548         mfd_string_shadow = MFD_SHADOW_FULLSCREEN; // default
549 
550         get_object_short_name(triple, buf2, 50);
551         gr_set_font(ResGet(MFD_FONT));
552         mfd_draw_string(buf2, MFD_VIEW_WID - gr_string_width(buf2) - 2, AMMO_NAME_Y, GREEN_YELLOW_BASE, TRUE);
553 
554         draw_ammo_button(triple, 0, AMMO_BUTTON_Y);
555         draw_res_bm(REF_IMG_BullRightArrow, AMMO_BUTTON_W, AMMO_BUTTON_Y);
556     }
557 
558     if ((num_ammo_buttons == 0) && (ammo_count == 0)) {
559         draw_res_bm(REF_IMG_NoAmmo, (MFD_VIEW_WID - res_bm_width(REF_IMG_NoAmmo)) / 2, AMMO_BUTTON_Y);
560     }
561     mfd_add_rect(0, AMMO_STRING_Y, MFD_VIEW_WID, MFD_VIEW_HGT);
562     return;
563 }
564 
565 #define MFD_BEAM_WARM 0x40
566 #define MFD_BEAM_HOT 0x35
567 #define MFD_BEAM_READY 0x58
568 
569 ubyte temp_levels[TEMPR_WIDTH] = {1, 2, 2, 3, 3, 4, 5, 6, 7, 9, 11, 13, 16};
570 
571 // --------------------------------------------------------------------------
572 // mfd_weapon_draw_temp()
573 //
574 
mfd_weapon_draw_temp(ubyte temp)575 void mfd_weapon_draw_temp(ubyte temp) {
576     int i;
577 
578     gr_set_fcolor(MFD_BEAM_READY);
579 
580     for (i = 0; i < TEMPR_WIDTH; i++) {
581         if (i == 5)
582             gr_set_fcolor(MFD_BEAM_WARM);
583         else if (i == 10)
584             gr_set_fcolor(MFD_BEAM_HOT);
585 
586         if (temp >= i * TEMPR_DIV)
587             ss_vline(TEMPR_X + i * 2, TEMPR_Y + TEMPR_HEIGHT - temp_levels[i], TEMPR_Y + TEMPR_HEIGHT);
588     }
589 }
590 
591 // --------------------------------------------------------------------------
592 // mfd_weapon_draw_beam_status_bar()
593 //
594 // Takes the current charge and the maximum charge, and draws the dynamic
595 // portion of the beam weapon status bar
596 
mfd_weapon_draw_beam_status_bar(int amt,int setting,uchar does_overload)597 void mfd_weapon_draw_beam_status_bar(int amt, int setting, uchar does_overload) {
598     ubyte setting_x;
599 
600     setting = (BEAM_SETTING_VAL(setting) < MIN_ENERGY_USE) ? 1 : (BEAM_SETTING_VAL(setting) - MIN_ENERGY_USE);
601     if (does_overload)
602         setting <<= 1;
603 
604     setting++;
605 
606     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
607 
608     gr_set_fcolor(MFD_BEAMWPN_STAT_BORDER); // OUTLINE
609     ss_box(MfdBeamStatusRect.ul.x - 1, MfdBeamStatusRect.ul.y - 1, MfdBeamStatusRect.lr.x + 1,
610            MfdBeamStatusRect.lr.y + 1);
611 
612     setting_x = (ubyte)fix_int(setting * mfd_pixels_per_charge_unit);
613     setting_x = lg_min(MfdBeamStatusRect.lr.x - MfdBeamStatusRect.ul.x, setting_x);
614 
615     // draw the settings bar only if we're not in overload mode
616 
617     if (!in_or_out)
618         draw_raw_resource_bm(REF_IMG_BeamSetting, MfdBeamStatusRect.ul.x + setting_x - 3, MfdBeamStatusRect.ul.y - 1);
619 
620     mfd_add_rect(MfdBeamStatusRect.ul.x, MfdBeamStatusRect.ul.y, MfdBeamStatusRect.lr.x, MfdBeamStatusRect.lr.y);
621     return;
622 }
623 
624 // --------------------------------------------------------------------------
625 // mfd_weapon_expose_beam()
626 //
627 // Exposes relevant weapons mfd info for a beam weapon.
628 
mfd_weapon_expose_beam(weapon_slot * ws,ubyte id,uchar Redraw)629 void mfd_weapon_expose_beam(weapon_slot *ws, ubyte id, uchar Redraw) {
630     char buf[ENERGY_TEXT_LEN];
631     uchar does_overload = FALSE;
632     extern uchar does_weapon_overload(int type, int subtype);
633 
634     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
635     if (id == MFD_LEFT) {
636         if (MFDGetLastLeftBeamHeat > ws->ammo)
637             Redraw = TRUE;
638         MFDSetLastLeftBeamHeat(ws->ammo);
639     }
640     if (id == MFD_RIGHT) {
641         if (MFDGetLastRightBeamHeat > ws->ammo)
642             Redraw = TRUE;
643         MFDSetLastRightBeamHeat(ws->ammo);
644     }
645 
646     does_overload = does_weapon_overload(ws->type, ws->subtype);
647     if (does_overload) {
648         // if beam weapon is not set to overload then draw overload button, otherwise say "overload enabled"
649         if (Redraw && OVERLOAD_VALUE(ws->setting)) {
650             short w = res_bm_width(REF_IMG_BeamOverloadOn);
651 
652             draw_raw_resource_bm(REF_IMG_BeamOverloadOn, 1, OVERLOAD_BUTTON_Y);
653             mfd_add_rect(1, OVERLOAD_BUTTON_Y, 1 + w, MFD_VIEW_HGT);
654 
655             get_string(REF_STR_Overload, buf, ENERGY_TEXT_LEN);
656             mfd_draw_string(buf, 1, SETTING_TEXT, MFD_BEAM_HOT, TRUE);
657         } else {
658             short w = res_bm_width(REF_IMG_BeamOverload);
659 
660             if (Redraw) {
661                 (ws->ammo < MINIMUM_OVERLOAD) ? draw_raw_resource_bm(REF_IMG_BeamOverload, 1, OVERLOAD_BUTTON_Y)
662                                               : draw_raw_resource_bm(REF_IMG_BeamOverloadOff, 1, OVERLOAD_BUTTON_Y);
663                 mfd_add_rect(1, OVERLOAD_BUTTON_Y, 1 + w, MFD_VIEW_HGT);
664                 get_string(REF_STR_EnergySetting, buf, ENERGY_TEXT_LEN);
665                 mfd_draw_string(buf, 1, SETTING_TEXT, MFD_BEAM_READY, TRUE);
666             }
667         }
668     }
669 
670     get_string(REF_STR_LowSetting, buf, ENERGY_TEXT_LEN);
671     mfd_draw_string(buf, 2, MFD_BEAM_RECT_Y1 - 1, MFD_BEAM_READY, TRUE);
672     get_string(REF_STR_HighSetting, buf, ENERGY_TEXT_LEN);
673     mfd_draw_string(buf, 57, MFD_BEAM_RECT_Y1 - 1, MFD_BEAM_READY, TRUE);
674 
675     draw_raw_resource_bm(REF_IMG_BeamTemperature, TEMPR_X, TEMPR_Y);
676 
677     mfd_weapon_draw_temp(ws->ammo);
678 
679     // Redraw the beam energy status bar
680     mfd_weapon_draw_beam_status_bar(ws->ammo, ws->setting, does_overload);
681 
682     return;
683 }
684 
685 // ---------------------------------------------------------------------------
686 // mfd_weapon_handler()
687 //
688 // Mostly responsible for figuring out which ammo boxes were clicked on
689 // to select new ammo, and for manipulating the charge on beam weapons
690 
mfd_weapon_handler(MFD * m,uiEvent * e)691 uchar mfd_weapon_handler(MFD *m, uiEvent *e) {
692     weapon_slot *ws;
693     int triple;
694 
695     // Get the triple for the current weapon
696     ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]];
697     triple = MAKETRIP(CLASS_GUN, ws->type, ws->subtype);
698 
699     switch (ws->type) {
700     case GUN_SUBCLASS_BEAM:     // Is beam charge being set?
701     case GUN_SUBCLASS_BEAMPROJ: // Is beam charge being set?
702         //         if (!(mouse->action & ~MOUSE_MOTION) && !(mouse->buttons & (1 << MOUSE_LBUTTON)))
703         //            return FALSE;
704         return mfd_weapon_beam_handler(m, e);
705         break;
706     case GUN_SUBCLASS_PISTOL:
707     case GUN_SUBCLASS_AUTO:
708     case GUN_SUBCLASS_SPECIAL:
709         if (e->mouse_data.action == MOUSE_MOTION)
710             return FALSE;
711         return mfd_weapon_projectile_handler(m, e, ws);
712         break;
713     default:
714         break;
715     }
716     return FALSE;
717 }
718 
719 ubyte old_energy_setting = 0xFF;
720 
721 // ---------------------------------------------------------------------------
722 // mfd_weapon_beam_handler()
723 //
724 // This is the handler for beam type weapons in the weapons mfd.
725 
726 extern uchar does_weapon_overload(int type, int subtype);
727 
mfd_weapon_beam_handler(MFD * m,uiEvent * e)728 uchar mfd_weapon_beam_handler(MFD *m, uiEvent *e) {
729     uchar retval = TRUE;
730     LGRect r;
731     ubyte setting, setting_x;
732     weapon_slot *ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]];
733     uchar overld = does_weapon_overload(ws->type, ws->subtype);
734 
735 #ifdef CURSOR_BACKUPS
736     extern grs_bitmap backup_mfd_cursor;
737     extern uchar *backup[NUM_BACKUP_BITS];
738 #endif
739 
740     // We're interested in this event iff its a mouse up,down,action event
741     // in the beam status bar.
742     if (!(e->mouse_data.action & (MOUSE_LUP | MOUSE_LDOWN | MOUSE_MOTION))) {
743         return FALSE;
744     }
745 
746     if (overld) {
747         // okay - here's the overload button code
748         if (e->mouse_data.action & MOUSE_LDOWN) {
749 
750             if (ws->ammo < MINIMUM_OVERLOAD) {
751                 // set up the rect for the overload button
752                 r.ul.x = (MFD_VIEW_WID - res_bm_width(REF_IMG_NoAmmo)) / 2;
753                 r.ul.y = OVERLOAD_BUTTON_Y;
754                 r.lr.x = r.ul.x + res_bm_width(REF_IMG_BeamOverload);
755                 r.lr.y = OVERLOAD_BUTTON_Y + res_bm_height(REF_IMG_BeamOverload);
756                 RECT_OFFSETTED_RECT(&r, m->rect.ul, &r);
757 
758                 // check if we clicked in the button
759                 if (RECT_TEST_PT(&r, e->pos)) {
760                     // toggle between the two values
761                     chg_set_flg(INVENTORY_UPDATE);
762                     OVERLOAD_VALUE(ws->setting) ? OVERLOAD_RESET(ws->setting) : OVERLOAD_SET(ws->setting);
763                     mfd_force_update(); // make sure it redraws the mfd
764                     return TRUE;
765                 }
766             }
767         }
768     }
769 
770     RECT_OFFSETTED_RECT(&MfdBeamStatusRect, m->rect.ul, &r);
771 
772     if (!RECT_TEST_PT(&r, e->pos)) {
773         uiCursorStack *cs;
774 
775         uiGetRegionCursorStack(MFD_REGION(m), &cs);
776         uiPopCursorEvery(cs, &slider_cursor);
777         mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE);
778         if (!in_or_out)
779             return retval;
780     }
781     if (!in_or_out && (e->mouse_data.buttons == 0))
782         return retval;
783 
784     // If the left button was pushed, we constrain the mouse to
785     // the beam status bar, and change the cursor appropriately
786     if (e->mouse_data.action & MOUSE_LDOWN) {
787 
788         in_or_out = TRUE;
789 
790         // Constrain the mouse to a 1-pixel y
791         slider_cursor.hotspot.x = slider_cursor_bmap.w / 2;
792         slider_cursor.hotspot.y = (slider_cursor_bmap.h / 2) + e->pos.y - (r.ul.y + r.lr.y) / 2;
793         ui_mouse_constrain_xy(r.ul.x + 1, e->pos.y, r.lr.x - 2, e->pos.y);
794         beam_constrain = m->id;
795         // Get our funky mfd-beam-phaser-setting cursor
796         uiPushRegionCursor(MFD_REGION(m), &slider_cursor);
797 #ifdef CURSOR_BACKUPS
798         backup[20] = (uchar *)malloc(f->bm.w * f->bm.h);
799         LG_memcpy(backup[20], f->bm.bits, f->bm.w * f->bm.h);
800         gr_init_bm(&backup_mfd_cursor, backup[14], BMT_FLAT8, 0, mfd_cursor.w, mfd_cursor.h);
801 #endif
802         retval = TRUE;
803     }
804 
805     // If the left button was released, unconstrain the mouse and reset
806     // the cursor bitmap.  There's no else here for the weird case that
807     // an up and down event might happen "simultaneously"
808     if (e->mouse_data.action & MOUSE_LUP) {
809 
810         in_or_out = FALSE;
811 
812         // Let the mouse run free
813         // note that we are NOT using the UI here since we're already looking at grd_cap
814         mouse_constrain_xy(0, 0, grd_cap->w - 1, grd_cap->h - 1);
815         beam_constrain = NO_CONSTRAIN;
816 
817         uiPopRegionCursor(MFD_REGION(m));
818         mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE);
819 
820         retval = TRUE;
821     }
822 
823     // Calculate the max charge setting based on mouse position (making goofy
824     // exceptions for the endpoints for aesthetics) and set the weapon
825     // accordingly.
826     setting_x = e->pos.x - r.ul.x;
827     if ((e->mouse_data.action & MOUSE_MOTION) && (setting_x == old_energy_setting))
828         return retval;
829     old_energy_setting = setting_x;
830 
831     setting = (ubyte)fix_int(setting_x * mfd_charge_units_per_pixel);
832 
833     if (overld)
834         setting >>= 1;
835 
836     setting += MIN_ENERGY_USE;
837     uiSetCursor();
838 
839     set_beam_weapon_max_charge(player_struct.actives[ACTIVE_WEAPON], setting);
840     //      min(MAX_HEAT,setting));
841 
842     return TRUE;
843 }
844 
845 // ---------------------------------------------------------------------------
846 // mfd_weapon_projectile_handler()
847 //
848 // This is the handler for projectile weapons in the weapons mfd.
849 
mfd_weapon_projectile_handler(MFD * m,uiEvent * e,weapon_slot * ws)850 uchar mfd_weapon_projectile_handler(MFD *m, uiEvent *e, weapon_slot *ws) {
851     int ammo_subclass, num_ammo_buttons;
852     ubyte ammo_types[3];
853     LGPoint pos = e->pos;
854 
855     pos.x -= m->rect.ul.x;
856     pos.y -= m->rect.ul.y;
857 
858     if (pos.y < AMMO_BUTTON_Y)
859         return FALSE;
860     // If we're already loaded, check for double click.
861     if (ws->ammo > 0) {
862         uchar retval = FALSE;
863         if (e->mouse_data.action & UI_MOUSE_LDOUBLE) {
864             extern void unload_current_weapon(void);
865 
866             unload_current_weapon();
867             MFDSetLeftAmmo(0xFF);
868             MFDSetRightAmmo(0xFF);
869             mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE);
870             mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
871             retval = TRUE;
872         } else if (e->mouse_data.action & MOUSE_LDOWN) {
873             string_message_info(REF_STR_DClickToUnload);
874             retval = TRUE;
875         }
876         return retval;
877     }
878 
879     // If it wasn't a left-mouse-button down event, throw it away.
880     if (!(e->mouse_data.action & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)))
881         return FALSE;
882 
883     // Get the ammo data for the current weapon
884     get_available_ammo_type(ws->type, ws->subtype, &num_ammo_buttons, ammo_types, &ammo_subclass);
885     {
886         int b = pos.x / AMMO_BUTTON_W;
887         int atype;
888         for (atype = 0; atype < num_ammo_buttons; atype++) {
889             // If we have a hit, we let each mfd know independently that
890             // it needs to redraw its ammo buttons, to save redraw time
891             if (b == ammo_types[atype] && change_ammo_type(b)) {
892                 if (weapon_mfd_temp) {
893                     int mfd;
894                     for (mfd = 0; mfd < NUM_MFDS; mfd++) {
895                         if (player_struct.mfd_current_slots[mfd] == MFD_WEAPON_SLOT)
896                             restore_mfd_slot(mfd);
897                     }
898                     weapon_mfd_temp = FALSE;
899                 }
900                 MFDSetLeftAmmo(0xFF);
901                 MFDSetRightAmmo(0xFF);
902                 mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE);
903                 mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
904                 break;
905             }
906         }
907     }
908 
909     return TRUE;
910 }
911 
weapon_mfd_for_reload(void)912 void weapon_mfd_for_reload(void) {
913     uchar target_pri;
914     uchar take_mfd;
915 
916     extern int mfd_choose_func(int func, int slot);
917 
918     // Do not take down target mfd in favor of weapon in this case!
919     target_pri = mfd_funcs[MFD_TARGET_FUNC].priority;
920     mfd_funcs[MFD_TARGET_FUNC].priority = 1;
921     take_mfd = mfd_choose_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT);
922     if (player_struct.mfd_current_slots[take_mfd] != MFD_WEAPON_SLOT) {
923         save_mfd_slot(take_mfd);
924         weapon_mfd_temp = TRUE;
925     }
926     mfd_change_slot(take_mfd, MFD_WEAPON_SLOT);
927     mfd_funcs[MFD_TARGET_FUNC].priority = target_pri;
928 }
929 
930 // ===========================================================================
931 //                             * THE ITEM MFD *
932 // ===========================================================================
933 
934 // OK, the item MFD is pretty heinous, and, in fact, they all
935 // really want to be their own MFDfuncs, so, let's grant them their wish!
936 
937 #define MFDGetLastItemClass(m) mfd_fdata[MFD_ITEM_FUNC][(m)]
938 #define MFDGetLastItemType(m) mfd_fdata[MFD_ITEM_FUNC][(m) + 2]
939 #define MFDGetCurrItemClass(m) mfd_fdata[MFD_ITEM_FUNC][(m) + 4]
940 #define MFDGetCurrItemType(m) mfd_fdata[MFD_ITEM_FUNC][(m) + 6]
941 
942 #define MFDSetLastItemClass(m, n) mfd_fdata[MFD_ITEM_FUNC][(m)] = (n)
943 #define MFDSetLastItemType(m, n) mfd_fdata[MFD_ITEM_FUNC][(m) + 2] = (n)
944 #define MFDSetCurrItemClass(m, n) mfd_fdata[MFD_ITEM_FUNC][(m) + 4] = (n)
945 #define MFDSetCurrItemType(m, n) mfd_fdata[MFD_ITEM_FUNC][(m) + 6] = (n)
946 
947 LGRect MfdGrenadeBox[2];
948 
949 #define MFD_GRENADE_BOX_X1 27
950 #define MFD_GRENADE_BOX_X2 47
951 #define MFD_GRENADE_BOX_Y 5
952 #define MFD_GRENADE_BOX_W 5
953 #define MFD_GRENADE_BOX_H 5
954 
955 #define HARDWARE_BUTTON_H 13
956 #define HARDWARE_BUTTON_W 44
957 #define HARDWARE_BUTTON_X ((MFD_VIEW_WID - HARDWARE_BUTTON_W) / 2)
958 #define HARDWARE_BUTTON_Y (MFD_VIEW_HGT - HARDWARE_BUTTON_H - 1)
959 
960 #define DRUG_BUTTON_H 13
961 #define DRUG_BUTTON_W 32
962 #define DRUG_BUTTON_X ((MFD_VIEW_WID - DRUG_BUTTON_W) / 2)
963 #define DRUG_BUTTON_Y (MFD_VIEW_HGT - 1 - DRUG_BUTTON_H)
964 
965 // --------------------------------------------------------------------------
966 // mfd_item_init()
967 //
968 // Sets some info.
969 
mfd_item_init(MFD_Func * mfd)970 errtype mfd_item_init(MFD_Func *mfd) {
971     MFDSetCurrItemClass(0, MFD_INV_NULL);
972     MFDSetCurrItemClass(1, MFD_INV_NULL);
973     MFDSetLastItemClass(0, MFD_INV_NULL);
974     MFDSetLastItemClass(1, MFD_INV_NULL);
975 
976     MfdGrenadeBox[0].ul.x = MFD_GRENADE_BOX_X1;
977     MfdGrenadeBox[0].ul.y = MFD_GRENADE_BOX_Y;
978     MfdGrenadeBox[0].lr.x = MFD_GRENADE_BOX_X1 + MFD_GRENADE_BOX_W;
979     MfdGrenadeBox[0].lr.y = MFD_GRENADE_BOX_Y + MFD_GRENADE_BOX_H;
980 
981     MfdGrenadeBox[1].ul.x = MFD_GRENADE_BOX_X2;
982     MfdGrenadeBox[1].ul.y = MFD_GRENADE_BOX_Y;
983     MfdGrenadeBox[1].lr.x = MFD_GRENADE_BOX_X2 + MFD_GRENADE_BOX_W;
984     MfdGrenadeBox[1].lr.y = MFD_GRENADE_BOX_Y + MFD_GRENADE_BOX_H;
985 
986     return OK;
987 }
988 
989 //--------------------------------------------------------------
990 // Like mini-expose, but gets the name for you, and
991 // conforms to our rect-o-tronic update facility
992 
mfd_item_micro_expose(uchar full,int triple)993 void mfd_item_micro_expose(uchar full, int triple) {
994     if (!full_game_3d) {
995         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
996         ss_bitmap(&mfd_background, 0, 0);
997         // gr_bitmap(&mfd_background, 0, 0);
998     }
999     if (full) {
1000         LGPoint siz;
1001         int id;
1002         short y = 2;
1003         char buf[LNAME_BUFSIZE];
1004         mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1005         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1006 
1007         get_object_long_name(triple, buf, LNAME_BUFSIZE);
1008         siz = mfd_draw_string(buf, X_MARGIN, y, GREEN_YELLOW_BASE, TRUE);
1009         y += siz.y + 2;
1010 
1011         // Draw the appropriate weapon art
1012         id = mfd_bmap_id(triple);
1013         if (RefIndexValid((RefTable *)ResGet(REFID(id)), REFINDEX(id)))
1014             draw_raw_resource_bm(id, (MFD_VIEW_WID - res_bm_width(id)) / 2, y);
1015         else
1016             ResUnlock(REFID(id));
1017     }
1018     return;
1019 }
1020 
1021 //--------------------------------------------------------------
1022 // Called by things that know they have hi-res art to display.
1023 
mfd_item_micro_hires_expose(uchar full,int triple)1024 void mfd_item_micro_hires_expose(uchar full, int triple) {
1025     if (!full_game_3d) {
1026         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1027         gr_bitmap(&mfd_background, 0, 0);
1028     }
1029     if (full) {
1030         LGPoint siz;
1031         int id;
1032         short y = 2;
1033         char buf[LNAME_BUFSIZE];
1034 
1035         mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1036         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1037 
1038         get_object_long_name(triple, buf, LNAME_BUFSIZE);
1039         siz = mfd_draw_string(buf, X_MARGIN, y, GREEN_YELLOW_BASE, TRUE);
1040         y += siz.y + 2;
1041 
1042         id = mfd_bmap_id(triple);
1043         if (RefIndexValid((RefTable *)ResGet(REFID(id)), REFINDEX(id)))
1044             draw_hires_resource_bm(id, (SCONV_X(MFD_VIEW_WID) - res_bm_width(id)) / 2, SCONV_Y(y));
1045         else
1046             ResUnlock(REFID(id));
1047     }
1048     return;
1049 }
1050 
1051 // --------------------------------------------------------------------------
1052 // mfd_item_draw_grenade_setting_boxes()
1053 //
1054 // Draws the little increment/decrement boxes on either side of the
1055 // "time left before grenade blows up" setting on the MFD.
1056 
1057 #ifdef OLD_GRENADE_BUTTONS
mfd_item_draw_grenade_setting_boxes(MFD * m)1058 void mfd_item_draw_grenade_setting_boxes(MFD *m) {
1059     char buf[2];
1060     int i;
1061 
1062     for (i = 0; i < 2; i++) {
1063         gr_set_fcolor(WHITE);
1064         ss_rect(MfdGrenadeBox[i].ul.x, MfdGrenadeBox[i].ul.y, MfdGrenadeBox[i].lr.x, MfdGrenadeBox[i].lr.y);
1065         gr_set_fcolor(MFD_BTTN_FLASH);
1066         ss_box(MfdGrenadeBox[i].ul.x, MfdGrenadeBox[i].ul.y, MfdGrenadeBox[i].lr.x, MfdGrenadeBox[i].lr.y);
1067     }
1068 
1069     gr_set_fcolor(BLACK);
1070     sprintf(buf, "-");
1071     ss_string("-", MfdGrenadeBox[0].ul.x + 1, MfdGrenadeBox[0].ul.y + 1);
1072     sprintf(buf, "+");
1073     ss_string(buf, MfdGrenadeBox[1].ul.x + 1, MfdGrenadeBox[1].ul.y + 1);
1074 
1075     return;
1076 }
1077 #endif // OLD_GRENADE_BUTTONS
1078 
1079 // --------------------------------------------------------------------------
1080 // mfd_item_expose()
1081 //
1082 // Draws an overlay of a drug molecule or whatever in the current slot.
1083 
1084 #define NULL_ACTIVE 0xFF
1085 static ubyte cat2active[MFD_INV_CATEGORIES] = {
1086     NULL_ACTIVE,   ACTIVE_DRUG,    ACTIVE_HARDWARE,    ACTIVE_GRENADE,      ACTIVE_CART,
1087     ACTIVE_WEAPON, ACTIVE_GENERAL, ACTIVE_COMBAT_SOFT, ACTIVE_DEFENSE_SOFT, ACTIVE_MISC_SOFT,
1088 };
1089 
1090 #define TRASH_BUTTON REF_IMG_DiscardButton
1091 
1092 #define NULL_REF MKREF(ID_NULL, 0)
1093 
1094 Ref smallstuffSpews[] = {NULL_REF, NULL_REF, NULL_REF, REF_STR_gearSpew0,
1095                          NULL_REF, NULL_REF, NULL_REF, REF_STR_plotSpew0};
1096 
mfd_general_inv_expose(MFD * mfd,ubyte control,ObjID id,uchar full)1097 void mfd_general_inv_expose(MFD *mfd, ubyte control, ObjID id, uchar full) {
1098     int triple;
1099     Ref spew = NULL_REF;
1100 
1101     if (id == OBJ_NULL) {
1102         draw_blank_mfd();
1103         return;
1104     }
1105     triple = ID2TRIP(id);
1106     mfd_item_micro_expose(full, triple);
1107     if (full && !(ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) {
1108         short x = (MFD_VIEW_WID - res_bm_width(TRASH_BUTTON)) / 2;
1109         short y = (MFD_VIEW_HGT - res_bm_height(TRASH_BUTTON)) / 2;
1110         draw_raw_resource_bm(TRASH_BUTTON, x, y);
1111         mfd_add_rect(x, y, x + res_bm_width(TRASH_BUTTON), y + res_bm_height(TRASH_BUTTON));
1112     }
1113     if (objs[id].obclass == CLASS_SMALLSTUFF)
1114         spew = smallstuffSpews[objs[id].subclass];
1115     if (spew != NULL_REF)
1116         draw_mfd_item_spew(spew + objs[id].info.type, 1);
1117 }
1118 
mfd_general_inv_handler(MFD * m,uiEvent * ev,int row)1119 uchar mfd_general_inv_handler(MFD *m, uiEvent *ev, int row) {
1120     ObjID id = player_struct.inventory[row];
1121     if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & MOUSE_LDOWN))
1122         return FALSE;
1123     if (!(ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) {
1124         LGPoint pos = MakePoint(ev->pos.x - m->rect.ul.x, ev->pos.y - m->rect.ul.y);
1125         short x = (MFD_VIEW_WID - res_bm_width(TRASH_BUTTON)) / 2;
1126         short y = (MFD_VIEW_HGT - res_bm_height(TRASH_BUTTON)) / 2;
1127         LGRect r;
1128         r.ul = r.lr = MakePoint(x, y);
1129         r.lr.x += res_bm_width(TRASH_BUTTON);
1130         r.lr.y += res_bm_height(TRASH_BUTTON);
1131         if (RECT_TEST_PT(&r, pos)) {
1132             int i;
1133             obj_destroy(id);
1134             for (i = row + 1; i < NUM_GENERAL_SLOTS; i++)
1135                 player_struct.inventory[i - 1] = player_struct.inventory[i];
1136             player_struct.inventory[NUM_GENERAL_SLOTS - 1] = OBJ_NULL;
1137             drain_energy(1);
1138             mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, TRUE, MFD_EMPTY, FALSE);
1139             chg_set_flg(INVENTORY_UPDATE);
1140         }
1141     }
1142     return TRUE;
1143 }
1144 
1145 #define STRINGS_PER_WARE (REF_STR_wareSpew1 - REF_STR_wareSpew0)
1146 //#define SPEW_VERT_MARGIN 10
1147 #define SPEW_VERT_MARGIN 2
1148 
draw_mfd_item_spew(Ref id,int n)1149 void draw_mfd_item_spew(Ref id, int n) {
1150     uchar oldwrap = mfd_string_wrap;
1151     short w, h;
1152     short x, y;
1153     char buf[256];
1154     int i;
1155 
1156     gr_set_font(ResLock(MFD_FONT));
1157     buf[0] = '\0';
1158 #ifdef CONCATENATE_ITEMSPEW
1159     for (i = 0; i < n; i++, id++)
1160 #else
1161     i = n - 1;
1162     id += i;
1163 #endif
1164         get_string(id, buf + strlen(buf), sizeof(buf) - strlen(buf));
1165     gr_string_wrap(buf, MFD_VIEW_WID - 2);
1166     gr_string_size(buf, &w, &h);
1167     x = (MFD_VIEW_WID - w) / 2;
1168     y = (MFD_VIEW_HGT - h - SPEW_VERT_MARGIN) / 2 + SPEW_VERT_MARGIN;
1169     mfd_string_wrap = FALSE;
1170     mfd_full_draw_string(buf, x, y, GREEN_YELLOW_BASE, MFD_FONT, TRUE, TRUE);
1171     mfd_string_wrap = oldwrap;
1172     ResUnlock(MFD_FONT);
1173 }
1174 
mfd_item_expose(MFD * m,ubyte control)1175 void mfd_item_expose(MFD *m, ubyte control) {
1176     ubyte lastclass, currclass, lasttype, currtype = MFD_INV_NOTYPE;
1177     uchar FullRedraw = FALSE;
1178     int triple;
1179 
1180     currclass = MFDGetCurrItemClass(m->id);
1181     lastclass = MFDGetLastItemClass(m->id);
1182 
1183     if (cat2active[currclass] != NULL_ACTIVE)
1184         currtype = player_struct.actives[cat2active[currclass]];
1185     if (currtype == MFD_INV_NOTYPE)
1186         currclass = MFD_INV_NULL;
1187     lasttype = MFDGetLastItemType(m->id);
1188     MFDSetCurrItemType(m->id, currtype);
1189 
1190     MFDSetLastItemClass(m->id, currclass);
1191     MFDSetLastItemType(m->id, MFDGetCurrItemType(m->id));
1192 
1193     if (control & MFD_EXPOSE) {
1194 
1195         if ((control & MFD_EXPOSE_FULL) || (currclass != lastclass) || (currtype != lasttype))
1196             FullRedraw = TRUE;
1197 
1198         // If there is no currently selected member of the current class,
1199         // draw the blank screen thingie
1200         if (MFDGetCurrItemType(m->id) == EMPTY_WEAPON_SLOT) {
1201             mfd_expose_blank(m, control);
1202             return;
1203         }
1204 
1205         PUSH_CANVAS(pmfd_canvas);
1206         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1207         mfd_clear_rects();
1208 
1209         switch (currclass) {
1210 
1211         case MFD_INV_NULL:
1212 
1213             if (FullRedraw) {
1214                 draw_blank_mfd();
1215             }
1216             break;
1217 
1218         case MFD_INV_DRUG:
1219 
1220             triple = get_triple_from_class_nth_item(CLASS_DRUG, currtype);
1221 
1222             mfd_item_micro_expose(FullRedraw, triple);
1223             // mfd_item_micro_hires_expose(FullRedraw, triple);
1224             if (FullRedraw)
1225                 draw_mfd_item_spew(REF_STR_drugSpew0 + currtype, 1);
1226             draw_res_bm(REF_IMG_Apply, DRUG_BUTTON_X, DRUG_BUTTON_Y);
1227             mfd_add_rect(DRUG_BUTTON_X, DRUG_BUTTON_Y, DRUG_BUTTON_X + DRUG_BUTTON_W, MFD_VIEW_HGT);
1228             break;
1229 
1230         case MFD_INV_GRENADE:
1231 
1232             triple = get_triple_from_class_nth_item(CLASS_GRENADE, currtype);
1233             mfd_item_micro_expose(FullRedraw, triple);
1234             // mfd_item_micro_hires_expose(FullRedraw, triple);
1235             break;
1236 
1237         case MFD_INV_HARDWARE:
1238 
1239             //            if (currtype == HARDWARE_DATA_READER) {
1240             //               ss_safe_set_cliprect(0,0,MFD_VIEW_WID,MFD_VIEW_HGT);
1241             //               ss_bitmap(&mfd_background,0,0);
1242             //               mfd_draw_string("Video reader",5,10,WHITE,TRUE);
1243             //               mfd_draw_string("Text reader",5,15,WHITE,TRUE);
1244             //               mfd_draw_string("Email reader",5,20,WHITE,TRUE);
1245             //            }
1246             //            else
1247             {
1248                 extern uchar is_passive_hardware(int n);
1249                 int id;
1250                 short x = HARDWARE_BUTTON_X, y = HARDWARE_BUTTON_Y;
1251                 triple = get_triple_from_class_nth_item(CLASS_HARDWARE, currtype);
1252 
1253                 mfd_item_micro_expose(FullRedraw, triple);
1254                 // mfd_item_micro_hires_expose(FullRedraw, triple);
1255                 if (!is_passive_hardware(currtype)) {
1256                     id = (player_struct.hardwarez_status[currtype] & WARE_ON) ? REF_IMG_Active : REF_IMG_Inactive;
1257                     draw_res_bm(id, x, y);
1258                     mfd_add_rect(x, y, x + HARDWARE_BUTTON_W, MFD_VIEW_HGT);
1259                 }
1260                 if (FullRedraw) {
1261                     uchar version = player_struct.hardwarez[currtype];
1262                     draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * currtype, version);
1263                 }
1264             }
1265 
1266             break;
1267 
1268         case MFD_INV_AMMO:
1269             mfd_ammo_expose(control);
1270             break;
1271 
1272         case MFD_INV_SOFT_COMBAT:
1273         case MFD_INV_SOFT_DEFENSE:
1274             triple = get_ware_triple(currclass - MFD_INV_SOFT_COMBAT + WARE_SOFT_COMBAT, currtype);
1275             mfd_item_micro_expose(FullRedraw, triple);
1276             break;
1277         case MFD_INV_SOFT_MISC:
1278             // Monkey see, monkey do, monkey will destroy you.
1279             {
1280                 extern uchar is_oneshot_misc_software(int n);
1281                 short x = HARDWARE_BUTTON_X, y = HARDWARE_BUTTON_Y;
1282                 triple = get_ware_triple(currclass - MFD_INV_SOFT_COMBAT + WARE_SOFT_COMBAT, currtype);
1283 
1284                 mfd_item_micro_expose(FullRedraw, triple);
1285                 if (is_oneshot_misc_software(currtype)) {
1286                     draw_res_bm(REF_IMG_Activate, x, y);
1287                     mfd_add_rect(x, y, x + HARDWARE_BUTTON_W, MFD_VIEW_HGT);
1288                 }
1289             }
1290             break;
1291         case MFD_INV_GENINV:
1292             mfd_general_inv_expose(m, control, player_struct.inventory[currtype], FullRedraw);
1293             break;
1294 
1295         default:
1296             break;
1297         }
1298 
1299         POP_CANVAS();
1300 
1301         mfd_update_rects(m);
1302     }
1303 
1304     return;
1305 }
1306 
1307 // ---------------------------------------------------------------------------
1308 // mfd_item_handler()
1309 //
1310 // Handle clicks to, for now, the +- grenade set-time boxes.
1311 
mfd_item_handler(MFD * m,uiEvent * e)1312 uchar mfd_item_handler(MFD *m, uiEvent *e) {
1313     uchar retval = FALSE;
1314 
1315     if (!(e->mouse_data.action & MOUSE_LDOWN))
1316         return retval;
1317 
1318     switch (MFDGetCurrItemClass(m->id)) {
1319 
1320     case MFD_INV_GRENADE:
1321 
1322 #ifdef OLD_GRENADE_BUTTONS
1323     {
1324         int i;
1325         LGRect r[2];
1326         int triple;
1327         ubyte min, max;
1328         triple = get_triple_from_class_nth_item(CLASS_GRENADE, MFDGetCurrItemType(m->id));
1329         if (!(TRIP2SC(triple) == GRENADE_SUBCLASS_TIMED))
1330             return retval;
1331 
1332         min = TimedGrenadeProps[SCTRIP(triple)].min_time_set;
1333         max = TimedGrenadeProps[SCTRIP(triple)].max_time_set;
1334 
1335         RECT_OFFSETTED_RECT(&MfdGrenadeBox[0], m->rect.ul, &(r[0]));
1336         RECT_OFFSETTED_RECT(&MfdGrenadeBox[1], m->rect.ul, &(r[1]));
1337 
1338         for (i = 0; i < 2; i++) {
1339 
1340             if (RECT_TEST_PT(&r[i], e->pos)) {
1341 
1342                 if (i == 0) {
1343                     if (player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)] > min)
1344                         player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)]--;
1345                 } else if (i == 1) {
1346                     if (player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)] < max)
1347                         player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)]++;
1348                 }
1349 
1350                 mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1351             }
1352         }
1353     }
1354 #endif // OLD_GRENADE_BUTTONS
1355     break;
1356     case MFD_INV_HARDWARE: {
1357         LGRect r = {{HARDWARE_BUTTON_X, HARDWARE_BUTTON_Y},
1358                     {HARDWARE_BUTTON_X + HARDWARE_BUTTON_W, HARDWARE_BUTTON_Y + HARDWARE_BUTTON_H}};
1359         RECT_OFFSETTED_RECT(&r, m->rect.ul, &r);
1360         if (RECT_TEST_PT(&r, e->pos)) {
1361             use_ware(WARE_HARD, player_struct.actives[ACTIVE_HARDWARE]);
1362             mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1363             retval = TRUE;
1364         }
1365     } break;
1366     case MFD_INV_SOFT_MISC: {
1367         LGRect r = {{HARDWARE_BUTTON_X, HARDWARE_BUTTON_Y},
1368                     {HARDWARE_BUTTON_X + HARDWARE_BUTTON_W, HARDWARE_BUTTON_Y + HARDWARE_BUTTON_H}};
1369         RECT_OFFSETTED_RECT(&r, m->rect.ul, &r);
1370         if (RECT_TEST_PT(&r, e->pos)) {
1371             use_ware(WARE_SOFT_MISC, player_struct.actives[ACTIVE_MISC_SOFT]);
1372             mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1373             retval = TRUE;
1374         }
1375     } break;
1376     case MFD_INV_DRUG: {
1377         LGRect r = {{DRUG_BUTTON_X, DRUG_BUTTON_Y}, {DRUG_BUTTON_X + DRUG_BUTTON_W, DRUG_BUTTON_Y + DRUG_BUTTON_H}};
1378         RECT_OFFSETTED_RECT(&r, m->rect.ul, &r);
1379         // Why is it that for wares, it's use_drug whereas for drugs it's drug_use?  Perhaps we'll never know.
1380         if (RECT_TEST_PT(&r, e->pos)) {
1381             drug_use(player_struct.actives[ACTIVE_DRUG]);
1382             mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1383             retval = TRUE;
1384         }
1385     } break;
1386     case MFD_INV_GENINV:
1387         mfd_general_inv_handler(m, e, player_struct.actives[ACTIVE_GENERAL]);
1388         break;
1389 
1390     case MFD_INV_AMMO:
1391         mfd_ammo_handler(m, e);
1392         break;
1393     }
1394 
1395     return retval;
1396 }
1397 
1398 // ----------------------
1399 // * ITEM MFD FOR LANTERN
1400 // ----------------------
1401 
1402 #define LANTERN_BARRAY_X 2
1403 #define LANTERN_BARRAY_WD (MFD_VIEW_WID - 6)
1404 #define LANTERN_BARRAY_Y 45
1405 
1406 #define LANTERN_LAST_SETTING(mfd) (player_struct.mfd_func_data[MFD_LANTERN_FUNC][mfd])
1407 #define LANTERN_LAST_VERSION(mfd) (player_struct.mfd_func_data[MFD_LANTERN_FUNC][NUM_MFDS + mfd])
1408 #define LANTERN_LAST_STATE(mfd) (player_struct.mfd_func_data[MFD_LANTERN_FUNC][2 * NUM_MFDS + mfd])
1409 #define LANTERN_BARRAY_IDX 0
1410 
1411 extern uchar muzzle_fire_light;
1412 
1413 int energy_cost(int warenum);
1414 
mfd_lantern_button_handler(MFD * mfd,LGPoint bttn,uiEvent * ev,void * data)1415 uchar mfd_lantern_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) {
1416     int n = CPTRIP(LANTERN_HARD_TRIPLE);
1417     int s = player_struct.hardwarez_status[n];
1418     void mfd_lantern_setting(int setting);
1419 
1420     if (bttn.x >= player_struct.hardwarez[n] || !(ev->subtype & MOUSE_LDOWN))
1421         return FALSE; // Version too high
1422     if (bttn.x == LAMP_SETTING(s)) {
1423         use_ware(WARE_HARD, n);
1424         return TRUE;
1425     }
1426     mfd_lantern_setting(bttn.x);
1427     return TRUE;
1428 }
1429 
mfd_lantern_setting(int setting)1430 void mfd_lantern_setting(int setting) {
1431     int n = CPTRIP(LANTERN_HARD_TRIPLE);
1432     int s = player_struct.hardwarez_status[n];
1433 
1434     if (s & WARE_ON)
1435         set_player_energy_spend(player_struct.energy_spend - energy_cost(n));
1436     LAMP_SETTING_SET(s, setting);
1437     player_struct.hardwarez_status[n] = s;
1438     if (!muzzle_fire_light) {
1439         player_struct.light_value = s;
1440         lamp_set_vals();
1441     }
1442     if (player_struct.hardwarez_status[n] & WARE_ON)
1443         set_player_energy_spend(lg_min(MAX_ENERGY, (int)player_struct.energy_spend + energy_cost(n)));
1444     mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1445 }
1446 
mfd_lanternware_init(MFD_Func * f)1447 errtype mfd_lanternware_init(MFD_Func *f) {
1448     int cnt = 0;
1449     LGPoint bsize;
1450     LGPoint bdims;
1451     LGRect r;
1452     errtype err;
1453     bsize.x = res_bm_width(REF_IMG_LitLamp0);
1454     bsize.y = res_bm_height(REF_IMG_LitLamp0);
1455     bdims.x = LAMP_VERSIONS;
1456     bdims.y = 1;
1457     r.ul.x = LANTERN_BARRAY_X;
1458     r.ul.y = LANTERN_BARRAY_Y;
1459     r.lr.x = r.ul.x + LANTERN_BARRAY_WD;
1460     r.lr.y = r.ul.y + bsize.y;
1461     err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_lantern_button_handler, NULL);
1462     if (err != OK)
1463         return err;
1464     f->handler_count = cnt;
1465     return OK;
1466 }
1467 
mfd_lanternware_expose(MFD * mfd,ubyte control)1468 void mfd_lanternware_expose(MFD *mfd, ubyte control) {
1469     int n, s, v;
1470     uchar full = control & MFD_EXPOSE_FULL;
1471     if (control == 0)
1472         return;
1473     n = CPTRIP(LANTERN_HARD_TRIPLE);
1474     s = player_struct.hardwarez_status[n];
1475     v = player_struct.hardwarez[n];
1476     PUSH_CANVAS(pmfd_canvas);
1477     mfd_clear_rects();
1478     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1479     if (!full_game_3d)
1480         ss_bitmap(&mfd_background, 0, 0);
1481     // gr_bitmap(&mfd_background, 0, 0);
1482     mfd_item_micro_expose(full, LANTERN_HARD_TRIPLE);
1483     // mfd_item_micro_hires_expose(full,LANTERN_HARD_TRIPLE);
1484     if (full)
1485         draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v);
1486     if (full || LAMP_SETTING(s) != LANTERN_LAST_SETTING(mfd->id) || LANTERN_LAST_VERSION(mfd->id) != v ||
1487         LANTERN_LAST_STATE(mfd->id) != (s & WARE_ON)) {
1488         int xjump = LANTERN_BARRAY_WD - res_bm_width(REF_IMG_LitLamp0);
1489         int i;
1490         for (i = 0; i < v; i++) {
1491             int lit = i == LAMP_SETTING(s) && (s & WARE_ON);
1492             int id = (lit) ? REF_IMG_LitLamp0 : REF_IMG_UnlitLamp0;
1493             short x = LANTERN_BARRAY_X + i * xjump / (LAMP_VERSIONS - 1);
1494             short y = LANTERN_BARRAY_Y;
1495             draw_raw_resource_bm(id + i, x, LANTERN_BARRAY_Y);
1496             if (i == LAMP_SETTING(s)) {
1497                 gr_set_fcolor(GREEN_BASE + 2);
1498                 ss_box(x - 1, y - 1, x + res_bm_width(id + i) + 1, y + res_bm_height(id + i) + 1);
1499             }
1500         }
1501         mfd_add_rect(LANTERN_BARRAY_X - 2, LANTERN_BARRAY_Y - 2, LANTERN_BARRAY_X + LANTERN_BARRAY_WD + 2,
1502                      MFD_VIEW_HGT);
1503         LANTERN_LAST_SETTING(mfd->id) = LAMP_SETTING(s);
1504         LANTERN_LAST_VERSION(mfd->id) = v;
1505         LANTERN_LAST_STATE(mfd->id) = s & WARE_ON;
1506     }
1507     POP_CANVAS();
1508     mfd_update_rects(mfd);
1509 }
1510 
1511 // ----------------------
1512 // * ITEM MFD FOR SHIELD
1513 // ----------------------
1514 
1515 #define SHIELD_BARRAY_X 2
1516 #define SHIELD_BARRAY_WD (MFD_VIEW_WID - 4)
1517 #define SHIELD_BARRAY_Y 45
1518 
1519 #define SHIELD_LAST_STATUS(mfd) (player_struct.mfd_func_data[MFD_SHIELD_FUNC][mfd])
1520 #define SHIELD_LAST_VERSION(mfd) (player_struct.mfd_func_data[MFD_SHIELD_FUNC][NUM_MFDS + mfd])
1521 #define SHIELD_BARRAY_IDX 0
1522 
1523 #define SHIELD_SETTINGS 3
1524 
1525 void mfd_shield_setting(int setting);
1526 uchar mfd_shield_handler(MFD *m, uiEvent *e);
1527 uchar mfd_shield_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data);
1528 errtype mfd_shield_init(MFD_Func *f);
1529 void mfd_shieldware_expose(MFD *mfd, ubyte control);
1530 
mfd_shield_handler(MFD * m,uiEvent * e)1531 uchar mfd_shield_handler(MFD *m, uiEvent *e) {
1532     uchar retval = FALSE;
1533     LGPoint pos = e->pos;
1534     ubyte n = CPTRIP(SHIELD_HARD_TRIPLE);
1535     ubyte v = player_struct.hardwarez[n];
1536     if (v != SHIELD_VERSIONS) // are we at max version
1537         return FALSE;
1538     if (!(e->subtype & MOUSE_LDOWN))
1539         return FALSE;
1540     pos.x -= m->rect.ul.x - SHIELD_BARRAY_X;
1541     pos.y -= m->rect.ul.y - SHIELD_BARRAY_Y;
1542     if (pos.x > 0 && pos.x < SHIELD_BARRAY_WD && pos.y > 0) {
1543         use_ware(WARE_HARD, n);
1544         mfd_shield_setting(v - 1);
1545         retval = TRUE;
1546     }
1547     return retval;
1548 }
1549 
mfd_shield_button_handler(MFD * mfd,LGPoint bttn,uiEvent * ev,void * data)1550 uchar mfd_shield_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) {
1551     int n = CPTRIP(SHIELD_HARD_TRIPLE);
1552     int s = player_struct.hardwarez_status[n];
1553 
1554     if (bttn.x >= player_struct.hardwarez[n] || !(ev->subtype & MOUSE_LDOWN))
1555         return FALSE; // Version too high
1556     if (SHIELD_SETTING(s) == bttn.x) {
1557         use_ware(WARE_HARD, n);
1558         return TRUE;
1559     }
1560     mfd_shield_setting(bttn.x);
1561     return TRUE;
1562 }
1563 
mfd_shield_setting(int setting)1564 void mfd_shield_setting(int setting) {
1565     extern void shield_set_absorb(void);
1566     int n = CPTRIP(SHIELD_HARD_TRIPLE);
1567     int s = player_struct.hardwarez_status[n];
1568 
1569     if (s & WARE_ON)
1570         set_player_energy_spend(player_struct.energy_spend - energy_cost(n));
1571     SHIELD_SETTING_SET(s, setting);
1572     player_struct.hardwarez_status[n] = s;
1573     if (s & WARE_ON) {
1574         shield_set_absorb();
1575         set_player_energy_spend(lg_min(MAX_ENERGY, (int)player_struct.energy_spend + energy_cost(n)));
1576     }
1577     mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1578 }
1579 
mfd_shield_init(MFD_Func * f)1580 errtype mfd_shield_init(MFD_Func *f) {
1581     int cnt = 0;
1582     LGPoint bsize;
1583     LGPoint bdims;
1584     LGRect r;
1585     errtype err;
1586     bsize.x = res_bm_width(REF_IMG_LitShield0);
1587     bsize.y = res_bm_height(REF_IMG_LitShield0);
1588     bdims.x = SHIELD_SETTINGS;
1589     bdims.y = 1;
1590     r.ul.x = SHIELD_BARRAY_X;
1591     r.ul.y = SHIELD_BARRAY_Y;
1592     r.lr.x = r.ul.x + SHIELD_BARRAY_WD;
1593     r.lr.y = r.ul.y + bsize.y;
1594     err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_shield_button_handler, NULL);
1595     if (err != OK)
1596         return err;
1597     f->handler_count = cnt;
1598     return OK;
1599 }
1600 
mfd_shieldware_expose(MFD * mfd,ubyte control)1601 void mfd_shieldware_expose(MFD *mfd, ubyte control) {
1602     int n, s, v;
1603     uchar full = control & MFD_EXPOSE_FULL;
1604     if (control == 0)
1605         return;
1606     n = CPTRIP(SHIELD_HARD_TRIPLE);
1607     s = player_struct.hardwarez_status[n];
1608     v = player_struct.hardwarez[n];
1609     PUSH_CANVAS(pmfd_canvas);
1610     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1611     mfd_clear_rects();
1612     if (!full_game_3d)
1613         ss_bitmap(&mfd_background, 0, 0);
1614     // gr_bitmap(&mfd_background, 0, 0);
1615     mfd_item_micro_expose(full, SHIELD_HARD_TRIPLE);
1616     // mfd_item_micro_hires_expose(full,SHIELD_HARD_TRIPLE);
1617     if (full)
1618         draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v);
1619     if (full || s != SHIELD_LAST_STATUS(mfd->id) || SHIELD_LAST_VERSION(mfd->id) != v) {
1620         int xjump = SHIELD_BARRAY_WD / SHIELD_SETTINGS;
1621         int i;
1622         if (v >= SHIELD_VERSIONS) {
1623             int id = (s & WARE_ON) ? REF_IMG_LitShieldSuper : REF_IMG_UnlitShieldSuper;
1624             draw_raw_resource_bm(id, SHIELD_BARRAY_X + (SHIELD_BARRAY_WD - res_bm_width(id)) / 2, SHIELD_BARRAY_Y);
1625             mfd_add_rect(SHIELD_BARRAY_X, SHIELD_BARRAY_Y, SHIELD_BARRAY_X + SHIELD_BARRAY_WD, MFD_VIEW_WID);
1626         } else
1627             for (i = 0; i < v; i++) {
1628                 int id = (i == SHIELD_SETTING(s) && (s & WARE_ON)) ? REF_IMG_LitShield0 : REF_IMG_UnlitShield0;
1629                 grs_bitmap *bm = lock_bitmap_from_ref(id + i);
1630                 short x = SHIELD_BARRAY_X + i * xjump;
1631                 short y = SHIELD_BARRAY_Y;
1632                 mfd_draw_bitmap(bm, x, y);
1633                 if (i == SHIELD_SETTING(s)) {
1634                     gr_set_fcolor(GREEN_BASE + 2);
1635                     ss_box(x - 1, y - 1, x + bm->w + 1, y + bm->h + 1);
1636                 }
1637                 mfd_add_rect(x - 2, y - 2, x + bm->w + 2, y + bm->h + 2);
1638                 RefUnlock(id + i);
1639             }
1640         SHIELD_LAST_STATUS(mfd->id) = s;
1641         SHIELD_LAST_VERSION(mfd->id) = v;
1642     }
1643     POP_CANVAS();
1644     mfd_update_rects(mfd);
1645 }
1646 
1647 // ----------------------
1648 // * ITEM MFD FOR MOTION WARE
1649 // ----------------------
1650 
1651 #define MOTION_BARRAY_WD (3 * MFD_VIEW_WID / 4)
1652 #define MOTION_BARRAY_X ((MFD_VIEW_WID - MOTION_BARRAY_WD) / 2)
1653 #define MOTION_BARRAY_Y 48
1654 
1655 #define MOTION_LAST_STATUS(mfd) (player_struct.mfd_func_data[MFD_MOTION_FUNC][mfd])
1656 #define MOTION_LAST_VERSION(mfd) (player_struct.mfd_func_data[MFD_MOTION_FUNC][NUM_MFDS + mfd])
1657 #define MOTION_BARRAY_IDX 0
1658 
1659 #define MOTION_SETTING LAMP_SETTING
1660 #define MOTION_SETTING_SET LAMP_SETTING_SET
1661 
1662 #define MOTION_BUTTONS 2
1663 
1664 uchar mfd_motion_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data);
1665 errtype mfd_motion_init(MFD_Func *f);
1666 void mfd_motionware_expose(MFD *mfd, ubyte control);
1667 
mfd_motion_button_handler(MFD * mfd,LGPoint bttn,uiEvent * ev,void * data)1668 uchar mfd_motion_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) {
1669     int n = CPTRIP(MOTION_HARD_TRIPLE);
1670     int s = player_struct.hardwarez_status[n];
1671     //   if (bttn.x >= player_struct.hardwarez[n] ||
1672     if (!(ev->subtype & MOUSE_LDOWN))
1673         return FALSE;
1674     if (MOTION_SETTING(s) == bttn.x) {
1675         use_ware(WARE_HARD, n);
1676         return TRUE;
1677     }
1678     if (s & WARE_ON)
1679         set_player_energy_spend(player_struct.energy_spend - energy_cost(n));
1680     MOTION_SETTING_SET(s, bttn.x);
1681     player_struct.hardwarez_status[n] = s;
1682     if (!(s & WARE_ON)) {
1683         //      use_ware(WARE_HARD,n);
1684     } else {
1685         motionware_mode = bttn.x + 1;
1686         set_player_energy_spend(lg_min(MAX_ENERGY, (int)player_struct.energy_spend + energy_cost(n)));
1687     }
1688     mfd_notify_func(MFD_MOTION_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1689     return TRUE;
1690 }
1691 
mfd_motion_init(MFD_Func * f)1692 errtype mfd_motion_init(MFD_Func *f) {
1693     int cnt = 0;
1694     LGPoint bsize;
1695     LGPoint bdims;
1696     LGRect r;
1697     errtype err;
1698     bsize.x = res_bm_width(REF_IMG_LitMotion0);
1699     bsize.y = res_bm_height(REF_IMG_LitMotion0);
1700     bdims.x = MOTION_BUTTONS;
1701     bdims.y = 1;
1702     r.ul.x = MOTION_BARRAY_X;
1703     r.ul.y = MOTION_BARRAY_Y;
1704     r.lr.x = r.ul.x + MOTION_BARRAY_WD;
1705     r.lr.y = r.ul.y + bsize.y;
1706     err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_motion_button_handler, NULL);
1707     if (err != OK)
1708         return err;
1709     f->handler_count = cnt;
1710     return OK;
1711 }
1712 
mfd_motionware_expose(MFD * mfd,ubyte control)1713 void mfd_motionware_expose(MFD *mfd, ubyte control) {
1714     int n, s, v;
1715     uchar full = control & MFD_EXPOSE_FULL;
1716     if (control == 0)
1717         return;
1718     n = CPTRIP(MOTION_HARD_TRIPLE);
1719     s = player_struct.hardwarez_status[n];
1720     v = player_struct.hardwarez[n];
1721     PUSH_CANVAS(pmfd_canvas);
1722     mfd_clear_rects();
1723     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1724     mfd_item_micro_expose(full, MOTION_HARD_TRIPLE);
1725     // mfd_item_micro_hires_expose(full,MOTION_HARD_TRIPLE);
1726     if (full)
1727         draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v);
1728     if (full || s != MOTION_LAST_STATUS(mfd->id) || MOTION_LAST_VERSION(mfd->id) != v) {
1729         int xjump = MOTION_BARRAY_WD / MOTION_BUTTONS;
1730         int i;
1731         for (i = 0; i < lg_min(v, MOTION_BUTTONS); i++) {
1732             int id = ((i == MOTION_SETTING(s) && (s & WARE_ON))) ? REF_IMG_LitMotion0 : REF_IMG_UnlitMotion0;
1733             grs_bitmap *bm = lock_bitmap_from_ref(id + i);
1734             short x = MOTION_BARRAY_X + i * xjump;
1735             short y = MOTION_BARRAY_Y;
1736             mfd_draw_bitmap(bm, x, y);
1737             if (i == MOTION_SETTING(s)) {
1738                 gr_set_fcolor(GREEN_BASE + 2);
1739                 ss_box(x - 1, y - 1, x + bm->w + 1, y + bm->h + 1);
1740             }
1741             mfd_add_rect(x - 2, y - 2, x + bm->w + 2, y + bm->h + 2);
1742             RefUnlock(id + i);
1743         }
1744         MOTION_LAST_STATUS(mfd->id) = s;
1745         MOTION_LAST_VERSION(mfd->id) = v;
1746     }
1747     POP_CANVAS();
1748     mfd_update_rects(mfd);
1749 }
1750 
1751 // -----------------
1752 // TIMED GRENADE MFD
1753 // -----------------
1754 
1755 #define MFD_GRENADE_FUNC 10
1756 
1757 #define GRENADE_TIME_UNIT 10 // fraction of second for each time second unit
1758 
1759 #define GRENADE_SLIDER_X MFD_BEAM_RECT_X1
1760 #define GRENADE_SLIDER_Y 51
1761 #define GRENADE_SLIDER_W 70
1762 #define GRENADE_SLIDER_H 5
1763 
1764 #define GRENADE_SLIDER_IDX 0
1765 
1766 #define GRENADE_MOUSE_CONSTRAINED (player_struct.mfd_func_data[MFD_GRENADE_FUNC][7])
1767 
1768 #define GRENADE_HIRES_CUTOFF 100
1769 
1770 uchar mfd_grenade_slider_handler(MFD *m, short val, uiEvent *ev, void *data);
1771 uchar mfd_grenade_handler(MFD *m, uiEvent *ev);
1772 errtype mfd_grenade_init(MFD_Func *f);
1773 void mfd_grenade_expose(MFD *mfd, ubyte control);
1774 
mfd_grenade_slider_handler(MFD * m,short val,uiEvent * ev,void * data)1775 uchar mfd_grenade_slider_handler(MFD *m, short val, uiEvent *ev, void *data) {
1776     int n = player_struct.actives[ACTIVE_GRENADE];
1777     int triple = nth_after_triple(MAKETRIP(CLASS_GRENADE, 0, 0), n);
1778     short min = TimedGrenadeProps[SCTRIP(triple)].min_time_set * GRENADE_TIME_UNIT;
1779     short max = TimedGrenadeProps[SCTRIP(triple)].max_time_set * GRENADE_TIME_UNIT;
1780     short width = GRENADE_SLIDER_W;
1781     short setting;
1782 
1783     if (max > 2 * GRENADE_HIRES_CUTOFF) {
1784         width /= 2;
1785         if (val >= GRENADE_SLIDER_W / 2) {
1786             val -= GRENADE_SLIDER_W / 2;
1787             min = GRENADE_HIRES_CUTOFF;
1788         } else
1789             max = GRENADE_HIRES_CUTOFF;
1790     }
1791     setting = val * (max - min) / width + min;
1792     player_struct.grenades_time_setting[n] = setting;
1793     if (ev->subtype & MOUSE_LDOWN) {
1794         LGRect r = mfd_funcs[MFD_GRENADE_FUNC].handlers[GRENADE_SLIDER_IDX].r;
1795         int my = ev->pos.y;
1796         RECT_OFFSETTED_RECT(&r, m->rect.ul, &r);
1797         slider_cursor.hotspot.x = slider_cursor_bmap.w / 2;
1798         slider_cursor.hotspot.y = (slider_cursor_bmap.h / 2) + my - (r.ul.y + r.lr.y) / 2;
1799         ui_mouse_constrain_xy(r.ul.x, my, r.lr.x - 2, my);
1800 
1801         GRENADE_MOUSE_CONSTRAINED = m->id + 1;
1802         // Get our funky mfd-beam-phaser-setting cursor
1803 #ifdef CURSOR_BACKUPS
1804         backup[20] = (uchar *)malloc(f->bm.w * f->bm.h);
1805         LG_memcpy(backup[20], f->bm.bits, f->bm.w * f->bm.h);
1806         gr_init_bm(&backup_mfd_cursor, backup[14], BMT_FLAT8, 0, mfd_cursor.w, mfd_cursor.h);
1807 #endif
1808         uiPushRegionCursor(MFD_REGION(m), &slider_cursor);
1809     }
1810     if ((ev->mouse_data.buttons & (1 << MOUSE_LBUTTON)) == 0) {
1811         uiCursorStack *cs;
1812         uiGetRegionCursorStack(MFD_REGION(m), &cs);
1813         uiPopCursorEvery(cs, &slider_cursor);
1814 
1815         if (GRENADE_MOUSE_CONSTRAINED) {
1816             mouse_unconstrain();
1817             GRENADE_MOUSE_CONSTRAINED = 0;
1818             mfd_notify_func(MFD_GRENADE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE);
1819         }
1820     }
1821     mfd_notify_func(MFD_GRENADE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1822     return TRUE;
1823 }
1824 
mfd_grenade_handler(MFD * m,uiEvent * ev)1825 uchar mfd_grenade_handler(MFD *m, uiEvent *ev) {
1826     LGRect r = mfd_funcs[MFD_GRENADE_FUNC].handlers[GRENADE_SLIDER_IDX].r;
1827     RECT_MOVE(&r, m->rect.ul);
1828     if (!RECT_TEST_PT(&r, ev->pos)) {
1829         uiCursorStack *cs;
1830         uiGetRegionCursorStack(MFD_REGION(m), &cs);
1831         uiPopCursorEvery(cs, &slider_cursor);
1832         mfd_notify_func(MFD_GRENADE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
1833     }
1834     return FALSE;
1835 }
1836 
mfd_grenade_init(MFD_Func * f)1837 errtype mfd_grenade_init(MFD_Func *f) {
1838     int cnt = 0;
1839     errtype err;
1840     LGRect r = {{GRENADE_SLIDER_X, GRENADE_SLIDER_Y},
1841                 {GRENADE_SLIDER_X + GRENADE_SLIDER_W, GRENADE_SLIDER_Y + GRENADE_SLIDER_H}};
1842     err = MFDSliderInit(&f->handlers[cnt++], &r, mfd_grenade_slider_handler, NULL);
1843     if (err != OK)
1844         return err;
1845     f->handler_count = cnt;
1846 #ifdef CURSOR_BACKUPS
1847     backup[21] = (uchar *)Malloc(slider_bmap.w * slider_bmap.h);
1848     LG_memcpy(backup[21], slider_bmap.bits, slider_bmap.w * slider_bmap.h);
1849     gr_init_bm(&backup_slider_cursor, backup[21], BMT_FLAT8, 0, slider_bmap.w, slider_bmap.h);
1850 #endif
1851     return OK;
1852 }
1853 
1854 #define LAST_GRENADE(mfd) (player_struct.mfd_func_data[MFD_GRENADE_FUNC][mfd])
1855 #define LAST_GRENADE_SETTING(mfd) (player_struct.mfd_func_data[MFD_GRENADE_FUNC][mfd + 2])
1856 
1857 #define GRENADE_SLIDER_BORDER MFD_BEAMWPN_STAT_BORDER
1858 #define GRENADE_SLIDER_SETTING_COLOR MFD_BEAMWPN_STAT_CHARGE
1859 
1860 #define TIME_TEXT_Y (GRENADE_SLIDER_Y - 8)
1861 #define TIME_TEXT_LEN 128
1862 
mfd_grenade_expose(MFD * mfd,ubyte control)1863 void mfd_grenade_expose(MFD *mfd, ubyte control) {
1864     MFD_Func *f = &mfd_funcs[MFD_GRENADE_FUNC];
1865     int n = player_struct.actives[ACTIVE_GRENADE];
1866     int triple = nth_after_triple(MAKETRIP(CLASS_GRENADE, 0, 0), n);
1867     uchar full = (control & MFD_EXPOSE_FULL) || (n != LAST_GRENADE(mfd->id));
1868     short setting = player_struct.grenades_time_setting[n];
1869 
1870     if (control == 0) {
1871 
1872         uiCursorStack *cs;
1873         uiGetRegionCursorStack(MFD_REGION(mfd), &cs);
1874         uiPopCursorEvery(cs, &slider_cursor);
1875 
1876         if (GRENADE_MOUSE_CONSTRAINED == mfd->id + 1) {
1877             mouse_unconstrain();
1878             GRENADE_MOUSE_CONSTRAINED = 0;
1879         }
1880         return;
1881     }
1882     mfd_clear_rects();
1883     PUSH_CANVAS(pmfd_canvas);
1884     ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1885     if (!full_game_3d)
1886         ss_bitmap(&mfd_background, 0, 0);
1887     // gr_bitmap(&mfd_background, 0, 0);
1888     mfd_item_micro_expose(full, triple);
1889     // mfd_item_micro_hires_expose(full,triple);
1890     LAST_GRENADE(mfd->id) = n;
1891     if (full || LAST_GRENADE_SETTING(mfd->id) != setting) {
1892         char buf[TIME_TEXT_LEN];
1893         LGRect *r = &f->handlers[GRENADE_SLIDER_IDX].r;
1894         short min = TimedGrenadeProps[SCTRIP(triple)].min_time_set * GRENADE_TIME_UNIT;
1895         short max = TimedGrenadeProps[SCTRIP(triple)].max_time_set * GRENADE_TIME_UNIT;
1896         short width = GRENADE_SLIDER_W;
1897         short base = 0;
1898         short x;
1899         if (max > 2 * GRENADE_HIRES_CUTOFF) {
1900             width /= 2;
1901             if (setting < GRENADE_HIRES_CUTOFF)
1902                 max = GRENADE_HIRES_CUTOFF;
1903             else {
1904                 base = GRENADE_SLIDER_W / 2;
1905                 min = GRENADE_HIRES_CUTOFF;
1906             }
1907         }
1908         x = (setting - min) * width / (max - min) + base;
1909         gr_set_fcolor(GRENADE_SLIDER_BORDER);
1910         ss_box(r->ul.x - 1, r->ul.y - 1, r->lr.x, r->lr.y);
1911         mfd_add_rect(r->ul.x - 1, r->ul.y - 1, r->lr.x, r->lr.y + 1);
1912         gr_set_fcolor(GRENADE_SLIDER_SETTING_COLOR);
1913         if (!GRENADE_MOUSE_CONSTRAINED)
1914             draw_raw_resource_bm(REF_IMG_BeamSetting, r->ul.x + x - 3, r->ul.y - 1);
1915 
1916 
1917         sprintf(buf, "%s %d.%d", get_string(REF_STR_TimeSetting, NULL, TIME_TEXT_LEN), setting / 10, setting % 10);
1918         //numtostring(setting / 10, buf + strlen(buf)); // itoa(setting/10,buf+strlen(buf),10);
1919         //strcat(buf, ".");
1920         //numtostring(setting % 10, buf + strlen(buf)); // itoa(setting%10,buf+strlen(buf),10);
1921         {
1922             LGRect r = {{GRENADE_SLIDER_X, TIME_TEXT_Y}, {MFD_VIEW_WID, GRENADE_SLIDER_Y - 1}};
1923             mfd_partial_clear(&r);
1924         }
1925         mfd_draw_string(buf, GRENADE_SLIDER_X, TIME_TEXT_Y, GRENADE_SLIDER_BORDER, TRUE);
1926         LAST_GRENADE_SETTING(mfd->id) = setting;
1927     }
1928     POP_CANVAS();
1929     mfd_update_rects(mfd);
1930 }
1931 
1932 // ------------------
1933 // * THE BIO WARE MFD
1934 // ------------------
1935 
1936 #define BIO_TEXT_X 29
1937 
1938 // ---------------------------------------------------------------------------
1939 // mfd_bioware_expose()
1940 //
1941 // This is the bioware, activated in the info window.  It displays stats.
1942 // As of now, it's pretty sketchy.
1943 
1944 #define LAST_HP(mfd) (mfd_fdata[MFD_BIOWARE_FUNC][4 * mfd])
1945 #define LAST_FATIGUE(mfd) (mfd_fdata[MFD_BIOWARE_FUNC][1 + 4 * mfd])
1946 #define LAST_DRUGBITS(mfd) (*(ushort *)&mfd_fdata[MFD_BIOWARE_FUNC][2 + 4 * mfd])
1947 
1948 // this is stolen from gamesys.c
1949 #define MAX_FATIGUE 10000
1950 #define MAX_HP UCHAR_MAX
1951 
1952 #define BIO_DRUG_UP 1   // experincing normal effects
1953 #define BIO_DRUG_DOWN 2 // experiencing after effects.
1954 #define BIO_DRUG_CLEAN 0
1955 
1956 #define BITS_PER_DRUG 2
1957 
mfd_bioware_expose(MFD * m,ubyte control)1958 void mfd_bioware_expose(MFD *m, ubyte control) {
1959     uchar full = control & MFD_EXPOSE_FULL;
1960     int i, y = 2, triple;
1961     char buf2[12];
1962     LGRect r;
1963 
1964     r.ul.x = BIO_TEXT_X;
1965     r.ul.y = 20;
1966     r.lr.x = MFD_VIEW_WID;
1967     r.lr.y = MFD_VIEW_HGT;
1968 
1969     // turn off the bioware if there's no exposed mfd.
1970     {
1971         extern WARE HardWare[NUM_HARDWAREZ];
1972         int i;
1973         uchar on = full || control & MFD_EXPOSE;
1974         for (i = 0; i < NUM_MFDS; i++) {
1975             ubyte slot = player_struct.mfd_current_slots[i];
1976             ubyte func = mfd_get_func(i, slot);
1977             if (func == MFD_BIOWARE_FUNC && (control != 0 || m->id != i))
1978                 on = TRUE;
1979         }
1980         if (on == !(player_struct.hardwarez_status[HARDWARE_BIOWARE] & WARE_ON)) {
1981             use_ware(WARE_HARD, HARDWARE_BIOWARE);
1982         }
1983     }
1984 
1985     if (control & MFD_EXPOSE) {
1986         char *s;
1987         char pct[] = "%";
1988         short x;
1989         ubyte v = player_struct.hardwarez[HARDWARE_BIOWARE];
1990         int ref = MKREF(RES_mfdArtOverlays, MFD_ART_HUMAN);
1991         uchar stam = player_struct.drug_status[CPTRIP(STAMINA_DRUG_TRIPLE)] > 0;
1992 
1993         PUSH_CANVAS(pmfd_canvas);
1994         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
1995         mfd_clear_rects();
1996         if (!full_game_3d)
1997             ss_bitmap(&mfd_background, 0, 0);
1998         // gr_bitmap(&mfd_background, 0, 0);
1999         gr_set_font(ResLock(MFD_FONT));
2000 
2001         if (full) {
2002             draw_res_bm(ref, 0, 0);
2003             mfd_add_rect(0, 0, res_bm_width(ref), res_bm_height(ref));
2004         }
2005 #ifdef BIOWARE_TITLE
2006         // Title
2007         if (full) {
2008             s = get_temp_string(REF_STR_BiowareTitle);
2009             mfd_draw_string(s, BIO_TEXT_X, y, GREEN_YELLOW_BASE, TRUE);
2010         }
2011         y += Y_STEP;
2012 #endif
2013 
2014         // Health
2015         if (full || (LAST_HP(m->id) != player_struct.hit_points)) {
2016             uint hp;
2017             LGRect rest;
2018             s = get_temp_string(REF_STR_BiowareHealth);
2019             x = BIO_TEXT_X + gr_string_width(s);
2020             if (full)
2021                 mfd_draw_string(s, BIO_TEXT_X, y, GREEN_YELLOW_BASE, TRUE);
2022             hp = (100 * player_struct.hit_points + (MAX_HP / 2)) / MAX_HP;
2023             if (hp == 0 && player_struct.hit_points > 0)
2024                 hp = 1;
2025             sprintf(buf2, "%d", hp);
2026 
2027             // hack to save space in display.  Use "I" for "1", knowing
2028             // that "I" is narrower.
2029             string_replace_char(buf2, '1', 'I');
2030 
2031             mfd_draw_string(buf2, x, y, GREEN_YELLOW_BASE, TRUE);
2032             x += gr_string_width(buf2);
2033             mfd_draw_string(pct, x, y, GREEN_YELLOW_BASE, TRUE);
2034             x += gr_string_width(pct);
2035             rest.ul.x = x;
2036             rest.ul.y = y;
2037             rest.lr.x = MFD_VIEW_WID;
2038             rest.lr.y = y + Y_STEP;
2039             mfd_partial_clear(&rest);
2040             LAST_HP(m->id) = player_struct.hit_points;
2041         }
2042         y += Y_STEP;
2043 
2044         // Fatigue
2045         if (full || stam || (LAST_FATIGUE(m->id) != 100 * (uint)player_struct.fatigue / MAX_FATIGUE)) {
2046             LGRect rest;
2047             ubyte f = 100 * (uint)player_struct.fatigue / MAX_FATIGUE;
2048             s = get_temp_string(REF_STR_BiowareFatigue);
2049             x = BIO_TEXT_X + gr_string_width(s);
2050             if (full)
2051                 mfd_draw_string(s, BIO_TEXT_X, y, GREEN_YELLOW_BASE, TRUE);
2052             if (stam) {
2053                 strcpy(buf2, "--");
2054                 mfd_draw_string(buf2, x, y, GREEN_YELLOW_BASE, TRUE);
2055                 x += gr_string_width(buf2);
2056             } else {
2057                 sprintf(buf2, "%d", f); // itoa(f,buf2,10);
2058 
2059                 // hack to save space in display.  Use "I" for "1", knowing
2060                 // that "I" is narrower.
2061                 string_replace_char(buf2, '1', 'I');
2062                 mfd_draw_string(buf2, x, y, GREEN_YELLOW_BASE, TRUE);
2063                 x += gr_string_width(buf2);
2064                 mfd_draw_string(pct, x, y, GREEN_YELLOW_BASE, TRUE);
2065                 x += gr_string_width(pct);
2066             }
2067 
2068             rest.ul.x = x;
2069             rest.ul.y = y;
2070             rest.lr.x = MFD_VIEW_WID;
2071             rest.lr.y = y + Y_STEP;
2072             mfd_partial_clear(&rest);
2073             LAST_FATIGUE(m->id) = f;
2074         }
2075         y += Y_STEP;
2076 
2077         if (v > 1) {
2078             ushort drugbits = 0;
2079             for (i = 0; i < NUM_DRUGS; i++) {
2080                 if (player_struct.drug_status[i] > 0)
2081                     drugbits |= (BIO_DRUG_UP << (i * BITS_PER_DRUG));
2082                 if (player_struct.drug_status[i] < 0)
2083                     drugbits |= (BIO_DRUG_DOWN << (i * BITS_PER_DRUG));
2084             }
2085 
2086             if (full || drugbits != LAST_DRUGBITS(m->id)) {
2087                 short savey = y;
2088                 LAST_DRUGBITS(m->id) = drugbits;
2089                 for (i = 0; i < NUM_DRUGS; i++, drugbits >>= BITS_PER_DRUG) {
2090                     ushort drugged = drugbits & ((1 << BITS_PER_DRUG) - 1);
2091                     if (drugged != BIO_DRUG_CLEAN) {
2092                         ubyte color = (drugged == BIO_DRUG_UP) ? GREEN_BASE + 3 : GOOD_RED;
2093                         triple = drug2triple(i);
2094                         get_object_short_name(triple, buf2, sizeof(buf2));
2095                         mfd_draw_string(buf2, BIO_TEXT_X, y, color, TRUE);
2096                         y += Y_STEP;
2097                     }
2098                 }
2099                 mfd_add_rect(BIO_TEXT_X, savey, MFD_VIEW_WID, savey + Y_STEP * NUM_DRUGS);
2100             }
2101         }
2102         if (full)
2103             mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
2104 
2105         ResUnlock(MFD_FONT);
2106 
2107         POP_CANVAS();
2108         mfd_update_rects(m);
2109     }
2110 
2111     return;
2112 }
2113 
2114 // -------------------
2115 // * THE ANIMATION MFD
2116 // -------------------
2117 
2118 // ---------------------------------------------------------------------------
2119 // mfd_anim_init()
2120 //
2121 // Open the space station resource file for animation
2122 
mfd_anim_init()2123 errtype mfd_anim_init() {
2124 #ifdef USING_DORKY_BROKEN_ANIM
2125     if (ResOpenFile("space4.res") < 0)
2126         critical_error(CRITERR_RES | 5);
2127 #endif
2128 
2129     return OK;
2130 }
2131 
2132 // ---------------------------------------------------------------------------
2133 // mfd_anim_expose()
2134 //
2135 // Strictly temporary code.  Starts or stops the space station animation.
2136 
mfd_anim_expose(MFD * m,ubyte control)2137 void mfd_anim_expose(MFD *m, ubyte control) {
2138 #ifndef NO_DUMMIES
2139     MFD *dummy;
2140     ubyte dummy2;
2141     dummy = m;
2142     dummy2 = control;
2143 #endif
2144 #ifdef USING_DORKY_BROKEN_ANIM
2145     static uchar AnimOn[2];
2146     static ActAnim *anim[2];
2147 
2148     if (control & MFD_EXPOSE) {
2149 
2150         gr_set_fcolor((long)BLACK);
2151         ss_rect(m->rect.ul.x, m->rect.ul.y, m->rect.lr.x, m->rect.lr.y);
2152 
2153         anim[m->id] = AnimPlayRegion(REF_ANIM_space4, &(m->reg), m->rect.ul, 0);
2154         chg_set_sta(ANIM_UPDATE);
2155         AnimOn[m->id] = TRUE;
2156     } else {
2157 
2158         AnimKill((anim[m->id]));
2159         AnimOn[m->id] = FALSE;
2160         if ((AnimOn[0] == FALSE) && (AnimOn[1] == FALSE))
2161             chg_unset_sta(ANIM_UPDATE);
2162     }
2163 #endif
2164     return;
2165 }
2166 
2167 // SHODAN!!
2168 // Note this expects all appropriate 2d preparation to already be done!!
2169 
2170 errtype draw_shodan_influence(MFD *mfd, uchar amt);
2171 
draw_shodan_influence(MFD * mfd,uchar amt)2172 errtype draw_shodan_influence(MFD *mfd, uchar amt) {
2173     char *s = get_temp_string(SHODAN_FAILURE_STRING);
2174 
2175     amt = lg_min(NUM_SHODAN_MUGS - 1, amt >> SHODAN_INTERVAL_SHIFT);
2176     draw_raw_res_bm_temp(REF_IMG_EmailMugShotBase + FIRST_SHODAN_MUG + amt, 0, 0);
2177 
2178     gr_set_font(ResLock(MFD_FONT));
2179     gr_string_wrap(s, MFD_VIEW_WID);
2180     mfd_draw_string(s, 2, 2, SHODAN_COLOR, TRUE);
2181     ResUnlock(MFD_FONT);
2182     gr_font_string_unwrap(s);
2183 
2184     mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
2185     return (OK);
2186 }
2187 
2188 // ELEVATOR PANEL MFD
2189 // ------------------
2190 
2191 // NOMENCLATURE: "level" refers to the internal, unique, game-system
2192 // number for a level.  "floor" refers to the in-game floor number for
2193 // a floor.
2194 
2195 #define NUM_ELEV_LVLS 15
2196 #define NUM_ELEVATOR_BUTTONS 12
2197 #define ELEV_BTTN_ROWS 4
2198 #define ELEV_BTTN_COLS ((NUM_ELEVATOR_BUTTONS + ELEV_BTTN_ROWS - 1) / ELEV_BTTN_ROWS)
2199 #define ELEV_BTTNS_X 11
2200 #define ELEV_BTTNS_WD (MFD_VIEW_WID - 20)
2201 #define ELEV_BTTNS_Y 21
2202 #define ELEV_BTTNS_HT (MFD_VIEW_HGT - ELEV_BTTNS_Y - 2)
2203 #define ELEV_BTTN_HT 8
2204 #define ELEV_BTTN_WD 11
2205 #define ELEV_STATUS_Y 3
2206 #define ELEV_STATUS_X 50
2207 
2208 #define ELEV_STATUS_FONT RES_mediumLEDFont
2209 #define ELEV_STATUS_COLOR (GOOD_RED)
2210 
2211 typedef struct _elev_data {
2212     ushort shownlvls; // level shown on the button panel (bitmask)
2213     ushort reachlvls; // level actually reachable        (bitmask)
2214     struct _mfd_specific {
2215         ubyte currlev : 4;  // current level shown
2216         ubyte selected : 4; // currently selected button
2217     } stat, mfd_last[NUM_MFDS];
2218 } elev_data_type;
2219 
2220 uchar curr_elev_special = 0;
2221 
2222 errtype mfd_elevator_setlev(MFD *mfd, short lev, elev_data_type *elev_data);
2223 uchar mfd_elevator_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data);
2224 errtype mfd_elevator_init(MFD_Func *f);
2225 void mfd_setup_elevator(ushort levmask, ushort reachmask, ushort curlevel, uchar special);
2226 char *level_to_floor(int lev_num, char *buf);
2227 void mfd_elevator_expose(MFD *mfd, ubyte control);
2228 
mfd_elevator_setlev(MFD * mfd,short lev,elev_data_type * elev_data)2229 errtype mfd_elevator_setlev(MFD *mfd, short lev, elev_data_type *elev_data) {
2230     elev_data->stat.currlev = lev;
2231     mfd_notify_func(MFD_ELEV_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE);
2232     mfd_update_current_slot(mfd->id, MFD_CHANGEBIT_FULL, 0);
2233     return (OK);
2234 }
2235 
mfd_elevator_button_handler(MFD * mfd,LGPoint bttn,uiEvent * ev,void * data)2236 uchar mfd_elevator_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) {
2237     elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0];
2238     int b = bttn.x * ELEV_BTTN_ROWS + bttn.y;
2239     int c;
2240     ubyte l;
2241     ubyte reachl;
2242     ushort bit;
2243 
2244     if (!(ev->mouse_data.action & MOUSE_LDOWN))
2245         return FALSE;
2246 
2247     // If SHODAN has defeated us, indicate this for our expose func
2248     if (curr_elev_special) {
2249         elev_data->mfd_last[mfd->id].currlev = 0xF;
2250         mfd_notify_func(MFD_ELEV_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE);
2251         mfd_update_current_slot(mfd->id, MFD_CHANGEBIT_FULL, 0);
2252         return (OK);
2253     }
2254 
2255     for (c = 0, reachl = 0, l = 0, bit = 1; l < NUM_ELEV_LVLS; l++, bit = bit << 1) {
2256         if (elev_data->reachlvls & bit)
2257             reachl++;
2258         if (elev_data->shownlvls & bit) {
2259             c++;
2260             if (c > b)
2261                 break;
2262         }
2263     }
2264     if (l >= NUM_ELEV_LVLS)
2265         return TRUE;
2266     elev_data->stat.selected = b;
2267 #ifdef PLAYTEST
2268     mprintf("Pushing button %d\n", b);
2269 #endif
2270     if (!(bit & elev_data->reachlvls)) {
2271         string_message_info(REF_STR_ElevatorNoMove);
2272 #ifdef PLAYTEST
2273         mprintf("Can't get to that level\n");
2274 #endif
2275     } else {
2276         if (me_bits_music(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y)) != ELEVATOR_ZONE)
2277             string_message_info(REF_STR_UseTooFar);
2278         else {
2279             int oldlev;
2280             oldlev = elev_data->stat.currlev;
2281             mfd_elevator_setlev(mfd, l, elev_data);
2282             if (!elevator_use(l, reachl - 1))
2283                 mfd_elevator_setlev(mfd, oldlev, elev_data);
2284         }
2285     }
2286     return TRUE;
2287 }
2288 
2289 #define TEST_ELEVPANEL
2290 
mfd_elevator_init(MFD_Func * f)2291 errtype mfd_elevator_init(MFD_Func *f) {
2292     int cnt = 0;
2293     errtype err;
2294     LGPoint bsize = {ELEV_BTTN_WD, ELEV_BTTN_HT};
2295     LGPoint bdims = {ELEV_BTTN_COLS, ELEV_BTTN_ROWS};
2296     LGRect r = {{ELEV_BTTNS_X, ELEV_BTTNS_Y}, {ELEV_BTTNS_X + ELEV_BTTNS_WD, ELEV_BTTNS_Y + ELEV_BTTNS_HT}};
2297     err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_elevator_button_handler, NULL);
2298     if (err != OK)
2299         return err;
2300     f->handler_count = cnt;
2301 #ifdef TEST_ELEVPANEL
2302     {
2303         elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0];
2304         elev_data->shownlvls = 0xFFF;
2305         elev_data->reachlvls = 0xF0F;
2306     }
2307 #endif
2308     return OK;
2309 }
2310 
2311 // Does every thing but set the slot..
mfd_setup_elevator(ushort levmask,ushort reachmask,ushort curlevel,uchar special)2312 void mfd_setup_elevator(ushort levmask, ushort reachmask, ushort curlevel, uchar special) {
2313     elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0];
2314     elev_data->shownlvls = levmask;
2315     elev_data->reachlvls = reachmask;
2316     elev_data->stat.currlev = curlevel;
2317     elev_data->stat.selected = 0xF;
2318     curr_elev_special = special;
2319     mfd_notify_func(MFD_ELEV_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE);
2320 }
2321 
level_to_floor(int lev_num,char * buf)2322 char *level_to_floor(int lev_num, char *buf) {
2323     int bpos = 0;
2324     char grov_init = toupper(get_temp_string(REF_STR_GroveWord)[0]);
2325 
2326     switch (lev_num) {
2327     case 0:
2328         buf[bpos++] = toupper(get_temp_string(REF_STR_ReactorWord)[0]);
2329         break;
2330     case 11:
2331         buf[bpos++] = grov_init;
2332         buf[bpos++] = '4';
2333         break;
2334     case 12:
2335         buf[bpos++] = grov_init;
2336         buf[bpos++] = '1';
2337         break;
2338     case 13:
2339         buf[bpos++] = grov_init;
2340         buf[bpos++] = '2';
2341         break;
2342     default:
2343         if (lev_num >= 10) {
2344             buf[bpos++] = '0' + ((lev_num / 10) % 10);
2345             buf[bpos++] = '0' + (lev_num % 10);
2346         } else
2347             buf[bpos++] = '0' + lev_num;
2348         break;
2349     }
2350     if (bpos != 0)
2351         buf[bpos] = '\0'; // add trailing stop
2352     return (buf);
2353 }
2354 
2355 #define NUMBER_BUFSZ 4 // don't forget the terminator!
2356 
2357 // if we null your panel_ref, return the value
2358 // before we nulled it.  Otherwise, return NULL.
2359 //
panel_ref_unexpose(int mfdid,int func)2360 ObjID panel_ref_unexpose(int mfdid, int func) {
2361     uchar found = FALSE;
2362     int id = NUM_MFDS;
2363     ObjID pr = player_struct.panel_ref;
2364 
2365     while (mfd_yield_func(func, &id))
2366         if (id != mfdid)
2367             return OBJ_NULL;
2368         else
2369             found = TRUE;
2370 
2371     if (found)
2372         check_panel_ref(TRUE);
2373     else
2374         player_struct.panel_ref = OBJ_NULL;
2375     return pr;
2376 }
2377 
mfd_elevator_expose(MFD * mfd,ubyte control)2378 void mfd_elevator_expose(MFD *mfd, ubyte control) {
2379     elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0];
2380     char buf[NUMBER_BUFSZ];
2381     uchar full = control & MFD_EXPOSE_FULL;
2382     if (control == 0) {
2383         panel_ref_unexpose(mfd->id, MFD_ELEV_FUNC);
2384         return;
2385     }
2386     mfd_clear_rects();
2387     PUSH_CANVAS(pmfd_canvas);
2388     if (full)
2389         mfd_clear_view();
2390     if (curr_elev_special == 0)
2391         elev_data->mfd_last[mfd->id].currlev = 0;
2392     if (elev_data->mfd_last[mfd->id].currlev == 0xF)
2393         draw_shodan_influence(mfd, curr_elev_special);
2394     else {
2395         if (full || elev_data->mfd_last[mfd->id].currlev != elev_data->stat.currlev) {
2396             short w, h;
2397             int lev = elev_data->stat.currlev;
2398             gr_set_font(ResLock(ELEV_STATUS_FONT));
2399             level_to_floor(lev, buf);
2400             gr_string_size(buf, &w, &h);
2401             mfd_draw_font_string(buf, ELEV_STATUS_X - w, ELEV_STATUS_Y, ELEV_STATUS_COLOR, ELEV_STATUS_FONT, TRUE);
2402             elev_data->mfd_last[mfd->id].currlev = lev;
2403             ResUnlock(ELEV_STATUS_FONT);
2404         }
2405         if (full || elev_data->mfd_last[mfd->id].selected != elev_data->stat.selected) {
2406             int i;
2407             int l;
2408             short w, h;
2409             //         LGPoint bstep = { ELEV_BTTNS_WD/ELEV_BTTN_COLS,
2410             //                        ELEV_BTTNS_HT/ELEV_BTTN_ROWS };
2411             gr_set_font(ResLock(MFD_FONT));
2412             for (i = 0, l = 0; i < NUM_ELEVATOR_BUTTONS; i++) {
2413                 ubyte clr;
2414                 LGPoint bttn;
2415 
2416                 bttn.x = ELEV_BTTNS_X + (i / ELEV_BTTN_ROWS) * (ELEV_BTTNS_WD - ELEV_BTTN_WD) / (ELEV_BTTN_COLS - 1);
2417                 bttn.y = ELEV_BTTNS_Y + (i % ELEV_BTTN_ROWS) * (ELEV_BTTNS_HT - ELEV_BTTN_HT) / (ELEV_BTTN_ROWS - 1);
2418 
2419                 if (i > 0)
2420                     l++;
2421                 for (; l < NUM_ELEV_LVLS; l++)
2422                     if ((1 << l) & elev_data->shownlvls)
2423                         break;
2424                 if (l >= NUM_ELEV_LVLS)
2425                     break;
2426                 if (!(elev_data->reachlvls & (1 << l)))
2427                     clr = UNAVAILABLE_ITEM_COLOR;
2428                 else if (i == elev_data->stat.selected)
2429                     clr = SELECTED_ITEM_COLOR;
2430                 else
2431                     clr = ITEM_COLOR;
2432                 gr_set_fcolor((long)clr);
2433                 ss_box(bttn.x, bttn.y, bttn.x + ELEV_BTTN_WD, bttn.y + ELEV_BTTN_HT);
2434                 gr_set_fcolor((long)ITEM_COLOR + 2);
2435                 ss_box(bttn.x - 1, bttn.y - 1, bttn.x + ELEV_BTTN_WD + 1, bttn.y + ELEV_BTTN_HT + 1);
2436                 level_to_floor(l, buf);
2437                 gr_string_size(buf, &w, &h);
2438                 mfd_draw_string(buf, bttn.x + (ELEV_BTTN_WD - w) / 2 + 1, bttn.y + 1, clr, TRUE);
2439             }
2440             elev_data->mfd_last[mfd->id].selected = elev_data->stat.selected;
2441             mfd_add_rect(ELEV_BTTNS_X, ELEV_BTTNS_Y, ELEV_BTTNS_X + ELEV_BTTNS_WD, ELEV_BTTNS_Y + ELEV_BTTNS_HT);
2442             ResUnlock(MFD_FONT);
2443         }
2444     }
2445     POP_CANVAS();
2446     mfd_update_rects(mfd);
2447 }
2448 
2449 // ------------------
2450 // KEYPAD MFD
2451 // ------------------
2452 
2453 #define NUM_KEYPAD_BUTTONS 12
2454 #define KEYPAD_BTTN_ROWS 4
2455 #define KEYPAD_BTTN_COLS 3
2456 #define KEYPAD_X_MARGIN 0
2457 #define KEYPAD_Y_MARGIN 2
2458 #define KEYPAD_BTTNS_X 19
2459 #define KEYPAD_BTTNS_WD (MFD_VIEW_WID - (2 * KEYPAD_BTTNS_X) - 1 - KEYPAD_X_MARGIN)
2460 //#define KEYPAD_BTTNS_WD    (MFD_VIEW_WID - (3* KEYPAD_BTTNS_X))
2461 #define KEYPAD_BTTNS_Y 20
2462 #define KEYPAD_BTTNS_HT (MFD_VIEW_HGT - KEYPAD_BTTNS_Y - KEYPAD_Y_MARGIN - 1)
2463 #define KEYPAD_BTTN_HT 8
2464 #define KEYPAD_BTTN_WD 11
2465 #define KEYPAD_STATUS_Y 3
2466 #define KEYPAD_STATUS_X 60
2467 
2468 #define MAX_KEYPAD_DIGITS 3
2469 
2470 #define KEYPAD_STATUS_FONT RES_mediumLEDFont
2471 #define KEYPAD_STATUS_COLOR (GOOD_RED)
2472 
2473 bool gKeypadOverride = false; // When this is true, don't move the player.
2474 
2475 typedef struct _keypad_data {
2476     uchar curr_digit;
2477     uchar last_digit;
2478     uchar digits[MAX_KEYPAD_DIGITS];
2479     uchar special;
2480 } keypad_data_type;
2481 
2482 uchar keypad_num(int b);
2483 char *keypad_name(int b, char *buf);
2484 char *mfd_keypad_assemble(keypad_data_type *keypad_data, char *buf);
2485 errtype mfd_keypad_input(MFD *m, char b_num);
2486 uchar keypad_hotkey_func(ushort keycode, uint32_t context, intptr_t data);
2487 void install_keypad_hotkeys(void);
2488 uchar mfd_keypad_handler(MFD *m, uiEvent *ev);
2489 uchar mfd_keypad_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data);
2490 errtype mfd_keypad_init(MFD_Func *f);
2491 void mfd_keypad_expose(MFD *mfd, ubyte control);
2492 
keypad_num(int b)2493 uchar keypad_num(int b) {
2494 #ifdef KEYPAD_NUM_CASE
2495     uchar retval;
2496     // as Doug points out, this case statement is expressed algorithmically,
2497     // but hey, this is already done, easier to modify and maybe even easier
2498     // to understand.
2499     switch (b) {
2500     case 0:
2501         retval = 1;
2502         break;
2503     case 1:
2504         retval = 4;
2505         break;
2506     case 2:
2507         retval = 7;
2508         break;
2509     case 3:
2510         retval = 10;
2511         break;
2512     case 4:
2513         retval = 2;
2514         break;
2515     case 5:
2516         retval = 5;
2517         break;
2518     case 6:
2519         retval = 8;
2520         break;
2521     case 7:
2522         retval = 0;
2523         break;
2524     case 8:
2525         retval = 3;
2526         break;
2527     case 9:
2528         retval = 6;
2529         break;
2530     case 10:
2531         retval = 9;
2532         break;
2533     case 11:
2534         retval = 11;
2535         break;
2536     }
2537     return (retval);
2538 #endif
2539     static uchar retval[] = {1, 4, 7, 10, 2, 5, 8, 0, 3, 6, 9, 11};
2540     return (retval[b]);
2541 }
2542 
2543 // note that the b in keypad_name is already converted from keypad_num
keypad_name(int b,char * buf)2544 char *keypad_name(int b, char *buf) {
2545     switch (b) {
2546     case 10:
2547         strcpy(buf, "-");
2548         break;
2549     case 11:
2550         strcpy(buf, "C");
2551         break;
2552     default:
2553         sprintf(buf, "%d", b);
2554         break;
2555     }
2556     return (buf);
2557 }
2558 
mfd_keypad_assemble(keypad_data_type * keypad_data,char * buf)2559 char *mfd_keypad_assemble(keypad_data_type *keypad_data, char *buf) {
2560     char tmp[5];
2561     int i;
2562     strcpy(buf, "");
2563     for (i = 0; i < keypad_data->curr_digit; i++) {
2564         strcat(buf, keypad_name(keypad_data->digits[i], tmp));
2565     }
2566     return (buf);
2567 }
2568 
mfd_keypad_input(MFD * mfd,char b_num)2569 errtype mfd_keypad_input(MFD *mfd, char b_num) {
2570     keypad_data_type *keypad_data = (keypad_data_type *)&player_struct.mfd_func_data[MFD_KEYPAD_FUNC][0];
2571     extern errtype keypad_trigger(ObjID id, uchar digits[MAX_KEYPAD_DIGITS]);
2572 
2573     switch (b_num) {
2574     case 10:
2575         if (keypad_data->curr_digit != 0)
2576             keypad_data->curr_digit--;
2577         break;
2578     case 11:
2579         keypad_data->curr_digit = 0;
2580         break;
2581     default:
2582         if (keypad_data->curr_digit == MAX_KEYPAD_DIGITS)
2583             mfd_setup_keypad(keypad_data->special);
2584         keypad_data->digits[keypad_data->curr_digit] = b_num;
2585         keypad_data->curr_digit++;
2586         break;
2587     }
2588     play_digi_fx_obj(SFX_MFD_KEYPAD, 1, player_struct.panel_ref);
2589     if (keypad_data->special == 0 && keypad_data->curr_digit == MAX_KEYPAD_DIGITS) {
2590         keypad_trigger(player_struct.panel_ref, keypad_data->digits);
2591     }
2592     mfd_notify_func(MFD_KEYPAD_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE);
2593     return (OK);
2594 }
2595 
keypad_hotkey_func(ushort keycode,uint32_t context,intptr_t data)2596 uchar keypad_hotkey_func(ushort keycode, uint32_t context, intptr_t data) {
2597     extern MFD mfd[];
2598     uchar digit = kb2ascii(keycode) - '0';
2599     int m = NUM_MFDS;
2600     while (mfd_yield_func(MFD_KEYPAD_FUNC, &m)) {
2601         mfd_keypad_input(&mfd[m], digit);
2602         return TRUE;
2603     }
2604     return FALSE;
2605 }
2606 
install_keypad_hotkeys(void)2607 void install_keypad_hotkeys(void) {
2608     int i;
2609     for (i = 0; i < 10; i++)
2610         // KLC      hotkey_add(('0'+i)|KB_FLAG_DOWN|KB_FLAG_2ND, DEMO_CONTEXT, keypad_hotkey_func, 0);
2611         hotkey_add(('0' + i) | KB_FLAG_DOWN, DEMO_CONTEXT, keypad_hotkey_func, 0);
2612 }
2613 
mfd_keypad_handler(MFD * m,uiEvent * ev)2614 uchar mfd_keypad_handler(MFD *m, uiEvent *ev) {
2615     uchar retval = FALSE;
2616     char n;
2617 
2618     if (ev->type != UI_EVENT_KBD_COOKED)
2619         return (FALSE);
2620     if (!(ev->cooked_key_data.code & KB_FLAG_DOWN))
2621         return (FALSE);
2622     n = (ev->cooked_key_data.code & 0xFF) - '0';
2623     if ((n < 0) || (n > 9))
2624         return (FALSE);
2625     mfd_keypad_input(m, n);
2626     return (FALSE);
2627 }
2628 
mfd_keypad_button_handler(MFD * mfd,LGPoint bttn,uiEvent * ev,void * data)2629 uchar mfd_keypad_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) {
2630     int b = bttn.x * KEYPAD_BTTN_ROWS + bttn.y;
2631 
2632     // Filter out anything that isn't a left-click down at all
2633     if ((ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)) == 0)
2634         return (FALSE);
2635     mfd_keypad_input(mfd, keypad_num(b));
2636     return TRUE;
2637 }
2638 
mfd_keypad_init(MFD_Func * f)2639 errtype mfd_keypad_init(MFD_Func *f) {
2640     int cnt = 0;
2641     errtype err;
2642     LGPoint bsize = {KEYPAD_BTTN_WD, KEYPAD_BTTN_HT};
2643     LGPoint bdims = {KEYPAD_BTTN_COLS, KEYPAD_BTTN_ROWS};
2644     LGRect r = {{KEYPAD_BTTNS_X, KEYPAD_BTTNS_Y}, {KEYPAD_BTTNS_X + KEYPAD_BTTNS_WD, KEYPAD_BTTNS_Y + KEYPAD_BTTNS_HT}};
2645     err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_keypad_button_handler, NULL);
2646     if (err != OK)
2647         return err;
2648     f->handler_count = cnt;
2649     return OK;
2650 }
2651 
2652 // Does every thing but set the slot..
mfd_setup_keypad(char special)2653 void mfd_setup_keypad(char special) {
2654     int i;
2655     keypad_data_type *keypad_data = (keypad_data_type *)&player_struct.mfd_func_data[MFD_KEYPAD_FUNC][0];
2656     keypad_data->curr_digit = 0;
2657     for (i = 0; i < MAX_KEYPAD_DIGITS; i++)
2658         keypad_data->digits[i] = 0;
2659     keypad_data->special = special;
2660     mfd_notify_func(MFD_KEYPAD_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE);
2661 }
2662 
mfd_keypad_expose(MFD * mfd,ubyte control)2663 void mfd_keypad_expose(MFD *mfd, ubyte control) {
2664     keypad_data_type *keypad_data = (keypad_data_type *)&player_struct.mfd_func_data[MFD_KEYPAD_FUNC][0];
2665     char buf[NUMBER_BUFSZ];
2666     uchar full = control & MFD_EXPOSE_FULL;
2667     if (control == 0) {
2668         ObjID pr;
2669         pr = panel_ref_unexpose(mfd->id, MFD_KEYPAD_FUNC);
2670         if (pr)
2671             objs[pr].info.current_frame = 0;
2672         gKeypadOverride = FALSE;
2673         return;
2674     }
2675     mfd_clear_rects();
2676     PUSH_CANVAS(pmfd_canvas);
2677     if (full)
2678         mfd_clear_view();
2679     if ((keypad_data->special > 0) && (keypad_data->curr_digit > 0))
2680         draw_shodan_influence(mfd, keypad_data->special);
2681     else {
2682         if (full || (keypad_data->last_digit != keypad_data->curr_digit)) {
2683             int i;
2684             short w, h;
2685             //         LGPoint bstep = { KEYPAD_BTTNS_WD/KEYPAD_BTTN_COLS,
2686             //                        KEYPAD_BTTNS_HT/KEYPAD_BTTN_ROWS };
2687 
2688             // Draw cool LED at top of MFD
2689             gr_set_font(ResLock(KEYPAD_STATUS_FONT));
2690             mfd_keypad_assemble(keypad_data, buf);
2691             gr_string_size(buf, &w, &h);
2692             mfd_draw_font_string(buf, KEYPAD_STATUS_X - w, KEYPAD_STATUS_Y, KEYPAD_STATUS_COLOR, KEYPAD_STATUS_FONT,
2693                                  TRUE);
2694             keypad_data->last_digit = keypad_data->curr_digit;
2695             ResUnlock(KEYPAD_STATUS_FONT);
2696 
2697             // Draw buttons
2698             gr_set_font(ResLock(MFD_FONT));
2699             for (i = 0; i < NUM_KEYPAD_BUTTONS; i++) {
2700                 ubyte clr;
2701                 LGPoint bttn;
2702 
2703                 bttn.x = KEYPAD_BTTNS_X +
2704                          (i / KEYPAD_BTTN_ROWS) * (KEYPAD_BTTNS_WD - KEYPAD_BTTN_WD) / (KEYPAD_BTTN_COLS - 1);
2705                 bttn.y = KEYPAD_BTTNS_Y +
2706                          (i % KEYPAD_BTTN_ROWS) * (KEYPAD_BTTNS_HT - KEYPAD_BTTN_HT) / (KEYPAD_BTTN_ROWS - 1);
2707 
2708                 if ((keypad_data->curr_digit > 0) &&
2709                     (keypad_num(i) == keypad_data->digits[keypad_data->curr_digit - 1]))
2710                     clr = SELECTED_ITEM_COLOR;
2711                 else
2712                     clr = ITEM_COLOR;
2713                 gr_set_fcolor((long)clr);
2714                 ss_box(bttn.x, bttn.y, bttn.x + KEYPAD_BTTN_WD, bttn.y + KEYPAD_BTTN_HT);
2715                 gr_set_fcolor(ITEM_COLOR + 2);
2716                 ss_box(bttn.x - 1, bttn.y - 1, bttn.x + KEYPAD_BTTN_WD + 1, bttn.y + KEYPAD_BTTN_HT + 1);
2717                 keypad_name(keypad_num(i), buf);
2718                 gr_string_size(buf, &w, &h);
2719                 mfd_draw_string(buf, bttn.x + (KEYPAD_BTTN_WD - w) / 2 + 1, bttn.y + 1, clr, TRUE);
2720             }
2721             mfd_add_rect(KEYPAD_BTTNS_X, KEYPAD_BTTNS_Y, KEYPAD_BTTNS_X + KEYPAD_BTTNS_WD,
2722                          KEYPAD_BTTNS_Y + KEYPAD_BTTNS_HT);
2723             ResUnlock(MFD_FONT);
2724         }
2725     }
2726     POP_CANVAS();
2727     mfd_update_rects(mfd);
2728 }
2729 
2730 /*
2731 // --------------------
2732 //   HUD WARE MFD
2733 // --------------------
2734 
2735 #define MFD_HUD_FUNC 11
2736 
2737 #define HUD_SETTING_MASK 0xF8
2738 #define HUD_SETTING_SHF  3
2739 
2740 #define HUDWARE_STATUS (player_struct.hardwarez_status[CPTRIP(HUD_GOG_TRIPLE)])
2741 #define HUDWARE_VERSION (player_struct.hardwarez[CPTRIP(HUD_GOG_TRIPLE)])
2742 
2743 
2744 ushort hud_ware_bits[] = { HUD_COMPASS, HUD_DETECT_EXP, HUD_GRENADE };
2745 
2746 #define NUM_HUDWARE_DISPLAYS (sizeof(hud_ware_bits)/sizeof(ushort))
2747 
2748 #define HUDWARE_DISPLAY_AVAILABLE(dnum) (HUDWARE_VERSION & ( 1 << (dnum)))
2749 
2750 // Note the use of negative logic here....
2751 #define HUDWARE_DISPLAY_ACTIVE(dnum) (!(HUDWARE_STATUS & (1 << ((dnum) + HUD_SETTING_SHF))))
2752 #define HUDWARE_DISPLAY_TOGGLE(dnum) (HUDWARE_STATUS ^= (1 << (dnum) + HUD_SETTING_SHF))
2753 
2754 uchar mfd_hud_button_handler(MFD* m, LGPoint bttn, uiEvent* ev, void* data)
2755 {
2756 
2757    // Check to see if we actually have the specified display.
2758    if (!HUDWARE_DISPLAY_AVAILABLE(bttn.y)) return FALSE;
2759    // Toggle the display
2760    if (ev->type == UI_EVENT_MOUSE &&  ev->subtype & (MOUSE_LDOWN|MOUSE_RDOWN))
2761       HUDWARE_DISPLAY_TOGGLE(bttn.y);
2762 
2763    if (HUDWARE_STATUS & WARE_ON) // update the actual hud.
2764    {
2765       if (HUDWARE_DISPLAY_ACTIVE(bttn.y))
2766          hud_set(hud_ware_bits[bttn.y]);
2767       else
2768          hud_unset(hud_ware_bits[bttn.y]);
2769    }
2770    mfd_notify_func(MFD_HUD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE);
2771    return TRUE;
2772 }
2773 
2774 
2775 #define HUDWARE_BUTTON_X 2
2776 #define HUDWARE_BUTTON_Y 16
2777 #define HUDWARE_LINE_SPACING 7
2778 
2779 #define HUDWARE_LAST_STATUS(mfd) mfd_fdata[MFD_HUD_FUNC][mfd]
2780 
2781 #define HUDWARE_BOX_COLOR ITEM_COLOR
2782 #define HUDWARE_BOX_SIZE  HUDWARE_LINE_SPACING
2783 
2784 #ifdef HUDWARE_MFD
2785 
2786 errtype mfd_hud_init(MFD_Func* f)
2787 {
2788    errtype err;
2789    LGPoint bsize = { MFD_VIEW_WID, HUDWARE_LINE_SPACING};
2790    LGPoint bdims = { 1, NUM_HUDWARE_DISPLAYS };
2791    LGRect  brect = { { 0, HUDWARE_BUTTON_Y },
2792                   { MFD_VIEW_WID, HUDWARE_BUTTON_Y + NUM_HUDWARE_DISPLAYS*HUDWARE_LINE_SPACING}};
2793    int cnt = 0;
2794    err = MFDBttnArrayInit(&f->handlers[cnt++],&brect,bdims,bsize,mfd_hud_button_handler,NULL);
2795    if (err != OK) return err;
2796    f->handler_count = cnt;
2797    return OK;
2798 }
2799 
2800 void mfd_hud_expose(MFD* mfd, ubyte control)
2801 {
2802    uchar full = control & MFD_EXPOSE_FULL;
2803    if (control == 0) return;
2804 
2805    mfd_clear_rects();
2806    PUSH_CANVAS(pmfd_canvas);
2807    ss_safe_set_cliprect(0,0,MFD_VIEW_WID,MFD_VIEW_HGT);
2808 
2809    // Lay down the "background"
2810    mfd_item_micro_expose(TRUE,HUD_GOG_TRIPLE);
2811    // clear rects so that we don't draw it if we don't have to
2812    if (!full) mfd_clear_rects();
2813 
2814    if (full || HUDWARE_STATUS != HUDWARE_LAST_STATUS(mfd->id))
2815    {
2816       int i;
2817       for (i = 0; i < NUM_HUDWARE_DISPLAYS; i++)
2818          if (HUDWARE_DISPLAY_AVAILABLE(i))
2819          {
2820             short x = HUDWARE_BUTTON_X;
2821             short y = HUDWARE_BUTTON_Y + i * HUDWARE_LINE_SPACING;
2822             short textx;
2823             char* s;
2824             // draw an "x" if active
2825             if (HUDWARE_DISPLAY_ACTIVE(i))
2826             {
2827                gr_set_fcolor(GOOD_RED);
2828                ss_int_line(x,y,x+HUDWARE_BOX_SIZE-1,y+HUDWARE_BOX_SIZE-1);
2829                ss_int_line(x,y+HUDWARE_BOX_SIZE-1,x+HUDWARE_BOX_SIZE-1,y);
2830             }
2831             gr_set_fcolor(HUDWARE_BOX_COLOR);
2832             ss_box(x,y,x+HUDWARE_BOX_SIZE,y+HUDWARE_BOX_SIZE);
2833             gr_set_font(ResLock(MFD_FONT));
2834             s = get_temp_string(REF_STR_HudBase+i);
2835             textx = (MFD_VIEW_WID - HUDWARE_BOX_SIZE - gr_string_width(s))/2 + HUDWARE_BOX_SIZE;
2836             mfd_draw_string(s,textx,y+1,gr_get_fcolor(),TRUE);
2837             ResUnlock(MFD_FONT);
2838             mfd_add_rect(x,y,MFD_VIEW_WID,y+HUDWARE_LINE_SPACING);
2839          }
2840       HUDWARE_LAST_STATUS(mfd->id) = HUDWARE_STATUS;
2841    }
2842    POP_CANVAS();
2843    mfd_update_rects(mfd);
2844 }
2845 
2846 void hudware_update_status(uchar on)
2847 {
2848    int i;
2849    for (i = 0; i < NUM_HUDWARE_DISPLAYS; i++)
2850       if (HUDWARE_DISPLAY_ACTIVE(i) && on)
2851          hud_set(hud_ware_bits[i]);
2852       else
2853          hud_unset(hud_ware_bits[i]);
2854 }
2855 
2856 #endif // HUDWARE_MFD
2857 */
2858 
2859 // ----------------------------------------------------------
2860 // THE GOOFY SEVERED HEAD MFD
2861 // ----------------------------------------------------------
2862 
2863 void severed_head_expose(MFD *mfd, ubyte control);
2864 
severed_head_expose(MFD * mfd,ubyte control)2865 void severed_head_expose(MFD *mfd, ubyte control) {
2866     uchar full = control & MFD_EXPOSE_FULL;
2867     if (full) {
2868         ubyte headnum = player_struct.actives[ACTIVE_GENERAL];
2869         ObjID head = player_struct.inventory[headnum];
2870         int mug;
2871         uint trip = ID2TRIP(head);
2872 
2873         if (head == OBJ_NULL || !(trip == HEAD_TRIPLE || trip == HEAD2_TRIPLE))
2874             return;
2875         // clear update rects
2876         mfd_clear_rects();
2877         // set up canvas
2878         gr_push_canvas(pmfd_canvas);
2879         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
2880         // Clear the canvas by drawing the background bitmap
2881         if (!full_game_3d)
2882             ss_bitmap(&mfd_background, 0, 0);
2883         // gr_bitmap(&mfd_background, 0, 0);
2884 
2885         mug = REF_IMG_EmailMugShotBase + objSmallstuffs[objs[head].specID].data1;
2886 	FrameDesc *f = RefLock(mug);
2887 	if (f != NULL) {
2888 	    ss_bitmap(&f->bm, (MFD_VIEW_WID - f->bm.w) / 2, (MFD_VIEW_HGT - f->bm.h) / 2);
2889 	    RefUnlock(mug);
2890 	} else {
2891 	    WARN("severed_head_expose(): could not load head art ", mug);
2892 	}
2893 
2894         // draw the name
2895         mfd_draw_string(get_object_long_name(ID2TRIP(head), NULL, 0), X_MARGIN, 2, GREEN_YELLOW_BASE, TRUE);
2896         mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
2897 
2898         // Pop the canvas
2899         gr_pop_canvas();
2900         // Now that we've popped the canvas, we can send the
2901         // updated mfd to screen
2902         mfd_update_rects(mfd);
2903     }
2904 }
2905 
2906 // -------------------
2907 // * GENERIC BLANK MFD
2908 // -------------------
2909 
2910 // ---------------------------------------------------------------------------
2911 // mfd_expose_blank()
2912 //
2913 // Draw whatever we're supposed to draw if we're looking at an empty slot.
2914 
mfd_expose_blank(MFD * m,ubyte control)2915 void mfd_expose_blank(MFD *m, ubyte control) {
2916     if (full_game_3d) {
2917         full_visible &= ~visible_mask(m->id);
2918         chg_set_sta(FULLSCREEN_UPDATE);
2919         return;
2920     }
2921     if ((control & MFD_EXPOSE) && !full_game_3d) {
2922 
2923         PUSH_CANVAS(pmfd_canvas);
2924         ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
2925 
2926         draw_blank_mfd();
2927 
2928         POP_CANVAS();
2929         mfd_update_display(m, 0, 0, MFD_VIEW_WID, MFD_VIEW_HGT);
2930     }
2931 
2932     return;
2933 }
2934 
2935 // ---------------------------------------------------------------------------
2936 //                  CALLS FROM OTHER MODULES TO THE MFD SYSTEM
2937 // ---------------------------------------------------------------------------
2938 
2939 // ---------------------------------------------------------------------------
2940 // set_inventory_mfd()
2941 //
2942 // Called by the inventory whenever a new drug, ware, something is clicked
2943 // on in the inventory panel.
2944 
2945 ulong catbasetrips[MFD_INV_CATEGORIES] = {
2946     0,
2947     MAKETRIP(CLASS_DRUG, 0, 0),
2948     MAKETRIP(CLASS_HARDWARE, 0, 0),
2949     MAKETRIP(CLASS_GRENADE, 0, 0),
2950     MAKETRIP(CLASS_AMMO, 0, 0),
2951     MAKETRIP(CLASS_GUN, 0, 0),
2952     0, // general inventory
2953     MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_OFFENSE, 0),
2954     MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_DEFENSE, 0),
2955     MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_ONESHOT, 0),
2956 };
2957 
2958 // look, another array that should not exist.
2959 ubyte catactives[] = {
2960     0,
2961     ACTIVE_DRUG,
2962     ACTIVE_HARDWARE,
2963     ACTIVE_GRENADE,
2964     ACTIVE_CART,
2965     ACTIVE_WEAPON,
2966     ACTIVE_GENERAL,
2967     ACTIVE_COMBAT_SOFT,
2968     ACTIVE_DEFENSE_SOFT,
2969     ACTIVE_MISC_SOFT,
2970 };
2971 
2972 ubyte activecats[] = {
2973     MFD_INV_WEAPON,      MFD_INV_GRENADE,      MFD_INV_DRUG,      MFD_INV_AMMO,   MFD_INV_HARDWARE,
2974     MFD_INV_SOFT_COMBAT, MFD_INV_SOFT_DEFENSE, MFD_INV_SOFT_MISC, MFD_INV_GENINV, 0,
2975 };
2976 
2977 extern void set_current_active(int);
2978 void update_item_mfd(void);
2979 uchar mfd_distance_remove(ubyte slot_func);
2980 uchar mfd_target_qual(void);
2981 uchar mfd_automap_qual(void);
2982 uchar mfd_weapon_qual(void);
2983 
set_inventory_mfd(ubyte obclass,ubyte type,uchar grab)2984 void set_inventory_mfd(ubyte obclass, ubyte type, uchar grab) {
2985     int i;
2986     ubyte func = MFD_EMPTY_FUNC;
2987     ubyte slot;
2988     MFD_Status stat;
2989     uchar classhit = FALSE;
2990 
2991     switch (obclass) {
2992 
2993     case MFD_INV_WEAPON:
2994 
2995         // The "grab" arg is TRUE in case blanked out by an inventory drop
2996         func = MFD_WEAPON_FUNC;
2997         slot = MFD_WEAPON_SLOT;
2998         stat = MFD_ACTIVE;
2999         if (type == MFD_INV_NOTYPE) {
3000             stat = MFD_EMPTY;
3001         }
3002         break;
3003 
3004     case MFD_INV_NULL:
3005 
3006         if (type == MFD_INV_NOTYPE)
3007             break;
3008         for (i = 0; i < NUM_MFDS; i++) {
3009             MFDSetCurrItemClass(i, obclass);
3010         }
3011         func = MFD_ITEM_FUNC;
3012         slot = MFD_ITEM_SLOT;
3013         stat = MFD_EMPTY;
3014         break;
3015 
3016     default:
3017         for (i = 0; i < NUM_MFDS; i++) {
3018             if (MFDGetCurrItemClass(i) == obclass)
3019                 classhit = TRUE;
3020             if (type != MFD_INV_NOTYPE)
3021                 MFDSetCurrItemClass(i, obclass);
3022         }
3023         slot = MFD_ITEM_SLOT;
3024         stat = MFD_ACTIVE;
3025         if (type == MFD_INV_NOTYPE) {
3026             if (classhit) {
3027                 mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, TRUE, MFD_EMPTY, TRUE);
3028                 player_struct.actives[catactives[obclass]] = 0;
3029             }
3030         } else if (obclass == MFD_INV_GENINV && player_struct.inventory[type] == OBJ_NULL) {
3031             mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, grab, MFD_EMPTY, TRUE);
3032         } else {
3033             ulong opnum = (obclass != MFD_INV_GENINV) ? OPTRIP(catbasetrips[obclass]) + type
3034                                                       : OPNUM(player_struct.inventory[type]);
3035             func = ObjProps[opnum].mfd_id;
3036             if (func == MFD_EMPTY_FUNC)
3037                 func = MFD_ITEM_FUNC;
3038             set_current_active(catactives[obclass]);
3039         }
3040         break;
3041     }
3042     if (func != MFD_EMPTY_FUNC) {
3043         mfd_notify_func(func, slot, grab, stat, TRUE);
3044 #ifdef RAISE_ON_SELECT
3045         if (full_game_3d) {
3046             int i;
3047             for (i = 0; i < NUM_MFDS; i++) {
3048                 if (player_struct.mfd_current_slots[i] == MFD_ITEM_SLOT) {
3049 #ifdef STEREO_SUPPORT
3050                     if (convert_use_mode == 5) {
3051                         full_visible = FULL_MFD_MASK(i);
3052                     } else
3053 #endif
3054                         full_visible |= FULL_MFD_MASK(i);
3055                 }
3056             }
3057         }
3058 #endif
3059     }
3060     // THEN we check to see if we need to take over the info mfd
3061 
3062     switch (obclass) {
3063 
3064     case MFD_INV_HARDWARE:
3065 
3066         switch (type) {
3067 
3068         case HARDWARE_BIOWARE:
3069 
3070             if (WareActive(player_struct.hardwarez_status[HARDWARE_BIOWARE]))
3071                 mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE);
3072 
3073             break;
3074         }
3075 
3076         break;
3077     }
3078 
3079     if (obclass != MFD_INV_NULL && type != MFD_INV_NOTYPE)
3080         player_struct.actives[catactives[obclass]] = type;
3081 
3082     return;
3083 }
3084 
update_item_mfd(void)3085 void update_item_mfd(void) {
3086     ubyte curr = player_struct.current_active;
3087     if (curr != NULL_ACTIVE) {
3088         ubyte obclass = activecats[curr];
3089         ubyte type = player_struct.actives[curr];
3090         ulong opnum =
3091             (obclass != MFD_INV_GENINV) ? OPTRIP(catbasetrips[obclass]) + type : OPNUM(player_struct.inventory[type]);
3092         int func = ObjProps[opnum].mfd_id;
3093         if (func != MFD_EMPTY_FUNC) {
3094             mfd_notify_func(func, MFD_ITEM_FUNC, TRUE, MFD_ACTIVE, TRUE);
3095         }
3096     }
3097 }
3098 
mfd_distance_remove(ubyte slot_func)3099 uchar mfd_distance_remove(ubyte slot_func) {
3100     switch (slot_func) {
3101     case MFD_KEYPAD_FUNC:
3102     case MFD_FIXTURE_FUNC:
3103     case MFD_ELEV_FUNC:
3104     case MFD_BARK_FUNC:
3105     case MFD_ACCESSPANEL_FUNC:
3106     case MFD_GUMP_FUNC:
3107     case MFD_GRIDPANEL_FUNC:
3108         return TRUE;
3109     }
3110     return FALSE;
3111 }
3112 
3113 // -------
3114 // DEFAULT MFD FUNC QUALIFYING FUNCTIONS
mfd_target_qual(void)3115 uchar mfd_target_qual(void) { return (player_struct.hardwarez[HARDWARE_TARGET] > 0); }
3116 
mfd_automap_qual(void)3117 uchar mfd_automap_qual(void) { return (player_struct.hardwarez[HARDWARE_AUTOMAP] > 0); }
3118 
mfd_weapon_qual(void)3119 uchar mfd_weapon_qual(void) {
3120     return (player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].type != EMPTY_WEAPON_SLOT);
3121 }
3122 
3123 // --------------------------------------------------------
3124 // THE STATIC MFD_FUNCS ARRAY
3125 
3126 extern void mfd_view360_expose(MFD *mfd, ubyte control);
3127 extern void mfd_dummy_expose(MFD *mfd, ubyte control);
3128 extern void mfd_fixture_expose(MFD *mfd, ubyte control);
3129 extern uchar mfd_fixture_handler(MFD *mfd, uiEvent *e);
3130 extern void mfd_emailmug_expose(MFD *mfd, ubyte control);
3131 extern uchar mfd_emailmug_handler(MFD *mfd, uiEvent *e);
3132 extern errtype mfd_emailware_init(MFD_Func *f);
3133 extern void mfd_emailware_expose(MFD *, ubyte);
3134 extern void mfd_plotware_expose(MFD *, ubyte);
3135 extern errtype mfd_plotware_init(MFD_Func *f);
3136 extern void mfd_bark_expose(MFD *, ubyte);
3137 extern errtype mfd_accesspanel_init(MFD_Func *f);
3138 extern uchar mfd_accesspanel_handler(MFD *mfd, uiEvent *ev);
3139 extern void mfd_accesspanel_expose(MFD *mfd, ubyte control);
3140 extern errtype mfd_gridpanel_init(MFD_Func *f);
3141 extern void mfd_gridpanel_expose(MFD *mfd, ubyte control);
3142 extern uchar mfd_gridpanel_handler(MFD *mfd, uiEvent *ev);
3143 extern void mfd_targetware_expose(MFD *mfd, ubyte control);
3144 extern uchar mfd_targetware_handler(MFD *mfd, uiEvent *ev);
3145 extern void mfd_gump_expose(MFD *mfd, ubyte control);
3146 extern uchar mfd_gump_handler(MFD *mfd, uiEvent *ev);
3147 extern void mfd_accesscard_expose(MFD *mfd, ubyte control);
3148 extern void mfd_biohelp_expose(MFD *mfd, ubyte control);
3149 extern errtype mfd_biohelp_init(MFD_Func *f);
3150 extern uchar mfd_biohelp_handler(MFD *mfd, uiEvent *ev);
3151 extern void mfd_cspace_expose(MFD *mfd, ubyte control);
3152 extern void mfd_viewhelp_expose(MFD *mfd, ubyte control);
3153 extern errtype mfd_viewhelp_init(MFD_Func *f);
3154 extern void mfd_gear_expose(MFD *mfd, ubyte control);
3155 extern uchar mfd_gear_handler(MFD *mfd, uiEvent *ev);
3156 
3157 #define PANEL_PRIORITY 37
3158 
3159 MFD_Func mfd_funcs[MFD_NUM_FUNCS] = {
3160     // MFD_EMPTY_FUNC   0
3161     {mfd_expose_blank, NULL, NULL, 255, MFD_NOSAVEREST},
3162     // MFD_ITEM_FUNC    1
3163     {mfd_item_expose, mfd_item_handler, mfd_item_init, 40},
3164     // MFD_MAP_FUNC     2
3165     {mfd_map_expose, mfd_map_handler, mfd_map_init, 20, MFD_INCREMENTAL},
3166     // MFD_TARGET_FUNC  3
3167     {mfd_target_expose, mfd_target_handler, NULL, 21},
3168     // MFD_ANIM_FUNC    4
3169     {mfd_expose_blank, NULL, NULL, 255},
3170     // MFD_WEAPON_FUNC  5
3171     {mfd_weapon_expose, mfd_weapon_handler, mfd_weapon_init, 25},
3172     // MFD_BIOWARE_FUNC 6
3173     {mfd_bioware_expose, NULL, NULL, 50, MFD_NOSAVEREST},
3174     // MFD_LANTERN_FUNC 7
3175     {mfd_lanternware_expose, NULL, mfd_lanternware_init, 38},
3176     // MFD_3DVIEW_FUNC  8
3177     {mfd_view360_expose, NULL, NULL, 25, MFD_NOSAVEREST},
3178     // MFD_ELEV_FUNC    9
3179     {mfd_elevator_expose, NULL, mfd_elevator_init, PANEL_PRIORITY, MFD_NOSAVEREST},
3180     // MFD_GRENADE_FUNC 10
3181     {mfd_grenade_expose, mfd_grenade_handler, mfd_grenade_init, 32},
3182     // MFD_HUD_FUNC     11
3183     {
3184         mfd_expose_blank,
3185     },
3186     // MFD_FIXTURE_FUNC 12
3187     {mfd_fixture_expose, mfd_fixture_handler, NULL, 32, MFD_NOSAVEREST},
3188     // MFD_KEYPAD_FUNC  13
3189     {mfd_keypad_expose, mfd_keypad_handler, mfd_keypad_init, PANEL_PRIORITY, MFD_NOSAVEREST},
3190     // MFD_EMAILMUG_FUNC 14
3191     {mfd_emailmug_expose, mfd_emailmug_handler, NULL, 60, MFD_NOSAVEREST},
3192     // MFD_EMAILWARE_FUNC 15
3193     {mfd_emailware_expose, NULL, mfd_emailware_init, 60},
3194     // MFD_PLOTWARE_FUNC  16
3195     {mfd_plotware_expose, NULL, mfd_plotware_init, 55},
3196     // MFD_BARK_FUNC 17
3197     {mfd_bark_expose, NULL, NULL, 255, MFD_NOSAVEREST},
3198     // MFD_ACCESSPANEL_FUNC 18
3199     {mfd_accesspanel_expose, mfd_accesspanel_handler, mfd_accesspanel_init, PANEL_PRIORITY,
3200      MFD_INCREMENTAL | MFD_NOSAVEREST},
3201     // MFD_SHIELD_FUNC 19
3202     {mfd_shieldware_expose, mfd_shield_handler, mfd_shield_init, 36},
3203     // MFD_MOTION_FUNC 20
3204     {mfd_motionware_expose, NULL, mfd_motion_init, 36},
3205     // MFD_SEVERED_HEAD_FUNC 21
3206     {severed_head_expose, NULL, NULL, 250},
3207     // MFD_TARGETWARE_FUNC 22
3208     {mfd_targetware_expose, mfd_targetware_handler, NULL, 40},
3209     // MFD_GUMP_FUNC 23
3210     {mfd_gump_expose, mfd_gump_handler, NULL, 40, MFD_NOSAVEREST},
3211     // MFD_CARD_FUNC 24
3212     {mfd_accesscard_expose, NULL, NULL, 40},
3213     // MFD_BIOHELP_FUNC 25
3214     {mfd_biohelp_expose, mfd_biohelp_handler, mfd_biohelp_init, 40},
3215     // MFD_GRIDPANEL_FUNC 26
3216     {mfd_gridpanel_expose, mfd_gridpanel_handler, mfd_gridpanel_init, PANEL_PRIORITY, MFD_NOSAVEREST},
3217     // MFD_GAMES_FUNC 27
3218     {mfd_games_expose, mfd_games_handler, mfd_games_init, PANEL_PRIORITY, MFD_INCREMENTAL | MFD_NOSAVEREST},
3219     // MFD_CYBERSPACE_FUNC 28
3220     {mfd_cspace_expose, NULL, NULL, 40},
3221     // MFD_VIEWHELP_FUNC 29
3222     {mfd_viewhelp_expose, NULL, mfd_viewhelp_init, 40},
3223     // MFD_GEAR_FUNC 30
3224     {mfd_gear_expose, mfd_gear_handler, NULL, 40}};
3225