1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 //
26 //      ID Engine
27 //      ID_US_1.c - User Manager - General routines
28 //      v1.1d1
29 //      By Jason Blochowiak
30 //      Hacked up for Catacomb 3D
31 //
32 
33 //
34 //      This module handles dealing with user input & feedback
35 //
36 //      Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching,
37 //              and Refresh Mgrs, Memory Mgr for background save/restore
38 //
39 //      Globals:
40 //              ingame - Flag set by game indicating if a game is in progress
41 //      abortgame - Flag set if the current game should be aborted (if a load
42 //                      game fails)
43 //              loadedgame - Flag set if a game was loaded
44 //              abortprogram - Normally nil, this points to a terminal error message
45 //                      if the program needs to abort
46 //              restartgame - Normally set to gd_Continue, this is set to one of the
47 //                      difficulty levels if a new game should be started
48 //              PrintX, PrintY - Where the User Mgr will print (global coords)
49 //              WindowX,WindowY,WindowW,WindowH - The dimensions of the current
50 //                      window
51 //
52 
53 
54 #include "id_heads.h"
55 
56 
57 #define VW_UpdateScreen() VH_UpdateScreen()
58 void VH_UpdateScreen();
59 
60 
61 // Global variables
62 char* abortprogram;
63 int16_t PrintX;
64 int16_t PrintY;
65 int16_t WindowX;
66 int16_t WindowY;
67 int16_t WindowW;
68 int16_t WindowH;
69 
70 US_CursorStruct US_CustomCursor; // JAM
71 bool use_custom_cursor = false; // JAM
72 
73 // Internal variables
74 #define ConfigVersion 1
75 
76 bool US_Started;
77 
78 bool Button0;
79 bool Button1;
80 bool CursorBad;
81 int16_t CursorX;
82 int16_t CursorY;
83 
84 void (* USL_MeasureString)(
85     const char*,
86     int*,
87     int*) = VW_MeasurePropString;
88 
89 void (*USL_DrawString)(const char*) = VWB_DrawPropString;
90 
91 SaveGame Games[MaxSaveGames];
92 
93 
94 // BBi
95 namespace {
96 
97 
98 SDL_TimerID sys_timer_id;
99 std::atomic<uint32_t> sys_timer_ticks;
100 
101 
sys_timer_callback(Uint32 interval,void * param)102 Uint32 sys_timer_callback(
103     Uint32 interval,
104     void* param)
105 {
106     static_cast<void>(param);
107 
108     ++::TimeCount;
109     sys_timer_ticks = ::SDL_GetTicks();
110 
111     return interval;
112 }
113 
114 
115 } // namespace
116 
117 
sys_get_timer_ticks()118 uint32_t sys_get_timer_ticks()
119 {
120     return ::sys_timer_ticks;
121 }
122 // BBi
123 
124 
125 HighScores Scores;
126 
127 ///////////////////////////////////////////////////////////////////////////
128 //
129 //      US_Shutdown() - Shuts down the User Mgr
130 //
131 ///////////////////////////////////////////////////////////////////////////
US_Shutdown()132 void US_Shutdown()
133 {
134     if (!::US_Started) {
135         return;
136     }
137 
138     // BBi
139     if (::SDL_RemoveTimer(sys_timer_id) == SDL_FALSE) {
140         bstone::Log::write_warning("Failed to remove a timer.");
141     }
142 
143     sys_timer_id = 0;
144     // BBi
145 
146     ::US_Started = false;
147 }
148 
149 ///////////////////////////////////////////////////////////////////////////
150 //
151 //      US_Print() - Prints a string in the current window. Newlines are
152 //              supported.
153 //
154 ///////////////////////////////////////////////////////////////////////////
US_Print(const char * s)155 void US_Print(
156     const char* s)
157 {
158     std::vector<char> buffer(
159         s, s + std::string::traits_type::length(s) + 1);
160     s = &buffer[0];
161 
162     char c;
163     const char* se;
164     int w, h;
165 
166     while (*s) {
167         se = s;
168         while ((c = *se) != '\0' && (c != '\n')) {
169             se++;
170         }
171         *(char*)se = '\0';
172 
173         USL_MeasureString(s, &w, &h);
174         px = PrintX;
175         py = PrintY;
176         USL_DrawString(s);
177 
178         s = se;
179         if (c) {
180             *(char*)se = c;
181             s++;
182 
183             PrintX = WindowX;
184             PrintY = static_cast<uint16_t>(PrintY + h);
185         } else {
186             PrintX = static_cast<uint16_t>(PrintX + w);
187         }
188     }
189 }
190 
191 ///////////////////////////////////////////////////////////////////////////
192 //
193 //      US_PrintUnsigned() - Prints an unsigned long
194 //
195 ///////////////////////////////////////////////////////////////////////////
US_PrintUnsigned(uint32_t n)196 void US_PrintUnsigned(
197     uint32_t n)
198 {
199     auto buffer = std::to_string(n);
200     ::US_Print(buffer.c_str());
201 }
202 
203 ///////////////////////////////////////////////////////////////////////////
204 //
205 //      USL_PrintInCenter() - Prints a string in the center of the given rect
206 //
207 ///////////////////////////////////////////////////////////////////////////
USL_PrintInCenter(const char * s,Rect r)208 void USL_PrintInCenter(
209     const char* s,
210     Rect r)
211 {
212     int w;
213     int h;
214     int rw;
215     int rh;
216 
217     USL_MeasureString(s, &w, &h);
218     rw = r.lr.x - r.ul.x;
219     rh = r.lr.y - r.ul.y;
220 
221     px = static_cast<int16_t>(r.ul.x + ((rw - w) / 2));
222     py = static_cast<int16_t>(r.ul.y + ((rh - h) / 2));
223     USL_DrawString(s);
224 }
225 
226 ///////////////////////////////////////////////////////////////////////////
227 //
228 //      US_PrintCentered() - Prints a string centered in the current window.
229 //
230 ///////////////////////////////////////////////////////////////////////////
US_PrintCentered(const char * s)231 void US_PrintCentered(
232     const char* s)
233 {
234     Rect r;
235 
236     r.ul.x = WindowX;
237     r.ul.y = WindowY;
238     r.lr.x = r.ul.x + WindowW;
239     r.lr.y = r.ul.y + WindowH;
240 
241     USL_PrintInCenter(s, r);
242 }
243 
244 ///////////////////////////////////////////////////////////////////////////
245 //
246 //      US_CPrintLine() - Prints a string centered on the current line and
247 //              advances to the next line. Newlines are not supported.
248 //
249 ///////////////////////////////////////////////////////////////////////////
US_CPrintLine(const char * s)250 void US_CPrintLine(
251     const char* s)
252 {
253     int w;
254     int h;
255 
256     USL_MeasureString(s, &w, &h);
257 
258     if (w > WindowW) {
259         ::Quit("String exceeds width.");
260     }
261     px = static_cast<int16_t>(WindowX + ((WindowW - w) / 2));
262     py = PrintY;
263     USL_DrawString(s);
264     PrintY = static_cast<uint16_t>(PrintY + h);
265 }
266 
267 ///////////////////////////////////////////////////////////////////////////
268 //
269 //      US_CPrint() - Prints a string in the current window. Newlines are
270 //              supported.
271 //
272 ///////////////////////////////////////////////////////////////////////////
US_CPrint(const char * s)273 void US_CPrint(
274     const char* s)
275 {
276     std::string string(s);
277 
278     if (string.back() != '\n') {
279         string += '\n';
280     }
281 
282     std::string::size_type line_begin = 0;
283 
284     while (true) {
285         auto line_end = string.find(
286             '\n',
287             line_begin);
288 
289         if (line_end == std::string::npos) {
290             break;
291         }
292 
293         auto substring = string.substr(
294             line_begin,
295             line_end - line_begin);
296 
297         ::US_CPrintLine(substring.c_str());
298 
299         line_begin = line_end + 1;
300     }
301 }
302 
303 ///////////////////////////////////////////////////////////////////////////
304 //
305 //      US_ClearWindow() - Clears the current window to white and homes the
306 //              cursor
307 //
308 ///////////////////////////////////////////////////////////////////////////
US_ClearWindow()309 void US_ClearWindow()
310 {
311     VWB_Bar(WindowX, WindowY, WindowW, WindowH, 0xEF);
312     PrintX = WindowX;
313     PrintY = WindowY;
314 }
315 
316 ///////////////////////////////////////////////////////////////////////////
317 //
318 //      US_DrawWindow() - Draws a frame and sets the current window parms
319 //
320 ///////////////////////////////////////////////////////////////////////////
US_DrawWindow(uint16_t x,uint16_t y,uint16_t w,uint16_t h)321 void US_DrawWindow(
322     uint16_t x,
323     uint16_t y,
324     uint16_t w,
325     uint16_t h)
326 {
327     uint16_t i,
328            sx, sy, sw, sh;
329 
330     WindowX = x * 8;
331     WindowY = y * 8;
332     WindowW = w * 8;
333     WindowH = h * 8;
334 
335     PrintX = WindowX;
336     PrintY = WindowY;
337 
338     sx = (x - 1) * 8;
339     sy = (y - 1) * 8;
340     sw = (w + 1) * 8;
341     sh = (h + 1) * 8;
342 
343     US_ClearWindow();
344 
345     VWB_DrawTile8(sx, sy, 0), VWB_DrawTile8(sx, sy + sh, 5);
346     for (i = sx + 8; i <= sx + sw - 8; i += 8) {
347         VWB_DrawTile8(i, sy, 1), VWB_DrawTile8(i, sy + sh, 6);
348     }
349     VWB_DrawTile8(i, sy, 2), VWB_DrawTile8(i, sy + sh, 7);
350 
351     for (i = sy + 8; i <= sy + sh - 8; i += 8) {
352         VWB_DrawTile8(sx, i, 3), VWB_DrawTile8(sx + sw, i, 4);
353     }
354 }
355 
356 ///////////////////////////////////////////////////////////////////////////
357 //
358 //      US_CenterWindow() - Generates a window of a given width & height in the
359 //              middle of the screen
360 //
361 ///////////////////////////////////////////////////////////////////////////
US_CenterWindow(uint16_t w,uint16_t h)362 void US_CenterWindow(
363     uint16_t w,
364     uint16_t h)
365 {
366     US_DrawWindow(((MaxX / 8) - w) / 2, ((MaxY / 8) - h) / 2, w, h);
367 }
368 
369 ///////////////////////////////////////////////////////////////////////////
370 //
371 //      US_SaveWindow() - Saves the current window parms into a record for
372 //              later restoration
373 //
374 ///////////////////////////////////////////////////////////////////////////
US_SaveWindow(WindowRec * win)375 void US_SaveWindow(
376     WindowRec* win)
377 {
378     win->x = WindowX;
379     win->y = WindowY;
380     win->w = WindowW;
381     win->h = WindowH;
382 
383     win->px = PrintX;
384     win->py = PrintY;
385 }
386 
387 ///////////////////////////////////////////////////////////////////////////
388 //
389 //      US_RestoreWindow() - Sets the current window parms to those held in the
390 //              record
391 //
392 ///////////////////////////////////////////////////////////////////////////
US_RestoreWindow(WindowRec * win)393 void US_RestoreWindow(
394     WindowRec* win)
395 {
396     WindowX = win->x;
397     WindowY = win->y;
398     WindowW = win->w;
399     WindowH = win->h;
400 
401     PrintX = win->px;
402     PrintY = win->py;
403 }
404 
405 ///////////////////////////////////////////////////////////////////////////
406 //
407 //      USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()
408 //
409 ///////////////////////////////////////////////////////////////////////////
USL_XORICursor(int16_t x,int16_t y,char * s,int cursor)410 static void USL_XORICursor(
411     int16_t x,
412     int16_t y,
413     char* s,
414     int cursor)
415 {
416     static bool status; // VGA doesn't XOR...
417     char buf[MaxString];
418     int temp;
419     int w, h;
420 
421     strcpy(buf, s);
422     buf[cursor] = '\0';
423     USL_MeasureString(buf, &w, &h);
424 
425     px = static_cast<int16_t>(x + w - 1);
426     py = y;
427 
428     VL_WaitVBL(1);
429 
430     status = !status;
431 
432     if (status) {
433         USL_DrawString("\x80");
434     } else {
435         temp = fontcolor;
436         fontcolor = backcolor;
437         USL_DrawString("\x80");
438         fontcolor = static_cast<uint8_t>(temp);
439     }
440 
441 }
442 
443 // JAM BEGIN - New Function
444 ///////////////////////////////////////////////////////////////////////////
445 //
446 //      USL_CustomCursor() - Toggle Displays the custom text cursor.
447 //      Used by US_LineInput()
448 //
449 ///////////////////////////////////////////////////////////////////////////
USL_CustomCursor(int16_t x,int16_t y,char * s,int cursor)450 static void USL_CustomCursor(
451     int16_t x,
452     int16_t y,
453     char* s,
454     int cursor)
455 {
456     static bool status; // VGA doesn't XOR...
457     char buf[MaxString];
458     int temp;
459     int w, h;
460 
461     strcpy(buf, s);
462     buf[cursor] = '\0';
463     USL_MeasureString(buf, &w, &h);
464 
465     px = static_cast<int16_t>(x + w - 1);
466     py = y;
467 
468     temp = fontcolor;
469     auto temp_font = fontnumber;
470 
471     fontnumber = US_CustomCursor.font_number;
472 
473     status = !status;
474 
475     if (status) {
476         fontcolor = static_cast<uint8_t>(US_CustomCursor.cursor_color);
477     } else {
478         fontcolor = backcolor;
479     }
480 
481     VL_WaitVBL(1);
482 
483     USL_DrawString(&US_CustomCursor.cursor_char);
484     fontcolor = static_cast<uint8_t>(temp);
485     fontnumber = temp_font;
486 
487 }
488 // JAM END - New Function
489 
490 ///////////////////////////////////////////////////////////////////////////
491 //
492 //      US_LineInput() - Gets a line of user input at (x,y), the string defaults
493 //              to whatever is pointed at by def. Input is restricted to maxchars
494 //              chars or maxwidth pixels wide. If the user hits escape (and escok is
495 //              true), nothing is copied into buf, and false is returned. If the
496 //              user hits return, the current string is copied into buf, and true is
497 //              returned
498 //
499 ///////////////////////////////////////////////////////////////////////////
US_LineInput(int16_t x,int16_t y,char * buf,char * def,bool escok,int16_t maxchars,int16_t maxwidth)500 bool US_LineInput(
501     int16_t x,
502     int16_t y,
503     char* buf,
504     char* def,
505     bool escok,
506     int16_t maxchars,
507     int16_t maxwidth)
508 {
509     bool redraw,
510          cursorvis, cursormoved,
511          done, result = false;
512     ScanCode sc;
513     char c,
514          s[MaxString], olds[MaxString];
515     int i,
516         cursor,
517         w, h,
518         len, temp;
519     uint32_t lasttime;
520 
521     if (def) {
522         strcpy(s, def);
523     } else {
524         *s = '\0';
525     }
526     *olds = '\0';
527     cursor = static_cast<uint16_t>(strlen(s));
528     cursormoved = redraw = true;
529 
530     cursorvis = done = false;
531     lasttime = TimeCount;
532     LastASCII = key_None;
533     LastScan = ScanCode::sc_none;
534 
535     while (!done) {
536         if (cursorvis) {
537             if (use_custom_cursor) { // JAM
538                 USL_CustomCursor(x, y, s, cursor); // JAM
539             } else {
540                 USL_XORICursor(x, y, s, cursor);
541             }
542         }
543 
544         ::in_handle_events();
545 
546         sc = LastScan;
547         LastScan = ScanCode::sc_none;
548         c = LastASCII;
549         LastASCII = key_None;
550 
551         switch (sc) {
552         case ScanCode::sc_return:
553             strcpy(buf, s);
554             done = true;
555             result = true;
556             c = key_None;
557             break;
558         case ScanCode::sc_escape:
559             if (escok) {
560                 done = true;
561                 result = false;
562             }
563             c = key_None;
564             break;
565 
566         case ScanCode::sc_backspace:
567             if (cursor) {
568                 strcpy(s + cursor - 1, s + cursor);
569                 cursor--;
570                 redraw = true;
571             }
572             c = key_None;
573             cursormoved = true;
574             break;
575 
576         case ScanCode::sc_up_arrow:
577         case ScanCode::sc_down_arrow:
578         case ScanCode::sc_page_up:
579         case ScanCode::sc_page_down:
580         case ScanCode::sc_insert:
581             c = key_None;
582             break;
583 
584         default:
585             break;
586         }
587 
588         if (c) {
589             len = static_cast<uint16_t>(strlen(s));
590             USL_MeasureString(s, &w, &h);
591 
592             if (isprint(c) && (len < MaxString - 1)
593                 && ((!maxchars) || (len < maxchars))
594                 && ((!maxwidth) || (w < maxwidth)))
595             {
596                 for (i = len + 1; i > cursor; i--) {
597                     s[i] = s[i - 1];
598                 }
599                 s[cursor++] = c;
600                 redraw = true;
601             }
602         }
603 
604         if (redraw) {
605             px = x;
606             py = y;
607             temp = fontcolor;
608             fontcolor = backcolor;
609             USL_DrawString(olds);
610             fontcolor = static_cast<uint8_t>(temp);
611             strcpy(olds, s);
612 
613             px = x;
614             py = y;
615             USL_DrawString(s);
616 
617             redraw = false;
618         }
619 
620         if (cursormoved) {
621             cursorvis = false;
622             lasttime = TimeCount - TickBase;
623 
624             cursormoved = false;
625         }
626 
627         if (TimeCount - lasttime > TickBase / 2) {
628             lasttime = TimeCount;
629 
630             cursorvis ^= true;
631         }
632 
633         if (cursorvis) {
634             if (use_custom_cursor) { // JAM
635                 USL_CustomCursor(x, y, s, cursor); // JAM
636             } else {
637                 USL_XORICursor(x, y, s, cursor);
638             }
639         }
640 
641         VW_UpdateScreen();
642     }
643 
644     if (cursorvis) {
645         if (use_custom_cursor) { // JAM
646             USL_CustomCursor(x, y, s, cursor); // JAM
647         } else {
648             USL_XORICursor(x, y, s, cursor);
649         }
650     }
651 
652     if (!result) {
653         px = x;
654         py = y;
655         USL_DrawString(olds);
656     }
657     VW_UpdateScreen();
658 
659     IN_ClearKeysDown();
660     return result;
661 }
662 
US_Startup()663 void US_Startup()
664 {
665     if (::US_Started) {
666         return;
667     }
668 
669     // BBi
670     sys_timer_ticks = 0;
671 
672     sys_timer_id = ::SDL_AddTimer(
673         1000 / TickBase,
674         sys_timer_callback,
675         nullptr);
676 
677     if (sys_timer_id == 0) {
678         ::Quit("Failed to add a timer.");
679     }
680     // BBi
681 
682     ::US_InitRndT(true); // Initialize the random number generator
683     ::US_Started = true;
684 }
685