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