]> icculus.org git repositories - btb/d2x.git/blob - main/hud.c
use new GET_INTEL_* macros
[btb/d2x.git] / main / hud.c
1 /* $Id: hud.c,v 1.8 2003-10-10 09:36:35 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Routines for displaying HUD messages...
18  *
19  * Old Log:
20  * Revision 1.4  1995/08/24  16:03:09  allender
21  * fix up message placement
22  *
23  * Revision 1.3  1995/08/18  10:25:21  allender
24  * added support for pixel doubling using PC game font
25  *
26  * Revision 1.2  1995/08/12  11:33:22  allender
27  * removed #ifdef NEWDEMO -- always in
28  *
29  * Revision 1.1  1995/05/16  15:26:32  allender
30  * Initial revision
31  *
32  * Revision 2.2  1995/03/30  16:36:40  mike
33  * text localization.
34  *
35  * Revision 2.1  1995/03/06  15:23:50  john
36  * New screen techniques.
37  *
38  * Revision 2.0  1995/02/27  11:30:41  john
39  * New version 2.0, which has no anonymous unions, builds with
40  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
41  *
42  * Revision 1.27  1995/01/23  16:51:30  mike
43  * Show hud messages on 3d if window in three largest sizes.
44  *
45  * Revision 1.26  1995/01/17  17:42:45  rob
46  * Made message timeout for HUD messages longer.
47  *
48  * Revision 1.25  1995/01/04  11:39:03  rob
49  * Made HUD text get out of the way of large HUD messages.
50  *
51  * Revision 1.24  1995/01/01  14:20:32  rob
52  * longer timer for hud messages.
53  *
54  *
55  * Revision 1.23  1994/12/15  13:04:34  mike
56  * Replace Players[Player_num].time_total references with GameTime.
57  *
58  * Revision 1.22  1994/12/13  12:55:12  mike
59  * move press any key to continue message when you are dead to bottom of window.
60  *
61  * Revision 1.21  1994/12/07  17:08:01  rob
62  * removed unnecessary debug info.
63  *
64  * Revision 1.20  1994/12/07  16:24:16  john
65  * Took out code that kept track of messages differently for different
66  * screen modes... I made it so they just draw differently depending on screen mode.
67  *
68  * Revision 1.19  1994/12/07  15:42:57  rob
69  * Added a bunch of debug stuff to look for HUD message problems in net games...
70  *
71  * Revision 1.18  1994/12/06  16:30:35  yuan
72  * Localization
73  *
74  * Revision 1.17  1994/12/05  00:32:36  mike
75  * fix randomness of color on status bar hud messages.
76  *
77  * Revision 1.16  1994/11/19  17:05:53  rob
78  * Moved dead_player_message down to avoid overwriting HUD messages.
79  *
80  * Revision 1.15  1994/11/18  23:37:56  john
81  * Changed some shorts to ints.
82  *
83  * Revision 1.14  1994/11/12  16:38:25  mike
84  * clear some annoying debug messages.
85  *
86  * Revision 1.13  1994/11/11  15:36:39  mike
87  * write hud messages on background if 3d window small enough
88  *
89  * Revision 1.12  1994/10/20  09:49:31  mike
90  * Reduce number of messages.
91  *
92  * Revision 1.11  1994/10/17  10:49:15  john
93  * Took out some warnings.
94  *
95  * Revision 1.10  1994/10/17  10:45:13  john
96  * Made the player able to abort death by pressing any button or key.
97  *
98  * Revision 1.9  1994/10/13  15:17:33  mike
99  * Remove afterburner references.
100  *
101  * Revision 1.8  1994/10/11  12:06:32  mike
102  * Only show message advertising death sequence abort after player exploded.
103  *
104  * Revision 1.7  1994/10/10  17:21:53  john
105  * Made so instead of saying too many messages, it scrolls off the
106  * oldest message.
107  *
108  * Revision 1.6  1994/10/07  23:05:39  john
109  * Fixed bug with HUD not drawing stuff sometimes...
110  * ( I had a circular buffer that I was stepping thru
111  * to draw text that went: for (i=first;i<last;i++)...
112  * duh!! last could be less than first.)
113  * /
114  *
115  * Revision 1.5  1994/09/16  13:08:20  mike
116  * Arcade stuff, afterburner stuff.
117  *
118  * Revision 1.4  1994/09/14  16:26:57  mike
119  * player_dead_message.
120  *
121  * Revision 1.3  1994/08/18  16:35:45  john
122  * Made gauges messages stay up a bit longer.
123  *
124  * Revision 1.2  1994/08/18  12:10:21  john
125  * Made HUD messages scroll.
126  *
127  * Revision 1.1  1994/08/18  11:22:09  john
128  * Initial revision
129  *
130  *
131  */
132
133
134 #ifdef HAVE_CONFIG_H
135 #include <conf.h>
136 #endif
137
138 #include <stdio.h>
139 #include <string.h>
140 #include <stdlib.h>
141
142 #include "hudmsg.h"
143
144 #include "pstypes.h"
145 #include "u_mem.h"
146 #include "strutil.h"
147 #include "console.h"
148 #include "inferno.h"
149 #include "game.h"
150 #include "screens.h"
151 #include "gauges.h"
152 #include "physics.h"
153 #include "error.h"
154
155 #include "menu.h"           // For the font.
156 #include "mono.h"
157 #include "collide.h"
158 #include "newdemo.h"
159 #include "player.h"
160 #include "gamefont.h"
161
162 #include "wall.h"
163 #include "screens.h"
164 #include "text.h"
165 #include "laser.h"
166 #include "args.h"
167 #include "pa_enabl.h"
168
169 int hud_first = 0;
170 int hud_last = 0;
171
172 int HUD_nmessages = 0;
173 fix HUD_message_timer = 0;      // Time, relative to Players[Player_num].time (int.frac seconds.frac), at which to erase gauge message
174 char  HUD_messages[HUD_MAX_NUM][HUD_MESSAGE_LENGTH+5];
175
176 extern void copy_background_rect(int left,int top,int right,int bot);
177 char Displayed_background_message[2][HUD_MESSAGE_LENGTH] = {"",""};
178 int Last_msg_ycrd = -1;
179 int Last_msg_height = 6;
180 int HUD_color = -1;
181
182 int     MSG_Playermessages = 0;
183 int     MSG_Noredundancy = 0;
184
185 int Modex_hud_msg_count;
186
187 #define LHX(x)      ((x)*(FontHires?2:1))
188 #define LHY(y)      ((y)*(FontHires?2.4:1))
189
190 #ifdef WINDOWS
191 int extra_clear=0;
192 #endif
193
194 // ----------------------------------------------------------------------------
195 void clear_background_messages(void)
196 {
197         #ifdef WINDOWS
198         if (extra_clear == FrameCount)          //don't do extra clear on same frame
199                 return;
200         #endif
201
202 #ifdef WINDOWS
203         if (((Cockpit_mode == CM_STATUS_BAR) || (Cockpit_mode == CM_FULL_SCREEN)) && (Last_msg_ycrd != -1) && (dd_VR_render_sub_buffer[0].yoff >= 6)) {
204                 dd_grs_canvas *canv_save = dd_grd_curcanv;
205 #else
206         if (((Cockpit_mode == CM_STATUS_BAR) || (Cockpit_mode == CM_FULL_SCREEN)) && (Last_msg_ycrd != -1) && (VR_render_sub_buffer[0].cv_bitmap.bm_y >= 6)) {
207                 grs_canvas      *canv_save = grd_curcanv;
208 #endif
209
210                 WINDOS( dd_gr_set_current_canvas(get_current_game_screen()),
211                                 gr_set_current_canvas(get_current_game_screen())
212                 );
213
214                 PA_DFX (pa_set_frontbuffer_current());
215                 PA_DFX (copy_background_rect(0, Last_msg_ycrd, grd_curcanv->cv_bitmap.bm_w, Last_msg_ycrd+Last_msg_height-1));
216                 PA_DFX (pa_set_backbuffer_current());
217                 copy_background_rect(0, Last_msg_ycrd, grd_curcanv->cv_bitmap.bm_w, Last_msg_ycrd+Last_msg_height-1);
218
219                 WINDOS(
220                         dd_gr_set_current_canvas(canv_save),
221                         gr_set_current_canvas(canv_save)
222                 );
223
224                 #ifdef WINDOWS
225                 if (extra_clear || !GRMODEINFO(modex)) {
226                         extra_clear = 0;
227                         Last_msg_ycrd = -1;
228                 }
229                 else
230                         extra_clear = FrameCount;
231                 #else
232                 Last_msg_ycrd = -1;
233                 #endif
234         }
235
236         Displayed_background_message[VR_current_page][0] = 0;
237
238 }
239
240 void HUD_clear_messages()
241 {
242         int i;
243         HUD_nmessages = 0;
244         hud_first = hud_last = 0;
245         HUD_message_timer = 0;
246         clear_background_messages();
247         for (i = 0; i < HUD_MAX_NUM; i++)
248                 sprintf(HUD_messages[i], "SlagelSlagel!!");
249 }
250
251
252 extern int Guided_in_big_window;
253 extern int max_window_h;
254
255 extern grs_canvas *print_to_canvas(char *s,grs_font *font, int fc, int bc, int double_flag);
256
257 //      -----------------------------------------------------------------------------
258 //      print to buffer, double heights, and blit bitmap to screen
259 void modex_hud_message(int x, int y, char *s, grs_font *font, int color)
260 {
261         grs_canvas *temp_canv;
262
263         temp_canv = print_to_canvas(s, font, color, -1, 1);
264
265         gr_bitmapm(x,y,&temp_canv->cv_bitmap);
266
267         gr_free_canvas(temp_canv);
268 }
269
270 extern int max_window_w;
271
272 // ----------------------------------------------------------------------------
273 //      Writes a message on the HUD and checks its timer.
274 void HUD_render_message_frame()
275 {
276         int i, y,n;
277         int h,w,aw;
278
279         if (( HUD_nmessages < 0 ) || (HUD_nmessages > HUD_MAX_NUM))
280                 Int3(); // Get Rob!
281
282         if ( (HUD_nmessages < 1 ) && (Modex_hud_msg_count == 0))
283                 return;
284
285         HUD_message_timer -= FrameTime;
286
287         #ifdef WINDOWS
288         if (extra_clear)
289                 clear_background_messages();                    //      If in status bar mode and no messages, then erase.
290         #endif
291
292         if ( HUD_message_timer < 0 )    {
293                 // Timer expired... get rid of oldest message...
294                 if (hud_last!=hud_first)        {
295                         int     temp;
296
297                         //&HUD_messages[hud_first][0] is deing deleted...;
298                         hud_first = (hud_first+1) % HUD_MAX_NUM;
299                         HUD_message_timer = F1_0*2;
300                         HUD_nmessages--;
301                         if (HUD_nmessages == 0)
302                                 Modex_hud_msg_count = 2;
303                         temp = Last_msg_ycrd;
304                         clear_background_messages();                    //      If in status bar mode and no messages, then erase.
305                         if (Modex_hud_msg_count)
306                                 Last_msg_ycrd = temp;
307                 }
308         }
309
310         if (HUD_nmessages > 0 ) {
311
312                 if (HUD_color == -1)
313                         HUD_color = BM_XRGB(0,28,0);
314
315         #ifdef WINDOWS
316                 if ( (VR_render_mode==VR_NONE) && ((Cockpit_mode == CM_STATUS_BAR) || (Cockpit_mode == CM_FULL_SCREEN)) && (dd_VR_render_sub_buffer[0].yoff >= (max_window_h/8))) {
317         #else
318                 if ( (VR_render_mode==VR_NONE) && ((Cockpit_mode == CM_STATUS_BAR) || (Cockpit_mode == CM_FULL_SCREEN)) && (VR_render_sub_buffer[0].cv_bitmap.bm_y >= (max_window_h/8))) {
319         #endif
320                         // Only display the most recent message in this mode
321                         char    *message = HUD_messages[(hud_first+HUD_nmessages-1) % HUD_MAX_NUM];
322
323                         if (strcmp(Displayed_background_message[VR_current_page], message)) {
324                                 int     ycrd;
325                                 WINDOS(
326                                         dd_grs_canvas *canv_save = dd_grd_curcanv,
327                                         grs_canvas      *canv_save = grd_curcanv
328                                 );
329
330                                 #ifdef MACINTOSH
331                                 if (Scanline_double)
332                                         FontHires=1;    // always display hires font outside of display
333                                 #endif
334
335                                 WINDOS(
336                                         ycrd = dd_grd_curcanv->yoff - (SMALL_FONT->ft_h+2),
337                                         ycrd = grd_curcanv->cv_bitmap.bm_y - (SMALL_FONT->ft_h+2)
338                                 );
339
340                                 if (ycrd < 0)
341                                         ycrd = 0;
342
343                                 WINDOS(
344                                         dd_gr_set_current_canvas(get_current_game_screen()),
345                                         gr_set_current_canvas(get_current_game_screen())
346                                 );
347
348                                 gr_set_curfont( SMALL_FONT );
349                                 gr_get_string_size(message, &w, &h, &aw );
350                                 clear_background_messages();
351
352
353                                 if (grd_curcanv->cv_bitmap.bm_type == BM_MODEX) {
354                                         WIN(Int3());    // No no no no ....
355                                         ycrd -= h;
356                                         h *= 2;
357                                         modex_hud_message((grd_curcanv->cv_bitmap.bm_w-w)/2, ycrd, message, SMALL_FONT, HUD_color);
358                                         if (Modex_hud_msg_count > 0) {
359                                                 Modex_hud_msg_count--;
360                                                 Displayed_background_message[VR_current_page][0] = '!';
361                                         } else
362                                                 strcpy(Displayed_background_message[VR_current_page], message);
363                                 } else {
364                                 WIN(DDGRLOCK(dd_grd_curcanv));
365                                         gr_set_fontcolor( HUD_color, -1);
366                                         PA_DFX (pa_set_frontbuffer_current());
367                                         PA_DFX (gr_printf((grd_curcanv->cv_bitmap.bm_w-w)/2, ycrd, message ));
368                                         PA_DFX (pa_set_backbuffer_current());
369                                         gr_printf((grd_curcanv->cv_bitmap.bm_w-w)/2, ycrd, message );
370                                         strcpy(Displayed_background_message[VR_current_page], message);
371                                 WIN(DDGRUNLOCK(dd_grd_curcanv));
372                                 }
373
374                         WINDOS(
375                                 dd_gr_set_current_canvas(canv_save),
376                                 gr_set_current_canvas(canv_save)
377                         );
378
379                                 Last_msg_ycrd = ycrd;
380                                 Last_msg_height = h;
381
382                                 #ifdef MACINTOSH
383                                 if (Scanline_double)
384                                         FontHires=0;    // always display hires font outside of display
385                                 #endif
386
387                         }
388                 } else {
389
390                         gr_set_curfont( SMALL_FONT );
391
392                         if ( (Cockpit_mode == CM_FULL_SCREEN) || (Cockpit_mode == CM_LETTERBOX) ) {
393                                 if (Game_window_w == max_window_w)
394                                         y = SMALL_FONT->ft_h/2;
395                                 else
396                                         y= SMALL_FONT->ft_h * 2;
397                         } else
398                                 y = SMALL_FONT->ft_h/2;
399
400                   if (Guided_missile[Player_num] && Guided_missile[Player_num]->type==OBJ_WEAPON && Guided_missile[Player_num]->id==GUIDEDMISS_ID &&
401                       Guided_missile[Player_num]->signature==Guided_missile_sig[Player_num] && Guided_in_big_window)
402                               y+=SMALL_FONT->ft_h+3;
403
404                 WIN(DDGRLOCK(dd_grd_curcanv));
405                         for (i=0; i<HUD_nmessages; i++ )        {
406                                 n = (hud_first+i) % HUD_MAX_NUM;
407                                 if ((n < 0) || (n >= HUD_MAX_NUM))
408                                         Int3(); // Get Rob!!
409                                 if (!strcmp(HUD_messages[n], "This is a bug."))
410                                         Int3(); // Get Rob!!
411                                 gr_get_string_size(&HUD_messages[n][0], &w, &h, &aw );
412                                 gr_set_fontcolor( HUD_color, -1);
413
414                                 PA_DFX (pa_set_frontbuffer_current());
415                                 PA_DFX(gr_string((grd_curcanv->cv_bitmap.bm_w-w)/2,y, &HUD_messages[n][0] ));
416                                 PA_DFX (pa_set_backbuffer_current());
417                                 gr_string((grd_curcanv->cv_bitmap.bm_w-w)/2,y, &HUD_messages[n][0] );
418                                 y += h+1;
419                         }
420                 WIN(DDGRUNLOCK(dd_grd_curcanv));
421                 }
422         }
423         #ifndef WINDOWS
424         else if (get_current_game_screen()->cv_bitmap.bm_type == BM_MODEX) {
425                 if (Modex_hud_msg_count) {
426                         int     temp = Last_msg_ycrd;
427                         Modex_hud_msg_count--;
428                         clear_background_messages();                    //      If in status bar mode and no messages, then erase.
429                         Last_msg_ycrd = temp;
430                 }
431         }
432         #endif
433
434         gr_set_curfont( GAME_FONT );
435 }
436
437 int PlayerMessage=1;
438
439
440 // Call to flash a message on the HUD.  Returns true if message drawn.
441 // (message might not be drawn if previous message was same)
442 int HUD_init_message_va(char * format, va_list args)
443 {
444         int temp;
445         char *message = NULL;
446         char *last_message=NULL;
447 #if 0
448         int temp2;
449         char *cleanmessage;
450 #endif
451         char con_message[HUD_MESSAGE_LENGTH + 3];
452
453         Modex_hud_msg_count = 2;
454
455         if ( (hud_last < 0) || (hud_last >= HUD_MAX_NUM))
456                 Int3(); // Get Rob!!
457
458         // -- mprintf((0, "message timer: %7.3f\n", f2fl(HUD_message_timer)));
459         message = &HUD_messages[hud_last][0];
460         vsprintf(message,format,args);
461
462
463         /* Produce a colorised version and send it to the console */
464         if (HUD_color == -1)
465                 HUD_color = BM_XRGB(0,28,0);
466         con_message[0] = CC_COLOR;
467         con_message[1] = HUD_color;
468         con_message[2] = '\0';
469         strcat(con_message, message);
470         con_printf(CON_NORMAL, "%s\n", con_message);
471
472         // Added by Leighton
473
474    if ((Game_mode & GM_MULTI) && FindArg("-noredundancy"))
475          if (!strnicmp ("You already",message,11))
476                 return 0;
477
478    if ((Game_mode & GM_MULTI) && FindArg("-PlayerMessages") && PlayerMessage==0)
479                 return 0;
480
481         if (HUD_nmessages > 0)  {
482                 if (hud_last==0)
483                         last_message = &HUD_messages[HUD_MAX_NUM-1][0];
484                 else
485                         last_message = &HUD_messages[hud_last-1][0];
486         }
487
488         temp = (hud_last+1) % HUD_MAX_NUM;
489
490         if ( temp==hud_first )  {
491                 // If too many messages, remove oldest message to make room
492                 hud_first = (hud_first+1) % HUD_MAX_NUM;
493                 HUD_nmessages--;
494         }
495
496         if (last_message && (!strcmp(last_message, message))) {
497                 HUD_message_timer = F1_0*3;             // 1 second per 5 characters
498                 return 0;       // ignore since it is the same as the last one
499         }
500
501         hud_last = temp;
502         // Check if memory has been overwritten at this point.
503         if (strlen(message) >= HUD_MESSAGE_LENGTH)
504                 Error( "Your message to HUD is too long.  Limit is %i characters.\n", HUD_MESSAGE_LENGTH);
505         #ifdef NEWDEMO
506         if (Newdemo_state == ND_STATE_RECORDING )
507                 newdemo_record_hud_message( message );
508         #endif
509         HUD_message_timer = F1_0*3;             // 1 second per 5 characters
510         HUD_nmessages++;
511
512         return 1;
513 }
514
515
516 int HUD_init_message(char * format, ... )
517 {
518         int ret;
519         va_list args;
520
521         va_start(args, format);
522         ret = HUD_init_message_va(format, args);
523         va_end(args);
524
525         return ret;
526 }
527
528
529 //@@void player_dead_message(void)
530 //@@{
531 //@@    if (!Arcade_mode && Player_exploded) { //(ConsoleObject->flags & OF_EXPLODING)) {
532 //@@            gr_set_curfont( SMALL_FONT );
533 //@@            if (HUD_color == -1)
534 //@@                    HUD_color = BM_XRGB(0,28,0);
535 //@@            gr_set_fontcolor( HUD_color, -1);
536 //@@
537 //@@            gr_printf(0x8000, grd_curcanv->cv_bitmap.bm_h-8, TXT_PRESS_ANY_KEY);
538 //@@            gr_set_curfont( GAME_FONT );
539 //@@    }
540 //@@
541 //@@}
542
543 void player_dead_message(void)
544 {
545     if (Player_exploded) {
546         if ( Players[Player_num].lives < 2 )    {
547             int x, y, w, h, aw;
548             gr_set_curfont( HUGE_FONT );
549             gr_get_string_size( TXT_GAME_OVER, &w, &h, &aw );
550             w += 20;
551             h += 8;
552             x = (grd_curcanv->cv_w - w ) / 2;
553             y = (grd_curcanv->cv_h - h ) / 2;
554
555             NO_DFX (Gr_scanline_darkening_level = 2*7);
556             NO_DFX (gr_setcolor( BM_XRGB(0,0,0) ));
557             NO_DFX (gr_rect( x, y, x+w, y+h ));
558             Gr_scanline_darkening_level = GR_FADE_LEVELS;
559
560             gr_string(0x8000, (grd_curcanv->cv_h - grd_curcanv->cv_font->ft_h)/2 + h/8, TXT_GAME_OVER );
561
562 #if 0
563             // Automatically exit death after 10 secs
564             if ( GameTime > Player_time_of_death + F1_0*10 ) {
565                     Function_mode = FMODE_MENU;
566                     Game_mode = GM_GAME_OVER;
567                     longjmp( LeaveGame, 1 );        // Exit out of game loop
568             }
569 #endif
570
571         }
572         gr_set_curfont( GAME_FONT );
573         if (HUD_color == -1)
574             HUD_color = BM_XRGB(0,28,0);
575         gr_set_fontcolor( HUD_color, -1);
576         gr_string(0x8000, grd_curcanv->cv_h-(grd_curcanv->cv_font->ft_h+3), TXT_PRESS_ANY_KEY);
577     }
578 }
579
580 // void say_afterburner_status(void)
581 // {
582 //      if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
583 //              HUD_init_message("Afterburner engaged.");
584 //      else
585 //              HUD_init_message("Afterburner disengaged.");
586 // }
587
588 void hud_message(int class, char *format, ...)
589 {
590         va_list vp;
591
592         va_start(vp, format);
593         if ((!MSG_Noredundancy || (class & MSGC_NOREDUNDANCY)) &&
594             (!MSG_Playermessages || !(Game_mode & GM_MULTI) ||
595              (class & MSGC_PLAYERMESSAGES)))
596                 HUD_init_message_va(format, vp);
597         va_end(vp);
598 }
599