1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1998-2000 by DooM Legacy Team.
4 // Copyright (C) 1999-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file console.c
11 /// \brief Console drawing and input
12
13 #ifdef __GNUC__
14 #include <unistd.h>
15 #endif
16
17 #include "doomdef.h"
18 #include "console.h"
19 #include "g_game.h"
20 #include "g_input.h"
21 #include "hu_stuff.h"
22 #include "keys.h"
23 #include "r_main.h"
24 #include "r_defs.h"
25 #include "sounds.h"
26 #include "st_stuff.h"
27 #include "s_sound.h"
28 #include "v_video.h"
29 #include "i_video.h"
30 #include "z_zone.h"
31 #include "i_system.h"
32 #include "i_threads.h"
33 #include "d_main.h"
34 #include "m_menu.h"
35 #include "filesrch.h"
36 #include "m_misc.h"
37
38 #ifdef _WINDOWS
39 #include "win32/win_main.h"
40 #endif
41
42 #ifdef HWRENDER
43 #include "hardware/hw_main.h"
44 #endif
45
46 #define MAXHUDLINES 20
47
48 #ifdef HAVE_THREADS
49 I_mutex con_mutex;
50
51 # define Lock_state() I_lock_mutex(&con_mutex)
52 # define Unlock_state() I_unlock_mutex(con_mutex)
53 #else/*HAVE_THREADS*/
54 # define Lock_state()
55 # define Unlock_state()
56 #endif/*HAVE_THREADS*/
57
58 static boolean con_started = false; // console has been initialised
59 boolean con_startup = false; // true at game startup
60 boolean con_refresh = false; // screen needs refreshing
61 static boolean con_forcepic = true; // at startup toggle console translucency when first off
62 boolean con_recalc; // set true when screen size has changed
63
64 static tic_t con_tick; // console ticker for anim or blinking prompt cursor
65 // con_scrollup should use time (currenttime - lasttime)..
66
67 static boolean consoletoggle; // true when console key pushed, ticker will handle
68 static boolean consoleready; // console prompt is ready
69
70 INT32 con_destlines; // vid lines used by console at final position
71 static INT32 con_curlines; // vid lines currently used by console
72
73 INT32 con_clipviewtop; // (useless)
74
75 static INT32 con_hudlines; // number of console heads up message lines
76 static INT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
77
78 INT32 con_clearlines; // top screen lines to refresh when view reduced
79 boolean con_hudupdate; // when messages scroll, we need a backgrnd refresh
80
81 // console text output
82 static char *con_line; // console text output current line
83 static size_t con_cx; // cursor position in current line
84 static size_t con_cy; // cursor line number in con_buffer, is always
85 // increasing, and wrapped around in the text
86 // buffer using modulo.
87
88 static size_t con_totallines; // lines of console text into the console buffer
89 static size_t con_width; // columns of chars, depend on vid mode width
90
91 static size_t con_scrollup; // how many rows of text to scroll up (pgup/pgdn)
92 UINT32 con_scalefactor; // text size scale factor
93
94 // hold 32 last lines of input for history
95 #define CON_MAXPROMPTCHARS 256
96 #define CON_PROMPTCHAR '$'
97
98 static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
99
100 static INT32 inputline; // current input line number
101 static INT32 inputhist; // line number of history input line to restore
102 static size_t input_cur; // position of cursor in line
103 static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
104 static size_t input_len; // length of current line, used to bound cursor and such
105 // notice: input does NOT include the "$" at the start of the line. - 11/3/16
106
107 // protos.
108 static void CON_InputInit(void);
109 static void CON_RecalcSize(void);
110 static void CON_ChangeHeight(void);
111
112 static void CON_DrawBackpic(void);
113 static void CONS_hudlines_Change(void);
114 static void CONS_backcolor_Change(void);
115
116 //======================================================================
117 // CONSOLE VARS AND COMMANDS
118 //======================================================================
119 #ifdef macintosh
120 #define CON_BUFFERSIZE 4096 // my compiler can't handle local vars >32k
121 #else
122 #define CON_BUFFERSIZE 16384
123 #endif
124
125 static char con_buffer[CON_BUFFERSIZE];
126
127 // how many seconds the hud messages lasts on the screen
128 static consvar_t cons_msgtimeout = CVAR_INIT ("con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL);
129
130 // number of lines displayed on the HUD
131 static consvar_t cons_hudlines = CVAR_INIT ("con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change);
132
133 // number of lines console move per frame
134 // (con_speed needs a limit, apparently)
135 static CV_PossibleValue_t speed_cons_t[] = {{0, "MIN"}, {64, "MAX"}, {0, NULL}};
136 static consvar_t cons_speed = CVAR_INIT ("con_speed", "8", CV_SAVE, speed_cons_t, NULL);
137
138 // percentage of screen height to use for console
139 static consvar_t cons_height = CVAR_INIT ("con_height", "50", CV_SAVE, CV_Unsigned, NULL);
140
141 static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}, {0, NULL}};
142 // whether to use console background picture, or translucent mode
143 static consvar_t cons_backpic = CVAR_INIT ("con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL);
144
145 static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, {2, "Sepia"},
146 {3, "Brown"}, {4, "Pink"}, {5, "Raspberry"},
147 {6, "Red"}, {7, "Creamsicle"}, {8, "Orange"},
148 {9, "Gold"}, {10,"Yellow"}, {11,"Emerald"},
149 {12,"Green"}, {13,"Cyan"}, {14,"Steel"},
150 {15,"Periwinkle"}, {16,"Blue"}, {17,"Purple"},
151 {18,"Lavender"},
152 {0, NULL}};
153
154
155 consvar_t cons_backcolor = CVAR_INIT ("con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change);
156
157 static void CON_Print(char *msg);
158
159 //
160 //
CONS_hudlines_Change(void)161 static void CONS_hudlines_Change(void)
162 {
163 INT32 i;
164
165 Lock_state();
166
167 // Clear the currently displayed lines
168 for (i = 0; i < con_hudlines; i++)
169 con_hudtime[i] = 0;
170
171 if (cons_hudlines.value < 1)
172 cons_hudlines.value = 1;
173 else if (cons_hudlines.value > MAXHUDLINES)
174 cons_hudlines.value = MAXHUDLINES;
175
176 con_hudlines = cons_hudlines.value;
177
178 Unlock_state();
179
180 CONS_Printf(M_GetText("Number of console HUD lines is now %d\n"), con_hudlines);
181 }
182
183 // Clear console text buffer
184 //
CONS_Clear_f(void)185 static void CONS_Clear_f(void)
186 {
187 Lock_state();
188
189 memset(con_buffer, 0, CON_BUFFERSIZE);
190
191 con_cx = 0;
192 con_cy = con_totallines-1;
193 con_line = &con_buffer[con_cy*con_width];
194 con_scrollup = 0;
195
196 Unlock_state();
197 }
198
199 // Choose english keymap
200 //
201 /*static void CONS_English_f(void)
202 {
203 shiftxform = english_shiftxform;
204 CONS_Printf(M_GetText("%s keymap.\n"), M_GetText("English"));
205 }*/
206
207 static char *bindtable[NUMINPUTS];
208
CONS_Bind_f(void)209 static void CONS_Bind_f(void)
210 {
211 size_t na;
212 INT32 key;
213
214 na = COM_Argc();
215
216 if (na != 2 && na != 3)
217 {
218 CONS_Printf(M_GetText("bind <keyname> [<command>]: create shortcut keys to command(s)\n"));
219 CONS_Printf("\x82%s", M_GetText("Bind table :\n"));
220 na = 0;
221 for (key = 0; key < NUMINPUTS; key++)
222 if (bindtable[key])
223 {
224 CONS_Printf("%s : \"%s\"\n", G_KeynumToString(key), bindtable[key]);
225 na = 1;
226 }
227 if (!na)
228 CONS_Printf(M_GetText("(empty)\n"));
229 return;
230 }
231
232 key = G_KeyStringtoNum(COM_Argv(1));
233 if (key <= 0 || key >= NUMINPUTS)
234 {
235 CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n"));
236 return;
237 }
238
239 Z_Free(bindtable[key]);
240 bindtable[key] = NULL;
241
242 if (na == 3)
243 bindtable[key] = Z_StrDup(COM_Argv(2));
244 }
245
246 //======================================================================
247 // CONSOLE SETUP
248 //======================================================================
249
250 // Font colormap colors
251 // TODO: This could probably be improved somehow...
252 // These colormaps are 99% identical, with just a few changed bytes
253 // This could EASILY be handled by modifying a centralised colormap
254 // for software depending on the prior state - but yknow, OpenGL...
255 UINT8 *yellowmap, *magentamap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap, *skymap, *purplemap, *aquamap, *peridotmap, *azuremap, *brownmap, *rosymap, *invertmap;
256
257 // Console BG color
258 UINT8 *consolebgmap = NULL;
259 UINT8 *promptbgmap = NULL;
260 static UINT8 promptbgcolor = UINT8_MAX;
261
CON_SetupBackColormapEx(INT32 color,boolean prompt)262 void CON_SetupBackColormapEx(INT32 color, boolean prompt)
263 {
264 UINT16 i, palsum;
265 UINT8 j, palindex;
266 UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
267 INT32 shift = 6;
268
269 if (color == INT32_MAX)
270 color = cons_backcolor.value;
271
272 shift = 6; // 12 colors -- shift of 7 means 6 colors
273
274 switch (color)
275 {
276 case 0: palindex = 15; break; // White
277 case 1: palindex = 31; break; // Black
278 case 2: palindex = 251; break; // Sepia
279 case 3: palindex = 239; break; // Brown
280 case 4: palindex = 215; shift = 7; break; // Pink
281 case 5: palindex = 37; shift = 7; break; // Raspberry
282 case 6: palindex = 47; shift = 7; break; // Red
283 case 7: palindex = 53; shift = 7; break; // Creamsicle
284 case 8: palindex = 63; break; // Orange
285 case 9: palindex = 56; shift = 7; break; // Gold
286 case 10: palindex = 79; shift = 7; break; // Yellow
287 case 11: palindex = 119; shift = 7; break; // Emerald
288 case 12: palindex = 111; break; // Green
289 case 13: palindex = 136; shift = 7; break; // Cyan
290 case 14: palindex = 175; shift = 7; break; // Steel
291 case 15: palindex = 166; shift = 7; break; // Periwinkle
292 case 16: palindex = 159; break; // Blue
293 case 17: palindex = 187; shift = 7; break; // Purple
294 case 18: palindex = 199; shift = 7; break; // Lavender
295 // Default green
296 default: palindex = 111; break;
297 }
298
299 if (prompt)
300 {
301 if (!promptbgmap)
302 promptbgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
303
304 if (color == promptbgcolor)
305 return;
306 else
307 promptbgcolor = color;
308 }
309 else if (!consolebgmap)
310 consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
311
312 // setup background colormap
313 for (i = 0, j = 0; i < 768; i += 3, j++)
314 {
315 palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
316 if (prompt)
317 promptbgmap[j] = (UINT8)(palindex - palsum);
318 else
319 consolebgmap[j] = (UINT8)(palindex - palsum);
320 }
321 }
322
CON_SetupBackColormap(void)323 void CON_SetupBackColormap(void)
324 {
325 CON_SetupBackColormapEx(cons_backcolor.value, false);
326 CON_SetupBackColormapEx(1, true); // default to gray
327 }
328
CONS_backcolor_Change(void)329 static void CONS_backcolor_Change(void)
330 {
331 CON_SetupBackColormapEx(cons_backcolor.value, false);
332 }
333
CON_SetupColormaps(void)334 static void CON_SetupColormaps(void)
335 {
336 INT32 i;
337 UINT8 *memorysrc = (UINT8 *)Z_Malloc((256*15), PU_STATIC, NULL);
338
339 magentamap = memorysrc;
340 yellowmap = (magentamap+256);
341 lgreenmap = (yellowmap+256);
342 bluemap = (lgreenmap+256);
343 redmap = (bluemap+256);
344 graymap = (redmap+256);
345 orangemap = (graymap+256);
346 skymap = (orangemap+256);
347 purplemap = (skymap+256);
348 aquamap = (purplemap+256);
349 peridotmap = (aquamap+256);
350 azuremap = (peridotmap+256);
351 brownmap = (azuremap+256);
352 rosymap = (brownmap+256);
353 invertmap = (rosymap+256);
354
355 // setup the other colormaps, for console text
356
357 // these don't need to be aligned, unless you convert the
358 // V_DrawMappedPatch() into optimised asm.
359
360 for (i = 0; i < (256*15); i++, ++memorysrc)
361 *memorysrc = (UINT8)(i & 0xFF); // remap each color to itself...
362
363 #define colset(map, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
364 map[0x0] = (UINT8)a;\
365 map[0x1] = (UINT8)b;\
366 map[0x2] = (UINT8)c;\
367 map[0x3] = (UINT8)d;\
368 map[0x4] = (UINT8)e;\
369 map[0x5] = (UINT8)f;\
370 map[0x6] = (UINT8)g;\
371 map[0x7] = (UINT8)h;\
372 map[0x8] = (UINT8)i;\
373 map[0x9] = (UINT8)j;\
374 map[0xA] = (UINT8)k;\
375 map[0xB] = (UINT8)l;\
376 map[0xC] = (UINT8)m;\
377 map[0xD] = (UINT8)n;\
378 map[0xE] = (UINT8)o;\
379 map[0xF] = (UINT8)p;
380
381 // Tried to keep the colors vanilla while adding some shades in between them ~SonicX8000
382
383 // 0x1 0x3 0x9 0xF
384 colset(magentamap, 177, 177, 178, 178, 178, 180, 180, 180, 182, 182, 182, 182, 184, 184, 184, 185);
385 colset(yellowmap, 82, 82, 73, 73, 73, 64, 64, 64, 66, 66, 66, 66, 67, 67, 67, 68);
386 colset(lgreenmap, 96, 96, 98, 98, 98, 101, 101, 101, 104, 104, 104, 104, 106, 106, 106, 107);
387 colset(bluemap, 146, 146, 147, 147, 147, 149, 149, 149, 152, 152, 152, 152, 155, 155, 155, 157);
388 colset(redmap, 32, 32, 33, 33, 33, 35, 35, 35, 39, 39, 39, 39, 42, 42, 42, 44);
389 colset(graymap, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23);
390 colset(orangemap, 50, 50, 52, 52, 52, 54, 54, 54, 56, 56, 56, 56, 59, 59, 59, 60);
391 colset(skymap, 129, 129, 130, 130, 130, 131, 131, 131, 133, 133, 133, 133, 135, 135, 135, 136);
392 colset(purplemap, 160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165);
393 colset(aquamap, 120, 120, 121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 125);
394 colset(peridotmap, 72, 72, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 94);
395 colset(azuremap, 144, 144, 145, 145, 145, 146, 146, 146, 170, 170, 170, 170, 171, 171, 171, 172);
396 colset(brownmap, 219, 219, 221, 221, 221, 222, 222, 222, 224, 224, 224, 224, 227, 227, 227, 229);
397 colset(rosymap, 200, 200, 201, 201, 201, 202, 202, 202, 203, 203, 203, 203, 204, 204, 204, 205);
398
399 #undef colset
400
401 // Yeah just straight up invert it like a normal person
402 for (i = 0x00; i <= 0x1F; i++)
403 invertmap[0x1F - i] = i;
404
405 // Init back colormap
406 CON_SetupBackColormap();
407 }
408
409 // Setup the console text buffer
410 //
CON_Init(void)411 void CON_Init(void)
412 {
413 INT32 i;
414
415 for (i = 0; i < NUMINPUTS; i++)
416 bindtable[i] = NULL;
417
418 Lock_state();
419
420 // clear all lines
421 memset(con_buffer, 0, CON_BUFFERSIZE);
422
423 // make sure it is ready for the loading screen
424 con_width = 0;
425
426 Unlock_state();
427
428 CON_RecalcSize();
429
430 CON_SetupColormaps();
431
432 Lock_state();
433
434 //note: CON_Ticker should always execute at least once before D_Display()
435 con_clipviewtop = -1; // -1 does not clip
436
437 con_hudlines = atoi(cons_hudlines.defaultvalue);
438
439 Unlock_state();
440
441 // setup console input filtering
442 CON_InputInit();
443
444 // register our commands
445 //
446 COM_AddCommand("cls", CONS_Clear_f);
447 //COM_AddCommand("english", CONS_English_f);
448 // set console full screen for game startup MAKE SURE VID_Init() done !!!
449 Lock_state();
450
451 con_destlines = vid.height;
452 con_curlines = vid.height;
453
454 Unlock_state();
455
456 if (!dedicated)
457 {
458 Lock_state();
459
460 con_started = true;
461 con_startup = true;
462 con_refresh = true; // needs explicit screen refresh until we are in the main game loop
463 consoletoggle = false;
464
465 Unlock_state();
466
467 CV_RegisterVar(&cons_msgtimeout);
468 CV_RegisterVar(&cons_hudlines);
469 CV_RegisterVar(&cons_speed);
470 CV_RegisterVar(&cons_height);
471 CV_RegisterVar(&cons_backpic);
472 CV_RegisterVar(&cons_backcolor);
473 COM_AddCommand("bind", CONS_Bind_f);
474 }
475 else
476 {
477 Lock_state();
478
479 con_started = true;
480 con_startup = false;
481 con_refresh = false; // disable explicit screen refresh
482 consoletoggle = true;
483
484 Unlock_state();
485 }
486 }
487 // Console input initialization
488 //
CON_InputInit(void)489 static void CON_InputInit(void)
490 {
491 Lock_state();
492
493 // prepare the first prompt line
494 memset(inputlines, 0, sizeof (inputlines));
495 inputline = 0;
496 input_cur = input_sel = input_len = 0;
497
498 Unlock_state();
499 }
500
501 //======================================================================
502 // CONSOLE EXECUTION
503 //======================================================================
504
505 // Called at screen size change to set the rows and line size of the
506 // console text buffer.
507 //
CON_RecalcSize(void)508 static void CON_RecalcSize(void)
509 {
510 size_t conw, oldcon_width, oldnumlines, i, oldcon_cy;
511 char *tmp_buffer;
512 char *string;
513
514 Lock_state();
515
516 switch (cv_constextsize.value)
517 {
518 case V_NOSCALEPATCH:
519 con_scalefactor = 1;
520 break;
521 case V_SMALLSCALEPATCH:
522 con_scalefactor = vid.smalldupx;
523 break;
524 case V_MEDSCALEPATCH:
525 con_scalefactor = vid.meddupx;
526 break;
527 default: // Full scaling
528 con_scalefactor = vid.dupx;
529 break;
530 }
531
532 con_recalc = false;
533
534 if (dedicated)
535 conw = 1;
536 else
537 conw = (vid.width>>3) / con_scalefactor - 2;
538
539 if (con_curlines == vid.height) // first init
540 {
541 con_curlines = vid.height;
542 con_destlines = vid.height;
543 }
544
545 if (con_destlines > 0) // Resize console if already open
546 {
547 CON_ChangeHeight();
548 con_curlines = con_destlines;
549 }
550
551 // check for change of video width
552 if (conw == con_width)
553 {
554 Unlock_state();
555 return; // didn't change
556 }
557
558 Unlock_state();
559
560 tmp_buffer = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL);
561 string = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); // BP: it is a line but who know
562
563 Lock_state();
564
565 oldcon_width = con_width;
566 oldnumlines = con_totallines;
567 oldcon_cy = con_cy;
568 M_Memcpy(tmp_buffer, con_buffer, CON_BUFFERSIZE);
569
570 if (conw < 1)
571 con_width = (BASEVIDWIDTH>>3) - 2;
572 else
573 con_width = conw;
574
575 con_width += 11; // Graue 06-19-2004 up to 11 control chars per line
576
577 con_totallines = CON_BUFFERSIZE / con_width;
578 memset(con_buffer, ' ', CON_BUFFERSIZE);
579
580 con_cx = 0;
581 con_cy = con_totallines-1;
582 con_line = &con_buffer[con_cy*con_width];
583 con_scrollup = 0;
584
585 Unlock_state();
586
587 // re-arrange console text buffer to keep text
588 if (oldcon_width) // not the first time
589 {
590 for (i = oldcon_cy + 1; i < oldcon_cy + oldnumlines; i++)
591 {
592 if (tmp_buffer[(i%oldnumlines)*oldcon_width])
593 {
594 M_Memcpy(string, &tmp_buffer[(i%oldnumlines)*oldcon_width], oldcon_width);
595 conw = oldcon_width - 1;
596 while (string[conw] == ' ' && conw)
597 conw--;
598 string[conw+1] = '\n';
599 string[conw+2] = '\0';
600 CON_Print(string);
601 }
602 }
603 }
604
605 Z_Free(string);
606 Z_Free(tmp_buffer);
607 }
608
CON_ChangeHeight(void)609 static void CON_ChangeHeight(void)
610 {
611 INT32 minheight;
612
613 Lock_state();
614
615 minheight = 20 * con_scalefactor; // 20 = 8+8+4
616
617 // toggle console in
618 con_destlines = (cons_height.value*vid.height)/100;
619 if (con_destlines < minheight)
620 con_destlines = minheight;
621 else if (con_destlines > vid.height)
622 con_destlines = vid.height;
623
624 con_destlines &= ~0x3; // multiple of text row height
625
626 Unlock_state();
627 }
628
629 // Handles Console moves in/out of screen (per frame)
630 //
CON_MoveConsole(void)631 static void CON_MoveConsole(void)
632 {
633 fixed_t conspeed;
634
635 Lock_state();
636
637 conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT);
638
639 // instant
640 if (!cons_speed.value)
641 {
642 con_curlines = con_destlines;
643 return;
644 }
645
646 // up/down move to dest
647 if (con_curlines < con_destlines)
648 {
649 con_curlines += FixedInt(conspeed);
650 if (con_curlines > con_destlines)
651 con_curlines = con_destlines;
652 }
653 else if (con_curlines > con_destlines)
654 {
655 con_curlines -= FixedInt(conspeed);
656 if (con_curlines < con_destlines)
657 con_curlines = con_destlines;
658 }
659
660 Unlock_state();
661 }
662
663 // Clear time of console heads up messages
664 //
CON_ClearHUD(void)665 void CON_ClearHUD(void)
666 {
667 INT32 i;
668
669 Lock_state();
670
671 for (i = 0; i < con_hudlines; i++)
672 con_hudtime[i] = 0;
673
674 Unlock_state();
675 }
676
677 // Force console to move out immediately
678 // note: con_ticker will set consoleready false
CON_ToggleOff(void)679 void CON_ToggleOff(void)
680 {
681 Lock_state();
682
683 if (!con_destlines)
684 {
685 Unlock_state();
686 return;
687 }
688
689 con_destlines = 0;
690 con_curlines = 0;
691 CON_ClearHUD();
692 con_forcepic = 0;
693 con_clipviewtop = -1; // remove console clipping of view
694
695 I_UpdateMouseGrab();
696
697 Unlock_state();
698 }
699
CON_Ready(void)700 boolean CON_Ready(void)
701 {
702 boolean ready;
703 Lock_state();
704 {
705 ready = consoleready;
706 }
707 Unlock_state();
708 return ready;
709 }
710
711 // Console ticker: handles console move in/out, cursor blinking
712 //
CON_Ticker(void)713 void CON_Ticker(void)
714 {
715 INT32 i;
716 INT32 minheight;
717
718 Lock_state();
719
720 minheight = 20 * con_scalefactor; // 20 = 8+8+4
721
722 // cursor blinking
723 con_tick++;
724 con_tick &= 7;
725
726 // console key was pushed
727 if (consoletoggle)
728 {
729 consoletoggle = false;
730
731 // toggle off console
732 if (con_destlines > 0)
733 {
734 con_destlines = 0;
735 CON_ClearHUD();
736 I_UpdateMouseGrab();
737 }
738 else
739 CON_ChangeHeight();
740 }
741
742 // console movement
743 if (con_destlines != con_curlines)
744 CON_MoveConsole();
745
746 // clip the view, so that the part under the console is not drawn
747 con_clipviewtop = -1;
748 if (cons_backpic.value) // clip only when using an opaque background
749 {
750 if (con_curlines > 0)
751 con_clipviewtop = con_curlines - viewwindowy - 1 - 10;
752 // NOTE: BIG HACK::SUBTRACT 10, SO THAT WATER DON'T COPY LINES OF THE CONSOLE
753 // WINDOW!!! (draw some more lines behind the bottom of the console)
754 if (con_clipviewtop < 0)
755 con_clipviewtop = -1; // maybe not necessary, provided it's < 0
756 }
757
758 // check if console ready for prompt
759 if (con_destlines >= minheight)
760 consoleready = true;
761 else
762 consoleready = false;
763
764 // make overlay messages disappear after a while
765 for (i = 0; i < con_hudlines; i++)
766 {
767 con_hudtime[i]--;
768 if (con_hudtime[i] < 0)
769 con_hudtime[i] = 0;
770 }
771
772 Unlock_state();
773 }
774
775 //
776 // ----
777 //
778 // Shortcuts for adding and deleting characters, strings, and sections
779 // Necessary due to moving cursor
780 //
781
CON_InputClear(void)782 static void CON_InputClear(void)
783 {
784 Lock_state();
785
786 memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
787 input_cur = input_sel = input_len = 0;
788
789 Unlock_state();
790 }
791
CON_InputSetString(const char * c)792 static void CON_InputSetString(const char *c)
793 {
794 Lock_state();
795
796 memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
797 strcpy(inputlines[inputline], c);
798 input_cur = input_sel = input_len = strlen(c);
799
800 Unlock_state();
801 }
802
CON_InputAddString(const char * c)803 static void CON_InputAddString(const char *c)
804 {
805 size_t csize = strlen(c);
806
807 Lock_state();
808
809 if (input_len + csize > CON_MAXPROMPTCHARS-1)
810 {
811 Unlock_state();
812 return;
813 }
814 if (input_cur != input_len)
815 memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
816 memcpy(&inputlines[inputline][input_cur], c, csize);
817 input_len += csize;
818 input_sel = (input_cur += csize);
819
820 Unlock_state();
821 }
822
CON_InputDelSelection(void)823 static void CON_InputDelSelection(void)
824 {
825 size_t start, end, len;
826
827 Lock_state();
828
829 if (!input_cur)
830 {
831 Unlock_state();
832 return;
833 }
834
835 if (input_cur > input_sel)
836 {
837 start = input_sel;
838 end = input_cur;
839 }
840 else
841 {
842 start = input_cur;
843 end = input_sel;
844 }
845 len = (end - start);
846
847 if (end != input_len)
848 memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
849 memset(&inputlines[inputline][input_len - len], 0, len);
850
851 input_len -= len;
852 input_sel = input_cur = start;
853
854 Unlock_state();
855 }
856
CON_InputAddChar(char c)857 static void CON_InputAddChar(char c)
858 {
859 if (input_len >= CON_MAXPROMPTCHARS-1)
860 return;
861
862 Lock_state();
863
864 if (input_cur != input_len)
865 memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
866 inputlines[inputline][input_cur++] = c;
867 inputlines[inputline][++input_len] = 0;
868 input_sel = input_cur;
869
870 Unlock_state();
871 }
872
CON_InputDelChar(void)873 static void CON_InputDelChar(void)
874 {
875 if (!input_cur)
876 return;
877
878 Lock_state();
879
880 if (input_cur != input_len)
881 memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
882 inputlines[inputline][--input_len] = 0;
883 input_sel = --input_cur;
884
885 Unlock_state();
886 }
887
888 //
889 // ----
890 //
891
892 // Handles console key input
893 //
CON_Responder(event_t * ev)894 boolean CON_Responder(event_t *ev)
895 {
896 static UINT8 consdown = false; // console is treated differently due to rare usage
897
898 // sequential completions a la 4dos
899 static char completion[80];
900
901 static INT32 skips;
902
903 static INT32 com_skips;
904 static INT32 var_skips;
905 static INT32 alias_skips;
906
907 const char *cmd = NULL;
908 INT32 key;
909
910 if (chat_on)
911 return false;
912
913 // let go keyup events, don't eat them
914 if (ev->type != ev_keydown && ev->type != ev_console)
915 {
916 if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
917 consdown = false;
918 return false;
919 }
920
921 key = ev->data1;
922
923 // check for console toggle key
924 if (ev->type != ev_console)
925 {
926 if (modeattacking || metalrecording || marathonmode)
927 return false;
928
929 if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1])
930 {
931 if (consdown) // ignore repeat
932 return true;
933 consoletoggle = true;
934 consdown = true;
935 return true;
936 }
937
938 // check other keys only if console prompt is active
939 if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
940 {
941 if (! menuactive && bindtable[key])
942 {
943 COM_BufAddText(bindtable[key]);
944 COM_BufAddText("\n");
945 return true;
946 }
947 return false;
948 }
949
950 // escape key toggle off console
951 if (key == KEY_ESCAPE)
952 {
953 consoletoggle = true;
954 return true;
955 }
956 }
957
958 // Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
959 if (key == KEY_LSHIFT || key == KEY_RSHIFT
960 || key == KEY_LCTRL || key == KEY_RCTRL
961 || key == KEY_LALT || key == KEY_RALT)
962 return true;
963
964 if (key == KEY_LEFTARROW)
965 {
966 if (input_cur != 0)
967 {
968 if (ctrldown)
969 input_cur = M_JumpWordReverse(inputlines[inputline], input_cur);
970 else
971 --input_cur;
972 }
973 if (!shiftdown)
974 input_sel = input_cur;
975 return true;
976 }
977 else if (key == KEY_RIGHTARROW)
978 {
979 if (input_cur < input_len)
980 {
981 if (ctrldown)
982 input_cur += M_JumpWord(&inputlines[inputline][input_cur]);
983 else
984 ++input_cur;
985 }
986 if (!shiftdown)
987 input_sel = input_cur;
988 return true;
989 }
990
991 // backspace and delete command prompt
992 if (input_sel != input_cur)
993 {
994 if (key == KEY_BACKSPACE || key == KEY_DEL)
995 {
996 CON_InputDelSelection();
997 return true;
998 }
999 }
1000 else if (key == KEY_BACKSPACE)
1001 {
1002 if (ctrldown)
1003 {
1004 input_sel = M_JumpWordReverse(inputlines[inputline], input_cur);
1005 CON_InputDelSelection();
1006 }
1007 else
1008 CON_InputDelChar();
1009 return true;
1010 }
1011 else if (key == KEY_DEL)
1012 {
1013 if (input_cur == input_len)
1014 return true;
1015
1016 if (ctrldown)
1017 {
1018 input_sel = input_cur + M_JumpWord(&inputlines[inputline][input_cur]);
1019 CON_InputDelSelection();
1020 }
1021 else
1022 {
1023 ++input_cur;
1024 CON_InputDelChar();
1025 }
1026 return true;
1027 }
1028
1029 // ctrl modifier -- changes behavior, adds shortcuts
1030 if (ctrldown)
1031 {
1032 // show all cvars/commands that match what we have inputted
1033 if (key == KEY_TAB)
1034 {
1035 size_t i, len;
1036
1037 if (!completion[0])
1038 {
1039 if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
1040 return true;
1041 strcpy(completion, inputlines[inputline]);
1042 }
1043 len = strlen(completion);
1044
1045 //first check commands
1046 CONS_Printf("\nCommands:\n");
1047 for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
1048 CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
1049 if (i == 0) CONS_Printf(" (none)\n");
1050
1051 //now we move on to CVARs
1052 CONS_Printf("Variables:\n");
1053 for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
1054 CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
1055 if (i == 0) CONS_Printf(" (none)\n");
1056
1057 //and finally aliases
1058 CONS_Printf("Aliases:\n");
1059 for (i = 0, cmd = COM_CompleteAlias(completion, i); cmd; cmd = COM_CompleteAlias(completion, ++i))
1060 CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
1061 if (i == 0) CONS_Printf(" (none)\n");
1062
1063 completion[0] = 0;
1064
1065 return true;
1066 }
1067 // ---
1068
1069 if (key == KEY_HOME) // oldest text in buffer
1070 {
1071 con_scrollup = (con_totallines-((con_curlines-16)>>3));
1072 return true;
1073 }
1074 else if (key == KEY_END) // most recent text in buffer
1075 {
1076 con_scrollup = 0;
1077 return true;
1078 }
1079
1080 if (key == 'x' || key == 'X')
1081 {
1082 if (input_sel > input_cur)
1083 I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
1084 else
1085 I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
1086 CON_InputDelSelection();
1087 completion[0] = 0;
1088 return true;
1089 }
1090 else if (key == 'c' || key == 'C')
1091 {
1092 if (input_sel > input_cur)
1093 I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
1094 else
1095 I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
1096 return true;
1097 }
1098 else if (key == 'v' || key == 'V')
1099 {
1100 const char *paste = I_ClipboardPaste();
1101 if (input_sel != input_cur)
1102 CON_InputDelSelection();
1103 if (paste != NULL)
1104 CON_InputAddString(paste);
1105 completion[0] = 0;
1106 return true;
1107 }
1108
1109 // Select all
1110 if (key == 'a' || key == 'A')
1111 {
1112 input_sel = 0;
1113 input_cur = input_len;
1114 return true;
1115 }
1116
1117 // ...why shouldn't it eat the key? if it doesn't, it just means you
1118 // can control Sonic from the console, which is silly
1119 return true;//return false;
1120 }
1121
1122 // command completion forward (tab) and backward (shift-tab)
1123 if (key == KEY_TAB)
1124 {
1125 // sequential command completion forward and backward
1126
1127 // remember typing for several completions (a-la-4dos)
1128 if (!completion[0])
1129 {
1130 if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
1131 return true;
1132 strcpy(completion, inputlines[inputline]);
1133 skips = 0;
1134 com_skips = 0;
1135 var_skips = 0;
1136 alias_skips = 0;
1137 }
1138 else
1139 {
1140 if (shiftdown)
1141 {
1142 if (skips > 0)
1143 skips--;
1144 }
1145 else
1146 {
1147 skips++;
1148 }
1149 }
1150
1151 if (skips <= com_skips)
1152 {
1153 cmd = COM_CompleteCommand(completion, skips);
1154
1155 if (cmd && skips == com_skips)
1156 {
1157 com_skips ++;
1158 var_skips ++;
1159 alias_skips++;
1160 }
1161 }
1162
1163 if (!cmd && skips <= var_skips)
1164 {
1165 cmd = CV_CompleteVar(completion, skips - com_skips);
1166
1167 if (cmd && skips == var_skips)
1168 {
1169 var_skips ++;
1170 alias_skips++;
1171 }
1172 }
1173
1174 if (!cmd && skips <= alias_skips)
1175 {
1176 cmd = COM_CompleteAlias(completion, skips - var_skips);
1177
1178 if (cmd && skips == alias_skips)
1179 {
1180 alias_skips++;
1181 }
1182 }
1183
1184 if (cmd)
1185 {
1186 CON_InputSetString(va("%s ", cmd));
1187 }
1188 else
1189 {
1190 skips--;
1191 }
1192
1193 return true;
1194 }
1195
1196 // move up (backward) in console textbuffer
1197 if (key == KEY_PGUP)
1198 {
1199 if (con_scrollup < (con_totallines-((con_curlines-16)>>3)))
1200 con_scrollup++;
1201 return true;
1202 }
1203 else if (key == KEY_PGDN)
1204 {
1205 if (con_scrollup > 0)
1206 con_scrollup--;
1207 return true;
1208 }
1209 else if (key == KEY_HOME)
1210 {
1211 input_cur = 0;
1212 if (!shiftdown)
1213 input_sel = input_cur;
1214 return true;
1215 }
1216 else if (key == KEY_END)
1217 {
1218 input_cur = input_len;
1219 if (!shiftdown)
1220 input_sel = input_cur;
1221 return true;
1222 }
1223
1224 // At this point we're messing with input
1225 // Clear completion
1226 completion[0] = 0;
1227
1228 // command enter
1229 if (key == KEY_ENTER)
1230 {
1231 if (!input_len)
1232 return true;
1233
1234 // push the command
1235 COM_BufAddText(inputlines[inputline]);
1236 COM_BufAddText("\n");
1237
1238 CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
1239
1240 inputline = (inputline+1) & 31;
1241 inputhist = inputline;
1242 CON_InputClear();
1243
1244 return true;
1245 }
1246
1247 // move back in input history
1248 if (key == KEY_UPARROW)
1249 {
1250 // copy one of the previous inputlines to the current
1251 do
1252 inputhist = (inputhist - 1) & 31; // cycle back
1253 while (inputhist != inputline && !inputlines[inputhist][0]);
1254
1255 // stop at the last history input line, which is the
1256 // current line + 1 because we cycle through the 32 input lines
1257 if (inputhist == inputline)
1258 inputhist = (inputline + 1) & 31;
1259
1260 CON_InputSetString(inputlines[inputhist]);
1261 return true;
1262 }
1263
1264 // move forward in input history
1265 if (key == KEY_DOWNARROW)
1266 {
1267 if (inputhist == inputline)
1268 return true;
1269 do
1270 inputhist = (inputhist + 1) & 31;
1271 while (inputhist != inputline && !inputlines[inputhist][0]);
1272
1273 // back to currentline
1274 if (inputhist == inputline)
1275 CON_InputClear();
1276 else
1277 CON_InputSetString(inputlines[inputhist]);
1278 return true;
1279 }
1280
1281 // allow people to use keypad in console (good for typing IP addresses) - Calum
1282 if (key >= KEY_KEYPAD7 && key <= KEY_KPADDEL)
1283 {
1284 char keypad_translation[] = {'7','8','9','-',
1285 '4','5','6','+',
1286 '1','2','3',
1287 '0','.'};
1288
1289 key = keypad_translation[key - KEY_KEYPAD7];
1290 }
1291 else if (key == KEY_KPADSLASH)
1292 key = '/';
1293
1294 if (key >= 'a' && key <= 'z')
1295 {
1296 if (capslock ^ shiftdown)
1297 key = shiftxform[key];
1298 }
1299 else if (shiftdown)
1300 key = shiftxform[key];
1301
1302 // enter a char into the command prompt
1303 if (key < 32 || key > 127)
1304 return true;
1305
1306 if (input_sel != input_cur)
1307 CON_InputDelSelection();
1308 CON_InputAddChar(key);
1309
1310 return true;
1311 }
1312
1313 // Insert a new line in the console text buffer
1314 //
CON_Linefeed(void)1315 static void CON_Linefeed(void)
1316 {
1317 // set time for heads up messages
1318 con_hudtime[con_cy%con_hudlines] = cons_msgtimeout.value*TICRATE;
1319
1320 con_cy++;
1321 con_cx = 0;
1322
1323 con_line = &con_buffer[(con_cy%con_totallines)*con_width];
1324 memset(con_line, ' ', con_width);
1325
1326 // make sure the view borders are refreshed if hud messages scroll
1327 con_hudupdate = true; // see HU_Erase()
1328 }
1329
1330 // Outputs text into the console text buffer
CON_Print(char * msg)1331 static void CON_Print(char *msg)
1332 {
1333 size_t l;
1334 INT32 controlchars = 0; // for color changing
1335 char color = '\x80'; // keep color across lines
1336
1337 if (msg == NULL)
1338 return;
1339
1340 if (*msg == '\3') // chat text, makes ding sound
1341 S_StartSound(NULL, sfx_radio);
1342 else if (*msg == '\4') // chat action, dings and is in yellow
1343 {
1344 *msg = '\x82'; // yellow
1345 S_StartSound(NULL, sfx_radio);
1346 }
1347
1348 Lock_state();
1349
1350 if (!(*msg & 0x80))
1351 {
1352 con_line[con_cx++] = '\x80';
1353 controlchars = 1;
1354 }
1355
1356 while (*msg)
1357 {
1358 // skip non-printable characters and white spaces
1359 while (*msg && *msg <= ' ')
1360 {
1361 if (*msg & 0x80)
1362 {
1363 color = con_line[con_cx++] = *(msg++);
1364 controlchars++;
1365 continue;
1366 }
1367 else if (*msg == '\r') // carriage return
1368 {
1369 con_cy--;
1370 CON_Linefeed();
1371 color = '\x80';
1372 controlchars = 0;
1373 }
1374 else if (*msg == '\n') // linefeed
1375 {
1376 CON_Linefeed();
1377 con_line[con_cx++] = color;
1378 controlchars = 1;
1379 }
1380 else if (*msg == ' ') // space
1381 {
1382 con_line[con_cx++] = ' ';
1383 if (con_cx - controlchars >= con_width-11)
1384 {
1385 CON_Linefeed();
1386 con_line[con_cx++] = color;
1387 controlchars = 1;
1388 }
1389 }
1390 else if (*msg == '\t')
1391 {
1392 // adds tab spaces for nice layout in console
1393
1394 do
1395 {
1396 con_line[con_cx++] = ' ';
1397 } while ((con_cx - controlchars) % 4 != 0);
1398
1399 if (con_cx - controlchars >= con_width-11)
1400 {
1401 CON_Linefeed();
1402 con_line[con_cx++] = color;
1403 controlchars = 1;
1404 }
1405 }
1406 msg++;
1407 }
1408
1409 if (*msg == '\0')
1410 {
1411 Unlock_state();
1412 return;
1413 }
1414
1415 // printable character
1416 for (l = 0; l < (con_width-11) && msg[l] > ' '; l++)
1417 ;
1418
1419 // word wrap
1420 if ((con_cx - controlchars) + l > con_width-11)
1421 {
1422 CON_Linefeed();
1423 con_line[con_cx++] = color;
1424 controlchars = 1;
1425 }
1426
1427 // a word at a time
1428 for (; l > 0; l--)
1429 con_line[con_cx++] = *(msg++);
1430 }
1431
1432 Unlock_state();
1433 }
1434
CON_LogMessage(const char * msg)1435 void CON_LogMessage(const char *msg)
1436 {
1437 char txt[8192], *t;
1438 const char *p = msg, *e = txt+sizeof (txt)-2;
1439
1440 for (t = txt; *p != '\0'; p++)
1441 {
1442 if (*p == '\n' || *p >= ' ') // don't log or console print CON_Print's control characters
1443 *t++ = *p;
1444
1445 if (t >= e)
1446 {
1447 *t = '\0'; //end of string
1448 I_OutputMsg("%s", txt); //print string
1449 t = txt; //reset t pointer
1450 memset(txt,'\0', sizeof (txt)); //reset txt
1451 }
1452 }
1453 *t = '\0'; //end of string
1454 I_OutputMsg("%s", txt);
1455 }
1456
1457 // Console print! Wahooo! Lots o fun!
1458 //
1459
CONS_Printf(const char * fmt,...)1460 void CONS_Printf(const char *fmt, ...)
1461 {
1462 va_list argptr;
1463 static char *txt = NULL;
1464 boolean refresh;
1465
1466 if (txt == NULL)
1467 txt = malloc(8192);
1468
1469 va_start(argptr, fmt);
1470 vsprintf(txt, fmt, argptr);
1471 va_end(argptr);
1472
1473 // echo console prints to log file
1474 DEBFILE(txt);
1475
1476 // write message in con text buffer
1477 if (con_started)
1478 CON_Print(txt);
1479
1480 CON_LogMessage(txt);
1481
1482 Lock_state();
1483
1484 // make sure new text is visible
1485 con_scrollup = 0;
1486 refresh = con_refresh;
1487
1488 Unlock_state();
1489
1490 // if not in display loop, force screen update
1491 if (refresh)
1492 {
1493 CON_Drawer(); // here we display the console text
1494 I_FinishUpdate(); // page flip or blit buffer
1495 }
1496 }
1497
CONS_Alert(alerttype_t level,const char * fmt,...)1498 void CONS_Alert(alerttype_t level, const char *fmt, ...)
1499 {
1500 va_list argptr;
1501 static char *txt = NULL;
1502
1503 if (txt == NULL)
1504 txt = malloc(8192);
1505
1506 va_start(argptr, fmt);
1507 vsprintf(txt, fmt, argptr);
1508 va_end(argptr);
1509
1510 switch (level)
1511 {
1512 case CONS_NOTICE:
1513 // no notice for notices, hehe
1514 CONS_Printf("\x83" "%s" "\x80 ", M_GetText("NOTICE:"));
1515 break;
1516 case CONS_WARNING:
1517 refreshdirmenu |= REFRESHDIR_WARNING;
1518 CONS_Printf("\x82" "%s" "\x80 ", M_GetText("WARNING:"));
1519 break;
1520 case CONS_ERROR:
1521 refreshdirmenu |= REFRESHDIR_ERROR;
1522 CONS_Printf("\x85" "%s" "\x80 ", M_GetText("ERROR:"));
1523 break;
1524 }
1525
1526 // I am lazy and I feel like just letting CONS_Printf take care of things.
1527 // Is that okay?
1528 CONS_Printf("%s", txt);
1529 }
1530
CONS_Debug(INT32 debugflags,const char * fmt,...)1531 void CONS_Debug(INT32 debugflags, const char *fmt, ...)
1532 {
1533 va_list argptr;
1534 static char *txt = NULL;
1535
1536 if ((cv_debug & debugflags) != debugflags)
1537 return;
1538
1539 if (txt == NULL)
1540 txt = malloc(8192);
1541
1542 va_start(argptr, fmt);
1543 vsprintf(txt, fmt, argptr);
1544 va_end(argptr);
1545
1546 // Again I am lazy, oh well
1547 CONS_Printf("%s", txt);
1548 }
1549
1550
1551 // Print an error message, and wait for ENTER key to continue.
1552 // To make sure the user has seen the message
1553 //
CONS_Error(const char * msg)1554 void CONS_Error(const char *msg)
1555 {
1556 #if defined(RPC_NO_WINDOWS_H) && defined(_WINDOWS)
1557 if (!graphics_started)
1558 {
1559 MessageBoxA(vid.WndParent, msg, "SRB2 Warning", MB_OK);
1560 return;
1561 }
1562 #endif
1563 CONS_Printf("\x82%s", msg); // write error msg in different colour
1564 CONS_Printf(M_GetText("Press ENTER to continue\n"));
1565
1566 // dirty quick hack, but for the good cause
1567 while (I_GetKey() != KEY_ENTER)
1568 I_OsPolling();
1569 }
1570
1571 //======================================================================
1572 // CONSOLE DRAW
1573 //======================================================================
1574
1575 // draw console prompt line
1576 //
CON_DrawInput(void)1577 static void CON_DrawInput(void)
1578 {
1579 INT32 charwidth = (INT32)con_scalefactor << 3;
1580 const char *p = inputlines[inputline];
1581 size_t c, clen, cend;
1582 UINT8 lellip = 0, rellip = 0;
1583 INT32 x, y, i;
1584
1585 y = con_curlines - 12 * con_scalefactor;
1586 x = charwidth*2;
1587
1588 clen = con_width-13;
1589
1590 if (input_len <= clen)
1591 {
1592 c = 0;
1593 clen = input_len;
1594 }
1595 else // input line scrolls left if it gets too long
1596 {
1597 clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
1598
1599 if (input_cur <= clen/2)
1600 {
1601 // Close enough to right edge to show all
1602 c = 0;
1603 // Always will truncate right side from this position, so always draw right ellipsis
1604 rellip = 1;
1605 }
1606 else
1607 {
1608 // Cursor in the middle (or right side) of input
1609 // Move over for the ellipsis
1610 c = input_cur - (clen/2) + 2;
1611 x += charwidth*2;
1612 lellip = 1;
1613
1614 if (c + clen >= input_len)
1615 {
1616 // Cursor in the right side of input
1617 // We were too far over, so move back
1618 c = input_len - clen;
1619 }
1620 else
1621 {
1622 // Cursor in the middle -- ellipses on both sides
1623 clen -= 2;
1624 rellip = 1;
1625 }
1626 }
1627 }
1628
1629 if (lellip)
1630 {
1631 x -= charwidth*3;
1632 if (input_sel < c)
1633 V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
1634 for (i = 0; i < 3; ++i, x += charwidth)
1635 V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
1636 }
1637 else
1638 V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
1639
1640 for (cend = c + clen; c < cend; ++c, x += charwidth)
1641 {
1642 if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
1643 {
1644 V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
1645 V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, true);
1646 }
1647 else
1648 V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, true);
1649
1650 if (c == input_cur && con_tick >= 4)
1651 V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
1652 }
1653 if (cend == input_cur && con_tick >= 4)
1654 V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
1655 if (rellip)
1656 {
1657 if (input_sel > cend)
1658 V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
1659 for (i = 0; i < 3; ++i, x += charwidth)
1660 V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
1661 }
1662 }
1663
1664 // draw the last lines of console text to the top of the screen
CON_DrawHudlines(void)1665 static void CON_DrawHudlines(void)
1666 {
1667 UINT8 *p;
1668 size_t i;
1669 INT32 y;
1670 INT32 charflags = 0;
1671 INT32 charwidth = 8 * con_scalefactor;
1672 INT32 charheight = 8 * con_scalefactor;
1673
1674 if (con_hudlines <= 0)
1675 return;
1676
1677 if (chat_on && OLDCHAT)
1678 y = charheight; // leave place for chat input in the first row of text (only do it if consolechat is on.)
1679 else
1680 y = 0;
1681
1682 for (i = con_cy - con_hudlines+1; i <= con_cy; i++)
1683 {
1684 size_t c;
1685 INT32 x;
1686
1687 if ((signed)i < 0)
1688 continue;
1689 if (con_hudtime[i%con_hudlines] == 0)
1690 continue;
1691
1692 p = (UINT8 *)&con_buffer[(i%con_totallines)*con_width];
1693
1694 for (c = 0, x = 0; c < con_width; c++, x += charwidth, p++)
1695 {
1696 while (*p & 0x80) // Graue 06-19-2004
1697 {
1698 charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
1699 p++;
1700 }
1701 if (*p < HU_FONTSTART)
1702 ;//charwidth = 4 * con_scalefactor;
1703 else
1704 {
1705 //charwidth = (hu_font['A'-HU_FONTSTART]->width) * con_scalefactor;
1706 V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
1707 }
1708 }
1709
1710 //V_DrawCharacter(x, y, (p[c]&0xff) | cv_constextsize.value | V_NOSCALESTART, true);
1711 y += charheight;
1712 }
1713
1714 // top screen lines that might need clearing when view is reduced
1715 con_clearlines = y; // this is handled by HU_Erase();
1716 }
1717
1718 // Lactozilla: Draws the console's background picture.
CON_DrawBackpic(void)1719 static void CON_DrawBackpic(void)
1720 {
1721 patch_t *con_backpic;
1722 lumpnum_t piclump;
1723 int x, w, h;
1724
1725 // Get the lumpnum for CONSBACK, STARTUP (Only during game startup) or fallback into MISSING.
1726 if (con_startup)
1727 piclump = W_CheckNumForName("STARTUP");
1728 else
1729 piclump = W_CheckNumForName("CONSBACK");
1730
1731 if (piclump == LUMPERROR)
1732 piclump = W_GetNumForName("MISSING");
1733
1734 // Cache the patch.
1735 con_backpic = W_CachePatchNum(piclump, PU_PATCH);
1736
1737 // Center the backpic, and draw a vertically cropped patch.
1738 w = (con_backpic->width * vid.dupx);
1739 x = (vid.width / 2) - (w / 2);
1740 h = con_curlines/vid.dupy;
1741
1742 // If the patch doesn't fill the entire screen,
1743 // then fill the sides with a solid color.
1744 if (x > 0)
1745 {
1746 column_t *column = (column_t *)((UINT8 *)(con_backpic->columns) + (con_backpic->columnofs[0]));
1747 if (!column->topdelta)
1748 {
1749 UINT8 *source = (UINT8 *)(column) + 3;
1750 INT32 color = (source[0] | V_NOSCALESTART);
1751 // left side
1752 V_DrawFill(0, 0, x, con_curlines, color);
1753 // right side
1754 V_DrawFill((x + w), 0, (vid.width - w), con_curlines, color);
1755 }
1756 }
1757
1758 // Draw the patch.
1759 V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, V_NOSCALESTART, con_backpic,
1760 0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h);
1761
1762 // Unlock the cached patch.
1763 W_UnlockCachedPatch(con_backpic);
1764 }
1765
1766 // draw the console background, text, and prompt if enough place
1767 //
CON_DrawConsole(void)1768 static void CON_DrawConsole(void)
1769 {
1770 UINT8 *p;
1771 size_t i;
1772 INT32 y;
1773 INT32 charflags = 0;
1774 INT32 charwidth = (INT32)con_scalefactor << 3;
1775 INT32 charheight = charwidth;
1776 INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4
1777
1778 if (con_curlines <= 0)
1779 return;
1780
1781 //FIXME: refresh borders only when console bg is translucent
1782 con_clearlines = con_curlines; // clear console draw from view borders
1783 con_hudupdate = true; // always refresh while console is on
1784
1785 // draw console background
1786 if (cons_backpic.value || con_forcepic)
1787 CON_DrawBackpic();
1788 else
1789 {
1790 // inu: no more width (was always 0 and vid.width)
1791 if (rendermode != render_none)
1792 V_DrawFadeConsBack(con_curlines); // translucent background
1793 }
1794
1795 // draw console text lines from top to bottom
1796 if (con_curlines < minheight)
1797 return;
1798
1799 i = con_cy - con_scrollup;
1800
1801 // skip the last empty line due to the cursor being at the start of a new line
1802 i--;
1803
1804 i -= (con_curlines - minheight) / charheight;
1805
1806 if (rendermode == render_none) return;
1807
1808 for (y = (con_curlines-minheight) % charheight; y <= con_curlines-minheight; y += charheight, i++)
1809 {
1810 INT32 x;
1811 size_t c;
1812
1813 p = (UINT8 *)&con_buffer[((i > 0 ? i : 0)%con_totallines)*con_width];
1814
1815 for (c = 0, x = charwidth; c < con_width; c++, x += charwidth, p++)
1816 {
1817 while (*p & 0x80)
1818 {
1819 charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
1820 p++;
1821 }
1822 V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
1823 }
1824 }
1825
1826 // draw prompt if enough place (not while game startup)
1827 if ((con_curlines == con_destlines) && (con_curlines >= minheight) && !con_startup)
1828 CON_DrawInput();
1829 }
1830
1831 // Console refresh drawer, call each frame
1832 //
CON_Drawer(void)1833 void CON_Drawer(void)
1834 {
1835 Lock_state();
1836
1837 if (!con_started || !graphics_started)
1838 {
1839 Unlock_state();
1840 return;
1841 }
1842
1843 if (con_recalc)
1844 {
1845 CON_RecalcSize();
1846 if (con_curlines <= 0)
1847 CON_ClearHUD();
1848 }
1849
1850 if (con_curlines > 0)
1851 CON_DrawConsole();
1852 else if (gamestate == GS_LEVEL
1853 || gamestate == GS_INTERMISSION || gamestate == GS_ENDING || gamestate == GS_CUTSCENE
1854 || gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
1855 CON_DrawHudlines();
1856
1857 Unlock_state();
1858 }
1859