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/olh.c $
21  * $Revision: 1.27 $
22  * $Author: dc $
23  * $Date: 1994/11/21 09:02:38 $
24  *
25  */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "Prefs.h"
31 
32 #include "player.h"
33 #include "gamestrn.h"
34 #include "objapp.h"
35 #include "objects.h"
36 #include "objprop.h"
37 #include "gameobj.h"
38 #include "hudobj.h"
39 #include "hud.h"
40 #include "mainloop.h"
41 #include "gamescr.h"
42 #include "olhint.h"
43 #include "faketime.h"
44 #include "objbit.h"
45 #include "objuse.h"
46 #include "doorparm.h"
47 #include "render.h"
48 #include "strwrap.h"
49 #include "tools.h"
50 #include "game_screen.h"
51 #include "fullscrn.h"
52 #include "input.h"
53 #include "grenades.h"
54 #include "mfdext.h"
55 #include "olhext.h"
56 #include "cit2d.h"
57 #include "gr2ss.h"
58 #include "hkeyfunc.h"
59 #include "status.h"
60 
61 #include "otrip.h"
62 #include "cybstrng.h"
63 
64 extern void SDLDraw(void);
65 
66 // -------------------------------------------------
67 //          ON-LINE HELP FOR SYSTEM SHOCK
68 // -------------------------------------------------
69 
70 uchar olh_active = TRUE;
71 uchar olh_overlay_on = FALSE;
72 olh_data olh_object = {OBJ_NULL, {0, 0}};
73 
74 // ---------
75 // INTERNALS
76 // ---------
77 
78 char *get_olh_string(ObjID obj, char *buf);
79 LGPoint draw_olh_string(char *s, short xl, short yl);
80 void olh_do_panel_ref(short xl, short yl);
81 void olh_do_callout(short xl, short yl);
82 uchar is_compound_use_obj(ObjID obj);
83 void olh_do_cursor(short xl, short yl);
84 
85 #define IS_SCREEN(obj)                                                                                       \
86     (objs[obj].obclass == CLASS_BIGSTUFF && objs[obj].subclass == BIGSTUFF_SUBCLASS_ONTHEWALL &&             \
87      (objs[obj].info.type == TRIP2TY(SCREEN_TRIPLE) || objs[obj].info.type == TRIP2TY(SUPERSCREEN_TRIPLE) || \
88       objs[obj].info.type == TRIP2TY(BIGSCREEN_TRIPLE)))
89 
olh_candidate(ObjID obj)90 uchar olh_candidate(ObjID obj) {
91     uchar check_dist = FALSE;
92     uchar retval = FALSE;
93 
94     if (objs[obj].info.inst_flags & OLH_INST_FLAG)
95         return FALSE;
96     switch (ID2TRIP(obj)) {
97     case CAMERA_TRIPLE:
98     case LARGCPU_TRIPLE:
99         check_dist = FALSE;
100         retval = TRUE;
101         break;
102     default:
103         if (USE_MODE(obj) == NULL_USE_MODE)
104             return FALSE;
105         break;
106     }
107     switch (objs[obj].obclass) {
108     case CLASS_DOOR:
109         if ((ID2TRIP(obj) == LABFORCE_TRIPLE) || (ID2TRIP(obj) == RESFORCE_TRIPLE))
110             return FALSE;
111         check_dist = !door_locked(obj) && !door_moving(obj, FALSE) && DOOR_REALLY_CLOSED(obj);
112         break;
113     case CLASS_BIGSTUFF:
114         if (IS_SCREEN(obj)) {
115             extern char camera_map[NUM_HACK_CAMERAS];
116             extern ObjID hack_cam_objs[NUM_HACK_CAMERAS];
117             ObjSpecID sid = objs[obj].specID;
118             short v = objBigstuffs[sid].data2 & 0x7F;
119             if ((v >= FIRST_CAMERA_TMAP) && (v < FIRST_CAMERA_TMAP + NUM_HACK_CAMERAS)) {
120                 if (camera_map[v - FIRST_CAMERA_TMAP] && hack_cam_objs[v - FIRST_CAMERA_TMAP])
121                     check_dist = TRUE;
122             }
123             break;
124         }
125         if (ID2TRIP(obj) == SURG_MACH_TRIPLE) {
126             check_dist = TRUE;
127             break;
128         }
129         if (((ObjProps[OPNUM(obj)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) == STUFF_OBJUSE_FLAG) {
130             if (objBigstuffs[objs[obj].specID].data1 != 0)
131                 check_dist = TRUE;
132             break;
133         }
134     case CLASS_SMALLSTUFF:
135         if (USE_MODE(obj) == USE_USE_MODE) {
136             check_dist = TRUE;
137             break;
138         }
139         if (USE_MODE(obj) == PICKUP_USE_MODE) {
140             if (ObjProps[OPNUM(obj)].flags & INVENTORY_GENERAL)
141                 check_dist = TRUE;
142             break;
143         }
144         // smallstuff falls through to default.
145     default:
146         if (USE_MODE(obj) == PICKUP_USE_MODE || USE_MODE(obj) == USE_USE_MODE)
147             check_dist = TRUE;
148         break;
149     }
150 
151     if (check_dist) {
152         int mode = USE_MODE(obj);
153         fix crit = (mode == PICKUP_USE_MODE) ? MAX_PICKUP_DIST : MAX_USE_DIST;
154 
155         if (check_object_dist(obj, PLAYER_OBJ, crit))
156             retval = TRUE;
157     }
158     return retval;
159 }
160 
161 short use_mode_idx[] = {
162     REFINDEX(REF_STR_helpTake),
163     REFINDEX(REF_STR_helpUse),
164     -1,
165     -1,
166 };
167 
168 short weap_subclass_idx[] = {
169     REFINDEX(REF_STR_helpAttackGun), REFINDEX(REF_STR_helpAttackAuto), REFINDEX(REF_STR_helpAttackGun),
170     REFINDEX(REF_STR_helpAttackHTH), REFINDEX(REF_STR_helpAttackGun),  REFINDEX(REF_STR_helpAttackGun),
171 };
172 
173 extern uchar is_container(ObjID id, int **d1, int **d2);
174 
175 // basically we chose a string id for a string
176 // that has  %s, and lg_strintf the name into the
177 // the string.
get_olh_string(ObjID obj,char * buf)178 char *get_olh_string(ObjID obj, char *buf) {
179 
180     int *d1, *d2;
181     int obclass = objs[obj].obclass;
182     Ref r = 0;
183     short mode;
184 
185     switch (obclass) {
186     case CLASS_CRITTER: {
187         int w = player_struct.actives[ACTIVE_WEAPON];
188         int type = player_struct.weapons[w].type;
189         if (type == EMPTY_WEAPON_SLOT)
190             return strcpy(buf, get_object_long_name(ID2TRIP(obj), NULL, 0));
191         r = MKREF(RES_olh_strings, weap_subclass_idx[type]);
192         goto got_id;
193     }
194     case CLASS_DOOR:
195         r = REF_STR_helpDoor;
196         goto got_id;
197     default:
198         break;
199     }
200     switch (ID2TRIP(obj)) {
201     case CAMERA_TRIPLE:
202     case LARGCPU_TRIPLE:
203         r = REF_STR_helpSecurity;
204         goto got_id;
205     }
206     mode = USE_MODE(obj);
207     if (is_container(obj, &d1, &d2) && mode == USE_USE_MODE) {
208         r = REF_STR_helpSearch;
209         goto got_id;
210     }
211     if (use_mode_idx[mode] != -1) {
212         r = MKREF(RES_olh_strings, use_mode_idx[mode]);
213         goto got_id;
214     }
215     // more cases go here...
216 
217 got_id:
218     if (r != 0) {
219         char *s = (char *)RefLock(r);
220         sprintf(buf, s, get_object_long_name(ID2TRIP(obj), NULL, 0));
221         RefUnlock(r);
222     }
223     return buf;
224 }
225 
226 // ---------
227 // EXTERNALS
228 // ---------
229 extern bool DoubleSize;
230 
231 //-----------------------------
232 // olh_scan_objects()
233 //
234 // This gets called to detect objects in front of the player that have
235 // help strings.  It sets up for olh_do_hudobjs.
236 
237 extern void olh_scan_objs(void);
238 
239 #define SCAN_FREQ_SHF (APPROX_CIT_CYCLE_SHFT - 2)
240 
olh_scan_objects(void)241 void olh_scan_objects(void) {
242     static uint last_scan = 0;
243 
244     if (*tmd_ticks >> SCAN_FREQ_SHF <= last_scan)
245         return;
246     if (input_cursor_mode == INPUT_OBJECT_CURSOR)
247         return;
248     if (player_struct.panel_ref != OBJ_NULL)
249         return;
250     olh_scan_objs();
251     if (olh_object.obj == OBJ_NULL)
252         return;
253 #ifdef SET_HUDOBJ
254     if (hudobj_rect_capable(olh_object.obj)) {
255         hudobj_set_id(olh_object.obj, TRUE);
256     }
257 #endif // SET_HUDOBJ
258 }
259 
draw_olh_string(char * s,short xl,short yl)260 LGPoint draw_olh_string(char *s, short xl, short yl) {
261     short w, h;
262     short x, y;
263 
264     string_replace_char(s, '\n', CHAR_SOFTSP);
265     gr_set_font(ResGet(RES_tinyTechFont));
266     gr_set_fcolor(hud_colors[hud_color_bank][2]);
267     gr_string_wrap(s, OLH_WRAP_WID);
268     gr_string_size(s, &w, &h);
269     ss_point_convert(&xl, &yl, TRUE);
270     if (DoubleSize) {
271         xl *= 2;
272         yl = yl * 2 + 1;
273     }
274     x = SCREEN_VIEW_X - xl + SCREEN_VIEW_WIDTH - w - 1;
275     y = SCREEN_VIEW_Y - yl + SCREEN_VIEW_HEIGHT - h - 1;
276     draw_shadowed_string(s, x, y, TRUE);
277     return MakePoint(x - 1, y + (h / 2));
278 }
279 
280 ushort fixture_panel_stringrefs[] = {
281     REFINDEX(REF_STR_helpPanel),    REFINDEX(REF_STR_helpPanel),    REFINDEX(REF_STR_helpPanel),
282     REFINDEX(REF_STR_helpPanel),    REFINDEX(REF_STR_helpElevator), REFINDEX(REF_STR_helpElevator),
283     REFINDEX(REF_STR_helpElevator), REFINDEX(REF_STR_helpKeypad),   REFINDEX(REF_STR_helpKeypad),
284     REFINDEX(REF_STR_helpPanel),    REFINDEX(REF_STR_helpPanel),
285 };
286 
287 extern char *get_object_lookname(ObjID, char *, int);
288 
olh_do_panel_ref(short xl,short yl)289 void olh_do_panel_ref(short xl, short yl) {
290     int *d1, *d2;
291     ObjID obj = player_struct.panel_ref;
292     char buf[80];
293 
294     buf[0] = '\0';
295     if (is_container(obj, &d1, &d2) && ((d1 != NULL && *d1 != 0) || (d2 != NULL && *d2 != 0))) {
296         char namebuf[80];
297 
298         get_object_lookname(obj, namebuf, sizeof(namebuf));
299         sprintf(buf, get_temp_string(REF_STR_helpGump), namebuf);
300     } else if (objs[obj].obclass == CLASS_FIXTURE) {
301         Ref ref = 0;
302         if (objs[obj].subclass == FIXTURE_SUBCLASS_CONTROL)
303             ref = REF_STR_helpSwitch;
304         else if (objs[obj].subclass == FIXTURE_SUBCLASS_PANEL) {
305             extern uchar comparator_check(int comparator, ObjID obj, uchar *special_code);
306             uchar special;
307             ObjFixture *pfixt = &objFixtures[objs[obj].specID];
308             uchar rv = comparator_check(pfixt->comparator, obj, &special);
309             if (special == 0)
310                 ref = MKREF(RES_olh_strings, fixture_panel_stringrefs[objs[obj].info.type]);
311         }
312         if (ref != 0)
313             get_string(ref, buf, sizeof(buf));
314     }
315     if (buf[0] != '\0')
316         draw_olh_string(buf, xl, yl);
317 }
318 
olh_do_callout(short xl,short yl)319 void olh_do_callout(short xl, short yl) {
320     int best_rect = -1;
321     ObjID obj = olh_object.obj;
322 
323     if (obj == OBJ_NULL)
324         return;
325 #ifdef SET_HUDOBJ
326     if (hudobj_rect_capable(ID2TRIP(obj))) {
327         int j;
328         for (j = 0; j < current_num_hudobjs; j++) {
329             struct _hudobj_data *dat = &hudobj_vec[j];
330             if (dat->id == obj) {
331                 best_rect = j;
332                 break;
333             }
334         }
335         /* perhaps in studlier versions we'll do computations
336            to decide whether we're no longer a candidate,
337            rather than just blow away its candidacy */
338         hudobj_set_id(obj, FALSE);
339         if (best_rect == -1)
340             olh_object.obj = OBJ_NULL;
341     }
342 #endif // SET_HUDOBJ
343        //   if (obj != OBJ_NULL)
344     {
345         char buf[80];
346         char *s = get_olh_string(obj, buf);
347         LGPoint spos = draw_olh_string(s, xl, yl);
348         LGPoint pos = olh_object.loc;
349         pos.x = (int)(pos.x + 1) * SCAN_RATIO;
350         pos.y = (int)(pos.y + 1) * SCAN_RATIO;
351         if (DoubleSize) {
352             pos.x *= 2;
353             pos.y *= 2;
354         }
355         ss_int_line(spos.x - 1, spos.y - 1, pos.x, pos.y);
356     }
357     if (best_rect != -1) {
358         struct _hudobj_data *dat = &hudobj_vec[best_rect];
359         gr_set_fcolor(hud_colors[hud_color_bank][0]);
360         ss_box(dat->xl - 1, dat->yl - 1, dat->xh + 1, dat->yh + 1);
361     }
362 }
363 
364 // A "compound use" object is one of those nasty objects
365 // that used on another object by double clicking on that
366 // object.
367 
is_compound_use_obj(ObjID obj)368 uchar is_compound_use_obj(ObjID obj) {
369     if (objs[obj].obclass != CLASS_SMALLSTUFF)
370         return FALSE;
371     if (objs[obj].subclass == SMALLSTUFF_SUBCLASS_PLOT)
372         return TRUE;
373     switch (ID2TRIP(obj)) {
374     case EPICK_TRIPLE:
375     case HEAD_TRIPLE:
376     case HEAD2_TRIPLE:
377         return TRUE;
378     default:
379         break;
380     }
381     return FALSE;
382 }
383 
olh_do_cursor(short xl,short yl)384 void olh_do_cursor(short xl, short yl) {
385     ObjID obj = object_on_cursor;
386     // this should be a different string if the cursor is
387     // a live grenade.
388     char buf[80];
389     if ((objs[obj].obclass == CLASS_GRENADE &&
390          objGrenades[objs[obj].specID].flags & GREN_ACTIVE_FLAG) ||
391         ObjProps[OPNUM(obj)].flags & USELESS_FLAG)
392         get_string(REF_STR_helpGrenade, buf, sizeof(buf));
393     else {
394         char stringbuf[80];
395         char namebuf[80];
396         Ref id = is_compound_use_obj(obj) ? REF_STR_helpCompound : REF_STR_helpCursor;
397 
398         get_string(id, stringbuf, sizeof(stringbuf));
399         get_object_lookname(obj, namebuf, sizeof(namebuf));
400         sprintf(buf, stringbuf, namebuf);
401     }
402     draw_olh_string(buf, xl, yl);
403 }
404 
405 // -------------------------------------------------
406 // olh_do_hudobjs is called by the hud system to
407 // draw olh hud.
408 
olh_do_hudobjs(short xl,short yl)409 void olh_do_hudobjs(short xl, short yl) {
410     extern uchar saveload_static;
411     if (global_fullmap->cyber || saveload_static)
412         return;
413     if (input_cursor_mode == INPUT_OBJECT_CURSOR)
414         olh_do_cursor(xl, yl);
415     else if (player_struct.panel_ref != OBJ_NULL) {
416         int i;
417         for (i = 0; i < NUM_MFDS; i++)
418             if (player_struct.mfd_current_slots[i] == MFD_INFO_SLOT) {
419                 olh_do_panel_ref(xl, yl);
420                 return;
421             }
422     } else
423         olh_do_callout(xl, yl);
424 }
425 
426 /*KLC - no longer used
427 void olh_init(void)
428 {
429    extern  void olh_init_scan(void);
430    olh_init_scan();
431 }
432 */
433 
olh_closedown(void)434 void olh_closedown(void) { olh_object.obj = OBJ_NULL; }
435 
olh_shutdown(void)436 void olh_shutdown(void) {
437     extern void olh_free_scan(void);
438     olh_free_scan();
439 }
440 
441 short _olh_overlay_keys[] = {
442     ' ' | KB_FLAG_DOWN,
443     '?' | KB_FLAG_DOWN,
444 };
445 
446 #define NUM_OVERLAY_KEYS (sizeof(_olh_overlay_keys) / sizeof(_olh_overlay_keys[0]))
447 
olh_overlay(void)448 void olh_overlay(void) {
449     extern LGCursor globcursor;
450     extern char which_lang;
451     uchar done = FALSE;
452 
453     status_bio_end();
454     uiPushGlobalCursor(&globcursor);
455     gr_push_canvas(grd_screen_canvas);
456     uiHideMouse(NULL);
457     draw_res_bm(REF_IMG_bmHelpOverlayEnglish + MKREF(which_lang, 0), 0, 0);
458     uiShowMouse(NULL);
459     gr_pop_canvas();
460     uiFlush();
461 
462     while (!done) {
463         ushort key;
464         ss_mouse_event me;
465         extern void pump_events(void);
466         pump_events(); // DG: apparently this can loop for a long time waiting for input w/o game_loop() being able to
467                        // update events
468 
469         tight_loop(FALSE);
470         // FIXME It crash on Linux
471         /*if (mouse_next(&me) == OK)
472         {
473           if (me.type == MOUSE_LDOWN)
474                   done = TRUE;
475         }*/
476         if (kb_get_cooked(&key)) {
477             int i;
478             for (i = 0; i < NUM_OVERLAY_KEYS; i++)
479                 if (_olh_overlay_keys[i] == key) {
480                     done = TRUE;
481                     if (i != 0)
482                         hotkey_dispatch(key);
483                 }
484         }
485 
486         SDLDraw();
487     }
488 
489     uiPopGlobalCursor();
490     uiFlush();
491     olh_overlay_on = FALSE;
492 	gr_clear(0); //makes red pixels go away, but real problem is probably in REF_IMG_bmBlankMFD
493     screen_draw();
494     status_bio_start();
495 }
496 
toggle_olh_func(ushort keycode,uint32_t context,intptr_t data)497 uchar toggle_olh_func(ushort keycode, uint32_t context, intptr_t data) {
498     if (!olh_active) {
499         string_message_info(REF_STR_helpOn);
500         olh_active = TRUE;
501     } else {
502         string_message_info(REF_STR_helpOff);
503         olh_active = FALSE;
504         ResUnlock(RES_olh_strings); // KLC - added to free strings.
505     }
506     gShockPrefs.goOnScreenHelp = olh_active; // KLC - Yeah, got to update this one too and
507     SavePrefs();                             // KLC - save the prefs out to disk.
508     return TRUE;
509 }
510 
olh_overlay_func(ushort keycode,uint32_t context,intptr_t data)511 uchar olh_overlay_func(ushort keycode, uint32_t context, intptr_t data) {
512     if (global_fullmap->cyber) {
513         string_message_info(REF_STR_NotAvailCspace);
514         return TRUE;
515     }
516     if (full_game_3d) {
517         change_mode_func(keycode, context, GAME_LOOP);
518     }
519     olh_overlay_on = TRUE;
520     return TRUE;
521 }
522