1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 	Created by Loren Petrich,
21 	Dec. 23, 2000
22 	Contains everything shared between screen.cpp and screen_sdl.cpp
23 
24 Dec 29, 2000 (Loren Petrich):
25 	Added stuff for doing screen messages
26 
27 Mar 19, 2001 (Loren Petrich):
28 	Added some even bigger screen resolutions
29 
30 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
31 	Added accessors for datafields now opaque in Carbon
32 
33  Aug 6, 2003 (Woody Zenfell):
34 	Minor tweaks to screen_printf() mechanism (safer; resets when screen_reset called)
35 */
36 
37 #include "computer_interface.h"
38 #include "fades.h"
39 #include "network.h"
40 #include "OGL_Render.h"
41 #include "overhead_map.h"
42 #include "screen.h"
43 #include <stdarg.h>
44 
45 extern SDL_Surface *world_pixels;
46 
47 #define DESIRED_SCREEN_WIDTH 640
48 #define DESIRED_SCREEN_HEIGHT 480
49 
50 // Biggest possible of those defined
51 #define MAXIMUM_WORLD_WIDTH 1900
52 #define MAXIMUM_WORLD_HEIGHT 1200
53 
54 #define DEFAULT_WORLD_WIDTH 640
55 #define DEFAULT_WORLD_HEIGHT 320
56 
57 #include "Console.h"
58 #include "screen_drawing.h"
59 
60 #include "network_games.h"
61 #include "Image_Blitter.h"
62 #include "OGL_Blitter.h"
63 
64 /* ---------- globals */
65 
66 struct color_table *uncorrected_color_table; /* the pristine color environment of the game (can be 16bit) */
67 struct color_table *world_color_table; /* the gamma-corrected color environment of the game (can be 16bit) */
68 struct color_table *interface_color_table; /* always 8bit, for mixed-mode (i.e., valkyrie) fades */
69 struct color_table *visible_color_table; /* the color environment the player sees (can be 16bit) */
70 
71 struct view_data *world_view; /* should be static */
72 
73 // Convenient package for the drawing target (contains dimensions and pixel-row pointers)
74 struct bitmap_definition *world_pixels_structure;
75 
76 static struct screen_mode_data screen_mode;
77 
78 #define FRAME_SAMPLE_SIZE 20
79 bool displaying_fps= false;
80 short frame_count, frame_index;
81 int32 frame_ticks[64];
82 
83 // LP addition:
84 // whether to show one's position
85 bool ShowPosition = false;
86 bool ShowScores = false;
87 
88 // Whether rendering of the HUD has been requested
89 static bool HUD_RenderRequest = false;
90 static bool Term_RenderRequest = false;
91 
92 static bool screen_initialized= false;
93 static bool nonlocal_script_hud= false;
94 
95 short bit_depth= NONE;
96 short interface_bit_depth= NONE;
97 
98 // LP addition: this is defined in overhead_map.c
99 // It indicates whether to render the overhead map in OpenGL
100 extern bool OGL_MapActive;
101 
102 
103 // Current screen messages:
104 const int NumScreenMessages = 7;
105 struct ScreenMessage
106 {
107 	enum {
108 		Len = 256
109 	};
110 
111 	int TimeRemaining;	// How many more engine ticks until the message expires?
112 	char Text[Len];		// Text to display
113 
ScreenMessageScreenMessage114 	ScreenMessage(): TimeRemaining(0) {Text[0] = 0;}
115 };
116 
117 static int MostRecentMessage = NumScreenMessages-1;
118 static ScreenMessage Messages[NumScreenMessages];
119 
120 /* SB */
121 static struct ScriptHUDElement {
122 	/* this needs optimized (sorry, making fun of my grandmother...) */
123 	/* it's char[4] instead of int32 to make the OpenGL support simpler to implement */
124 	unsigned char icon[1024];
125 	bool isicon;
126 	int color;
127 	std::string text;
128 	Image_Blitter sdl_blitter;
129 #ifdef HAVE_OPENGL
130 	OGL_Blitter ogl_blitter;
131 #endif
132 } ScriptHUDElements[MAXIMUM_NUMBER_OF_NETWORK_PLAYERS][MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS];
133 /* /SB */
134 
135 /* ---------- private prototypes */
136 
137 static void set_overhead_map_status(bool status);
138 static void set_terminal_status(bool status);
139 
140 /* ---------- code */
141 
142 /* SB */
143 namespace icon {
144 
nextc(const char * & p,size_t & rem)145   static inline char nextc(const char*& p, size_t& rem) {
146     if(rem == 0) throw "end of string";
147     --rem;
148     return *(p++);
149   }
150 
151   // we can't use ctype for this because of locales and hexadecimal
isadigit(char p)152   static inline bool isadigit(char p) {
153     if(p >= '0' && p <= '9') return true;
154     else if(p >= 'A' && p <= 'F') return true;
155     else if(p >= 'a' && p <= 'f') return true;
156     else return false;
157   }
158 
digit(char p)159   static inline unsigned char digit(char p) {
160     if(p >= '0' && p <= '9') return p - '0';
161     else if(p >= 'A' && p <= 'F') return p - 'A' + 0xA;
162     else if(p >= 'a' && p <= 'f') return p - 'a' + 0xA;
163     else throw "invalid digit";
164   }
165 
readuc(const char * & p,size_t & rem)166   static inline unsigned char readuc(const char*& p, size_t& rem) {
167     char a = nextc(p, rem), b;
168     b = nextc(p, rem);
169     return (digit(a) << 4) | digit(b);
170   }
171 
parseicon(const char * p,size_t rem,unsigned char palette[1024],int & numcolors,unsigned char graphic[256])172   static bool parseicon(const char* p, size_t rem,
173 			unsigned char palette[1024], int& numcolors,
174 			unsigned char graphic[256]) {
175     char chars[256];
176     try {
177       char oc, c;
178       size_t n, m;
179       numcolors = 0;
180       while(1) {
181 	c = nextc(p, rem);
182 	if(c >= '0' && c <= '9')
183 	  numcolors = numcolors * 10 + (c - '0');
184 	else break;
185       }
186       if(numcolors == 0) return 1;
187       oc = c;
188       do {
189 	c = nextc(p, rem);
190       } while(c == oc);
191       n = 0;
192       while(n < numcolors) {
193 	chars[n] = c;
194 	palette[n * 4] = readuc(p, rem);
195 	palette[n * 4 + 1] = readuc(p, rem);
196 	palette[n * 4 + 2] = readuc(p, rem);
197 	c = nextc(p, rem); /* ignore a char, UNLESS... */
198 	if(isadigit(c)) {  /* ...it's a digit */
199 	  --p; ++rem; /* let readuc see it */
200 	  palette[n * 4 + 3] = readuc(p, rem);
201 	  nextc(p, rem); /* remember to ignore another char */
202 	}
203 	else
204 	  palette[n * 4 + 3] = 255;
205 	++n;
206 	c = nextc(p, rem);
207       }
208       n = 0;
209       while(n < 256) {
210 	for(m = 0; m < numcolors; ++m) {
211 	  if(chars[m] == c) {
212 	    graphic[n++] = m;
213 	    break;
214 	  }
215 	}
216 	c = nextc(p, rem);
217       }
218     } catch(...) {
219       return false;
220     }
221     return true;
222   }
223 
seticon(int player,int idx,unsigned char palette[1024],unsigned char graphic[256])224   void seticon(int player, int idx, unsigned char palette[1024], unsigned char graphic[256]) {
225     unsigned char* p1, *p2, px;
226     int n;
227     p1 = ScriptHUDElements[player][idx].icon;
228     p2 = graphic;
229     for(n = 0; n < 256; ++n) {
230       px = *(p2++);
231       *(p1++) = palette[px * 4 + 3];
232       *(p1++) = palette[px * 4];
233       *(p1++) = palette[px * 4 + 1];
234       *(p1++) = palette[px * 4 + 2];
235     }
236     ScriptHUDElements[player][idx].isicon = true;
237 	SDL_Surface *srf;
238 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
239 	srf = SDL_CreateRGBSurfaceFrom(ScriptHUDElements[player][idx].icon, 16, 16, 32, 64, 0xFF<<8, 0xFF<<16, 0xFF<<24, 0xFF);
240 #else
241 	srf = SDL_CreateRGBSurfaceFrom(ScriptHUDElements[player][idx].icon, 16, 16, 32, 64, 0xFF<<16, 0xFF<<8, 0xFF, 0xFF<<24);
242 #endif
243 #ifdef HAVE_OPENGL
244 	if (OGL_IsActive()) {
245 		ScriptHUDElements[player][idx].ogl_blitter.Load(*srf);
246 	} else
247 #endif
248 	{
249 		ScriptHUDElements[player][idx].sdl_blitter.Load(*srf);
250 	}
251 	SDL_FreeSurface(srf);
252   }
253 
254 }
255 
IsScriptHUDNonlocal()256 bool IsScriptHUDNonlocal() {
257   return nonlocal_script_hud;
258 }
259 
SetScriptHUDNonlocal(bool nonlocal)260 void SetScriptHUDNonlocal(bool nonlocal) {
261   nonlocal_script_hud = nonlocal;
262 }
263 
SetScriptHUDColor(int player,int idx,int color)264 void SetScriptHUDColor(int player, int idx, int color) {
265   player %= MAXIMUM_NUMBER_OF_NETWORK_PLAYERS;
266   idx %= MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS; /* o_o */
267   ScriptHUDElements[player][idx].color = color % 8; /* O_O */
268 }
269 
SetScriptHUDText(int player,int idx,const char * text)270 void SetScriptHUDText(int player, int idx, const char* text) {
271   player %= MAXIMUM_NUMBER_OF_NETWORK_PLAYERS;
272   idx %= MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS;
273   if(!text) ScriptHUDElements[player][idx].text.clear();
274   else ScriptHUDElements[player][idx].text = text;
275 }
276 
SetScriptHUDIcon(int player,int idx,const char * text,size_t rem)277 bool SetScriptHUDIcon(int player, int idx, const char* text, size_t rem) {
278   unsigned char palette[1024], graphic[256];
279   int numcolors;
280   player %= MAXIMUM_NUMBER_OF_NETWORK_PLAYERS;
281   idx %= MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS;
282   if(text) {
283     if(!icon::parseicon(text, rem, palette, numcolors, graphic)) return false;
284     icon::seticon(player, idx, palette, graphic);
285   } else ScriptHUDElements[player][idx].isicon = false;
286   return true;
287 }
288 
SetScriptHUDSquare(int player,int idx,int _color)289 void SetScriptHUDSquare(int player, int idx, int _color) {
290   unsigned char palette[4]; /* short, I KNOW. */
291   unsigned char graphic[256];
292   player %= MAXIMUM_NUMBER_OF_NETWORK_PLAYERS;
293   idx %= MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS;
294   ScriptHUDElements[player][idx].color = _color % 8;
295   memset(graphic, 0, 256);
296   SDL_Color color;
297   _get_interface_color(_color+_computer_interface_text_color, &color);
298   palette[0] = color.r;
299   palette[1] = color.g;
300   palette[2] = color.b;
301   palette[3] = 0xff;
302   icon::seticon(player, idx, palette, graphic);
303 }
304 /* /SB */
305 
reset_messages()306 void reset_messages()
307 {
308 	// ZZZ: reset screen_printf's
309 	for(int i = 0; i < NumScreenMessages; i++)
310 		Messages[i].TimeRemaining = 0;
311 	/* SB: reset HUD elements */
312 	for(int p = 0; p < MAXIMUM_NUMBER_OF_NETWORK_PLAYERS; p++) {
313 		for(int i = 0; i < MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS; i++) {
314 			ScriptHUDElements[p][i].color = 1;
315 			ScriptHUDElements[p][i].text.clear();
316 			ScriptHUDElements[p][i].isicon = false;
317 		}
318 	}
319         nonlocal_script_hud = false;
320 }
321 
322 // LP addition: this resets the screen; useful when starting a game
reset_screen()323 void reset_screen()
324 {
325 	// Resetting cribbed from initialize_screen()
326 	world_view->overhead_map_scale= DEFAULT_OVERHEAD_MAP_SCALE;
327 	world_view->overhead_map_active= false;
328 	world_view->terminal_mode_active= false;
329 	world_view->horizontal_scale= 1, world_view->vertical_scale= 1;
330 
331 	// LP change:
332 	ResetFieldOfView();
333 
334 	reset_messages();
335 }
336 
337 // LP change: resets field of view to whatever the player had had when reviving
ResetFieldOfView()338 void ResetFieldOfView()
339 {
340 	world_view->tunnel_vision_active = false;
341 
342 	if (current_player->extravision_duration)
343 	{
344 		world_view->field_of_view = EXTRAVISION_FIELD_OF_VIEW;
345 		world_view->target_field_of_view = EXTRAVISION_FIELD_OF_VIEW;
346 	}
347 	else
348 	{
349 		world_view->field_of_view = NORMAL_FIELD_OF_VIEW;
350 		world_view->target_field_of_view = NORMAL_FIELD_OF_VIEW;
351 	}
352 }
353 
354 
zoom_overhead_map_out(void)355 bool zoom_overhead_map_out(
356 	void)
357 {
358 	bool Success = false;
359 	if (world_view->overhead_map_scale > OVERHEAD_MAP_MINIMUM_SCALE)
360 	{
361 		world_view->overhead_map_scale--;
362 		Success = true;
363 	}
364 
365 	return Success;
366 }
367 
zoom_overhead_map_in(void)368 bool zoom_overhead_map_in(
369 	void)
370 {
371 	bool Success = false;
372 	if (world_view->overhead_map_scale < OVERHEAD_MAP_MAXIMUM_SCALE)
373 	{
374 		world_view->overhead_map_scale++;
375 		Success = true;
376 	}
377 
378 	return Success;
379 }
380 
start_teleporting_effect(bool out)381 void start_teleporting_effect(
382 	bool out)
383 {
384 	if (View_DoFoldEffect())
385 		start_render_effect(world_view, out ? _render_effect_fold_out : _render_effect_fold_in);
386 }
387 
start_extravision_effect(bool out)388 void start_extravision_effect(
389 	bool out)
390 {
391 	// LP change: doing this by setting targets
392 	world_view->target_field_of_view = out ? EXTRAVISION_FIELD_OF_VIEW : NORMAL_FIELD_OF_VIEW;
393 }
394 
395 // LP addition:
396 void start_tunnel_vision_effect(bool out);
397 
398 //CP addition: returns the screen info
get_screen_mode(void)399 screen_mode_data *get_screen_mode(
400 	void)
401 {
402 	return &screen_mode;
403 }
404 
405 /* These should be replaced with better preferences control functions */
406 // LP change: generalizing this
game_window_is_full_screen(void)407 bool game_window_is_full_screen(
408 	void)
409 {
410 	return !alephone::Screen::instance()->hud();
411 }
412 
413 
change_gamma_level(short gamma_level)414 void change_gamma_level(
415 	short gamma_level)
416 {
417 	screen_mode.gamma_level= gamma_level;
418 	gamma_correct_color_table(uncorrected_color_table, world_color_table, gamma_level);
419 	stop_fade();
420 	obj_copy(*visible_color_table, *world_color_table);
421 	assert_world_color_table(interface_color_table, world_color_table);
422 	change_screen_mode(&screen_mode, false);
423 	set_fade_effect(NONE);
424 }
425 
426 /* ---------- private code */
427 
428 // LP addition: routine for displaying text
429 
430 // Globals for communicating with the SDL contents of DisplayText
431 static SDL_Surface *DisplayTextDest = NULL;
432 static font_info *DisplayTextFont = NULL;
433 static short DisplayTextStyle = 0;
434 
435 /*static*/ void DisplayText(short BaseX, short BaseY, const char *Text, unsigned char r = 0xff, unsigned char g = 0xff, unsigned char b = 0xff)
436 {
437 #ifdef HAVE_OPENGL
438 	// OpenGL version:
439 	// activate only in the main view, and also if OpenGL is being used for the overhead map
440 	if((OGL_MapActive || !world_view->overhead_map_active) && !world_view->terminal_mode_active)
441 		if (OGL_RenderText(BaseX, BaseY, Text, r, g, b)) return;
442 #endif
443 
444 	draw_text(DisplayTextDest, Text, BaseX+1, BaseY+1, SDL_MapRGB(world_pixels->format, 0x00, 0x00, 0x00), DisplayTextFont, DisplayTextStyle);
445 	draw_text(DisplayTextDest, Text, BaseX, BaseY, SDL_MapRGB(world_pixels->format, r, g, b), DisplayTextFont, DisplayTextStyle);
446 
447 }
448 
449 /*static*/ void DisplayTextCursor(SDL_Surface *s, short BaseX, short BaseY, const char *Text, short Offset, unsigned char r = 0xff, unsigned char g = 0xff, unsigned char b = 0xff)
450 {
451 	SDL_Rect cursor_rect;
452 	cursor_rect.x = BaseX + text_width(Text, Offset, DisplayTextFont, DisplayTextStyle);
453 	cursor_rect.w = 1;
454 	cursor_rect.y = BaseY - DisplayTextFont->get_ascent();
455 	cursor_rect.h = DisplayTextFont->get_height();
456 
457 	SDL_Rect shadow_rect = cursor_rect;
458 	shadow_rect.x += 1;
459 	shadow_rect.y += 1;
460 
461 #ifdef HAVE_OPENGL
462 	// OpenGL version:
463 	// activate only in the main view, and also if OpenGL is being used for the overhead map
464 	if((OGL_MapActive || !world_view->overhead_map_active) && !world_view->terminal_mode_active)
465 		if (OGL_RenderTextCursor(cursor_rect, r, g, b)) return;
466 #endif
467 
468 	SDL_FillRect(s, &shadow_rect, SDL_MapRGB(world_pixels->format, 0x00, 0x00, 0x00));
469 	SDL_FillRect(s, &cursor_rect, SDL_MapRGB(world_pixels->format, r, g, b));
470 }
471 
DisplayTextWidth(const char * Text)472 uint16 DisplayTextWidth(const char *Text)
473 {
474 	return text_width(Text, DisplayTextFont, DisplayTextStyle);
475 }
476 
update_fps_display(SDL_Surface * s)477 static void update_fps_display(SDL_Surface *s)
478 {
479 	if (displaying_fps && !player_in_terminal_mode(current_player_index))
480 	{
481 		uint32 ticks = SDL_GetTicks();
482 		char fps[sizeof("120.00fps (10000 ms)")];
483 		char ms[sizeof("(10000 ms)")];
484 
485 		frame_ticks[frame_index]= ticks;
486 		frame_index= (frame_index+1)%FRAME_SAMPLE_SIZE;
487 		if (frame_count<FRAME_SAMPLE_SIZE)
488 		{
489 			frame_count+= 1;
490 			strncpy(fps, "--", sizeof(fps));
491 		}
492 		else
493 		{
494 			float count = (FRAME_SAMPLE_SIZE * MACHINE_TICKS_PER_SECOND) / float(ticks-frame_ticks[frame_index]);
495 			int latency = NetGetLatency();
496 			if (latency > -1)
497 				sprintf(ms, "(%i ms)", latency);
498 			else
499 				ms[0] = '\0';
500 
501 			if (count >= TICKS_PER_SECOND)
502 				sprintf(fps, "%lu%s %s",(unsigned long)TICKS_PER_SECOND,".00fps", ms);
503 			else
504 				sprintf(fps, "%3.2ffps %s", count, ms);
505 		}
506 
507 		FontSpecifier& Font = GetOnScreenFont();
508 
509 		DisplayTextDest = s;
510 		DisplayTextFont = Font.Info;
511 		DisplayTextStyle = Font.Style;
512 		short X0 = 0;
513 		short Y0 = s->h;
514 
515 		// The line spacing is a generalization of "5" for larger fonts
516 		short Offset = Font.LineSpacing / 3;
517 		short X = X0 + Offset;
518 		short Y = Y0 - Offset;
519 		if (Console::instance()->input_active())
520 		{
521 			Y -= Font.LineSpacing;
522 		}
523 		DisplayText(X,Y,fps);
524 
525 	}
526 	else
527 	{
528 		frame_count= frame_index= 0;
529 	}
530 }
531 
532 
DisplayPosition(SDL_Surface * s)533 static void DisplayPosition(SDL_Surface *s)
534 {
535 	if (!ShowPosition) return;
536 
537 	FontSpecifier& Font = GetOnScreenFont();
538 
539 	DisplayTextDest = s;
540 	DisplayTextFont = Font.Info;
541 	DisplayTextStyle = Font.Style;
542 	short X0 = 0;
543 	short Y0 = 0;
544 
545 	short LineSpacing = Font.LineSpacing;
546 	short X = X0 + LineSpacing/3;
547 	short Y = Y0 + LineSpacing;
548 	const float FLOAT_WORLD_ONE = float(WORLD_ONE);
549 	const float AngleConvert = 360/float(FULL_CIRCLE);
550 	sprintf(temporary, "X       = %8.3f",world_view->origin.x/FLOAT_WORLD_ONE);
551 	DisplayText(X,Y,temporary);
552 	Y += LineSpacing;
553 	sprintf(temporary, "Y       = %8.3f",world_view->origin.y/FLOAT_WORLD_ONE);
554 	DisplayText(X,Y,temporary);
555 	Y += LineSpacing;
556 	sprintf(temporary, "Z       = %8.3f",world_view->origin.z/FLOAT_WORLD_ONE);
557 	DisplayText(X,Y,temporary);
558 	Y += LineSpacing;
559 	sprintf(temporary, "Polygon = %8d",world_view->origin_polygon_index);
560 	DisplayText(X,Y,temporary);
561 	Y += LineSpacing;
562 	short Angle = world_view->yaw;
563 	if (Angle > HALF_CIRCLE) Angle -= FULL_CIRCLE;
564 	sprintf(temporary, "Yaw     = %8.3f",AngleConvert*Angle);
565 	DisplayText(X,Y,temporary);
566 	Y += LineSpacing;
567 	Angle = world_view->pitch;
568 	if (Angle > HALF_CIRCLE) Angle -= FULL_CIRCLE;
569 	sprintf(temporary, "Pitch   = %8.3f",AngleConvert*Angle);
570 	DisplayText(X,Y,temporary);
571 
572 }
573 
DisplayInputLine(SDL_Surface * s)574 static void DisplayInputLine(SDL_Surface *s)
575 {
576   if (Console::instance()->input_active() &&
577       !Console::instance()->displayBuffer().empty()) {
578     FontSpecifier& Font = GetOnScreenFont();
579 
580   DisplayTextDest = s;
581   DisplayTextFont = Font.Info;
582   DisplayTextStyle = Font.Style;
583   short X0 = 0;
584   short Y0 = s->h;
585 
586   short Offset = Font.LineSpacing / 3;
587   short X = X0 + Offset;
588   short Y = Y0 - Offset;
589   const char *buf = Console::instance()->displayBuffer().c_str();
590   DisplayText(X, Y, buf);
591   DisplayTextCursor(s, X, Y, buf, Console::instance()->cursor_position());
592   }
593 }
594 
DisplayMessages(SDL_Surface * s)595 static void DisplayMessages(SDL_Surface *s)
596 {
597 	FontSpecifier& Font = GetOnScreenFont();
598 
599 	DisplayTextDest = s;
600 	DisplayTextFont = Font.Info;
601 	DisplayTextStyle = Font.Style;
602 	short X0 = 0;
603 	short Y0 = 0;
604 
605 	short LineSpacing = Font.LineSpacing;
606 	short X = X0 + LineSpacing/3;
607 	short Y = Y0 + LineSpacing;
608 	if (ShowPosition) Y += 6*LineSpacing;	// Make room for the position data
609 	/* SB */
610 	short view = nonlocal_script_hud ? local_player_index : current_player_index;
611 	for(int i = 0; i < MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS; ++i) {
612 		if(!ScriptHUDElements[view][i].text.empty()) {
613 			short x2 = X, sk = Font.TextWidth("AAAAAAAAAAAAAA"),
614 				icon_skip, icon_drop;
615 			switch(get_screen_mode()->hud_scale_level) {
616 			case 0:
617 				icon_drop = 2;
618 				break;
619 			case 1:
620 				if(MainScreenLogicalHeight() >= 960)
621 					icon_drop = 4;
622 				else
623 					icon_drop = 2;
624 				break;
625 			case 2:
626 				if(MainScreenLogicalHeight() >= 480)
627 					icon_drop = MainScreenLogicalHeight() * 2 / 480;
628 				else
629 					icon_drop = 2;
630 				break;
631 			}
632 			bool had_icon;
633 			/* Yes, I KNOW this is the same i as above. I know what I'm doing. */
634 			for(i = 0; i < MAXIMUM_NUMBER_OF_SCRIPT_HUD_ELEMENTS; ++i) {
635 				if(ScriptHUDElements[view][i].text.empty()) continue;
636 				if(ScriptHUDElements[view][i].isicon) {
637 					had_icon = true;
638 
639 					SDL_Rect rect;
640 					rect.x = x2;
641 					rect.y = Y - Font.Ascent + Font.Leading;
642 					rect.w = rect.h = 16;
643                                         icon_skip = 20;
644 					switch(get_screen_mode()->hud_scale_level) {
645 					case 1:
646 						if(MainScreenLogicalHeight() >= 960) {
647 							rect.w *= 2;
648 							rect.h *= 2;
649 							icon_skip *= 2;
650 						}
651 						break;
652 					case 2:
653 						if(MainScreenLogicalHeight() > 480) {
654 							rect.w = rect.w * MainScreenLogicalHeight() / 480;
655 							rect.h = rect.h * MainScreenLogicalHeight() / 480;
656 							icon_skip = icon_skip * MainScreenLogicalHeight() / 480;
657 						}
658 						break;
659 					}
660 #ifdef HAVE_OPENGL
661 					if(OGL_IsActive()) {
662 						ScriptHUDElements[view][i].ogl_blitter.Draw(rect);
663 					}
664 					else
665 #endif
666 					{
667 						ScriptHUDElements[view][i].sdl_blitter.Draw(s, rect);
668 					}
669 					x2 += icon_skip;
670 				}
671 				SDL_Color color;
672 				_get_interface_color(ScriptHUDElements[view][i].color+_computer_interface_text_color, &color);
673 				DisplayText(x2,Y + (ScriptHUDElements[view][i].isicon ? icon_drop : 0),ScriptHUDElements[view][i].text.c_str(), color.r, color.g, color.b);
674 				x2 += sk;
675 				if(ScriptHUDElements[view][i].isicon)
676 					x2 -= icon_skip;
677 			}
678 			Y += LineSpacing;
679 			if(had_icon)
680 				Y += icon_drop;
681 			break;
682 		}
683 	}
684 	/* /SB */
685 	//	for (int k=0; k<NumScreenMessages; k++)
686 	for (int k = NumScreenMessages - 1; k >= 0; k--)
687 	{
688 	  int Which = (MostRecentMessage+NumScreenMessages-k) % NumScreenMessages;
689 		while (Which < 0)
690 			Which += NumScreenMessages;
691 		ScreenMessage& Message = Messages[Which];
692 		if (Message.TimeRemaining <= 0) continue;
693 		Message.TimeRemaining--;
694 
695 		DisplayText(X,Y,Message.Text);
696 		Y += LineSpacing;
697 	}
698 
699 }
700 
701 extern short local_player_index;
702 extern bool game_is_networked;
703 
DisplayNetMicStatus(SDL_Surface * s)704 static void DisplayNetMicStatus(SDL_Surface *s)
705 {
706 	if (!game_is_networked) return;
707 
708 	// the net mic status is a message, and a colored text "icon"
709 	string icon;
710 	string status;
711 	SDL_Color iconColor;
712 
713 	if (!current_netgame_allows_microphone())
714 	{
715 		if (dynamic_world->speaking_player_index == local_player_index)
716 		{
717 			status = "disabled";
718 			icon = "  x";
719 			iconColor.r = 0xff;
720 			iconColor.g = iconColor.b = 0x0;
721 			iconColor.a = 0xff;
722 
723 		}
724 		else
725 		{
726 			return;
727 		}
728 	}
729 	else if (dynamic_world->speaking_player_index == local_player_index)
730 	{
731 		if (GET_GAME_OPTIONS() & _force_unique_teams)
732 			status = "all";
733 		else
734 			status = "team";
735 		icon = "<!>";
736 
737 		player_data *player = get_player_data(dynamic_world->speaking_player_index);
738 		if (GET_GAME_OPTIONS() & _force_unique_teams)
739 			_get_interface_color(PLAYER_COLOR_BASE_INDEX + player->color, &iconColor);
740 		else
741 			_get_interface_color(PLAYER_COLOR_BASE_INDEX + player->team, &iconColor);
742 	}
743 	else if (dynamic_world->speaking_player_index != NONE)
744 	{
745 		// find the name and color of the person who is speaking
746 		player_data *player = get_player_data(dynamic_world->speaking_player_index);
747 		status = player->name;
748 		_get_interface_color(PLAYER_COLOR_BASE_INDEX + player->color, &iconColor);
749 		icon = ">!<";
750 	}
751 	else
752 	{
753 		return;
754 	}
755 
756 	FontSpecifier& Font = GetOnScreenFont();
757 
758 	DisplayTextDest = s;
759 	DisplayTextFont = Font.Info;
760 	DisplayTextStyle = Font.Style;
761 
762 	short Y = s->h - Font.LineSpacing / 3;
763 	if (Console::instance()->input_active())
764 	{
765 		Y -= Font.LineSpacing;
766 	}
767 	short Xicon = s->w - DisplayTextWidth(icon.c_str()) - Font.LineSpacing / 3;
768 	short Xstatus = Xicon - DisplayTextWidth(" ") - DisplayTextWidth(status.c_str());
769 
770 	DisplayText(Xicon, Y, icon.c_str(), iconColor.r, iconColor.g, iconColor.b);
771 	DisplayText(Xstatus, Y, status.c_str());
772 }
773 
774 static const SDL_Color Green = { 0x0, 0xff, 0x0, 0xff };
775 static const SDL_Color Yellow = { 0xff, 0xff, 0x0, 0xff };
776 static const SDL_Color Red = { 0xff, 0x0, 0x0, 0xff };
777 static const SDL_Color Gray = { 0x7f, 0x7f, 0x7f, 0xff };
778 
DisplayScores(SDL_Surface * s)779 static void DisplayScores(SDL_Surface *s)
780 {
781 	if (!game_is_networked || !ShowScores) return;
782 
783 	// assume a proportional font
784 	int CWidth = DisplayTextWidth("W");
785 
786 	// field widths
787 	static const int kNameWidth = 20;
788 	int WName = CWidth * kNameWidth;
789 	static const int kScoreWidth = 5;
790 	int WScore = CWidth * kScoreWidth;
791 	static const int kPingWidth = 7;
792 	int WPing = CWidth * kPingWidth;
793 	int WJitter = CWidth * kPingWidth;
794 	int WErrors = CWidth * kPingWidth;
795 	static const int kIdWidth = 2;
796 	int WId = CWidth * kIdWidth;
797 
798 	FontSpecifier& Font = GetOnScreenFont();
799 
800 	DisplayTextDest = s;
801 	DisplayTextFont = Font.Info;
802 	DisplayTextStyle = Font.Style;
803 
804 	int H = Font.LineSpacing * (dynamic_world->player_count + 1);
805 	int W = WName + WScore + WPing + WJitter + WErrors + WId;
806 	int X = (s->w - W) / 2;
807 	int Y = std::max((s->h - H) / 2, Font.LineSpacing * NumScreenMessages) + Font.LineSpacing;
808 
809 	int XName = X;
810 	int XScore = XName + WName + CWidth;
811 	int XPing = XScore + WScore + CWidth;
812 	int XJitter = XPing + WPing + CWidth;
813 	int XErrors = XJitter + WPing + CWidth;
814 	int XId = XErrors + WPing + CWidth;
815 
816 	// draw headers
817 	DisplayText(XName, Y, "Name", 0xbf, 0xbf, 0xbf);
818 	DisplayText(XScore + WScore - DisplayTextWidth("Score"), Y, "Score", 0xbf, 0xbf, 0xbf);
819 	DisplayText(XPing + WPing - DisplayTextWidth("Delay"), Y, "Delay", 0xbf, 0xbf, 0xbf);
820 	DisplayText(XJitter + WPing - DisplayTextWidth("Jitter"), Y, "Jitter", 0xbf, 0xbf, 0xbf);
821 	DisplayText(XErrors + WPing - DisplayTextWidth("Errors"), Y, "Errors", 0xbf, 0xbf, 0xbf);
822 	DisplayText(XId + WId - DisplayTextWidth("ID"), Y, "ID", 0xbf, 0xbf, 0xbf);
823 	Y += Font.LineSpacing;
824 	player_ranking_data rankings[MAXIMUM_NUMBER_OF_PLAYERS];
825 	calculate_player_rankings(rankings);
826 	for (int i = 0; i < dynamic_world->player_count; ++i)
827 	{
828 		player_data *player = get_player_data(rankings[i].player_index);
829 
830 		SDL_Color color;
831 		_get_interface_color(PLAYER_COLOR_BASE_INDEX + player->color, &color);
832 
833 		strncpy(temporary, player->name, 256);
834 		temporary[kNameWidth + 1] = '\0';
835 		DisplayText(XName, Y, temporary, color.r, color.g, color.b);
836 
837 		calculate_ranking_text(temporary, rankings[i].ranking);
838 		temporary[kScoreWidth + 1] = '\0';
839 		DisplayText(XScore + WScore - DisplayTextWidth(temporary), Y, temporary, color.r, color.g, color.b);
840 
841 		const NetworkStats& stats = NetGetStats(rankings[i].player_index);
842 
843 		if (stats.latency == NetworkStats::invalid)
844 		{
845 			strncpy(temporary, " ", 256);
846 		}
847 		else if (stats.latency == NetworkStats::disconnected)
848 		{
849 			strncpy(temporary, "DC", 256);
850 		}
851 		else
852 		{
853 			sprintf(temporary, "%i ms", stats.latency);
854 		}
855 		SDL_Color color2;
856 		if (stats.latency == NetworkStats::invalid || stats.latency == NetworkStats::disconnected)
857 			color2 = Gray;
858 		else if (stats.latency < 150)
859 			color2 = Green;
860 		else if (stats.latency < 350)
861 			color2 = Yellow;
862 		else
863 			color2 = Red;
864 
865 		temporary[kPingWidth + 1] = '\0';
866 		DisplayText(XPing + WPing - DisplayTextWidth(temporary), Y, temporary, color2.r, color2.g, color2.b);
867 
868 		if (stats.jitter == NetworkStats::invalid)
869 		{
870 			strncpy(temporary, " ", 256);
871 		}
872 		else if (stats.jitter == NetworkStats::disconnected)
873 		{
874 			strncpy(temporary, "DC", 256);
875 		}
876 		else
877 		{
878 			sprintf(temporary, "%i ms", stats.jitter);
879 		}
880 		if (stats.jitter == NetworkStats::invalid || stats.jitter == NetworkStats::disconnected)
881 		{
882 			color2 = Gray;
883 		}
884 		else if (stats.jitter < 75)
885 		{
886 			color2 = Green;
887 		}
888 		else if (stats.jitter < 150)
889 		{
890 			color2 = Yellow;
891 		}
892 		else
893 		{
894 			color2 = Red;
895 		}
896 		temporary[kPingWidth + 1] = '\0';
897 		DisplayText(XJitter + WPing - DisplayTextWidth(temporary), Y, temporary, color2.r, color2.g, color2.b);
898 
899 		sprintf(temporary, "%i", stats.errors);
900 		temporary[kPingWidth + 1] = '\0';
901 		if (stats.errors > 0)
902 			color2 = Yellow;
903 		else
904 			color2 = Green;
905 		DisplayText(XErrors + WPing - DisplayTextWidth(temporary), Y, temporary, color2.r, color2.g, color2.b);
906 
907 		sprintf(temporary, "%i", rankings[i].player_index);
908 		DisplayText(XId + WId - DisplayTextWidth(temporary), Y, temporary, color.r, color.g, color.b);
909 
910 		Y += Font.LineSpacing;
911 	}
912 }
913 
set_overhead_map_status(bool status)914 static void set_overhead_map_status( /* it has changed, this is the new status */
915 	bool status)
916 {
917 	world_view->overhead_map_active= status;
918 }
919 
set_terminal_status(bool status)920 static void set_terminal_status( /* It has changed, this is the new state.. */
921 	bool status)
922 {
923 	bool restore_effect= false;
924 	short effect = 0, phase = 0;
925 
926 	if(!status)
927 	{
928 		if(world_view->effect==_render_effect_fold_out)
929 		{
930 			effect= world_view->effect;
931 			phase= world_view->effect_phase;
932 			restore_effect= true;
933 		}
934 	}
935 	world_view->terminal_mode_active= status;
936 
937 	if(restore_effect)
938 	{
939 		world_view->effect= effect;
940 		world_view->effect_phase= phase;
941 	}
942 
943 	/* Dirty the view.. */
944 	dirty_terminal_view(current_player_index);
945 }
946 
947 // For getting and setting tunnel-vision mode
GetTunnelVision()948 bool GetTunnelVision() {return world_view->tunnel_vision_active;}
SetTunnelVision(bool TunnelVisionOn)949 bool SetTunnelVision(bool TunnelVisionOn)
950 {
951 	// LP: simplifying tunnel-vision-activation/deactivation behavior
952 	world_view->tunnel_vision_active = TunnelVisionOn;
953 	start_tunnel_vision_effect(TunnelVisionOn);
954 	return world_view->tunnel_vision_active;
955 }
956 
957 // This is for requesting the drawing of the Heads-Up Display;
958 // this is done because its drawing is now done when the main display is drawn
RequestDrawingHUD()959 void RequestDrawingHUD()
960 {
961 	HUD_RenderRequest = true;
962 }
963 
964 // This is for requesting the drawing of the Terminal;
965 // this is done because its drawing is now done when the main display is drawn
RequestDrawingTerm()966 void RequestDrawingTerm()
967 {
968 	Term_RenderRequest = true;
969 }
970 
971 // LP addition: display message on the screen;
972 // this really puts the current message into a buffer
973 // Code cribbed from csstrings
screen_printf(const char * format,...)974 void screen_printf(const char *format, ...)
975 {
976 	MostRecentMessage = (MostRecentMessage + 1) % NumScreenMessages;
977 	while (MostRecentMessage < 0)
978 		MostRecentMessage += NumScreenMessages;
979 	ScreenMessage& Message = Messages[MostRecentMessage];
980 
981 	Message.TimeRemaining = 7*TICKS_PER_SECOND;
982 
983 	va_list list;
984 
985 	va_start(list,format);
986 	// ZZZ: [v]sprintf is evil, generally: hard to guarantee you don't overflow target buffer
987 	// using [v]snprintf instead
988 	vsnprintf(Message.Text,sizeof(Message.Text),format,list);
989 	va_end(list);
990 }
991