/*
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.
*/
/* Modifications
Copyright 2003-2004 Bytonic Software
Copyright 2010 Google Inc.
*/
package com.googlecode.gwtquake.shared.client;
import static com.googlecode.gwtquake.shared.common.Constants.NUM_CON_TIMES;
import static com.googlecode.gwtquake.shared.common.Constants.ca_active;
import static com.googlecode.gwtquake.shared.common.Constants.key_console;
import static com.googlecode.gwtquake.shared.common.Constants.key_game;
import static com.googlecode.gwtquake.shared.common.Constants.key_menu;
import static com.googlecode.gwtquake.shared.common.Constants.key_message;
import static com.googlecode.gwtquake.shared.common.Globals.chat_buffer;
import static com.googlecode.gwtquake.shared.common.Globals.chat_bufferlen;
import static com.googlecode.gwtquake.shared.common.Globals.chat_team;
import static com.googlecode.gwtquake.shared.common.Globals.cls;
import static com.googlecode.gwtquake.shared.common.Globals.con;
import static com.googlecode.gwtquake.shared.common.Globals.con_notifytime;
import static com.googlecode.gwtquake.shared.common.Globals.edit_line;
import static com.googlecode.gwtquake.shared.common.Globals.key_linepos;
import static com.googlecode.gwtquake.shared.common.Globals.key_lines;
import static com.googlecode.gwtquake.shared.common.Globals.re;
import static com.googlecode.gwtquake.shared.common.Globals.viddef;
import java.util.Arrays;
import com.googlecode.gwtquake.shared.common.Com;
import com.googlecode.gwtquake.shared.common.CommandBuffer;
import com.googlecode.gwtquake.shared.common.ConsoleVariables;
import com.googlecode.gwtquake.shared.common.Constants;
import com.googlecode.gwtquake.shared.common.ExecutableCommand;
import com.googlecode.gwtquake.shared.common.Globals;
import com.googlecode.gwtquake.shared.game.Commands;
import com.googlecode.gwtquake.shared.util.Vargs;
/**
* Console
*/
public final class Console {
public static ExecutableCommand ToggleConsole_f = new ExecutableCommand() {
public void execute() {
Screen.EndLoadingPlaque(); // get rid of loading plaque
if (Globals.cl.attractloop) {
CommandBuffer.AddText("killserver\n");
return;
}
if (Globals.cls.state == Constants.ca_disconnected) {
// start the demo loop again
CommandBuffer.AddText("d1\n");
return;
}
Key.ClearTyping();
Console.ClearNotify();
if (Globals.cls.key_dest == Constants.key_console) {
Menu.ForceMenuOff();
ConsoleVariables.Set("paused", "0");
} else {
Menu.ForceMenuOff();
Globals.cls.key_dest = Constants.key_console;
if (ConsoleVariables.VariableValue("maxclients") == 1
&& Globals.server_state != 0)
ConsoleVariables.Set("paused", "1");
}
}
};
public static ExecutableCommand Clear_f = new ExecutableCommand() {
public void execute() {
Arrays.fill(Globals.con.text, (byte) ' ');
}
};
/**
*
*/
public static void Init() {
Globals.con.linewidth = -1;
CheckResize();
Com.Printf("Console initialized.\n");
//
// register our commands
//
Globals.con_notifytime = ConsoleVariables.Get("con_notifytime", "3", 0);
Commands.addCommand("toggleconsole", ToggleConsole_f);
Commands.addCommand("togglechat", ToggleChat_f);
Commands.addCommand("messagemode", MessageMode_f);
Commands.addCommand("messagemode2", MessageMode2_f);
Commands.addCommand("clear", Clear_f);
Globals.con.initialized = true;
}
/**
* If the line width has changed, reformat the buffer.
*/
public static void CheckResize() {
int width = (Globals.viddef.width >> 3) - 2;
if (width > Constants.MAXCMDLINE) width = Constants.MAXCMDLINE;
if (width == Globals.con.linewidth)
return;
if (width < 1) { // video hasn't been initialized yet
width = 38;
Globals.con.linewidth = width;
Globals.con.totallines = Constants.CON_TEXTSIZE
/ Globals.con.linewidth;
Arrays.fill(Globals.con.text, (byte) ' ');
} else {
int oldwidth = Globals.con.linewidth;
Globals.con.linewidth = width;
int oldtotallines = Globals.con.totallines;
Globals.con.totallines = Constants.CON_TEXTSIZE
/ Globals.con.linewidth;
int numlines = oldtotallines;
if (Globals.con.totallines < numlines)
numlines = Globals.con.totallines;
int numchars = oldwidth;
if (Globals.con.linewidth < numchars)
numchars = Globals.con.linewidth;
byte[] tbuf = new byte[Constants.CON_TEXTSIZE];
System
.arraycopy(Globals.con.text, 0, tbuf, 0,
Constants.CON_TEXTSIZE);
Arrays.fill(Globals.con.text, (byte) ' ');
for (int i = 0; i < numlines; i++) {
for (int j = 0; j < numchars; j++) {
Globals.con.text[(Globals.con.totallines - 1 - i)
* Globals.con.linewidth + j] = tbuf[((Globals.con.current
- i + oldtotallines) % oldtotallines)
* oldwidth + j];
}
}
Console.ClearNotify();
}
Globals.con.current = Globals.con.totallines - 1;
Globals.con.display = Globals.con.current;
}
public static void ClearNotify() {
int i;
for (i = 0; i < Constants.NUM_CON_TIMES; i++)
Globals.con.times[i] = 0;
}
/*
* ================ Con_ToggleChat_f ================
*/
static ExecutableCommand ToggleChat_f = new ExecutableCommand() {
public void execute() {
Key.ClearTyping();
if (cls.key_dest == key_console) {
if (cls.state == ca_active) {
Menu.ForceMenuOff();
cls.key_dest = key_game;
}
} else
cls.key_dest = key_console;
ClearNotify();
}
};
/*
* ================ Con_MessageMode_f ================
*/
static ExecutableCommand MessageMode_f = new ExecutableCommand() {
public void execute() {
chat_team = false;
cls.key_dest = key_message;
}
};
/*
* ================ Con_MessageMode2_f ================
*/
static ExecutableCommand MessageMode2_f = new ExecutableCommand() {
public void execute() {
chat_team = true;
cls.key_dest = key_message;
}
};
/*
* =============== Con_Linefeed ===============
*/
static void Linefeed() {
Globals.con.x = 0;
if (Globals.con.display == Globals.con.current)
Globals.con.display++;
Globals.con.current++;
int i = (Globals.con.current % Globals.con.totallines)
* Globals.con.linewidth;
int e = i + Globals.con.linewidth;
while (i++ < e)
Globals.con.text[i] = ' ';
}
/*
* ================ Con_Print
*
* Handles cursor positioning, line wrapping, etc All console printing must
* go through this in order to be logged to disk If no console is visible,
* the text will appear at the top of the game window ================
*/
private static int cr;
public static void Print(String txt) {
int y;
int c, l;
int mask;
int txtpos = 0;
if (!con.initialized)
return;
if (txt.charAt(0) == 1 || txt.charAt(0) == 2) {
mask = 128; // go to colored text
txtpos++;
} else
mask = 0;
while (txtpos < txt.length()) {
c = txt.charAt(txtpos);
// count word length
for (l = 0; l < con.linewidth && l < (txt.length() - txtpos); l++)
if (txt.charAt(l + txtpos) <= ' ')
break;
// word wrap
if (l != con.linewidth && (con.x + l > con.linewidth))
con.x = 0;
txtpos++;
if (cr != 0) {
con.current--;
cr = 0;
}
if (con.x == 0) {
Console.Linefeed();
// mark time for transparent overlay
if (con.current >= 0)
con.times[con.current % NUM_CON_TIMES] = cls.realtime;
}
switch (c) {
case '\n':
con.x = 0;
break;
case '\r':
con.x = 0;
cr = 1;
break;
default: // display character and advance
y = con.current % con.totallines;
con.text[y * con.linewidth + con.x] = (byte) (c | mask | con.ormask);
con.x++;
if (con.x >= con.linewidth)
con.x = 0;
break;
}
}
}
/*
* ============== Con_CenteredPrint ==============
*/
static void CenteredPrint(String text) {
int l = text.length();
l = (con.linewidth - l) / 2;
if (l < 0)
l = 0;
StringBuffer sb = new StringBuffer(1024);
for (int i = 0; i < l; i++)
sb.append(' ');
sb.append(text);
sb.append('\n');
sb.setLength(1024);
Console.Print(sb.toString());
}
/*
* ==============================================================================
*
* DRAWING
*
* ==============================================================================
*/
/*
* ================ Con_DrawInput
*
* The input line scrolls horizontally if typing goes beyond the right edge
* ================
*/
static void DrawInput() {
int i;
byte[] text;
int start = 0;
if (cls.key_dest == key_menu)
return;
if (cls.key_dest != key_console && cls.state == ca_active)
return; // don't draw anything (always draw if not active)
text = key_lines[edit_line];
// add the cursor frame
text[key_linepos] = (byte) (10 + ((int) (cls.realtime >> 8) & 1));
// fill out remainder with spaces
for (i = key_linepos + 1; i < con.linewidth; i++)
text[i] = ' ';
// prestep if horizontally scrolling
if (key_linepos >= con.linewidth)
start += 1 + key_linepos - con.linewidth;
// draw it
// y = con.vislines-16;
re.DrawString(1, con.vislines - 22, text, 0, con.linewidth);
// remove cursor
key_lines[edit_line][key_linepos] = 0;
}
/*
* ================ Con_DrawNotify
*
* Draws the last few lines of output transparently over the game top
* ================
*/
static void DrawNotify() {
int x, v;
int text;
int i;
int time;
String s;
int skip;
v = 0;
for (i = con.current - NUM_CON_TIMES + 1; i <= con.current; i++) {
if (i < 0)
continue;
time = (int) con.times[i % NUM_CON_TIMES];
if (time == 0)
continue;
time = (int) (cls.realtime - time);
if (time > con_notifytime.value * 1000)
continue;
text = (i % con.totallines) * con.linewidth;
re.DrawString(1 << 3, v, con.text, text, con.linewidth);
v += 8;
}
if (cls.key_dest == key_message) {
if (chat_team) {
re.DrawString(8, v, "say_team:");
skip = 11;
} else {
re.DrawString(8, v, "say:");
skip = 5;
}
s = chat_buffer;
if (chat_bufferlen > (viddef.width >> 3) - (skip + 1))
s = s.substring(chat_bufferlen
- ((viddef.width >> 3) - (skip + 1)));
re.DrawString(skip << 3, v, s);
x = s.length();
re.DrawChar((x + skip) << 3, v, (int) (10 + ((cls.realtime >> 8) & 1)));
v += 8;
}
if (v != 0) {
Screen.AddDirtyPoint(0, 0);
Screen.AddDirtyPoint(viddef.width - 1, v);
}
}
/*
* ================ Con_DrawConsole
*
* Draws the console with the solid background ================
*/
static void DrawConsole(float frac) {
int i, x, y;
int rows;
int row;
int lines;
String version;
lines = (int) (viddef.height * frac);
if (lines <= 0)
return;
if (lines > viddef.height)
lines = viddef.height;
// draw the background
re.DrawStretchPic(0, -viddef.height + lines, viddef.width,
viddef.height, "conback");
Screen.AddDirtyPoint(0, 0);
Screen.AddDirtyPoint(viddef.width - 1, lines - 1);
version = Com.sprintf("v%4.2f", new Vargs(1).add(Constants.VERSION));
re.DrawString(viddef.width - 44, lines - 12, version);
// draw the text
con.vislines = lines;
rows = (lines - 22) >> 3; // rows of text to draw
y = lines - 30;
// draw from the bottom up
if (con.display != con.current) {
// draw arrows to show the buffer is backscrolled
re.DrawString(8, y, "^^^^");
y -= 8;
rows--;
}
row = con.display;
for (i = 0; i < rows; i++, y -= 8, row--) {
if (row < 0)
break;
if (con.current - row >= con.totallines)
break; // past scrollback wrap point
int first = (row % con.totallines) * con.linewidth;
re.DrawString(8, y, con.text, first, con.linewidth);
}
// draw the input prompt, user text, and cursor if desired
DrawInput();
}
}