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 #define __NEWMFD_SRC
20 
21 // NEWMFD.C
22 
23 /*
24  * $Source: r:/prj/cit/src/RCS/newmfd.c $
25  * $Revision: 1.153 $
26  * $Author: xemu $
27  * $Date: 1994/11/21 22:03:39 $
28  *
29  */
30 
31 // Source code for controlling the multi-function displays (MFDs)
32 // All MFD infrastructure belongs here, all expose/handler callbacks
33 // belong in mfdfunc.c
34 
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "game_screen.h" // for the root region
39 #include "fullscrn.h"
40 #include "mfdint.h"
41 #include "mfdext.h"
42 #include "mfddims.h"
43 #include "input.h"
44 #include "player.h"
45 #include "tools.h"
46 #include "mainloop.h"
47 #include "gameloop.h"
48 #include "mfdart.h" // For blank MFD art
49 #include "gamescr.h"
50 #include "musicai.h" // for digital FX
51 #include "sfxlist.h" // same
52 #include "citres.h"
53 #include "weapons.h"
54 #include "colors.h"
55 #include "cit2d.h"
56 #include "gamestrn.h"
57 #include "popups.h"
58 #include "statics.h"
59 #include "gr2ss.h"
60 //#include <inp6d.h>
61 //#include <i6dvideo.h>
62 
63 #include "cybstrng.h"
64 
65 // -----------------------
66 // Player_Struct Accessors
67 // -----------------------
68 
69 #define mfd_empty_func(mfd_num)            player_struct.mfd_empty_funcs[(mfd_num)]
70 #define mfd_index(mfd_num)                 player_struct.mfd_current_slots[(mfd_num)]
71 #define set_mfd_to_slot(mfd_id, vs, as)    player_struct.mfd_virtual_slots[(mfd_id)][(vs)] = (as)
72 #define set_default_to_func(mfd_num, fnum) player_struct.mfd_empty_funcs[(mfd_num)] = (fnum)
73 #define mfd_get_active_func(mfd_id)        mfd_get_func((mfd_id), (mfd_index((mfd_id))))
74 
75 // -------
76 // Globals
77 // -------
78 
79 #define MFD_NUM_BTTNS MFD_NUM_VIRTUAL_SLOTS
80 
81 MFD mfd[2];         // Our actual MFD's
82 uchar Flash = TRUE; // State of blinking buttons
83 LGCursor mfd_bttn_cursors[NUM_MFDS];
84 grs_bitmap mfd_bttn_bitmaps[NUM_MFDS];
85 
86 grs_canvas _offscreen_mfd, _fullscreen_mfd;
87 
88 #define mfdL mfd[MFD_LEFT]
89 #define mfdR mfd[MFD_RIGHT]
90 
91 // -----------
92 // Prototypes
93 // -----------
94 void mfd_language_change(void);
95 void mfd_set_slot(ubyte mfd_id, ubyte newSlot, uchar OnOff);
96 void mfd_draw_all_buttons(ubyte mfd_id);
97 errtype mfd_update_screen_mode();
98 errtype mfd_clear_all();
99 void mfd_change_fullscreen(uchar on);
100 
101 void mfd_clear_func(ubyte func_id);
102 int mfd_choose_func(int my_func, int my_slot);
103 void mfd_zoom_rect(LGRect *start, int mfdnum);
104 void fullscreen_refresh_mfd(ubyte mfd_id);
105 
106 uchar mfd_object_cursor_handler(uiEvent *ev, LGRegion *, int which_mfd);
107 uchar mfd_scan_opacity(int mfd_id, LGPoint epos);
108 
109 void mfd_draw_button_panel(ubyte mfd_id);
110 void mfd_draw_button(ubyte mfd_id, ubyte b);
111 void mfd_select_button(int which_panel, int which_button);
112 
113 void mfd_default_mru(uchar func);
114 void set_mfd_from_defaults(int mfd_id, uchar func, uchar slot);
115 void cap_mfds_with_func(uchar func, uchar max);
116 
117 // KLC  dbg_mfd_state used to be here.
118 
119 // ------------------
120 //    INITIALIZERS
121 // ------------------
122 
123 // ---------------------------------------------------------------------------
124 // init_newmfd()
125 //
126 // Initialize the MFD system (called from init_all() in init.c)
127 
init_newmfd()128 void init_newmfd() {
129     ubyte i;
130 
131     // Set the default MFD function
132     set_default_to_func(MFD_LEFT, MFD_EMPTY_FUNC);
133     set_default_to_func(MFD_RIGHT, MFD_EMPTY_FUNC);
134 
135     // Now set actual MFD slots to point at virtual slots
136     for (i = 0; i < NUM_MFDS; i++)
137         mfd[i].id = i;
138 
139     player_struct.mfd_current_slots[MFD_LEFT] = MFD_WEAPON_SLOT;
140     player_struct.mfd_current_slots[MFD_RIGHT] = MFD_ITEM_SLOT;
141 
142     for (i = 0; i < MFD_NUM_VIRTUAL_SLOTS; i++) {
143         set_mfd_to_slot(MFD_LEFT, i, i);
144         set_mfd_to_slot(MFD_RIGHT, i, i);
145     }
146 
147     for (i = 0; i < MFD_NUM_FUNCS; i++)
148         if (mfd_funcs[i].flags & MFD_INCREMENTAL)
149             player_struct.mfd_func_status[i] |= 1 << 4;
150 
151     chg_set_flg(MFD_UPDATE);
152 
153     return;
154 }
155 
156 // ---------------------------------------------------------------------------
157 // init_newmfd_button_cursors()
158 //
159 // Initialize the twelve goofy cursors, each of which hovers over an MFD button,
160 // as spec'd in last nights warren/artist/programmers meeting (SPAZ 8/5)
161 
162 static char *cursor_strings[MFD_NUM_BTTNS];
163 static char cursor_strbuf[128];
164 
mfd_language_change(void)165 void mfd_language_change(void) {
166     load_string_array(REF_STR_MFDCursor, cursor_strings, cursor_strbuf, sizeof(cursor_strbuf), MFD_NUM_BTTNS);
167 }
168 
init_newmfd_button_cursors()169 void init_newmfd_button_cursors() {
170     int i;
171     mfd_language_change();
172     for (i = 0; i < NUM_MFDS; i++) {
173         LGCursor *c = &mfd_bttn_cursors[i];
174         grs_bitmap *bm = &mfd_bttn_bitmaps[i];
175         LGPoint offset = {0, 0};
176         make_popup_cursor(c, bm, cursor_strings[i], i, TRUE, offset);
177     }
178 }
179 
180 // ---------------------------------------------------------------------------
181 // screen_init_mfd_draw()
182 //
183 // Basically, just draw the friggin' buttons and set mfd's to their
184 // first slot. (called from screen_start() in screen.c)
185 
screen_init_mfd_draw()186 void screen_init_mfd_draw() {
187     mfd_set_slot(MFD_LEFT, mfd_index(MFD_LEFT), TRUE);
188     mfd_set_slot(MFD_RIGHT, mfd_index(MFD_RIGHT), TRUE);
189 
190     mfd_draw_all_buttons(MFD_LEFT);
191     mfd_draw_all_buttons(MFD_RIGHT);
192 
193     return;
194 }
195 
196 #ifdef SVGA_SUPPORT
197 #define MAX_WD(x) (fix_int(fix_mul_div(fix_make((x), 0), fix_make(1024, 0), fix_make(320, 0))))
198 #define MAX_HT(y) (fix_int(fix_mul_div(fix_make((y), 0), fix_make(768, 0), fix_make(200, 0))))
199 #endif
200 
201 // ---------------------------------------------------------------------------
202 // screen_init_mfd();
203 //
204 // Declare the appropriate regions for the MFD's and their button panels.
205 // (called from screen_start() in screen.c)
206 
screen_init_mfd(uchar fullscrn)207 void screen_init_mfd(uchar fullscrn) {
208     static uchar done_init = FALSE;
209     FrameDesc *f;
210     int id;
211     int lval, rval;
212 
213     lval = MFD_LEFT;  // Screen callbacks need to know their
214     rval = MFD_RIGHT; // left from their right, thusly
215 
216     // Set up the Rect structures for MFD screen-space
217 
218     // Left View Window
219     mfdL.rect.ul.x = MFD_VIEW_LFTX;
220     mfdL.rect.ul.y = MFD_VIEW_Y;
221     mfdL.rect.lr.x = MFD_VIEW_LFTX + MFD_VIEW_WID;
222     mfdL.rect.lr.y = MFD_VIEW_Y + MFD_VIEW_HGT;
223 
224     // Right View Window
225     mfdR.rect.ul.x = MFD_VIEW_RGTX;
226     mfdR.rect.ul.y = MFD_VIEW_Y;
227     mfdR.rect.lr.x = MFD_VIEW_RGTX + MFD_VIEW_WID;
228     mfdR.rect.lr.y = MFD_VIEW_Y + MFD_VIEW_HGT;
229 
230     // Left Button Panel
231     mfdL.bttn.rect.ul.x = MFD_BTTN_LFTX;
232     mfdL.bttn.rect.ul.y = MFD_BTTN_Y;
233     mfdL.bttn.rect.lr.x = MFD_BTTN_LFTX + MFD_BTTN_WID;
234     mfdL.bttn.rect.lr.y = MFD_BTTN_Y + MFD_BTTN_HGT;
235 
236     // Right Button Panel
237     mfdR.bttn.rect.ul.x = MFD_BTTN_RGTX;
238     mfdR.bttn.rect.ul.y = MFD_BTTN_Y;
239     mfdR.bttn.rect.lr.x = MFD_BTTN_RGTX + MFD_BTTN_WID;
240     mfdR.bttn.rect.lr.y = MFD_BTTN_Y + MFD_BTTN_HGT;
241 
242     // Now, actually create the four regions, and add handlers
243     if (!fullscrn) {
244         macro_region_create(root_region, &(mfdL.reg), &(mfdL.rect));
245         macro_region_create(root_region, &(mfdR.reg), &(mfdR.rect));
246         macro_region_create(root_region, &(mfdL.bttn.reg), &(mfdL.bttn.rect));
247         macro_region_create(root_region, &(mfdR.bttn.reg), &(mfdR.bttn.rect));
248 
249         uiInstallRegionHandler(&(mfdL.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback, MFD_LEFT,
250                                &id);
251         uiInstallRegionHandler(&(mfdR.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback,
252                                MFD_RIGHT, &id);
253 
254         uiInstallRegionHandler(&(mfdL.bttn.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback,
255                                MFD_LEFT, &id);
256         uiInstallRegionHandler(&(mfdR.bttn.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback,
257                                MFD_RIGHT, &id);
258     } else {
259         uiCursorStack *cs;
260         macro_region_create(fullview_region, &(mfdL.reg2), &(mfdL.rect));
261         uiGetRegionCursorStack(&(mfdL.reg), &cs);
262         uiSetRegionCursorStack(&(mfdL.reg2), cs);
263         macro_region_create(fullview_region, &(mfdR.reg2), &(mfdR.rect));
264         uiGetRegionCursorStack(&(mfdR.reg), &cs);
265         uiSetRegionCursorStack(&(mfdR.reg2), cs);
266         region_create(fullview_region, &(mfdL.bttn.reg2), &(mfdL.bttn.rect), 2, 0, REG_USER_CONTROLLED, NULL, NULL,
267                       NULL, NULL);
268         uiGetRegionCursorStack(&(mfdL.bttn.reg), &cs);
269         uiSetRegionCursorStack(&(mfdL.bttn.reg2), cs);
270         region_create(fullview_region, &(mfdR.bttn.reg2), &(mfdR.bttn.rect), 2, 0, REG_USER_CONTROLLED, NULL, NULL,
271                       NULL, NULL);
272         uiGetRegionCursorStack(&(mfdR.bttn.reg), &cs);
273         uiSetRegionCursorStack(&(mfdR.bttn.reg2), cs);
274 
275         uiInstallRegionHandler(&(mfdL.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback_full,
276                                MFD_LEFT, &id);
277         uiInstallRegionHandler(&(mfdR.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback_full,
278                                MFD_RIGHT, &id);
279 
280         uiInstallRegionHandler(&(mfdL.bttn.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback,
281                                MFD_LEFT, &id);
282         uiInstallRegionHandler(&(mfdR.bttn.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback,
283                                MFD_RIGHT, &id);
284     }
285 
286     if (!done_init) {
287         done_init = TRUE;
288 
289         // CC: These bytes get made on the fly now
290         mfd_canvas_bits = (uchar *)malloc(MAX_WD(MFD_VIEW_WID) * MAX_HT(MFD_VIEW_HGT));
291 
292         // Pull in the background bitmap
293         f = RefLock(REF_IMG_bmBlankMFD);
294         mfd_background = f->bm;
295         mfd_background.bits = (uchar *)malloc(MAX_WD(MFD_VIEW_WID) * MAX_HT(MFD_VIEW_HGT));
296 
297         LG_memcpy(mfd_background.bits, (f + 1), f->bm.w * f->bm.h);
298         RefUnlock(REF_IMG_bmBlankMFD);
299 
300         gr_init_canvas(&_offscreen_mfd, mfd_canvas_bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT);
301         gr_init_canvas(&_fullscreen_mfd, mfd_background.bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT);
302         pmfd_canvas = &_offscreen_mfd;
303         init_newmfd_button_cursors();
304         mfd_init_funcs();
305     }
306     return;
307 }
308 
309 #ifdef SVGA_SUPPORT
mfd_update_screen_mode()310 errtype mfd_update_screen_mode() {
311     if (convert_use_mode == 0) {
312         gr_init_canvas(&_offscreen_mfd, mfd_canvas_bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT);
313         gr_init_canvas(&_fullscreen_mfd, mfd_background.bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT);
314     } else {
315 
316         int new_width = SCONV_X(MFD_VIEW_WID);
317         int new_height = SCONV_Y(MFD_VIEW_HGT);
318 
319         // CC: Resize the MFD bytes to fit the new mode
320         free(mfd_background.bits);
321         free(mfd_canvas_bits);
322 
323         mfd_background.bits = (uchar *)malloc(new_width * new_height);
324         mfd_canvas_bits = (uchar *)malloc(new_width * new_height);
325 
326         // Copy the background bytes
327         grs_bitmap *bm = lock_bitmap_from_ref(REF_IMG_bmBlankMFD);
328         LG_memcpy(mfd_background.bits, bm->bits, bm->w * bm->h);
329         RefUnlock(REF_IMG_bmBlankMFD);
330 
331         gr_init_canvas(&_offscreen_mfd, mfd_canvas_bits, BMT_FLAT8, new_width, new_height);
332         gr_init_canvas(&_fullscreen_mfd, mfd_background.bits, BMT_FLAT8, new_width, new_height);
333     }
334     return (OK);
335 }
336 
mfd_clear_all()337 errtype mfd_clear_all() {
338     if (full_game_3d) {
339         gr_push_canvas(&_offscreen_mfd);
340         gr_clear(0);
341         gr_pop_canvas();
342         gr_push_canvas(&_fullscreen_mfd);
343         gr_clear(0);
344         gr_pop_canvas();
345     }
346     return (OK);
347 }
348 #endif
349 
350 // ---------------------------------------------
351 // mfd_change_fullscreen(uchar on);
352 //
353 // set up and clean up for fullscreen mode.
354 
mfd_change_fullscreen(uchar on)355 void mfd_change_fullscreen(uchar on) {
356     if (on) {
357         gr_push_canvas(&_fullscreen_mfd);
358         gr_clear(0);
359         gr_pop_canvas();
360         gr_push_canvas(&_offscreen_mfd);
361         gr_clear(0);
362         gr_pop_canvas();
363     } else {
364         // we use the mfd background for the canvas, so
365         // put the background bitmap back
366         grs_bitmap *bm = lock_bitmap_from_ref(REF_IMG_bmBlankMFD);
367         RefUnlock(REF_IMG_bmBlankMFD);
368         LG_memcpy(_fullscreen_mfd.bm.bits, bm->bits, bm->w * bm->h);
369     }
370 }
371 
372 // ---------------------------------------------------------------------------
373 // keyboard_init_mfd()
374 //
375 // Tell the function keys that they're supposed to map to our button panels.
376 // (Called from init_input() in input.c)
377 
keyboard_init_mfd()378 void keyboard_init_mfd() {
379     extern void install_keypad_hotkeys(void);
380     /* KLC leave out F-keys and char codes.
381 
382        hotkey_add(KEY_F1, DEMO_CONTEXT,mfd_button_callback_kb,0);
383        hotkey_add(KEY_F2, DEMO_CONTEXT,mfd_button_callback_kb,1);
384        hotkey_add(KEY_F3, DEMO_CONTEXT,mfd_button_callback_kb,2);
385        hotkey_add(KEY_F4, DEMO_CONTEXT,mfd_button_callback_kb,3);
386        hotkey_add(KEY_F5, DEMO_CONTEXT,mfd_button_callback_kb,4);
387        hotkey_add(KEY_F6, DEMO_CONTEXT,mfd_button_callback_kb,5);
388        hotkey_add(KEY_F7, DEMO_CONTEXT,mfd_button_callback_kb,6);
389        hotkey_add(KEY_F8, DEMO_CONTEXT,mfd_button_callback_kb,7);
390        hotkey_add(KEY_F9, DEMO_CONTEXT,mfd_button_callback_kb,8);
391        hotkey_add(KEY_F10,DEMO_CONTEXT,mfd_button_callback_kb,9);
392     */
393     install_keypad_hotkeys();
394 }
395 
396 // --------------
397 //    FROBBERS
398 // --------------
399 
400 // ---------------------------------------------------------------------------
401 // set_slot_to_func()
402 //
403 // Sets a slot to point to a given function struct, and sets status too.
404 
set_slot_to_func(ubyte snum,ubyte fnum,MFD_Status stat)405 void set_slot_to_func(ubyte snum, ubyte fnum, MFD_Status stat) {
406 
407     player_struct.mfd_all_slots[snum] = fnum;
408 
409     if ((player_struct.mfd_slot_status[snum] == MFD_FLASH) && (stat == MFD_ACTIVE))
410         ;
411     else
412         player_struct.mfd_slot_status[snum] = stat;
413 
414     return;
415 }
416 
417 // ---------------------------------------------------------------------------
418 // mfd_clear_func()
419 //
420 // Set a functions last update to current game time, clear its CHANGEBIT
421 // field if set.
422 
mfd_clear_func(ubyte func_id)423 void mfd_clear_func(ubyte func_id) {
424     mfd_funcs[func_id].last = player_struct.game_time;
425 
426     player_struct.mfd_func_status[func_id] &= ~MFD_CHANGEBIT;
427     player_struct.mfd_func_status[func_id] &= ~MFD_CHANGEBIT_FULL;
428     return;
429 }
430 
431 #define MFD_STEREO_HACK_MODE 6
432 
433 // ---------------------------------------------------------------------------
434 // mfd_notify_func()
435 //
436 // Let a function know its been changed and needs re-exposure.
437 // Also check a specified slot to see if that function is there: if
438 // is not, we might grab it and put it there.  We also set the slot's
439 // status as demanded.
440 
441 //#define MFD_STEREO_HACK_MODE  ((i6d_device == I6D_CTM) ? 6 : 7)
mfd_notify_func(ubyte fnum,ubyte snum,uchar Grab,MFD_Status stat,uchar Full)442 void mfd_notify_func(ubyte fnum, ubyte snum, uchar Grab, MFD_Status stat, uchar Full) {
443     ubyte i, j;
444     int oldf = player_struct.mfd_all_slots[snum];
445     byte mfd_but[NUM_MFDS];
446 
447     if (fnum == NOTIFY_ANY_FUNC)
448         fnum = oldf;
449 
450     for (i = 0; i < NUM_MFDS; i++)
451         mfd_but[i] = -1;
452 
453     if ((oldf != fnum) && (Grab)) {
454         player_struct.mfd_all_slots[snum] = fnum;
455         Full = TRUE;
456     }
457 
458     player_struct.mfd_func_status[fnum] |= MFD_CHANGEBIT;
459     if (Full) {
460         void mfd_default_mru(uchar func);
461 #ifdef SVGA_SUPPORT
462         uchar old_over = gr2ss_override;
463         short temp;
464         gr2ss_override = OVERRIDE_ALL;
465         ss_set_hack_mode(MFD_STEREO_HACK_MODE, &temp);
466 #endif
467 
468         for (i = 0; i < NUM_MFDS; i++) {
469             if (oldf != fnum && player_struct.mfd_current_slots[i] == snum) {
470                 mfd_funcs[oldf].expose(&(mfd[i]), 0);
471             }
472         }
473 #ifdef SVGA_SUPPORT
474         ss_set_hack_mode(0, &temp);
475         gr2ss_override = old_over;
476 #endif
477         player_struct.mfd_func_status[fnum] |= MFD_CHANGEBIT_FULL;
478         mfd_default_mru(fnum);
479     }
480 
481     if (player_struct.mfd_all_slots[snum] == fnum) {
482         set_slot_to_func(snum, fnum, stat);
483 
484         // Find which buttons we have to redraw because of the change
485         for (i = 0; i < NUM_MFDS; i++) {
486             for (j = 0; j < MFD_NUM_VIRTUAL_SLOTS; j++) {
487                 if (player_struct.mfd_virtual_slots[i][j] == snum) {
488                     mfd_but[i] = j;
489                     if (player_struct.mfd_current_slots[i] == j) {
490                         player_struct.mfd_slot_status[snum] = stat;
491                         if (stat == MFD_FLASH)
492                             player_struct.mfd_slot_status[snum] = MFD_ACTIVE;
493                     }
494                 }
495             }
496         }
497 
498         // Now redraw them
499         if (_current_loop <= FULLSCREEN_LOOP && !global_fullmap->cyber)
500             for (i = 0; i < NUM_MFDS; i++)
501                 if (mfd_but[i] != -1)
502                     mfd_draw_button(i, mfd_but[i]);
503     }
504 
505     chg_set_flg(MFD_UPDATE);
506 
507     return;
508 }
509 
510 // ---------------------------------------------------------------------------
511 // mfd_get_func()
512 //
513 // Returns an MFD's slot's function.
514 
mfd_get_func(ubyte mfd_id,ubyte s)515 ubyte mfd_get_func(ubyte mfd_id, ubyte s) {
516     ubyte slot_num;
517 
518     slot_num = player_struct.mfd_virtual_slots[mfd_id][s];
519 
520     if (player_struct.mfd_slot_status[slot_num] == MFD_EMPTY)
521         return player_struct.mfd_empty_funcs[mfd_id];
522     else
523         return player_struct.mfd_all_slots[slot_num];
524 }
525 
526 // ---------------------------------------------------------------------------
527 // mfd_set_slot()
528 //
529 // Sets the mfd to a given slot without caring about turning off what was
530 // previously there, or any change-related state.  Permits usage from both
531 // initializer and slot-changer.
532 
mfd_set_slot(ubyte mfd_id,ubyte newSlot,uchar OnOff)533 void mfd_set_slot(ubyte mfd_id, ubyte newSlot, uchar OnOff) {
534     MFD_Func *f;
535     ubyte old_index;
536     ubyte f_id;
537     ubyte new_slot;
538     ubyte old_slot;
539 
540     old_index = player_struct.mfd_current_slots[mfd_id];
541     old_slot = player_struct.mfd_virtual_slots[mfd_id][old_index];
542     new_slot = player_struct.mfd_virtual_slots[mfd_id][newSlot];
543 
544     if (!OnOff && ((player_struct.mfd_slot_status[old_slot] != MFD_EMPTY) ||
545                    (player_struct.mfd_slot_status[new_slot] != MFD_EMPTY))) {
546         uchar old_over = gr2ss_override;
547         short temp;
548         gr2ss_override = OVERRIDE_ALL;
549         ss_set_hack_mode(MFD_STEREO_HACK_MODE, &temp);
550         f_id = mfd_get_func(mfd_id, newSlot);
551         f = &(mfd_funcs[f_id]);
552         f->expose(&(mfd[mfd_id]), 0);
553         ss_set_hack_mode(0, &temp);
554         gr2ss_override = old_over;
555     }
556 
557     if (player_struct.mfd_slot_status[new_slot] == MFD_FLASH) {
558         player_struct.mfd_slot_status[new_slot] = MFD_ACTIVE;
559 
560         // We have to tell other panel about this button change!
561         if (global_fullmap->cyber) {
562             if (mfd_id == MFD_LEFT)
563                 mfd_draw_button(MFD_RIGHT, newSlot);
564             else
565                 mfd_draw_button(MFD_LEFT, newSlot);
566         }
567     }
568 
569     if (OnOff) {
570         player_struct.mfd_current_slots[mfd_id] = newSlot;
571         mfd_force_update_single(mfd_id);
572         if (full_game_3d) {
573             if (!(full_visible & visible_mask(mfd_id))) {
574                 if (mfd_id == MFD_LEFT)
575                     gr_push_canvas(&_offscreen_mfd);
576                 else
577                     gr_push_canvas(&_fullscreen_mfd);
578                 gr_clear(0);
579                 gr_pop_canvas();
580             }
581 #ifdef STEREO_SUPPORT
582             if (convert_use_mode == 5)
583                 full_visible = visible_mask(mfd_id);
584             else
585 #endif
586             {
587                 full_visible |= visible_mask(mfd_id);
588             }
589             full_raise_region(&mfd[mfd_id].reg2);
590             chg_set_sta(FULLSCREEN_UPDATE);
591         }
592     }
593 
594     return;
595 }
596 
597 // ---------------------------------------------------------------------------
598 // mfd_change_slot()
599 //
600 // Shifts an mfd over to a new slot.
601 
mfd_change_slot(ubyte mfd_id,ubyte new_slot)602 void mfd_change_slot(ubyte mfd_id, ubyte new_slot) {
603     ubyte old;
604 
605     if (global_fullmap->cyber && (new_slot != MFD_INFO_SLOT || mfd_id != MFD_RIGHT))
606         return; // no slots in c-space
607     old = player_struct.mfd_current_slots[mfd_id];
608 
609     if (new_slot == old && !full_game_3d)
610         return;
611 
612     // Tell old slot it needs to stop drawing whatever it was in that mfd
613     if (new_slot != old)
614         mfd_set_slot(mfd_id, old, FALSE);
615 
616     // Set to new slot and draw its graphics if neccessary
617     mfd_set_slot(mfd_id, new_slot, TRUE);
618 
619     // Update the buttons
620     if (!global_fullmap->cyber) {
621         mfd_draw_button(mfd_id, old);
622         mfd_draw_button(mfd_id, new_slot);
623     }
624 
625     return;
626 }
627 
628 // ---------------------------------------------------------------------------
629 // mfd_grab()
630 //
631 // Picks an MFD to grab (i.e. the MFD whose information is
632 // lowest priority.  Returns the mfd id.
633 
mfd_grab(void)634 int mfd_grab(void) {
635     int i;
636     ubyte min = 0;
637     int id;
638     for (i = 0; i < NUM_MFDS; i++) {
639         ubyte slot = player_struct.mfd_current_slots[i];
640         ubyte func = mfd_get_func(i, slot);
641         ubyte p = mfd_funcs[func].priority;
642         if (p > min) {
643             min = p;
644             id = i;
645         }
646     }
647     return id;
648 }
649 
650 // ---------------------------------------------------------------------------
651 // mfd_grab_func()
652 //
653 // Like mfd_grab(), except specifies a func number.  If any mfd is already
654 // set to that func, returns that mfd id instead of the lowest prority.
655 // Otherwise, if there is already an mfd on the given slot, returns that
656 // mfd.  If neither of these conditions holds, returns the mfd with the
657 // lowest priority.  If other mfds are on the same slot as the one we
658 // are grabbing for a new func, try to restore to them.
659 
660 // mfd_choose_func() does the same thing as mfd_grab_func, but does not
661 // assume we necessarily actually want to grab the mfd it returns, and
662 // therefore does not restore to other mfds on the same slot.
663 //
mfd_choose_func(int my_func,int my_slot)664 int mfd_choose_func(int my_func, int my_slot) {
665     int i;
666     ubyte min = 0;
667     ubyte slot, func, p;
668     int lowid, retval, sameslotid = -1;
669 
670     for (i = 0; i < NUM_MFDS; i++) {
671         slot = player_struct.mfd_current_slots[i];
672         func = mfd_get_func(i, slot);
673         p = mfd_funcs[func].priority;
674 
675         if (func == my_func)
676             return i;
677         if (slot == my_slot)
678             sameslotid = i;
679         if (p > min) {
680             min = p;
681             lowid = i;
682         }
683     }
684     if (sameslotid != -1)
685         retval = sameslotid;
686     else
687         retval = lowid;
688 
689     return retval;
690 }
691 
mfd_grab_func(int my_func,int my_slot)692 int mfd_grab_func(int my_func, int my_slot) {
693     ubyte mfd, slot;
694     int i;
695 
696     mfd = mfd_choose_func(my_func, my_slot);
697     slot = player_struct.mfd_current_slots[mfd];
698 
699     // if more than one mfd is on the slot we're grabbing, try
700     // restoring to the other slots.
701     for (i = 0; i < NUM_MFDS; i++) {
702         if (i != mfd && player_struct.mfd_current_slots[i] == slot) {
703             restore_mfd_slot(i);
704             break;
705         }
706     }
707     return mfd;
708 }
709 
710 // -----------------------------------------------------------------------
711 // mfd_yield_func()
712 //
713 //   If no mfd has func as its current function, returns FALSE.  Otherwise,
714 // if *mfd_id is NUM_MFDS, sets mfd_id to the lowest mfd id which has func as
715 // its function and returns TRUE.  Otherwise, sets mfd_id to the lowest
716 // such id which is greater than the one provided and returns TRUE, or
717 // returns FALSE if there is no such greater mfd.  Thus acts as an
718 // iterator on mfd's with the given func.
719 //   Why, you may ask?  'Cause it's pretty much exactly as easy as a
720 // function which just finds out if some mfd has this current function,
721 // which is what I need, and it's loads more generally useful.  Har har.
722 
mfd_yield_func(int func,int * mfd_id)723 uchar mfd_yield_func(int func, int *mfd_id) {
724     int id;
725 
726     for (id = (*mfd_id != NUM_MFDS) ? (*mfd_id) + 1 : 0; id < NUM_MFDS; id++) {
727         if (mfd_get_active_func(id) == func) {
728             *mfd_id = id;
729             return TRUE;
730         }
731     }
732     return FALSE;
733 }
734 
735 // -----------------------------------------------------------
736 // mfd_zoom_rect(Rect* start, int mfd)
737 //
738 // Zooms a rect from the specified starting point to the
739 // the indicated rect.
740 
mfd_zoom_rect(LGRect * start,int mfdnum)741 void mfd_zoom_rect(LGRect *start, int mfdnum) {
742     extern void zoom_rect(LGRect * start, LGRect * end);
743     DEBUG("Zooming mfd %i", mfdnum);
744     LGRect r1, r2;
745     play_digi_fx(SFX_ZOOM_BOX, 1);
746     r1 = *start;
747     r2 = mfd[mfdnum].rect;
748     zoom_rect(&r1, &r2);
749 }
750 
751 // ------------------------
752 //    CALLBACK FUNCTIONS
753 // ------------------------
754 
755 // ------------------------------------------------------------------
756 // mfd_object_cursor_handler() gets called for events in the MFD
757 // region with an object on the cursor.
758 
759 extern uchar inventory_add_object(ObjID, bool);
760 uchar object_button_down = FALSE;
761 
mfd_object_cursor_handler(uiEvent * ev,LGRegion * reg,int which_mfd)762 uchar mfd_object_cursor_handler(uiEvent *ev, LGRegion *reg, int which_mfd) {
763     uchar retval = FALSE;
764     int trip, mid;
765     int new_slot = -1;
766     ObjID obj = object_on_cursor;
767     if (ev->type != UI_EVENT_MOUSE)
768         return TRUE;
769     if (ev->subtype & (MOUSE_RDOWN | MOUSE_LDOWN)) {
770         object_button_down = TRUE;
771         retval = TRUE;
772     }
773     if ((ev->subtype & (MOUSE_LUP | MOUSE_RUP)) && object_button_down) {
774         extern uchar gump_num_objs;
775         uchar is_gump = mfd_get_active_func(which_mfd) == MFD_GUMP_FUNC && gump_num_objs != 0;
776 
777         object_button_down = FALSE;
778         retval = TRUE;
779         if (inventory_add_object(object_on_cursor, !is_gump)) {
780             if (!is_gump) {
781                 switch (objs[obj].obclass) {
782                 case CLASS_GUN:
783                     new_slot = MFD_WEAPON_SLOT;
784                     break;
785                 case CLASS_AMMO:
786                     trip = current_weapon_trip();
787                     if (trip != -1 && gun_takes_ammo(trip, ID2TRIP(obj)) &&
788                         player_struct.mfd_current_slots[which_mfd] == MFD_WEAPON_SLOT)
789                         ; // do nothing
790                     else
791                         new_slot = MFD_ITEM_SLOT;
792                     break;
793 
794                 case CLASS_SOFTWARE:
795                     if (objs[obj].subclass == SOFTWARE_SUBCLASS_DATA) {
796                         new_slot = MFD_INFO_SLOT;
797                         break;
798                     }
799                 default:
800                     new_slot = MFD_ITEM_SLOT;
801                 }
802                 for (mid = 0; mid < NUM_MFDS; mid++) {
803                     if (mid != which_mfd && mfd_index(mid) == new_slot)
804                         restore_mfd_slot(mid);
805                 }
806                 mfd_change_slot(which_mfd, new_slot);
807                 mfd_force_update_single(which_mfd);
808             }
809             pop_cursor_object();
810         }
811     }
812     return retval;
813 }
814 
815     // ---------------------------------------------------------------------------
816     // mfd_view_callback()
817     //
818     // The callback for the MFD view windows.  Triggered by mouseclicks inside
819     // the regions.
820 
821 #define SEARCH_MARGIN 2
822 
mfd_scan_opacity(int mfd_id,LGPoint epos)823 uchar mfd_scan_opacity(int mfd_id, LGPoint epos) {
824     uchar retval = FALSE;
825     LGPoint pos = epos;
826     short x, y;
827     grs_canvas *cv = ((int)mfd_id == MFD_RIGHT) ? &_fullscreen_mfd : &_offscreen_mfd;
828 
829     pos.x -= mfd[mfd_id].reg.abs_x;
830     pos.y -= mfd[mfd_id].reg.abs_y;
831     gr_push_canvas(cv);
832     for (x = pos.x - SEARCH_MARGIN; x <= pos.x + SEARCH_MARGIN; x++)
833         for (y = pos.y - SEARCH_MARGIN; y <= pos.y + SEARCH_MARGIN; y++)
834             if (gr_get_pixel(x, y) != 0)
835                 retval = TRUE;
836     gr_pop_canvas();
837     return retval;
838 }
839 
mfd_view_callback_full(uiEvent * e,LGRegion * r,intptr_t udata)840 uchar mfd_view_callback_full(uiEvent *e, LGRegion *r, intptr_t udata) {
841     uchar retval = FALSE;
842     uchar mask;
843     if (udata == MFD_RIGHT)
844         mask = FULL_R_MFD_MASK;
845     else
846         mask = FULL_L_MFD_MASK;
847     if (full_visible & mask) {
848         retval = mfd_view_callback(e, r, udata);
849         if (!retval) {
850             retval = mfd_scan_opacity(udata, e->pos);
851         }
852     }
853     return retval;
854 }
855 
mfd_view_callback(uiEvent * e,LGRegion * r,intptr_t udata)856 uchar mfd_view_callback(uiEvent *e, LGRegion *r, intptr_t udata) {
857     int i;
858     int which_mfd;
859     MFD *m;
860     ubyte func_id;
861     MFD_Func *f;
862 
863     LGRegion dummy; // dummy
864     dummy = *r;     // dummy
865 
866     which_mfd = (int)udata;
867 
868     // We should pass on info to the appropriate slot's current handler
869     if (which_mfd == MFD_LEFT)
870         m = &mfdL;
871     else
872         m = &mfdR;
873 
874     if (input_cursor_mode == INPUT_OBJECT_CURSOR)
875         return mfd_object_cursor_handler(e, r, which_mfd);
876     else
877         object_button_down = FALSE;
878     func_id = mfd_get_active_func(which_mfd);
879     f = &(mfd_funcs[func_id]);
880     if (f->simp && f->simp(m, e))
881         return TRUE;
882     for (i = 0; i < f->handler_count; i++) {
883         LGPoint pos = e->pos;
884 #ifdef STEREO_SUPPORT
885         if (convert_use_mode == 5) {
886             pos.y -= m->rect.ul.y;
887             switch (i6d_device) {
888             case I6D_CTM:
889                 if (which_mfd == 0)
890                     pos.x -= m->rect.ul.x;
891                 else
892                     pos.x -= (m->rect.ul.x << 1);
893                 break;
894             case I6D_VFX1:
895                 Warning(("original pos.x = %d, m->rect.ul.x = %d!\n", pos.x, m->rect.ul.x));
896                 pos.x -= (m->rect.ul.x);
897                 break;
898             }
899         } else {
900 #endif
901             pos.x -= m->rect.ul.x;
902             pos.y -= m->rect.ul.y;
903 #ifdef STEREO_SUPPORT
904         }
905 #endif
906         if (RECT_TEST_PT(&f->handlers[i].r, pos))
907             if (f->handlers[i].proc(m, e, &f->handlers[i]))
908                 return TRUE;
909     }
910 
911     return FALSE;
912 }
913 
914 // ---------------------------------------------------------------------------
915 // mfd_button_callback()
916 //
917 // The callback for the MFD button panels.  Triggered by mouseclicks inside
918 // the button panels.
919 int last_mfd_cnum[NUM_MFDS] = {-1, -1};
mfd_button_callback(uiEvent * e,LGRegion * r,intptr_t udata)920 uchar mfd_button_callback(uiEvent *e, LGRegion *r, intptr_t udata) {
921     int cnum, which_panel, which_button;
922     div_t result;
923 
924 #ifndef NO_DUMMIES
925     LGRegion dummy;
926     dummy = *r;
927 #endif
928 
929     if (global_fullmap->cyber) {
930         uiSetRegionDefaultCursor(r, NULL);
931         return FALSE;
932     } else {
933         which_panel = (int)udata;
934 
935         // Divide mouseclick height to discover which button we meant
936         result = div((e->pos.y - MFD_BTTN_Y), MFD_BTTN_SZ + MFD_BTTN_BLNK);
937         which_button = result.quot;
938 
939         cnum = which_button;
940 
941         if (player_struct.mfd_slot_status[which_button] == MFD_UNAVAIL || !popup_cursors) {
942             if ((cnum != last_mfd_cnum[which_panel])) {
943                 last_mfd_cnum[which_panel] = cnum;
944                 uiSetRegionDefaultCursor(r, &globcursor);
945             }
946         }
947         if (player_struct.mfd_slot_status[which_button] != MFD_UNAVAIL) {
948             if ((cnum != last_mfd_cnum[which_panel]) && popup_cursors) {
949                 LGPoint offset = {0, 0};
950                 last_mfd_cnum[which_panel] = cnum;
951                 free(mfd_bttn_bitmaps[which_panel].bits);
952                 make_popup_cursor(&mfd_bttn_cursors[which_panel], &mfd_bttn_bitmaps[which_panel], cursor_strings[cnum],
953                                   which_panel, TRUE, offset);
954                 uiSetRegionDefaultCursor(r, &mfd_bttn_cursors[which_panel]);
955             }
956 
957             if (!(e->mouse_data.action & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)))
958                 return TRUE; // ignore all but left clickdowns
959 
960             // If things are ok, select button
961             if ((result.rem < MFD_BTTN_SZ) && (which_button < MFD_NUM_VIRTUAL_SLOTS))
962                 mfd_select_button(which_panel, which_button);
963         }
964     }
965 
966     return TRUE;
967 }
968 
969 // ---------------------------------------------------------------------------
970 // mfd_button_callback_kb()
971 //
972 // The callback for the MFD button panels, as triggered by function keys
973 
mfd_button_callback_kb(ushort keycode,uint32_t context,intptr_t data)974 uchar mfd_button_callback_kb(ushort keycode, uint32_t context, intptr_t data) {
975     int which_panel, which_button;
976     int fkeynum;
977 
978     if (!global_fullmap->cyber) {
979         fkeynum = (int)context; // dummy funcs
980         fkeynum = (int)keycode;
981 
982         fkeynum = (int)data;
983 
984         if (fkeynum >= MFD_NUM_VIRTUAL_SLOTS)
985             which_panel = MFD_RIGHT;
986         else
987             which_panel = MFD_LEFT;
988 
989         which_button = ((int)data) % MFD_NUM_VIRTUAL_SLOTS;
990 
991         mfd_select_button(which_panel, which_button);
992     }
993 
994     return TRUE;
995 }
996 
997 // ---------------------------------------------------------------------------
998 // mfd_select_button()
999 //
1000 // A more specific version of mfd_button_callback(), where we've figured
1001 // out which button exactly has been hit, whether because we came here
1002 // straight from a function key or through the mouse callback parser.
1003 // The passed argument is the equivalent of the function key number, even
1004 // if we got it from the mouse handler.
1005 
mfd_select_button(int which_panel,int which_button)1006 void mfd_select_button(int which_panel, int which_button) {
1007     // Play sound effect
1008     ubyte old = player_struct.mfd_current_slots[which_panel];
1009     int hnd = play_digi_fx(SFX_MFD_BUTTON, 1);
1010 
1011     if (hnd >= 0) {
1012         snd_digi_parms *ssp;
1013         ssp = snd_sample_parms(hnd);
1014 
1015         // Woo hoo, hardcode city
1016         if (which_panel == MFD_LEFT)
1017             ssp->pan = 30;
1018         else
1019             ssp->pan = 97;
1020     }
1021 
1022     if (full_game_3d && which_button == old && (full_visible & visible_mask(which_panel))) {
1023         full_visible &= ~visible_mask(which_panel);
1024     } else
1025         mfd_change_slot((ubyte)which_panel, (ubyte)which_button);
1026 
1027     return;
1028 }
1029 
1030 // ---------------------------------------------------------------------------
1031 // mfd_update()
1032 //
1033 // This is what gets called from the main loop each frame.
1034 
mfd_update()1035 void mfd_update() {
1036     static uchar LastFlash = FALSE;
1037     ubyte steps_cache[NUM_MFDS] = {0, 0};
1038 
1039     int i, j;
1040     ubyte slots[NUM_MFDS];
1041     ubyte status_cache[NUM_MFDS];
1042 
1043     Flash = (bool)((player_struct.game_time / MFD_BTTN_FLASH_TIME) % 2);
1044 
1045     if (!global_fullmap->cyber)
1046         for (i = 0; i < NUM_MFDS; i++) {
1047             for (j = 0; j < MFD_NUM_VIRTUAL_SLOTS; j++) {
1048                 slots[i] = player_struct.mfd_virtual_slots[i][j];
1049                 if (player_struct.mfd_slot_status[slots[i]] == MFD_FLASH) {
1050                     chg_set_flg(MFD_UPDATE);
1051                     if (LastFlash != Flash)
1052                         mfd_draw_button(i, j);
1053                 }
1054             }
1055         }
1056     if (LastFlash != Flash)
1057         LastFlash = Flash;
1058 
1059         // Is it time to update appropriate mfd's?
1060         // Check only current slots, and look at flag to see
1061         // if they need constant update
1062 
1063 #ifndef BAD_BITS_BUG_FIXED
1064     _fullscreen_mfd.bm.bits = mfd_background.bits;
1065     _offscreen_mfd.bm.bits = mfd_canvas_bits;
1066 #endif // BAD_BITS_BUG_FIXED
1067 
1068     // This code totally depends on our item func implementation.
1069     i = NUM_MFDS;
1070     if (mfd_yield_func(MFD_ITEM_FUNC, &i)) {
1071         extern void update_item_mfd(void);
1072         update_item_mfd();
1073     }
1074 
1075     // Build the status cache.
1076     for (i = 0; i < NUM_MFDS; i++) {
1077         ubyte f_id = mfd_get_active_func(i);
1078         MFD_Func *f = &(mfd_funcs[f_id]);
1079         status_cache[i] = (player_struct.mfd_func_status[f_id]);
1080         if (f->flags & MFD_INCREMENTAL) {
1081             long deltat = (player_struct.game_time - f->last) >> 4;
1082             ubyte increment = (player_struct.mfd_func_status[f_id] >> 4);
1083             ubyte num_steps = (increment > 0) ? lg_max(0, deltat / increment) : 0;
1084             steps_cache[i] = num_steps;
1085             chg_set_flg(MFD_UPDATE);
1086         }
1087     }
1088 
1089     // Now update the stati that need it.
1090     for (i = 0; i < NUM_MFDS; i++) {
1091         if ((status_cache[i] & (MFD_CHANGEBIT | MFD_CHANGEBIT_FULL)) || steps_cache[i] > 0) {
1092             ubyte f_id = mfd_get_active_func(i);
1093             mfd_clear_func(f_id);
1094             mfd_update_current_slot(i, status_cache[i], steps_cache[i]);
1095         }
1096     }
1097     return;
1098 }
1099 
1100 // ---------------------------------------------------------------------------
1101 // mfd_update_current_slot()
1102 //
1103 // See if we need to update anything in the current slot being
1104 // viewed in an MFD.  Returns TRUE if it updated a function.
1105 
mfd_update_current_slot(ubyte mfd_id,ubyte status,ubyte num_steps)1106 uchar mfd_update_current_slot(ubyte mfd_id, ubyte status, ubyte num_steps) {
1107     MFD_Func *f;
1108     ubyte f_id;
1109     ubyte control;
1110     MFD *m;
1111 
1112     extern ObjID check_panel_ref(uchar puntme);
1113     extern uchar mfd_distance_remove(ubyte func);
1114 
1115     f_id = mfd_get_active_func(mfd_id);
1116     f = &(mfd_funcs[f_id]);
1117     m = &(mfd[mfd_id]);
1118     if (player_struct.panel_ref == OBJ_NULL && mfd_distance_remove(f_id)) {
1119         check_panel_ref(TRUE);
1120     }
1121 
1122     // If the change bit is set, or if the function is incremental
1123     // and enough time has gone by, then we need to expose
1124 
1125     {
1126 #ifdef SVGA_SUPPORT
1127         uchar old_over = gr2ss_override;
1128         short temp;
1129         gr2ss_override = OVERRIDE_ALL;
1130         ss_set_hack_mode(MFD_STEREO_HACK_MODE, &temp);
1131 #endif
1132         control = (num_steps << 4) | MFD_EXPOSE;
1133         if (full_game_3d || (status & MFD_CHANGEBIT_FULL))
1134             control |= MFD_EXPOSE_FULL;
1135 
1136         // Okay, if we are in full screen mode, secretly switch the fine canvas
1137         // usually served in this restaurant with our own Folger's brand canvas
1138 
1139         pmfd_canvas = (full_game_3d && mfd_id == MFD_RIGHT) ? &_fullscreen_mfd : &_offscreen_mfd;
1140         if (full_game_3d && (control & MFD_EXPOSE_FULL)) {
1141             gr_push_canvas(pmfd_canvas);
1142             gr_clear(0);
1143             gr_pop_canvas();
1144         }
1145 
1146         f->expose(m, control); // pass # steps + flags to
1147 #ifdef SVGA_SUPPORT
1148         ss_set_hack_mode(0, &temp);
1149         gr2ss_override = old_over;
1150 #endif
1151 
1152         return TRUE;
1153     }
1154 }
1155 
1156 // ---------------------------------------------------------------------------
1157 // mfd_force_update()
1158 //
1159 // Forces a redraw of both the button panels and mfd slots
1160 
mfd_force_update()1161 void mfd_force_update() {
1162     ubyte i;
1163     for (i = 0; i < NUM_MFDS; i++) {
1164         mfd_force_update_single(i);
1165     }
1166 }
1167 
1168 // ---------------------------------------------------------------------------
1169 // mfd_force_update()
1170 //
1171 // Forces a redraw of one of the button panels and mfd slots
1172 
mfd_force_update_single(int which_mfd)1173 void mfd_force_update_single(int which_mfd) {
1174     ubyte f_id, s_id;
1175     MFD_Status stat;
1176 
1177     if (_current_loop <= FULLSCREEN_LOOP)
1178         mfd_draw_all_buttons(which_mfd);
1179 
1180     f_id = mfd_get_active_func(which_mfd);
1181     s_id = player_struct.mfd_virtual_slots[which_mfd][mfd_index(which_mfd)];
1182     stat = player_struct.mfd_slot_status[s_id];
1183 
1184     mfd_notify_func(f_id, s_id, FALSE, stat, TRUE);
1185 
1186     return;
1187 }
1188 
1189 //--------------------------------------------------------
1190 // fullscreen_refresh_mfd()
1191 //
1192 // re-blits a single mfd.
1193 
fullscreen_refresh_mfd(ubyte mfd_id)1194 void fullscreen_refresh_mfd(ubyte mfd_id) {
1195     ushort a, b, c, d;
1196     LGRect r;
1197     MFD *m = &mfd[mfd_id];
1198     uchar visible = (full_visible & visible_mask(mfd_id)) != 0;
1199 #ifdef SVGA_SUPPORT
1200     uchar old_over = gr2ss_override;
1201 #endif
1202     if (visible) {
1203         pmfd_canvas = (mfd_id == MFD_RIGHT) ? &_fullscreen_mfd : &_offscreen_mfd;
1204 
1205         r.ul = MakePoint(0, 0);
1206         r.lr = MakePoint(MFD_VIEW_WID, MFD_VIEW_HGT);
1207         RECT_MOVE(&r, m->rect.ul);
1208         STORE_CLIP(a, b, c, d);
1209 #ifdef SVGA_SUPPORT
1210         gr2ss_override = OVERRIDE_ALL;
1211 #endif
1212 #ifdef STEREO_SUPPORT
1213         if (convert_use_mode == 5) {
1214             pmfd_canvas->bm.flags |= BMF_TRANS;
1215             if (mfd_id == 0) {
1216                 ss_safe_set_cliprect(r.ul.x, 0, r.lr.x << 1, r.lr.y);
1217                 if (i6d_device == I6D_CTM)
1218                     ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, -5);
1219                 else
1220                     ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, m->rect.ul.y);
1221             } else {
1222                 ss_safe_set_cliprect(r.ul.x >> 1, 0, r.lr.x, r.lr.y);
1223                 if (i6d_device == I6D_CTM)
1224                     ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x >> 1, -5);
1225                 else
1226                     ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x >> 1, m->rect.ul.y);
1227             }
1228             pmfd_canvas->bm.flags &= ~BMF_TRANS;
1229         } else {
1230 #endif
1231             ss_safe_set_cliprect(r.ul.x, r.ul.y, r.lr.x, r.lr.y);
1232             pmfd_canvas->bm.flags |= BMF_TRANS;
1233             ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, m->rect.ul.y);
1234             pmfd_canvas->bm.flags &= ~BMF_TRANS;
1235 #ifdef STEREO_SUPPORT
1236         }
1237 #endif
1238 #ifdef SVGA_SUPPORT
1239         gr2ss_override = old_over;
1240 #endif
1241         RESTORE_CLIP(a, b, c, d);
1242     }
1243     region_set_invisible(&m->reg2, !visible);
1244 }
1245 
1246 // ---------------------------
1247 //    MFD INTERNAL GRAPHICS
1248 // ---------------------------
1249 
1250 // ---------------------------------------------------------------------------
1251 // mfd_draw_button()
1252 //
1253 // Draws a button in a given color code depending on its status.
1254 
1255 uchar cyber_button_back_door = FALSE;
1256 
mfd_draw_button(ubyte mfd_id,ubyte b)1257 void mfd_draw_button(ubyte mfd_id, ubyte b) {
1258     MFD *m;
1259     LGRect r;
1260     ubyte slot;
1261 #ifdef SVGA_SUPPORT
1262     uchar old_over = gr2ss_override;
1263     gr2ss_override = OVERRIDE_ALL;
1264 #endif
1265 
1266     if (global_fullmap->cyber && full_game_3d)
1267         return;
1268 
1269     m = &(mfd[mfd_id]);
1270 
1271     r.ul.x = m->bttn.rect.ul.x + 1;
1272     r.ul.y = m->bttn.rect.ul.y + 1;
1273     r.ul.y += (b * (MFD_BTTN_SZ + MFD_BTTN_BLNK));
1274 
1275     r.lr.x = r.ul.x + MFD_BTTN_WID - 1;
1276     r.lr.y = r.ul.y + MFD_BTTN_SZ;
1277 
1278     slot = player_struct.mfd_virtual_slots[mfd_id][b];
1279 
1280     if (player_struct.mfd_slot_status[slot] == MFD_EMPTY)
1281         gr_set_fcolor(MFD_BTTN_EMPTY);
1282     else if (player_struct.mfd_slot_status[slot] == MFD_ACTIVE)
1283         gr_set_fcolor(MFD_BTTN_ACTIVE);
1284     else if (player_struct.mfd_slot_status[slot] == MFD_UNAVAIL)
1285         gr_set_fcolor(MFD_BTTN_UNAVAIL);
1286     else if (player_struct.mfd_slot_status[slot] == MFD_FLASH) {
1287         if (Flash)
1288             gr_set_fcolor((long)MFD_BTTN_FLASH); // Is blink on or off?
1289         else
1290             gr_set_fcolor((long)MFD_BTTN_EMPTY); // Draw appropriately
1291     }
1292 
1293     if (mfd_index(m->id) == b)
1294         gr_set_fcolor((long)MFD_BTTN_SELECT); // current
1295 
1296     uiHideMouse(&r);
1297     ss_rect(r.ul.x, r.ul.y, r.lr.x - 2, r.lr.y - 2);
1298 /*{
1299         short		bx = (mfd_id == 0) ? 3 : 629;
1300         short		by = 333 + (b*26);
1301         gr_rect(bx, by, bx+6, by+17);
1302 }*/
1303 #ifdef SVGA_SUPPORT
1304     gr2ss_override = old_over;
1305 #endif
1306     uiShowMouse(&r);
1307 
1308     return;
1309 }
1310 
1311     // ---------------------------------------------------------------------------
1312     // mfd_draw_button_panel()
1313     //
1314     // Draws an MFD's panel of buttons, and their associated background art as well
1315 
1316 #define MFD_PANEL_Y 326
1317 #define MFD_LEFT_PANEL_X 1
1318 #define MFD_RIGHT_PANEL_X 627
1319 
mfd_draw_button_panel(ubyte mfd_id)1320 void mfd_draw_button_panel(ubyte mfd_id) {
1321     int x[2] = {MFD_LEFT_PANEL_X, MFD_RIGHT_PANEL_X};
1322 
1323     draw_res_bm(REF_IMG_bmMFDButtonBackground, x[mfd_id], MFD_PANEL_Y);
1324     mfd_draw_all_buttons(mfd_id);
1325     return;
1326 }
1327 
1328 // ---------------------------------------------------------------------------
1329 // mfd_draw_all_buttons()
1330 //
1331 // Draws an MFD's panel of buttons.
1332 
mfd_draw_all_buttons(ubyte mfd_id)1333 void mfd_draw_all_buttons(ubyte mfd_id) {
1334     ubyte i;
1335 
1336     for (i = 0; i < MFD_NUM_VIRTUAL_SLOTS; i++)
1337         mfd_draw_button(mfd_id, i);
1338 
1339     return;
1340 }
1341 
1342 // ---------------------------------------------------------------------------
1343 // mfd_draw_string()
1344 //
1345 // Draws a string to the mfd canvas, at a relative x, y location.  It is
1346 // the calling expose functions responsibility to recopy from the canvas
1347 // to the screen.  Returns a point describing the pixel dimensions of the string
1348 
1349 uchar mfd_string_wrap = TRUE;
1350 ubyte mfd_string_shadow = MFD_SHADOW_FULLSCREEN;
1351 
mfd_full_draw_string(char * s,short x,short y,long c,int font,uchar DrawString,uchar transp)1352 LGPoint mfd_full_draw_string(char *s, short x, short y, long c, int font, uchar DrawString, uchar transp) {
1353     LGPoint siz;
1354     short w, h;
1355     ushort sc1, sc2, sc3, sc4;
1356     short border = 0;
1357     grs_font *thefont = ResLock(font);
1358 
1359     x = lg_min(lg_max(x, 0), MFD_VIEW_WID - 1);
1360     y = lg_min(lg_max(y, 0), MFD_VIEW_HGT - 1);
1361     STORE_CLIP(sc1, sc2, sc3, sc4);
1362     if ((full_game_3d && mfd_string_shadow == MFD_SHADOW_FULLSCREEN) ||
1363         mfd_string_shadow == MFD_SHADOW_ALWAYS)
1364         border = 1;
1365 
1366     gr_set_font(thefont);
1367     if (mfd_string_wrap)
1368       gr_string_wrap(s, MFD_VIEW_WID - x - 1);
1369     gr_string_size(s, &w, &h);
1370     w = lg_min(w, MFD_VIEW_WID - x - border);
1371     h = lg_min(h, MFD_VIEW_HGT - y - border);
1372     siz.x = w;
1373     siz.y = h;
1374     if (w <= 0 || h <= 0)
1375         goto out;
1376     ss_safe_set_cliprect(lg_max(x - border, 0), lg_max(y - border, 0), x + w + border, y + h + border);
1377     if (!full_game_3d && !transp)
1378         ss_bitmap(&mfd_background, 0, 0);
1379     // gr_bitmap(&mfd_background,0,0);
1380     if (DrawString) {
1381         gr_set_fcolor(c);
1382         draw_shadowed_string(s, x, y, border > 0);
1383     }
1384     mfd_add_rect(x - border, y - border, x + w + border, y + h + border);
1385 out:
1386     if (mfd_string_wrap)
1387       gr_font_string_unwrap(s);
1388     ResUnlock(font);
1389     RESTORE_CLIP(sc1, sc2, sc3, sc4);
1390 
1391     return siz;
1392 }
1393 
mfd_draw_font_string(char * s,short x,short y,long c,int font,uchar DrawString)1394 LGPoint mfd_draw_font_string(char *s, short x, short y, long c, int font, uchar DrawString) {
1395     // Hey, this used to always specify non-transparent strings,but that just plain
1396     // seemed wrong, so I switched it.... -- Xemu
1397     return mfd_full_draw_string(s, x, y, c, font, DrawString, TRUE);
1398 }
1399 
mfd_draw_string(char * s,short x,short y,long c,uchar DrawString)1400 LGPoint mfd_draw_string(char *s, short x, short y, long c, uchar DrawString) {
1401     return mfd_draw_font_string(s, x, y, c, RES_tinyTechFont, DrawString);
1402 }
1403 
1404 // ----------------------------------------------------------------------
1405 // mfd_draw_bitmap() draws a bitmap and adds its rect to the
1406 // update list.
1407 
mfd_draw_bitmap(grs_bitmap * bmp,short x,short y)1408 void mfd_draw_bitmap(grs_bitmap *bmp, short x, short y) {
1409     ss_bitmap(bmp, x, y);
1410     mfd_add_rect(x, y, x + bmp->w, y + bmp->h);
1411 }
1412 
1413 // --------------------------------------------------------------------------
1414 // mfd_partial_clear()
1415 //
1416 // Clears a portion of an mfd canvas
1417 
mfd_partial_clear(LGRect * r)1418 void mfd_partial_clear(LGRect *r) {
1419     if (!full_game_3d) {
1420         ss_safe_set_cliprect(r->ul.x, r->ul.y, r->lr.x, r->lr.y);
1421         mfd_add_rect(r->ul.x, r->ul.y, r->lr.x, r->lr.y);
1422         ss_bitmap(&mfd_background, 0, 0);
1423         // gr_bitmap(&mfd_background, 0, 0);
1424     }
1425 
1426     return;
1427 }
1428 
1429     // -------------------------------------------------------------------
1430     // UPDATE RECT STUFF
1431     //
1432     // Here we are collecting a list refresh rectangles which will be
1433     // updated from the off-screen mfd canvas
1434 
1435 #define NUM_MFD_RECTS 16
1436 LGRect mfd_update_list[NUM_MFD_RECTS];
1437 int mfd_num_updates = 0;
1438 
1439 // -------------------------------------------------------------------
1440 //
1441 // mfd_clear_rects(), clears the update list to empty
1442 
mfd_clear_rects(void)1443 void mfd_clear_rects(void) { mfd_num_updates = 0; }
1444 
1445 // -------------------------------------------------------------------
1446 //
1447 // mfd_add_rect() adds a rect to the rect list.
1448 
mfd_add_rect(short x,short y,short x1,short y1)1449 errtype mfd_add_rect(short x, short y, short x1, short y1) {
1450     int i;
1451     LGRect r;
1452     short tmp;
1453     // check for invalid rect
1454     if (x > x1) {
1455         tmp = x;
1456         x = x1;
1457         x1 = tmp;
1458     }
1459     if (y > y1) {
1460         tmp = y;
1461         y = y1;
1462         y1 = tmp;
1463     }
1464     r.ul.x = x;
1465     r.ul.y = y;
1466     r.lr.x = x1;
1467     r.lr.y = y1;
1468     for (i = 0; i < mfd_num_updates; i++)
1469         if (RECT_TEST_SECT(&mfd_update_list[i], &r)) {
1470             // If we intersect with some existing rect, union it in to r and
1471             // Delete it from the list
1472             RECT_UNION(&mfd_update_list[i], &r, &r);
1473             if (i != mfd_num_updates - 1)
1474                 mfd_update_list[i] = mfd_update_list[mfd_num_updates - 1];
1475             mfd_num_updates--;
1476             i = 0; // We might now intersect a previous one.
1477         }
1478     if (mfd_num_updates >= NUM_MFD_RECTS)
1479         return ERR_DOVERFLOW;
1480     mfd_update_list[mfd_num_updates++] = r;
1481     return OK;
1482 }
1483 
1484 // -------------------------------------------------------------------
1485 //
1486 // mfd_update_rects() Updates all rects in the update list, then clears
1487 // the update list.
1488 
mfd_update_rects(MFD * m)1489 void mfd_update_rects(MFD *m) {
1490     int i;
1491     for (i = 0; i < mfd_num_updates; i++) {
1492         LGRect *r = &mfd_update_list[i];
1493         // Filter out degenerate rects!
1494         if ((r->lr.x <= r->ul.x) || (r->lr.y <= r->ul.y))
1495             continue;
1496         mfd_update_display(m, r->ul.x, r->ul.y, r->lr.x, r->lr.y);
1497     }
1498     mfd_num_updates = 0;
1499 }
1500 
1501 // --------------------------------------------------------------------------
1502 // mfd_update_display()
1503 //
1504 // Updates a portion of the view window from canvas.
1505 
mfd_update_display(MFD * m,short x0,short y0,short x1,short y1)1506 void mfd_update_display(MFD *m, short x0, short y0, short x1, short y1) {
1507     ushort a, b, c, d;
1508     uchar old_over = gr2ss_override;
1509 
1510     if (!full_game_3d) {
1511         LGRect r;
1512 
1513         if ((x0 > x1) || (y0 > y1))
1514             return;
1515 
1516         r.ul.x = x0;
1517         r.ul.y = y0;
1518         r.lr.x = x1;
1519         r.lr.y = y1;
1520 
1521         RECT_OFFSETTED_RECT(&r, m->rect.ul, &r);
1522         if (!RECT_TEST_SECT(&r, &m->rect))
1523             return;
1524         RectSect(&r, &m->rect, &r);
1525 
1526         gr_push_canvas(grd_screen_canvas);
1527         uiHideMouse(&r);
1528         STORE_CLIP(a, b, c, d);
1529         gr2ss_override = OVERRIDE_ALL;
1530         ss_safe_set_cliprect(r.ul.x, r.ul.y, r.lr.x, r.lr.y);
1531         ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, m->rect.ul.y);
1532 
1533         gr2ss_override = old_over;
1534         uiShowMouse(&r);
1535         RESTORE_CLIP(a, b, c, d);
1536         gr_pop_canvas();
1537     }
1538 
1539     return;
1540 }
1541 
1542 // ***********************************************
1543 // **** SAVE/RESTORE and DEFAULT MFD MANAGER *****
1544 // ***********************************************
1545 
1546 // for saving and restoring mfd settings around "panel" mfd's, we
1547 // use our secret 6th slot.
1548 
1549 typedef uchar (*mfd_def_qual)(void);
1550 
1551 extern uchar mfd_target_qual(void);
1552 extern uchar mfd_automap_qual(void);
1553 extern uchar mfd_weapon_qual(void);
1554 
1555 typedef struct {
1556     uchar func;
1557     uchar slot;
1558     mfd_def_qual qual;
1559 } mfd_default;
1560 
1561 mfd_default default_mfds[] = {{MFD_MAP_FUNC, MFD_MAP_SLOT, &mfd_automap_qual},
1562                               {MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, &mfd_weapon_qual},
1563                               {MFD_TARGET_FUNC, MFD_TARGET_SLOT, &mfd_target_qual}};
1564 
1565 #define NUM_MFD_DEFAULTS (sizeof(default_mfds) / sizeof(mfd_default))
1566 
mfd_default_mru(uchar func)1567 void mfd_default_mru(uchar func) {
1568     int i, pos = -1;
1569     mfd_default tmp;
1570 
1571     for (i = 0; i < NUM_MFD_DEFAULTS; i++) {
1572         if (default_mfds[i].func == func) {
1573             pos = i;
1574             tmp = default_mfds[i];
1575             break;
1576         }
1577     }
1578     if (pos < 0)
1579         return; // func not in default list
1580 
1581     for (i = pos; i > 0; i--)
1582         default_mfds[i] = default_mfds[i - 1];
1583     default_mfds[0] = tmp;
1584 }
1585 
save_mfd_slot(int mfd_id)1586 void save_mfd_slot(int mfd_id) {
1587     uchar func, mask;
1588     int slot;
1589 
1590     if (global_fullmap->cyber)
1591         return;
1592     if (player_struct.mfd_save_slot[mfd_id] < 0) {
1593         slot = player_struct.mfd_current_slots[mfd_id];
1594 
1595         func = mfd_get_func(mfd_id, slot);
1596         if (!(mfd_funcs[func].flags & MFD_NOSAVEREST)) {
1597             player_struct.mfd_save_slot[mfd_id] = slot;
1598             set_slot_to_func(MFD_SPECIAL_SLOT + mfd_id, func, MFD_ACTIVE);
1599             if (full_game_3d) {
1600                 mask = visible_mask(mfd_id);
1601                 player_struct.mfd_save_vis &= ~mask;
1602                 player_struct.mfd_save_vis |= (mask & full_visible);
1603             }
1604         }
1605     }
1606 }
1607 
1608 // sets mfd to given slot and func, unless the func passed in is MFD_EMPTY_FUNC
1609 // or some MFD already has that func.  In that case, set to some other slot
1610 // from a list of hopefully useful defaults.
1611 // note that if you pass in MFD_EMPTY_FUNC, a new setting is always selected,
1612 // so the slot argument is ignored.
1613 
set_mfd_from_defaults(int mfd_id,uchar func,uchar slot)1614 void set_mfd_from_defaults(int mfd_id, uchar func, uchar slot) {
1615     uchar def, mid;
1616     uchar check;
1617 
1618     def = 0;
1619     do {
1620         check = FALSE;
1621         for (mid = 0; mid < NUM_MFDS; mid++) {
1622             if (func == MFD_EMPTY_FUNC || func == mfd_get_func(mid, player_struct.mfd_current_slots[mid])) {
1623                 // don't restore func that we already have on some mfd.
1624                 if (default_mfds[def].qual()) {
1625                     func = default_mfds[def].func;
1626                     slot = default_mfds[def].slot;
1627                 }
1628                 def++;
1629                 check = TRUE;
1630                 break;
1631             }
1632         }
1633     } while (check && def < NUM_MFD_DEFAULTS);
1634 
1635     if (func == MFD_EMPTY_FUNC)
1636         slot = (global_fullmap->cyber) ? MFD_INFO_SLOT : MFD_ITEM_SLOT; // failure case
1637     mfd_notify_func(func, slot, TRUE, MFD_ACTIVE, TRUE);
1638     if (!full_game_3d || (full_visible & FULL_MFD_MASK(mfd_id)))
1639         mfd_change_slot(mfd_id, slot);
1640 }
1641 
1642 // scans throught the mfd's looking for mfd's that are set to the given slot/func.
1643 // once it have found max such mfd's, starts setting any subsequent mfd's
1644 // with that func to defaults as above.
1645 
cap_mfds_with_func(uchar func,uchar max)1646 void cap_mfds_with_func(uchar func, uchar max) {
1647     int mid;
1648 
1649     for (mid = 0; mid < NUM_MFDS; mid++) {
1650         if (mfd_get_func(mid, player_struct.mfd_current_slots[mid]) == func) {
1651             if (max == 0)
1652                 restore_mfd_slot(mid);
1653             else
1654                 max--;
1655         }
1656     }
1657 }
1658 
restore_mfd_slot(int mfd_id)1659 void restore_mfd_slot(int mfd_id) {
1660     uchar func, slot;
1661     if (global_fullmap->cyber)
1662         return;
1663     if (full_game_3d && !(visible_mask(mfd_id) & full_visible))
1664         return;
1665     if (player_struct.mfd_save_slot[mfd_id] < 0) {
1666         func = MFD_EMPTY_FUNC;
1667         slot = MFD_INFO_SLOT;
1668     } else {
1669         func = player_struct.mfd_all_slots[MFD_SPECIAL_SLOT + mfd_id];
1670         slot = player_struct.mfd_save_slot[mfd_id];
1671     }
1672 
1673     set_mfd_from_defaults(mfd_id, func, slot);
1674     player_struct.mfd_save_slot[mfd_id] = -1;
1675     full_visible &= ~(visible_mask(mfd_id));
1676 #ifdef STEREO_SUPPORT
1677     if (convert_use_mode == 5)
1678         full_visible = (player_struct.mfd_save_vis & visible_mask(mfd_id));
1679     else
1680 #endif
1681     {
1682         full_visible |= (player_struct.mfd_save_vis & visible_mask(mfd_id));
1683     }
1684 }
1685