1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (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 // DESCRIPTION:  Heads-up displays
16 //
17 
18 
19 #include <ctype.h>
20 
21 #include "doomdef.h"
22 #include "doomkeys.h"
23 
24 #include "z_zone.h"
25 
26 #include "deh_main.h"
27 #include "i_input.h"
28 #include "i_swap.h"
29 #include "i_video.h"
30 
31 #include "hu_stuff.h"
32 #include "hu_lib.h"
33 #include "m_controls.h"
34 #include "m_misc.h"
35 #include "w_wad.h"
36 
37 #include "s_sound.h"
38 
39 #include "doomstat.h"
40 
41 // Data.
42 #include "dstrings.h"
43 #include "sounds.h"
44 
45 //
46 // Locally used constants, shortcuts.
47 //
48 #define HU_TITLE        (mapnames[gamemap-1])
49 #define HU_TITLEHEIGHT  1
50 #define HU_TITLEX       0
51 
52 // haleyjd 09/01/10: [STRIFE] 167 -> 160 to move up level name
53 #define HU_TITLEY       (160 - SHORT(hu_font[0]->height))
54 
55 #define HU_INPUTTOGGLE  't'
56 #define HU_INPUTX       HU_MSGX
57 #define HU_INPUTY       (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1))
58 #define HU_INPUTWIDTH   64
59 #define HU_INPUTHEIGHT  1
60 
61 char *chat_macros[10] =
62 {
63     HUSTR_CHATMACRO0,
64     HUSTR_CHATMACRO1,
65     HUSTR_CHATMACRO2,
66     HUSTR_CHATMACRO3,
67     HUSTR_CHATMACRO4,
68     HUSTR_CHATMACRO5,
69     HUSTR_CHATMACRO6,
70     HUSTR_CHATMACRO7,
71     HUSTR_CHATMACRO8,
72     HUSTR_CHATMACRO9
73 };
74 
75 // villsa [STRIFE]
76 char player_names[8][16] =
77 {
78     "1: ",
79     "2: ",
80     "3: ",
81     "4: ",
82     "5: ",
83     "6: ",
84     "7: ",
85     "8: "
86 };
87 
88 char                    chat_char; // remove later.
89 static player_t*        plr;
90 patch_t*                hu_font[HU_FONTSIZE];
91 patch_t*                yfont[HU_FONTSIZE];   // haleyjd 09/18/10: [STRIFE]
92 static hu_textline_t    w_title;
93 boolean                 chat_on;
94 static hu_itext_t       w_chat;
95 static boolean          always_off = false;
96 static char             chat_dest[MAXPLAYERS];
97 static hu_itext_t       w_inputbuffer[MAXPLAYERS];
98 
99 static boolean          message_on;
100 boolean                 message_dontfuckwithme;
101 static boolean          message_nottobefuckedwith;
102 
103 static hu_stext_t       w_message;
104 static int              message_counter;
105 
106 //extern int              showMessages; [STRIFE] no such variable
107 
108 static boolean          headsupactive = false;
109 
110 // haleyjd 20130915 [STRIFE]: need nickname
111 extern char *nickname;
112 
113 // haleyjd 20130915 [STRIFE]: true if setting nickname
114 static boolean hu_setting_name = false;
115 
116 //
117 // Builtin map names.
118 // The actual names can be found in DStrings.h.
119 //
120 
121 // haleyjd 08/31/10: [STRIFE] Changed for Strife level names.
122 // List of names for levels.
123 
124 char *mapnames[] =
125 {
126     // Strife map names
127 
128     // First "episode" - Quest to destroy the Order's Castle
129     HUSTR_1,
130     HUSTR_2,
131     HUSTR_3,
132     HUSTR_4,
133     HUSTR_5,
134     HUSTR_6,
135     HUSTR_7,
136     HUSTR_8,
137     HUSTR_9,
138 
139     // Second "episode" - Kill the Bishop and Make a Choice
140     HUSTR_10,
141     HUSTR_11,
142     HUSTR_12,
143     HUSTR_13,
144     HUSTR_14,
145     HUSTR_15,
146     HUSTR_16,
147     HUSTR_17,
148     HUSTR_18,
149     HUSTR_19,
150 
151     // Third "episode" - Shut down Factory, kill Loremaster and Entity
152     HUSTR_20,
153     HUSTR_21,
154     HUSTR_22,
155     HUSTR_23,
156     HUSTR_24,
157     HUSTR_25,
158     HUSTR_26,
159     HUSTR_27,
160     HUSTR_28,
161     HUSTR_29,
162 
163     // "Secret" levels - Abandoned Base and Training Facility
164     HUSTR_30,
165     HUSTR_31,
166 
167     // Demo version maps
168     HUSTR_32,
169     HUSTR_33,
170     HUSTR_34
171 };
172 
173 //
174 // HU_Init
175 //
176 // haleyjd 09/18/10: [STRIFE]
177 // * Modified to load yfont along with hu_font.
178 //
HU_Init(void)179 void HU_Init(void)
180 {
181     int		i;
182     int		j;
183     char	buffer[9];
184 
185     // load the heads-up font
186     j = HU_FONTSTART;
187     for (i=0;i<HU_FONTSIZE;i++)
188     {
189         DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
190         hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
191 
192         // haleyjd 09/18/10: load yfont as well; and yes, this is exactly
193         // how Rogue did it :P
194         buffer[2] = 'B';
195         yfont[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
196     }
197 }
198 
199 //
200 // HU_Stop
201 //
202 // [STRIFE] Verified unmodified.
203 //
HU_Stop(void)204 void HU_Stop(void)
205 {
206     headsupactive = false;
207 }
208 
209 //
210 // HU_Start
211 //
212 // haleyjd 09/18/10: [STRIFE] Added a hack for nickname at the end.
213 //
HU_Start(void)214 void HU_Start(void)
215 {
216     int         i;
217     char*       s;
218 
219     // haleyjd 20120211: [STRIFE] not called here.
220     //if (headsupactive)
221     //    HU_Stop();
222 
223     // haleyjd 20120211: [STRIFE] moved up
224     // create the map title widget
225     HUlib_initTextLine(&w_title,
226                        HU_TITLEX, HU_TITLEY,
227                        hu_font,
228                        HU_FONTSTART);
229 
230     // haleyjd 08/31/10: [STRIFE] Get proper map name.
231     s = HU_TITLE;
232 
233     // [STRIFE] Removed Chex Quest stuff.
234 
235     // dehacked substitution to get modified level name
236     s = DEH_String(s);
237 
238     while (*s)
239         HUlib_addCharToTextLine(&w_title, *(s++));
240 
241     // haleyjd 20120211: [STRIFE] check for headsupactive
242     if(!headsupactive)
243     {
244         plr = &players[consoleplayer];
245         message_on = false;
246         message_dontfuckwithme = false;
247         message_nottobefuckedwith = false;
248         chat_on = false;
249 
250         // create the message widget
251         HUlib_initSText(&w_message,
252                         HU_MSGX, HU_MSGY, HU_MSGHEIGHT,
253                         hu_font,
254                         HU_FONTSTART, &message_on);
255 
256         // create the chat widget
257         HUlib_initIText(&w_chat,
258                         HU_INPUTX, HU_INPUTY,
259                         hu_font,
260                         HU_FONTSTART, &chat_on);
261 
262         // create the inputbuffer widgets
263         for (i=0 ; i<MAXPLAYERS ; i++)
264             HUlib_initIText(&w_inputbuffer[i], 0, 0, 0, 0, &always_off);
265 
266         headsupactive = true;
267 
268         // haleyjd 09/18/10: [STRIFE] nickname weirdness.
269         if(nickname != player_names[consoleplayer])
270         {
271             if(nickname != NULL && *nickname)
272             {
273                 DEH_printf("have one\n");
274                 nickname = player_names[consoleplayer];
275             }
276         }
277     }
278 }
279 
280 //
281 // HU_Drawer
282 //
283 // [STRIFE] Verified unmodified.
284 //
HU_Drawer(void)285 void HU_Drawer(void)
286 {
287     HUlib_drawSText(&w_message);
288     HUlib_drawIText(&w_chat);
289     if (automapactive)
290         HUlib_drawTextLine(&w_title, false);
291 }
292 
293 //
294 // HU_Erase
295 //
296 // [STRIFE] Verified unmodified.
297 //
HU_Erase(void)298 void HU_Erase(void)
299 {
300     HUlib_eraseSText(&w_message);
301     HUlib_eraseIText(&w_chat);
302     HUlib_eraseTextLine(&w_title);
303 }
304 
305 //
306 // HU_addMessage
307 //
308 // haleyjd 09/18/10: [STRIFE] New function
309 // See if you can tell whether or not I had trouble with this :P
310 // Looks to be extremely buggy, hackish, and error-prone.
311 //
312 // <Markov> This is definitely not the best that Rogue had to offer. Markov.
313 //
314 //  Fastcall Registers:   edx          ebx
315 //      Temp Registers:   esi          edi
HU_addMessage(char * prefix,char * message)316 void HU_addMessage(char *prefix, char *message)
317 {
318     char  c;         // eax
319     int   width = 0; // edx
320     char *rover1;    // ebx (in first loop)
321     char *rover2;    // ecx (in second loop)
322     char *bufptr;    // ebx (in second loop)
323     char buffer[HU_MAXLINELENGTH+2];  // esp+52h
324 
325     // Loop 1: Total up width of prefix.
326     rover1 = prefix;
327     if(rover1)
328     {
329         while((c = *rover1))
330         {
331             c = toupper(c) - HU_FONTSTART;
332             ++rover1;
333 
334             if(c < 0 || c >= HU_FONTSIZE)
335                 width += 4;
336             else
337                 width += SHORT(hu_font[(int) c]->width);
338         }
339     }
340 
341     // Loop 2: Copy as much of message into buffer as will fit on screen
342     bufptr = buffer;
343     rover2 = message;
344     while((c = *rover2))
345     {
346         if((c == ' ' || c == '-') && width > 285)
347             break;
348 
349         *bufptr = c;
350         ++bufptr;       // BUG: No check for overflow.
351         ++rover2;
352         c = toupper(c);
353 
354         if(c == ' ' || c < '!' || c >= '_')
355             width += 4;
356         else
357         {
358             c -= HU_FONTSTART;
359             width += SHORT(hu_font[(int) c]->width);
360         }
361     }
362 
363     // Too big to fit?
364     // BUG: doesn't consider by how much it's over.
365     if(width > 320)
366     {
367         // backup a char... hell if I know why.
368         --bufptr;
369         --rover2;
370     }
371 
372     // rover2 is not at the end?
373     if((c = *rover2))
374     {
375         // if not ON a space...
376         if(c != ' ')
377         {
378             // back up both pointers til one is found.
379             // BUG: no check against LHS of buffer. Hurr!
380             while(*bufptr != ' ')
381             {
382                 --bufptr;
383                 --rover2;
384             }
385         }
386     }
387 
388     *bufptr = '\0';
389 
390     // Add two message lines.
391     HUlib_addMessageToSText(&w_message, prefix, buffer);
392     HUlib_addMessageToSText(&w_message, NULL,   rover2);
393 }
394 
395 //
396 // HU_Ticker
397 //
398 // haleyjd 09/18/10: [STRIFE] Changes to split up message into two lines,
399 // and support for player names (STRIFE-TODO: unfinished!)
400 //
HU_Ticker(void)401 void HU_Ticker(void)
402 {
403     int i, rc;
404     char c;
405     //char *prefix;  STRIFE-TODO
406 
407     // tick down message counter if message is up
408     if (message_counter && !--message_counter)
409     {
410         message_on = false;
411         message_nottobefuckedwith = false;
412     }
413 
414     // haleyjd 20110219: [STRIFE] this condition was removed
415     //if (showMessages || message_dontfuckwithme)
416     //{
417         // display message if necessary
418         if ((plr->message && !message_nottobefuckedwith)
419             || (plr->message && message_dontfuckwithme))
420         {
421             //HUlib_addMessageToSText(&w_message, 0, plr->message);
422             HU_addMessage(NULL, plr->message); // haleyjd [STRIFE]
423             plr->message = 0;
424             message_on = true;
425             message_counter = HU_MSGTIMEOUT;
426             message_nottobefuckedwith = message_dontfuckwithme;
427             message_dontfuckwithme = 0;
428         }
429     //} // else message_on = false;
430 
431     // check for incoming chat characters
432     if (netgame)
433     {
434         for (i=0 ; i<MAXPLAYERS; i++)
435         {
436             if (!playeringame[i])
437                 continue;
438             if (i != consoleplayer
439                 && (c = players[i].cmd.chatchar))
440             {
441                 if (c <= HU_CHANGENAME) // [STRIFE]: allow HU_CHANGENAME here
442                     chat_dest[i] = c;
443                 else
444                 {
445                     rc = HUlib_keyInIText(&w_inputbuffer[i], c);
446                     if (rc && c == KEY_ENTER)
447                     {
448                         if (w_inputbuffer[i].l.len
449                             && (chat_dest[i] == consoleplayer+1
450                              || chat_dest[i] == HU_BROADCAST))
451                         {
452                             HU_addMessage(player_names[i],
453                                           w_inputbuffer[i].l.l);
454 
455                             message_nottobefuckedwith = true;
456                             message_on = true;
457                             message_counter = HU_MSGTIMEOUT;
458                             S_StartSound(0, sfx_radio);
459                         }
460                         else if(chat_dest[i] == HU_CHANGENAME)
461                         {
462                             // haleyjd 20130915 [STRIFE]: set player name
463                             DEH_snprintf(player_names[i], sizeof(player_names[i]),
464                                          "%.13s: ", w_inputbuffer[i].l.l);
465                         }
466                         HUlib_resetIText(&w_inputbuffer[i]);
467                     }
468                 }
469                 players[i].cmd.chatchar = 0;
470             }
471         }
472     }
473 }
474 
475 #define QUEUESIZE		128
476 
477 static char	chatchars[QUEUESIZE];
478 static int	head = 0;
479 static int	tail = 0;
480 
481 //
482 // HU_queueChatChar
483 //
484 // haleyjd 09/18/10: [STRIFE]
485 // * No message is given if a chat queue overflow occurs.
486 //
HU_queueChatChar(char c)487 void HU_queueChatChar(char c)
488 {
489     chatchars[head] = c;
490     if (((head + 1) & (QUEUESIZE-1)) != tail)
491     {
492         head = (head + 1) & (QUEUESIZE-1);
493     }
494 }
495 
496 //
497 // HU_dequeueChatChar
498 //
499 // [STRIFE] Verified unmodified.
500 //
HU_dequeueChatChar(void)501 char HU_dequeueChatChar(void)
502 {
503     char c;
504 
505     if (head != tail)
506     {
507         c = chatchars[tail];
508         tail = (tail + 1) & (QUEUESIZE-1);
509     }
510     else
511     {
512         c = 0;
513     }
514 
515     return c;
516 }
517 
518 // fraggle 01/05/15: New functions to support the Chocolate input interface.
StartChatInput(void)519 static void StartChatInput(void)
520 {
521     chat_on = true;
522     I_StartTextInput(HU_INPUTX, HU_INPUTY, SCREENWIDTH, HU_INPUTY + 8);
523 }
524 
StopChatInput(void)525 static void StopChatInput(void)
526 {
527     chat_on = false;
528     I_StopTextInput();
529 }
530 
531 //
532 // HU_Responder
533 //
534 // haleyjd 09/18/10: [STRIFE]
535 // * Mostly unmodified, except:
536 //   - The default value of key_message_refresh is changed. That is handled
537 //     elsewhere in Choco, however.
538 //   - There is support for setting the player name through the chat
539 //     mechanism.
540 //
HU_Responder(event_t * ev)541 boolean HU_Responder(event_t *ev)
542 {
543     static char         lastmessage[HU_MAXLINELENGTH+1];
544     char*               macromessage;
545     boolean             eatkey = false;
546     static boolean      altdown = false;
547     unsigned char       c;
548     int                 i;
549     int                 numplayers;
550 
551     static int          num_nobrainers = 0;
552 
553     numplayers = 0;
554     for (i=0 ; i<MAXPLAYERS ; i++)
555         numplayers += playeringame[i];
556 
557     if (ev->data1 == KEY_RSHIFT)
558     {
559         return false;
560     }
561     else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT)
562     {
563         altdown = ev->type == ev_keydown;
564         return false;
565     }
566 
567     if (ev->type != ev_keydown)
568         return false;
569 
570     if (!chat_on)
571     {
572         if (ev->data1 == key_message_refresh)
573         {
574             message_on = true;
575             message_counter = HU_MSGTIMEOUT;
576             eatkey = true;
577         }
578         else if (netgame && ev->data2 == key_multi_msg)
579         {
580             StartChatInput();
581             eatkey = true;
582             HUlib_resetIText(&w_chat);
583             HU_queueChatChar(HU_BROADCAST);
584         }
585         // [STRIFE]: You cannot go straight to chatting with a particular
586         // player from here... you must press 't' first. See below.
587     }
588     else
589     {
590         c = ev->data3;
591         // send a macro
592         if (altdown)
593         {
594             c = c - '0';
595             if (c > 9)
596                 return false;
597             // fprintf(stderr, "got here\n");
598             macromessage = chat_macros[c];
599 
600             // kill last message with a '\n'
601             HU_queueChatChar(KEY_ENTER); // DEBUG!!!
602 
603             // send the macro message
604             while (*macromessage)
605                 HU_queueChatChar(*macromessage++);
606             HU_queueChatChar(KEY_ENTER);
607 
608             // leave chat mode and notify that it was sent
609             StopChatInput();
610             M_StringCopy(lastmessage, chat_macros[c], sizeof(lastmessage));
611             plr->message = lastmessage;
612             eatkey = true;
613         }
614         else
615         {
616             if(w_chat.l.len) // [STRIFE]: past first char of chat?
617             {
618                 eatkey = HUlib_keyInIText(&w_chat, c);
619                 if (eatkey)
620                     HU_queueChatChar(c);
621             }
622             else
623             {
624                 // [STRIFE]: check for player-specific message;
625                 // slightly different than vanilla, to allow keys to be customized
626                 for(i = 0; i < MAXPLAYERS; i++)
627                 {
628                     if (ev->data1 == key_multi_msgplayer[i])
629                         break;
630                 }
631                 if(i < MAXPLAYERS)
632                 {
633                     // talking to self?
634                     if(i == consoleplayer)
635                     {
636                         num_nobrainers++;
637                         if (num_nobrainers < 3)
638                             plr->message = DEH_String(HUSTR_TALKTOSELF1);
639                         else if (num_nobrainers < 6)
640                             plr->message = DEH_String(HUSTR_TALKTOSELF2);
641                         else if (num_nobrainers < 9)
642                             plr->message = DEH_String(HUSTR_TALKTOSELF3);
643                         else if (num_nobrainers < 32)
644                             plr->message = DEH_String(HUSTR_TALKTOSELF4);
645                         else
646                             plr->message = DEH_String(HUSTR_TALKTOSELF5);
647                     }
648                     else
649                     {
650                         eatkey = true;
651                         HU_queueChatChar(i+1);
652                         DEH_snprintf(lastmessage, sizeof(lastmessage),
653                             "Talking to: %c", '1' + i);
654                         plr->message = lastmessage;
655                     }
656                 }
657                 else if(c == '$') // [STRIFE]: name changing
658                 {
659                     eatkey = true;
660                     HU_queueChatChar(HU_CHANGENAME);
661                     M_StringCopy(lastmessage, DEH_String("Changing Name:"),
662                                  sizeof(lastmessage));
663                     plr->message = lastmessage;
664                     hu_setting_name = true;
665                 }
666                 else
667                 {
668                     eatkey = HUlib_keyInIText(&w_chat, c);
669                     if (eatkey)
670                         HU_queueChatChar(c);
671                 }
672             }
673 
674             if (c == KEY_ENTER)
675             {
676                 StopChatInput();
677                 if (w_chat.l.len)
678                 {
679                     // [STRIFE]: name setting
680                     if(hu_setting_name)
681                     {
682                         DEH_snprintf(lastmessage, sizeof(lastmessage),
683                             "%s now %.13s", player_names[consoleplayer],
684                             w_chat.l.l);
685                         // haleyjd 20141024: missing name set for local client
686                         DEH_snprintf(player_names[consoleplayer],
687                             sizeof(player_names[consoleplayer]),
688                             "%.13s: ", w_chat.l.l);
689                         hu_setting_name = false;
690                     }
691                     else
692                     {
693                         M_StringCopy(lastmessage, w_chat.l.l,
694                                      sizeof(lastmessage));
695                     }
696                     plr->message = lastmessage;
697                 }
698             }
699             else if (c == KEY_ESCAPE)
700             {
701                 StopChatInput();
702             }
703         }
704     }
705 
706     return eatkey;
707 }
708