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