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