1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/sideicon.c $
21  * $Revision: 1.53 $
22  * $Author: mahk $
23  * $Date: 1994/11/23 04:31:51 $
24  *
25  */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "input.h"
31 #include "sideicon.h"
32 #include "sideart.h"
33 #include "popups.h"
34 #include "gamestrn.h"
35 #include "cybstrng.h"
36 #include "tools.h"
37 #include "mainloop.h"
38 #include "game_screen.h"
39 #include "fullscrn.h"
40 #include "wares.h"
41 #include "objsim.h"
42 #include "objclass.h"
43 #include "otrip.h"
44 #include "player.h"
45 #include "faketime.h"
46 #include "mfdext.h"
47 #include "gameloop.h"
48 #include "textmaps.h"
49 #include "criterr.h"
50 #include "objapp.h"
51 #include "objwarez.h"
52 #include "hkeyfunc.h"
53 #include "musicai.h"
54 #include "sfxlist.h"
55 #include "gr2ss.h"
56 #include "canvchek.h"
57 
58 // ---------
59 // Constants
60 // ---------
61 
62 #define NUM_SIDE_ICONS      10
63 
64 #define SIDE_ICONS_TOP_Y    24
65 #define SIDE_ICONS_LEFT_X   4
66 #define SIDE_ICONS_RIGHT_X  300
67 #define SIDE_ICONS_HEIGHT   15
68 #define SIDE_ICONS_WIDTH    15
69 #define SIDE_ICONS_VSPACE   8
70 
71 #define ICON_ART_ITEMS      2
72 #define ICON_ART_OFF        0
73 #define ICON_ART_ON         1
74 #define ICON_ART_BACKGROUND 255
75 
76 #define FLASH_RATE          256
77 #define MAX_FLASH_COUNT     12
78 
79 // ----------------
80 // Local Prototypes
81 // ----------------
82 
83 void side_icon_language_change(void);
84 uchar side_icon_mouse_callback(uiEvent *e, LGRegion *r, intptr_t udata);
85 void zoom_side_icon_to_mfd(int icon, int waretype, int wnum);
86 void zoom_to_side_icon(LGPoint from, int icon);
87 uchar side_icon_hotkey_func(ushort keycode, uint32_t context, intptr_t i);
88 void side_icon_draw_bm(LGRect *r, ubyte icon, ubyte art);
89 
90 // ----------
91 // Structures
92 // ----------
93 
94 typedef struct _side_icon {
95     LGRect r;
96     uchar flashstate;
97     ubyte flashcount;
98     ubyte state;
99 } SIDE_ICON;
100 
101 typedef struct _icon_data {
102     byte waretype;
103     long waretrip;
104     int flashfx;
105 } ICON_DATA;
106 
107 // -------
108 // Globals
109 // -------
110 
111 SIDE_ICON side_icons[NUM_SIDE_ICONS];
112 #ifdef PRELOAD_BITMAPS
113 grs_bitmap side_icon_bms[NUM_SIDE_ICONS][ICON_ART_ITEMS];
114 grs_bitmap side_icon_background;
115 #else
116 #define side_icon_bmid(icon, art) (MKREF(RES_SideIconArt, (ICON_ART_ITEMS * icon) + art + 1))
117 #define side_icon_backid (MKREF(RES_SideIconArt, 0))
118 #endif
119 
120 #ifdef PROGRAM_SIDEICON
121 static char shiftnums[] = ")!@#$%^&*(";
122 static uchar programmed_sideicon = 0;
123 #endif
124 
125 // this is in wares.c
126 extern long ware_base_triples[NUM_WARE_TYPES];
127 
128 #define IDX_OF_TYPE(type, trip) (OPTRIP(trip) - OPTRIP(ware_base_triples[type]))
129 
130 static ICON_DATA icon_data[NUM_SIDE_ICONS] = {
131 
132     {WARE_HARD, BIOSCAN_HARD_TRIPLE}, {WARE_HARD, FULLSCR_HARD_TRIPLE}, {WARE_HARD, SENS_HARD_TRIPLE},
133     {WARE_HARD, LANTERN_HARD_TRIPLE}, {WARE_HARD, SHIELD_HARD_TRIPLE},
134 
135     {WARE_HARD, INFRA_GOG_TRIPLE},    {WARE_HARD, NAV_HARD_TRIPLE},     {WARE_HARD, VIDTEX_HARD_TRIPLE, SFX_EMAIL},
136     {WARE_HARD, MOTION_HARD_TRIPLE},  {WARE_HARD, JET_HARD_TRIPLE},
137 };
138 
139 grs_bitmap icon_cursor_bm[2];
140 LGCursor icon_cursor[2];
141 static char *cursor_strings[NUM_SIDE_ICONS];
142 static char cursor_strbuf[128];
143 
144 // ============
145 // INITIALIZERS
146 // ============
147 
148 // ---------------------------------------------------------------------------
149 // init_all_side_icons()
150 //
151 // Initialize all side icons to "unset" settings (should be called before
152 // wares_init()!).  Also sets up their on-screen locations.
153 // And, as of 7/22, loads in all the bitmaps from memory.
154 
side_icon_language_change(void)155 void side_icon_language_change(void) {
156     load_string_array(REF_STR_IconCursor, cursor_strings, cursor_strbuf, sizeof(cursor_strbuf), NUM_SIDE_ICONS);
157 }
158 
init_all_side_icons()159 void init_all_side_icons() {
160     int i;
161 
162     // Now, figure out on-screen locations
163 
164     for (i = 0; i < (NUM_SIDE_ICONS / 2); i++) // left side first
165     {
166         side_icons[i].r.ul.x = SIDE_ICONS_LEFT_X;
167         side_icons[i].r.ul.y = SIDE_ICONS_TOP_Y + (i * (SIDE_ICONS_HEIGHT + SIDE_ICONS_VSPACE));
168 
169         side_icons[i].r.lr.x = side_icons[i].r.ul.x + SIDE_ICONS_WIDTH;
170         side_icons[i].r.lr.y = side_icons[i].r.ul.y + SIDE_ICONS_HEIGHT;
171     }
172 
173     for (i = (NUM_SIDE_ICONS / 2); i < NUM_SIDE_ICONS; i++) // now right side
174     {
175         side_icons[i].r.ul.x = SIDE_ICONS_RIGHT_X;
176         side_icons[i].r.ul.y = side_icons[i - (NUM_SIDE_ICONS / 2)].r.ul.y;
177 
178         side_icons[i].r.lr.x = side_icons[i].r.ul.x + SIDE_ICONS_WIDTH;
179         side_icons[i].r.lr.y = side_icons[i].r.ul.y + SIDE_ICONS_HEIGHT;
180     }
181 }
182 
init_side_icon_popups(void)183 void init_side_icon_popups(void) {
184     side_icon_language_change();
185     for (int i = 0; i < 2; i++) {
186         LGPoint offset = {0, -1};
187         LGCursor *c = &icon_cursor[i];
188         grs_bitmap *bm = &icon_cursor_bm[i];
189         make_popup_cursor(c, bm, cursor_strings[i * NUM_SIDE_ICONS / 2], i + POPUP_ICON_LEFT, TRUE, offset);
190     }
191 }
192 
193 #ifdef DUMMY // not yet, bucko
194 
init_side_icon_hotkeys(void)195 void init_side_icon_hotkeys(void) {
196     uchar side_icon_hotkey_func(ushort key, uint32_t context, intptr_t i);
197     uchar side_icon_progset_hotkey_func(ushort key, uint32_t context, intptr_t i);
198     uchar lantern_change_setting_hkey(ushort key, uint32_t context, intptr_t i);
199     uchar shield_change_setting_hkey(ushort key, uint32_t context, intptr_t i);
200     uchar side_icon_prog_hotkey_func(ushort key, uint32_t context, intptr_t notused);
201     int i;
202 
203     hotkey_add(KB_FLAG_ALT | KB_FLAG_DOWN | '4', DEMO_CONTEXT, lantern_change_setting_hkey, 0);
204     hotkey_add(KB_FLAG_ALT | KB_FLAG_DOWN | '5', DEMO_CONTEXT, shield_change_setting_hkey, 0);
205 
206     hotkey_add(KB_FLAG_DOWN | '0', DEMO_CONTEXT, side_icon_hotkey_func, NUM_SIDE_ICONS - 1);
207 #ifdef PROGRAM_SIDEICON
208     hotkey_add('`', DEMO_CONTEXT, ide_icon_prog_hotkey_func, 0);
209     hotkey_add(KB_FLAG_DOWN | shiftnums[0], DEMO_CONTEXT, side_icon_progset_hotkey_func,
210                NUM_SIDE_ICONS - 1);
211 #endif
212     for (i = 0; i < NUM_SIDE_ICONS - 1; i++) {
213         hotkey_add(KB_FLAG_DOWN | ('1' + i), DEMO_CONTEXT, side_icon_hotkey_func, i);
214 #ifdef PROGRAM_SIDEICON
215         hotkey_add(KB_FLAG_DOWN | shiftnums[1 + i], DEMO_CONTEXT, side_icon_progset_hotkey_func,
216                    i);
217 #endif
218     }
219 }
220 
221 #endif // DUMMY
222 
223 // ---------------------------------------------------------------------------
224 // init_side_icon()
225 //
226 
227 // ---------------------------------------------------------------------------
228 // screen_init_side_icons();
229 //
230 // Declare the appropriate regions for the side icons
231 // (called from screen_start() in screen.c)
232 
screen_init_side_icons(LGRegion * root)233 void screen_init_side_icons(LGRegion *root) {
234     int id;
235     LGRegion *left_region, *right_region;
236     LGRect r;
237     left_region = (LGRegion *)malloc(sizeof(LGRegion));
238     right_region = (LGRegion *)malloc(sizeof(LGRegion));
239 
240     // Wow, having a LGRegion for each of the side icons is totally uncool
241     // Let's just have two regions, and figure out from there.
242 
243     r.ul = side_icons[0].r.ul;
244     r.lr = side_icons[(NUM_SIDE_ICONS - 1) / 2].r.lr;
245     macro_region_create_with_autodestroy(root, left_region, &r);
246     uiInstallRegionHandler(left_region, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, &side_icon_mouse_callback, 0,
247                            &id);
248     uiSetRegionDefaultCursor(left_region, NULL);
249 
250     r.ul = side_icons[(NUM_SIDE_ICONS + 1) / 2].r.ul;
251     r.lr = side_icons[(NUM_SIDE_ICONS - 1)].r.lr;
252     macro_region_create_with_autodestroy(root, right_region, &r);
253     uiInstallRegionHandler(right_region, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, &side_icon_mouse_callback,
254                            ((NUM_SIDE_ICONS + 1) / 2), &id);
255     uiSetRegionDefaultCursor(right_region, NULL);
256 
257     return;
258 }
259 
260 // =========
261 // SELECTION
262 // =========
263 
264 // ----------------------------------------------------------------
265 // select_side_icon() selects a given side icon.
266 
zoom_side_icon_to_mfd(int icon,int waretype,int wnum)267 void zoom_side_icon_to_mfd(int icon, int waretype, int wnum) {
268     extern ubyte waretype2invtype[];
269     extern void mfd_zoom_rect(LGRect * start, int mfdnum);
270 
271     int mfd;
272 
273     mfd = mfd_grab_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT);
274     mfd_zoom_rect(&side_icons[icon].r, mfd);
275     set_inventory_mfd(waretype2invtype[waretype], wnum, TRUE);
276     mfd_change_slot(mfd, MFD_ITEM_SLOT);
277 }
278 
279 // ---------------------------------------------------------------------------
280 // side_icon_mouse_callback()
281 //
282 // Callback function for mouse clicks inside the side icons.
283 
284 extern LGCursor globcursor;
285 
286 int last_side_icon = -1;
side_icon_mouse_callback(uiEvent * e,LGRegion * r,intptr_t udata)287 uchar side_icon_mouse_callback(uiEvent *e, LGRegion *r, intptr_t udata) {
288     extern uchar fullscrn_icons;
289     uchar retval = FALSE;
290     int i, type, num;
291 
292     if (!global_fullmap->cyber && !(full_game_3d && !fullscrn_icons)) {
293         int ver;
294 
295         i = (int)udata + (e->pos.y - SIDE_ICONS_TOP_Y) / (SIDE_ICONS_HEIGHT + SIDE_ICONS_VSPACE);
296         type = icon_data[i].waretype;
297         num = IDX_OF_TYPE(type, icon_data[i].waretrip);
298         ver = get_player_ware_version(type, num);
299 
300         if (!RECT_TEST_PT(&side_icons[i].r, e->pos) || ver == 0) {
301             uiSetRegionDefaultCursor(r, &globcursor);
302             last_side_icon = -1;
303             return FALSE;
304         }
305         if (popup_cursors) {
306             if (last_side_icon != i) {
307                 uchar side = i * 2 / NUM_SIDE_ICONS;
308                 LGCursor *c = &icon_cursor[side];
309                 grs_bitmap *bm = &icon_cursor_bm[side];
310                 LGPoint offset = {0, -1};
311 
312                 free(bm->bits);
313                 make_popup_cursor(c, bm, cursor_strings[i], side + POPUP_ICON_LEFT, TRUE, offset);
314                 uiSetRegionDefaultCursor(r, c);
315                 last_side_icon = i;
316             }
317         }
318         /*
319               if (m->action & MOUSE_RDOWN)
320               {
321                  zoom_side_icon_to_mfd(i,type,num);
322                  retval = TRUE;
323               }
324         */
325 
326         if (!(e->mouse_data.action & MOUSE_LDOWN))
327             return retval; // ignore click releases
328                            //   mprintf("  Side Icon %d: CYBER(%d,%d) [%x] REAL(%d,%d) [%x]\n",
329                            //      i, side_icons[i].cyber_type, side_icons[i].cyber_num,
330                            //      side_icons[i].cyber_set, side_icons[i].real_type,
331                            //      side_icons[i].real_num, side_icons[i].real_set);
332 
333         if (type >= 0)
334             use_ware(type, num);
335         retval = TRUE;
336     }
337     if (global_fullmap->cyber || (full_game_3d && !fullscrn_icons) || !popup_cursors) {
338         last_side_icon = -1;
339         uiSetRegionDefaultCursor(r, NULL);
340     }
341 
342     return retval;
343 }
344 
side_icon_hotkey_func(ushort keycode,uint32_t context,intptr_t i)345 uchar side_icon_hotkey_func(ushort keycode, uint32_t context, intptr_t i) {
346     int type = icon_data[i].waretype;
347     int num = IDX_OF_TYPE(type, icon_data[i].waretrip);
348     if ((!global_fullmap->cyber) || (i == 1)) {
349         if (type >= 0)
350             use_ware(type, num);
351     }
352     return TRUE;
353 }
354 
355 #ifdef PROGRAM_SIDEICON
side_icon_progset_hotkey_func(ushort keycode,uint32_t context,intptr_t i)356 uchar side_icon_progset_hotkey_func(ushort keycode, uint32_t context, intptr_t i) {
357     char mess[80];
358     int l;
359     programmed_sideicon = i;
360     get_string(REF_STR_PresetSideicon, mess, 80);
361     l = strlen(mess);
362     get_object_short_name(icon_data[i].waretrip, mess + l, 80 - l);
363     message_info(mess);
364     return TRUE;
365 }
366 
side_icon_prog_hotkey_func(ushort keycode,uint32_t context,intptr_t notused)367 uchar side_icon_prog_hotkey_func(ushort keycode, uint32_t context, intptr_t notused) {
368     return (side_icon_hotkey_func(keycode, context, programmed_sideicon));
369 }
370 #endif
371 
372 // ========
373 // GRAPHICS
374 // ========
375 
376 // ---------------------------------------------------------------------------
377 // side_icon_expose_all()
378 //
379 // Sort of an initial-draw-everything type of routine
380 
side_icon_expose_all()381 void side_icon_expose_all() {
382     ubyte i;
383 
384     for (i = 0; i < NUM_SIDE_ICONS; i++)
385         side_icon_expose(i);
386 
387     return;
388 }
389 
390 // ----------------------------------------------------
391 // zoom_to_side_icon(Point from, int icon)
392 // zooms a LGRect to a side icon and then exposes it.
393 
zoom_to_side_icon(LGPoint from,int icon)394 void zoom_to_side_icon(LGPoint from, int icon) {
395     extern void zoom_rect(LGRect * s, LGRect * f);
396     LGRect start = {{-3, -3}, {3, 3}};
397     LGRect dest;
398     RECT_MOVE(&start, from);
399     dest = side_icons[icon].r;
400     zoom_rect(&start, &dest);
401     side_icon_expose(icon);
402 }
403 
404 // ---------------------------------------------------------------------------
405 // side_icon_draw_bm()
406 //
407 // Draws a side icon of the specified ware, version, and status.
408 
side_icon_draw_bm(LGRect * r,ubyte icon,ubyte art)409 void side_icon_draw_bm(LGRect *r, ubyte icon, ubyte art) {
410 #ifdef SVGA_SUPPORT
411     uchar old_over = gr2ss_override;
412     gr2ss_override = OVERRIDE_ALL;
413 #endif
414     if (is_onscreen())
415         uiHideMouse(r);
416     if (art == ICON_ART_BACKGROUND)
417         draw_raw_resource_bm(side_icon_backid, r->ul.x, r->ul.y);
418     // draw_hires_resource_bm(side_icon_backid, SCONV_X(r->ul.x), SCONV_Y(r->ul.y));
419     else
420         draw_raw_resource_bm(side_icon_bmid(icon, art), r->ul.x, r->ul.y);
421     // draw_hires_resource_bm(side_icon_bmid(icon,art), SCONV_X(r->ul.x), SCONV_Y(r->ul.y));
422     if (is_onscreen())
423         uiShowMouse(r);
424 #ifdef SVGA_SUPPORT
425     gr2ss_override = old_over;
426 #endif
427     return;
428 }
429 
430 // ---------------------------------------------------------------------------
431 // side_icon_expose()
432 //
433 // Draw a side icon appropriately, depending on the ware it points at,
434 // and its state.
435 
side_icon_expose(ubyte icon_num)436 void side_icon_expose(ubyte icon_num) {
437     ubyte *player_wares, *player_status;
438     WARE *wares;
439     int type, num, n;
440     LGRect *r;
441     extern uchar fullscrn_icons;
442 
443     if (full_game_3d && (global_fullmap->cyber || !(fullscrn_icons)))
444         return;
445     r = &(side_icons[icon_num].r);
446 
447     type = icon_data[icon_num].waretype;
448     num = IDX_OF_TYPE(type, icon_data[icon_num].waretrip);
449 
450     if (type < 0)
451         return;
452     get_ware_pointers(type, &player_wares, &player_status, &wares, &n);
453 
454     // Possible expose cases:
455     //
456     // 1) We don't have the appropriate ware for that side icon.
457     if (player_wares[num] == 0) {
458 
459         // Expose screen background
460         //      Spew(DSRC_TESTING_Test9,("icon number %d is empty\n",icon_num));
461         if (!full_game_3d)
462             side_icon_draw_bm(r, icon_num, ICON_ART_BACKGROUND);
463     }
464 
465     // 3) We have the ware, and it's trying to get our attention.
466     //
467     else if (player_status[num] & WARE_FLASH) {
468         uchar fs = ((*tmd_ticks / FLASH_RATE) % 2);
469         if (fs == side_icons[icon_num].flashstate && !full_game_3d)
470             return;
471 
472         if (fs) {
473             side_icon_draw_bm(r, icon_num, ICON_ART_ON);
474             if (fs != side_icons[icon_num].flashstate)
475                 if (icon_data[icon_num].flashfx != 0)
476                     if (QUESTBIT_GET(0x12c))
477                         play_digi_fx(icon_data[icon_num].flashfx, 1);
478         } else
479             side_icon_draw_bm(r, icon_num, ICON_ART_OFF);
480         if (fs != side_icons[icon_num].flashstate) {
481             side_icons[icon_num].flashcount++;
482             if (side_icons[icon_num].flashcount >= MAX_FLASH_COUNT) {
483                 side_icons[icon_num].flashcount = 0;
484                 player_status[num] &= ~WARE_FLASH;
485             }
486         }
487         side_icons[icon_num].flashstate = fs;
488     }
489 
490     // 2) We have the ware, but we're turning it off.
491     //
492     else if (!(player_status[num] & WARE_ON)) {
493 
494         // Expose darkened bitmap of current version
495         //      Spew(DSRC_TESTING_Test9,("icon number %d is off\n",icon_num));
496         side_icon_draw_bm(r, icon_num, ICON_ART_OFF);
497     } else {
498 
499         // Expose normal (active) bitmap of current ware version
500         side_icon_draw_bm(r, icon_num, ICON_ART_ON);
501     }
502 
503     return;
504 }
505 
506 // ---------------------------------------------------------------------------
507 // side_icon_load_bitmaps()
508 //
509 // Load the bitmaps for all side icons and states from the resource system.
510 
side_icon_load_bitmaps()511 errtype side_icon_load_bitmaps() {
512 #ifdef PRELOAD_BITMAPS
513     RefTable *side_icon_rft;
514     int i, j, index /*, file_handle */;
515 
516     //  file_handle = ResOpenFile("sideart.res");
517     //   if (file_handle < 0) critical_error(CRITERR_RES|6);
518 
519     side_icon_rft = ResLock(RES_SideIconArt);
520     load_bitmap_from_res(&side_icon_background, RES_SideIconArt, 0, side_icon_rft, FALSE, NULL, NULL);
521 
522     for (i = 0; i < NUM_SIDE_ICONS; i++) {
523 
524         for (j = 0; j < ICON_ART_ITEMS; j++) {
525 
526             index = (ICON_ART_ITEMS * i) + j + 1;
527             load_bitmap_from_res(&(side_icon_bms[i][j]), RES_SideIconArt, index, side_icon_rft, FALSE, NULL, NULL);
528         }
529     }
530     ResUnlock(RES_SideIconArt);
531 //   ResCloseFile(file_handle);
532 #endif
533 
534     return (OK);
535 }
536 
side_icon_free_bitmaps()537 errtype side_icon_free_bitmaps() {
538 #ifdef PRELOAD_BITMAPS
539     int i, j, index;
540     Free(side_icon_background.bits);
541     for (i = 0; i < NUM_SIDE_ICONS; i++) {
542 
543         for (j = 0; j < ICON_ART_ITEMS; j++) {
544 
545             index = (ICON_ART_ITEMS * i) + j;
546             Free(side_icon_bms[i][j].bits);
547         }
548     }
549 #endif
550     return (OK);
551 }
552