/* * SCR.java * Copyright (C) 2003 * * $Id: SCR.java,v 1.4 2004-07-08 20:56:49 hzi Exp $ */ /* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package jake2.client; import jake2.Globals; import jake2.game.Cmd; import jake2.game.cvar_t; import jake2.qcommon.*; import jake2.sound.*; import jake2.sys.Sys; import jake2.util.Vargs; import java.awt.Dimension; /** * SCR */ public final class SCR extends Globals { // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc static String[][] sb_nums = { { "num_0", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_minus" }, { "anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5", "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus" } }; /* full screen console put up loading plaque blanked background with loading plaque blanked background with menu cinematics full screen image for quit and victory end of unit intermissions */ static float scr_con_current; // aproaches scr_conlines at scr_conspeed static float scr_conlines; // 0.0 to 1.0 lines of console to display static boolean scr_initialized; // ready to draw static int scr_draw_loading; // scr_vrect ist in Globals definiert // position of render window on screen static cvar_t scr_viewsize; static cvar_t scr_conspeed; static cvar_t scr_centertime; static cvar_t scr_showturtle; static cvar_t scr_showpause; static cvar_t scr_printspeed; static cvar_t scr_netgraph; static cvar_t scr_timegraph; static cvar_t scr_debuggraph; static cvar_t scr_graphheight; static cvar_t scr_graphscale; static cvar_t scr_graphshift; static cvar_t scr_drawall; static cvar_t fps; static dirty_t scr_dirty = new dirty_t(); static dirty_t[] scr_old_dirty = { new dirty_t(), new dirty_t()}; static String crosshair_pic; static int crosshair_width, crosshair_height; static class dirty_t { int x1; int x2; int y1; int y2; } /* =============================================================================== BAR GRAPHS =============================================================================== */ // typedef struct // { // float value; // int color; // } graphsamp_t; static class graphsamp_t { float value; int color; } static int current; static graphsamp_t[] values = new graphsamp_t[1024]; static { for (int n = 0; n < 1024; n++) values[n] = new graphsamp_t(); } /* ============== SCR_DebugGraph ============== */ public static void DebugGraph(float value, int color) { values[current & 1023].value = value; values[current & 1023].color = color; current++; } /* ============== SCR_DrawDebugGraph ============== */ static void DrawDebugGraph() { int a, x, y, w, i, h; float v; int color; // draw the graph w = scr_vrect.width; x = scr_vrect.x; y = scr_vrect.y + scr_vrect.height; re.DrawFill(x, (int) (y - scr_graphheight.value), w, (int) scr_graphheight.value, 8); for (a = 0; a < w; a++) { i = (current - 1 - a + 1024) & 1023; v = values[i].value; color = values[i].color; v = v * scr_graphscale.value + scr_graphshift.value; if (v < 0) v += scr_graphheight.value * (1 + (int) (-v / scr_graphheight.value)); h = (int) v % (int) scr_graphheight.value; re.DrawFill(x + w - 1 - a, y - h, 1, h, color); } } /* =============================================================================== CENTER PRINTING =============================================================================== */ // char scr_centerstring[1024]; static String scr_centerstring; static float scr_centertime_start; // for slow victory printing static float scr_centertime_off; static int scr_center_lines; static int scr_erase_center; /* ============== SCR_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ static void CenterPrint(String str) { //char *s; int s; StringBuffer line = new StringBuffer(64); int i, j, l; //strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); scr_centerstring = str; scr_centertime_off = scr_centertime.value; scr_centertime_start = cl.time; // count the number of lines for centering scr_center_lines = 1; s = 0; while (s < str.length()) { if (str.charAt(s) == '\n') scr_center_lines++; s++; } // echo it to the console Com.Printf( "\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); s = 0; if (str.length() != 0) { do { // scan the width of the line for (l = 0; l < 40 && (l + s) < str.length(); l++) if (str.charAt(s + l) == '\n' || str.charAt(s + l) == 0) break; for (i = 0; i < (40 - l) / 2; i++) line.append(' '); for (j = 0; j < l; j++) { line.append(str.charAt(s + j)); } line.append('\n'); Com.Printf(line.toString()); while (s < str.length() && str.charAt(s) != '\n') s++; if (s == str.length()) break; s++; // skip the \n } while (true); } Com.Printf( "\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); Console.ClearNotify(); } static void DrawCenterString() { String cs = scr_centerstring + "\0"; int start; int l; int j; int x, y; int remaining; if (cs == null) return; if (cs.length() == 0) return; // the finale prints the characters one at a time remaining = 9999; scr_erase_center = 0; start = 0; if (scr_center_lines <= 4) y = (int) (viddef.height * 0.35); else y = 48; do { // scan the width of the line for (l = 0; l < 40; l++) if (start + l == cs.length() - 1 || cs.charAt(start + l) == '\n') break; x = (viddef.width - l * 8) / 2; SCR.AddDirtyPoint(x, y); for (j = 0; j < l; j++, x += 8) { re.DrawChar(x, y, cs.charAt(start + j)); if (remaining == 0) return; remaining--; } SCR.AddDirtyPoint(x, y + 8); y += 8; while (start < cs.length() && cs.charAt(start) != '\n') start++; if (start == cs.length()) break; start++; // skip the \n } while (true); } static void CheckDrawCenterString() { scr_centertime_off -= cls.frametime; if (scr_centertime_off <= 0) return; DrawCenterString(); } // ============================================================================= /* ================= SCR_CalcVrect Sets scr_vrect, the coordinates of the rendered window ================= */ static void CalcVrect() { int size; // bound viewsize if (scr_viewsize.value < 40) Cvar.Set("viewsize", "40"); if (scr_viewsize.value > 100) Cvar.Set("viewsize", "100"); size = (int) scr_viewsize.value; scr_vrect.width = viddef.width * size / 100; scr_vrect.width &= ~7; scr_vrect.height = viddef.height * size / 100; scr_vrect.height &= ~1; scr_vrect.x = (viddef.width - scr_vrect.width) / 2; scr_vrect.y = (viddef.height - scr_vrect.height) / 2; } /* ================= SCR_SizeUp_f Keybinding command ================= */ static void SizeUp_f() { Cvar.SetValue("viewsize", scr_viewsize.value + 10); } /* ================= SCR_SizeDown_f Keybinding command ================= */ static void SizeDown_f() { Cvar.SetValue("viewsize", scr_viewsize.value - 10); } /* ================= SCR_Sky_f Set a specific sky and rotation speed ================= */ static void Sky_f() { float rotate; float[] axis = { 0, 0, 0 }; if (Cmd.Argc() < 2) { Com.Printf("Usage: sky \n"); return; } if (Cmd.Argc() > 2) rotate = Float.parseFloat(Cmd.Argv(2)); else rotate = 0; if (Cmd.Argc() == 6) { axis[0] = Float.parseFloat(Cmd.Argv(3)); axis[1] = Float.parseFloat(Cmd.Argv(4)); axis[2] = Float.parseFloat(Cmd.Argv(5)); } else { axis[0] = 0; axis[1] = 0; axis[2] = 1; } re.SetSky(Cmd.Argv(1), rotate, axis); } // ============================================================================ /* ================== SCR_Init ================== */ static void Init() { scr_viewsize = Cvar.Get("viewsize", "100", CVAR_ARCHIVE); scr_conspeed = Cvar.Get("scr_conspeed", "3", 0); scr_showturtle = Cvar.Get("scr_showturtle", "0", 0); scr_showpause = Cvar.Get("scr_showpause", "1", 0); scr_centertime = Cvar.Get("scr_centertime", "2.5", 0); scr_printspeed = Cvar.Get("scr_printspeed", "8", 0); scr_netgraph = Cvar.Get("netgraph", "1", 0); scr_timegraph = Cvar.Get("timegraph", "1", 0); scr_debuggraph = Cvar.Get("debuggraph", "1", 0); scr_graphheight = Cvar.Get("graphheight", "32", 0); scr_graphscale = Cvar.Get("graphscale", "1", 0); scr_graphshift = Cvar.Get("graphshift", "0", 0); scr_drawall = Cvar.Get("scr_drawall", "1", 0); fps = Cvar.Get("fps", "0", 0); // // register our commands // Cmd.AddCommand("timerefresh", new xcommand_t() { public void execute() { TimeRefresh_f(); } }); Cmd.AddCommand("loading", new xcommand_t() { public void execute() { Loading_f(); } }); Cmd.AddCommand("sizeup", new xcommand_t() { public void execute() { SizeUp_f(); } }); Cmd.AddCommand("sizedown", new xcommand_t() { public void execute() { SizeDown_f(); } }); Cmd.AddCommand("sky", new xcommand_t() { public void execute() { Sky_f(); } }); scr_initialized = true; } /* ============== SCR_DrawNet ============== */ static void DrawNet() { if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < CMD_BACKUP - 1) return; re.DrawPic(scr_vrect.x + 64, scr_vrect.y, "net"); } /* ============== SCR_DrawPause ============== */ static void DrawPause() { Dimension dim = new Dimension(); if (scr_showpause.value == 0) // turn off for screenshots return; if (cl_paused.value == 0) return; re.DrawGetPicSize(dim, "pause"); re.DrawPic((viddef.width - dim.width) / 2, viddef.height / 2 + 8, "pause"); } /* ============== SCR_DrawLoading ============== */ static void DrawLoading() { Dimension dim = new Dimension(); if (scr_draw_loading == 0) return; scr_draw_loading = 0; re.DrawGetPicSize(dim, "loading"); re.DrawPic((viddef.width - dim.width) / 2, (viddef.height - dim.height) / 2, "loading"); } // ============================================================================= /* ================== SCR_RunConsole Scroll it up or down ================== */ static void RunConsole() { // decide on the height of the console if (cls.key_dest == key_console) scr_conlines = 0.5f; // half screen else scr_conlines = 0; // none visible if (scr_conlines < scr_con_current) { scr_con_current -= scr_conspeed.value * cls.frametime; if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { scr_con_current += scr_conspeed.value * cls.frametime; if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } } /* ================== SCR_DrawConsole ================== */ static void DrawConsole() { Console.CheckResize(); if (cls.state == ca_disconnected || cls.state == ca_connecting) { // forced full screen console Console.DrawConsole(1.0f); return; } if (cls.state != ca_active || !cl.refresh_prepped) { // connected, but can't render Console.DrawConsole(0.5f); re.DrawFill(0, viddef.height / 2, viddef.width, viddef.height / 2, 0); return; } if (scr_con_current != 0) { Console.DrawConsole(scr_con_current); } else { if (cls.key_dest == key_game || cls.key_dest == key_message) Console.DrawNotify(); // only draw notify in game } } // ============================================================================= /* ================ SCR_BeginLoadingPlaque ================ */ public static void BeginLoadingPlaque() { S.StopAllSounds(); cl.sound_prepped = false; // don't play ambients if (cls.disable_screen != 0) return; if (developer.value != 0) return; if (cls.state == ca_disconnected) return; // if at console, don't bring up the plaque if (cls.key_dest == key_console) return; if (cl.cinematictime > 0) scr_draw_loading = 2; // clear to balack first else scr_draw_loading = 1; UpdateScreen(); cls.disable_screen = Sys.Milliseconds(); cls.disable_servercount = cl.servercount; } /* ================ SCR_EndLoadingPlaque ================ */ public static void EndLoadingPlaque() { cls.disable_screen = 0; Console.ClearNotify(); } /* ================ SCR_Loading_f ================ */ static void Loading_f() { BeginLoadingPlaque(); } /* ================ SCR_TimeRefresh_f ================ */ static void TimeRefresh_f() { int i; int start, stop; float time; if (cls.state != ca_active) return; start = Sys.Milliseconds(); if (Cmd.Argc() == 2) { // run without page flipping re.BeginFrame(0); for (i = 0; i < 128; i++) { cl.refdef.viewangles[1] = i / 128.0f * 360.0f; re.RenderFrame(cl.refdef); } re.EndFrame(); } else { for (i = 0; i < 128; i++) { cl.refdef.viewangles[1] = i / 128.0f * 360.0f; re.BeginFrame(0); re.RenderFrame(cl.refdef); re.EndFrame(); } } stop = Sys.Milliseconds(); time = (stop - start) / 1000.0f; Com.Printf("%f seconds (%f fps)\n", new Vargs(2).add(time).add(128.0f / time)); } static void DirtyScreen() { AddDirtyPoint(0, 0); AddDirtyPoint(viddef.width - 1, viddef.height - 1); } /* ============== SCR_TileClear Clear any parts of the tiled background that were drawn on last frame ============== */ static void TileClear() { int i; int top, bottom, left, right; dirty_t clear = new dirty_t(); if (scr_drawall.value != 0) DirtyScreen(); // for power vr or broken page flippers... if (scr_con_current == 1.0f) return; // full screen console if (scr_viewsize.value == 100) return; // full screen rendering if (cl.cinematictime > 0) return; // full screen cinematic // erase rect will be the union of the past three frames // so tripple buffering works properly clear = scr_dirty; for (i = 0; i < 2; i++) { if (scr_old_dirty[i].x1 < clear.x1) clear.x1 = scr_old_dirty[i].x1; if (scr_old_dirty[i].x2 > clear.x2) clear.x2 = scr_old_dirty[i].x2; if (scr_old_dirty[i].y1 < clear.y1) clear.y1 = scr_old_dirty[i].y1; if (scr_old_dirty[i].y2 > clear.y2) clear.y2 = scr_old_dirty[i].y2; } scr_old_dirty[1] = scr_old_dirty[0]; scr_old_dirty[0] = scr_dirty; scr_dirty.x1 = 9999; scr_dirty.x2 = -9999; scr_dirty.y1 = 9999; scr_dirty.y2 = -9999; // don't bother with anything convered by the console) top = (int) (scr_con_current * viddef.height); if (top >= clear.y1) clear.y1 = top; if (clear.y2 <= clear.y1) return; // nothing disturbed top = scr_vrect.y; bottom = top + scr_vrect.height - 1; left = scr_vrect.x; right = left + scr_vrect.width - 1; if (clear.y1 < top) { // clear above view screen i = clear.y2 < top - 1 ? clear.y2 : top - 1; re.DrawTileClear(clear.x1, clear.y1, clear.x2 - clear.x1 + 1, i - clear.y1 + 1, "backtile"); clear.y1 = top; } if (clear.y2 > bottom) { // clear below view screen i = clear.y1 > bottom + 1 ? clear.y1 : bottom + 1; re.DrawTileClear(clear.x1, i, clear.x2 - clear.x1 + 1, clear.y2 - i + 1, "backtile"); clear.y2 = bottom; } if (clear.x1 < left) { // clear left of view screen i = clear.x2 < left - 1 ? clear.x2 : left - 1; re.DrawTileClear(clear.x1, clear.y1, i - clear.x1 + 1, clear.y2 - clear.y1 + 1, "backtile"); clear.x1 = left; } if (clear.x2 > right) { // clear left of view screen i = clear.x1 > right + 1 ? clear.x1 : right + 1; re.DrawTileClear(i, clear.y1, clear.x2 - i + 1, clear.y2 - clear.y1 + 1, "backtile"); clear.x2 = right; } } // =============================================================== static final int STAT_MINUS = 10; // num frame for '-' stats digit static final int ICON_WIDTH = 24; static final int ICON_HEIGHT = 24; static final int CHAR_WIDTH = 16; static final int ICON_SPACE = 8; /* ================ SizeHUDString Allow embedded \n in the string ================ */ static void SizeHUDString(String string, Dimension dim) { int lines, width, current; lines = 1; width = 0; current = 0; for (int i = 0; i < string.length(); i++) { if (string.charAt(i) == '\n') { lines++; current = 0; } else { current++; if (current > width) width = current; } } dim.width = width * 8; dim.height = lines * 8; } static void DrawHUDString(String string, int x, int y, int centerwidth, int xor) { int margin; //char line[1024]; StringBuffer line = new StringBuffer(1024); int i; margin = x; for (int l = 0; l < string.length();) { // scan out one line of text from the string line = new StringBuffer(1024); while (string.charAt(l) != '\n') line.append(string.charAt(l)); l++; if (centerwidth != 0) x = margin + (centerwidth - line.length() * 8) / 2; else x = margin; for (i = 0; i < line.length(); i++) { re.DrawChar(x, y, line.charAt(i) ^ xor); x += 8; } if (l < string.length()) { l++; // skip the \n x = margin; y += 8; } } } /* ============== SCR_DrawField ============== */ static void DrawField(int x, int y, int color, int width, int value) { char ptr; String num; int l; int frame; if (width < 1) return; // draw number string if (width > 5) width = 5; AddDirtyPoint(x, y); AddDirtyPoint(x + width * CHAR_WIDTH + 2, y + 23); num = "" + value; l = num.length(); if (l > width) l = width; x += 2 + CHAR_WIDTH * (width - l); ptr = num.charAt(0); for (int i = 0; i < l; i++) { ptr = num.charAt(i); if (ptr == '-') frame = STAT_MINUS; else frame = ptr - '0'; re.DrawPic(x, y, sb_nums[color][frame]); x += CHAR_WIDTH; } } /* =============== SCR_TouchPics Allows rendering code to cache all needed sbar graphics =============== */ static void TouchPics() { int i, j; for (i = 0; i < 2; i++) for (j = 0; j < 11; j++) re.RegisterPic(sb_nums[i][j]); if (crosshair.value != 0.0f) { if (crosshair.value > 3.0f || crosshair.value < 0.0f) crosshair.value = 3.0f; crosshair_pic = "ch" + (int) crosshair.value; Dimension dim = new Dimension(); re.DrawGetPicSize(dim, crosshair_pic); crosshair_width = dim.width; crosshair_height = dim.height; if (crosshair_width == 0) crosshair_pic = ""; } } /* ================ SCR_ExecuteLayoutString ================ */ static void ExecuteLayoutString(String s) { int x, y; int value; String token; int width; int index; clientinfo_t ci; if (cls.state != ca_active || !cl.refresh_prepped) return; // if (!s[0]) if (s == null || s.length() == 0) return; x = 0; y = 0; width = 3; Com.ParseHelp ph = new Com.ParseHelp(s); while (!ph.isEof()) { token = Com.Parse(ph); if (token.equals("xl")) { token = Com.Parse(ph); x = atoi(token); continue; } if (token.equals("xr")) { token = Com.Parse(ph); x = viddef.width + atoi(token); continue; } if (token.equals("xv")) { token = Com.Parse(ph); x = viddef.width / 2 - 160 + atoi(token); continue; } if (token.equals("yt")) { token = Com.Parse(ph); y = atoi(token); continue; } if (token.equals("yb")) { token = Com.Parse(ph); y = viddef.height + atoi(token); continue; } if (token.equals("yv")) { token = Com.Parse(ph); y = viddef.height / 2 - 120 + atoi(token); continue; } if (token.equals("pic")) { // draw a pic from a stat number token = Com.Parse(ph); value = cl.frame.playerstate.stats[atoi(token)]; if (value >= MAX_IMAGES) Com.Error(ERR_DROP, "Pic >= MAX_IMAGES"); if (cl.configstrings[CS_IMAGES + value] != null) { AddDirtyPoint(x, y); AddDirtyPoint(x + 23, y + 23); re.DrawPic(x, y, cl.configstrings[CS_IMAGES + value]); } continue; } if (token.equals("client")) { // draw a deathmatch client block int score, ping, time; token = Com.Parse(ph); x = viddef.width / 2 - 160 + atoi(token); token = Com.Parse(ph); y = viddef.height / 2 - 120 + atoi(token); AddDirtyPoint(x, y); AddDirtyPoint(x + 159, y + 31); token = Com.Parse(ph); value = atoi(token); if (value >= MAX_CLIENTS || value < 0) Com.Error(ERR_DROP, "client >= MAX_CLIENTS"); ci = cl.clientinfo[value]; token = Com.Parse(ph); score = atoi(token); token = Com.Parse(ph); ping = atoi(token); token = Com.Parse(ph); time = atoi(token); Console.DrawAltString(x + 32, y, ci.name); Console.DrawString(x + 32, y + 8, "Score: "); Console.DrawAltString(x + 32 + 7 * 8, y + 8, "" + score); Console.DrawString(x + 32, y + 16, "Ping: " + ping); Console.DrawString(x + 32, y + 24, "Time: " + time); if (ci.icon == null) ci = cl.baseclientinfo; re.DrawPic(x, y, ci.iconname); continue; } if (token.equals("ctf")) { // draw a ctf client block int score, ping; token = Com.Parse(ph); x = viddef.width / 2 - 160 + atoi(token); token = Com.Parse(ph); y = viddef.height / 2 - 120 + atoi(token); AddDirtyPoint(x, y); AddDirtyPoint(x + 159, y + 31); token = Com.Parse(ph); value = atoi(token); if (value >= MAX_CLIENTS || value < 0) Com.Error(ERR_DROP, "client >= MAX_CLIENTS"); ci = cl.clientinfo[value]; token = Com.Parse(ph); score = atoi(token); token = Com.Parse(ph); ping = atoi(token); if (ping > 999) ping = 999; // sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name); String block = Com.sprintf("%3d %3d %-12.12s", new Vargs(3).add(score).add(ping).add(ci.name)); if (value == cl.playernum) Console.DrawAltString(x, y, block); else Console.DrawString(x, y, block); continue; } if (token.equals("picn")) { // draw a pic from a name token = Com.Parse(ph); AddDirtyPoint(x, y); AddDirtyPoint(x + 23, y + 23); re.DrawPic(x, y, token); continue; } if (token.equals("num")) { // draw a number token = Com.Parse(ph); width = atoi(token); token = Com.Parse(ph); value = cl.frame.playerstate.stats[atoi(token)]; DrawField(x, y, 0, width, value); continue; } if (token.equals("hnum")) { // health number int color; width = 3; value = cl.frame.playerstate.stats[STAT_HEALTH]; if (value > 25) color = 0; // green else if (value > 0) color = (cl.frame.serverframe >> 2) & 1; // flash else color = 1; if ((cl.frame.playerstate.stats[STAT_FLASHES] & 1) != 0) re.DrawPic(x, y, "field_3"); DrawField(x, y, color, width, value); continue; } if (token.equals("anum")) { // ammo number int color; width = 3; value = cl.frame.playerstate.stats[STAT_AMMO]; if (value > 5) color = 0; // green else if (value >= 0) color = (cl.frame.serverframe >> 2) & 1; // flash else continue; // negative number = don't show if ((cl.frame.playerstate.stats[STAT_FLASHES] & 4) != 0) re.DrawPic(x, y, "field_3"); DrawField(x, y, color, width, value); continue; } if (token.equals("rnum")) { // armor number int color; width = 3; value = cl.frame.playerstate.stats[STAT_ARMOR]; if (value < 1) continue; color = 0; // green if ((cl.frame.playerstate.stats[STAT_FLASHES] & 2) != 0) re.DrawPic(x, y, "field_3"); DrawField(x, y, color, width, value); continue; } if (token.equals("stat_string")) { token = Com.Parse(ph); index = atoi(token); if (index < 0 || index >= MAX_CONFIGSTRINGS) Com.Error(ERR_DROP, "Bad stat_string index"); index = cl.frame.playerstate.stats[index]; if (index < 0 || index >= MAX_CONFIGSTRINGS) Com.Error(ERR_DROP, "Bad stat_string index"); Console.DrawString(x, y, cl.configstrings[index]); continue; } if (token.equals("cstring")) { token = Com.Parse(ph); DrawHUDString(token, x, y, 320, 0); continue; } if (token.equals("string")) { token = Com.Parse(ph); Console.DrawString(x, y, token); continue; } if (token.equals("cstring2")) { token = Com.Parse(ph); DrawHUDString(token, x, y, 320, 0x80); continue; } if (token.equals("string2")) { token = Com.Parse(ph); Console.DrawAltString(x, y, token); continue; } if (token.equals("if")) { // draw a number token = Com.Parse(ph); value = cl.frame.playerstate.stats[atoi(token)]; if (value == 0) { // skip to endif // while (s && strcmp(token, "endif") ) // { // token = Com.Parse(ph); // } while (!ph.isEof() && !(token = Com.Parse(ph)).equals("endif")); } continue; } } } /* ================ SCR_DrawStats The status bar is a small layout program that is based on the stats array ================ */ static void DrawStats() { //TODO: SCR.ExecuteLayoutString(cl.configstrings[CS_STATUSBAR]); } /* ================ SCR_DrawLayout ================ */ static final int STAT_LAYOUTS = 13; static void DrawLayout() { if (cl.frame.playerstate.stats[STAT_LAYOUTS] != 0) return; SCR.ExecuteLayoutString(cl.layout); } // ======================================================= /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. ================== */ static void UpdateScreen2() { int numframes; int i; float[] separation = { 0, 0 }; // if the screen is disabled (loading plaque is up, or vid mode changing) // do nothing at all if (cls.disable_screen != 0) { if (Sys.Milliseconds() - cls.disable_screen > 120000) { cls.disable_screen = 0; Com.Printf("Loading plaque timed out.\n"); } return; } if (!scr_initialized || !con.initialized) return; // not initialized yet /* ** range check cl_camera_separation so we don't inadvertently fry someone's ** brain */ if (cl_stereo_separation.value > 1.0) Cvar.SetValue("cl_stereo_separation", 1.0f); else if (cl_stereo_separation.value < 0) Cvar.SetValue("cl_stereo_separation", 0.0f); if (cl_stereo.value != 0) { numframes = 2; separation[0] = -cl_stereo_separation.value / 2; separation[1] = cl_stereo_separation.value / 2; } else { separation[0] = 0; separation[1] = 0; numframes = 1; } for (i = 0; i < numframes; i++) { re.BeginFrame(separation[i]); if (scr_draw_loading == 2) { // loading plaque over black screen Dimension dim = new Dimension(); re.CinematicSetPalette(null); scr_draw_loading = 0; // false re.DrawGetPicSize(dim, "loading"); re.DrawPic((viddef.width - dim.width) / 2, (viddef.height - dim.height) / 2, "loading"); } // if a cinematic is supposed to be running, handle menus // and console specially else if (cl.cinematictime > 0) { if (cls.key_dest == key_menu) { if (cl.cinematicpalette_active) { re.CinematicSetPalette(null); cl.cinematicpalette_active = false; } Menu.Draw(); } else if (cls.key_dest == key_console) { if (cl.cinematicpalette_active) { re.CinematicSetPalette(null); cl.cinematicpalette_active = false; } DrawConsole(); } else { // TODO impl: cl_cin.c for cinematics DrawCinematic(); } } else { // make sure the game palette is active if (cl.cinematicpalette_active) { re.CinematicSetPalette(null); cl.cinematicpalette_active = false; } // do 3D refresh drawing, and then update the screen CalcVrect(); // clear any dirty part of the background TileClear(); V.RenderView(separation[i]); DrawStats(); // TODO impl this if ((cl.frame.playerstate.stats[STAT_LAYOUTS] & 1) != 0) DrawLayout(); if ((cl.frame.playerstate.stats[STAT_LAYOUTS] & 2) != 0) CL.DrawInventory(); DrawNet(); CheckDrawCenterString(); DrawFPS(); // // if (scr_timegraph->value) // SCR_DebugGraph (cls.frametime*300, 0); // // if (scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value) // SCR_DrawDebugGraph (); // DrawPause(); DrawConsole(); Menu.Draw(); DrawLoading(); } } Globals.re.EndFrame(); } /* ================= SCR_DrawCrosshair ================= */ static void DrawCrosshair() { if (crosshair.value == 0.0f) return; if (crosshair.modified) { crosshair.modified = false; SCR.TouchPics(); } if (crosshair_pic.length() == 0) return; re.DrawPic( scr_vrect.x + ((scr_vrect.width - crosshair_width) >> 1), scr_vrect.y + ((scr_vrect.height - crosshair_height) >> 1), crosshair_pic); } private static xcommand_t updateScreenCallback = new xcommand_t() { public void execute() { UpdateScreen2(); } }; // wird anstelle von der richtigen UpdateScreen benoetigt public static void UpdateScreen() { Globals.re.updateScreen(updateScreenCallback); } /* ================= SCR_AddDirtyPoint ================= */ static void AddDirtyPoint(int x, int y) { if (x < scr_dirty.x1) scr_dirty.x1 = x; if (x > scr_dirty.x2) scr_dirty.x2 = x; if (y < scr_dirty.y1) scr_dirty.y1 = y; if (y > scr_dirty.y2) scr_dirty.y2 = y; } private static int lastframes = 0; private static int lasttime = 0; private static String fpsvalue = ""; static void DrawFPS() { if (fps.value > 0.0f) { int diff = cls.realtime - lasttime; if (diff > (int) (fps.value * 1000)) { fpsvalue = (cls.framecount - lastframes) * 100000 / diff / 100.0f + " fps"; lastframes = cls.framecount; lasttime = cls.realtime; } int x = viddef.width - 8 * fpsvalue.length() - 2; for (int i = 0; i < fpsvalue.length(); i++) { re.DrawChar(x, 2, fpsvalue.charAt(i)); x += 8; } } } /* ================================================================= cl_cin.c Play Cinematics ================================================================= */ // typedef struct // { // byte *data; // int count; // } cblock_t; // // typedef struct // { // qboolean restart_sound; // int s_rate; // int s_width; // int s_channels; // // int width; // int height; // byte *pic; // byte *pic_pending; // // // order 1 huffman stuff // int *hnodes1; // [256][256][2]; // int numhnodes1[256]; // // int h_used[512]; // int h_count[512]; // } cinematics_t; // // cinematics_t cin; // // /* // ================================================================= // // PCX LOADING // // ================================================================= // */ // // // /* // ============== // SCR_LoadPCX // ============== // */ // void SCR_LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height) // { // byte *raw; // pcx_t *pcx; // int x, y; // int len; // int dataByte, runLength; // byte *out, *pix; // // *pic = NULL; // // // // // load the file // // // len = FS_LoadFile (filename, (void **)&raw); // if (!raw) // return; // Com_Printf ("Bad pcx file %s\n", filename); // // // // // parse the PCX file // // // pcx = (pcx_t *)raw; // raw = &pcx->data; // // if (pcx->manufacturer != 0x0a // || pcx->version != 5 // || pcx->encoding != 1 // || pcx->bits_per_pixel != 8 // || pcx->xmax >= 640 // || pcx->ymax >= 480) // { // Com_Printf ("Bad pcx file %s\n", filename); // return; // } // // out = Z_Malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); // // *pic = out; // // pix = out; // // if (palette) // { // *palette = Z_Malloc(768); // memcpy (*palette, (byte *)pcx + len - 768, 768); // } // // if (width) // *width = pcx->xmax+1; // if (height) // *height = pcx->ymax+1; // // for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) // { // for (x=0 ; x<=pcx->xmax ; ) // { // dataByte = *raw++; // // if((dataByte & 0xC0) == 0xC0) // { // runLength = dataByte & 0x3F; // dataByte = *raw++; // } // else // runLength = 1; // // while(runLength-- > 0) // pix[x++] = dataByte; // } // // } // // if ( raw - (byte *)pcx > len) // { // Com_Printf ("PCX file %s was malformed", filename); // Z_Free (*pic); // *pic = NULL; // } // // FS_FreeFile (pcx); // } // // ============================================================= /* ================== SCR_StopCinematic ================== */ static void StopCinematic() { cl.cinematictime = 0; // done // if (cin.pic) // { // Z_Free (cin.pic); // cin.pic = NULL; // } // if (cin.pic_pending) // { // Z_Free (cin.pic_pending); // cin.pic_pending = NULL; // } if (cl.cinematicpalette_active) { re.CinematicSetPalette(null); cl.cinematicpalette_active = false; } // if (cl.cinematic_file) // { // fclose (cl.cinematic_file); // cl.cinematic_file = NULL; // } // if (cin.hnodes1) // { // Z_Free (cin.hnodes1); // cin.hnodes1 = NULL; // } // // // switch back down to 11 khz sound if necessary // if (cin.restart_sound) // { // cin.restart_sound = false; // CL_Snd_Restart_f (); // } // } /* ==================== SCR_FinishCinematic Called when either the cinematic completes, or it is aborted ==================== */ static void FinishCinematic() { // tell the server to advance to the next map / cinematic MSG.WriteByte(cls.netchan.message, clc_stringcmd); SZ.Print(cls.netchan.message, "nextserver " + cl.servercount + '\n'); } // ========================================================================== // /* // ================== // SmallestNode1 // ================== // */ // int SmallestNode1 (int numhnodes) // { // int i; // int best, bestnode; // // best = 99999999; // bestnode = -1; // for (i=0 ; i>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // //----------- // if (nodenum < 256) // { // hnodes = hnodesbase + (nodenum<<9); // *out_p++ = nodenum; // if (!--count) // break; // nodenum = cin.numhnodes1[nodenum]; // } // nodenum = hnodes[nodenum*2 + (inbyte&1)]; // inbyte >>=1; // } // // if (input - in.data != in.count && input - in.data != in.count+1) // { // Com_Printf ("Decompression overread by %i", (input - in.data) - in.count); // } // out.count = out_p - out.data; // // return out; // } // // /* // ================== // SCR_ReadNextFrame // ================== // */ // byte *SCR_ReadNextFrame (void) // { // int r; // int command; // byte samples[22050/14*4]; // byte compressed[0x20000]; // int size; // byte *pic; // cblock_t in, huf1; // int start, end, count; // // // read the next frame // r = fread (&command, 4, 1, cl.cinematic_file); // if (r == 0) // we'll give it one more chance // r = fread (&command, 4, 1, cl.cinematic_file); // // if (r != 1) // return NULL; // command = LittleLong(command); // if (command == 2) // return NULL; // last frame marker // // if (command == 1) // { // read palette // FS_Read (cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file); // cl.cinematicpalette_active=0; // dubious.... exposes an edge case // } // // // decompress the next frame // FS_Read (&size, 4, cl.cinematic_file); // size = LittleLong(size); // if (size > sizeof(compressed) || size < 1) // Com_Error (ERR_DROP, "Bad compressed frame size"); // FS_Read (compressed, size, cl.cinematic_file); // // // read sound // start = cl.cinematicframe*cin.s_rate/14; // end = (cl.cinematicframe+1)*cin.s_rate/14; // count = end - start; // // FS_Read (samples, count*cin.s_width*cin.s_channels, cl.cinematic_file); // // S_RawSamples (count, cin.s_rate, cin.s_width, cin.s_channels, samples); // // in.data = compressed; // in.count = size; // // huf1 = Huff1Decompress (in); // // pic = huf1.data; // // cl.cinematicframe++; // // return pic; // } // // /* ================== SCR_RunCinematic ================== */ static void RunCinematic() { int frame; if (cl.cinematictime <= 0) { StopCinematic(); return; } // if (cl.cinematicframe == -1) // return; // static image // // if (cls.key_dest != key_game) // { // pause if menu or console is up // cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; // return; // } // // frame = (cls.realtime - cl.cinematictime)*14.0/1000; // if (frame <= cl.cinematicframe) // return; // if (frame > cl.cinematicframe+1) // { // Com_Printf ("Dropped frame: %i > %i\n", frame, cl.cinematicframe+1); // cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; // } // if (cin.pic) // Z_Free (cin.pic); // cin.pic = cin.pic_pending; // cin.pic_pending = NULL; // cin.pic_pending = SCR_ReadNextFrame (); // if (!cin.pic_pending) // { // SCR_StopCinematic (); // SCR_FinishCinematic (); // cl.cinematictime = 1; // hack to get the black screen behind loading // SCR_BeginLoadingPlaque (); // cl.cinematictime = 0; // return; // } } /* ================== SCR_DrawCinematic Returns true if a cinematic is active, meaning the view rendering should be skipped ================== */ static boolean DrawCinematic() { // if (cl.cinematictime <= 0) // { return false; // } // // if (cls.key_dest == key_menu) // { // blank screen and pause if menu is up // re.CinematicSetPalette(NULL); // cl.cinematicpalette_active = false; // return true; // } // // if (!cl.cinematicpalette_active) // { // re.CinematicSetPalette(cl.cinematicpalette); // cl.cinematicpalette_active = true; // } // // if (!cin.pic) // return true; // // re.DrawStretchRaw (0, 0, viddef.width, viddef.height, // cin.width, cin.height, cin.pic); // // return true; } /* ================== SCR_PlayCinematic ================== */ static void PlayCinematic(String arg) { // int width, height; // byte *palette; // char name[MAX_OSPATH], *dot; // int old_khz; // // // make sure CD isn't playing music //CDAudio.Stop(); cl.cinematicframe = 0; // dot = strstr (arg, "."); // if (dot && !strcmp (dot, ".pcx")) // { // static pcx image // Com_sprintf (name, sizeof(name), "pics/%s", arg); // SCR_LoadPCX (name, &cin.pic, &palette, &cin.width, &cin.height); // cl.cinematicframe = -1; // cl.cinematictime = 1; // SCR_EndLoadingPlaque (); // cls.state = ca_active; // if (!cin.pic) // { // Com_Printf ("%s not found.\n", name); // cl.cinematictime = 0; // } // else // { // memcpy (cl.cinematicpalette, palette, sizeof(cl.cinematicpalette)); // Z_Free (palette); // } // return; // } // // Com_sprintf (name, sizeof(name), "video/%s", arg); // FS_FOpenFile (name, &cl.cinematic_file); // if (!cl.cinematic_file) // { // Com_Error (ERR_DROP, "Cinematic %s not found.\n", name); FinishCinematic(); cl.cinematictime = 0; // done return; // } // // SCR_EndLoadingPlaque (); // // cls.state = ca_active; // // FS_Read (&width, 4, cl.cinematic_file); // FS_Read (&height, 4, cl.cinematic_file); // cin.width = LittleLong(width); // cin.height = LittleLong(height); // // FS_Read (&cin.s_rate, 4, cl.cinematic_file); // cin.s_rate = LittleLong(cin.s_rate); // FS_Read (&cin.s_width, 4, cl.cinematic_file); // cin.s_width = LittleLong(cin.s_width); // FS_Read (&cin.s_channels, 4, cl.cinematic_file); // cin.s_channels = LittleLong(cin.s_channels); // // Huff1TableInit (); // // // switch up to 22 khz sound if necessary // old_khz = Cvar_VariableValue ("s_khz"); // if (old_khz != cin.s_rate/1000) // { // cin.restart_sound = true; // Cvar_SetValue ("s_khz", cin.s_rate/1000); // CL_Snd_Restart_f (); // Cvar_SetValue ("s_khz", old_khz); // } // // cl.cinematicframe = 0; // cin.pic = SCR_ReadNextFrame (); // cl.cinematictime = Sys_Milliseconds (); } }