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