1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13 
14 /*
15  * $Source: /cvs/cvsroot/d2x/main/hud.c,v $
16  * $Revision: 1.5 $
17  * $Author: btb $
18  * $Date: 2002/10/11 05:14:59 $
19  *
20  * Routines for displaying HUD messages...
21  *
22  * $Log: hud.c,v $
23  * Revision 1.5  2002/10/11 05:14:59  btb
24  * make hud_message work correctly
25  *
26  * Revision 1.4  2002/10/10 19:08:15  btb
27  * whitespace
28  *
29  * Revision 1.3  2001/11/04 09:00:25  bradleyb
30  * Enable d1x-style hud_message
31  *
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include <conf.h>
37 #endif
38 
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <ctype.h>
43 
44 #include "hudmsg.h"
45 
46 #include "pstypes.h"
47 #include "u_mem.h"
48 #include "strutil.h"
49 #include "console.h"
50 #include "inferno.h"
51 #include "game.h"
52 #include "screens.h"
53 #include "gauges.h"
54 #include "physics.h"
55 #include "error.h"
56 
57 #include "menu.h"			// For the font.
58 #include "mono.h"
59 #include "collide.h"
60 #include "newdemo.h"
61 #include "player.h"
62 #include "gamefont.h"
63 
64 #include "wall.h"
65 #include "screens.h"
66 #include "text.h"
67 #include "laser.h"
68 #include "args.h"
69 #include "pa_enabl.h"
70 
71 int hud_first = 0;
72 int hud_last = 0;
73 
74 int 	HUD_nmessages = 0;
75 fix	HUD_message_timer = 0;		// Time, relative to Players[Player_num].time (int.frac seconds.frac), at which to erase gauge message
76 char  HUD_messages[HUD_MAX_NUM][HUD_MESSAGE_LENGTH+5];
77 
78 extern void copy_background_rect(int left,int top,int right,int bot);
79 char Displayed_background_message[2][HUD_MESSAGE_LENGTH] = {"",""};
80 int	Last_msg_ycrd = -1;
81 int	Last_msg_height = 6;
82 int	HUD_color = -1;
83 
84 int     MSG_Playermessages = 0;
85 int     MSG_Noredundancy = 0;
86 
87 int	Modex_hud_msg_count;
88 
89 #define LHX(x)		((x)*(FontHires?2:1))
90 #define LHY(y)		((y)*(FontHires?2.4:1))
91 
92 #ifdef WINDOWS
93 int extra_clear=0;
94 #endif
95 
96 //	-----------------------------------------------------------------------------
clear_background_messages(void)97 void clear_background_messages(void)
98 {
99 	#ifdef WINDOWS
100 	if (extra_clear == FrameCount) 		//don't do extra clear on same frame
101 		return;
102 	#endif
103 
104 #ifdef WINDOWS
105 	if (((Cockpit_mode == CM_STATUS_BAR) || (Cockpit_mode == CM_FULL_SCREEN)) && (Last_msg_ycrd != -1) && (dd_VR_render_sub_buffer[0].yoff >= 6)) {
106 		dd_grs_canvas *canv_save = dd_grd_curcanv;
107 #else
108 	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)) {
109 		grs_canvas	*canv_save = grd_curcanv;
110 #endif
111 
112 		WINDOS(	dd_gr_set_current_canvas(get_current_game_screen()),
113 				gr_set_current_canvas(get_current_game_screen())
114 		);
115 
116 		PA_DFX (pa_set_frontbuffer_current());
117 		PA_DFX (copy_background_rect(0, Last_msg_ycrd, grd_curcanv->cv_bitmap.bm_w, Last_msg_ycrd+Last_msg_height-1));
118 		PA_DFX (pa_set_backbuffer_current());
119 		copy_background_rect(0, Last_msg_ycrd, grd_curcanv->cv_bitmap.bm_w, Last_msg_ycrd+Last_msg_height-1);
120 
121 		WINDOS(
122 			dd_gr_set_current_canvas(canv_save),
123 			gr_set_current_canvas(canv_save)
124 		);
125 
126 		#ifdef WINDOWS
127 		if (extra_clear || !GRMODEINFO(modex)) {
128 			extra_clear = 0;
129 			Last_msg_ycrd = -1;
130 		}
131 		else
132 			extra_clear = FrameCount;
133 		#else
134 		Last_msg_ycrd = -1;
135 		#endif
136 	}
137 
138 	Displayed_background_message[VR_current_page][0] = 0;
139 
140 }
141 
142 void HUD_clear_messages()
143 {
144 	int i;
145 	HUD_nmessages = 0;
146 	hud_first = hud_last = 0;
147 	HUD_message_timer = 0;
148 	clear_background_messages();
149 	for (i = 0; i < HUD_MAX_NUM; i++)
150 		sprintf(HUD_messages[i], "SlagelSlagel!!");
151 }
152 
153 
154 extern int Guided_in_big_window;
155 extern int max_window_h;
156 
157 extern grs_canvas *print_to_canvas(char *s,grs_font *font, int fc, int bc, int double_flag);
158 
159 //	-----------------------------------------------------------------------------
160 //	print to buffer, double heights, and blit bitmap to screen
161 void modex_hud_message(int x, int y, char *s, grs_font *font, int color)
162 {
163 	grs_canvas *temp_canv;
164 
165 	temp_canv = print_to_canvas(s, font, color, -1, 1);
166 
167 	gr_bitmapm(x,y,&temp_canv->cv_bitmap);
168 
169 	gr_free_canvas(temp_canv);
170 }
171 
172 extern int max_window_w;
173 
174 //	-----------------------------------------------------------------------------
175 //	Writes a message on the HUD and checks its timer.
176 void HUD_render_message_frame()
177 {
178 	int i, y,n;
179 	int h,w,aw;
180 
181 	if (( HUD_nmessages < 0 ) || (HUD_nmessages > HUD_MAX_NUM))
182 		Int3(); // Get Rob!
183 
184 	if ( (HUD_nmessages < 1 ) && (Modex_hud_msg_count == 0))
185 		return;
186 
187 	HUD_message_timer -= FrameTime;
188 
189 	#ifdef WINDOWS
190 	if (extra_clear)
191 		clear_background_messages();			//	If in status bar mode and no messages, then erase.
192 	#endif
193 
194 	if ( HUD_message_timer < 0 )	{
195 		// Timer expired... get rid of oldest message...
196 		if (hud_last!=hud_first)	{
197 			int	temp;
198 
199 			//&HUD_messages[hud_first][0] is deing deleted...;
200 			hud_first = (hud_first+1) % HUD_MAX_NUM;
201 			HUD_message_timer = F1_0*2;
202 			HUD_nmessages--;
203 			if (HUD_nmessages == 0)
204 				Modex_hud_msg_count = 2;
205 			temp = Last_msg_ycrd;
206 			clear_background_messages();			//	If in status bar mode and no messages, then erase.
207 			if (Modex_hud_msg_count)
208 				Last_msg_ycrd = temp;
209 		}
210 	}
211 
212 	if (HUD_nmessages > 0 )	{
213 
214 		if (HUD_color == -1)
215 			HUD_color = BM_XRGB(0,28,0);
216 
217 	#ifdef WINDOWS
218 		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))) {
219 	#else
220 		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))) {
221 	#endif
222 			// Only display the most recent message in this mode
223 			char	*message = HUD_messages[(hud_first+HUD_nmessages-1) % HUD_MAX_NUM];
224 
225 			if (strcmp(Displayed_background_message[VR_current_page], message)) {
226 				int	ycrd;
227 				WINDOS(
228 					dd_grs_canvas *canv_save = dd_grd_curcanv,
229 					grs_canvas	*canv_save = grd_curcanv
230 				);
231 
232 				#ifdef MACINTOSH
233 				if (Scanline_double)
234 					FontHires=1;	// always display hires font outside of display
235 				#endif
236 
237 				WINDOS(
238 					ycrd = dd_grd_curcanv->yoff - (SMALL_FONT->ft_h+2),
239 					ycrd = grd_curcanv->cv_bitmap.bm_y - (SMALL_FONT->ft_h+2)
240 				);
241 
242 				if (ycrd < 0)
243 					ycrd = 0;
244 
245 				WINDOS(
246 					dd_gr_set_current_canvas(get_current_game_screen()),
247 					gr_set_current_canvas(get_current_game_screen())
248 				);
249 
250 				gr_set_curfont( SMALL_FONT );
251 				gr_get_string_size(message, &w, &h, &aw );
252 				clear_background_messages();
253 
254 
255 				if (grd_curcanv->cv_bitmap.bm_type == BM_MODEX) {
256 					WIN(Int3());    // No no no no ....
257 					ycrd -= h;
258 					h *= 2;
259 					modex_hud_message((grd_curcanv->cv_bitmap.bm_w-w)/2, ycrd, message, SMALL_FONT, HUD_color);
260 					if (Modex_hud_msg_count > 0) {
261 						Modex_hud_msg_count--;
262 						Displayed_background_message[VR_current_page][0] = '!';
263 					} else
264 						strcpy(Displayed_background_message[VR_current_page], message);
265 				} else {
266 				WIN(DDGRLOCK(dd_grd_curcanv));
267 					gr_set_fontcolor( HUD_color, -1);
268 					PA_DFX (pa_set_frontbuffer_current());
269 					PA_DFX (gr_printf((grd_curcanv->cv_bitmap.bm_w-w)/2, ycrd, message ));
270 					PA_DFX (pa_set_backbuffer_current());
271 					gr_printf((grd_curcanv->cv_bitmap.bm_w-w)/2, ycrd, message );
272 					strcpy(Displayed_background_message[VR_current_page], message);
273 				WIN(DDGRUNLOCK(dd_grd_curcanv));
274 				}
275 
276 			WINDOS(
277 				dd_gr_set_current_canvas(canv_save),
278 				gr_set_current_canvas(canv_save)
279 			);
280 
281 				Last_msg_ycrd = ycrd;
282 				Last_msg_height = h;
283 
284 				#ifdef MACINTOSH
285 				if (Scanline_double)
286 					FontHires=0;	// always display hires font outside of display
287 				#endif
288 
289 			}
290 		} else {
291 
292 			gr_set_curfont( SMALL_FONT );
293 
294 			if ( (Cockpit_mode == CM_FULL_SCREEN) || (Cockpit_mode == CM_LETTERBOX) ) {
295 				if (Game_window_w == max_window_w)
296 					y = SMALL_FONT->ft_h/2;
297 				else
298 				 	y= SMALL_FONT->ft_h * 2;
299 			} else
300 				y = SMALL_FONT->ft_h/2;
301 
302 		  if (Guided_missile[Player_num] && Guided_missile[Player_num]->type==OBJ_WEAPON && Guided_missile[Player_num]->id==GUIDEDMISS_ID &&
303 		      Guided_missile[Player_num]->signature==Guided_missile_sig[Player_num] && Guided_in_big_window)
304 			      y+=SMALL_FONT->ft_h+3;
305 
306 		WIN(DDGRLOCK(dd_grd_curcanv));
307 		  	for (i=0; i<HUD_nmessages; i++ )	{
308 				n = (hud_first+i) % HUD_MAX_NUM;
309 				if ((n < 0) || (n >= HUD_MAX_NUM))
310 					Int3(); // Get Rob!!
311 				if (!strcmp(HUD_messages[n], "This is a bug."))
312 					Int3(); // Get Rob!!
313 				gr_get_string_size(&HUD_messages[n][0], &w, &h, &aw );
314 				gr_set_fontcolor( HUD_color, -1);
315 
316 				PA_DFX (pa_set_frontbuffer_current());
317 				PA_DFX(gr_string((grd_curcanv->cv_bitmap.bm_w-w)/2,y, &HUD_messages[n][0] ));
318 				PA_DFX (pa_set_backbuffer_current());
319 				gr_string((grd_curcanv->cv_bitmap.bm_w-w)/2,y, &HUD_messages[n][0] );
320 				y += h+1;
321 			}
322 		WIN(DDGRUNLOCK(dd_grd_curcanv));
323 		}
324 	}
325 	#ifndef WINDOWS
326 	else if (get_current_game_screen()->cv_bitmap.bm_type == BM_MODEX) {
327 		if (Modex_hud_msg_count) {
328 			int	temp = Last_msg_ycrd;
329 			Modex_hud_msg_count--;
330 			clear_background_messages();			//	If in status bar mode and no messages, then erase.
331 			Last_msg_ycrd = temp;
332 		}
333 	}
334 	#endif
335 
336 	gr_set_curfont( GAME_FONT );
337 }
338 
339 int PlayerMessage=1;
340 
341 
342 // Call to flash a message on the HUD.  Returns true if message drawn.
343 //  (message might not be drawn if previous message was same)
344 int HUD_init_message_va(char * format, va_list args)
345 {
346 	int temp, temp2;
347 	char *message = NULL;
348 	char *last_message=NULL;
349 	char *cleanmessage;
350 
351 	Modex_hud_msg_count = 2;
352 
353 	if ( (hud_last < 0) || (hud_last >= HUD_MAX_NUM))
354 		Int3(); // Get Rob!!
355 
356 	// -- mprintf((0, "message timer: %7.3f\n", f2fl(HUD_message_timer)));
357 	message = &HUD_messages[hud_last][0];
358 	vsprintf(message,format,args);
359 
360 	/* Produce a sanitised version and send it to the console */
361 	cleanmessage = d_strdup(message);
362 	for (temp=0,temp2=0; message[temp]!=0; temp++)
363 	{
364         	if (isprint(message[temp])) cleanmessage[temp2++] = message[temp];
365 		else temp++; /* Skip next character as well */
366 	}
367 	cleanmessage[temp2] = 0;
368 	con_printf(CON_NORMAL, "%s\n", message);
369 	d_free(cleanmessage);
370 
371 	// Added by Leighton
372 
373    if ((Game_mode & GM_MULTI) && FindArg("-noredundancy"))
374 	 if (!strnicmp ("You already",message,11))
375 		return 0;
376 
377    if ((Game_mode & GM_MULTI) && FindArg("-PlayerMessages") && PlayerMessage==0)
378 		return 0;
379 
380 	if (HUD_nmessages > 0)	{
381 		if (hud_last==0)
382 			last_message = &HUD_messages[HUD_MAX_NUM-1][0];
383 		else
384 			last_message = &HUD_messages[hud_last-1][0];
385 	}
386 
387 	temp = (hud_last+1) % HUD_MAX_NUM;
388 
389 	if ( temp==hud_first )	{
390 		// If too many messages, remove oldest message to make room
391 		hud_first = (hud_first+1) % HUD_MAX_NUM;
392 		HUD_nmessages--;
393 	}
394 
395 	if (last_message && (!strcmp(last_message, message))) {
396 		HUD_message_timer = F1_0*3;		// 1 second per 5 characters
397 		return 0;	// ignore since it is the same as the last one
398 	}
399 
400 	hud_last = temp;
401 	// Check if memory has been overwritten at this point.
402 	if (strlen(message) >= HUD_MESSAGE_LENGTH)
403 		Error( "Your message to HUD is too long.  Limit is %i characters.\n", HUD_MESSAGE_LENGTH);
404 	#ifdef NEWDEMO
405 	if (Newdemo_state == ND_STATE_RECORDING )
406 		newdemo_record_hud_message( message );
407 	#endif
408 	HUD_message_timer = F1_0*3;		// 1 second per 5 characters
409 	HUD_nmessages++;
410 
411 	return 1;
412 }
413 
414 
415 int HUD_init_message(char * format, ... )
416 {
417 	int ret;
418 	va_list args;
419 
420 	va_start(args, format);
421 	ret = HUD_init_message_va(format, args);
422 	va_end(args);
423 
424 	return ret;
425 }
426 
427 
428 //@@void player_dead_message(void)
429 //@@{
430 //@@	if (!Arcade_mode && Player_exploded) { //(ConsoleObject->flags & OF_EXPLODING)) {
431 //@@		gr_set_curfont( SMALL_FONT );
432 //@@		if (HUD_color == -1)
433 //@@			HUD_color = BM_XRGB(0,28,0);
434 //@@		gr_set_fontcolor( HUD_color, -1);
435 //@@
436 //@@		gr_printf(0x8000, grd_curcanv->cv_bitmap.bm_h-8, TXT_PRESS_ANY_KEY);
437 //@@		gr_set_curfont( GAME_FONT );
438 //@@	}
439 //@@
440 //@@}
441 
442 void player_dead_message(void)
443 {
444     if (Player_exploded) {
445         if ( Players[Player_num].lives < 2 )    {
446             int x, y, w, h, aw;
447             gr_set_curfont( HUGE_FONT );
448             gr_get_string_size( TXT_GAME_OVER, &w, &h, &aw );
449             w += 20;
450             h += 8;
451             x = (grd_curcanv->cv_w - w ) / 2;
452             y = (grd_curcanv->cv_h - h ) / 2;
453 
454             NO_DFX (Gr_scanline_darkening_level = 2*7);
455             NO_DFX (gr_setcolor( BM_XRGB(0,0,0) ));
456             NO_DFX (gr_rect( x, y, x+w, y+h ));
457             Gr_scanline_darkening_level = GR_FADE_LEVELS;
458 
459             gr_string(0x8000, (grd_curcanv->cv_h - grd_curcanv->cv_font->ft_h)/2 + h/8, TXT_GAME_OVER );
460 
461 #if 0
462             // Automatically exit death after 10 secs
463             if ( GameTime > Player_time_of_death + F1_0*10 ) {
464                     Function_mode = FMODE_MENU;
465                     Game_mode = GM_GAME_OVER;
466                     longjmp( LeaveGame, 1 );        // Exit out of game loop
467             }
468 #endif
469 
470         }
471         gr_set_curfont( GAME_FONT );
472         if (HUD_color == -1)
473             HUD_color = BM_XRGB(0,28,0);
474         gr_set_fontcolor( HUD_color, -1);
475         gr_string(0x8000, grd_curcanv->cv_h-(grd_curcanv->cv_font->ft_h+3), TXT_PRESS_ANY_KEY);
476     }
477 }
478 
479 // void say_afterburner_status(void)
480 // {
481 // 	if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
482 // 		HUD_init_message("Afterburner engaged.");
483 // 	else
484 // 		HUD_init_message("Afterburner disengaged.");
485 // }
486 
487 void hud_message(int class, char *format, ...)
488 {
489 	va_list vp;
490 
491 	va_start(vp, format);
492 	if ((!MSG_Noredundancy || (class & MSGC_NOREDUNDANCY)) &&
493 	    (!MSG_Playermessages || !(Game_mode & GM_MULTI) ||
494 	     (class & MSGC_PLAYERMESSAGES)))
495 		HUD_init_message_va(format, vp);
496 	va_end(vp);
497 }
498 
499