diff options
Diffstat (limited to 'src/jake2/client')
44 files changed, 23240 insertions, 0 deletions
diff --git a/src/jake2/client/CDAudio.java b/src/jake2/client/CDAudio.java new file mode 100644 index 0000000..49c5c42 --- /dev/null +++ b/src/jake2/client/CDAudio.java @@ -0,0 +1,43 @@ +/* +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. + +*/ + +// Created on 31.01.2004 by RST. +// $Id: CDAudio.java,v 1.1 2004-07-07 19:58:32 hzi Exp $ + +package jake2.client; + + +// import jake2.*; +// import jake2.client.*; +// import jake2.game.*; +// import jake2.qcommon.*; +// import jake2.render.*; +// import jake2.server.*; + +public class CDAudio { + + public static void CDAudio_Play(int i, boolean b) { + // TODO:implement CDAudio_Play + } + + public static void Stop() { + // TODO impl: CDAudio.Stop() + } +} diff --git a/src/jake2/client/CL.java b/src/jake2/client/CL.java new file mode 100644 index 0000000..58ce048 --- /dev/null +++ b/src/jake2/client/CL.java @@ -0,0 +1,1625 @@ +/* + * CL.java + * Copyright (C) 2004 + * + * $Id: CL.java,v 1.1 2004-07-07 19:58:33 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.Defines; +import jake2.Globals; +import jake2.game.*; +import jake2.qcommon.*; +import jake2.server.SV_MAIN; +import jake2.sys.*; +import jake2.util.Vargs; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; + +/** + * CL + */ +public final class CL extends CL_pred { + + //// cl_main.c -- client main loop + + /* + ==================== + CL_WriteDemoMessage + + Dumps the current net message, prefixed by the length + ==================== + */ + static void WriteDemoMessage() { + int swlen; + + // the first eight bytes are just packet sequencing stuff + swlen = net_message.cursize - 8; + + try { + cls.demofile.writeInt(swlen); + //fwrite (&swlen, 4, 1, cls.demofile); + cls.demofile.write(net_message.data, 8, swlen); + //fwrite (net_message.data+8, len, 1, cls.demofile); + } catch (IOException e) {} + + } + + /* + ==================== + CL_Stop_f + + stop recording a demo + ==================== + */ + static xcommand_t Stop_f = new xcommand_t() { + public void execute() { + try { + + int len; + + if (!cls.demorecording) { + Com.Printf("Not recording a demo.\n"); + return; + } + + // finish up + len = -1; + cls.demofile.writeInt(len); + cls.demofile.close(); + cls.demofile = null; + cls.demorecording = false; + Com.Printf("Stopped demo.\n"); + + } + catch (IOException e) { + } + } + }; + + /* + ==================== + CL_Record_f + + record <demoname> + + Begins recording a demo from the current position + ==================== + */ + private static entity_state_t nullstate = new entity_state_t(null); + static xcommand_t Record_f = new xcommand_t() { + public void execute() { + try { + String name; + byte buf_data[] = new byte[MAX_MSGLEN]; + sizebuf_t buf = new sizebuf_t(); + int i; + int len; + entity_state_t ent; + + if (Cmd.Argc() != 2) { + Com.Printf("record <demoname>\n"); + return; + } + + if (cls.demorecording) { + Com.Printf("Already recording.\n"); + return; + } + + if (cls.state != ca_active) { + Com.Printf("You must be in a level to record.\n"); + return; + } + + // + // open the demo file + // + name = FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2"; + + Com.Printf("recording to " + name + ".\n"); + FS.CreatePath(name); + cls.demofile = new RandomAccessFile(name, "rw"); + if (cls.demofile == null) { + Com.Printf("ERROR: couldn't open.\n"); + return; + } + cls.demorecording = true; + + // don't start saving messages until a non-delta compressed message is received + cls.demowaiting = true; + + // + // write out messages to hold the startup information + // + SZ.Init(buf, buf_data, MAX_MSGLEN); + + // send the serverdata + MSG.WriteByte(buf, svc_serverdata); + MSG.WriteInt(buf, PROTOCOL_VERSION); + MSG.WriteInt(buf, 0x10000 + cl.servercount); + MSG.WriteByte(buf, 1); // demos are always attract loops + MSG.WriteString(buf, cl.gamedir); + MSG.WriteShort(buf, cl.playernum); + + MSG.WriteString(buf, cl.configstrings[CS_NAME]); + + // configstrings + for (i = 0; i < MAX_CONFIGSTRINGS; i++) { + if (cl.configstrings[i].length()>0) { + if (buf.cursize + cl.configstrings[i].length() + 32 > buf.maxsize) { // write it out + //len = LittleLong(buf.cursize); + //fwrite(& len, 4, 1, cls.demofile); + cls.demofile.writeInt(buf.cursize); + //fwrite(buf.data, buf.cursize, 1, cls.demofile); + cls.demofile.write(buf.data, 0, buf.cursize); + buf.cursize = 0; + } + + MSG.WriteByte(buf, svc_configstring); + MSG.WriteShort(buf, i); + MSG.WriteString(buf, cl.configstrings[i]); + } + + } + + // baselines + //memset( nullstate, 0, sizeof(nullstate)); + for (i = 0; i < MAX_EDICTS; i++) { + ent = cl_entities[i].baseline; + if (ent.modelindex == 0) + continue; + + if (buf.cursize + 64 > buf.maxsize) { // write it out + //len = LittleLong(buf.cursize); + //fwrite(& len, 4, 1, cls.demofile); + cls.demofile.writeInt(buf.cursize); + //fwrite(buf.data, buf.cursize, 1, cls.demofile); + cls.demofile.write(buf.data, 0, buf.cursize); + buf.cursize = 0; + } + + MSG.WriteByte(buf, svc_spawnbaseline); + MSG.WriteDeltaEntity(nullstate, cl_entities[i].baseline, buf, true, true); + } + + MSG.WriteByte(buf, svc_stufftext); + MSG.WriteString(buf, "precache\n"); + + // write it to the demo file + + //len = LittleLong(buf.cursize); + //fwrite(& len, 4, 1, cls.demofile); + cls.demofile.writeInt(buf.cursize); + //fwrite(buf.data, buf.cursize, 1, cls.demofile); + cls.demofile.write(buf.data, 0, buf.cursize); + // the rest of the demo file will be individual frames + + } + catch (IOException e) { + } + } + }; + + /* + ================== + CL_ForwardToServer_f + ================== + */ + static xcommand_t ForwardToServer_f = new xcommand_t() { + public void execute() { + if (cls.state != ca_connected && cls.state != ca_active) { + Com.Printf("Can't \"" + Cmd.Argv(0) + "\", not connected\n"); + return; + } + + // don't forward the first argument + if (Cmd.Argc() > 1) { + MSG.WriteByte(cls.netchan.message, clc_stringcmd); + SZ.Print(cls.netchan.message, Cmd.Args()); + } + } + }; + + /* + ================== + CL_Pause_f + ================== + */ + static xcommand_t Pause_f = new xcommand_t() { + public void execute() { + // never pause in multiplayer + + if (Cvar.VariableValue("maxclients") > 1 || Com.ServerState() == 0) { + Cvar.SetValue("paused", 0); + return; + } + + Cvar.SetValue("paused", cl_paused.value); + } + }; + + /* + ================== + CL_Quit_f + ================== + */ + static xcommand_t Quit_f = new xcommand_t() { + public void execute() { + Disconnect(); + Com.Quit(); + } + }; + + /* + ======================= + CL_SendConnectPacket + + We have gotten a challenge from the server, so try and + connect. + ====================== + */ + static void SendConnectPacket() { + netadr_t adr = new netadr_t(); + int port; + + if (!NET.StringToAdr(cls.servername, adr)) { + Com.Printf("Bad server address\n"); + cls.connect_time = 0; + return; + } + if (adr.port == 0) + adr.port = PORT_SERVER; + // adr.port = BigShort(PORT_SERVER); + + port = (int) Cvar.VariableValue("qport"); + userinfo_modified = false; + + Netchan.OutOfBandPrint( + NS_CLIENT, + adr, + "connect " + PROTOCOL_VERSION + " " + port + " " + cls.challenge + " \"" + Cvar.Userinfo() + "\"\n"); + } + + /* + ================= + CL_CheckForResend + + Resend a connect message if the last one has timed out + ================= + */ + static void CheckForResend() { + netadr_t adr = new netadr_t(); + + // if the local server is running and we aren't + // then connect + if (cls.state == ca_disconnected && Com.ServerState() != 0) { + cls.state = ca_connecting; + cls.servername = "localhost"; + // we don't need a challenge on the localhost + CL.SendConnectPacket(); + return; + } + + // resend if we haven't gotten a reply yet + if (cls.state != ca_connecting) + return; + + if (cls.realtime - cls.connect_time < 3000) + return; + + if (!NET.StringToAdr(cls.servername, adr)) { + Com.Printf("Bad server address\n"); + cls.state = ca_disconnected; + return; + } + if (adr.port == 0) + // adr.port = BigShort(PORT_SERVER); + adr.port = PORT_SERVER; + + cls.connect_time = cls.realtime; // for retransmit requests + + Com.Printf("Connecting to " + cls.servername + "...\n"); + + Netchan.OutOfBandPrint(NS_CLIENT, adr, "getchallenge\n"); + } + + /* + ================ + CL_Connect_f + + ================ + */ + static xcommand_t Connect_f = new xcommand_t() { + public void execute() { + String server; + + if (Cmd.Argc() != 2) { + Com.Printf("usage: connect <server>\n"); + return; + } + + if (Com.ServerState() != 0) { + // if running a local server, kill it and reissue + SV_MAIN.SV_Shutdown("Server quit\n", false); + } else { + CL.Disconnect(); + } + + server = Cmd.Argv(1); + + NET.Config(true); // allow remote + + CL.Disconnect(); + + cls.state = ca_connecting; + //strncpy (cls.servername, server, sizeof(cls.servername)-1); + cls.servername = server; + cls.connect_time = -99999; + // CL_CheckForResend() will fire immediately + } + }; + + /* + ===================== + CL_Rcon_f + + Send the rest of the command line over as + an unconnected command. + ===================== + */ + static xcommand_t Rcon_f = new xcommand_t() { + public void execute() { + StringBuffer message = new StringBuffer(1024); + int i; + netadr_t to = new netadr_t(); + + if (rcon_client_password.string == null) { + Com.Printf("You must set 'rcon_password' before\nissuing an rcon command.\n"); + return; + } + + message.append((char)255); + message.append((char)255); + message.append((char)255); + message.append((char)255); + + NET.Config(true); // allow remote + + //strcat (message, "rcon "); + message.append("rcon "); + + //strcat (message, rcon_client_password.string); + message.append(rcon_client_password.string); + //strcat (message, " "); + message.append(" "); + + for (i = 1; i < Cmd.Argc(); i++) { + //strcat (message, Cmd.Argv(i)); + message.append(Cmd.Argv(i)); + //strcat (message, " "); + message.append(" "); + } + + if (cls.state >= ca_connected) + to = cls.netchan.remote_address; + else { + if (strlen(rcon_address.string) == 0) { + Com.Printf( + "You must either be connected,\nor set the 'rcon_address' cvar\nto issue rcon commands\n"); + + return; + } + NET.StringToAdr(rcon_address.string, to); + if (to.port == 0) + //to.port = BigShort (PORT_SERVER); + to.port = PORT_SERVER; + } + message.append('\0'); + String b = message.toString(); + NET.SendPacket(NS_CLIENT, b.length(), b.getBytes(), to); + } + }; + + /* + ===================== + CL_ClearState + + ===================== + */ + + static void ClearState() { + S.StopAllSounds(); + CL.ClearEffects(); + CL.ClearTEnts(); + + // wipe the entire cl structure + + cl = new client_state_t(); + for (int i = 0; i < cl_entities.length; i++) { + cl_entities[i] = new centity_t(); + } + + SZ.Clear(cls.netchan.message); + } + + /* + ===================== + CL_Disconnect + + Goes from a connected state to full screen console state + Sends a disconnect message to the server + This is also called on Com_Error, so it shouldn't cause any errors + ===================== + */ + + static void Disconnect() { + + String fin; + + if (cls.state == ca_disconnected) + return; + + if (cl_timedemo != null && cl_timedemo.value != 0.0f) { + int time; + + time = (int) (Sys.Milliseconds() - cl.timedemo_start); + if (time > 0) + Com.Printf( + "%i frames, %3.1f seconds: %3.1f fps\n", + new Vargs(3).add(cl.timedemo_frames).add(time / 1000.0).add(cl.timedemo_frames * 1000.0 / time)); + } + + VectorClear(cl.refdef.blend); + //re.CinematicSetPalette(null); + + Menu.ForceMenuOff(); + + cls.connect_time = 0; + + // SCR.StopCinematic(); + + if (cls.demorecording) + CL.Stop_f.execute(); + + // send a disconnect message to the server + fin = (char) clc_stringcmd + "disconnect"; + Netchan.Transmit(cls.netchan, fin.length(), fin.getBytes()); + Netchan.Transmit(cls.netchan, fin.length(), fin.getBytes()); + Netchan.Transmit(cls.netchan, fin.length(), fin.getBytes()); + + CL.ClearState(); + + // stop download + if (cls.download != null) { + fclose(cls.download); + cls.download = null; + // fclose(cls.download); + // cls.download = NULL; + } + + cls.state = ca_disconnected; + } + + static xcommand_t Disconnect_f = new xcommand_t() { + public void execute() { + Com.Error(ERR_DROP, "Disconnected from server"); + } + }; + + /* + ================= + CL_Changing_f + + Just sent as a hint to the client that they should + drop to full console + ================= + */ + static xcommand_t Changing_f = new xcommand_t() { + public void execute() { + //ZOID + //if we are downloading, we don't change! + // This so we don't suddenly stop downloading a map + + if (cls.download != null) + return; + + SCR.BeginLoadingPlaque(); + cls.state = ca_connected; // not active anymore, but not disconnected + Com.Printf("\nChanging map...\n"); + } + }; + + /* + ================= + CL_Reconnect_f + + The server is changing levels + ================= + */ + static xcommand_t Reconnect_f = new xcommand_t() { + public void execute() { + //ZOID + //if we are downloading, we don't change! This so we don't suddenly stop downloading a map + if (cls.download != null) + return; + + S.StopAllSounds(); + if (cls.state == ca_connected) { + Com.Printf("reconnecting...\n"); + cls.state = ca_connected; + MSG.WriteChar(cls.netchan.message, clc_stringcmd); + MSG.WriteString(cls.netchan.message, "new"); + return; + } + + if (cls.servername != null) { + if (cls.state >= ca_connected) { + CL.Disconnect(); + cls.connect_time = cls.realtime - 1500; + } else + cls.connect_time = -99999; // fire immediately + + cls.state = ca_connecting; + Com.Printf("reconnecting...\n"); + } + } + }; + + /* + ================= + CL_ParseStatusMessage + + Handle a reply from a ping + ================= + */ + static void ParseStatusMessage() { + String s; + + s = MSG.ReadString(net_message); + + Com.Printf(s + "\n"); + Menu.AddToServerList(net_from, s); + } + + /* + ================= + CL_PingServers_f + ================= + */ + static xcommand_t PingServers_f = new xcommand_t() { + public void execute() { + int i; + netadr_t adr = new netadr_t(); + //char name[32]; + String name; + String adrstring; + cvar_t noudp; + cvar_t noipx; + + NET.Config(true); // allow remote + + // send a broadcast packet + Com.Printf("pinging broadcast...\n"); + + noudp = Cvar.Get("noudp", "0", CVAR_NOSET); + if (noudp.value == 0.0f) { + adr.type = NA_BROADCAST; + adr.port = PORT_SERVER; + //adr.port = BigShort(PORT_SERVER); + Netchan.OutOfBandPrint(NS_CLIENT, adr, "info " + PROTOCOL_VERSION); + } + + // we use no IPX + noipx = Cvar.Get("noipx", "1", CVAR_NOSET); + if (noipx.value == 0.0f) { + adr.type = NA_BROADCAST_IPX; + //adr.port = BigShort(PORT_SERVER); + adr.port = PORT_SERVER; + Netchan.OutOfBandPrint(NS_CLIENT, adr, "info " + PROTOCOL_VERSION); + } + + // send a packet to each address book entry + for (i = 0; i < 16; i++) { + //Com_sprintf (name, sizeof(name), "adr%i", i); + name = "adr" + i; + adrstring = Cvar.VariableString(name); + if (adrstring == null || adrstring.length() == 0) + continue; + + Com.Printf("pinging " + adrstring + "...\n"); + if (!NET.StringToAdr(adrstring, adr)) { + Com.Printf("Bad address: " + adrstring + "\n"); + continue; + } + if (adr.port == 0) + //adr.port = BigShort(PORT_SERVER); + adr.port = PORT_SERVER; + Netchan.OutOfBandPrint(NS_CLIENT, adr, "info " + PROTOCOL_VERSION); + } + } + }; + + + /* + ================= + CL_Skins_f + + Load or download any custom player skins and models + ================= + */ + static xcommand_t Skins_f = new xcommand_t() { + public void execute() { + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (cl.configstrings[CS_PLAYERSKINS + i] == null) + continue; + Com.Printf("client " + i + ": " + cl.configstrings[CS_PLAYERSKINS + i] + "\n"); + SCR.UpdateScreen(); + Sys.SendKeyEvents(); // pump message loop + CL.ParseClientinfo(i); + } + } + }; + + /* + ================= + CL_ConnectionlessPacket + + Responses to broadcasts, etc + ================= + */ + static void ConnectionlessPacket() { + String s; + String c; + + MSG.BeginReading(net_message); + MSG.ReadLong(net_message); // skip the -1 + + s = MSG.ReadStringLine(net_message); + + Cmd.TokenizeString(s.toCharArray(), false); + + c = Cmd.Argv(0); + + Com.Printf(NET.AdrToString(net_from) + ": " + c + " \n"); + + // server connection + if (c.equals("client_connect")) { + if (cls.state == ca_connected) { + Com.Printf("Dup connect received. Ignored.\n"); + return; + } + Netchan.Setup(NS_CLIENT, cls.netchan, net_from, cls.quakePort); + MSG.WriteChar(cls.netchan.message, clc_stringcmd); + MSG.WriteString(cls.netchan.message, "new"); + cls.state = ca_connected; + return; + } + + // server responding to a status broadcast + if (c.equals("info")) { + CL.ParseStatusMessage(); + return; + } + + // remote command from gui front end + if (c.equals ("cmd")) { + if (!NET.IsLocalAddress(net_from)) { + Com.Printf("Command packet from remote host. Ignored.\n"); + return; + } + s = MSG.ReadString(net_message); + Cbuf.AddText(s); + Cbuf.AddText("\n"); + return; + } + // print command from somewhere + if (c.equals("print")) { + s = MSG.ReadString(net_message); + Com.Printf(s); + return; + } + + // ping from somewhere + if (c.equals("ping")) { + Netchan.OutOfBandPrint(NS_CLIENT, net_from, "ack"); + return; + } + + // challenge from the server we are connecting to + if (c.equals("challenge")) { + cls.challenge = Integer.parseInt(Cmd.Argv(1)); + CL.SendConnectPacket(); + return; + } + + // echo request from server + if (c.equals("echo")) { + Netchan.OutOfBandPrint(NS_CLIENT, net_from, Cmd.Argv(1)); + return; + } + + Com.Printf("Unknown command.\n"); + } + + /* + ================= + CL_DumpPackets + + A vain attempt to help bad TCP stacks that cause problems + when they overflow + ================= + */ + static void DumpPackets() { + while (NET.GetPacket(NS_CLIENT, net_from, net_message)) { + Com.Printf("dumping a packet\n"); + } + } + + /* + ================= + CL_ReadPackets + ================= + */ + static void ReadPackets() { + while (NET.GetPacket(NS_CLIENT, net_from, net_message)) { + + // + // remote command packet + // + if (net_message.data[0] == -1 && net_message.data[1] == -1 + && net_message.data[2] == -1 && net_message.data[3] == -1) { + // if (*(int *)net_message.data == -1) + CL.ConnectionlessPacket(); + continue; + } + + if (cls.state == ca_disconnected || cls.state == ca_connecting) + continue; // dump it if not connected + + if (net_message.cursize < 8) { + Com.Printf(NET.AdrToString(net_from) + ": Runt packet\n"); + continue; + } + + // + // packet from server + // + if (!NET.CompareAdr(net_from, cls.netchan.remote_address)) { + Com.DPrintf(NET.AdrToString(net_from) + ":sequenced packet without connection\n"); + continue; + } + if (!Netchan.Process(cls.netchan, net_message)) + continue; // wasn't accepted for some reason + ParseServerMessage(); + } + + // + // check timeout + // + if (cls.state >= ca_connected && cls.realtime - cls.netchan.last_received > cl_timeout.value * 1000) { + if (++cl.timeoutcount > 5) // timeoutcount saves debugger + { + Com.Printf("\nServer connection timed out.\n"); + CL.Disconnect(); + return; + } + } + else + cl.timeoutcount = 0; + } + + // ============================================================================= + + /* + ============== + CL_FixUpGender_f + ============== + */ + static void FixUpGender() { + + String sk; + + if (gender_auto.value != 0.0f) { + + if (gender.modified) { + // was set directly, don't override the user + gender.modified = false; + return; + } + + sk = skin.string; + if (sk.startsWith("male") || sk.startsWith("cyborg")) + Cvar.Set("gender", "male"); + else if (sk.startsWith("female") || sk.startsWith("crackhor")) + Cvar.Set("gender", "female"); + else + Cvar.Set("gender", "none"); + gender.modified = false; + } + } + + /* + ============== + CL_Userinfo_f + ============== + */ + static xcommand_t Userinfo_f = new xcommand_t() { + public void execute() { + Com.Printf("User info settings:\n"); + Info.Print(Cvar.Userinfo()); + } + }; + + /* + ================= + CL_Snd_Restart_f + + Restart the sound subsystem so it can pick up + new parameters and flush all sounds + ================= + */ + static xcommand_t Snd_Restart_f = new xcommand_t() { + public void execute() { + S.Shutdown(); + S.Init(); + CL.RegisterSounds(); + } + }; + + static int precache_check; // for autodownload of precache items + static int precache_spawncount; + static int precache_tex; + static int precache_model_skin; + + static byte precache_model[]; // used for skin checking in alias models + + public static final int PLAYER_MULT = 5; + + // ENV_CNT is map load, ENV_CNT+1 is first env map + public static final int ENV_CNT = (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT); + public static final int TEXTURE_CNT = (ENV_CNT + 13); + + static String env_suf[] = { "rt", "bk", "lf", "ft", "up", "dn" }; + + public static void RequestNextDownload() { + int map_checksum = 0; // for detecting cheater maps + //char fn[MAX_OSPATH]; + String fn; + + qfiles.dmdl_t pheader; + + if (cls.state != ca_connected) + return; + + if (SV_MAIN.allow_download.value == 0 && precache_check < ENV_CNT) + precache_check = ENV_CNT; + + // ZOID + if (precache_check == CS_MODELS) { // confirm map + precache_check = CS_MODELS + 2; // 0 isn't used + if (SV_MAIN.allow_download_maps.value != 0) + if (!CheckOrDownloadFile(cl.configstrings[CS_MODELS + 1])) + return; // started a download + } + if (precache_check >= CS_MODELS && precache_check < CS_MODELS + MAX_MODELS) { + if (SV_MAIN.allow_download_models.value != 0) { + while (precache_check < CS_MODELS + MAX_MODELS && cl.configstrings[precache_check].length() > 0) { + if (cl.configstrings[precache_check].charAt(0) == '*' || cl.configstrings[precache_check].charAt(0) == '#') { + precache_check++; + continue; + } + if (precache_model_skin == 0) { + if (!CheckOrDownloadFile(cl.configstrings[precache_check])) { + precache_model_skin = 1; + return; // started a download + } + precache_model_skin = 1; + } + + // checking for skins in the model + if (precache_model == null) { + + precache_model = FS.LoadFile(cl.configstrings[precache_check]); + if (precache_model == null) { + precache_model_skin = 0; + precache_check++; + continue; // couldn't load it + } + ByteBuffer bb = ByteBuffer.wrap(precache_model); + int header = Globals.endian.LittleLong(bb.getInt()); + + if (header != qfiles.IDALIASHEADER) { + // not an alias model + FS.FreeFile(precache_model); + precache_model = null; + precache_model_skin = 0; + precache_check++; + continue; + } + pheader = new qfiles.dmdl_t(ByteBuffer.wrap(precache_model)); + if (Globals.endian.LittleLong(pheader.version) != ALIAS_VERSION) { + precache_check++; + precache_model_skin = 0; + continue; // couldn't load it + } + } + + pheader = new qfiles.dmdl_t(ByteBuffer.wrap(precache_model)); + + int num_skins = Globals.endian.LittleLong(pheader.num_skins); + + while (precache_model_skin - 1 < num_skins) { + Com.Printf("critical code section because of endian mess!"); + + String name = + new String( + precache_model, + Globals.endian.LittleLong(pheader.ofs_skins) + (precache_model_skin - 1) * MAX_SKINNAME, + MAX_SKINNAME * num_skins); + + if (!CheckOrDownloadFile(name)) { + precache_model_skin++; + return; // started a download + } + precache_model_skin++; + } + if (precache_model != null) { + FS.FreeFile(precache_model); + precache_model = null; + } + precache_model_skin = 0; + precache_check++; + } + } + precache_check = CS_SOUNDS; + } + if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS + MAX_SOUNDS) { + if (SV_MAIN.allow_download_sounds.value != 0) { + if (precache_check == CS_SOUNDS) + precache_check++; // zero is blank + while (precache_check < CS_SOUNDS + MAX_SOUNDS && cl.configstrings[precache_check].length() > 0) { + if (cl.configstrings[precache_check].charAt(0) == '*') { + precache_check++; + continue; + } + fn = "sound/" + cl.configstrings[precache_check++]; + if (!CheckOrDownloadFile(fn)) + return; // started a download + } + } + precache_check = CS_IMAGES; + } + if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES + MAX_IMAGES) { + if (precache_check == CS_IMAGES) + precache_check++; // zero is blank + + while (precache_check < CS_IMAGES + MAX_IMAGES && cl.configstrings[precache_check].length() > 0) { + fn = "pics/" + cl.configstrings[precache_check++] + ".pcx"; + if (!CheckOrDownloadFile(fn)) + return; // started a download + } + precache_check = CS_PLAYERSKINS; + } + // skins are special, since a player has three things to download: + // model, weapon model and skin + // so precache_check is now *3 + if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) { + if (SV_MAIN.allow_download_players.value != 0) { + while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) { + + int i, n; + //char model[MAX_QPATH], skin[MAX_QPATH], * p; + String model, skin, p; + + i = (precache_check - CS_PLAYERSKINS) / PLAYER_MULT; + n = (precache_check - CS_PLAYERSKINS) % PLAYER_MULT; + + if (cl.configstrings[CS_PLAYERSKINS + i].length() == 0) { + precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT; + continue; + } + + int pos = cl.configstrings[CS_PLAYERSKINS + i].indexOf('\\'); + if (pos != -1) + pos++; + else + pos = 0; + + model = cl.configstrings[CS_PLAYERSKINS + i].substring(pos); + + pos = model.indexOf('/'); + + if (pos == -1) + pos = model.indexOf('\\'); + + if (pos != -1) { + skin = model.substring(pos + 1); + } + else + skin = ""; + + switch (n) { + case 0 : // model + fn = "players/" + model + "/tris.md2"; + if (!CheckOrDownloadFile(fn)) { + precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1; + return; // started a download + } + n++; + /*FALL THROUGH*/ + + case 1 : // weapon model + fn = "players/" + model + "/weapon.md2"; + if (!CheckOrDownloadFile(fn)) { + precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2; + return; // started a download + } + n++; + /*FALL THROUGH*/ + + case 2 : // weapon skin + fn = "players/" + model + "/weapon.pcx"; + if (!CheckOrDownloadFile(fn)) { + precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3; + return; // started a download + } + n++; + /*FALL THROUGH*/ + + case 3 : // skin + fn = "players/" + model + "/" + skin + ".pcx"; + if (!CheckOrDownloadFile(fn)) { + precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4; + return; // started a download + } + n++; + /*FALL THROUGH*/ + + case 4 : // skin_i + fn = "players/" + model + "/" + skin + "_i.pcx"; + if (!CheckOrDownloadFile(fn)) { + precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5; + return; // started a download + } + // move on to next model + precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT; + } + } + } + // precache phase completed + precache_check = ENV_CNT; + } + + if (precache_check == ENV_CNT) { + precache_check = ENV_CNT + 1; + + CM.intwrap iw = new CM.intwrap(map_checksum); + + CM.CM_LoadMap(cl.configstrings[CS_MODELS + 1], true, iw); + map_checksum = iw.i; +// TODO MD4 check abgeklemmt +// if ((map_checksum ^ atoi(cl.configstrings[CS_MAPCHECKSUM])) != 0) { +// Com.Error( +// ERR_DROP, +// "Local map version differs from server: " + map_checksum + " != '" + cl.configstrings[CS_MAPCHECKSUM] + "'\n"); +// return; +// } + } + + if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) { + if (SV_MAIN.allow_download.value != 0 && SV_MAIN.allow_download_maps.value != 0) { + while (precache_check < TEXTURE_CNT) { + int n = precache_check++ -ENV_CNT - 1; + + if ((n & 1) != 0) + fn = "env/" + cl.configstrings[CS_SKY] + env_suf[n / 2] + ".pcx"; + else + fn = "env/" + cl.configstrings[CS_SKY] + env_suf[n / 2] + ".tga"; + if (!CheckOrDownloadFile(fn)) + return; // started a download + } + } + precache_check = TEXTURE_CNT; + } + + if (precache_check == TEXTURE_CNT) { + precache_check = TEXTURE_CNT + 1; + precache_tex = 0; + } + + // confirm existance of textures, download any that don't exist + if (precache_check == TEXTURE_CNT + 1) { + // from qcommon/cmodel.c + // extern int numtexinfo; + // extern mapsurface_t map_surfaces[]; + + if (SV_MAIN.allow_download.value != 0 && SV_MAIN.allow_download_maps.value != 0) { + while (precache_tex < CM.numtexinfo) { + //char fn[MAX_OSPATH]; + + fn = "textures/" + CM.map_surfaces[precache_tex++].rname + ".wal"; + if (!CheckOrDownloadFile(fn)) + return; // started a download + } + } + precache_check = TEXTURE_CNT + 999; + } + + // ZOID + CL.RegisterSounds(); + PrepRefresh(); + + MSG.WriteByte(cls.netchan.message, clc_stringcmd); + MSG.WriteString(cls.netchan.message, "begin " + precache_spawncount + "\n"); + } + + /* + ================= + CL_Precache_f + + The server will send this command right + before allowing the client into the server + ================= + */ + static xcommand_t Precache_f = new xcommand_t() { + public void execute() { + /* Yet another hack to let old demos work + the old precache sequence */ + + if (Cmd.Argc() < 2) { + + CM.intwrap iw = new CM.intwrap(0); // for detecting cheater maps + + CM.CM_LoadMap(cl.configstrings[CS_MODELS + 1], true, iw); + int mapchecksum = iw.i ; + CL.RegisterSounds(); + CL.PrepRefresh(); + return; + } + + precache_check = CS_MODELS; + precache_spawncount = atoi(Cmd.Argv(1)); + precache_model = null; + precache_model_skin = 0; + + RequestNextDownload(); + } + }; + + /* + ================= + CL_InitLocal + ================= + */ + public static void InitLocal() { + cls.state = Defines.ca_disconnected; + cls.realtime = Sys.Milliseconds(); + + InitInput(); + + adr0 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr1 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr2 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr3 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr4 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr5 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr6 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr7 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + adr8 = Cvar.Get("adr0", "", CVAR_ARCHIVE); + + // + // register our variables + // + cl_stereo_separation = Cvar.Get("cl_stereo_separation", "0.4", CVAR_ARCHIVE); + cl_stereo = Cvar.Get("cl_stereo", "0", 0); + + cl_add_blend = Cvar.Get("cl_blend", "1", 0); + cl_add_lights = Cvar.Get("cl_lights", "1", 0); + cl_add_particles = Cvar.Get("cl_particles", "1", 0); + cl_add_entities = Cvar.Get("cl_entities", "1", 0); + cl_gun = Cvar.Get("cl_gun", "1", 0); + cl_footsteps = Cvar.Get("cl_footsteps", "1", 0); + cl_noskins = Cvar.Get("cl_noskins", "0", 0); + cl_autoskins = Cvar.Get("cl_autoskins", "0", 0); + cl_predict = Cvar.Get("cl_predict", "1", 0); + + cl_maxfps = Cvar.Get("cl_maxfps", "90", 0); + + cl_upspeed = Cvar.Get("cl_upspeed", "200", 0); + cl_forwardspeed = Cvar.Get("cl_forwardspeed", "200", 0); + cl_sidespeed = Cvar.Get("cl_sidespeed", "200", 0); + cl_yawspeed = Cvar.Get("cl_yawspeed", "140", 0); + cl_pitchspeed = Cvar.Get("cl_pitchspeed", "150", 0); + cl_anglespeedkey = Cvar.Get("cl_anglespeedkey", "1.5", 0); + + cl_run = Cvar.Get("cl_run", "0", CVAR_ARCHIVE); + freelook = Cvar.Get("freelook", "0", CVAR_ARCHIVE); + lookspring = Cvar.Get("lookspring", "0", CVAR_ARCHIVE); + lookstrafe = Cvar.Get("lookstrafe", "0", CVAR_ARCHIVE); + sensitivity = Cvar.Get("sensitivity", "3", CVAR_ARCHIVE); + + m_pitch = Cvar.Get("m_pitch", "0.022", CVAR_ARCHIVE); + m_yaw = Cvar.Get("m_yaw", "0.022", 0); + m_forward = Cvar.Get("m_forward", "1", 0); + m_side = Cvar.Get("m_side", "1", 0); + + cl_shownet = Cvar.Get("cl_shownet", "0", 0); + cl_showmiss = Cvar.Get("cl_showmiss", "0", 0); + cl_showclamp = Cvar.Get("showclamp", "0", 0); + cl_timeout = Cvar.Get("cl_timeout", "120", 0); + cl_paused = Cvar.Get("paused", "0", 0); + cl_timedemo = Cvar.Get("timedemo", "0", 0); + + rcon_client_password = Cvar.Get("rcon_password", "", 0); + rcon_address = Cvar.Get("rcon_address", "", 0); + + cl_lightlevel = Cvar.Get("r_lightlevel", "0", 0); + + // + // userinfo + // + info_password = Cvar.Get("password", "", CVAR_USERINFO); + info_spectator = Cvar.Get("spectator", "0", CVAR_USERINFO); + name = Cvar.Get("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE); + skin = Cvar.Get("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE); + rate = Cvar.Get("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE); // FIXME + msg = Cvar.Get("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE); + hand = Cvar.Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); + fov = Cvar.Get("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE); + gender = Cvar.Get("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE); + gender_auto = Cvar.Get("gender_auto", "1", CVAR_ARCHIVE); + gender.modified = false; // clear this so we know when user sets it manually + + cl_vwep = Cvar.Get("cl_vwep", "1", CVAR_ARCHIVE); + + // + // register our commands + // + Cmd.AddCommand("cmd", ForwardToServer_f); + Cmd.AddCommand("pause", Pause_f); + Cmd.AddCommand("pingservers", PingServers_f); + Cmd.AddCommand("skins", Skins_f); + + Cmd.AddCommand("userinfo", Userinfo_f); + Cmd.AddCommand("snd_restart", Snd_Restart_f); + + Cmd.AddCommand("changing", Changing_f); + Cmd.AddCommand("disconnect", Disconnect_f); + Cmd.AddCommand("record", Record_f); + Cmd.AddCommand("stop", Stop_f); + + Cmd.AddCommand("quit", Quit_f); + + Cmd.AddCommand("connect", Connect_f); + Cmd.AddCommand("reconnect", Reconnect_f); + + Cmd.AddCommand("rcon", Rcon_f); + + Cmd.AddCommand("precache", Precache_f); + + Cmd.AddCommand("download", Download_f); + + // + // forward to server commands + // + // the only thing this does is allow command completion + // to work -- all unknown commands are automatically + // forwarded to the server + Cmd.AddCommand("wave", null); + Cmd.AddCommand("inven", null); + Cmd.AddCommand("kill", null); + Cmd.AddCommand("use", null); + Cmd.AddCommand("drop", null); + Cmd.AddCommand("say", null); + Cmd.AddCommand("say_team", null); + Cmd.AddCommand("info", null); + Cmd.AddCommand("prog", null); + Cmd.AddCommand("give", null); + Cmd.AddCommand("god", null); + Cmd.AddCommand("notarget", null); + Cmd.AddCommand("noclip", null); + Cmd.AddCommand("invuse", null); + Cmd.AddCommand("invprev", null); + Cmd.AddCommand("invnext", null); + Cmd.AddCommand("invdrop", null); + Cmd.AddCommand("weapnext", null); + Cmd.AddCommand("weapprev", null); + + } + + /* + =============== + CL_WriteConfiguration + + Writes key bindings and archived cvars to config.cfg + =============== + */ + static void WriteConfiguration() { + RandomAccessFile f; + String path; + + if (cls.state == ca_uninitialized) + return; + + path = FS.Gamedir() + "/config.cfg"; + f = fopen(path, "rw"); + try + { + f.seek(0); + f.setLength(0); + } + catch (IOException e1) + {} + if (f == null) { + Com.Printf("Couldn't write config.cfg.\n"); + return; + } + try { + f.writeBytes("// generated by quake, do not modify\n"); + } + catch (IOException e) {} + //fprintf (f, "// generated by quake, do not modify\n"); + Key.WriteBindings(f); + fclose(f); + Cvar.WriteVariables(path); + } + + /* + ================== + CL_FixCvarCheats + + ================== + */ + public static class cheatvar_t { + String name; + String value; + cvar_t var; + } + + public static String cheatvarsinfo[][] = { { "timescale", "1" }, { + "timedemo", "0" }, { + "r_drawworld", "1" }, { + "cl_testlights", "0" }, { + "r_fullbright", "0" }, { + "r_drawflat", "0" }, { + "paused", "0" }, { + "fixedtime", "0" }, { + "sw_draworder", "0" }, { + "gl_lightmap", "0" }, { + "gl_saturatelighting", "0" }, { + null, null } + }; + public static cheatvar_t cheatvars[]; + + static { + cheatvars = new cheatvar_t[cheatvarsinfo.length]; + for (int n = 0; n < cheatvarsinfo.length; n++) { + cheatvars[n] = new cheatvar_t(); + cheatvars[n].name = cheatvarsinfo[n][0]; + cheatvars[n].value = cheatvarsinfo[n][1]; + } + } + + static int numcheatvars; + + public static void FixCvarCheats() { + int i; + cheatvar_t var; + + if (0 == strcmp(cl.configstrings[CS_MAXCLIENTS], "1") || 0 == cl.configstrings[CS_MAXCLIENTS].length()) + return; // single player can cheat + + // find all the cvars if we haven't done it yet + if (0 == numcheatvars) { + while (cheatvars[numcheatvars].name != null) { + cheatvars[numcheatvars].var = Cvar.Get(cheatvars[numcheatvars].name, cheatvars[numcheatvars].value, 0); + numcheatvars++; + } + } + + // make sure they are all set to the proper values + for (i = 0; i < numcheatvars; i++) { + var = cheatvars[i]; + if (0 != strcmp(var.var.string, var.value)) { + Cvar.Set(var.name, var.value); + } + } + } + + // ============================================================================ + + /* + ================== + CL_SendCommand + + ================== + */ + public static void SendCommand() { + // get new key events + Sys.SendKeyEvents(); + + // allow mice or other external controllers to add commands + IN.Commands(); + + // process console commands + Cbuf.Execute(); + + // fix any cheating cvars + FixCvarCheats(); + + // send intentions now + SendCmd(); + + // resend a connection request if necessary + CheckForResend(); + } + + /* + ================== + CL_Frame + + ================== + */ + private static int extratime; + private static int lasttimecalled; + + public static void Frame(int msec) { + + extratime += msec; + + if (cl_timedemo.value == 0.0f) { + if (cls.state == ca_connected && extratime < 100) { + return; // don't flood packets out while connecting + } + if (extratime < 1000 / cl_maxfps.value) { + return; // framerate is too high + } + } + + // let the mouse activate or deactivate + IN.Frame(); + + // decide the simulation time + cls.frametime = extratime / 1000.0f; + cl.time += extratime; + cls.realtime = curtime; + + extratime = 0; + + if (cls.frametime > (1.0f / 5)) + cls.frametime = (1.0f / 5); + + // if in the debugger last frame, don't timeout + if (msec > 5000) + cls.netchan.last_received = Sys.Milliseconds(); + + // fetch results from server + CL.ReadPackets(); + + // send a new command message to the server + SendCommand(); + + // predict all unacknowledged movements + CL.PredictMovement(); + + // allow rendering DLL change + VID.CheckChanges(); + if (!cl.refresh_prepped && cls.state == ca_active) { + CL.PrepRefresh(); + // TODO force GC after level loading + System.gc(); + System.gc(); + } + + SCR.UpdateScreen(); + + // update audio + S.Update(cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up); + + // advance local effects for next frame + CL.RunDLights(); + CL.RunLightStyles(); + + SCR.RunConsole(); + + cls.framecount++; + } + + // ============================================================================ + + /* + =============== + CL_Shutdown + + FIXME: this is a callback from Sys_Quit and Com_Error. It would be better + to run quit through here before the final handoff to the sys code. + =============== + */ + static boolean isdown = false; + public static void Shutdown() { + + if (isdown) { + System.out.print("recursive shutdown\n"); + return; + } + isdown = true; + + WriteConfiguration(); + + S.Shutdown(); + IN.Shutdown(); + VID.Shutdown(); + } + + /** + * initialize client subsystem + */ + public static void Init() { + if (Globals.dedicated.value != 0.0f) + return; // nothing running on the client + + // all archived variables will now be loaded + + Console.Init(); //ok + + S.Init(); //empty + VID.Init(); + + V.Init(); + + Globals.net_message.data = Globals.net_message_buffer; + Globals.net_message.maxsize = Globals.net_message_buffer.length; + + Menu.Init(); + + SCR.Init(); + //Globals.cls.disable_screen = 1.0f; // don't draw yet + + CL.InitLocal(); + IN.Init(); + + FS.ExecAutoexec(); + Cbuf.Execute(); + } + + /** + * Called after an ERR_DROP was thrown. + */ + public static void Drop() { + if (Globals.cls.state == Defines.ca_uninitialized) + return; + if (Globals.cls.state == Defines.ca_disconnected) + return; + + CL.Disconnect(); + + // drop loading plaque unless this is the initial game start + if (Globals.cls.disable_servercount != -1) + SCR.EndLoadingPlaque(); // get rid of loading plaque + } + +} diff --git a/src/jake2/client/CL_ents.java b/src/jake2/client/CL_ents.java new file mode 100644 index 0000000..135f3fe --- /dev/null +++ b/src/jake2/client/CL_ents.java @@ -0,0 +1,1287 @@ +/* + * CL_ents.java + * Copyright (C) 2004 + * + * $Id: CL_ents.java,v 1.1 2004-07-07 19:58:35 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.game.entity_state_t; +import jake2.game.player_state_t; +import jake2.qcommon.*; +import jake2.render.model_t; + +/** + * CL_ents + */ +// cl_ents.c -- entity parsing and management +public class CL_ents extends CL_inv { + + // PGM + static int vidref_val; + // PGM + + /* + ========================================================================= + + FRAME PARSING + + ========================================================================= + */ + + /* + ================= + CL_ParseEntityBits + + Returns the entity number and the header bits + ================= + */ + static int bitcounts[] = new int[32]; /// just for protocol profiling + public static int ParseEntityBits(CM.intwrap bits) { + int b, total; + int i; + int number; + + total = MSG.ReadByte(net_message); + if ((total & U_MOREBITS1) != 0) { + b = MSG.ReadByte(net_message); + total |= b << 8; + } + if ((total & U_MOREBITS2) != 0) { + b = MSG.ReadByte(net_message); + total |= b << 16; + } + if ((total & U_MOREBITS3) != 0) { + b = MSG.ReadByte(net_message); + total |= b << 24; + } + + // count the bits for net profiling + for (i = 0; i < 32; i++) + if ((total & (1 << i)) != 0) + bitcounts[i]++; + + if ((total & U_NUMBER16) != 0) + number = MSG.ReadShort(net_message); + else + number = MSG.ReadByte(net_message); + + bits.i = total; + + return number; + } + + /* + ================== + CL_ParseDelta + + Can go from either a baseline or a previous packet_entity + ================== + */ + public static void ParseDelta(entity_state_t from, entity_state_t to, int number, int bits) { + // set everything to the state we are delta'ing from + to.set(from); + + VectorCopy(from.origin, to.old_origin); + to.number = number; + + if ((bits & U_MODEL) != 0) + to.modelindex = MSG.ReadByte(net_message); + if ((bits & U_MODEL2) != 0) + to.modelindex2 = MSG.ReadByte(net_message); + if ((bits & U_MODEL3) != 0) + to.modelindex3 = MSG.ReadByte(net_message); + if ((bits & U_MODEL4) != 0) + to.modelindex4 = MSG.ReadByte(net_message); + + if ((bits & U_FRAME8) != 0) + to.frame = MSG.ReadByte(net_message); + if ((bits & U_FRAME16) != 0) + to.frame = MSG.ReadShort(net_message); + + if ((bits & U_SKIN8) != 0 && (bits & U_SKIN16) != 0) //used for laser colors + to.skinnum = MSG.ReadLong(net_message); + else if ((bits & U_SKIN8) != 0) + to.skinnum = MSG.ReadByte(net_message); + else if ((bits & U_SKIN16) != 0) + to.skinnum = MSG.ReadShort(net_message); + + if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) + to.effects = MSG.ReadLong(net_message); + else if ((bits & U_EFFECTS8) != 0) + to.effects = MSG.ReadByte(net_message); + else if ((bits & U_EFFECTS16) != 0) + to.effects = MSG.ReadShort(net_message); + + if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) + to.renderfx = MSG.ReadLong(net_message); + else if ((bits & U_RENDERFX8) != 0) + to.renderfx = MSG.ReadByte(net_message); + else if ((bits & U_RENDERFX16) != 0) + to.renderfx = MSG.ReadShort(net_message); + + if ((bits & U_ORIGIN1) != 0) + to.origin[0] = MSG.ReadCoord(net_message); + if ((bits & U_ORIGIN2) != 0) + to.origin[1] = MSG.ReadCoord(net_message); + if ((bits & U_ORIGIN3) != 0) + to.origin[2] = MSG.ReadCoord(net_message); + + if ((bits & U_ANGLE1) != 0) + to.angles[0] = MSG.ReadAngle(net_message); + if ((bits & U_ANGLE2) != 0) + to.angles[1] = MSG.ReadAngle(net_message); + if ((bits & U_ANGLE3) != 0) + to.angles[2] = MSG.ReadAngle(net_message); + + if ((bits & U_OLDORIGIN) != 0) + MSG.ReadPos(net_message, to.old_origin); + + if ((bits & U_SOUND) != 0) + to.sound = MSG.ReadByte(net_message); + + if ((bits & U_EVENT) != 0) + to.event = MSG.ReadByte(net_message); + else + to.event = 0; + + if ((bits & U_SOLID) != 0) + to.solid = MSG.ReadShort(net_message); + } + + /* + ================== + CL_DeltaEntity + + Parses deltas from the given base and adds the resulting entity + to the current frame + ================== + */ + public static void DeltaEntity(frame_t frame, int newnum, entity_state_t old, int bits) { + centity_t ent; + entity_state_t state; + + ent = cl_entities[newnum]; + + state = cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES - 1)]; + cl.parse_entities++; + frame.num_entities++; + + ParseDelta(old, state, newnum, bits); + + // some data changes will force no lerping + if (state.modelindex != ent.current.modelindex + || state.modelindex2 != ent.current.modelindex2 + || state.modelindex3 != ent.current.modelindex3 + || state.modelindex4 != ent.current.modelindex4 + || Math.abs(state.origin[0] - ent.current.origin[0]) > 512 + || Math.abs(state.origin[1] - ent.current.origin[1]) > 512 + || Math.abs(state.origin[2] - ent.current.origin[2]) > 512 + || state.event == EV_PLAYER_TELEPORT + || state.event == EV_OTHER_TELEPORT) { + ent.serverframe = -99; + } + + if (ent.serverframe != cl.frame.serverframe - 1) { // wasn't in last update, so initialize some things + ent.trailcount = 1024; // for diminishing rocket / grenade trails + // duplicate the current state so lerping doesn't hurt anything + ent.prev.set(state); + if (state.event == EV_OTHER_TELEPORT) { + VectorCopy(state.origin, ent.prev.origin); + VectorCopy(state.origin, ent.lerp_origin); + } + else { + VectorCopy(state.old_origin, ent.prev.origin); + VectorCopy(state.old_origin, ent.lerp_origin); + } + } + else { // shuffle the last state to previous + // Copy ! + ent.prev.set(ent.current); + } + + ent.serverframe = cl.frame.serverframe; + // Copy ! + ent.current.set(state); + } + + /* + ================== + CL_ParsePacketEntities + + An svc_packetentities has just been parsed, deal with the + rest of the data stream. + ================== + */ + public static void ParsePacketEntities(frame_t oldframe, frame_t newframe) { + int newnum; + int bits=0; + + entity_state_t oldstate=null; + int oldindex, oldnum; + + newframe.parse_entities = cl.parse_entities; + newframe.num_entities = 0; + + // delta from the entities present in oldframe + oldindex = 0; + if (oldframe == null) + oldnum = 99999; + else { + if (oldindex >= oldframe.num_entities) + oldnum = 99999; + else { + oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; + oldnum = oldstate.number; + } + } + + while (true) { + CM.intwrap iw = new CM.intwrap(bits); + newnum = ParseEntityBits(iw); + bits = iw.i; + + if (newnum >= MAX_EDICTS) + Com.Error(ERR_DROP, "CL_ParsePacketEntities: bad number:" + newnum); + + if (net_message.readcount > net_message.cursize) + Com.Error(ERR_DROP, "CL_ParsePacketEntities: end of message"); + + if (0 == newnum) + break; + + while (oldnum < newnum) { // one or more entities from the old packet are unchanged + if (cl_shownet.value == 3) + Com.Printf(" unchanged: " + oldnum + "\n"); + DeltaEntity(newframe, oldnum, oldstate, 0); + + oldindex++; + + if (oldindex >= oldframe.num_entities) + oldnum = 99999; + else { + oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; + oldnum = oldstate.number; + } + } + + if ((bits & U_REMOVE) != 0) { // the entity present in oldframe is not in the current frame + if (cl_shownet.value == 3) + Com.Printf(" remove: " + newnum + "\n"); + if (oldnum != newnum) + Com.Printf("U_REMOVE: oldnum != newnum\n"); + + oldindex++; + + if (oldindex >= oldframe.num_entities) + oldnum = 99999; + else { + oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; + oldnum = oldstate.number; + } + continue; + } + + if (oldnum == newnum) { // delta from previous state + if (cl_shownet.value == 3) + Com.Printf(" delta: " + newnum + "\n"); + DeltaEntity(newframe, newnum, oldstate, bits); + + oldindex++; + + if (oldindex >= oldframe.num_entities) + oldnum = 99999; + else { + oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; + oldnum = oldstate.number; + } + continue; + } + + if (oldnum > newnum) { // delta from baseline + if (cl_shownet.value == 3) + Com.Printf(" baseline: " + newnum + "\n"); + DeltaEntity(newframe, newnum, cl_entities[newnum].baseline, bits); + continue; + } + + } + + // any remaining entities in the old frame are copied over + while (oldnum != 99999) { // one or more entities from the old packet are unchanged + if (cl_shownet.value == 3) + Com.Printf(" unchanged: " + oldnum + "\n"); + DeltaEntity(newframe, oldnum, oldstate, 0); + + oldindex++; + + if (oldindex >= oldframe.num_entities) + oldnum = 99999; + else { + oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; + oldnum = oldstate.number; + } + } + } + + /* + =================== + CL_ParsePlayerstate + =================== + */ + public static void ParsePlayerstate(frame_t oldframe, frame_t newframe) { + int flags; + player_state_t state; + int i; + int statbits; + + state = newframe.playerstate; + + // clear to old value before delta parsing + if (oldframe != null) + state.set(oldframe.playerstate); + else + //memset (state, 0, sizeof(*state)); + state.clear(); + + flags = MSG.ReadShort(net_message); + + // + // parse the pmove_state_t + // + if ((flags & PS_M_TYPE) != 0) + state.pmove.pm_type = MSG.ReadByte(net_message); + + if ((flags & PS_M_ORIGIN) != 0) { + state.pmove.origin[0] = MSG.ReadShort(net_message); + state.pmove.origin[1] = MSG.ReadShort(net_message); + state.pmove.origin[2] = MSG.ReadShort(net_message); + } + + if ((flags & PS_M_VELOCITY) != 0) { + state.pmove.velocity[0] = MSG.ReadShort(net_message); + state.pmove.velocity[1] = MSG.ReadShort(net_message); + state.pmove.velocity[2] = MSG.ReadShort(net_message); + } + + if ((flags & PS_M_TIME) != 0) + { + state.pmove.pm_time = (byte) MSG.ReadByte(net_message); + } + + if ((flags & PS_M_FLAGS) != 0) + state.pmove.pm_flags = (byte) MSG.ReadByte(net_message); + + if ((flags & PS_M_GRAVITY) != 0) + state.pmove.gravity = MSG.ReadShort(net_message); + + if ((flags & PS_M_DELTA_ANGLES) != 0) { + state.pmove.delta_angles[0] = MSG.ReadShort(net_message); + state.pmove.delta_angles[1] = MSG.ReadShort(net_message); + state.pmove.delta_angles[2] = MSG.ReadShort(net_message); + } + + if (cl.attractloop) + state.pmove.pm_type = PM_FREEZE; // demo playback + + // + // parse the rest of the player_state_t + // + if ((flags & PS_VIEWOFFSET) != 0) { + state.viewoffset[0] = MSG.ReadChar(net_message) * 0.25f; + state.viewoffset[1] = MSG.ReadChar(net_message) * 0.25f; + state.viewoffset[2] = MSG.ReadChar(net_message) * 0.25f; + } + + if ((flags & PS_VIEWANGLES) != 0) { + state.viewangles[0] = MSG.ReadAngle16(net_message); + state.viewangles[1] = MSG.ReadAngle16(net_message); + state.viewangles[2] = MSG.ReadAngle16(net_message); + } + + if ((flags & PS_KICKANGLES) != 0) { + + state.kick_angles[0] = MSG.ReadChar(net_message) * 0.25f; + state.kick_angles[1] = MSG.ReadChar(net_message) * 0.25f; + state.kick_angles[2] = MSG.ReadChar(net_message) * 0.25f; + + } + + if ((flags & PS_WEAPONINDEX) != 0) { + state.gunindex = MSG.ReadByte(net_message); + } + + if ((flags & PS_WEAPONFRAME) != 0) { + state.gunframe = MSG.ReadByte(net_message); + state.gunoffset[0] = MSG.ReadChar(net_message) * 0.25f; + state.gunoffset[1] = MSG.ReadChar(net_message) * 0.25f; + state.gunoffset[2] = MSG.ReadChar(net_message) * 0.25f; + state.gunangles[0] = MSG.ReadChar(net_message) * 0.25f; + state.gunangles[1] = MSG.ReadChar(net_message) * 0.25f; + state.gunangles[2] = MSG.ReadChar(net_message) * 0.25f; + } + + if ((flags & PS_BLEND) != 0) { + state.blend[0] = MSG.ReadByte(net_message) / 255.0f; + state.blend[1] = MSG.ReadByte(net_message) / 255.0f; + state.blend[2] = MSG.ReadByte(net_message) / 255.0f; + state.blend[3] = MSG.ReadByte(net_message) / 255.0f; + } + + if ((flags & PS_FOV) != 0) + state.fov = MSG.ReadByte(net_message); + + if ((flags & PS_RDFLAGS) != 0) + state.rdflags = MSG.ReadByte(net_message); + + // parse stats + statbits = MSG.ReadLong(net_message); + for (i = 0; i < MAX_STATS; i++) + if ((statbits & (1 << i))!=0) + state.stats[i] = MSG.ReadShort(net_message); + } + + /* + ================== + CL_FireEntityEvents + + ================== + */ + public static void FireEntityEvents(frame_t frame) { + entity_state_t s1; + int pnum, num; + + for (pnum = 0; pnum < frame.num_entities; pnum++) { + num = (frame.parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1); + s1 = cl_parse_entities[num]; + if (s1.event!=0) + EntityEvent(s1); + + // EF_TELEPORTER acts like an event, but is not cleared each frame + if ((s1.effects & EF_TELEPORTER)!=0) + CL_fx.TeleporterParticles(s1); + } + } + + /* + ================ + CL_ParseFrame + ================ + */ + public static void ParseFrame() { + int cmd; + int len; + frame_t old; + + //memset( cl.frame, 0, sizeof(cl.frame)); + cl.frame.reset(); + + cl.frame.serverframe = MSG.ReadLong(net_message); + cl.frame.deltaframe = MSG.ReadLong(net_message); + cl.frame.servertime = cl.frame.serverframe * 100; + + // BIG HACK to let old demos continue to work + if (cls.serverProtocol != 26) + cl.surpressCount = MSG.ReadByte(net_message); + + if (cl_shownet.value == 3) + Com.Printf(" frame:" + cl.frame.serverframe + " delta:" + cl.frame.deltaframe + "\n"); + + // If the frame is delta compressed from data that we + // no longer have available, we must suck up the rest of + // the frame, but not use it, then ask for a non-compressed + // message + if (cl.frame.deltaframe <= 0) { + cl.frame.valid = true; // uncompressed frame + old = null; + cls.demowaiting = false; // we can start recording now + } + else { + old = cl.frames[cl.frame.deltaframe & UPDATE_MASK]; + if (!old.valid) { // should never happen + Com.Printf("Delta from invalid frame (not supposed to happen!).\n"); + } + if (old.serverframe != cl.frame.deltaframe) { // The frame that the server did the delta from + // is too old, so we can't reconstruct it properly. + Com.Printf("Delta frame too old.\n"); + } + else if (cl.parse_entities - old.parse_entities > MAX_PARSE_ENTITIES - 128) { + Com.Printf("Delta parse_entities too old.\n"); + } + else + cl.frame.valid = true; // valid delta parse + } + + // clamp time + if (cl.time > cl.frame.servertime) + cl.time = cl.frame.servertime; + else if (cl.time < cl.frame.servertime - 100) + cl.time = cl.frame.servertime - 100; + + // read areabits + len = MSG.ReadByte(net_message); + MSG.ReadData(net_message, cl.frame.areabits, len); + + // read playerinfo + cmd = MSG.ReadByte(net_message); + CL_parse.SHOWNET(CL_parse.svc_strings[cmd]); + if (cmd != svc_playerinfo) + Com.Error(ERR_DROP, "CL_ParseFrame: not playerinfo"); + ParsePlayerstate(old, cl.frame); + + // read packet entities + cmd = MSG.ReadByte(net_message); + CL_parse.SHOWNET(CL_parse.svc_strings[cmd]); + if (cmd != svc_packetentities) + Com.Error(ERR_DROP, "CL_ParseFrame: not packetentities"); + + ParsePacketEntities(old, cl.frame); + + // save the frame off in the backup array for later delta comparisons + cl.frames[cl.frame.serverframe & UPDATE_MASK].set(cl.frame); + + if (cl.frame.valid) { + // getting a valid frame message ends the connection process + if (cls.state != ca_active) { + cls.state = ca_active; + cl.force_refdef = true; + + cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0] * 0.125f; + cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1] * 0.125f; + cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2] * 0.125f; + + VectorCopy(cl.frame.playerstate.viewangles, cl.predicted_angles); + if (cls.disable_servercount != cl.servercount && cl.refresh_prepped) + SCR.EndLoadingPlaque(); // get rid of loading plaque + } + cl.sound_prepped = true; // can start mixing ambient sounds + + // fire entity events + FireEntityEvents(cl.frame); + CL_pred.CheckPredictionError(); + } + } + + /* + ========================================================================== + + INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS + + ========================================================================== + */ + + public static model_t S_RegisterSexedModel(entity_state_t ent, String base) { + int n; + String p; + model_t mdl; + String model; + String buffer; + + // determine what model the client is using + model = ""; + + n = CS_PLAYERSKINS + ent.number - 1; + + if (cl.configstrings[n].length() >0) { + + int pos = cl.configstrings[n].indexOf('\\'); + if (pos!=-1) { + pos++; + model = cl.configstrings[n].substring(pos); + pos = model.indexOf('/'); + if (pos !=-1) + model = model.substring(0,pos); + } + } + // if we can't figure it out, they're male + if (model.length()==0) + model = "male"; + + buffer= "players/" + model + "/" + base + 1; + mdl = re.RegisterModel(buffer); + if (mdl==null) { + // not found, try default weapon model + buffer = "players/" + model + "/weapon.md2"; + mdl = re.RegisterModel(buffer); + if (mdl==null) { + // no, revert to the male model + buffer="players/male/" + base + 1; + mdl = re.RegisterModel(buffer); + if (mdl==null) { + // last try, default male weapon.md2 + buffer = "players/male/weapon.md2"; + mdl = re.RegisterModel(buffer); + } + } + } + + return mdl; + } + + // PMM - used in shell code + + /* + =============== + CL_AddPacketEntities + + =============== + */ + static int bfg_lightramp[] = { 300, 400, 600, 300, 150, 75 }; + + static void AddPacketEntities(frame_t frame) { + entity_t ent; + entity_state_t s1; + float autorotate; + int i; + int pnum; + centity_t cent; + int autoanim; + clientinfo_t ci; + int effects, renderfx; + + // bonus items rotate at a fixed rate + autorotate = anglemod(cl.time / 10); + + // brush models can auto animate their frames + autoanim = 2 * cl.time / 1000; + + //memset( ent, 0, sizeof(ent)); + ent = new entity_t(); + + for (pnum = 0; pnum < frame.num_entities; pnum++) { + s1 = cl_parse_entities[(frame.parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1)]; + + cent = cl_entities[s1.number]; + + effects = s1.effects; + renderfx = s1.renderfx; + + // set frame + if ((effects & EF_ANIM01)!=0) + ent.frame = autoanim & 1; + else if ((effects & EF_ANIM23)!=0) + ent.frame = 2 + (autoanim & 1); + else if ((effects & EF_ANIM_ALL)!=0) + ent.frame = autoanim; + else if ((effects & EF_ANIM_ALLFAST)!=0) + ent.frame = cl.time / 100; + else + ent.frame = s1.frame; + + // quad and pent can do different things on client + if ((effects & EF_PENT)!=0) { + effects &= ~EF_PENT; + effects |= EF_COLOR_SHELL; + renderfx |= RF_SHELL_RED; + } + + if ((effects & EF_QUAD)!=0) { + effects &= ~EF_QUAD; + effects |= EF_COLOR_SHELL; + renderfx |= RF_SHELL_BLUE; + } + // ====== + // PMM + if ((effects & EF_DOUBLE)!=0) { + effects &= ~EF_DOUBLE; + effects |= EF_COLOR_SHELL; + renderfx |= RF_SHELL_DOUBLE; + } + + if ((effects & EF_HALF_DAMAGE) !=0){ + effects &= ~EF_HALF_DAMAGE; + effects |= EF_COLOR_SHELL; + renderfx |= RF_SHELL_HALF_DAM; + } + // pmm + // ====== + ent.oldframe = cent.prev.frame; + ent.backlerp = 1.0f - cl.lerpfrac; + + if ((renderfx & (RF_FRAMELERP | RF_BEAM))!=0) { // step origin discretely, because the frames + // do the animation properly + VectorCopy(cent.current.origin, ent.origin); + VectorCopy(cent.current.old_origin, ent.oldorigin); + } + else { // interpolate origin + for (i = 0; i < 3; i++) { + ent.origin[i] = + ent.oldorigin[i] = cent.prev.origin[i] + cl.lerpfrac * (cent.current.origin[i] - cent.prev.origin[i]); + } + } + + // create a new entity + + // tweak the color of beams + if ((renderfx & RF_BEAM)!=0) { // the four beam colors are encoded in 32 bits of skinnum (hack) + ent.alpha = 0.30f; + ent.skinnum = (s1.skinnum >> ((rand() % 4) * 8)) & 0xff; + ent.model = null; + } + else { + // set skin + if (s1.modelindex == 255) { // use custom player skin + ent.skinnum = 0; + ci = cl.clientinfo[s1.skinnum & 0xff]; + ent.skin = ci.skin; + ent.model = ci.model; + if (null==ent.skin || null==ent.model) { + ent.skin = cl.baseclientinfo.skin; + ent.model = cl.baseclientinfo.model; + } + + // ============ + // PGM + if ((renderfx & RF_USE_DISGUISE)!=0) { + if (ent.skin.name.startsWith("players/male")) { + ent.skin = re.RegisterSkin("players/male/disguise.pcx"); + ent.model = re.RegisterModel("players/male/tris.md2"); + } + else if (ent.skin.name.startsWith( "players/female")) { + ent.skin = re.RegisterSkin("players/female/disguise.pcx"); + ent.model = re.RegisterModel("players/female/tris.md2"); + } + else if (ent.skin.name.startsWith("players/cyborg")) { + ent.skin = re.RegisterSkin("players/cyborg/disguise.pcx"); + ent.model = re.RegisterModel("players/cyborg/tris.md2"); + } + } + // PGM + // ============ + } + else { + ent.skinnum = s1.skinnum; + ent.skin = null; + ent.model = cl.model_draw[s1.modelindex]; + } + } + + // only used for black hole model right now, FIXME: do better + if (renderfx == RF_TRANSLUCENT) + ent.alpha = 0.70f; + + // render effects (fullbright, translucent, etc) + if ((effects & EF_COLOR_SHELL)!=0) + ent.flags = 0; // renderfx go on color shell entity + else + ent.flags = renderfx; + + // calculate angles + if ((effects & EF_ROTATE)!=0) { // some bonus items auto-rotate + ent.angles[0] = 0; + ent.angles[1] = autorotate; + ent.angles[2] = 0; + } + // RAFAEL + else if ((effects & EF_SPINNINGLIGHTS)!=0) { + ent.angles[0] = 0; + ent.angles[1] = anglemod(cl.time / 2) + s1.angles[1]; + ent.angles[2] = 180; + { + float[] forward={0,0,0}; + float[] start={0,0,0}; + + AngleVectors(ent.angles, forward, null, null); + VectorMA(ent.origin, 64, forward, start); + V.AddLight(start, 100, 1, 0, 0); + } + } + else { // interpolate angles + float a1, a2; + + for (i = 0; i < 3; i++) { + a1 = cent.current.angles[i]; + a2 = cent.prev.angles[i]; + ent.angles[i] = LerpAngle(a2, a1, cl.lerpfrac); + } + } + + if (s1.number == cl.playernum + 1) { + ent.flags |= RF_VIEWERMODEL; // only draw from mirrors + // FIXME: still pass to refresh + + if ((effects & EF_FLAG1)!=0) + V.AddLight(ent.origin, 225, 1.0f, 0.1f, 0.1f); + else if ((effects & EF_FLAG2)!=0) + V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1.0f); + else if ((effects & EF_TAGTRAIL)!=0) //PGM + V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f); //PGM + else if ((effects & EF_TRACKERTRAIL)!=0) //PGM + V.AddLight(ent.origin, 225, -1.0f, -1.0f, -1.0f); //PGM + + continue; + } + + // if set to invisible, skip + if (s1.modelindex==0) + continue; + + if ((effects & EF_BFG)!=0) { + ent.flags |= RF_TRANSLUCENT; + ent.alpha = 0.30f; + } + + // RAFAEL + if ((effects & EF_PLASMA)!=0) { + ent.flags |= RF_TRANSLUCENT; + ent.alpha = 0.6f; + } + + if ((effects & EF_SPHERETRANS)!=0) { + ent.flags |= RF_TRANSLUCENT; + // PMM - *sigh* yet more EF overloading + if ((effects & EF_TRACKERTRAIL)!=0) + ent.alpha = 0.6f; + else + ent.alpha = 0.3f; + } + // pmm + + // add to refresh list + V.AddEntity( ent); + + // color shells generate a seperate entity for the main model + if ((effects & EF_COLOR_SHELL)!=0) { + /* + PMM - at this point, all of the shells have been handled + if we're in the rogue pack, set up the custom mixing, otherwise just + keep going + if(Developer_searchpath(2) == 2) + { + all of the solo colors are fine. we need to catch any of the combinations that look bad + (double & half) and turn them into the appropriate color, and make double/quad something special + + */ + if ((renderfx & RF_SHELL_HALF_DAM)!=0) { + if (FS.Developer_searchpath(2) == 2) { + // ditch the half damage shell if any of red, blue, or double are on + if ((renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE))!=0) + renderfx &= ~RF_SHELL_HALF_DAM; + } + } + + if ((renderfx & RF_SHELL_DOUBLE)!=0) { + if (FS.Developer_searchpath(2) == 2) { + // lose the yellow shell if we have a red, blue, or green shell + if ((renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN))!=0) + renderfx &= ~RF_SHELL_DOUBLE; + // if we have a red shell, turn it to purple by adding blue + if ((renderfx & RF_SHELL_RED)!=0) + renderfx |= RF_SHELL_BLUE; + // if we have a blue shell (and not a red shell), turn it to cyan by adding green + else if ((renderfx & RF_SHELL_BLUE)!=0) + // go to green if it's on already, otherwise do cyan (flash green) + if ((renderfx & RF_SHELL_GREEN)!=0) + renderfx &= ~RF_SHELL_BLUE; + else + renderfx |= RF_SHELL_GREEN; + } + } + // } + // pmm + ent.flags = renderfx | RF_TRANSLUCENT; + ent.alpha = 0.30f; + V.AddEntity( ent); + } + + ent.skin = null; // never use a custom skin on others + ent.skinnum = 0; + ent.flags = 0; + ent.alpha = 0; + + // duplicate for linked models + if (s1.modelindex2!=0) { + if (s1.modelindex2 == 255) { // custom weapon + ci = cl.clientinfo[s1.skinnum & 0xff]; + i = (s1.skinnum >> 8); // 0 is default weapon model + if (0==cl_vwep.value || i > MAX_CLIENTWEAPONMODELS - 1) + i = 0; + ent.model = ci.weaponmodel[i]; + if (null==ent.model) { + if (i != 0) + ent.model = ci.weaponmodel[0]; + if (null==ent.model) + ent.model = cl.baseclientinfo.weaponmodel[0]; + } + } + else + ent.model = cl.model_draw[s1.modelindex2]; + + // PMM - check for the defender sphere shell .. make it translucent + // replaces the previous version which used the high bit on modelindex2 to determine transparency + if (cl.configstrings[CS_MODELS + (s1.modelindex2)].equalsIgnoreCase( "models/items/shell/tris.md2")) { + ent.alpha = 0.32f; + ent.flags = RF_TRANSLUCENT; + } + // pmm + + V.AddEntity( ent); + + //PGM - make sure these get reset. + ent.flags = 0; + ent.alpha = 0; + //PGM + } + if (s1.modelindex3!=0) { + ent.model = cl.model_draw[s1.modelindex3]; + V.AddEntity( ent); + } + if (s1.modelindex4!=0) { + ent.model = cl.model_draw[s1.modelindex4]; + V.AddEntity( ent); + } + + if ((effects & EF_POWERSCREEN)!=0) { + ent.model = CL_tent.cl_mod_powerscreen; + ent.oldframe = 0; + ent.frame = 0; + ent.flags |= (RF_TRANSLUCENT | RF_SHELL_GREEN); + ent.alpha = 0.30f; + V.AddEntity( ent); + } + + // add automatic particle trails + if ((effects & ~EF_ROTATE)!=0) { + if ((effects & EF_ROCKET)!=0) { + RocketTrail(cent.lerp_origin, ent.origin, cent); + V.AddLight(ent.origin, 200, 1, 1, 0); + } + // PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER. + // EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese! + else if ((effects & EF_BLASTER)!=0) { + // CL_BlasterTrail (cent.lerp_origin, ent.origin); + // PGM + if ((effects & EF_TRACKER)!=0) // lame... problematic? + { + CL_newfx.BlasterTrail2(cent.lerp_origin, ent.origin); + V.AddLight(ent.origin, 200, 0, 1, 0); + } + else { + BlasterTrail(cent.lerp_origin, ent.origin); + V.AddLight(ent.origin, 200, 1, 1, 0); + } + // PGM + } + else if ((effects & EF_HYPERBLASTER)!=0) { + if ((effects & EF_TRACKER)!=0) // PGM overloaded for blaster2. + V.AddLight(ent.origin, 200, 0, 1, 0); // PGM + else // PGM + V.AddLight(ent.origin, 200, 1, 1, 0); + } + else if ((effects & EF_GIB)!=0) { + DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects); + } + else if ((effects & EF_GRENADE)!=0) { + DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects); + } + else if ((effects & EF_FLIES)!=0) { + FlyEffect(cent, ent.origin); + } + else if ((effects & EF_BFG)!=0) { + + + if ((effects & EF_ANIM_ALLFAST)!=0) { + BfgParticles( ent); + i = 200; + } + else { + i = bfg_lightramp[s1.frame]; + } + V.AddLight(ent.origin, i, 0, 1, 0); + } + // RAFAEL + else if ((effects & EF_TRAP)!=0) { + ent.origin[2] += 32; + TrapParticles( ent); + i = (rand() % 100) + 100; + V.AddLight(ent.origin, i, 1, 0.8f, 0.1f); + } + else if ((effects & EF_FLAG1)!=0) { + FlagTrail(cent.lerp_origin, ent.origin, 242); + V.AddLight(ent.origin, 225, 1, 0.1f, 0.1f); + } + else if ((effects & EF_FLAG2)!=0) { + FlagTrail(cent.lerp_origin, ent.origin, 115); + V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1); + } + // ====== + // ROGUE + else if ((effects & EF_TAGTRAIL)!=0) { + CL_newfx.TagTrail(cent.lerp_origin, ent.origin, 220); + V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f); + } + else if ((effects & EF_TRACKERTRAIL)!=0) { + if ((effects & EF_TRACKER)!=0) { + float intensity; + + intensity = (float) (50 + (500 * (Math.sin(cl.time / 500.0) + 1.0))); + // FIXME - check out this effect in rendition + if (vidref_val == VIDREF_GL) + V.AddLight(ent.origin, intensity, -1.0f, -1.0f, -1.0f); + else + V.AddLight(ent.origin, -1.0f * intensity, 1.0f, 1.0f, 1.0f); + } + else { + CL_newfx.Tracker_Shell(cent.lerp_origin); + V.AddLight(ent.origin, 155, -1.0f, -1.0f, -1.0f); + } + } + else if ((effects & EF_TRACKER)!=0) { + CL_newfx.TrackerTrail(cent.lerp_origin, ent.origin, 0); + // FIXME - check out this effect in rendition + if (vidref_val == VIDREF_GL) + V.AddLight(ent.origin, 200, -1, -1, -1); + else + V.AddLight(ent.origin, -200, 1, 1, 1); + } + // ROGUE + // ====== + // RAFAEL + else if ((effects & EF_GREENGIB)!=0) { + DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects); + } + // RAFAEL + else if ((effects & EF_IONRIPPER)!=0) { + IonripperTrail(cent.lerp_origin, ent.origin); + V.AddLight(ent.origin, 100, 1, 0.5f, 0.5f); + } + // RAFAEL + else if ((effects & EF_BLUEHYPERBLASTER)!=0) { + V.AddLight(ent.origin, 200, 0, 0, 1); + } + // RAFAEL + else if ((effects & EF_PLASMA)!=0) { + if ((effects & EF_ANIM_ALLFAST)!=0) { + BlasterTrail(cent.lerp_origin, ent.origin); + } + V.AddLight(ent.origin, 130, 1, 0.5f, 0.5f); + } + } + + VectorCopy(ent.origin, cent.lerp_origin); + } + } + + /* + ============== + CL_AddViewWeapon + ============== + */ + static void AddViewWeapon(player_state_t ps, player_state_t ops) { + entity_t gun; // view model + int i; + + // allow the gun to be completely removed + if (0==cl_gun.value) + return; + + // don't draw gun if in wide angle view + if (ps.fov > 90) + return; + + //memset( gun, 0, sizeof(gun)); + gun = new entity_t(); + + if (gun_model!=null) + gun.model = gun_model; // development tool + else + gun.model = cl.model_draw[ps.gunindex]; + + if (gun.model==null) + return; + + // set up gun position + for (i = 0; i < 3; i++) { + gun.origin[i] = cl.refdef.vieworg[i] + ops.gunoffset[i] + cl.lerpfrac * (ps.gunoffset[i] - ops.gunoffset[i]); + gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle(ops.gunangles[i], ps.gunangles[i], cl.lerpfrac); + } + + if (gun_frame!=0) { + gun.frame = gun_frame; // development tool + gun.oldframe = gun_frame; // development tool + } + else { + gun.frame = ps.gunframe; + if (gun.frame == 0) + gun.oldframe = 0; // just changed weapons, don't lerp from old + else + gun.oldframe = ops.gunframe; + } + + gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL; + gun.backlerp = 1.0f - cl.lerpfrac; + VectorCopy(gun.origin, gun.oldorigin); // don't lerp at all + V.AddEntity( gun); + } + + /* + =============== + CL_CalcViewValues + + Sets cl.refdef view values + =============== + */ + static void CalcViewValues() { + int i; + float lerp, backlerp; + centity_t ent; + frame_t oldframe; + player_state_t ps, ops; + + // find the previous frame to interpolate from + ps = cl.frame.playerstate; + + i = (cl.frame.serverframe - 1) & UPDATE_MASK; + oldframe = cl.frames[i]; + + if (oldframe.serverframe != cl.frame.serverframe - 1 || !oldframe.valid) + oldframe = cl.frame; // previous frame was dropped or involid + ops = oldframe.playerstate; + + // see if the player entity was teleported this frame + if (Math.abs(ops.pmove.origin[0] - ps.pmove.origin[0]) > 256 * 8 + || Math.abs(ops.pmove.origin[1] - ps.pmove.origin[1]) > 256 * 8 + || Math.abs(ops.pmove.origin[2] - ps.pmove.origin[2]) > 256 * 8) + ops = ps; // don't interpolate + + ent = cl_entities[cl.playernum + 1]; + lerp = cl.lerpfrac; + + // calculate the origin + if ((cl_predict.value!=0) && 0==(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) { // use predicted values + int delta; + + backlerp = 1.0f - lerp; + for (i = 0; i < 3; i++) { + cl.refdef.vieworg[i] = + cl.predicted_origin[i] + + ops.viewoffset[i] + + cl.lerpfrac * (ps.viewoffset[i] - ops.viewoffset[i]) + - backlerp * cl.prediction_error[i]; + } + + // smooth out stair climbing + delta = (int) (cls.realtime - cl.predicted_step_time); + if (delta < 100) + cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01; + } + else { // just use interpolated values + for (i = 0; i < 3; i++) + cl.refdef.vieworg[i] = + ops.pmove.origin[i] * 0.125f + + ops.viewoffset[i] + + lerp * (ps.pmove.origin[i] * 0.125f + ps.viewoffset[i] - (ops.pmove.origin[i] * 0.125f + ops.viewoffset[i])); + } + + // if not running a demo or on a locked frame, add the local angle movement + if (cl.frame.playerstate.pmove.pm_type < PM_DEAD) { // use predicted values + for (i = 0; i < 3; i++) + cl.refdef.viewangles[i] = cl.predicted_angles[i]; + } + else { // just use interpolated values + for (i = 0; i < 3; i++) + cl.refdef.viewangles[i] = LerpAngle(ops.viewangles[i], ps.viewangles[i], lerp); + } + + for (i = 0; i < 3; i++) + cl.refdef.viewangles[i] += LerpAngle(ops.kick_angles[i], ps.kick_angles[i], lerp); + + AngleVectors(cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up); + + // interpolate field of view + cl.refdef.fov_x = ops.fov + lerp * (ps.fov - ops.fov); + + // don't interpolate blend color + for (i = 0; i < 4; i++) + cl.refdef.blend[i] = ps.blend[i]; + + // add the weapon + AddViewWeapon(ps, ops); + } + + /* + =============== + CL_AddEntities + + Emits all entities, particles, and lights to the refresh + =============== + */ + static void AddEntities() { + if (cls.state != ca_active) + return; + + if (cl.time > cl.frame.servertime) { + if (cl_showclamp.value!=0) + Com.Printf("high clamp " + (cl.time - cl.frame.servertime) + "\n"); + cl.time = cl.frame.servertime; + cl.lerpfrac = 1.0f; + } + else if (cl.time < cl.frame.servertime - 100) { + if (cl_showclamp.value!=0) + Com.Printf("low clamp " + (cl.frame.servertime - 100 - cl.time)+"\n"); + cl.time = cl.frame.servertime - 100; + cl.lerpfrac = 0; + } + else + cl.lerpfrac = 1.0f - (cl.frame.servertime - cl.time) * 0.01f; + + if (cl_timedemo.value!=0) + cl.lerpfrac = 1.0f; + + + /* is ok.. + CL_AddPacketEntities (cl.frame); + CL_AddTEnts (); + CL_AddParticles (); + CL_AddDLights (); + CL_AddLightStyles (); + */ + + CalcViewValues(); + // PMM - moved this here so the heat beam has the right values for the vieworg, and can lock the beam to the gun + AddPacketEntities( cl.frame); + + CL_tent.AddTEnts(); + AddParticles(); + CL_fx.AddDLights(); + AddLightStyles(); + } + + /* + =============== + CL_GetEntitySoundOrigin + + Called to get the sound spatialization origin + =============== + */ + void GetEntitySoundOrigin(int ent, float[] org) { + centity_t old; + + if (ent < 0 || ent >= MAX_EDICTS) + Com.Error(ERR_DROP, "CL_GetEntitySoundOrigin: bad ent"); + old = cl_entities[ent]; + VectorCopy(old.lerp_origin, org); + + // FIXME: bmodel issues... + } + +} diff --git a/src/jake2/client/CL_fx.java b/src/jake2/client/CL_fx.java new file mode 100644 index 0000000..1ce63ad --- /dev/null +++ b/src/jake2/client/CL_fx.java @@ -0,0 +1,2292 @@ +/* + * CL_fx.java + * Copyright (C) 2004 + * + * $Id: CL_fx.java,v 1.1 2004-07-07 19:58:36 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.game.M_Flash; +import jake2.game.entity_state_t; +import jake2.qcommon.Com; +import jake2.qcommon.MSG; + +/** + * CL_fx + */ +public class CL_fx extends CL_tent { + + static final float INSTANT_PARTICLE = -10000.0f; + + static class cdlight_t { + int key; // so entities can reuse same entry + float[] color = { 0, 0, 0 }; + float[] origin = { 0, 0, 0 }; + float radius; + float die; // stop lighting after this time + float decay; // drop this each second + float minlight; // don't add when contributing less + void clear() { + radius = + decay = die = minlight = color[0] = color[1] = color[2] = origin[0] = origin[1] = origin[2] = key = 0; + } + } + + static float[][] avelocities = new float[NUMVERTEXNORMALS][3]; + + /* + ============================================================== + + LIGHT STYLE MANAGEMENT + + ============================================================== + */ + + static class clightstyle_t { + int length; + float[] value = new float[3]; + float[] map = new float[MAX_QPATH]; + void clear() { + value[0] = value[1] = value[2] = length = 0; + for (int i = 0; i < map.length; i++) + map[i] = 0.0f; + } + } + static clightstyle_t[] cl_lightstyle = new clightstyle_t[MAX_LIGHTSTYLES]; + static { + for(int i=0; i<cl_lightstyle.length; i++) { + cl_lightstyle[i] = new clightstyle_t(); + } + } + static int lastofs; + + /* + ================ + CL_ClearLightStyles + ================ + */ + static void ClearLightStyles () + { + //memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); + for (int i = 0; i < cl_lightstyle.length; i++) + cl_lightstyle[i].clear(); + lastofs = -1; + } + + /* + ================ + CL_RunLightStyles + ================ + */ + static void RunLightStyles() { + int ofs; + int i; + clightstyle_t[] ls; + + ofs = cl.time / 100; + if (ofs == lastofs) + return; + lastofs = ofs; + ls = cl_lightstyle; + for (i = 0; i < ls.length; i++) { + if (ls[i].length == 0) { + ls[i].value[0] = ls[i].value[1] = ls[i].value[2] = 1.0f; + continue; + } + if (ls.length == 1) + ls[i].value[0] = ls[i].value[1] = ls[i].value[2] = ls[i].map[0]; + else + ls[i].value[0] = ls[i].value[1] = ls[i].value[2] = ls[i].map[ofs % ls[i].length]; + } + } + + static void SetLightstyle(int i) { + String s; + int j, k; + + s = cl.configstrings[i + CS_LIGHTS]; + + j = strlen(s); + if (j >= MAX_QPATH) + Com.Error(ERR_DROP, "svc_lightstyle length=" + j); + + cl_lightstyle[i].length = j; + + for (k = 0; k < j; k++) + cl_lightstyle[i].map[k] = (float) (s.charAt(k) - 'a') / (float) ('m' - 'a'); + } + + /* + ================ + CL_AddLightStyles + ================ + */ + static void AddLightStyles() { + int i; + clightstyle_t[] ls; + + ls = cl_lightstyle; + for (i = 0; i < ls.length; i++) + V.AddLightStyle(i, ls[i].value[0], ls[i].value[1], ls[i].value[2]); + } + + /* + ============================================================== + + DLIGHT MANAGEMENT + + ============================================================== + */ + + static cdlight_t[] cl_dlights = new cdlight_t[MAX_DLIGHTS]; + static { + for (int i = 0; i < cl_dlights.length; i++) + cl_dlights[i] = new cdlight_t(); + } + + /* + ================ + CL_ClearDlights + ================ + */ + static void ClearDlights() { + // memset (cl_dlights, 0, sizeof(cl_dlights)); + for (int i = 0; i < cl_dlights.length; i++) { + cl_dlights[i].clear(); + } + } + + /* + =============== + CL_AllocDlight + + =============== + */ + static cdlight_t AllocDlight(int key) { + int i; + cdlight_t[] dl; + + // first look for an exact key match + if (key != 0) { + dl = cl_dlights; + for (i = 0; i < MAX_DLIGHTS; i++) { + if (dl[i].key == key) { + //memset (dl, 0, sizeof(*dl)); + dl[i].clear(); + dl[i].key = key; + return dl[i]; + } + } + } + + // then look for anything else + dl = cl_dlights; + for (i = 0; i < MAX_DLIGHTS; i++) { + if (dl[i].die < cl.time) { + //memset (dl, 0, sizeof(*dl)); + dl[i].clear(); + dl[i].key = key; + return dl[i]; + } + } + + //dl = &cl_dlights[0]; + //memset (dl, 0, sizeof(*dl)); + dl[0].clear(); + dl[0].key = key; + return dl[0]; + } + + /* + =============== + CL_NewDlight + =============== + */ + static void NewDlight(int key, float x, float y, float z, float radius, float time) { + cdlight_t dl; + + dl = CL.AllocDlight(key); + dl.origin[0] = x; + dl.origin[1] = y; + dl.origin[2] = z; + dl.radius = radius; + dl.die = cl.time + time; + } + + /* + =============== + CL_RunDLights + + =============== + */ + static void RunDLights() { + int i; + cdlight_t[] dl; + + dl = cl_dlights; + for (i = 0; i < MAX_DLIGHTS; i++) { + if (dl[i].radius == 0.0f) + continue; + + if (dl[i].die < cl.time) { + dl[i].radius = 0; + return; + } + dl[i].radius -= cls.frametime * dl[i].decay; + if (dl[i].radius < 0) + dl[i].radius = 0; + } + } + + /* + ============== + CL_ParseMuzzleFlash + ============== + */ + static void ParseMuzzleFlash() { + float[] fv = new float[3]; + float[] rv = new float[3]; + cdlight_t dl; + int i, weapon; + centity_t pl; + int silenced; + float volume; + String soundname; + + i = MSG.ReadShort(net_message); + if (i < 1 || i >= MAX_EDICTS) + Com.Error(ERR_DROP, "CL_ParseMuzzleFlash: bad entity"); + + weapon = MSG.ReadByte(net_message); + silenced = weapon & MZ_SILENCED; + weapon &= ~MZ_SILENCED; + + pl = cl_entities[i]; + + dl = CL.AllocDlight(i); + VectorCopy(pl.current.origin, dl.origin); + AngleVectors(pl.current.angles, fv, rv, null); + VectorMA(dl.origin, 18, fv, dl.origin); + VectorMA(dl.origin, 16, rv, dl.origin); + if (silenced != 0) + dl.radius = 100 + (rand() & 31); + else + dl.radius = 200 + (rand() & 31); + dl.minlight = 32; + dl.die = cl.time; // + 0.1; + + if (silenced != 0) + volume = 0.2f; + else + volume = 1; + + switch (weapon) { + case MZ_BLASTER : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0); + break; + case MZ_BLUEHYPERBLASTER : + dl.color[0] = 0; + dl.color[1] = 0; + dl.color[2] = 1; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0); + break; + case MZ_HYPERBLASTER : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0); + break; + case MZ_MACHINEGUN : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0); + break; + case MZ_SHOTGUN : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0); + S.StartSound(null, i, CHAN_AUTO, S.RegisterSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1f); + break; + case MZ_SSHOTGUN : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0); + break; + case MZ_CHAINGUN1 : + dl.radius = 200 + (rand() & 31); + dl.color[0] = 1; + dl.color[1] = 0.25f; + dl.color[2] = 0; + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0); + break; + case MZ_CHAINGUN2 : + dl.radius = 225 + (rand() & 31); + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0; + dl.die = cl.time + 0.1f; // long delay + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0); + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0.05f); + break; + case MZ_CHAINGUN3 : + dl.radius = 250 + (rand() & 31); + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + dl.die = cl.time + 0.1f; // long delay + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0); + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0.033f); + //Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1); + soundname = "weapons/machgf" + ((rand() % 5) + 1) + "b.wav"; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound(soundname), volume, ATTN_NORM, 0.066f); + break; + case MZ_RAILGUN : + dl.color[0] = 0.5f; + dl.color[1] = 0.5f; + dl.color[2] = 1.0f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0); + break; + case MZ_ROCKET : + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0.2f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0); + S.StartSound(null, i, CHAN_AUTO, S.RegisterSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1f); + break; + case MZ_GRENADE : + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0); + S.StartSound(null, i, CHAN_AUTO, S.RegisterSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1f); + break; + case MZ_BFG : + dl.color[0] = 0; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0); + break; + + case MZ_LOGIN : + dl.color[0] = 0; + dl.color[1] = 1; + dl.color[2] = 0; + dl.die = cl.time + 1.0f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); + CL.LogoutEffect(pl.current.origin, weapon); + break; + case MZ_LOGOUT : + dl.color[0] = 1; + dl.color[1] = 0; + dl.color[2] = 0; + dl.die = cl.time + 1.0f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); + CL.LogoutEffect(pl.current.origin, weapon); + break; + case MZ_RESPAWN : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + dl.die = cl.time + 1.0f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); + CL.LogoutEffect(pl.current.origin, weapon); + break; + // RAFAEL + case MZ_PHALANX : + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0.5f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0); + break; + // RAFAEL + case MZ_IONRIPPER : + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0.5f; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0); + break; + + // ====================== + // PGM + case MZ_ETF_RIFLE : + dl.color[0] = 0.9f; + dl.color[1] = 0.7f; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/nail1.wav"), volume, ATTN_NORM, 0); + break; + case MZ_SHOTGUN2 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/shotg2.wav"), volume, ATTN_NORM, 0); + break; + case MZ_HEATBEAM : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + dl.die = cl.time + 100; + // S.StartSound (null, i, CHAN_WEAPON, S.RegisterSound("weapons/bfg__l1a.wav"), volume, ATTN_NORM, 0); + break; + case MZ_BLASTER2 : + dl.color[0] = 0; + dl.color[1] = 1; + dl.color[2] = 0; + // FIXME - different sound for blaster2 ?? + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0); + break; + case MZ_TRACKER : + // negative flashes handled the same in gl/soft until CL_AddDLights + dl.color[0] = -1; + dl.color[1] = -1; + dl.color[2] = -1; + S.StartSound(null, i, CHAN_WEAPON, S.RegisterSound("weapons/disint2.wav"), volume, ATTN_NORM, 0); + break; + case MZ_NUKE1 : + dl.color[0] = 1; + dl.color[1] = 0; + dl.color[2] = 0; + dl.die = cl.time + 100; + break; + case MZ_NUKE2 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + dl.die = cl.time + 100; + break; + case MZ_NUKE4 : + dl.color[0] = 0; + dl.color[1] = 0; + dl.color[2] = 1; + dl.die = cl.time + 100; + break; + case MZ_NUKE8 : + dl.color[0] = 0; + dl.color[1] = 1; + dl.color[2] = 1; + dl.die = cl.time + 100; + break; + // PGM + // ====================== + } + } + + /* + ============== + CL_ParseMuzzleFlash2 + ============== + */ + static void ParseMuzzleFlash2() { + int ent; + float[] origin = new float[3]; + int flash_number; + cdlight_t dl; + float[] forward = new float[3]; + float[] right = new float[3]; + String soundname; + + ent = MSG.ReadShort(net_message); + if (ent < 1 || ent >= MAX_EDICTS) + Com.Error(ERR_DROP, "CL_ParseMuzzleFlash2: bad entity"); + + flash_number = MSG.ReadByte(net_message); + + // locate the origin + AngleVectors(cl_entities[ent].current.angles, forward, right, null); + origin[0] = + cl_entities[ent].current.origin[0] + + forward[0] * M_Flash.monster_flash_offset[flash_number][0] + + right[0] * M_Flash.monster_flash_offset[flash_number][1]; + origin[1] = + cl_entities[ent].current.origin[1] + + forward[1] * M_Flash.monster_flash_offset[flash_number][0] + + right[1] * M_Flash.monster_flash_offset[flash_number][1]; + origin[2] = + cl_entities[ent].current.origin[2] + + forward[2] * M_Flash.monster_flash_offset[flash_number][0] + + right[2] * M_Flash.monster_flash_offset[flash_number][1] + + M_Flash.monster_flash_offset[flash_number][2]; + + dl = CL.AllocDlight(ent); + VectorCopy(origin, dl.origin); + dl.radius = 200 + (rand() & 31); + dl.minlight = 32; + dl.die = cl.time; // + 0.1; + + switch (flash_number) { + case MZ2_INFANTRY_MACHINEGUN_1 : + case MZ2_INFANTRY_MACHINEGUN_2 : + case MZ2_INFANTRY_MACHINEGUN_3 : + case MZ2_INFANTRY_MACHINEGUN_4 : + case MZ2_INFANTRY_MACHINEGUN_5 : + case MZ2_INFANTRY_MACHINEGUN_6 : + case MZ2_INFANTRY_MACHINEGUN_7 : + case MZ2_INFANTRY_MACHINEGUN_8 : + case MZ2_INFANTRY_MACHINEGUN_9 : + case MZ2_INFANTRY_MACHINEGUN_10 : + case MZ2_INFANTRY_MACHINEGUN_11 : + case MZ2_INFANTRY_MACHINEGUN_12 : + case MZ2_INFANTRY_MACHINEGUN_13 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_SOLDIER_MACHINEGUN_1 : + case MZ2_SOLDIER_MACHINEGUN_2 : + case MZ2_SOLDIER_MACHINEGUN_3 : + case MZ2_SOLDIER_MACHINEGUN_4 : + case MZ2_SOLDIER_MACHINEGUN_5 : + case MZ2_SOLDIER_MACHINEGUN_6 : + case MZ2_SOLDIER_MACHINEGUN_7 : + case MZ2_SOLDIER_MACHINEGUN_8 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_GUNNER_MACHINEGUN_1 : + case MZ2_GUNNER_MACHINEGUN_2 : + case MZ2_GUNNER_MACHINEGUN_3 : + case MZ2_GUNNER_MACHINEGUN_4 : + case MZ2_GUNNER_MACHINEGUN_5 : + case MZ2_GUNNER_MACHINEGUN_6 : + case MZ2_GUNNER_MACHINEGUN_7 : + case MZ2_GUNNER_MACHINEGUN_8 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_ACTOR_MACHINEGUN_1 : + case MZ2_SUPERTANK_MACHINEGUN_1 : + case MZ2_SUPERTANK_MACHINEGUN_2 : + case MZ2_SUPERTANK_MACHINEGUN_3 : + case MZ2_SUPERTANK_MACHINEGUN_4 : + case MZ2_SUPERTANK_MACHINEGUN_5 : + case MZ2_SUPERTANK_MACHINEGUN_6 : + case MZ2_TURRET_MACHINEGUN : // PGM + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_BOSS2_MACHINEGUN_L1 : + case MZ2_BOSS2_MACHINEGUN_L2 : + case MZ2_BOSS2_MACHINEGUN_L3 : + case MZ2_BOSS2_MACHINEGUN_L4 : + case MZ2_BOSS2_MACHINEGUN_L5 : + case MZ2_CARRIER_MACHINEGUN_L1 : // PMM + case MZ2_CARRIER_MACHINEGUN_L2 : // PMM + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0); + break; + + case MZ2_SOLDIER_BLASTER_1 : + case MZ2_SOLDIER_BLASTER_2 : + case MZ2_SOLDIER_BLASTER_3 : + case MZ2_SOLDIER_BLASTER_4 : + case MZ2_SOLDIER_BLASTER_5 : + case MZ2_SOLDIER_BLASTER_6 : + case MZ2_SOLDIER_BLASTER_7 : + case MZ2_SOLDIER_BLASTER_8 : + case MZ2_TURRET_BLASTER : // PGM + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_FLYER_BLASTER_1 : + case MZ2_FLYER_BLASTER_2 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_MEDIC_BLASTER_1 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("medic/medatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_HOVER_BLASTER_1 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_FLOAT_BLASTER_1 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_SOLDIER_SHOTGUN_1 : + case MZ2_SOLDIER_SHOTGUN_2 : + case MZ2_SOLDIER_SHOTGUN_3 : + case MZ2_SOLDIER_SHOTGUN_4 : + case MZ2_SOLDIER_SHOTGUN_5 : + case MZ2_SOLDIER_SHOTGUN_6 : + case MZ2_SOLDIER_SHOTGUN_7 : + case MZ2_SOLDIER_SHOTGUN_8 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_TANK_BLASTER_1 : + case MZ2_TANK_BLASTER_2 : + case MZ2_TANK_BLASTER_3 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_TANK_MACHINEGUN_1 : + case MZ2_TANK_MACHINEGUN_2 : + case MZ2_TANK_MACHINEGUN_3 : + case MZ2_TANK_MACHINEGUN_4 : + case MZ2_TANK_MACHINEGUN_5 : + case MZ2_TANK_MACHINEGUN_6 : + case MZ2_TANK_MACHINEGUN_7 : + case MZ2_TANK_MACHINEGUN_8 : + case MZ2_TANK_MACHINEGUN_9 : + case MZ2_TANK_MACHINEGUN_10 : + case MZ2_TANK_MACHINEGUN_11 : + case MZ2_TANK_MACHINEGUN_12 : + case MZ2_TANK_MACHINEGUN_13 : + case MZ2_TANK_MACHINEGUN_14 : + case MZ2_TANK_MACHINEGUN_15 : + case MZ2_TANK_MACHINEGUN_16 : + case MZ2_TANK_MACHINEGUN_17 : + case MZ2_TANK_MACHINEGUN_18 : + case MZ2_TANK_MACHINEGUN_19 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + //Com_sprintf(soundname, sizeof(soundname), "tank/tnkatk2%c.wav", 'a' + rand() % 5); + soundname = "tank/tnkatk2" + (char) ('a' + rand() % 5) + ".wav"; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound(soundname), 1, ATTN_NORM, 0); + break; + + case MZ2_CHICK_ROCKET_1 : + case MZ2_TURRET_ROCKET : // PGM + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0.2f; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_TANK_ROCKET_1 : + case MZ2_TANK_ROCKET_2 : + case MZ2_TANK_ROCKET_3 : + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0.2f; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_SUPERTANK_ROCKET_1 : + case MZ2_SUPERTANK_ROCKET_2 : + case MZ2_SUPERTANK_ROCKET_3 : + case MZ2_BOSS2_ROCKET_1 : + case MZ2_BOSS2_ROCKET_2 : + case MZ2_BOSS2_ROCKET_3 : + case MZ2_BOSS2_ROCKET_4 : + case MZ2_CARRIER_ROCKET_1 : + // case MZ2_CARRIER_ROCKET_2: + // case MZ2_CARRIER_ROCKET_3: + // case MZ2_CARRIER_ROCKET_4: + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0.2f; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("tank/rocket.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_GUNNER_GRENADE_1 : + case MZ2_GUNNER_GRENADE_2 : + case MZ2_GUNNER_GRENADE_3 : + case MZ2_GUNNER_GRENADE_4 : + dl.color[0] = 1; + dl.color[1] = 0.5f; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_GLADIATOR_RAILGUN_1 : + // PMM + case MZ2_CARRIER_RAILGUN : + case MZ2_WIDOW_RAIL : + // pmm + dl.color[0] = 0.5f; + dl.color[1] = 0.5f; + dl.color[2] = 1.0f; + break; + + // --- Xian's shit starts --- + case MZ2_MAKRON_BFG : + dl.color[0] = 0.5f; + dl.color[1] = 1; + dl.color[2] = 0.5f; + //S.StartSound (null, ent, CHAN_WEAPON, S.RegisterSound("makron/bfg_fire.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_MAKRON_BLASTER_1 : + case MZ2_MAKRON_BLASTER_2 : + case MZ2_MAKRON_BLASTER_3 : + case MZ2_MAKRON_BLASTER_4 : + case MZ2_MAKRON_BLASTER_5 : + case MZ2_MAKRON_BLASTER_6 : + case MZ2_MAKRON_BLASTER_7 : + case MZ2_MAKRON_BLASTER_8 : + case MZ2_MAKRON_BLASTER_9 : + case MZ2_MAKRON_BLASTER_10 : + case MZ2_MAKRON_BLASTER_11 : + case MZ2_MAKRON_BLASTER_12 : + case MZ2_MAKRON_BLASTER_13 : + case MZ2_MAKRON_BLASTER_14 : + case MZ2_MAKRON_BLASTER_15 : + case MZ2_MAKRON_BLASTER_16 : + case MZ2_MAKRON_BLASTER_17 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("makron/blaster.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_JORG_MACHINEGUN_L1 : + case MZ2_JORG_MACHINEGUN_L2 : + case MZ2_JORG_MACHINEGUN_L3 : + case MZ2_JORG_MACHINEGUN_L4 : + case MZ2_JORG_MACHINEGUN_L5 : + case MZ2_JORG_MACHINEGUN_L6 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("boss3/xfire.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_JORG_MACHINEGUN_R1 : + case MZ2_JORG_MACHINEGUN_R2 : + case MZ2_JORG_MACHINEGUN_R3 : + case MZ2_JORG_MACHINEGUN_R4 : + case MZ2_JORG_MACHINEGUN_R5 : + case MZ2_JORG_MACHINEGUN_R6 : + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + break; + + case MZ2_JORG_BFG_1 : + dl.color[0] = 0.5f; + dl.color[1] = 1; + dl.color[2] = 0.5f; + break; + + case MZ2_BOSS2_MACHINEGUN_R1 : + case MZ2_BOSS2_MACHINEGUN_R2 : + case MZ2_BOSS2_MACHINEGUN_R3 : + case MZ2_BOSS2_MACHINEGUN_R4 : + case MZ2_BOSS2_MACHINEGUN_R5 : + case MZ2_CARRIER_MACHINEGUN_R1 : // PMM + case MZ2_CARRIER_MACHINEGUN_R2 : // PMM + + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + + CL.ParticleEffect(origin, vec3_origin, 0, 40); + CL.SmokeAndFlash(origin); + break; + + // ====== + // ROGUE + case MZ2_STALKER_BLASTER : + case MZ2_DAEDALUS_BLASTER : + case MZ2_MEDIC_BLASTER_2 : + case MZ2_WIDOW_BLASTER : + case MZ2_WIDOW_BLASTER_SWEEP1 : + case MZ2_WIDOW_BLASTER_SWEEP2 : + case MZ2_WIDOW_BLASTER_SWEEP3 : + case MZ2_WIDOW_BLASTER_SWEEP4 : + case MZ2_WIDOW_BLASTER_SWEEP5 : + case MZ2_WIDOW_BLASTER_SWEEP6 : + case MZ2_WIDOW_BLASTER_SWEEP7 : + case MZ2_WIDOW_BLASTER_SWEEP8 : + case MZ2_WIDOW_BLASTER_SWEEP9 : + case MZ2_WIDOW_BLASTER_100 : + case MZ2_WIDOW_BLASTER_90 : + case MZ2_WIDOW_BLASTER_80 : + case MZ2_WIDOW_BLASTER_70 : + case MZ2_WIDOW_BLASTER_60 : + case MZ2_WIDOW_BLASTER_50 : + case MZ2_WIDOW_BLASTER_40 : + case MZ2_WIDOW_BLASTER_30 : + case MZ2_WIDOW_BLASTER_20 : + case MZ2_WIDOW_BLASTER_10 : + case MZ2_WIDOW_BLASTER_0 : + case MZ2_WIDOW_BLASTER_10L : + case MZ2_WIDOW_BLASTER_20L : + case MZ2_WIDOW_BLASTER_30L : + case MZ2_WIDOW_BLASTER_40L : + case MZ2_WIDOW_BLASTER_50L : + case MZ2_WIDOW_BLASTER_60L : + case MZ2_WIDOW_BLASTER_70L : + case MZ2_WIDOW_RUN_1 : + case MZ2_WIDOW_RUN_2 : + case MZ2_WIDOW_RUN_3 : + case MZ2_WIDOW_RUN_4 : + case MZ2_WIDOW_RUN_5 : + case MZ2_WIDOW_RUN_6 : + case MZ2_WIDOW_RUN_7 : + case MZ2_WIDOW_RUN_8 : + dl.color[0] = 0; + dl.color[1] = 1; + dl.color[2] = 0; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_WIDOW_DISRUPTOR : + dl.color[0] = -1; + dl.color[1] = -1; + dl.color[2] = -1; + S.StartSound(null, ent, CHAN_WEAPON, S.RegisterSound("weapons/disint2.wav"), 1, ATTN_NORM, 0); + break; + + case MZ2_WIDOW_PLASMABEAM : + case MZ2_WIDOW2_BEAMER_1 : + case MZ2_WIDOW2_BEAMER_2 : + case MZ2_WIDOW2_BEAMER_3 : + case MZ2_WIDOW2_BEAMER_4 : + case MZ2_WIDOW2_BEAMER_5 : + case MZ2_WIDOW2_BEAM_SWEEP_1 : + case MZ2_WIDOW2_BEAM_SWEEP_2 : + case MZ2_WIDOW2_BEAM_SWEEP_3 : + case MZ2_WIDOW2_BEAM_SWEEP_4 : + case MZ2_WIDOW2_BEAM_SWEEP_5 : + case MZ2_WIDOW2_BEAM_SWEEP_6 : + case MZ2_WIDOW2_BEAM_SWEEP_7 : + case MZ2_WIDOW2_BEAM_SWEEP_8 : + case MZ2_WIDOW2_BEAM_SWEEP_9 : + case MZ2_WIDOW2_BEAM_SWEEP_10 : + case MZ2_WIDOW2_BEAM_SWEEP_11 : + dl.radius = 300 + (rand() & 100); + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 0; + dl.die = cl.time + 200; + break; + // ROGUE + // ====== + + // --- Xian's shit ends --- + + } + } + + /* + =============== + CL_AddDLights + + =============== + */ + static void AddDLights() { + int i; + cdlight_t[] dl; + + dl = cl_dlights; + + // ===== + // PGM + if (vidref_val == VIDREF_GL) { + for (i = 0; i < MAX_DLIGHTS; i++) { + if (dl[i].radius == 0.0f) + continue; + V.AddLight(dl[i].origin, dl[i].radius, dl[i].color[0], dl[i].color[1], dl[i].color[2]); + } + } else { + for (i = 0; i < MAX_DLIGHTS; i++) { + if (dl[i].radius == 0.0f) + continue; + + // negative light in software. only black allowed + if ((dl[i].color[0] < 0) || (dl[i].color[1] < 0) || (dl[i].color[2] < 0)) { + dl[i].radius = - (dl[i].radius); + dl[i].color[0] = 1; + dl[i].color[1] = 1; + dl[i].color[2] = 1; + } + V.AddLight(dl[i].origin, dl[i].radius, dl[i].color[0], dl[i].color[1], dl[i].color[2]); + } + } + // PGM + // ===== + } + + /* + ============================================================== + + PARTICLE MANAGEMENT + + ============================================================== + */ + + static final int PARTICLE_GRAVITY = 40; + static cparticle_t active_particles, free_particles; + + static cparticle_t[] particles = new cparticle_t[MAX_PARTICLES]; + static { + for (int i = 0; i < particles.length; i++) + particles[i] = new cparticle_t(); + } + static int cl_numparticles = MAX_PARTICLES; + + /* + =============== + CL_ClearParticles + =============== + */ + static void ClearParticles() + { + int i; + + free_particles = particles[0]; + active_particles = null; + + for (i=0 ; i<particles.length - 1; i++) + particles[i].next = particles[i+1]; + particles[particles.length - 1].next = null; + } + + /* + =============== + CL_ParticleEffect + + Wall impact puffs + =============== + */ + static void ParticleEffect(float[] org, float[] dir, int color, int count) { + int i, j; + cparticle_t p; + float d; + + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color + (rand() & 7); + + d = rand() & 31; + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() & 7) - 4) + d * dir[j]; + p.vel[j] = crand() * 20; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_ParticleEffect2 + =============== + */ + static void ParticleEffect2(float[] org, float[] dir, int color, int count) { + int i, j; + cparticle_t p; + float d; + + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color; + + d = rand() & 7; + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() & 7) - 4) + d * dir[j]; + p.vel[j] = crand() * 20; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + } + + // RAFAEL + /* + =============== + CL_ParticleEffect3 + =============== + */ + static void ParticleEffect3(float[] org, float[] dir, int color, int count) { + int i, j; + cparticle_t p; + float d; + + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color; + + d = rand() & 7; + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() & 7) - 4) + d * dir[j]; + p.vel[j] = crand() * 20; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_TeleporterParticles + =============== + */ + static void TeleporterParticles(entity_state_t ent) { + int i, j; + cparticle_t p; + + for (i = 0; i < 8; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = 0xdb; + + for (j = 0; j < 2; j++) { + p.org[j] = ent.origin[j] - 16 + (rand() & 31); + p.vel[j] = crand() * 14; + } + + p.org[2] = ent.origin[2] - 8 + (rand() & 7); + p.vel[2] = 80 + (rand() & 7); + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -0.5f; + } + } + + /* + =============== + CL_LogoutEffect + + =============== + */ + static void LogoutEffect(float[] org, int type) { + int i, j; + cparticle_t p; + + for (i = 0; i < 500; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + + if (type == MZ_LOGIN) + p.color = 0xd0 + (rand() & 7); // green + else if (type == MZ_LOGOUT) + p.color = 0x40 + (rand() & 7); // red + else + p.color = 0xe0 + (rand() & 7); // yellow + + p.org[0] = org[0] - 16 + frand() * 32; + p.org[1] = org[1] - 16 + frand() * 32; + p.org[2] = org[2] - 24 + frand() * 56; + + for (j = 0; j < 3; j++) + p.vel[j] = crand() * 20; + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (1.0f + frand() * 0.3f); + } + } + + /* + =============== + CL_ItemRespawnParticles + + =============== + */ + static void ItemRespawnParticles(float[] org) { + int i, j; + cparticle_t p; + + for (i = 0; i < 64; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + + p.color = 0xd4 + (rand() & 3); // green + + p.org[0] = org[0] + crand() * 8; + p.org[1] = org[1] + crand() * 8; + p.org[2] = org[2] + crand() * 8; + + for (j = 0; j < 3; j++) + p.vel[j] = crand() * 8; + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY * 0.2f; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (1.0f + frand() * 0.3f); + } + } + + /* + =============== + CL_ExplosionParticles + =============== + */ + static void ExplosionParticles(float[] org) { + int i, j; + cparticle_t p; + + for (i = 0; i < 256; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = 0xe0 + (rand() & 7); + + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() % 32) - 16); + p.vel[j] = (rand() % 384) - 192; + } + + p.accel[0] = p.accel[1] = 0.0f; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -0.8f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_BigTeleportParticles + =============== + */ + private static int[] colortable = { 2 * 8, 13 * 8, 21 * 8, 18 * 8 }; + static void BigTeleportParticles(float[] org) { + int i; + cparticle_t p; + float angle, dist; + + for (i = 0; i < 4096; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + + p.color = colortable[rand() & 3]; + + angle = (float) (Math.PI * 2 * (rand() & 1023) / 1023.0); + dist = rand() & 31; + p.org[0] = (float) (org[0] + Math.cos(angle) * dist); + p.vel[0] = (float) (Math.cos(angle) * (70 + (rand() & 63))); + p.accel[0] = (float) (-Math.cos(angle) * 100); + + p.org[1] = (float) (org[1] + Math.sin(angle) * dist); + p.vel[1] = (float) (Math.sin(angle) * (70 + (rand() & 63))); + p.accel[1] = (float) (-Math.sin(angle) * 100); + + p.org[2] = org[2] + 8 + (rand() % 90); + p.vel[2] = -100 + (rand() & 31); + p.accel[2] = PARTICLE_GRAVITY * 4; + p.alpha = 1.0f; + + p.alphavel = -0.3f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_BlasterParticles + + Wall impact puffs + =============== + */ + static void BlasterParticles(float[] org, float[] dir) { + int i, j; + cparticle_t p; + float d; + int count; + + count = 40; + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = 0xe0 + (rand() & 7); + + d = rand() & 15; + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() & 7) - 4) + d * dir[j]; + p.vel[j] = dir[j] * 30 + crand() * 40; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_BlasterTrail + + =============== + */ + static void BlasterTrail(float[] start, float[] end) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + // FIXME: this is a really silly way to have a loop + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.3f + frand() * 0.2f); + p.color = 0xe0; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand(); + p.vel[j] = crand() * 5; + p.accel[j] = 0; + } + + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_QuadTrail + + =============== + */ + static void QuadTrail(float[] start, float[] end) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.8f + frand() * 0.2f); + p.color = 115; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 16; + p.vel[j] = crand() * 5; + p.accel[j] = 0; + } + + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_FlagTrail + + =============== + */ + static void FlagTrail(float[] start, float[] end, float color) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.8f + frand() * 0.2f); + p.color = color; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 16; + p.vel[j] = crand() * 5; + p.accel[j] = 0; + } + + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_DiminishingTrail + + =============== + */ + static void DiminishingTrail(float[] start, float[] end, centity_t old, int flags) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + float dec; + float orgscale; + float velscale; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 0.5f; + VectorScale(vec, dec, vec); + + if (old.trailcount > 900) { + orgscale = 4; + velscale = 15; + } else if (old.trailcount > 800) { + orgscale = 2; + velscale = 10; + } else { + orgscale = 1; + velscale = 5; + } + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + + // drop less particles as it flies + if ((rand() & 1023) < old.trailcount) { + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + if ((flags & EF_GIB) != 0) { + p.alpha = 1.0f; + p.alphavel = -1.0f / (1.0f + frand() * 0.4f); + p.color = 0xe8 + (rand() & 7); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * orgscale; + p.vel[j] = crand() * velscale; + p.accel[j] = 0; + } + p.vel[2] -= PARTICLE_GRAVITY; + } else if ((flags & EF_GREENGIB) != 0) { + p.alpha = 1.0f; + p.alphavel = -1.0f / (1.0f + frand() * 0.4f); + p.color = 0xdb + (rand() & 7); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * orgscale; + p.vel[j] = crand() * velscale; + p.accel[j] = 0; + } + p.vel[2] -= PARTICLE_GRAVITY; + } else { + p.alpha = 1.0f; + p.alphavel = -1.0f / (1.0f + frand() * 0.2f); + p.color = 4 + (rand() & 7); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * orgscale; + p.vel[j] = crand() * velscale; + } + p.accel[2] = 20; + } + } + + old.trailcount -= 5; + if (old.trailcount < 100) + old.trailcount = 100; + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_RocketTrail + + =============== + */ + static void RocketTrail(float[] start, float[] end, centity_t old) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + float dec; + + // smoke + CL.DiminishingTrail(start, end, old, EF_ROCKET); + + // fire + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 1; + VectorScale(vec, dec, vec); + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + + if ((rand() & 7) == 0) { + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + VectorClear(p.accel); + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1.0f + frand() * 0.2f); + p.color = 0xdc + (rand() & 3); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 5; + p.vel[j] = crand() * 20; + } + p.accel[2] = -PARTICLE_GRAVITY; + } + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_RailTrail + + =============== + */ + static void RailTrail(float[] start, float[] end) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + float dec; + float[] right = new float[3]; + float[] up = new float[3]; + int i; + float d, c, s; + float[] dir = new float[3]; + byte clr = 0x74; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + MakeNormalVectors(vec, right, up); + + for (i = 0; i < len; i++) { + if (free_particles == null) + return; + + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + VectorClear(p.accel); + + d = i * 0.1f; + c = (float)Math.cos(d); + s = (float)Math.sin(d); + + VectorScale(right, c, dir); + VectorMA(dir, s, up, dir); + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1.0f + frand() * 0.2f); + p.color = clr + (rand() & 7); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + dir[j] * 3; + p.vel[j] = dir[j] * 6; + } + + VectorAdd(move, vec, move); + } + + dec = 0.75f; + VectorScale(vec, dec, vec); + VectorCopy(start, move); + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + VectorClear(p.accel); + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.6f + frand() * 0.2f); + p.color = 0x0 + rand() & 15; + + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 3; + p.vel[j] = crand() * 3; + p.accel[j] = 0; + } + + VectorAdd(move, vec, move); + } + } + + // RAFAEL + /* + =============== + CL_IonripperTrail + =============== + */ + static void IonripperTrail(float[] start, float[] ent) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + int left = 0; + + VectorCopy(start, move); + VectorSubtract(ent, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + p.alpha = 0.5f; + p.alphavel = -1.0f / (0.3f + frand() * 0.2f); + p.color = 0xe4 + (rand() & 3); + + for (j = 0; j < 3; j++) { + p.org[j] = move[j]; + p.accel[j] = 0; + } + if (left != 0) { + left = 0; + p.vel[0] = 10; + } else { + left = 1; + p.vel[0] = -10; + } + + p.vel[1] = 0; + p.vel[2] = 0; + + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_BubbleTrail + + =============== + */ + static void BubbleTrail(float[] start, float[] end) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int i, j; + cparticle_t p; + float dec; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 32; + VectorScale(vec, dec, vec); + + for (i = 0; i < len; i += dec) { + if (free_particles == null) + return; + + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + VectorClear(p.accel); + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1.0f + frand() * 0.2f); + p.color = 4 + (rand() & 7); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 2; + p.vel[j] = crand() * 5; + } + p.vel[2] += 6; + + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_FlyParticles + =============== + */ + private static final int BEAMLENGTH = 16; + static void FlyParticles (float [] origin, int count) + { + int i; + cparticle_t p; + float angle; + float sr, sp, sy, cr, cp, cy; + float [] forward= new float[3]; + float dist = 64; + float ltime; + + + if (count > NUMVERTEXNORMALS) + count = NUMVERTEXNORMALS; + + if (avelocities[0][0] == 0.0f) + { + for (i=0 ; i<NUMVERTEXNORMALS ; i++) { + avelocities[i][0] = (rand()&255) * 0.01f; + avelocities[i][1] = (rand()&255) * 0.01f; + avelocities[i][2] = (rand()&255) * 0.01f; + } + } + + + ltime = cl.time / 1000.0f; + for (i=0 ; i<count ; i+=2) + { + angle = ltime * avelocities[i][0]; + sy = (float)Math.sin(angle); + cy = (float)Math.cos(angle); + angle = ltime * avelocities[i][1]; + sp = (float)Math.sin(angle); + cp = (float)Math.cos(angle); + angle = ltime * avelocities[i][2]; + sr = (float)Math.sin(angle); + cr = (float)Math.cos(angle); + + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + + dist = (float)Math.sin(ltime + i)*64; + p.org[0] = origin[0] + bytedirs[i][0]*dist + forward[0]*BEAMLENGTH; + p.org[1] = origin[1] + bytedirs[i][1]*dist + forward[1]*BEAMLENGTH; + p.org[2] = origin[2] + bytedirs[i][2]*dist + forward[2]*BEAMLENGTH; + + VectorClear (p.vel); + VectorClear (p.accel); + + p.color = 0; + //p.colorvel = 0; + + p.alpha = 1; + p.alphavel = -100; + } + } + + static void FlyEffect(centity_t ent, float[] origin) { + int n; + int count; + int starttime; + + if (ent.fly_stoptime < cl.time) { + starttime = cl.time; + ent.fly_stoptime = cl.time + 60000; + } else { + starttime = ent.fly_stoptime - 60000; + } + + n = cl.time - starttime; + if (n < 20000) + count = (int) ((n * 162) / 20000.0); + else { + n = ent.fly_stoptime - cl.time; + if (n < 20000) + count = (int) ((n * 162) / 20000.0); + else + count = 162; + } + + CL.FlyParticles(origin, count); + } + + /* + =============== + CL_BfgParticles + =============== + */ + //#define BEAMLENGTH 16 + static void BfgParticles(entity_t ent) { + int i; + cparticle_t p; + float angle; + float sr, sp, sy, cr, cp, cy; + float[] forward = new float[3]; + float dist = 64; + float[] v = new float[3]; + float ltime; + + if (avelocities[0][0] == 0.0f) { + for (i = 0; i < NUMVERTEXNORMALS; i++) { + avelocities[i][0] = (rand() & 255) * 0.01f; + avelocities[i][1] = (rand() & 255) * 0.01f; + avelocities[i][2] = (rand() & 255) * 0.01f; + } + } + + ltime = cl.time / 1000.0f; + for (i = 0; i < NUMVERTEXNORMALS; i++) { + angle = ltime * avelocities[i][0]; + sy = (float)Math.sin(angle); + cy = (float)Math.cos(angle); + angle = ltime * avelocities[i][1]; + sp = (float)Math.sin(angle); + cp = (float)Math.cos(angle); + angle = ltime * avelocities[i][2]; + sr = (float)Math.sin(angle); + cr = (float)Math.cos(angle); + + forward[0] = cp * cy; + forward[1] = cp * sy; + forward[2] = -sp; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + + dist = (float) (Math.sin(ltime + i) * 64); + p.org[0] = ent.origin[0] + bytedirs[i][0] * dist + forward[0] * BEAMLENGTH; + p.org[1] = ent.origin[1] + bytedirs[i][1] * dist + forward[1] * BEAMLENGTH; + p.org[2] = ent.origin[2] + bytedirs[i][2] * dist + forward[2] * BEAMLENGTH; + + VectorClear(p.vel); + VectorClear(p.accel); + + VectorSubtract(p.org, ent.origin, v); + dist = VectorLength(v) / 90.0f; + p.color = (float)Math.floor(0xd0 + dist * 7); + //p.colorvel = 0; + + p.alpha = 1.0f - dist; + p.alphavel = -100; + } + } + + /* + =============== + CL_TrapParticles + =============== + */ + // RAFAEL + static void TrapParticles(entity_t ent) { + float[] move = new float[3]; + float[] vec = new float[3]; + float[] start = new float[3]; + float[] end = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + + ent.origin[2] -= 14; + VectorCopy(ent.origin, start); + VectorCopy(ent.origin, end); + end[2] += 64; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + // FIXME: this is a really silly way to have a loop + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.3f + frand() * 0.2f); + p.color = 0xe0; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand(); + p.vel[j] = crand() * 15; + p.accel[j] = 0; + } + p.accel[2] = PARTICLE_GRAVITY; + + VectorAdd(move, vec, move); + } + + int i, k; + //cparticle_t p; + float vel; + float[] dir = new float[3]; + float[] org = new float[3]; + + ent.origin[2] += 14; + VectorCopy(ent.origin, org); + + for (i = -2; i <= 2; i += 4) + for (j = -2; j <= 2; j += 4) + for (k = -2; k <= 4; k += 4) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = 0xe0 + (rand() & 3); + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.3f + (rand() & 7) * 0.02f); + + p.org[0] = org[0] + i + ((rand() & 23) * crand()); + p.org[1] = org[1] + j + ((rand() & 23) * crand()); + p.org[2] = org[2] + k + ((rand() & 23) * crand()); + + dir[0] = j * 8; + dir[1] = i * 8; + dir[2] = k * 8; + + VectorNormalize(dir); + vel = 50 + rand() & 63; + VectorScale(dir, vel, p.vel); + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + } + + } + + + /* + =============== + CL_BFGExplosionParticles + =============== + */ + // FIXME combined with CL_ExplosionParticles + static void BFGExplosionParticles(float[] org) { + int i, j; + cparticle_t p; + + for (i = 0; i < 256; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = 0xd0 + (rand() & 7); + + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() % 32) - 16); + p.vel[j] = (rand() % 384) - 192; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -0.8f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_TeleportParticles + + =============== + */ + static void TeleportParticles(float[] org) { + int i, j, k; + cparticle_t p; + float vel; + float[] dir = new float[3]; + + for (i = -16; i <= 16; i += 4) + for (j = -16; j <= 16; j += 4) + for (k = -16; k <= 32; k += 4) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = 7 + (rand() & 7); + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.3f + (rand() & 7) * 0.02f); + + p.org[0] = org[0] + i + (rand() & 3); + p.org[1] = org[1] + j + (rand() & 3); + p.org[2] = org[2] + k + (rand() & 3); + + dir[0] = j * 8; + dir[1] = i * 8; + dir[2] = k * 8; + + VectorNormalize(dir); + vel = 50 + (rand() & 63); + VectorScale(dir, vel, p.vel); + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + } + } + + /* + =============== + CL_AddParticles + =============== + */ + static void AddParticles() { + cparticle_t p, next; + float alpha; + float time = 0.0f; + float time2; + float[] org = new float[3]; + int color; + cparticle_t active, tail; + + active = null; + tail = null; + + for (p = active_particles; p != null; p = next) { + next = p.next; + + // PMM - added INSTANT_PARTICLE handling for heat beam + if (p.alphavel != INSTANT_PARTICLE) { + time = (cl.time - p.time) * 0.001f; + alpha = p.alpha + time * p.alphavel; + if (alpha <= 0) { // faded out + p.next = free_particles; + free_particles = p; + continue; + } + } else { + alpha = p.alpha; + } + + p.next = null; + if (tail == null) + active = tail = p; + else { + tail.next = p; + tail = p; + } + + if (alpha > 1.0) + alpha = 1; + color = (int)p.color; + + time2 = time * time; + + org[0] = p.org[0] + p.vel[0] * time + p.accel[0] * time2; + org[1] = p.org[1] + p.vel[1] * time + p.accel[1] * time2; + org[2] = p.org[2] + p.vel[2] * time + p.accel[2] * time2; + + V.AddParticle(org, color, alpha); + // PMM + if (p.alphavel == INSTANT_PARTICLE) { + p.alphavel = 0.0f; + p.alpha = 0.0f; + } + } + + active_particles = active; + } + + /* + ============== + CL_EntityEvent + + An entity has just been parsed that has an event value + + the female events are there for backwards compatability + ============== + */ + static void EntityEvent(entity_state_t ent) { + switch (ent.event) { + case EV_ITEM_RESPAWN : + S.StartSound(null, ent.number, CHAN_WEAPON, S.RegisterSound("items/respawn1.wav"), 1, ATTN_IDLE, 0); + CL.ItemRespawnParticles(ent.origin); + break; + case EV_PLAYER_TELEPORT : + S.StartSound(null, ent.number, CHAN_WEAPON, S.RegisterSound("misc/tele1.wav"), 1, ATTN_IDLE, 0); + CL.TeleportParticles(ent.origin); + break; + case EV_FOOTSTEP : + if (cl_footsteps.value != 0.0f) + S.StartSound(null, ent.number, CHAN_BODY, cl_sfx_footsteps[rand() & 3], 1, ATTN_NORM, 0); + break; + case EV_FALLSHORT : + S.StartSound(null, ent.number, CHAN_AUTO, S.RegisterSound("player/land1.wav"), 1, ATTN_NORM, 0); + break; + case EV_FALL : + S.StartSound(null, ent.number, CHAN_AUTO, S.RegisterSound("*fall2.wav"), 1, ATTN_NORM, 0); + break; + case EV_FALLFAR : + S.StartSound(null, ent.number, CHAN_AUTO, S.RegisterSound("*fall1.wav"), 1, ATTN_NORM, 0); + break; + } + } + + /* + ============== + CL_ClearEffects + + ============== + */ + static void ClearEffects() { + CL.ClearParticles(); + CL.ClearDlights(); + CL.ClearLightStyles(); + } + +} diff --git a/src/jake2/client/CL_input.java b/src/jake2/client/CL_input.java new file mode 100644 index 0000000..1b6233c --- /dev/null +++ b/src/jake2/client/CL_input.java @@ -0,0 +1,564 @@ +/* + * CL_input.java + * Copyright (C) 2004 + * + * $Id: CL_input.java,v 1.1 2004-07-07 19:58:37 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.game.*; +import jake2.game.Cmd; +import jake2.game.usercmd_t; +import jake2.qcommon.*; +import jake2.qcommon.Cvar; +import jake2.qcommon.xcommand_t; +import jake2.sys.IN; + +/** + * CL_input + */ +public class CL_input extends CL_ents { + + static long frame_msec; + static long old_sys_frame_time; + + static cvar_t cl_nodelta; + + /* + =============================================================================== + + KEY BUTTONS + + Continuous button event tracking is complicated by the fact that two different + input sources (say, mouse button 1 and the control key) can both press the + same button, but the button should only be released when both of the + pressing key have been released. + + When a key event issues a button command (+forward, +attack, etc), it appends + its key number as a parameter to the command so it can be matched up with + the release. + + state bit 0 is the current state of the key + state bit 1 is edge triggered on the up to down transition + state bit 2 is edge triggered on the down to up transition + + + Key_Event (int key, qboolean down, unsigned time); + + +mlook src time + + =============================================================================== + */ + + static kbutton_t in_klook = new kbutton_t(); + static kbutton_t in_left = new kbutton_t(); + static kbutton_t in_right = new kbutton_t(); + static kbutton_t in_forward = new kbutton_t(); + static kbutton_t in_back = new kbutton_t(); + static kbutton_t in_lookup = new kbutton_t(); + static kbutton_t in_lookdown = new kbutton_t(); + static kbutton_t in_moveleft = new kbutton_t(); + static kbutton_t in_moveright = new kbutton_t(); + public static kbutton_t in_strafe = new kbutton_t(); + static kbutton_t in_speed = new kbutton_t(); + static kbutton_t in_use = new kbutton_t(); + static kbutton_t in_attack = new kbutton_t(); + static kbutton_t in_up = new kbutton_t(); + static kbutton_t in_down = new kbutton_t(); + + static int in_impulse; + + static void KeyDown(kbutton_t b) { + int k; + String c; + + c = Cmd.Argv(1); + if (c.length() > 0) + k = Integer.parseInt(c); + else + k = -1; // typed manually at the console for continuous down + + if (k == b.down[0] || k == b.down[1]) + return; // repeating key + + if (b.down[0] == 0) + b.down[0] = k; + else if (b.down[1] == 0) + b.down[1] = k; + else { + Com.Printf("Three keys down for a button!\n"); + return; + } + + if ((b.state & 1) != 0) + return; // still down + + // save timestamp + c = Cmd.Argv(2); + b.downtime = Long.parseLong(c); + if (b.downtime == 0) + b.downtime = sys_frame_time - 100; + + b.state |= 3; // down + impulse down + } + + static void KeyUp(kbutton_t b) { + int k; + String c; + long uptime; + + c = Cmd.Argv(1); + if (c.length() > 0) + k = Integer.parseInt(c); + else { + // typed manually at the console, assume for unsticking, so clear all + b.down[0] = b.down[1] = 0; + b.state = 4; // impulse up + return; + } + + if (b.down[0] == k) + b.down[0] = 0; + else if (b.down[1] == k) + b.down[1] = 0; + else + return; // key up without coresponding down (menu pass through) + if (b.down[0] != 0 || b.down[1] != 0) + return; // some other key is still holding it down + + if ((b.state & 1) == 0) + return; // still up (this should not happen) + + // save timestamp + c = Cmd.Argv(2); + uptime = Long.parseLong(c); + if (uptime != 0) + b.msec += uptime - b.downtime; + else + b.msec += 10; + + b.state &= ~1; // now up + b.state |= 4; // impulse up + } + + static void IN_KLookDown() {KeyDown(in_klook);} + static void IN_KLookUp() {KeyUp(in_klook);} + static void IN_UpDown() {KeyDown(in_up);} + static void IN_UpUp() {KeyUp(in_up);} + static void IN_DownDown() {KeyDown(in_down);} + static void IN_DownUp() {KeyUp(in_down);} + static void IN_LeftDown() {KeyDown(in_left);} + static void IN_LeftUp() {KeyUp(in_left);} + static void IN_RightDown() {KeyDown(in_right);} + static void IN_RightUp() {KeyUp(in_right);} + static void IN_ForwardDown() {KeyDown(in_forward);} + static void IN_ForwardUp() {KeyUp(in_forward);} + static void IN_BackDown() {KeyDown(in_back);} + static void IN_BackUp() {KeyUp(in_back);} + static void IN_LookupDown() {KeyDown(in_lookup);} + static void IN_LookupUp() {KeyUp(in_lookup);} + static void IN_LookdownDown() {KeyDown(in_lookdown);} + static void IN_LookdownUp() {KeyUp(in_lookdown);} + static void IN_MoveleftDown() {KeyDown(in_moveleft);} + static void IN_MoveleftUp() {KeyUp(in_moveleft);} + static void IN_MoverightDown() {KeyDown(in_moveright);} + static void IN_MoverightUp() {KeyUp(in_moveright);} + + static void IN_SpeedDown() {KeyDown(in_speed);} + static void IN_SpeedUp() {KeyUp(in_speed);} + static void IN_StrafeDown() {KeyDown(in_strafe);} + static void IN_StrafeUp() {KeyUp(in_strafe);} + + static void IN_AttackDown() {KeyDown(in_attack);} + static void IN_AttackUp() {KeyUp(in_attack);} + + static void IN_UseDown () {KeyDown(in_use);} + static void IN_UseUp () {KeyUp(in_use);} + + static void IN_Impulse () {in_impulse=Integer.parseInt(Cmd.Argv(1));} + + /* + =============== + CL_KeyState + + Returns the fraction of the frame that the key was down + =============== + */ + static float KeyState(kbutton_t key) { + float val; + long msec; + + key.state &= 1; // clear impulses + + msec = key.msec; + key.msec = 0; + + if (key.state != 0) { + // still down + msec += sys_frame_time - key.downtime; + key.downtime = sys_frame_time; + } + + val = (float)msec / frame_msec; + if (val < 0) + val = 0; + if (val > 1) + val = 1; + + return val; + } + +// ========================================================================== + + /* + ================ + CL_AdjustAngles + + Moves the local angle positions + ================ + */ + static void AdjustAngles() { + float speed; + float up, down; + + if ((in_speed.state & 1) != 0) + speed = cls.frametime * cl_anglespeedkey.value; + else + speed = cls.frametime; + + if ((in_strafe.state & 1) == 0) { + cl.viewangles[YAW] -= speed * cl_yawspeed.value * CL.KeyState(in_right); + cl.viewangles[YAW] += speed * cl_yawspeed.value * CL.KeyState(in_left); + } + if ((in_klook.state & 1) != 0) { + cl.viewangles[PITCH] -= speed * cl_pitchspeed.value * CL.KeyState(in_forward); + cl.viewangles[PITCH] += speed * cl_pitchspeed.value * CL.KeyState(in_back); + } + + up = CL.KeyState(in_lookup); + down = CL.KeyState(in_lookdown); + + cl.viewangles[PITCH] -= speed * cl_pitchspeed.value * up; + cl.viewangles[PITCH] += speed * cl_pitchspeed.value * down; + } + + /* + ================ + CL_BaseMove + + Send the intended movement message to the server + ================ + */ + static void BaseMove(usercmd_t cmd) { + CL.AdjustAngles(); + + //memset (cmd, 0, sizeof(*cmd)); + cmd.reset(); + + VectorCopy(cl.viewangles, cmd.angles); + if ((in_strafe.state & 1) != 0) { + cmd.sidemove += cl_sidespeed.value * CL.KeyState(in_right); + cmd.sidemove -= cl_sidespeed.value * CL.KeyState(in_left); + } + + cmd.sidemove += cl_sidespeed.value * CL.KeyState(in_moveright); + cmd.sidemove -= cl_sidespeed.value * CL.KeyState(in_moveleft); + + cmd.upmove += cl_upspeed.value * CL.KeyState(in_up); + cmd.upmove -= cl_upspeed.value * CL.KeyState(in_down); + + if ((in_klook.state & 1) == 0) { + cmd.forwardmove += cl_forwardspeed.value * CL.KeyState(in_forward); + cmd.forwardmove -= cl_forwardspeed.value * CL.KeyState(in_back); + } + + // + // adjust for speed key / running + // + if (((in_speed.state & 1) ^ (int) (cl_run.value)) != 0) { + cmd.forwardmove *= 2; + cmd.sidemove *= 2; + cmd.upmove *= 2; + } + + } + + static void ClampPitch() { + + float pitch; + + pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]); + if (pitch > 180) + pitch -= 360; + + if (cl.viewangles[PITCH] + pitch < -360) + cl.viewangles[PITCH] += 360; // wrapped + if (cl.viewangles[PITCH] + pitch > 360) + cl.viewangles[PITCH] -= 360; // wrapped + + if (cl.viewangles[PITCH] + pitch > 89) + cl.viewangles[PITCH] = 89 - pitch; + if (cl.viewangles[PITCH] + pitch < -89) + cl.viewangles[PITCH] = -89 - pitch; + } + + /* + ============== + CL_FinishMove + ============== + */ + static void FinishMove(usercmd_t cmd) { + int ms; + int i; + + // + // figure button bits + // + if ((in_attack.state & 3) != 0) + cmd.buttons |= BUTTON_ATTACK; + in_attack.state &= ~2; + + if ((in_use.state & 3) != 0) + cmd.buttons |= BUTTON_USE; + in_use.state &= ~2; + + if (anykeydown != 0 && cls.key_dest == key_game) + cmd.buttons |= BUTTON_ANY; + + // send milliseconds of time to apply the move + ms = (int)(cls.frametime * 1000); + if (ms > 250) + ms = 100; // time was unreasonable + cmd.msec = (byte)ms; + + CL.ClampPitch(); + for (i = 0; i < 3; i++) + cmd.angles[i] = (short)ANGLE2SHORT(cl.viewangles[i]); + + cmd.impulse = (byte)in_impulse; + in_impulse = 0; + + // send the ambient light level at the player's current position + cmd.lightlevel = (byte)cl_lightlevel.value; + } + + /* + ================= + CL_CreateCmd + ================= + */ + static usercmd_t CreateCmd() { + usercmd_t cmd = new usercmd_t(); + + frame_msec = sys_frame_time - old_sys_frame_time; + if (frame_msec < 1) + frame_msec = 1; + if (frame_msec > 200) + frame_msec = 200; + + // get basic movement from keyboard + CL.BaseMove(cmd); + + // allow mice or other external controllers to add to the move + IN.Move(cmd); + + CL.FinishMove(cmd); + + old_sys_frame_time = sys_frame_time; + + return cmd; + } + + /* + ============ + CL_InitInput + ============ + */ + static void InitInput() { + Cmd.AddCommand("centerview", new xcommand_t() { + public void execute() {IN.CenterView();}}); + + Cmd.AddCommand("+moveup", new xcommand_t() { + public void execute() {IN_UpDown();}}); + Cmd.AddCommand("-moveup", new xcommand_t() { + public void execute() {IN_UpUp();}}); + Cmd.AddCommand("+movedown", new xcommand_t() { + public void execute() {IN_DownDown();}}); + Cmd.AddCommand("-movedown", new xcommand_t() { + public void execute() {IN_DownUp();}}); + Cmd.AddCommand("+left", new xcommand_t() { + public void execute() {IN_LeftDown();}}); + Cmd.AddCommand("-left", new xcommand_t() { + public void execute() {IN_LeftUp();}}); + Cmd.AddCommand("+right", new xcommand_t() { + public void execute() {IN_RightDown();}}); + Cmd.AddCommand("-right", new xcommand_t() { + public void execute() {IN_RightUp();}}); + Cmd.AddCommand("+forward", new xcommand_t() { + public void execute() {IN_ForwardDown();}}); + Cmd.AddCommand("-forward", new xcommand_t() { + public void execute() {IN_ForwardUp();}}); + Cmd.AddCommand("+back", new xcommand_t() { + public void execute() {IN_BackDown();}}); + Cmd.AddCommand("-back", new xcommand_t() { + public void execute() {IN_BackUp();}}); + Cmd.AddCommand("+lookup", new xcommand_t() { + public void execute() {IN_LookupDown();}}); + Cmd.AddCommand("-lookup", new xcommand_t() { + public void execute() {IN_LookupUp();}}); + Cmd.AddCommand("+lookdown", new xcommand_t() { + public void execute() {IN_LookdownDown();}}); + Cmd.AddCommand("-lookdown", new xcommand_t() { + public void execute() {IN_LookdownUp();}}); + Cmd.AddCommand("+strafe", new xcommand_t() { + public void execute() {IN_StrafeDown();}}); + Cmd.AddCommand("-strafe", new xcommand_t() { + public void execute() {IN_StrafeUp();}}); + Cmd.AddCommand("+moveleft", new xcommand_t() { + public void execute() {IN_MoveleftDown();}}); + Cmd.AddCommand("-moveleft", new xcommand_t() { + public void execute() {IN_MoveleftUp();}}); + Cmd.AddCommand("+moveright", new xcommand_t() { + public void execute() {IN_MoverightDown();}}); + Cmd.AddCommand("-moveright", new xcommand_t() { + public void execute() {IN_MoverightUp();}}); + Cmd.AddCommand("+speed", new xcommand_t() { + public void execute() {IN_SpeedDown();}}); + Cmd.AddCommand("-speed", new xcommand_t() { + public void execute() {IN_SpeedUp();}}); + Cmd.AddCommand("+attack", new xcommand_t() { + public void execute() {IN_AttackDown();}}); + Cmd.AddCommand("-attack", new xcommand_t() { + public void execute() {IN_AttackUp();}}); + Cmd.AddCommand("+use", new xcommand_t() { + public void execute() {IN_UseDown();}}); + Cmd.AddCommand("-use", new xcommand_t() { + public void execute() {IN_UseUp();}}); + Cmd.AddCommand("impulse", new xcommand_t() { + public void execute() {IN_Impulse();}}); + Cmd.AddCommand("+klook", new xcommand_t() { + public void execute() {IN_KLookDown();}}); + Cmd.AddCommand("-klook", new xcommand_t() { + public void execute() {IN_KLookUp();}}); + + // TODO nodelta + cl_nodelta = Cvar.Get("cl_nodelta", "1",0); + } + + /* + ================= + CL_SendCmd + ================= + */ + static void SendCmd() { + sizebuf_t buf = new sizebuf_t(); + byte[] data = new byte[128]; + int i; + usercmd_t cmd, oldcmd; + usercmd_t nullcmd = new usercmd_t(); + int checksumIndex; + + // build a command even if not connected + + // save this command off for prediction + i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1); + cmd = cl.cmds[i]; + cl.cmd_time[i] = (int)cls.realtime; // for netgraph ping calculation + + cmd.set(CL.CreateCmd()); + + cl.cmd.set(cmd); + + if (cls.state == ca_disconnected || cls.state == ca_connecting) + return; + + if (cls.state == ca_connected) { + if (cls.netchan.message.cursize != 0 || curtime - cls.netchan.last_sent > 1000) + Netchan.Transmit(cls.netchan, 0, new byte[0]); + return; + } + + // send a userinfo update if needed + if (userinfo_modified) { + CL.FixUpGender(); + userinfo_modified = false; + MSG.WriteByte(cls.netchan.message, clc_userinfo); + MSG.WriteString(cls.netchan.message, Cvar.Userinfo()); + } + + SZ.Init(buf, data, data.length); + + if (cmd.buttons != 0 + && cl.cinematictime > 0 + && !cl.attractloop + && cls.realtime - cl.cinematictime > 1000) { // skip the rest of the cinematic + SCR.FinishCinematic(); + } + + // begin a client move command + MSG.WriteByte(buf, clc_move); + + // save the position for a checksum byte + checksumIndex = buf.cursize; + MSG.WriteByte(buf, 0); + + // let the server know what the last frame we + // got was, so the next message can be delta compressed + if (cl_nodelta.value != 0.0f || !cl.frame.valid || cls.demowaiting) + MSG.WriteLong(buf, -1); // no compression + else + MSG.WriteLong(buf, cl.frame.serverframe); + + // send this and the previous cmds in the message, so + // if the last packet was dropped, it can be recovered + i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1); + cmd = cl.cmds[i]; + //memset (nullcmd, 0, sizeof(nullcmd)); + nullcmd.reset(); + + MSG.WriteDeltaUsercmd(buf, nullcmd, cmd); + oldcmd = cmd; + + i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1); + cmd = cl.cmds[i]; + + MSG.WriteDeltaUsercmd(buf, oldcmd, cmd); + oldcmd = cmd; + + i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1); + cmd = cl.cmds[i]; + + MSG.WriteDeltaUsercmd(buf, oldcmd, cmd); + + // calculate a checksum over the move commands + buf.data[checksumIndex] = 0; + /*COM_BlockSequenceCRCByte( + buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, + cls.netchan.outgoing_sequence);*/ + + // + // deliver the message + // + Netchan.Transmit(cls.netchan, buf.cursize, buf.data); + } + +} diff --git a/src/jake2/client/CL_inv.java b/src/jake2/client/CL_inv.java new file mode 100644 index 0000000..ba219c7 --- /dev/null +++ b/src/jake2/client/CL_inv.java @@ -0,0 +1,152 @@ +/* + * CL_fx.java + * Copyright (C) 2004 + * + * $Id: CL_inv.java,v 1.1 2004-07-07 19:58:37 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. + +*/ + +// Created on 31.01.2004 by RST. + +package jake2.client; + +import jake2.qcommon.Com; +import jake2.qcommon.MSG; +import jake2.util.Vargs; + +/** + * CL_inv + */ +public class CL_inv extends CL_newfx { + + /* + ================ + CL_ParseInventory + ================ + */ + static void ParseInventory() { + int i; + + for (i = 0; i < MAX_ITEMS; i++) + cl.inventory[i] = MSG.ReadShort(net_message); + } + + /* + ================ + Inv_DrawString + ================ + */ + static void Inv_DrawString(int x, int y, String string) { + for (int i = 0; i < string.length(); i++) { + re.DrawChar(x, y, string.charAt(i)); + x += 8; + } + } + + static void SetStringHighBit(String s) { + byte[] b = s.getBytes(); + for (int i = 0; i < b.length; i++) { + b[i] = (byte) (b[i] | 128); + } + s = new String(b); + } + + /* + ================ + CL_DrawInventory + ================ + */ + static final int DISPLAY_ITEMS = 17; + + static void DrawInventory() { + int i, j; + int num, selected_num, item; + int[] index = new int[MAX_ITEMS]; + String string; + int x, y; + String binding; + String bind; + int selected; + int top; + + selected = cl.frame.playerstate.stats[STAT_SELECTED_ITEM]; + + num = 0; + selected_num = 0; + for (i = 0; i < MAX_ITEMS; i++) { + if (i == selected) + selected_num = num; + if (cl.inventory[i] != 0) { + index[num] = i; + num++; + } + } + + // determine scroll point + top = selected_num - DISPLAY_ITEMS / 2; + if (num - top < DISPLAY_ITEMS) + top = num - DISPLAY_ITEMS; + if (top < 0) + top = 0; + + x = (viddef.width - 256) / 2; + y = (viddef.height - 240) / 2; + + // repaint everything next frame + SCR.DirtyScreen(); + + re.DrawPic(x, y + 8, "inventory"); + + y += 24; + x += 24; + Inv_DrawString(x, y, "hotkey ### item"); + Inv_DrawString(x, y + 8, "------ --- ----"); + y += 16; + for (i = top; i < num && i < top + DISPLAY_ITEMS; i++) { + item = index[i]; + // search for a binding + //Com_sprintf (binding, sizeof(binding), "use %s", cl.configstrings[CS_ITEMS+item]); + binding = "use " + cl.configstrings[CS_ITEMS + item]; + bind = ""; + for (j = 0; j < 256; j++) + if (keybindings[j] != null && keybindings[j].equals(binding)) { + bind = Key.KeynumToString(j); + break; + } + + string = + Com.sprintf( + "%6s %3i %s", + new Vargs(3).add(bind).add(cl.inventory[item]).add(cl.configstrings[CS_ITEMS + item])); + if (item != selected) + SetStringHighBit(string); + else // draw a blinky cursor by the selected item + { + if (((int) (cls.realtime * 10) & 1) != 0) + re.DrawChar(x - 8, y, 15); + } + Inv_DrawString(x, y, string); + y += 8; + } + + } + +} diff --git a/src/jake2/client/CL_newfx.java b/src/jake2/client/CL_newfx.java new file mode 100644 index 0000000..bfa1c71 --- /dev/null +++ b/src/jake2/client/CL_newfx.java @@ -0,0 +1,1027 @@ +/* + * CL_newfx.java + * Copyright (C) 2004 + * + * $Id: CL_newfx.java,v 1.1 2004-07-07 19:58:38 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. + +*/ + +// Created on 31.01.2004 by RST. + +package jake2.client; + + +/** + * CL_newfx + */ +public class CL_newfx extends CL_fx { + + + + static void Flashlight(int ent, float[] pos) { + cdlight_t dl; + + dl = CL.AllocDlight(ent); + VectorCopy(pos, dl.origin); + dl.radius = 400; + dl.minlight = 250; + dl.die = cl.time + 100; + dl.color[0] = 1; + dl.color[1] = 1; + dl.color[2] = 1; + } + + /* + ====== + CL_ColorFlash - flash of light + ====== + */ + static void ColorFlash(float[] pos, int ent, int intensity, float r, float g, float b) { + cdlight_t dl; + + if ((vidref_val == VIDREF_SOFT) && ((r < 0) || (g < 0) || (b < 0))) { + intensity = -intensity; + r = -r; + g = -g; + b = -b; + } + + dl = CL.AllocDlight(ent); + VectorCopy(pos, dl.origin); + dl.radius = intensity; + dl.minlight = 250; + dl.die = cl.time + 100; + dl.color[0] = r; + dl.color[1] = g; + dl.color[2] = b; + } + + /* + ====== + CL_DebugTrail + ====== + */ + static void DebugTrail(float[] start, float[] end) { + float[] move = new float[3]; + float[] vec = new float[3]; + + float len; + // int j; + cparticle_t p; + float dec; + float[] right = new float[3]; + float[] up = new float[3]; + // int i; + // float d, c, s; + // float[] dir; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + MakeNormalVectors(vec, right, up); + + // VectorScale(vec, RT2_SKIP, vec); + + // dec = 1.0; + // dec = 0.75; + dec = 3; + VectorScale(vec, dec, vec); + VectorCopy(start, move); + + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + VectorClear(p.accel); + VectorClear(p.vel); + p.alpha = 1.0f; + p.alphavel = -0.1f; + // p.alphavel = 0; + p.color = 0x74 + (rand() & 7); + VectorCopy(move, p.org); + /* + for (j=0 ; j<3 ; j++) + { + p.org[j] = move[j] + crand()*2; + p.vel[j] = crand()*3; + p.accel[j] = 0; + } + */ + VectorAdd(move, vec, move); + } + + } + + /* + =============== + CL_SmokeTrail + =============== + */ + static void SmokeTrail(float[] start, float[] end, int colorStart, int colorRun, int spacing) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + VectorScale(vec, spacing, vec); + + // FIXME: this is a really silly way to have a loop + while (len > 0) { + len -= spacing; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1 + frand() * 0.5f); + p.color = colorStart + (rand() % colorRun); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 3; + p.accel[j] = 0; + } + p.vel[2] = 20 + crand() * 5; + + VectorAdd(move, vec, move); + } + } + + static void ForceWall(float[] start, float[] end, int color) { + float[] move = new float[3]; + float[] vec = new float[3]; + ; + float len; + int j; + cparticle_t p; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + VectorScale(vec, 4, vec); + + // FIXME: this is a really silly way to have a loop + while (len > 0) { + len -= 4; + + if (free_particles == null) + return; + + if (frand() > 0.3) { + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (3.0f + frand() * 0.5f); + p.color = color; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 3; + p.accel[j] = 0; + } + p.vel[0] = 0; + p.vel[1] = 0; + p.vel[2] = -40 - (crand() * 10); + } + + VectorAdd(move, vec, move); + } + } + + static void FlameEffects(centity_t ent, float[] origin) { + int n, count; + int j; + cparticle_t p; + + count = rand() & 0xF; + + for (n = 0; n < count; n++) { + if (free_particles == null) + return; + + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + VectorClear(p.accel); + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1 + frand() * 0.2f); + p.color = 226 + (rand() % 4); + for (j = 0; j < 3; j++) { + p.org[j] = origin[j] + crand() * 5; + p.vel[j] = crand() * 5; + } + p.vel[2] = crand() * -10; + p.accel[2] = -PARTICLE_GRAVITY; + } + + count = rand() & 0x7; + + for (n = 0; n < count; n++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1 + frand() * 0.5f); + p.color = 0 + (rand() % 4); + for (j = 0; j < 3; j++) { + p.org[j] = origin[j] + crand() * 3; + } + p.vel[2] = 20 + crand() * 5; + } + + } + + + /* + =============== + CL_GenericParticleEffect + =============== + */ + static void GenericParticleEffect(float[] org, float[] dir, int color, int count, int numcolors, int dirspread, float alphavel) { + int i, j; + cparticle_t p; + float d; + + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + if (numcolors > 1) + p.color = color + (rand() & numcolors); + else + p.color = color; + + d = rand() & dirspread; + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() & 7) - 4) + d * dir[j]; + p.vel[j] = crand() * 20; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + // VectorCopy (accel, p.accel); + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * alphavel); + // p.alphavel = alphavel; + } + } + + /* + =============== + CL_BubbleTrail2 (lets you control the # of bubbles by setting the distance between the spawns) + + =============== + */ + static void BubbleTrail2 (float[] start, float[] end, int dist) + { + float[] move = new float[3]; + float[] vec = new float[3];; + float len; + int i, j; + cparticle_t p; + float dec; + + VectorCopy (start, move); + VectorSubtract (end, start, vec); + len = VectorNormalize (vec); + + dec = dist; + VectorScale (vec, dec, vec); + + for (i=0 ; i<len ; i+=dec) + { + if (free_particles == null) + return; + + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + VectorClear (p.accel); + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (1+frand()*0.1f); + p.color = 4 + (rand()&7); + for (j=0 ; j<3 ; j++) + { + p.org[j] = move[j] + crand()*2; + p.vel[j] = crand()*10; + } + p.org[2] -= 4; +// p.vel[2] += 6; + p.vel[2] += 20; + + VectorAdd (move, vec, move); + } + } + + static void Heatbeam(float[] start, float[] forward) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + float[] right = new float[3]; + float[] up = new float[3]; + int i; + float c, s; + float[] dir = new float[3]; + float ltime; + float step = 32.0f, rstep; + float start_pt; + float rot; + float variance; + float[] end = new float[3]; + + VectorMA(start, 4096, forward, end); + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + // FIXME - pmm - these might end up using old values? + // MakeNormalVectors (vec, right, up); + VectorCopy(cl.v_right, right); + VectorCopy(cl.v_up, up); + if (vidref_val == VIDREF_GL) { // GL mode + VectorMA(move, -0.5f, right, move); + VectorMA(move, -0.5f, up, move); + } + // otherwise assume SOFT + + ltime = (float)cl.time / 1000.0f; + start_pt = ltime * 96.0f % step; + VectorMA(move, start_pt, vec, move); + + VectorScale(vec, step, vec); + + // Com_Printf ("%f\n", ltime); + rstep = (float) (Math.PI / 10.0); + float M_PI2 = (float) (Math.PI * 2.0); + for (i = (int)start_pt; i < len; i += step) { + if (i > step * 5) // don't bother after the 5th ring + break; + + for (rot = 0; rot < M_PI2; rot += rstep) { + + if (free_particles == null) + return; + + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + VectorClear(p.accel); + // rot+= fmod(ltime, 12.0)*M_PI; + // c = cos(rot)/2.0; + // s = sin(rot)/2.0; + // variance = 0.4 + ((float)rand()/(float)RAND_MAX) *0.2; + variance = 0.5f; + c = (float) (Math.cos(rot) * variance); + s = (float) (Math.sin(rot) * variance); + + // trim it so it looks like it's starting at the origin + if (i < 10) { + VectorScale(right, c * (i / 10.0f), dir); + VectorMA(dir, s * (i / 10.0f), up, dir); + } else { + VectorScale(right, c, dir); + VectorMA(dir, s, up, dir); + } + + p.alpha = 0.5f; + // p.alphavel = -1.0 / (1+frand()*0.2); + p.alphavel = -1000.0f; + // p.color = 0x74 + (rand()&7); + p.color = 223 - (rand() & 7); + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + dir[j] * 3; + // p.vel[j] = dir[j]*6; + p.vel[j] = 0; + } + } + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_ParticleSteamEffect + + Puffs with velocity along direction, with some randomness thrown in + =============== + */ + static void ParticleSteamEffect(float[] org, float[] dir, int color, int count, int magnitude) { + int i, j; + cparticle_t p; + float d; + float[] r = new float[3]; + float[] u = new float[3]; + + // vectoangles2 (dir, angle_dir); + // AngleVectors (angle_dir, f, r, u); + + MakeNormalVectors(dir, r, u); + + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color + (rand() & 7); + + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + magnitude * 0.1f * crand(); + // p.vel[j] = dir[j]*magnitude; + } + VectorScale(dir, magnitude, p.vel); + d = crand() * magnitude / 3; + VectorMA(p.vel, d, r, p.vel); + d = crand() * magnitude / 3; + VectorMA(p.vel, d, u, p.vel); + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY / 2; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + } + + static void ParticleSteamEffect2(cl_sustain_t self) + // float[] org, float[] dir, int color, int count, int magnitude) + { + int i, j; + cparticle_t p; + float d; + float[] r = new float[3]; + float[] u = new float[3]; + float[] dir = new float[3]; + + // vectoangles2 (dir, angle_dir); + // AngleVectors (angle_dir, f, r, u); + + VectorCopy(self.dir, dir); + MakeNormalVectors(dir, r, u); + + for (i = 0; i < self.count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = self.color + (rand() & 7); + + for (j = 0; j < 3; j++) { + p.org[j] = self.org[j] + self.magnitude * 0.1f * crand(); + // p.vel[j] = dir[j]*magnitude; + } + VectorScale(dir, self.magnitude, p.vel); + d = crand() * self.magnitude / 3; + VectorMA(p.vel, d, r, p.vel); + d = crand() * self.magnitude / 3; + VectorMA(p.vel, d, u, p.vel); + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY / 2; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + self.nextthink += self.thinkinterval; + } + + /* + =============== + CL_TrackerTrail + =============== + */ + static void TrackerTrail(float[] start, float[] end, int particleColor) { + float[] move = new float[3]; + float[] vec = new float[3]; + float[] forward = new float[3]; + float[] right = new float[3]; + float[] up = new float[3]; + float[] angle_dir = new float[3]; + float len; + cparticle_t p; + int dec; + float dist; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + VectorCopy(vec, forward); + vectoangles(forward, angle_dir); + AngleVectors(angle_dir, forward, right, up); + + dec = 3; + VectorScale(vec, 3, vec); + + // FIXME: this is a really silly way to have a loop + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -2.0f; + p.color = particleColor; + dist = DotProduct(move, forward); + VectorMA(move, (float)(8 * Math.cos(dist)), up, p.org); + for (int j=0 ; j<3 ; j++) { + p.vel[j] = 0; + p.accel[j] = 0; + } + p.vel[2] = 5; + + VectorAdd (move, vec, move); + } + } + + static void Tracker_Shell(float[] origin) { + float[] dir = new float[3]; + int i; + cparticle_t p; + + for(i=0;i<300;i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = INSTANT_PARTICLE; + p.color = 0; + + dir[0] = crand(); + dir[1] = crand(); + dir[2] = crand(); + VectorNormalize(dir); + + VectorMA(origin, 40, dir, p.org); + } + } + + static void MonsterPlasma_Shell(float[] origin) { + float[] dir = new float[3]; + int i; + cparticle_t p; + + for (i = 0; i < 40; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = INSTANT_PARTICLE; + p.color = 0xe0; + + dir[0] = crand(); + dir[1] = crand(); + dir[2] = crand(); + VectorNormalize(dir); + + VectorMA(origin, 10, dir, p.org); + // VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))), dir, p.org); + } + } + + private static int[] wb_colortable = {2*8,13*8,21*8,18*8}; + static void Widowbeamout(cl_sustain_t self) { + float[] dir = new float[3]; + int i; + cparticle_t p; + + float ratio; + + ratio = 1.0f - (((float)self.endtime - (float)cl.time) / 2100.0f); + + for (i = 0; i < 300; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = INSTANT_PARTICLE; + p.color = wb_colortable[rand() & 3]; + + dir[0] = crand(); + dir[1] = crand(); + dir[2] = crand(); + VectorNormalize(dir); + + VectorMA(self.org, (45.0f * ratio), dir, p.org); + // VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))), dir, p.org); + } + } + + private static int[] nb_colortable = {110, 112, 114, 116}; + static void Nukeblast(cl_sustain_t self) { + float[] dir = new float[3]; + int i; + cparticle_t p; + + float ratio; + + ratio = 1.0f - (((float)self.endtime - (float)cl.time) / 1000.0f); + + for (i = 0; i < 700; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = INSTANT_PARTICLE; + p.color = nb_colortable[rand() & 3]; + + dir[0] = crand(); + dir[1] = crand(); + dir[2] = crand(); + VectorNormalize(dir); + + VectorMA(self.org, (200.0f * ratio), dir, p.org); + // VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))), dir, p.org); + } + } + + private static int[] ws_colortable = {2*8,13*8,21*8,18*8}; + static void WidowSplash(float[] org) { + int i; + cparticle_t p; + float[] dir = new float[3]; + + for (i = 0; i < 256; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = ws_colortable[rand() & 3]; + + dir[0] = crand(); + dir[1] = crand(); + dir[2] = crand(); + VectorNormalize(dir); + VectorMA(org, 45.0f, dir, p.org); + VectorMA(vec3_origin, 40.0f, dir, p.vel); + + p.accel[0] = p.accel[1] = 0; + p.alpha = 1.0f; + + p.alphavel = -0.8f / (0.5f + frand() * 0.3f); + } + + } + + static void Tracker_Explode(float[] origin) { + float[] dir = new float[3]; + float[] backdir = new float[3]; + int i; + cparticle_t p; + + for (i = 0; i < 300; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f; + p.color = 0; + + dir[0] = crand(); + dir[1] = crand(); + dir[2] = crand(); + VectorNormalize(dir); + VectorScale(dir, -1, backdir); + + VectorMA(origin, 64, dir, p.org); + VectorScale(backdir, 64, p.vel); + } + + } + + /* + =============== + CL_TagTrail + + =============== + */ + static void TagTrail(float[] start, float[] end, float color) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + while (len >= 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.8f + frand() * 0.2f); + p.color = color; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand() * 16; + p.vel[j] = crand() * 5; + p.accel[j] = 0; + } + + VectorAdd(move, vec, move); + } + } + + /* + =============== + CL_ColorExplosionParticles + =============== + */ + static void ColorExplosionParticles(float[] org, int color, int run) { + int i, j; + cparticle_t p; + + for (i = 0; i < 128; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color + (rand() % run); + + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() % 32) - 16); + p.vel[j] = (rand() % 256) - 128; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -0.4f / (0.6f + frand() * 0.2f); + } + } + + /* + =============== + CL_ParticleSmokeEffect - like the steam effect, but unaffected by gravity + =============== + */ + static void ParticleSmokeEffect (float[] org, float[] dir, int color, int count, int magnitude) + { + int i, j; + cparticle_t p; + float d; + float[] r = new float[3]; + float[] u = new float[3]; + + MakeNormalVectors (dir, r, u); + + for (i=0 ; i<count ; i++) + { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color + (rand()&7); + + for (j=0 ; j<3 ; j++) + { + p.org[j] = org[j] + magnitude*0.1f*crand(); +// p.vel[j] = dir[j]*magnitude; + } + VectorScale (dir, magnitude, p.vel); + d = crand()*magnitude/3; + VectorMA (p.vel, d, r, p.vel); + d = crand()*magnitude/3; + VectorMA (p.vel, d, u, p.vel); + + p.accel[0] = p.accel[1] = p.accel[2] = 0; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand()*0.3f); + } + } + + /* + =============== + CL_BlasterParticles2 + + Wall impact puffs (Green) + =============== + */ + static void BlasterParticles2(float[] org, float[] dir, long color) { + int i, j; + cparticle_t p; + float d; + int count; + + count = 40; + for (i = 0; i < count; i++) { + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + + p.time = cl.time; + p.color = color + (rand() & 7); + + d = rand() & 15; + for (j = 0; j < 3; j++) { + p.org[j] = org[j] + ((rand() & 7) - 4) + d * dir[j]; + p.vel[j] = dir[j] * 30 + crand() * 40; + } + + p.accel[0] = p.accel[1] = 0; + p.accel[2] = -PARTICLE_GRAVITY; + p.alpha = 1.0f; + + p.alphavel = -1.0f / (0.5f + frand() * 0.3f); + } + } + + /* + =============== + CL_BlasterTrail2 + + Green! + =============== + */ + static void BlasterTrail2(float[] start, float[] end) { + float[] move = new float[3]; + float[] vec = new float[3]; + float len; + int j; + cparticle_t p; + int dec; + + VectorCopy(start, move); + VectorSubtract(end, start, vec); + len = VectorNormalize(vec); + + dec = 5; + VectorScale(vec, 5, vec); + + // FIXME: this is a really silly way to have a loop + while (len > 0) { + len -= dec; + + if (free_particles == null) + return; + p = free_particles; + free_particles = p.next; + p.next = active_particles; + active_particles = p; + VectorClear(p.accel); + + p.time = cl.time; + + p.alpha = 1.0f; + p.alphavel = -1.0f / (0.3f + frand() * 0.2f); + p.color = 0xd0; + for (j = 0; j < 3; j++) { + p.org[j] = move[j] + crand(); + p.vel[j] = crand() * 5; + p.accel[j] = 0; + } + + VectorAdd(move, vec, move); + } + } + +} diff --git a/src/jake2/client/CL_parse.java b/src/jake2/client/CL_parse.java new file mode 100644 index 0000000..e972ea6 --- /dev/null +++ b/src/jake2/client/CL_parse.java @@ -0,0 +1,802 @@ +/* + * CL_parse.java + * Copyright (C) 2004 + * + * $Id: CL_parse.java,v 1.1 2004-07-07 19:58:38 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 java.io.IOException; +import java.io.RandomAccessFile; + +import jake2.Defines; +import jake2.game.Cmd; +import jake2.game.entity_state_t; +import jake2.qcommon.CM; +import jake2.qcommon.Cbuf; +import jake2.qcommon.Com; +import jake2.qcommon.Cvar; +import jake2.qcommon.FS; +import jake2.qcommon.MSG; +import jake2.qcommon.SZ; +import jake2.qcommon.xcommand_t; +import jake2.render.model_t; +import jake2.sys.Sys; +import jake2.util.Lib; + +/** + * CL_parse + */ +public class CL_parse extends CL_view { + + //// cl_parse.c -- parse a message received from the server + + public static String svc_strings[] = + { + "svc_bad", + "svc_muzzleflash", + "svc_muzzlflash2", + "svc_temp_entity", + "svc_layout", + "svc_inventory", + "svc_nop", + "svc_disconnect", + "svc_reconnect", + "svc_sound", + "svc_print", + "svc_stufftext", + "svc_serverdata", + "svc_configstring", + "svc_spawnbaseline", + "svc_centerprint", + "svc_download", + "svc_playerinfo", + "svc_packetentities", + "svc_deltapacketentities", + "svc_frame" }; + + // ============================================================================= + + public static String DownloadFileName(String fn) { + if ("players".equals(fn)) + return BASEDIRNAME + "/" + fn; + else + return FS.Gamedir() + "/" + fn; + } + + /* + =============== + CL_CheckOrDownloadFile + + Returns true if the file exists, otherwise it attempts + to start a download from the server. + =============== + */ + public static boolean CheckOrDownloadFile(String filename) { + RandomAccessFile fp; + String name; + + if (filename.indexOf("..") != -1) { + Com.Printf("Refusing to download a path with ..\n"); + return true; + } + + if (FS.LoadFile(filename) != null) { // it exists, no need to download + return true; + } + + cls.downloadname = filename; + + // download to a temp name, and only rename + // to the real name when done, so if interrupted + // a runt file wont be left + Com.StripExtension(cls.downloadname, cls.downloadtempname); + cls.downloadtempname += ".tmp"; + + // ZOID + // check to see if we already have a tmp for this file, if so, try to resume + // open the file if not opened yet + name = DownloadFileName(cls.downloadtempname); + + fp = fopen(name, "r+b"); + if (fp != null) { // it exists + long len = 0; + + try { + len = fp.length(); + } + catch (IOException e) { + }; + + cls.download = fp; + + // give the server an offset to start the download + Com.Printf("Resuming " + cls.downloadname + "\n"); + MSG.WriteByte(cls.netchan.message, clc_stringcmd); + MSG.WriteString(cls.netchan.message, "download " + cls.downloadname + " " + len); + } + else { + // TODO bugfix cwei + cls.downloadname = cls.downloadname.toLowerCase(); + + Com.Printf("Downloading " + cls.downloadname + "\n"); + MSG.WriteByte(cls.netchan.message, clc_stringcmd); + MSG.WriteString(cls.netchan.message, "download " + cls.downloadname); + } + + cls.downloadnumber++; + + return false; + } + + /* + =============== + CL_Download_f + + Request a download from the server + =============== + */ + static xcommand_t Download_f = new xcommand_t() { + public void execute() { + String filename; + + if (Cmd.Argc() != 2) { + Com.Printf("Usage: download <filename>\n"); + return; + } + + filename = Cmd.Argv(1); + + if (strstr(filename, "..")) { + Com.Printf("Refusing to download a path with ..\n"); + return; + } + + if (FS.LoadFile(filename) != null) { // it exists, no need to download + Com.Printf("File already exists.\n"); + return; + } + + cls.downloadname = filename; + Com.Printf("Downloading " + cls.downloadname + "\n"); + + // download to a temp name, and only rename + // to the real name when done, so if interrupted + // a runt file wont be left + Com.StripExtension(cls.downloadname, cls.downloadtempname); + strcat(cls.downloadtempname, ".tmp"); + + MSG.WriteByte(cls.netchan.message, clc_stringcmd); + MSG.WriteString(cls.netchan.message, "download " + cls.downloadname); + + cls.downloadnumber++; + } + }; + + /* + ====================== + CL_RegisterSounds + ====================== + */ + static void RegisterSounds() { + int i; + S.BeginRegistration(); + CL.RegisterTEntSounds(); + for (i = 1; i < MAX_SOUNDS; i++) { + if (cl.configstrings[CS_SOUNDS + i] == null || cl.configstrings[CS_SOUNDS + i] == "") + break; + cl.sound_precache[i] = S.RegisterSound(cl.configstrings[CS_SOUNDS + i]); + Sys.SendKeyEvents(); // pump message loop + } + S.EndRegistration(); + } + + /* + ===================== + CL_ParseDownload + + A download message has been received from the server + ===================== + */ + public static void ParseDownload() { + int size, percent; + String name; + int r; + + // read the data + size = MSG.ReadShort(net_message); + percent = MSG.ReadByte(net_message); + if (size == -1) { + Com.Printf("Server does not have this file.\n"); + if (cls.download != null) { + // if here, we tried to resume a file but the server said no + fclose(cls.download); + cls.download = null; + } + CL.RequestNextDownload(); + return; + } + + // open the file if not opened yet + if (cls.download == null) { + name = DownloadFileName(cls.downloadtempname); + + FS.CreatePath(name); + + cls.download = fopen(name, "rw"); + if (cls.download == null) { + net_message.readcount += size; + Com.Printf("Failed to open " + cls.downloadtempname + "\n"); + CL.RequestNextDownload(); + return; + } + } + + //fwrite(net_message.data[net_message.readcount], 1, size, cls.download); + try { + cls.download.write(net_message.data, net_message.readcount, size); + } + catch (Exception e) { + } + net_message.readcount += size; + + if (percent != 100) { + // request next block + // change display routines by zoid + + MSG.WriteByte(cls.netchan.message, clc_stringcmd); + SZ.Print(cls.netchan.message, "nextdl"); + } + else { + String oldn, newn; + //char oldn[MAX_OSPATH]; + //char newn[MAX_OSPATH]; + + // Com.Printf ("100%%\n"); + + fclose(cls.download); + + // rename the temp file to it's final name + oldn = DownloadFileName(cls.downloadtempname); + newn = DownloadFileName(cls.downloadname); + r = Lib.rename(oldn, newn); + if (r != 0) + Com.Printf("failed to rename.\n"); + + cls.download = null; + cls.downloadpercent = 0; + + // get another file if needed + + CL.RequestNextDownload(); + } + } + + /* + ===================================================================== + + SERVER CONNECTING MESSAGES + + ===================================================================== + */ + + /* + ================== + CL_ParseServerData + ================== + */ + //checked once, was ok. + public static void ParseServerData() { + + String str; + int i; + + Com.DPrintf("Serverdata packet received.\n"); + // + // wipe the client_state_t struct + // + CL.ClearState(); + cls.state = ca_connected; + + // parse protocol version number + i = MSG.ReadLong(net_message); + cls.serverProtocol = i; + + // BIG HACK to let demos from release work with the 3.0x patch!!! + if (Com.ServerState() != 0 && PROTOCOL_VERSION == 34) { + } + else if (i != PROTOCOL_VERSION) + Com.Error(ERR_DROP, "Server returned version " + i + ", not " + PROTOCOL_VERSION); + + cl.servercount = MSG.ReadLong(net_message); + cl.attractloop = MSG.ReadByte(net_message) != 0; + + // game directory + str = MSG.ReadString(net_message); + cl.gamedir = str; + + // set gamedir + if (str.length() > 0 + && (FS.fs_gamedirvar.string == null || FS.fs_gamedirvar.string.length() == 0 || FS.fs_gamedirvar.string.equals(str)) + || (str.length() == 0 && (FS.fs_gamedirvar.string != null || FS.fs_gamedirvar.string.length() == 0))) + Cvar.Set("game", str); + + // parse player entity number + cl.playernum = MSG.ReadShort(net_message); + + // get the full level name + str = MSG.ReadString(net_message); + + if (cl.playernum == -1) { // playing a cinematic or showing a pic, not a level + SCR.PlayCinematic(str); + } + else { + // seperate the printfs so the server message can have a color +// 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"); +// Com.Printf('\02' + str + "\n"); + Com.Printf("Levelname:" + str + "\n"); + // need to prep refresh at next oportunity + cl.refresh_prepped = false; + } + } + + /* + ================== + CL_ParseBaseline + ================== + */ + public static void ParseBaseline() { + entity_state_t es; + int newnum; + + entity_state_t nullstate = new entity_state_t(null); + //memset(nullstate, 0, sizeof(nullstate)); + CM.intwrap bits = new CM.intwrap(0); + newnum = CL_ents.ParseEntityBits(bits); + es = cl_entities[newnum].baseline; + CL_ents.ParseDelta(nullstate, es, newnum, bits.i); + } + + /* + ================ + CL_LoadClientinfo + + ================ + */ + public static void LoadClientinfo(clientinfo_t ci, String s) { + int i; + int t; + + //char model_name[MAX_QPATH]; + //char skin_name[MAX_QPATH]; + //char model_filename[MAX_QPATH]; + //char skin_filename[MAX_QPATH]; + //char weapon_filename[MAX_QPATH]; + + String model_name,skin_name,model_filename, skin_filename, weapon_filename; + + ci.cinfo = s; + //ci.cinfo[sizeof(ci.cinfo) - 1] = 0; + + // isolate the player's name + ci.name = s; + //ci.name[sizeof(ci.name) - 1] = 0; + + t = s.indexOf('\\'); + //t = strstr(s, "\\"); + + if (t!=-1) { + ci.name = s.substring(0,t); + s = s.substring(t + 1, s.length()); + //s = t + 1; + } + + if (cl_noskins.value!=0 || s.length()!=0) { + + model_filename=("players/male/tris.md2"); + weapon_filename=("players/male/weapon.md2"); + skin_filename=("players/male/grunt.pcx"); + ci.iconname=("/players/male/grunt_i.pcx"); + + ci.model = re.RegisterModel(model_filename); + + ci.weaponmodel = new model_t[Defines.MAX_CLIENTWEAPONMODELS]; + ci.weaponmodel[0] = re.RegisterModel(weapon_filename); + ci.skin = re.RegisterSkin(skin_filename); + ci.icon = re.RegisterPic(ci.iconname); + } + else { + // isolate the model name + + int pos = s.indexOf('/'); + + if (pos == -1) + pos = s.indexOf('/'); + if (pos == -1) + { + pos =0; + Com.Error(Defines.ERR_FATAL, "Invalid model name:" + s); + } + + model_name = s.substring(0,pos); + + // isolate the skin name + skin_name = s.substring(pos+1, s.length()); + + // model file + model_filename = "players/" + model_name + "/tris.md2"; + ci.model = re.RegisterModel(model_filename); + + if (ci.model==null) { + model_name = "male"; + model_filename= "players/male/tris.md2"; + ci.model = re.RegisterModel(model_filename); + } + + // skin file + skin_filename = "players/" + model_name +"/"+ skin_name + ".pcx"; + ci.skin = re.RegisterSkin(skin_filename); + + // if we don't have the skin and the model wasn't male, + // see if the male has it (this is for CTF's skins) + if (ci.skin==null && !model_name.equalsIgnoreCase("male")) { + // change model to male + model_name = "male"; + model_filename= "players/male/tris.md2"; + ci.model = re.RegisterModel(model_filename); + + // see if the skin exists for the male model + skin_filename= "players/" + model_name + "/"+ skin_name+".pcx"; + ci.skin = re.RegisterSkin(skin_filename); + } + + // if we still don't have a skin, it means that the male model didn't have + // it, so default to grunt + if (ci.skin==null) { + // see if the skin exists for the male model + skin_filename= "players/" + model_name + "/grunt.pcx"; + ci.skin = re.RegisterSkin(skin_filename); + } + + // weapon file + for (i = 0; i < num_cl_weaponmodels; i++) { + weapon_filename= "players/"+model_name +"/" + cl_weaponmodels[i]; + ci.weaponmodel[i] = re.RegisterModel(weapon_filename); + if (null==ci.weaponmodel[i] && model_name.equals("cyborg")) { + // try male + weapon_filename="players/male/" + cl_weaponmodels[i]; + ci.weaponmodel[i] = re.RegisterModel(weapon_filename); + } + if (0==cl_vwep.value) + break; // only one when vwep is off + } + + // icon file + ci.iconname= "/players/"+model_name+"/" + skin_name+"_i.pcx"; + ci.icon = re.RegisterPic(ci.iconname); + } + + // must have loaded all data types to be valud + if (ci.skin==null || ci.icon==null || ci.model==null || ci.weaponmodel[0]==null) { + ci.skin = null; + ci.icon = null; + ci.model = null; + ci.weaponmodel[0] = null; + return; + } + } + + /* + ================ + CL_ParseClientinfo + + Load the skin, icon, and model for a client + ================ + */ + static void ParseClientinfo(int player) { + String s; + clientinfo_t ci; + + s = cl.configstrings[player + CS_PLAYERSKINS]; + + ci = cl.clientinfo[player]; + + LoadClientinfo(ci, s); + } + + /* + ================ + CL_ParseConfigString + ================ + */ + public static void ParseConfigString() { + int i; + String s; + String olds; + + i = MSG.ReadShort(net_message); + + if (i < 0 || i >= MAX_CONFIGSTRINGS) + Com.Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS"); + + s = MSG.ReadString(net_message); + + olds=cl.configstrings[i]; + cl.configstrings[i] = s; + + // do something apropriate + + if (i >= CS_LIGHTS && i < CS_LIGHTS + MAX_LIGHTSTYLES) + { + SetLightstyle(i - CS_LIGHTS); + } + else if (i == CS_CDTRACK) { + if (cl.refresh_prepped) + CDAudio.CDAudio_Play(atoi(cl.configstrings[CS_CDTRACK]), true); + } + else if (i >= CS_MODELS && i < CS_MODELS + MAX_MODELS) { + if (cl.refresh_prepped) { + cl.model_draw[i - CS_MODELS] = re.RegisterModel(cl.configstrings[i]); + if (cl.configstrings[i].startsWith("*")) + cl.model_clip[i - CS_MODELS] = CM.InlineModel(cl.configstrings[i]); + else + cl.model_clip[i - CS_MODELS] = null; + } + } + else if (i >= CS_SOUNDS && i < CS_SOUNDS + MAX_MODELS) { + if (cl.refresh_prepped) + cl.sound_precache[i - CS_SOUNDS] = SND_DMA.RegisterSound(cl.configstrings[i]); + } + else if (i >= CS_IMAGES && i < CS_IMAGES + MAX_MODELS) { + if (cl.refresh_prepped) + cl.image_precache[i - CS_IMAGES] = re.RegisterPic(cl.configstrings[i]); + } + else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS + MAX_CLIENTS) { + if (cl.refresh_prepped && strcmp(olds, s)!=0) + ParseClientinfo(i - CS_PLAYERSKINS); + } + } + + /* + ===================================================================== + + ACTION MESSAGES + + ===================================================================== + */ + + /* + ================== + CL_ParseStartSoundPacket + ================== + */ + public static void ParseStartSoundPacket() { + float[] pos_v={0,0,0}; + float pos[]; + int channel, ent; + int sound_num; + float volume; + float attenuation; + int flags; + float ofs; + + flags = MSG.ReadByte(net_message); + sound_num = MSG.ReadByte(net_message); + + if ((flags & SND_VOLUME) != 0) + volume = MSG.ReadByte(net_message) / 255.0f; + else + volume = DEFAULT_SOUND_PACKET_VOLUME; + + if ((flags & SND_ATTENUATION) != 0) + attenuation = MSG.ReadByte(net_message) / 64.0f; + else + attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; + + if ((flags & SND_OFFSET) != 0) + ofs = MSG.ReadByte(net_message) / 1000.0f; + else + ofs = 0; + + if ((flags & SND_ENT) != 0) { // entity reletive + channel = MSG.ReadShort(net_message); + ent = channel >> 3; + if (ent > MAX_EDICTS) + Com.Error(ERR_DROP, "CL_ParseStartSoundPacket: ent = " + ent); + + channel &= 7; + } + else { + ent = 0; + channel = 0; + } + + if ((flags & SND_POS) != 0) { // positioned in space + MSG.ReadPos(net_message, pos_v); + + pos = pos_v; + } + else // use entity number + pos = null; + + if (null==cl.sound_precache[sound_num]) + return; + + SND_DMA.StartSound(pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs); + } + + public static void SHOWNET(String s) { + if (cl_shownet.value >= 2) + Com.Printf(net_message.readcount - 1 + ":" + s + "\n"); + } + + /* + ===================== + CL_ParseServerMessage + ===================== + */ + public static void ParseServerMessage() { + int cmd; + String s; + int i; + + // + // if recording demos, copy the message out + // + //if (cl_shownet.value == 1) + //Com.Printf(net_message.cursize + " "); + //else if (cl_shownet.value >= 2) + //Com.Printf("------------------\n"); + + // + // parse the message + // + while (true) { + if (net_message.readcount > net_message.cursize) { + Com.Error(ERR_FATAL, "CL_ParseServerMessage: Bad server message:"); + break; + } + + cmd = MSG.ReadByte(net_message); + + if (cmd == -1) { + SHOWNET("END OF MESSAGE"); + break; + } + + if (cl_shownet.value >= 2) { + if (null == svc_strings[cmd]) + Com.Printf(net_message.readcount - 1 + ":BAD CMD " + cmd + "\n"); + else + SHOWNET(svc_strings[cmd]); + } + + // other commands + switch (cmd) { + default : + Com.Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message\n"); + break; + + case svc_nop : + // Com.Printf ("svc_nop\n"); + break; + + case svc_disconnect : + Com.Error(ERR_DISCONNECT, "Server disconnected\n"); + break; + + case svc_reconnect : + Com.Printf("Server disconnected, reconnecting\n"); + if (cls.download != null) { + //ZOID, close download + fclose(cls.download); + cls.download = null; + } + cls.state = ca_connecting; + cls.connect_time = -99999; // CL_CheckForResend() will fire immediately + break; + + case svc_print : + i = MSG.ReadByte(net_message); + if (i == PRINT_CHAT) { + SND_DMA.StartLocalSound("misc/talk.wav"); + con.ormask = 128; + } + Com.Printf(MSG.ReadString(net_message)); + con.ormask = 0; + break; + + case svc_centerprint : + SCR.CenterPrint(MSG.ReadString(net_message)); + break; + + case svc_stufftext : + s = MSG.ReadString(net_message); + Com.DPrintf("stufftext: " + s + "\n"); + Cbuf.AddText(s); + break; + + case svc_serverdata : + Cbuf.Execute(); // make sure any stuffed commands are done + ParseServerData(); + break; + + case svc_configstring : + ParseConfigString(); + break; + + case svc_sound : + ParseStartSoundPacket(); + break; + + case svc_spawnbaseline : + ParseBaseline(); + break; + + case svc_temp_entity : + ParseTEnt(); + break; + + case svc_muzzleflash : + CL_fx.ParseMuzzleFlash(); + break; + + case svc_muzzleflash2 : + ParseMuzzleFlash2(); + break; + + case svc_download : + ParseDownload(); + break; + + case svc_frame : + ParseFrame(); + break; + + case svc_inventory : + CL_inv.ParseInventory(); + break; + + case svc_layout : + s = MSG.ReadString(net_message); + cl.layout = s; + break; + + case svc_playerinfo : + case svc_packetentities : + case svc_deltapacketentities : + Com.Error(ERR_DROP, "Out of place frame data"); + break; + } + } + + CL_view.AddNetgraph(); + + // + // we don't know if it is ok to save a demo message until + // after we have parsed the frame + // + if (cls.demorecording && !cls.demowaiting) + CL.WriteDemoMessage(); + } +} diff --git a/src/jake2/client/CL_pred.java b/src/jake2/client/CL_pred.java new file mode 100644 index 0000000..cc8061f --- /dev/null +++ b/src/jake2/client/CL_pred.java @@ -0,0 +1,347 @@ +/* + * CL_pred.java + * Copyright (C) 2004 + * + * $Id: CL_pred.java,v 1.1 2004-07-07 19:58:39 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.game.*; +import jake2.qcommon.*; + +/** + * CL_pred + */ +public class CL_pred extends CL_parse +{ + + /* + =================== + CL_CheckPredictionError + =================== + */ + static void CheckPredictionError() + { + int frame; + int[] delta = new int[3]; + int i; + int len; + + if (cl_predict.value == 0.0f + || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION) != 0) + return; + + // calculate the last usercmd_t we sent that the server has processed + frame = cls.netchan.incoming_acknowledged; + frame &= (CMD_BACKUP - 1); + + // compare what the server returned with what we had predicted it to be + VectorSubtract( + cl.frame.playerstate.pmove.origin, + cl.predicted_origins[frame], + delta); + + // save the prediction error for interpolation + len = Math.abs(delta[0]) + Math.abs(delta[1]) + Math.abs(delta[2]); + if (len > 640) // 80 world units + { // a teleport or something + VectorClear(cl.prediction_error); + } + else + { + if (cl_showmiss.value != 0.0f + && (delta[0] != 0 || delta[1] != 0 || delta[2] != 0)) + Com.Printf( + "prediction miss on " + + cl.frame.serverframe + + ": " + + (delta[0] + delta[1] + delta[2]) + + "\n"); + + VectorCopy( + cl.frame.playerstate.pmove.origin, + cl.predicted_origins[frame]); + + // save for error itnerpolation + for (i = 0; i < 3; i++) + cl.prediction_error[i] = delta[i] * 0.125f; + } + } + + /* + ==================== + CL_ClipMoveToEntities + + ==================== + */ + static void ClipMoveToEntities( + float[] start, + float[] mins, + float[] maxs, + float[] end, + trace_t tr) + { + int i, x, zd, zu; + trace_t trace; + int headnode; + float[] angles; + entity_state_t ent; + int num; + cmodel_t cmodel; + float[] bmins = new float[3]; + float[] bmaxs = new float[3]; + + for (i = 0; i < cl.frame.num_entities; i++) + { + num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); + ent = cl_parse_entities[num]; + + if (ent.solid == 0) + continue; + + if (ent.number == cl.playernum + 1) + continue; + + if (ent.solid == 31) + { // special value for bmodel + cmodel = cl.model_clip[ent.modelindex]; + if (cmodel == null) + continue; + headnode = cmodel.headnode; + angles = ent.angles; + } + else + { // encoded bbox + x = 8 * (ent.solid & 31); + zd = 8 * ((ent.solid >>> 5) & 31); + zu = 8 * ((ent.solid >>> 10) & 63) - 32; + + bmins[0] = bmins[1] = -x; + bmaxs[0] = bmaxs[1] = x; + bmins[2] = -zd; + bmaxs[2] = zu; + + headnode = CM.HeadnodeForBox(bmins, bmaxs); + angles = vec3_origin; // boxes don't rotate + } + + if (tr.allsolid) + return; + + trace = + CM.TransformedBoxTrace( + start, + end, + mins, + maxs, + headnode, + MASK_PLAYERSOLID, + ent.origin, + angles); + + if (trace.allsolid + || trace.startsolid + || trace.fraction < tr.fraction) + { + // TODO bugfix cwei + //if (trace.ent == null) trace.ent = new edict_t(0); + trace.ent = ent.surrounding_ent; + if (tr.startsolid) + { + tr = trace; + tr.startsolid = true; + } + else + tr = trace; + } + else if (trace.startsolid) + tr.startsolid = true; + } + } + + /* + ================ + CL_PMTrace + ================ + */ + + static edict_t DUMMY_ENT = new edict_t(-1); + + static trace_t PMTrace(float[] start, float[] mins, float[] maxs, float[] end) { + trace_t t; + + // check against world + t = CM.BoxTrace(start, end, mins, maxs, 0, MASK_PLAYERSOLID); + + if (t.fraction < 1.0f) { + t.ent = DUMMY_ENT; + } + + // check all other solid models + CL.ClipMoveToEntities(start, mins, maxs, end, t); + + return t; + } + + static int PMpointcontents(float[] point) + { + int i; + entity_state_t ent; + int num; + cmodel_t cmodel; + int contents; + + contents = CM.PointContents(point, 0); + + for (i = 0; i < cl.frame.num_entities; i++) + { + num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); + ent = cl_parse_entities[num]; + + if (ent.solid != 31) // special value for bmodel + continue; + + cmodel = cl.model_clip[ent.modelindex]; + if (cmodel == null) + continue; + + contents + |= CM.TransformedPointContents( + point, + cmodel.headnode, + ent.origin, + ent.angles); + } + + return contents; + } + + /* + ================= + CL_PredictMovement + + Sets cl.predicted_origin and cl.predicted_angles + ================= + */ + static void PredictMovement() + { + int ack, current; + int frame; + int oldframe; + usercmd_t cmd; + pmove_t pm; + int i; + int step; + int oldz; + + if (cls.state != ca_active) + return; + + if (cl_paused.value != 0.0f) + return; + + if (cl_predict.value == 0.0f + || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION) != 0) + { // just set angles + for (i = 0; i < 3; i++) + { + cl.predicted_angles[i] = + cl.viewangles[i] + + SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[i]); + } + return; + } + + ack = cls.netchan.incoming_acknowledged; + current = cls.netchan.outgoing_sequence; + + // if we are too far out of date, just freeze + if (current - ack >= CMD_BACKUP) + { + if (cl_showmiss.value != 0.0f) + Com.Printf("exceeded CMD_BACKUP\n"); + return; + } + + // copy current state to pmove + //memset (pm, 0, sizeof(pm)); + pm = new pmove_t(); + + pm.trace = new pmove_t.TraceAdapter() + { + public trace_t trace( + float[] start, + float[] mins, + float[] maxs, + float[] end) + { + return CL.PMTrace(start, mins, maxs, end); + } + }; + pm.pointcontents = new pmove_t.PointContentsAdapter() + { + public int pointcontents(float[] point) + { + return CL.PMpointcontents(point); + } + }; + + PMove.pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]); + + // bugfix (rst) yeah !!!!!!!! found the B E W E G U N G S P R O B L E M. + pm.s.set(cl.frame.playerstate.pmove); + + // SCR_DebugGraph (current - ack - 1, 0); + frame = 0; + + // run frames + while (++ack < current) + { + frame = ack & (CMD_BACKUP - 1); + cmd = cl.cmds[frame]; + + pm.cmd.set(cmd); + + PMove.Pmove(pm); + + // save for debug checking + VectorCopy(pm.s.origin, cl.predicted_origins[frame]); + } + + oldframe = (ack - 2) & (CMD_BACKUP - 1); + oldz = cl.predicted_origins[oldframe][2]; + step = pm.s.origin[2] - oldz; + if (step > 63 && step < 160 && (pm.s.pm_flags & PMF_ON_GROUND) != 0) + { + cl.predicted_step = step * 0.125f; + cl.predicted_step_time = (int) (cls.realtime - cls.frametime * 500); + } + + // copy results out for rendering + cl.predicted_origin[0] = pm.s.origin[0] * 0.125f; + cl.predicted_origin[1] = pm.s.origin[1] * 0.125f; + cl.predicted_origin[2] = pm.s.origin[2] * 0.125f; + + VectorCopy(pm.viewangles, cl.predicted_angles); + } + +} diff --git a/src/jake2/client/CL_tent.java b/src/jake2/client/CL_tent.java new file mode 100644 index 0000000..7430a45 --- /dev/null +++ b/src/jake2/client/CL_tent.java @@ -0,0 +1,1720 @@ +/* + * CL_tent.java + * Copyright (C) 2004 + * + * $Id: CL_tent.java,v 1.1 2004-07-07 19:58:40 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.player_state_t; +import jake2.qcommon.Com; +import jake2.qcommon.MSG; +import jake2.render.model_t; + +/** + * CL_tent + */ +public class CL_tent extends Globals { + + static final int ex_free = 0; + static final int ex_explosion = 1; + static final int ex_misc = 2; + static final int ex_flash = 3; + static final int ex_mflash = 4; + static final int ex_poly = 5; + static final int ex_poly2 = 6; + + + static class explosion_t { + int type; + entity_t ent = new entity_t(); + + int frames; + float light; + float[] lightcolor = new float[3]; + float start; + int baseframe; + void clear() { + lightcolor[0] = lightcolor[1] = lightcolor[2] = + light = start = type = frames = baseframe = 0; + ent = new entity_t(); + } + } + + static final int MAX_EXPLOSIONS = 32; + static explosion_t[] cl_explosions = new explosion_t[MAX_EXPLOSIONS]; + static { + for (int i = 0; i < cl_explosions.length; i++) + cl_explosions[i] = new explosion_t(); + } + + static final int MAX_BEAMS = 32; + + static class beam_t { + int entity; + int dest_entity; + model_t model; + int endtime; + float[] offset = new float[3]; + float[] start = new float[3]; + float[] end = new float[3]; + void clear() { + offset[0] = offset[1] = offset[2] = + start[0] = start[1] = start[2] = + end[0] = end[1] = end[2] = + entity = dest_entity = endtime = 0; + model = null; + } + } + static beam_t[] cl_beams = new beam_t[MAX_BEAMS]; + // PMM - added this for player-linked beams. Currently only used by the plasma beam + static beam_t[] cl_playerbeams = new beam_t[MAX_BEAMS]; + static { + for (int i = 0; i < cl_beams.length; i++) + cl_beams[i] = new beam_t(); + for (int i = 0; i < cl_playerbeams.length; i++) + cl_playerbeams[i] = new beam_t(); + } + + static final int MAX_LASERS = 32; + + static class laser_t { + entity_t ent = new entity_t(); + int endtime; + void clear() { + endtime = 0; + ent = new entity_t(); + } + } + static laser_t[] cl_lasers = new laser_t[MAX_LASERS]; + static { + for (int i = 0; i < cl_lasers.length; i++) + cl_lasers[i] = new laser_t(); + } + +// ROGUE + static final int MAX_SUSTAINS = 32; + static cl_sustain_t[] cl_sustains = new cl_sustain_t[MAX_SUSTAINS]; + static { + for (int i = 0; i < cl_sustains.length; i++) + cl_sustains[i] = new cl_sustain_t(); + } +// ROGUE + + // all are references; + static sfx_t cl_sfx_ric1; + static sfx_t cl_sfx_ric2; + static sfx_t cl_sfx_ric3; + static sfx_t cl_sfx_lashit; + static sfx_t cl_sfx_spark5; + static sfx_t cl_sfx_spark6; + static sfx_t cl_sfx_spark7; + static sfx_t cl_sfx_railg; + static sfx_t cl_sfx_rockexp; + static sfx_t cl_sfx_grenexp; + static sfx_t cl_sfx_watrexp; + // RAFAEL + static sfx_t cl_sfx_plasexp; + static sfx_t cl_sfx_footsteps[] = new sfx_t[4]; + + static model_t cl_mod_explode; + static model_t cl_mod_smoke; + static model_t cl_mod_flash; + static model_t cl_mod_parasite_segment; + static model_t cl_mod_grapple_cable; + static model_t cl_mod_parasite_tip; + static model_t cl_mod_explo4; + static model_t cl_mod_bfg_explo; + static model_t cl_mod_powerscreen; + // RAFAEL + static model_t cl_mod_plasmaexplo; + + // ROGUE + static sfx_t cl_sfx_lightning; + static sfx_t cl_sfx_disrexp; + static model_t cl_mod_lightning; + static model_t cl_mod_heatbeam; + static model_t cl_mod_monster_heatbeam; + static model_t cl_mod_explo4_big; + +// ROGUE + /* + ================= + CL_RegisterTEntSounds + ================= + */ + static void RegisterTEntSounds() { + int i; + String name; + + // PMM - version stuff + // Com_Printf ("%s\n", ROGUE_VERSION_STRING); + // PMM + cl_sfx_ric1 = S.RegisterSound("world/ric1.wav"); + cl_sfx_ric2 = S.RegisterSound("world/ric2.wav"); + cl_sfx_ric3 = S.RegisterSound("world/ric3.wav"); + cl_sfx_lashit = S.RegisterSound("weapons/lashit.wav"); + cl_sfx_spark5 = S.RegisterSound("world/spark5.wav"); + cl_sfx_spark6 = S.RegisterSound("world/spark6.wav"); + cl_sfx_spark7 = S.RegisterSound("world/spark7.wav"); + cl_sfx_railg = S.RegisterSound("weapons/railgf1a.wav"); + cl_sfx_rockexp = S.RegisterSound("weapons/rocklx1a.wav"); + cl_sfx_grenexp = S.RegisterSound("weapons/grenlx1a.wav"); + cl_sfx_watrexp = S.RegisterSound("weapons/xpld_wat.wav"); + // RAFAEL + // cl_sfx_plasexp = S.RegisterSound ("weapons/plasexpl.wav"); + S.RegisterSound("player/land1.wav"); + + S.RegisterSound("player/fall2.wav"); + S.RegisterSound("player/fall1.wav"); + + for (i = 0; i < 4; i++) { + //Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1); + name = "player/step" + (i + 1) + ".wav"; + cl_sfx_footsteps[i] = S.RegisterSound(name); + } + + // PGM + cl_sfx_lightning = S.RegisterSound("weapons/tesla.wav"); + cl_sfx_disrexp = S.RegisterSound("weapons/disrupthit.wav"); + // version stuff + // sprintf (name, "weapons/sound%d.wav", ROGUE_VERSION_ID); + // if (name[0] == 'w') + // name[0] = 'W'; + // PGM + } + + /* + ================= + CL_RegisterTEntModels + ================= + */ + static void RegisterTEntModels() { + cl_mod_explode = re.RegisterModel("models/objects/explode/tris.md2"); + cl_mod_smoke = re.RegisterModel("models/objects/smoke/tris.md2"); + cl_mod_flash = re.RegisterModel("models/objects/flash/tris.md2"); + cl_mod_parasite_segment = re.RegisterModel("models/monsters/parasite/segment/tris.md2"); + cl_mod_grapple_cable = re.RegisterModel("models/ctf/segment/tris.md2"); + cl_mod_parasite_tip = re.RegisterModel("models/monsters/parasite/tip/tris.md2"); + cl_mod_explo4 = re.RegisterModel("models/objects/r_explode/tris.md2"); + cl_mod_bfg_explo = re.RegisterModel("sprites/s_bfg2.sp2"); + cl_mod_powerscreen = re.RegisterModel("models/items/armor/effect/tris.md2"); + + re.RegisterModel("models/objects/laser/tris.md2"); + re.RegisterModel("models/objects/grenade2/tris.md2"); + re.RegisterModel("models/weapons/v_machn/tris.md2"); + re.RegisterModel("models/weapons/v_handgr/tris.md2"); + re.RegisterModel("models/weapons/v_shotg2/tris.md2"); + re.RegisterModel("models/objects/gibs/bone/tris.md2"); + re.RegisterModel("models/objects/gibs/sm_meat/tris.md2"); + re.RegisterModel("models/objects/gibs/bone2/tris.md2"); + // RAFAEL + // re.RegisterModel ("models/objects/blaser/tris.md2"); + + re.RegisterPic("w_machinegun"); + re.RegisterPic("a_bullets"); + re.RegisterPic("i_health"); + re.RegisterPic("a_grenades"); + + // ROGUE + cl_mod_explo4_big = re.RegisterModel("models/objects/r_explode2/tris.md2"); + cl_mod_lightning = re.RegisterModel("models/proj/lightning/tris.md2"); + cl_mod_heatbeam = re.RegisterModel("models/proj/beam/tris.md2"); + cl_mod_monster_heatbeam = re.RegisterModel("models/proj/widowbeam/tris.md2"); + // ROGUE + } + + /* + ================= + CL_ClearTEnts + ================= + */ + static void ClearTEnts() { + // memset (cl_beams, 0, sizeof(cl_beams)); + for (int i = 0; i < cl_beams.length; i++) + cl_beams[i].clear(); + // memset (cl_explosions, 0, sizeof(cl_explosions)); + for (int i = 0; i < cl_explosions.length; i++) + cl_explosions[i].clear(); + // memset (cl_lasers, 0, sizeof(cl_lasers)); + for (int i = 0; i < cl_lasers.length; i++) + cl_lasers[i].clear(); + // + // ROGUE + // memset (cl_playerbeams, 0, sizeof(cl_playerbeams)); + for (int i = 0; i < cl_playerbeams.length; i++) + cl_playerbeams[i].clear(); + // memset (cl_sustains, 0, sizeof(cl_sustains)); + for (int i = 0; i < cl_sustains.length; i++) + cl_sustains[i].clear(); + // ROGUE + } + + /* + ================= + CL_AllocExplosion + ================= + */ + static explosion_t AllocExplosion() { + int i; + int time; + int index; + + for (i = 0; i < MAX_EXPLOSIONS; i++) { + if (cl_explosions[i].type == ex_free) { + //memset (&cl_explosions[i], 0, sizeof (cl_explosions[i])); + cl_explosions[i].clear(); + return cl_explosions[i]; + } + } + // find the oldest explosion + time = cl.time; + index = 0; + + for (i = 0; i < MAX_EXPLOSIONS; i++) + if (cl_explosions[i].start < time) { + time = (int)cl_explosions[i].start; + index = i; + } + //memset (&cl_explosions[index], 0, sizeof (cl_explosions[index])); + cl_explosions[index].clear(); + return cl_explosions[index]; + } + + /* + ================= + CL_SmokeAndFlash + ================= + */ + static void SmokeAndFlash(float[] origin) { + explosion_t ex; + + ex = CL.AllocExplosion(); + VectorCopy(origin, ex.ent.origin); + ex.type = ex_misc; + ex.frames = 4; + ex.ent.flags = RF_TRANSLUCENT; + ex.start = cl.frame.servertime - 100; + ex.ent.model = cl_mod_smoke; + + ex = CL.AllocExplosion(); + VectorCopy(origin, ex.ent.origin); + ex.type = ex_flash; + ex.ent.flags = RF_FULLBRIGHT; + ex.frames = 2; + ex.start = cl.frame.servertime - 100; + ex.ent.model = cl_mod_flash; + } + + /* + ================= + CL_ParseParticles + ================= + */ + static void ParseParticles() { + int color, count; + float[] pos = new float[3]; + float[] dir = new float[3]; + + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + + color = MSG.ReadByte(net_message); + + count = MSG.ReadByte(net_message); + + CL.ParticleEffect(pos, dir, color, count); + } + + /* + ================= + CL_ParseBeam + ================= + */ + static int ParseBeam(model_t model) { + int ent; + float[] start = new float[3]; + float[] end = new float[3]; + beam_t[] b; + int i; + + ent = MSG.ReadShort(net_message); + + MSG.ReadPos(net_message, start); + MSG.ReadPos(net_message, end); + + // override any beam with the same entity + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) + if (b[i].entity == ent) { + b[i].entity = ent; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorClear(b[i].offset); + return ent; + } + + // find a free beam + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) { + if (b[i].model == null || b[i].endtime < cl.time) { + b[i].entity = ent; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorClear(b[i].offset); + return ent; + } + } + Com.Printf("beam list overflow!\n"); + return ent; + } + + /* + ================= + CL_ParseBeam2 + ================= + */ + static int ParseBeam2(model_t model) { + int ent; + float[] start = new float[3]; + float[] end = new float[3]; + float[] offset = new float[3]; + beam_t[] b; + int i; + + ent = MSG.ReadShort(net_message); + + MSG.ReadPos(net_message, start); + MSG.ReadPos(net_message, end); + MSG.ReadPos(net_message, offset); + + // Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]); + + // override any beam with the same entity + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) + if (b[i].entity == ent) { + b[i].entity = ent; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorCopy(offset, b[i].offset); + return ent; + } + + // find a free beam + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) { + if (b[i].model == null || b[i].endtime < cl.time) { + b[i].entity = ent; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorCopy(offset, b[i].offset); + return ent; + } + } + Com.Printf("beam list overflow!\n"); + return ent; + } + + // ROGUE + /* + ================= + CL_ParsePlayerBeam + - adds to the cl_playerbeam array instead of the cl_beams array + ================= + */ + static int ParsePlayerBeam(model_t model) { + int ent; + float[] start = new float[3]; + float[] end = new float[3]; + float[] offset = new float[3]; + beam_t[] b; + int i; + + ent = MSG.ReadShort(net_message); + + MSG.ReadPos(net_message, start); + MSG.ReadPos(net_message, end); + // PMM - network optimization + if (model == cl_mod_heatbeam) + VectorSet(offset, 2, 7, -3); + else if (model == cl_mod_monster_heatbeam) { + model = cl_mod_heatbeam; + VectorSet(offset, 0, 0, 0); + } else + MSG.ReadPos(net_message, offset); + + // Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]); + + // override any beam with the same entity + // PMM - For player beams, we only want one per player (entity) so.. + b = cl_playerbeams; + for (i = 0; i < MAX_BEAMS; i++) { + if (b[i].entity == ent) { + b[i].entity = ent; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorCopy(offset, b[i].offset); + return ent; + } + } + + // find a free beam + b = cl_playerbeams; + for (i = 0; i < MAX_BEAMS; i++) { + if (b[i].model == null || b[i].endtime < cl.time) { + b[i].entity = ent; + b[i].model = model; + b[i].endtime = cl.time + 100; // PMM - this needs to be 100 to prevent multiple heatbeams + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorCopy(offset, b[i].offset); + return ent; + } + } + Com.Printf("beam list overflow!\n"); + return ent; + } +// rogue + + /* + ================= + CL_ParseLightning + ================= + */ + static int ParseLightning(model_t model) { + int srcEnt, destEnt; + float[] start = new float[3]; + float[] end = new float[3]; + beam_t[] b; + int i; + + srcEnt = MSG.ReadShort(net_message); + destEnt = MSG.ReadShort(net_message); + + MSG.ReadPos(net_message, start); + MSG.ReadPos(net_message, end); + + // override any beam with the same source AND destination entities + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) + if (b[i].entity == srcEnt && b[i].dest_entity == destEnt) { + // Com_Printf("%d: OVERRIDE %d . %d\n", cl.time, srcEnt, destEnt); + b[i].entity = srcEnt; + b[i].dest_entity = destEnt; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorClear(b[i].offset); + return srcEnt; + } + + // find a free beam + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) { + if (b[i].model == null || b[i].endtime < cl.time) { + // Com_Printf("%d: NORMAL %d . %d\n", cl.time, srcEnt, destEnt); + b[i].entity = srcEnt; + b[i].dest_entity = destEnt; + b[i].model = model; + b[i].endtime = cl.time + 200; + VectorCopy(start, b[i].start); + VectorCopy(end, b[i].end); + VectorClear(b[i].offset); + return srcEnt; + } + } + Com.Printf("beam list overflow!\n"); + return srcEnt; + } + + /* + ================= + CL_ParseLaser + ================= + */ + static void ParseLaser(int colors) { + float[] start = new float[3]; + float[] end = new float[3]; + laser_t[] l; + int i; + + MSG.ReadPos(net_message, start); + MSG.ReadPos(net_message, end); + + l = cl_lasers; + for (i = 0; i < MAX_LASERS; i++) { + if (l[i].endtime < cl.time) { + l[i].ent.flags = RF_TRANSLUCENT | RF_BEAM; + VectorCopy(start, l[i].ent.origin); + VectorCopy(end, l[i].ent.oldorigin); + l[i].ent.alpha = 0.30f; + l[i].ent.skinnum = (colors >> ((rand() % 4) * 8)) & 0xff; + l[i].ent.model = null; + l[i].ent.frame = 4; + l[i].endtime = cl.time + 100; + return; + } + } + } + +// ============= +// ROGUE + static void ParseSteam() { + float[] pos = new float[3]; + float[] dir = new float[3]; + int id, i; + int r; + int cnt; + int color; + int magnitude; + cl_sustain_t[] s; + cl_sustain_t free_sustain; + + id = MSG.ReadShort(net_message); // an id of -1 is an instant effect + if (id != -1) // sustains + { + // Com_Printf ("Sustain effect id %d\n", id); + free_sustain = null; + s = cl_sustains; + for (i = 0; i < MAX_SUSTAINS; i++) { + if (s[i].id == 0) { + free_sustain = s[i]; + break; + } + } + if (free_sustain != null) { + s[i].id = id; + s[i].count = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, s[i].org); + MSG.ReadDir(net_message, s[i].dir); + r = MSG.ReadByte(net_message); + s[i].color = r & 0xff; + s[i].magnitude = MSG.ReadShort(net_message); + s[i].endtime = cl.time + MSG.ReadLong(net_message); + s[i].think = new cl_sustain_t.ThinkAdapter() { + void think(cl_sustain_t self) { + CL.ParticleSteamEffect2(self); + } + }; + s[i].thinkinterval = 100; + s[i].nextthink = cl.time; + } else { + // Com_Printf ("No free sustains!\n"); + // FIXME - read the stuff anyway + cnt = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + r = MSG.ReadByte(net_message); + magnitude = MSG.ReadShort(net_message); + magnitude = MSG.ReadLong(net_message); // really interval + } + } else // instant + { + cnt = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + r = MSG.ReadByte(net_message); + magnitude = MSG.ReadShort(net_message); + color = r & 0xff; + CL.ParticleSteamEffect(pos, dir, color, cnt, magnitude); + // S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + } + } + + static void ParseWidow() { + float[] pos = new float[3]; + int id, i; + cl_sustain_t[] s; + cl_sustain_t free_sustain; + + id = MSG.ReadShort(net_message); + + free_sustain = null; + s = cl_sustains; + for (i = 0; i < MAX_SUSTAINS; i++) { + if (s[i].id == 0) { + free_sustain = s[i]; + break; + } + } + if (free_sustain != null) { + s[i].id = id; + MSG.ReadPos(net_message, s[i].org); + s[i].endtime = cl.time + 2100; + s[i].think = new cl_sustain_t.ThinkAdapter() { + void think(cl_sustain_t self) { + CL.Widowbeamout(self); + } + }; + s[i].thinkinterval = 1; + s[i].nextthink = cl.time; + } else // no free sustains + { + // FIXME - read the stuff anyway + MSG.ReadPos(net_message, pos); + } + } + + static void ParseNuke() { + float[] pos = new float[3]; + int i; + cl_sustain_t[] s; + cl_sustain_t free_sustain; + + free_sustain = null; + s = cl_sustains; + for (i = 0; i < MAX_SUSTAINS; i++) { + if (s[i].id == 0) { + free_sustain = s[i]; + break; + } + } + if (free_sustain != null) { + s[i].id = 21000; + MSG.ReadPos(net_message, s[i].org); + s[i].endtime = cl.time + 1000; + s[i].think = new cl_sustain_t.ThinkAdapter() { + void think(cl_sustain_t self) { + CL.Nukeblast(self); + } + }; + s[i].thinkinterval = 1; + s[i].nextthink = cl.time; + } else // no free sustains + { + // FIXME - read the stuff anyway + MSG.ReadPos(net_message, pos); + } + } + +// ROGUE +// ============= + + + /* + ================= + CL_ParseTEnt + ================= + */ + static int[] splash_color = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8}; + static void ParseTEnt() { + int type; + float[] pos = new float[3]; + float[] pos2 = new float[3]; + float[] dir = new float[3]; + explosion_t ex; + int cnt; + int color; + int r; + int ent; + int magnitude; + + type = MSG.ReadByte(net_message); + + switch (type) { + case TE_BLOOD : // bullet hitting flesh + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + CL.ParticleEffect(pos, dir, 0xe8, 60); + break; + + case TE_GUNSHOT : // bullet hitting wall + case TE_SPARKS : + case TE_BULLET_SPARKS : + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + if (type == TE_GUNSHOT) + CL.ParticleEffect(pos, dir, 0, 40); + else + CL.ParticleEffect(pos, dir, 0xe0, 6); + + if (type != TE_SPARKS) { + CL.SmokeAndFlash(pos); + + // impact sound + cnt = rand() & 15; + if (cnt == 1) + S.StartSound(pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0); + else if (cnt == 2) + S.StartSound(pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0); + else if (cnt == 3) + S.StartSound(pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0); + } + + break; + + case TE_SCREEN_SPARKS : + case TE_SHIELD_SPARKS : + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + if (type == TE_SCREEN_SPARKS) + CL.ParticleEffect(pos, dir, 0xd0, 40); + else + CL.ParticleEffect(pos, dir, 0xb0, 40); + //FIXME : replace or remove this sound + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_SHOTGUN : // bullet hitting wall + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + CL.ParticleEffect(pos, dir, 0, 20); + CL.SmokeAndFlash(pos); + break; + + case TE_SPLASH : // bullet hitting water + cnt = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + r = MSG.ReadByte(net_message); + if (r > 6) + color = 0x00; + else + color = splash_color[r]; + CL.ParticleEffect(pos, dir, color, cnt); + + if (r == SPLASH_SPARKS) { + r = rand() & 3; + if (r == 0) + S.StartSound(pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0); + else if (r == 1) + S.StartSound(pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0); + else + S.StartSound(pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0); + } + break; + + case TE_LASER_SPARKS : + cnt = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + color = MSG.ReadByte(net_message); + CL.ParticleEffect2(pos, dir, color, cnt); + break; + + // RAFAEL + case TE_BLUEHYPERBLASTER : + MSG.ReadPos(net_message, pos); + MSG.ReadPos(net_message, dir); + CL.BlasterParticles(pos, dir); + break; + + case TE_BLASTER : // blaster hitting wall + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + CL.BlasterParticles(pos, dir); + + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.ent.angles[0] = (float) (Math.acos(dir[2]) / Math.PI * 180); + // PMM - fixed to correct for pitch of 0 + if (dir[0] != 0.0f) + ex.ent.angles[1] = (float) (Math.atan2(dir[1], dir[0]) / Math.PI * 180); + else if (dir[1] > 0) + ex.ent.angles[1] = 90; + else if (dir[1] < 0) + ex.ent.angles[1] = 270; + else + ex.ent.angles[1] = 0; + + ex.type = ex_misc; + ex.ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT; + ex.start = cl.frame.servertime - 100; + ex.light = 150; + ex.lightcolor[0] = 1; + ex.lightcolor[1] = 1; + ex.ent.model = cl_mod_explode; + ex.frames = 4; + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_RAILTRAIL : // railgun effect + MSG.ReadPos(net_message, pos); + MSG.ReadPos(net_message, pos2); + CL.RailTrail(pos, pos2); + S.StartSound(pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0); + break; + + case TE_EXPLOSION2 : + case TE_GRENADE_EXPLOSION : + case TE_GRENADE_EXPLOSION_WATER : + MSG.ReadPos(net_message, pos); + + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.type = ex_poly; + ex.ent.flags = RF_FULLBRIGHT; + ex.start = cl.frame.servertime - 100; + ex.light = 350; + ex.lightcolor[0] = 1.0f; + ex.lightcolor[1] = 0.5f; + ex.lightcolor[2] = 0.5f; + ex.ent.model = cl_mod_explo4; + ex.frames = 19; + ex.baseframe = 30; + ex.ent.angles[1] = rand() % 360; + CL.ExplosionParticles(pos); + if (type == TE_GRENADE_EXPLOSION_WATER) + S.StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); + else + S.StartSound(pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0); + break; + + // RAFAEL + case TE_PLASMA_EXPLOSION : + MSG.ReadPos(net_message, pos); + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.type = ex_poly; + ex.ent.flags = RF_FULLBRIGHT; + ex.start = cl.frame.servertime - 100; + ex.light = 350; + ex.lightcolor[0] = 1.0f; + ex.lightcolor[1] = 0.5f; + ex.lightcolor[2] = 0.5f; + ex.ent.angles[1] = rand() % 360; + ex.ent.model = cl_mod_explo4; + if (frand() < 0.5) + ex.baseframe = 15; + ex.frames = 15; + CL.ExplosionParticles(pos); + S.StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); + break; + + case TE_EXPLOSION1 : + case TE_EXPLOSION1_BIG : // PMM + case TE_ROCKET_EXPLOSION : + case TE_ROCKET_EXPLOSION_WATER : + case TE_EXPLOSION1_NP : // PMM + MSG.ReadPos(net_message, pos); + + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.type = ex_poly; + ex.ent.flags = RF_FULLBRIGHT; + ex.start = cl.frame.servertime - 100; + ex.light = 350; + ex.lightcolor[0] = 1.0f; + ex.lightcolor[1] = 0.5f; + ex.lightcolor[2] = 0.5f; + ex.ent.angles[1] = rand() % 360; + if (type != TE_EXPLOSION1_BIG) // PMM + ex.ent.model = cl_mod_explo4; // PMM + else + ex.ent.model = cl_mod_explo4_big; + if (frand() < 0.5) + ex.baseframe = 15; + ex.frames = 15; + if ((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) // PMM + CL.ExplosionParticles(pos); // PMM + if (type == TE_ROCKET_EXPLOSION_WATER) + S.StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); + else + S.StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); + break; + + case TE_BFG_EXPLOSION : + MSG.ReadPos(net_message, pos); + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.type = ex_poly; + ex.ent.flags = RF_FULLBRIGHT; + ex.start = cl.frame.servertime - 100; + ex.light = 350; + ex.lightcolor[0] = 0.0f; + ex.lightcolor[1] = 1.0f; + ex.lightcolor[2] = 0.0f; + ex.ent.model = cl_mod_bfg_explo; + ex.ent.flags |= RF_TRANSLUCENT; + ex.ent.alpha = 0.30f; + ex.frames = 4; + break; + + case TE_BFG_BIGEXPLOSION : + MSG.ReadPos(net_message, pos); + CL.BFGExplosionParticles(pos); + break; + + case TE_BFG_LASER : + CL.ParseLaser(0xd0d1d2d3); + break; + + case TE_BUBBLETRAIL : + MSG.ReadPos(net_message, pos); + MSG.ReadPos(net_message, pos2); + CL.BubbleTrail(pos, pos2); + break; + + case TE_PARASITE_ATTACK : + case TE_MEDIC_CABLE_ATTACK : + ent = CL.ParseBeam(cl_mod_parasite_segment); + break; + + case TE_BOSSTPORT : // boss teleporting to station + MSG.ReadPos(net_message, pos); + CL.BigTeleportParticles(pos); + S.StartSound(pos, 0, 0, S.RegisterSound("misc/bigtele.wav"), 1, ATTN_NONE, 0); + break; + + case TE_GRAPPLE_CABLE : + ent = CL.ParseBeam2(cl_mod_grapple_cable); + break; + + // RAFAEL + case TE_WELDING_SPARKS : + cnt = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + color = MSG.ReadByte(net_message); + CL.ParticleEffect2(pos, dir, color, cnt); + + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.type = ex_flash; + // note to self + // we need a better no draw flag + ex.ent.flags = RF_BEAM; + ex.start = cl.frame.servertime - 0.1f; + ex.light = 100 + (rand() % 75); + ex.lightcolor[0] = 1.0f; + ex.lightcolor[1] = 1.0f; + ex.lightcolor[2] = 0.3f; + ex.ent.model = cl_mod_flash; + ex.frames = 2; + break; + + case TE_GREENBLOOD : + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + CL.ParticleEffect2(pos, dir, 0xdf, 30); + break; + + // RAFAEL + case TE_TUNNEL_SPARKS : + cnt = MSG.ReadByte(net_message); + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + color = MSG.ReadByte(net_message); + CL.ParticleEffect3(pos, dir, color, cnt); + break; + + // ============= + // PGM + // PMM -following code integrated for flechette (different color) + case TE_BLASTER2 : // green blaster hitting wall + case TE_FLECHETTE : // flechette + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + + // PMM + if (type == TE_BLASTER2) + CL.BlasterParticles2(pos, dir, 0xd0); + else + CL.BlasterParticles2(pos, dir, 0x6f); // 75 + + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.ent.angles[0] = (float) (Math.acos(dir[2]) / Math.PI * 180); + // PMM - fixed to correct for pitch of 0 + if (dir[0] != 0.0f) + ex.ent.angles[1] = (float) (Math.atan2(dir[1], dir[0]) / Math.PI * 180); + else if (dir[1] > 0) + ex.ent.angles[1] = 90; + else if (dir[1] < 0) + ex.ent.angles[1] = 270; + else + ex.ent.angles[1] = 0; + + ex.type = ex_misc; + ex.ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT; + + // PMM + if (type == TE_BLASTER2) + ex.ent.skinnum = 1; + else // flechette + ex.ent.skinnum = 2; + + ex.start = cl.frame.servertime - 100; + ex.light = 150; + // PMM + if (type == TE_BLASTER2) + ex.lightcolor[1] = 1; + else // flechette + { + ex.lightcolor[0] = 0.19f; + ex.lightcolor[1] = 0.41f; + ex.lightcolor[2] = 0.75f; + } + ex.ent.model = cl_mod_explode; + ex.frames = 4; + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_LIGHTNING : + ent = CL.ParseLightning(cl_mod_lightning); + S.StartSound(null, ent, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0); + break; + + case TE_DEBUGTRAIL : + MSG.ReadPos(net_message, pos); + MSG.ReadPos(net_message, pos2); + CL.DebugTrail(pos, pos2); + break; + + case TE_PLAIN_EXPLOSION : + MSG.ReadPos(net_message, pos); + + ex = CL.AllocExplosion(); + VectorCopy(pos, ex.ent.origin); + ex.type = ex_poly; + ex.ent.flags = RF_FULLBRIGHT; + ex.start = cl.frame.servertime - 100; + ex.light = 350; + ex.lightcolor[0] = 1.0f; + ex.lightcolor[1] = 0.5f; + ex.lightcolor[2] = 0.5f; + ex.ent.angles[1] = rand() % 360; + ex.ent.model = cl_mod_explo4; + if (frand() < 0.5) + ex.baseframe = 15; + ex.frames = 15; + if (type == TE_ROCKET_EXPLOSION_WATER) + S.StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); + else + S.StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); + break; + + case TE_FLASHLIGHT : + MSG.ReadPos(net_message, pos); + ent = MSG.ReadShort(net_message); + CL.Flashlight(ent, pos); + break; + + case TE_FORCEWALL : + MSG.ReadPos(net_message, pos); + MSG.ReadPos(net_message, pos2); + color = MSG.ReadByte(net_message); + CL.ForceWall(pos, pos2, color); + break; + + case TE_HEATBEAM : + ent = CL.ParsePlayerBeam(cl_mod_heatbeam); + break; + + case TE_MONSTER_HEATBEAM : + ent = CL.ParsePlayerBeam(cl_mod_monster_heatbeam); + break; + + case TE_HEATBEAM_SPARKS : + // cnt = MSG.ReadByte (net_message); + cnt = 50; + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + // r = MSG.ReadByte (net_message); + // magnitude = MSG.ReadShort (net_message); + r = 8; + magnitude = 60; + color = r & 0xff; + CL.ParticleSteamEffect(pos, dir, color, cnt, magnitude); + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_HEATBEAM_STEAM : + // cnt = MSG.ReadByte (net_message); + cnt = 20; + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + // r = MSG.ReadByte (net_message); + // magnitude = MSG.ReadShort (net_message); + // color = r & 0xff; + color = 0xe0; + magnitude = 60; + CL.ParticleSteamEffect(pos, dir, color, cnt, magnitude); + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_STEAM : + CL.ParseSteam(); + break; + + case TE_BUBBLETRAIL2 : + // cnt = MSG.ReadByte (net_message); + cnt = 8; + MSG.ReadPos(net_message, pos); + MSG.ReadPos(net_message, pos2); + CL.BubbleTrail2(pos, pos2, cnt); + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_MOREBLOOD : + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + CL.ParticleEffect(pos, dir, 0xe8, 250); + break; + + case TE_CHAINFIST_SMOKE : + dir[0] = 0; + dir[1] = 0; + dir[2] = 1; + MSG.ReadPos(net_message, pos); + CL.ParticleSmokeEffect(pos, dir, 0, 20, 20); + break; + + case TE_ELECTRIC_SPARKS : + MSG.ReadPos(net_message, pos); + MSG.ReadDir(net_message, dir); + // CL_ParticleEffect (pos, dir, 109, 40); + CL.ParticleEffect(pos, dir, 0x75, 40); + //FIXME : replace or remove this sound + S.StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); + break; + + case TE_TRACKER_EXPLOSION : + MSG.ReadPos(net_message, pos); + CL.ColorFlash(pos, 0, 150, -1, -1, -1); + CL.ColorExplosionParticles(pos, 0, 1); + // CL_Tracker_Explode (pos); + S.StartSound(pos, 0, 0, cl_sfx_disrexp, 1, ATTN_NORM, 0); + break; + + case TE_TELEPORT_EFFECT : + case TE_DBALL_GOAL : + MSG.ReadPos(net_message, pos); + CL.TeleportParticles(pos); + break; + + case TE_WIDOWBEAMOUT : + CL.ParseWidow(); + break; + + case TE_NUKEBLAST : + ParseNuke(); + break; + + case TE_WIDOWSPLASH : + MSG.ReadPos(net_message, pos); + CL.WidowSplash(pos); + break; + // PGM + // ============== + + default : + Com.Error(ERR_DROP, "CL_ParseTEnt: bad type"); + } + } + + /* + ================= + CL_AddBeams + ================= + */ + static void AddBeams() { + int i, j; + beam_t[] b; + float[] dist = new float[3]; + float[] org = new float[3]; + float d; + entity_t ent = new entity_t(); + float yaw, pitch; + float forward; + float len, steps; + float model_length; + + // update beams + b = cl_beams; + for (i = 0; i < MAX_BEAMS; i++) { + if (b[i].model == null || b[i].endtime < cl.time) + continue; + + // if coming from the player, update the start position + if (b[i].entity == cl.playernum + 1) // entity 0 is the world + { + VectorCopy(cl.refdef.vieworg, b[i].start); + b[i].start[2] -= 22; // adjust for view height + } + VectorAdd(b[i].start, b[i].offset, org); + + // calculate pitch and yaw + VectorSubtract(b[i].end, org, dist); + + if (dist[1] == 0 && dist[0] == 0) { + yaw = 0; + if (dist[2] > 0) + pitch = 90; + else + pitch = 270; + } else { + // PMM - fixed to correct for pitch of 0 + if (dist[0] != 0.0f) + yaw = (float) (Math.atan2(dist[1], dist[0]) * 180 / Math.PI); + else if (dist[1] > 0) + yaw = 90; + else + yaw = 270; + if (yaw < 0) + yaw += 360; + + forward = (float)Math.sqrt(dist[0] * dist[0] + dist[1] * dist[1]); + pitch = (float) (Math.atan2(dist[2], forward) * -180.0 / Math.PI); + if (pitch < 0) + pitch += 360.0; + } + + // add new entities for the beams + d = VectorNormalize(dist); + + //memset (&ent, 0, sizeof(ent)); + ent = new entity_t(); + if (b[i].model == cl_mod_lightning) { + model_length = 35.0f; + d -= 20.0; // correction so it doesn't end in middle of tesla + } else { + model_length = 30.0f; + } + steps = (float)Math.ceil(d / model_length); + len = (d - model_length) / (steps - 1); + + // PMM - special case for lightning model .. if the real length is shorter than the model, + // flip it around & draw it from the end to the start. This prevents the model from going + // through the tesla mine (instead it goes through the target) + if ((b[i].model == cl_mod_lightning) && (d <= model_length)) { + // Com_Printf ("special case\n"); + VectorCopy(b[i].end, ent.origin); + // offset to push beam outside of tesla model (negative because dist is from end to start + // for this beam) + // for (j=0 ; j<3 ; j++) + // ent.origin[j] -= dist[j]*10.0; + ent.model = b[i].model; + ent.flags = RF_FULLBRIGHT; + ent.angles[0] = pitch; + ent.angles[1] = yaw; + ent.angles[2] = rand() % 360; + V.AddEntity(ent); + return; + } + while (d > 0) { + VectorCopy(org, ent.origin); + ent.model = b[i].model; + if (b[i].model == cl_mod_lightning) { + ent.flags = RF_FULLBRIGHT; + ent.angles[0] = -pitch; + ent.angles[1] = yaw + 180.0f; + ent.angles[2] = rand() % 360; + } else { + ent.angles[0] = pitch; + ent.angles[1] = yaw; + ent.angles[2] = rand() % 360; + } + + // Com_Printf("B: %d . %d\n", b[i].entity, b[i].dest_entity); + V.AddEntity(ent); + + for (j = 0; j < 3; j++) + org[j] += dist[j] * len; + d -= model_length; + } + } + } + + //extern cvar_t *hand; + + /* + ================= + ROGUE - draw player locked beams + CL_AddPlayerBeams + ================= + */ + static void AddPlayerBeams() { + int i, j; + beam_t[] b; + float[] dist = new float[3]; + float[] org = new float[3]; + float d; + entity_t ent = new entity_t(); + float yaw, pitch; + float forward; + float len, steps; + int framenum = 0; + float model_length; + + float hand_multiplier; + frame_t oldframe; + player_state_t ps, ops; + + // PMM + if (hand != null) { + if (hand.value == 2) + hand_multiplier = 0; + else if (hand.value == 1) + hand_multiplier = -1; + else + hand_multiplier = 1; + } else { + hand_multiplier = 1; + } + // PMM + + // update beams + b = cl_playerbeams; + for (i = 0; i < MAX_BEAMS; i++) { + float[] f = new float[3]; + float[] u = new float[3]; + float[] r = new float[3]; + if (b[i].model == null || b[i].endtime < cl.time) + continue; + + if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) { + + // if coming from the player, update the start position + if (b[i].entity == cl.playernum + 1) // entity 0 is the world + { + // set up gun position + // code straight out of CL_AddViewWeapon + ps = cl.frame.playerstate; + j = (cl.frame.serverframe - 1) & UPDATE_MASK; + oldframe = cl.frames[j]; + + if (oldframe.serverframe != cl.frame.serverframe - 1 || !oldframe.valid) + oldframe = cl.frame; // previous frame was dropped or involid + + ops = oldframe.playerstate; + for (j = 0; j < 3; j++) { + b[i].start[j] = + cl.refdef.vieworg[j] + + ops.gunoffset[j] + + cl.lerpfrac * (ps.gunoffset[j] - ops.gunoffset[j]); + } + VectorMA(b[i].start, (hand_multiplier * b[i].offset[0]), cl.v_right, org); + VectorMA(org, b[i].offset[1], cl.v_forward, org); + VectorMA(org, b[i].offset[2], cl.v_up, org); + if ((hand != null) && (hand.value == 2)) { + VectorMA(org, -1, cl.v_up, org); + } + // FIXME - take these out when final + VectorCopy(cl.v_right, r); + VectorCopy(cl.v_forward, f); + VectorCopy(cl.v_up, u); + + } else + VectorCopy(b[i].start, org); + } else { + // if coming from the player, update the start position + if (b[i].entity == cl.playernum + 1) // entity 0 is the world + { + VectorCopy(cl.refdef.vieworg, b[i].start); + b[i].start[2] -= 22; // adjust for view height + } + VectorAdd(b[i].start, b[i].offset, org); + } + + // calculate pitch and yaw + VectorSubtract(b[i].end, org, dist); + + // PMM + if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam) && (b[i].entity == cl.playernum + 1)) { + + len = VectorLength(dist); + VectorScale(f, len, dist); + VectorMA(dist, (hand_multiplier * b[i].offset[0]), r, dist); + VectorMA(dist, b[i].offset[1], f, dist); + VectorMA(dist, b[i].offset[2], u, dist); + if ((hand != null) && (hand.value == 2)) { + VectorMA(org, -1, cl.v_up, org); + } + } + // PMM + + if (dist[1] == 0 && dist[0] == 0) { + yaw = 0; + if (dist[2] > 0) + pitch = 90; + else + pitch = 270; + } else { + // PMM - fixed to correct for pitch of 0 + if (dist[0] != 0.0f) + yaw = (float) (Math.atan2(dist[1], dist[0]) * 180 / Math.PI); + else if (dist[1] > 0) + yaw = 90; + else + yaw = 270; + if (yaw < 0) + yaw += 360; + + forward = (float)Math.sqrt(dist[0] * dist[0] + dist[1] * dist[1]); + pitch = (float) (Math.atan2(dist[2], forward) * -180.0 / Math.PI); + if (pitch < 0) + pitch += 360.0; + } + + if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) { + if (b[i].entity != cl.playernum + 1) { + framenum = 2; + // Com_Printf ("Third person\n"); + ent.angles[0] = -pitch; + ent.angles[1] = yaw + 180.0f; + ent.angles[2] = 0; + // Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0, b[i].offset[0], b[i].offset[1], b[i].offset[2]); + AngleVectors(ent.angles, f, r, u); + + // if it's a non-origin offset, it's a player, so use the hardcoded player offset + if (VectorCompare(b[i].offset, vec3_origin) == 0) { + VectorMA(org, - (b[i].offset[0]) + 1, r, org); + VectorMA(org, - (b[i].offset[1]), f, org); + VectorMA(org, - (b[i].offset[2]) - 10, u, org); + } else { + // if it's a monster, do the particle effect + CL.MonsterPlasma_Shell(b[i].start); + } + } else { + framenum = 1; + } + } + + // if it's the heatbeam, draw the particle effect + if ((cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam) && (b[i].entity == cl.playernum + 1))) { + CL.Heatbeam(org, dist); + } + + // add new entities for the beams + d = VectorNormalize(dist); + + //memset (&ent, 0, sizeof(ent)); + ent = new entity_t(); + if (b[i].model == cl_mod_heatbeam) { + model_length = 32.0f; + } else if (b[i].model == cl_mod_lightning) { + model_length = 35.0f; + d -= 20.0; // correction so it doesn't end in middle of tesla + } else { + model_length = 30.0f; + } + steps = (float)Math.ceil(d / model_length); + len = (d - model_length) / (steps - 1); + + // PMM - special case for lightning model .. if the real length is shorter than the model, + // flip it around & draw it from the end to the start. This prevents the model from going + // through the tesla mine (instead it goes through the target) + if ((b[i].model == cl_mod_lightning) && (d <= model_length)) { + // Com_Printf ("special case\n"); + VectorCopy(b[i].end, ent.origin); + // offset to push beam outside of tesla model (negative because dist is from end to start + // for this beam) + // for (j=0 ; j<3 ; j++) + // ent.origin[j] -= dist[j]*10.0; + ent.model = b[i].model; + ent.flags = RF_FULLBRIGHT; + ent.angles[0] = pitch; + ent.angles[1] = yaw; + ent.angles[2] = rand() % 360; + V.AddEntity(ent); + return; + } + while (d > 0) { + VectorCopy(org, ent.origin); + ent.model = b[i].model; + if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) { + // ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT; + // ent.alpha = 0.3; + ent.flags = RF_FULLBRIGHT; + ent.angles[0] = -pitch; + ent.angles[1] = yaw + 180.0f; + ent.angles[2] = (cl.time) % 360; + // ent.angles[2] = rand()%360; + ent.frame = framenum; + } else if (b[i].model == cl_mod_lightning) { + ent.flags = RF_FULLBRIGHT; + ent.angles[0] = -pitch; + ent.angles[1] = yaw + 180.0f; + ent.angles[2] = rand() % 360; + } else { + ent.angles[0] = pitch; + ent.angles[1] = yaw; + ent.angles[2] = rand() % 360; + } + + // Com_Printf("B: %d . %d\n", b[i].entity, b[i].dest_entity); + V.AddEntity(ent); + + for (j = 0; j < 3; j++) + org[j] += dist[j] * len; + d -= model_length; + } + } + } + + /* + ================= + CL_AddExplosions + ================= + */ + static void AddExplosions() { + entity_t ent; + int i; + explosion_t[] ex; + float frac; + int f; + + //memset (&ent, 0, sizeof(ent)); Pointer! + ent = null; + ex = cl_explosions; + for (i = 0; i < MAX_EXPLOSIONS; i++) { + if (ex[i].type == ex_free) + continue; + frac = (cl.time - ex[i].start) / 100.0f; + f = (int)Math.floor(frac); + + ent = ex[i].ent; + + switch (ex[i].type) { + case ex_mflash : + if (f >= ex[i].frames - 1) + ex[i].type = ex_free; + break; + case ex_misc : + if (f >= ex[i].frames - 1) { + ex[i].type = ex_free; + break; + } + ent.alpha = 1.0f - frac / (ex[i].frames - 1); + break; + case ex_flash : + if (f >= 1) { + ex[i].type = ex_free; + break; + } + ent.alpha = 1.0f; + break; + case ex_poly : + if (f >= ex[i].frames - 1) { + ex[i].type = ex_free; + break; + } + + ent.alpha = (16.0f - (float)f) / 16.0f; + + if (f < 10) { + ent.skinnum = (f >> 1); + if (ent.skinnum < 0) + ent.skinnum = 0; + } else { + ent.flags |= RF_TRANSLUCENT; + if (f < 13) + ent.skinnum = 5; + else + ent.skinnum = 6; + } + break; + case ex_poly2 : + if (f >= ex[i].frames - 1) { + ex[i].type = ex_free; + break; + } + + ent.alpha = (5.0f - (float)f) / 5.0f; + ent.skinnum = 0; + ent.flags |= RF_TRANSLUCENT; + break; + } + + if (ex[i].type == ex_free) + continue; + if (ex[i].light != 0.0f) { + V.AddLight( + ent.origin, + ex[i].light * ent.alpha, + ex[i].lightcolor[0], + ex[i].lightcolor[1], + ex[i].lightcolor[2]); + } + + VectorCopy(ent.origin, ent.oldorigin); + + if (f < 0) + f = 0; + ent.frame = ex[i].baseframe + f + 1; + ent.oldframe = ex[i].baseframe + f; + ent.backlerp = 1.0f - cl.lerpfrac; + + V.AddEntity(ent); + } + } + + /* + ================= + CL_AddLasers + ================= + */ + static void AddLasers() { + laser_t[] l; + int i; + + l = cl_lasers; + for (i = 0; i < MAX_LASERS; i++) { + if (l[i].endtime >= cl.time) + V.AddEntity(l[i].ent); + } + } + + /* PMM - CL_Sustains */ + static void ProcessSustain() { + cl_sustain_t[] s; + int i; + + s = cl_sustains; + for (i = 0; i < MAX_SUSTAINS; i++) { + if (s[i].id != 0) + if ((s[i].endtime >= cl.time) && (cl.time >= s[i].nextthink)) { + s[i].think.think(s[i]); + } else if (s[i].endtime < cl.time) + s[i].id = 0; + } + } + + /* + ================= + CL_AddTEnts + ================= + */ + static void AddTEnts() { + CL.AddBeams(); + // PMM - draw plasma beams + CL.AddPlayerBeams(); + CL.AddExplosions(); + CL.AddLasers(); + // PMM - set up sustain + CL.ProcessSustain(); + } +} diff --git a/src/jake2/client/CL_view.java b/src/jake2/client/CL_view.java new file mode 100644 index 0000000..2d2e7ab --- /dev/null +++ b/src/jake2/client/CL_view.java @@ -0,0 +1,189 @@ +/* + * CL_view.java + * Copyright (C) 2004 + * + * $Id: CL_view.java,v 1.1 2004-07-07 19:58:40 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 java.util.StringTokenizer; + +import jake2.qcommon.CM; +import jake2.qcommon.Com; +import jake2.qcommon.xcommand_t; +import jake2.sys.Sys; +import jake2.util.Vargs; + + + + + +public class CL_view extends CL_input { + + static int num_cl_weaponmodels; + static String[] cl_weaponmodels = new String[MAX_CLIENTWEAPONMODELS]; + + + /* + ================= + CL_PrepRefresh + + Call before entering a new level, or after changing dlls + ================= + */ + + static void PrepRefresh() { + re.updateScreen(new xcommand_t() { + public void execute() { + PrepRefresh2(); + } + }); + } + + static void PrepRefresh2() { + String mapname; + int i; + String name; + float rotate; + float[] axis = new float[3]; + + if ((i=cl.configstrings[CS_MODELS+1].length()) == 0) + return; // no map loaded + + SCR.AddDirtyPoint(0, 0); + SCR.AddDirtyPoint(viddef.width-1, viddef.height-1); + + // let the render dll load the map + mapname = cl.configstrings[CS_MODELS+1].substring(5, i - 4); // skip "maps/" + // cut off ".bsp" + + // register models, pics, and skins + Com.Printf("Map: " + mapname + "\r"); + SCR.UpdateScreen2(); + re.BeginRegistration(mapname); + Com.Printf(" \r"); + + // precache status bar pics + Com.Printf("pics\r"); + SCR.UpdateScreen2(); + SCR.TouchPics(); + Com.Printf(" \r"); + + CL.RegisterTEntModels(); + + num_cl_weaponmodels = 1; + cl_weaponmodels[0] = "weapon.md2"; + + for (i=1 ; i<MAX_MODELS && cl.configstrings[CS_MODELS+i].length() != 0 ; i++) { + name = new String(cl.configstrings[CS_MODELS+i]); + if (name.length() > 37) name = name.substring(0, 36); + + /* + if (name.charAt(0) != '*') + Com.Printf("name" + "\r"); + */ + SCR.UpdateScreen2(); + Sys.SendKeyEvents(); // pump message loop + if (name.charAt(0) == '#') { + // special player weapon model + if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS) { + cl_weaponmodels[num_cl_weaponmodels] = cl.configstrings[CS_MODELS+i].substring(1); + num_cl_weaponmodels++; + } + } else { + cl.model_draw[i] = re.RegisterModel(cl.configstrings[CS_MODELS+i]); + if (name.charAt(0) == '*') + cl.model_clip[i] = CM.InlineModel(cl.configstrings[CS_MODELS+i]); + else + cl.model_clip[i] = null; + } + if (name.charAt(0) != '*') + Com.Printf(" \r"); + } + + Com.Printf("images\r"); + SCR.UpdateScreen2(); + for (i=1 ; i<MAX_IMAGES && cl.configstrings[CS_IMAGES+i].length() > 0 ; i++) { + cl.image_precache[i] = re.RegisterPic(cl.configstrings[CS_IMAGES+i]); + Sys.SendKeyEvents(); // pump message loop + } + + Com.Printf(" \r"); + for (i=0 ; i<MAX_CLIENTS ; i++) { + if (cl.configstrings[CS_PLAYERSKINS+i].length() == 0) + continue; + Com.Printf("client %i\r", new Vargs(1).add(i)); + SCR.UpdateScreen2(); + Sys.SendKeyEvents(); // pump message loop + CL.ParseClientinfo(i); + Com.Printf(" \r"); + } + + CL_parse.LoadClientinfo(cl.baseclientinfo, "unnamed\\male/grunt"); + + // set sky textures and speed + Com.Printf("sky\r"); + SCR.UpdateScreen2(); + rotate = Float.parseFloat(cl.configstrings[CS_SKYROTATE]); + StringTokenizer st = new StringTokenizer(cl.configstrings[CS_SKYAXIS]); + axis[0] = Float.parseFloat(st.nextToken()); + axis[1] = Float.parseFloat(st.nextToken()); + axis[2] = Float.parseFloat(st.nextToken()); + re.SetSky(cl.configstrings[CS_SKY], rotate, axis); + Com.Printf(" \r"); + + // the renderer can now free unneeded stuff + re.EndRegistration (); + + // clear any lines of console text + Console.ClearNotify(); + + SCR.UpdateScreen2(); + cl.refresh_prepped = true; + cl.force_refdef = true; // make sure we have a valid refdef + } + + public static void AddNetgraph() { + int i; + int in; + int ping; + + // if using the debuggraph for something else, don't + // add the net lines + if (SCR.scr_debuggraph.value == 0.0f || SCR.scr_timegraph.value == 0.0f) + return; + + for (i=0 ; i<cls.netchan.dropped ; i++) + SCR.DebugGraph(30, 0x40); + + for (i=0 ; i<cl.surpressCount ; i++) + SCR.DebugGraph(30, 0xdf); + + // see what the latency was on this packet + in = cls.netchan.incoming_acknowledged & (CMD_BACKUP-1); + ping = (int)(cls.realtime - cl.cmd_time[in]); + ping /= 30; + if (ping > 30) + ping = 30; + SCR.DebugGraph (ping, 0xd0); + } +} diff --git a/src/jake2/client/Console.java b/src/jake2/client/Console.java new file mode 100644 index 0000000..d37bf5c --- /dev/null +++ b/src/jake2/client/Console.java @@ -0,0 +1,610 @@ +/* + * Con.java + * Copyright (C) 2003 + * + * $Id: Console.java,v 1.1 2004-07-07 19:58:41 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 java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Arrays; + +import jake2.Defines; +import jake2.Globals; +import jake2.game.Cmd; +import jake2.qcommon.*; +import jake2.util.Vargs; + +/** + * Console + */ +public final class Console extends Globals { + + public static xcommand_t ToggleConsole_f = new xcommand_t() { + public void execute() { + SCR.EndLoadingPlaque(); // get rid of loading plaque + + if (Globals.cl.attractloop) { + Cbuf.AddText("killserver\n"); + return; + } + + if (Globals.cls.state == Defines.ca_disconnected) { + // start the demo loop again + Cbuf.AddText("d1\n"); + return; + } + + Key.ClearTyping(); + Console.ClearNotify(); + + if (Globals.cls.key_dest == Defines.key_console) { + Menu.ForceMenuOff(); + Cvar.Set("paused", "0"); + } + else { + Menu.ForceMenuOff(); + Globals.cls.key_dest = Defines.key_console; + + if (Cvar.VariableValue("maxclients") == 1 && Com.ServerState()!= 0) + Cvar.Set("paused", "1"); + } + } + }; + + public static xcommand_t Clear_f = new xcommand_t() { + public void execute() { + Arrays.fill(Globals.con.text, (byte)' '); + } + }; + public static xcommand_t Dump_f = new xcommand_t() { + public void execute() { + + int l, x; + int line; + RandomAccessFile f; + byte[] buffer = new byte[1024]; + String name; + + if (Cmd.Argc() != 2) { + Com.Printf("usage: condump <filename>\n"); + return; + } + + //Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1)); + name = FS.Gamedir() + "/" + Cmd.Argv(1) + ".txt"; + + Com.Printf("Dumped console text to " + name + ".\n"); + FS.CreatePath(name); + f = fopen(name, "rw"); + if (f == null) { + Com.Printf("ERROR: couldn't open.\n"); + return; + } + + // skip empty lines + for (l = con.current - con.totallines + 1; l <= con.current; l++) { + line = (l % con.totallines) * con.linewidth; + for (x = 0; x < con.linewidth; x++) + if (con.text[line + x] != ' ') + break; + if (x != con.linewidth) + break; + } + + // write the remaining lines + buffer[con.linewidth] = 0; + for (; l <= con.current; l++) { + line = (l % con.totallines) * con.linewidth; + //strncpy (buffer, line, con.linewidth); + System.arraycopy(con.text, line, buffer, 0, con.linewidth); + for (x = con.linewidth - 1; x >= 0; x--) { + if (buffer[x] == ' ') + buffer[x] = 0; + else + break; + } + for (x = 0; buffer[x] != 0; x++) + buffer[x] &= 0x7f; + + buffer[x] = '\n'; + // fprintf (f, "%s\n", buffer); + try { + f.write(buffer, 0, x+1); + } catch (IOException e) {} + } + + fclose(f); + + } + }; + + /** + * + */ + public static void Init() { + Globals.con.linewidth = -1; + + CheckResize(); + + Com.Printf("Console initialized.\n"); + + // + // register our commands + // + Globals.con_notifytime = Cvar.Get("con_notifytime", "3", 0); + + Cmd.AddCommand("toggleconsole", ToggleConsole_f); + Cmd.AddCommand("togglechat", ToggleChat_f); + Cmd.AddCommand("messagemode", MessageMode_f); + Cmd.AddCommand("messagemode2", MessageMode2_f); + Cmd.AddCommand("clear", Clear_f); + Cmd.AddCommand("condump", Dump_f); + Globals.con.initialized = true; + } + + /** + * If the line width has changed, reformat the buffer. + */ + public static void CheckResize() { + int i, j, width, oldwidth, oldtotallines, numlines, numchars; + byte[] tbuf = new byte[Defines.CON_TEXTSIZE]; + + width = (Globals.viddef.width >> 3) - 2; + + if (width == Globals.con.linewidth) + return; + + if (width < 1) { // video hasn't been initialized yet + width = 38; + Globals.con.linewidth = width; + Globals.con.totallines = Defines.CON_TEXTSIZE / Globals.con.linewidth; + Arrays.fill(Globals.con.text, (byte)' '); + } + else { + oldwidth = Globals.con.linewidth; + Globals.con.linewidth = width; + oldtotallines = Globals.con.totallines; + Globals.con.totallines = Defines.CON_TEXTSIZE / Globals.con.linewidth; + numlines = oldtotallines; + + if (Globals.con.totallines < numlines) + numlines = Globals.con.totallines; + + numchars = oldwidth; + + if (Globals.con.linewidth < numchars) + numchars = Globals.con.linewidth; + + System.arraycopy(Globals.con.text, 0, tbuf, 0, Defines.CON_TEXTSIZE); + Arrays.fill(Globals.con.text, (byte)' '); + + for (i=0 ; i<numlines ; i++) { + for (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<Defines.NUM_CON_TIMES ; i++) + Globals.con.times[i] = 0; + } + + static void DrawString(int x, int y, String s) { + for (int i = 0; i < s.length(); i++) { + Globals.re.DrawChar(x, y, s.charAt(i)); + x+=8; + } + } + + static void DrawAltString(int x, int y, String s) { + for (int i = 0; i < s.length(); i++) { + Globals.re.DrawChar(x, y, s.charAt(i) ^ 0x80); + x+=8; + } + } + + + /* + ================ + Con_ToggleChat_f + ================ + */ + static xcommand_t ToggleChat_f = new xcommand_t() { + 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 xcommand_t MessageMode_f = new xcommand_t() { + public void execute() { + chat_team = false; + cls.key_dest = key_message; + } + }; + + /* + ================ + Con_MessageMode2_f + ================ + */ + static xcommand_t MessageMode2_f = new xcommand_t() { + 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 y; + 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; + + for (i=0 ; i<con.linewidth ; i++) + re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i]); + + // 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; + + for (x = 0 ; x < con.linewidth ; x++) + re.DrawChar( (x+1)<<3, v, con.text[text+x]); + + v += 8; + } + + if (cls.key_dest == key_message) { + if (chat_team) { + DrawString(8, v, "say_team:"); + skip = 11; + } else { + 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))); + + for (x = 0; x < s.length(); x++) { + re.DrawChar( (x+skip)<<3, v, s.charAt(x)); + } + re.DrawChar( (x+skip)<<3, v, (int)(10+((cls.realtime>>8)&1))); + v += 8; + } + + if (v != 0) { + SCR.AddDirtyPoint(0,0); + SCR.AddDirtyPoint(viddef.width-1, v); + } + } + + /* + ================ + Con_DrawConsole + + Draws the console with the solid background + ================ + */ + static void DrawConsole(float frac) { + int i, j, x, y, n; + int rows; + int text; + int row; + int lines; + String version; + String dlbar; + + 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"); + SCR.AddDirtyPoint(0,0); + SCR.AddDirtyPoint(viddef.width-1,lines-1); + + version = Com.sprintf("v%4.2f", new Vargs(1).add(VERSION)); + for (x=0 ; x<5 ; x++) + re.DrawChar (viddef.width-44+x*8, lines-12, 128 + version.charAt(x)); + + // 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 + for (x=0 ; x<con.linewidth ; x+=4) + re.DrawChar ( (x+1)<<3, 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; + + for (x=0 ; x<con.linewidth ; x++) + re.DrawChar( (x+1)<<3, y, con.text[x+first]); + } + + //ZOID + // draw the download bar + // figure out width + if (cls.download != null) { + if ((text = cls.downloadname.lastIndexOf('/')) != 0) + text++; + else + text = 0; + + x = con.linewidth - ((con.linewidth * 7) / 40); + y = x - (cls.downloadname.length()-text) - 8; + i = con.linewidth/3; + if (cls.downloadname.length()-text > i) { + y = x - i - 11; + int end = text + i - 1;; + dlbar = cls.downloadname.substring(text, end); + dlbar += "..."; + } else { + dlbar = cls.downloadname.substring(text); + } + dlbar += ": "; + i = strlen(dlbar); + dlbar += (char)0x80; + i++; + + // where's the dot go? + if (cls.downloadpercent == 0) + n = 0; + else + n = y * cls.downloadpercent / 100; + + StringBuffer sb = new StringBuffer(dlbar); + sb.ensureCapacity(1024); + for (j = 0; j < y; j++) { + if (j == n) + sb.append((char)0x83); + else + sb.append((char)0x81); + } + sb.append((char)0x82); + + dlbar = sb.toString(); + dlbar += Com.sprintf(" %02d%%", new Vargs(1).add(cls.downloadpercent)); + + // draw it + y = con.vislines-12; + for (i = 0; i < dlbar.length(); i++) + re.DrawChar ( (i+1)<<3, y, dlbar.charAt(i)); + } + //ZOID + + // draw the input prompt, user text, and cursor if desired + DrawInput(); + } +} diff --git a/src/jake2/client/Key.java b/src/jake2/client/Key.java new file mode 100644 index 0000000..33b25a2 --- /dev/null +++ b/src/jake2/client/Key.java @@ -0,0 +1,812 @@ +/* + * Key.java + * Copyright (C) 2003 + * + * $Id: Key.java,v 1.1 2004-07-07 19:58:42 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 java.io.IOException; +import java.io.RandomAccessFile; + +import jake2.Defines; +import jake2.Globals; +import jake2.game.Cmd; +import jake2.qcommon.*; +import jake2.qcommon.Cbuf; +import jake2.qcommon.Com; +import jake2.util.Lib; + +/** + * Key + */ +public class Key extends Globals { + // + // these are the key numbers that should be passed to Key_Event + // + public static final int K_TAB = 9; + public static final int K_ENTER = 13; + public static final int K_ESCAPE = 27; + public static final int K_SPACE = 32; + + // normal keys should be passed as lowercased ascii + + public static final int K_BACKSPACE = 127; + public static final int K_UPARROW = 128; + public static final int K_DOWNARROW = 129; + public static final int K_LEFTARROW = 130; + public static final int K_RIGHTARROW = 131; + + public static final int K_ALT = 132; + public static final int K_CTRL = 133; + public static final int K_SHIFT = 134; + public static final int K_F1 = 135; + public static final int K_F2 = 136; + public static final int K_F3 = 137; + public static final int K_F4 = 138; + public static final int K_F5 = 139; + public static final int K_F6 = 140; + public static final int K_F7 = 141; + public static final int K_F8 = 142; + public static final int K_F9 = 143; + public static final int K_F10 = 144; + public static final int K_F11 = 145; + public static final int K_F12 = 146; + public static final int K_INS = 147; + public static final int K_DEL = 148; + public static final int K_PGDN = 149; + public static final int K_PGUP = 150; + public static final int K_HOME = 151; + public static final int K_END = 152; + + public static final int K_KP_HOME = 160; + public static final int K_KP_UPARROW = 161; + public static final int K_KP_PGUP = 162; + public static final int K_KP_LEFTARROW = 163; + public static final int K_KP_5 = 164; + public static final int K_KP_RIGHTARROW = 165; + public static final int K_KP_END = 166; + public static final int K_KP_DOWNARROW = 167; + public static final int K_KP_PGDN = 168; + public static final int K_KP_ENTER = 169; + public static final int K_KP_INS = 170; + public static final int K_KP_DEL = 171; + public static final int K_KP_SLASH = 172; + public static final int K_KP_MINUS = 173; + public static final int K_KP_PLUS = 174; + + public static final int K_PAUSE = 255; + + // + // mouse buttons generate virtual keys + // + public static final int K_MOUSE1 = 200; + public static final int K_MOUSE2 = 201; + public static final int K_MOUSE3 = 202; + + // + // joystick buttons + // + public static final int K_JOY1 = 203; + public static final int K_JOY2 = 204; + public static final int K_JOY3 = 205; + public static final int K_JOY4 = 206; + + public static final int K_MWHEELDOWN = 239; + public static final int K_MWHEELUP = 240; + + static int key_waiting; + static int history_line = 0; + static boolean shift_down = false; + static int[] key_repeats = new int[256]; + static int[] keyshift = new int[256]; + static boolean[] menubound = new boolean[256]; + static boolean[] consolekeys = new boolean[256]; + + static String[] keynames = new String[256]; + + static { + keynames[K_TAB] = "TAB"; + keynames[K_ENTER] = "ENTER"; + keynames[K_ESCAPE] = "ESCAPE"; + keynames[K_SPACE] = "SPACE"; + keynames[K_BACKSPACE] = "BACKSPACE"; + keynames[K_UPARROW] = "UPARROW"; + keynames[K_DOWNARROW] = "DOWNARROW"; + keynames[K_LEFTARROW] = "LEFTARROW"; + keynames[K_RIGHTARROW] = "RIGHTARROW"; + keynames[K_ALT] = "ALT"; + keynames[K_CTRL] = "CTRL"; + keynames[K_SHIFT] = "SHIFT"; + + keynames[K_F1] = "F1"; + keynames[K_F2] = "F2"; + keynames[K_F3] = "F3"; + keynames[K_F4] = "F4"; + keynames[K_F5] = "F5"; + keynames[K_F6] = "F6"; + keynames[K_F7] = "F7"; + keynames[K_F8] = "F8"; + keynames[K_F9] = "F9"; + keynames[K_F10] = "F10"; + keynames[K_F11] = "F11"; + keynames[K_F12] = "F12"; + + keynames[K_INS] = "INS"; + keynames[K_DEL] = "DEL"; + keynames[K_PGDN] = "PGDN"; + keynames[K_PGUP] = "PGUP"; + keynames[K_HOME] = "HOME"; + keynames[K_END] = "END"; + + keynames[K_MOUSE1] = "MOUSE1"; + keynames[K_MOUSE2] = "MOUSE2"; + keynames[K_MOUSE3] = "MOUSE3"; + + // 00092 {"JOY1", K_JOY1}, + // 00093 {"JOY2", K_JOY2}, + // 00094 {"JOY3", K_JOY3}, + // 00095 {"JOY4", K_JOY4}, + + keynames[K_KP_HOME] = "KP_HOME"; + keynames[K_KP_UPARROW] = "KP_UPARROW"; + keynames[K_KP_PGUP] = "KP_PGUP"; + keynames[K_KP_LEFTARROW] = "KP_LEFTARROW"; + keynames[K_KP_5] = "KP_5"; + keynames[K_KP_RIGHTARROW] = "KP_RIGHTARROW"; + keynames[K_KP_END] = "KP_END"; + keynames[K_KP_DOWNARROW] = "KP_DOWNARROW"; + keynames[K_KP_PGDN] = "KP_PGDN"; + keynames[K_KP_ENTER] = "KP_ENTER"; + keynames[K_KP_INS] = "KP_INS"; + keynames[K_KP_DEL] = "KP_DEL"; + keynames[K_KP_SLASH] = "KP_SLASH"; + + keynames[K_KP_PLUS] = "KP_PLUS"; + keynames[K_KP_MINUS] = "KP_MINUS"; + + keynames[K_MWHEELUP] = "MWHEELUP"; + keynames[K_MWHEELDOWN] = "MWHEELDOWN"; + + keynames[K_PAUSE] = "PAUSE"; + keynames[';'] = "SEMICOLON"; // because a raw semicolon seperates commands + + keynames[0] = "NULL"; + } + + /** + * + */ + public static void Init() { + for (int i = 0; i < 32; i++) { + Globals.key_lines[i][0] = ']'; + Globals.key_lines[i][1] = 0; + } + Globals.key_linepos = 1; + + // + // init ascii characters in console mode + // + for (int i = 32; i < 128; i++) + consolekeys[i] = true; + consolekeys[K_ENTER] = true; + consolekeys[K_KP_ENTER] = true; + consolekeys[K_TAB] = true; + consolekeys[K_LEFTARROW] = true; + consolekeys[K_KP_LEFTARROW] = true; + consolekeys[K_RIGHTARROW] = true; + consolekeys[K_KP_RIGHTARROW] = true; + consolekeys[K_UPARROW] = true; + consolekeys[K_KP_UPARROW] = true; + consolekeys[K_DOWNARROW] = true; + consolekeys[K_KP_DOWNARROW] = true; + consolekeys[K_BACKSPACE] = true; + consolekeys[K_HOME] = true; + consolekeys[K_KP_HOME] = true; + consolekeys[K_END] = true; + consolekeys[K_KP_END] = true; + consolekeys[K_PGUP] = true; + consolekeys[K_KP_PGUP] = true; + consolekeys[K_PGDN] = true; + consolekeys[K_KP_PGDN] = true; + consolekeys[K_SHIFT] = true; + consolekeys[K_INS] = true; + consolekeys[K_KP_INS] = true; + consolekeys[K_KP_DEL] = true; + consolekeys[K_KP_SLASH] = true; + consolekeys[K_KP_PLUS] = true; + consolekeys[K_KP_MINUS] = true; + consolekeys[K_KP_5] = true; + + consolekeys['`'] = false; + consolekeys['~'] = false; + + for (int i = 0; i < 256; i++) + keyshift[i] = i; + for (int i = 'a'; i <= 'z'; i++) + keyshift[i] = i - 'a' + 'A'; + keyshift['1'] = '!'; + keyshift['2'] = '@'; + keyshift['3'] = '#'; + keyshift['4'] = '$'; + keyshift['5'] = '%'; + keyshift['6'] = '^'; + keyshift['7'] = '&'; + keyshift['8'] = '*'; + keyshift['9'] = '('; + keyshift['0'] = ')'; + keyshift['-'] = '_'; + keyshift['='] = '+'; + keyshift[','] = '<'; + keyshift['.'] = '>'; + keyshift['/'] = '?'; + keyshift[';'] = ':'; + keyshift['\''] = '"'; + keyshift['['] = '{'; + keyshift[']'] = '}'; + keyshift['`'] = '~'; + keyshift['\\'] = '|'; + + menubound[K_ESCAPE] = true; + for (int i = 0; i < 12; i++) + menubound[K_F1 + i] = true; + + // + // register our functions + // + Cmd.AddCommand("bind", Key.Bind_f); + Cmd.AddCommand("unbind", Key.Unbind_f); + Cmd.AddCommand("unbindall", Key.Unbindall_f); + Cmd.AddCommand("bindlist", Key.Bindlist_f); + } + + public static void ClearTyping() { + Globals.key_lines[Globals.edit_line][1] = 0; // clear any typing + Globals.key_linepos = 1; + } + + /** + * Called by the system between frames for both key up and key down events. + */ + public static void Event(int key, boolean down, long time) { + //System.out.println(key + " " + down); + //return; + String kb; + String cmd; + + // hack for modal presses + if (key_waiting == -1) { + if (down) + key_waiting = key; + return; + } + + // update auto-repeat status + if (down) { + key_repeats[key]++; + if (key != K_BACKSPACE + && key != K_PAUSE + && key != K_PGUP + && key != K_KP_PGUP + && key != K_PGDN + && key != K_KP_PGDN + && key_repeats[key] > 1) + return; // ignore most autorepeats + + if (key >= 200 && Globals.keybindings[key] == null) + Com.Printf(Key.KeynumToString(key) + " is unbound, hit F4 to set.\n"); + } + else { + key_repeats[key] = 0; + } + + if (key == K_SHIFT) + shift_down = down; + + // console key is hardcoded, so the user can never unbind it + if (key == '`' || key == '~') { + if (!down) + return; + try { + Console.ToggleConsole_f.execute(); + } + catch (Exception e) { + } + return; + } + + // any key during the attract mode will bring up the menu + if (Globals.cl.attractloop && Globals.cls.key_dest != Defines.key_menu && !(key >= K_F1 && key <= K_F12)) + key = K_ESCAPE; + + // menu key is hardcoded, so the user can never unbind it + if (key == K_ESCAPE) { + if (!down) + return; + + if (Globals.cl.frame.playerstate.stats[Defines.STAT_LAYOUTS] != 0 && Globals.cls.key_dest == Defines.key_game) { + // put away help computer / inventory + Cbuf.AddText("cmd putaway\n"); + return; + } + switch (Globals.cls.key_dest) { + case Defines.key_message : + Key.Message(key); + break; + case Defines.key_menu : + Menu.Keydown(key); + break; + case Defines.key_game : + case Defines.key_console : + Menu.Menu_Main_f(); + break; + default : + Com.Error(Defines.ERR_FATAL, "Bad cls.key_dest"); + } + return; + } + + // track if any key is down for BUTTON_ANY + Globals.keydown[key] = down; + if (down) { + if (key_repeats[key] == 1) + Globals.anykeydown++; + } + else { + Globals.anykeydown--; + if (Globals.anykeydown < 0) + Globals.anykeydown = 0; + } + + // + // key up events only generate commands if the game key binding is + // a button command (leading + sign). These will occur even in console mode, + // to keep the character from continuing an action started before a console + // switch. Button commands include the kenum as a parameter, so multiple + // downs can be matched with ups + // + if (!down) { + kb = Globals.keybindings[key]; + if (kb != null && kb.length()>0 && kb.charAt(0) == '+') { + cmd = "-" + kb.substring(1) + " " + key + " " + time + "\n"; + Cbuf.AddText(cmd); + } + if (keyshift[key] != key) { + kb = Globals.keybindings[keyshift[key]]; + if (kb != null && kb.length()>0 && kb.charAt(0) == '+') { + cmd = "-" + kb.substring(1) + " " + key + " " + time + "\n"; + Cbuf.AddText(cmd); + } + } + return; + } + + // + // if not a consolekey, send to the interpreter no matter what mode is + // + if ((Globals.cls.key_dest == Defines.key_menu && menubound[key]) + || (Globals.cls.key_dest == Defines.key_console && !consolekeys[key]) + || (Globals.cls.key_dest == Defines.key_game && (Globals.cls.state == Defines.ca_active || !consolekeys[key]))) { + kb = Globals.keybindings[key]; + if (kb != null) { + if (kb.length()>0 && kb.charAt(0) == '+') { + // button commands add keynum and time as a parm + cmd = kb + " " + key + " " + time + "\n"; + Cbuf.AddText(cmd); + } + else { + Cbuf.AddText(kb + "\n"); + } + } + return; + } + + if (!down) + return; // other systems only care about key down events + + if (shift_down) + key = keyshift[key]; + + switch (Globals.cls.key_dest) { + case Defines.key_message : + Key.Message(key); + break; + case Defines.key_menu : + Menu.Keydown(key); + break; + + case Defines.key_game : + case Defines.key_console : + Key.Console(key); + break; + default : + Com.Error(Defines.ERR_FATAL, "Bad cls.key_dest"); + } + } + + /** + * Returns a string (either a single ascii char, or a K_* name) for the + * given keynum. + */ + public static String KeynumToString(int keynum) { + if (keynum < 0 || keynum > 255) + return "<KEY NOT FOUND>"; + if (keynum > 32 && keynum < 127) + return Character.toString((char) keynum); + + if (keynames[keynum] != null) + return keynames[keynum]; + + return "<UNKNOWN KEYNUM>"; + } + + /** + * Returns a key number to be used to index keybindings[] by looking at + * the given string. Single ascii characters return themselves, while + * the K_* names are matched up. + */ + static int StringToKeynum(String str) { + + if (str == null) + return -1; + + if (str.length() == 1) + return str.charAt(0); + + for (int i = 0; i < keynames.length; i++) { + if (str.equalsIgnoreCase(keynames[i])) + return i; + } + + return -1; + } + + public static void Message(int key) { + + if (key == K_ENTER || key == K_KP_ENTER) { + if (Globals.chat_team) + Cbuf.AddText("say_team \""); + else + Cbuf.AddText("say \""); + + Cbuf.AddText(Globals.chat_buffer); + Cbuf.AddText("\"\n"); + + Globals.cls.key_dest = Defines.key_game; + Globals.chat_buffer = ""; + return; + } + + if (key == K_ESCAPE) { + Globals.cls.key_dest = Defines.key_game; + Globals.chat_buffer = ""; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key == K_BACKSPACE) { + if (Globals.chat_buffer.length() > 2) { + Globals.chat_buffer = Globals.chat_buffer.substring(0, Globals.chat_buffer.length() - 2); + } + else + Globals.chat_buffer = ""; + return; + } + + if (Globals.chat_buffer.length() > Defines.MAXCMDLINE) + return; // all full + + Globals.chat_buffer += (char) key; + } + + /** + * Interactive line editing and console scrollback. + */ + public static void Console(int key) { + + switch (key) { + case K_KP_SLASH : + key = '/'; + break; + case K_KP_MINUS : + key = '-'; + break; + case K_KP_PLUS : + key = '+'; + break; + case K_KP_HOME : + key = '7'; + break; + case K_KP_UPARROW : + key = '8'; + break; + case K_KP_PGUP : + key = '9'; + break; + case K_KP_LEFTARROW : + key = '4'; + break; + case K_KP_5 : + key = '5'; + break; + case K_KP_RIGHTARROW : + key = '6'; + break; + case K_KP_END : + key = '1'; + break; + case K_KP_DOWNARROW : + key = '2'; + break; + case K_KP_PGDN : + key = '3'; + break; + case K_KP_INS : + key = '0'; + break; + case K_KP_DEL : + key = '.'; + break; + } + + if (key == 'l') { + if (Globals.keydown[K_CTRL]) { + Cbuf.AddText("clear\n"); + return; + } + } + + if (key == K_ENTER || key == K_KP_ENTER) { + // backslash text are commands, else chat + if (Globals.key_lines[Globals.edit_line][1] == '\\' || Globals.key_lines[Globals.edit_line][1] == '/') + Cbuf.AddText( + new String(Globals.key_lines[Globals.edit_line], 2, Lib.strlen(Globals.key_lines[Globals.edit_line]) - 2)); + else + Cbuf.AddText( + new String(Globals.key_lines[Globals.edit_line], 1, Lib.strlen(Globals.key_lines[Globals.edit_line]) - 1)); + + Cbuf.AddText("\n"); + Com.Printf(new String(Globals.key_lines[Globals.edit_line], 0, Lib.strlen(Globals.key_lines[Globals.edit_line]))); + Globals.edit_line = (Globals.edit_line + 1) & 31; + history_line = Globals.edit_line; + Globals.key_lines[Globals.edit_line][0] = ']'; + Globals.key_linepos = 1; + if (Globals.cls.state == Defines.ca_disconnected) + SCR.UpdateScreen(); // force an update, because the command + // may take some time + return; + } + + if (key == K_TAB) { + // command completion + CompleteCommand(); + return; + } + + if ((key == K_BACKSPACE) || (key == K_LEFTARROW) || (key == K_KP_LEFTARROW) || ((key == 'h') && (Globals.keydown[K_CTRL]))) { + if (Globals.key_linepos > 1) + Globals.key_linepos--; + return; + } + + if ((key == K_UPARROW) || (key == K_KP_UPARROW) || ((key == 'p') && Globals.keydown[K_CTRL])) { + do { + history_line = (history_line - 1) & 31; + } + while (history_line != Globals.edit_line && Globals.key_lines[history_line][1] == 0); + if (history_line == Globals.edit_line) + history_line = (Globals.edit_line + 1) & 31; + Lib.strcpy(Globals.key_lines[Globals.edit_line], Globals.key_lines[history_line]); + Globals.key_linepos = Lib.strlen(Globals.key_lines[Globals.edit_line]); + return; + } + + if ((key == K_DOWNARROW) || (key == K_KP_DOWNARROW) || ((key == 'n') && Globals.keydown[K_CTRL])) { + if (history_line == Globals.edit_line) + return; + do { + history_line = (history_line + 1) & 31; + } + while (history_line != Globals.edit_line && Globals.key_lines[history_line][1] == 0); + if (history_line == Globals.edit_line) { + Globals.key_lines[Globals.edit_line][0] = ']'; + Globals.key_linepos = 1; + } + else { + Lib.strcpy(Globals.key_lines[Globals.edit_line], Globals.key_lines[history_line]); + Globals.key_linepos = Lib.strlen(Globals.key_lines[Globals.edit_line]); + } + return; + } + + if (key == K_PGUP || key == K_KP_PGUP) { + Globals.con.display -= 2; + return; + } + + if (key == K_PGDN || key == K_KP_PGDN) { + Globals.con.display += 2; + if (Globals.con.display > Globals.con.current) + Globals.con.display = Globals.con.current; + return; + } + + if (key == K_HOME || key == K_KP_HOME) { + Globals.con.display = Globals.con.current - Globals.con.totallines + 10; + return; + } + + if (key == K_END || key == K_KP_END) { + Globals.con.display = Globals.con.current; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (Globals.key_linepos < Defines.MAXCMDLINE - 1) { + Globals.key_lines[Globals.edit_line][Globals.key_linepos] = (byte) key; + Globals.key_linepos++; + Globals.key_lines[Globals.edit_line][Globals.key_linepos] = 0; + } + + } + + static void CompleteCommand() { + // 00166 char *cmd, *s; + // 00167 + // 00168 s = key_lines[edit_line]+1; + // 00169 if (*s == '\\' || *s == '/') + // 00170 s++; + // 00171 + // 00172 cmd = Cmd_CompleteCommand (s); + // 00173 if (!cmd) + // 00174 cmd = Cvar_CompleteVariable (s); + // 00175 if (cmd) + // 00176 { + // 00177 key_lines[edit_line][1] = '/'; + // 00178 strcpy (key_lines[edit_line]+2, cmd); + // 00179 key_linepos = strlen(cmd)+2; + // 00180 key_lines[edit_line][key_linepos] = ' '; + // 00181 key_linepos++; + // 00182 key_lines[edit_line][key_linepos] = 0; + // 00183 return; + // 00184 } + } + + public static xcommand_t Bind_f = new xcommand_t() { + public void execute() { + Key_Bind_f(); + } + }; + + static void Key_Bind_f() { + int c = Cmd.Argc(); + + if (c < 2) { + Com.Printf("bind <key> [command] : attach a command to a key\n"); + return; + } + int b = StringToKeynum(Cmd.Argv(1)); + if (b == -1) { + Com.Printf("\"" + Cmd.Argv(1) + "\" isn't a valid key\n"); + return; + } + + if (c == 2) { + if (Globals.keybindings[b] != null) + Com.Printf("\"" + Cmd.Argv(1) + "\" = \"" + Globals.keybindings[b] + "\"\n"); + else + Com.Printf("\"" + Cmd.Argv(1) + "\" is not bound\n"); + return; + } + + // copy the rest of the command line + String cmd = ""; // start out with a null string + for (int i = 2; i < c; i++) { + cmd += Cmd.Argv(i); + if (i != (c - 1)) + cmd += " "; + } + + SetBinding(b, cmd); + } + + static void SetBinding(int keynum, String binding) { + if (keynum == -1) + return; + + // free old bindings + Globals.keybindings[keynum] = null; + + Globals.keybindings[keynum] = binding; + } + + static xcommand_t Unbind_f = new xcommand_t() { + public void execute() { + Key_Unbind_f(); + } + }; + + static void Key_Unbind_f() { + + if (Cmd.Argc() != 2) { + Com.Printf("unbind <key> : remove commands from a key\n"); + return; + } + + int b = Key.StringToKeynum(Cmd.Argv(1)); + if (b == -1) { + Com.Printf("\"" + Cmd.Argv(1) + "\" isn't a valid key\n"); + return; + } + + Key.SetBinding(b, null); + } + + static xcommand_t Unbindall_f = new xcommand_t() { + public void execute() { + Key_Unbindall_f(); + } + }; + + static void Key_Unbindall_f() { + for (int i = 0; i < 256; i++) + Key.SetBinding(i, null); + } + + static xcommand_t Bindlist_f = new xcommand_t() { + public void execute() { + Key_Bindlist_f(); + } + }; + + static void Key_Bindlist_f() { + for (int i = 0; i < 256; i++) + if (Globals.keybindings[i] != null && Globals.keybindings[i].length() != 0) + Com.Printf(Key.KeynumToString(i) + " \"" + Globals.keybindings[i] + "\"\n"); + } + + static void ClearStates() { + int i; + + anykeydown = 0; + + for (i = 0; i < 256; i++) { + if (keydown[i] || key_repeats[i]!=0) + Event(i, false, 0); + keydown[i] = false; + key_repeats[i] = 0; + } + } + + public static void WriteBindings(RandomAccessFile f) { + for (int i = 0; i < 256; i++) + if (keybindings[i] != null && keybindings[i].length() > 0) + try { + f.writeBytes("bind " + KeynumToString(i) + " \"" + keybindings[i] + "\"\n"); + } catch (IOException e) {} + } + +} diff --git a/src/jake2/client/M.java b/src/jake2/client/M.java new file mode 100644 index 0000000..ebd9ebd --- /dev/null +++ b/src/jake2/client/M.java @@ -0,0 +1,573 @@ +/* + * M.java + * Copyright (C) 2003 + * + * $Id: M.java,v 1.1 2004-07-07 19:58:42 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.Defines; +import jake2.game.*; +import jake2.server.SV; +import jake2.util.Lib; +import jake2.util.Math3D; + +/** + * M + */ +public final class M { + + public static void M_CheckGround(edict_t ent) { + float[] point = { 0, 0, 0 }; + trace_t trace; + + if ((ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)) != 0) + return; + + if (ent.velocity[2] > 100) { + ent.groundentity = null; + return; + } + + // if the hull point one-quarter unit down is solid the entity is on ground + point[0] = ent.s.origin[0]; + point[1] = ent.s.origin[1]; + point[2] = ent.s.origin[2] - 0.25f; + + trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, point, ent, Defines.MASK_MONSTERSOLID); + + // check steepness + if (trace.plane.normal[2] < 0.7 && !trace.startsolid) { + ent.groundentity = null; + return; + } + + // ent.groundentity = trace.ent; + // ent.groundentity_linkcount = trace.ent.linkcount; + // if (!trace.startsolid && !trace.allsolid) + // VectorCopy (trace.endpos, ent.s.origin); + if (!trace.startsolid && !trace.allsolid) { + Math3D.VectorCopy(trace.endpos, ent.s.origin); + ent.groundentity = trace.ent; + ent.groundentity_linkcount = trace.ent.linkcount; + ent.velocity[2] = 0; + } + } + + public static boolean M_CheckBottom(edict_t ent) { + float[] mins = { 0, 0, 0 }; + float[] maxs = { 0, 0, 0 }; + float[] start = { 0, 0, 0 }; + float[] stop = { 0, 0, 0 }; + + trace_t trace; + int x, y; + float mid, bottom; + + Math3D.VectorAdd(ent.s.origin, ent.mins, mins); + Math3D.VectorAdd(ent.s.origin, ent.maxs, maxs); + + // if all of the points under the corners are solid world, don't bother + // with the tougher checks + // the corners must be within 16 of the midpoint + start[2] = mins[2] - 1; + for (x = 0; x <= 1; x++) + for (y = 0; y <= 1; y++) { + start[0] = x != 0 ? maxs[0] : mins[0]; + start[1] = y != 0 ? maxs[1] : mins[1]; + if (GameBase.gi.pointcontents.pointcontents(start) != Defines.CONTENTS_SOLID) { + GameBase.c_no++; + // + // check it for real... + // + start[2] = mins[2]; + + // the midpoint must be within 16 of the bottom + start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5f; + start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5f; + stop[2] = start[2] - 2 * GameBase.STEPSIZE; + trace = GameBase.gi.trace(start, GameBase.vec3_origin, GameBase.vec3_origin, stop, ent, Defines.MASK_MONSTERSOLID); + + if (trace.fraction == 1.0) + return false; + mid = bottom = trace.endpos[2]; + + // the corners must be within 16 of the midpoint + for (x = 0; x <= 1; x++) + for (y = 0; y <= 1; y++) { + start[0] = stop[0] = x != 0 ? maxs[0] : mins[0]; + start[1] = stop[1] = y != 0 ? maxs[1] : mins[1]; + + trace = + GameBase.gi.trace( + start, + GameBase.vec3_origin, + GameBase.vec3_origin, + stop, + ent, + Defines.MASK_MONSTERSOLID); + + if (trace.fraction != 1.0 && trace.endpos[2] > bottom) + bottom = trace.endpos[2]; + if (trace.fraction == 1.0 || mid - trace.endpos[2] > GameBase.STEPSIZE) + return false; + } + + GameBase.c_yes++; + return true; + } + } + + GameBase.c_yes++; + return true; // we got out easy + } + + /* + =============== + M_ChangeYaw + + =============== + *///ok + public static void M_ChangeYaw(edict_t ent) { + float ideal; + float current; + float move; + float speed; + + current = Math3D.anglemod(ent.s.angles[Defines.YAW]); + ideal = ent.ideal_yaw; + + if (current == ideal) + return; + + move = ideal - current; + speed = ent.yaw_speed; + if (ideal > current) { + if (move >= 180) + move = move - 360; + } + else { + if (move <= -180) + move = move + 360; + } + if (move > 0) { + if (move > speed) + move = speed; + } + else { + if (move < -speed) + move = -speed; + } + + ent.s.angles[Defines.YAW] = Math3D.anglemod(current + move); + } + + /* + ====================== + M_MoveToGoal + ====================== + */ + public static void M_MoveToGoal(edict_t ent, float dist) { + edict_t goal = ent.goalentity; + + if (ent.groundentity == null && (ent.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == 0) + return; + + // if the next step hits the enemy, return immediately + if (ent.enemy != null && SV.SV_CloseEnough(ent, ent.enemy, dist)) + return; + + // bump around... + if ((Lib.rand() & 3) == 1 || !SV.SV_StepDirection(ent, ent.ideal_yaw, dist)) { + if (ent.inuse) + SV.SV_NewChaseDir(ent, goal, dist); + } + } + + /* + =============== + M_walkmove + =============== + */ + public static boolean M_walkmove(edict_t ent, float yaw, float dist) { + float[] move = { 0, 0, 0 }; + + if ((ent.groundentity == null) && (ent.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == 0) + return false; + + yaw = (float) (yaw * Math.PI * 2 / 360); + + move[0] = (float) Math.cos(yaw) * dist; + move[1] = (float) Math.sin(yaw) * dist; + move[2] = 0; + + return SV.SV_movestep(ent, move, true); + } + + public static void M_CatagorizePosition(edict_t ent) { + float[] point = { 0, 0, 0 }; + int cont; + + // + // get waterlevel + // + point[0] = ent.s.origin[0]; + point[1] = ent.s.origin[1]; + point[2] = ent.s.origin[2] + ent.mins[2] + 1; + cont = Game.gi.pointcontents.pointcontents(point); + + if (0 == (cont & Defines.MASK_WATER)) { + ent.waterlevel = 0; + ent.watertype = 0; + return; + } + + ent.watertype = cont; + ent.waterlevel = 1; + point[2] += 26; + cont = GameBase.gi.pointcontents.pointcontents(point); + if (0 == (cont & Defines.MASK_WATER)) + return; + + ent.waterlevel = 2; + point[2] += 22; + cont = GameBase.gi.pointcontents.pointcontents(point); + if (0 != (cont & Defines.MASK_WATER)) + ent.waterlevel = 3; + } + + public static void M_WorldEffects(edict_t ent) { + int dmg; + + if (ent.health > 0) { + if (0 == (ent.flags & Defines.FL_SWIM)) { + if (ent.waterlevel < 3) { + ent.air_finished = GameBase.level.time + 12; + } + else if (ent.air_finished < GameBase.level.time) { + // drown! + if (ent.pain_debounce_time < GameBase.level.time) { + dmg = (int) (2f + 2f * Math.floor(GameBase.level.time - ent.air_finished)); + if (dmg > 15) + dmg = 15; + GameUtil.T_Damage( + ent, + GameBase.g_edicts[0], + GameBase.g_edicts[0], + GameBase.vec3_origin, + ent.s.origin, + GameBase.vec3_origin, + dmg, + 0, + Defines.DAMAGE_NO_ARMOR, + Defines.MOD_WATER); + ent.pain_debounce_time = GameBase.level.time + 1; + } + } + } + else { + if (ent.waterlevel > 0) { + ent.air_finished = GameBase.level.time + 9; + } + else if (ent.air_finished < GameBase.level.time) { + // suffocate! + if (ent.pain_debounce_time < GameBase.level.time) { + dmg = (int) (2 + 2 * Math.floor(GameBase.level.time - ent.air_finished)); + if (dmg > 15) + dmg = 15; + GameUtil.T_Damage( + ent, + GameBase.g_edicts[0], + GameBase.g_edicts[0], + GameBase.vec3_origin, + ent.s.origin, + GameBase.vec3_origin, + dmg, + 0, + Defines.DAMAGE_NO_ARMOR, + Defines.MOD_WATER); + ent.pain_debounce_time = GameBase.level.time + 1; + } + } + } + } + + if (ent.waterlevel == 0) { + if ((ent.flags & Defines.FL_INWATER) != 0) { + GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_out.wav"), 1, Defines.ATTN_NORM, 0); + ent.flags &= ~Defines.FL_INWATER; + } + return; + } + + if ((ent.watertype & Defines.CONTENTS_LAVA) != 0 && 0 == (ent.flags & Defines.FL_IMMUNE_LAVA)) { + if (ent.damage_debounce_time < GameBase.level.time) { + ent.damage_debounce_time = GameBase.level.time + 0.2f; + GameUtil.T_Damage( + ent, + GameBase.g_edicts[0], + GameBase.g_edicts[0], + GameBase.vec3_origin, + ent.s.origin, + GameBase.vec3_origin, + 10 * ent.waterlevel, + 0, + 0, + Defines.MOD_LAVA); + } + } + if ((ent.watertype & Defines.CONTENTS_SLIME) != 0 && 0 == (ent.flags & Defines.FL_IMMUNE_SLIME)) { + if (ent.damage_debounce_time < GameBase.level.time) { + ent.damage_debounce_time = GameBase.level.time + 1; + GameUtil.T_Damage( + ent, + GameBase.g_edicts[0], + GameBase.g_edicts[0], + GameBase.vec3_origin, + ent.s.origin, + GameBase.vec3_origin, + 4 * ent.waterlevel, + 0, + 0, + Defines.MOD_SLIME); + } + } + + if (0 == (ent.flags & Defines.FL_INWATER)) { + if (0 == (ent.svflags & Defines.SVF_DEADMONSTER)) { + if ((ent.watertype & Defines.CONTENTS_LAVA) != 0) + if (Lib.random() <= 0.5) + GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi.soundindex("player/lava1.wav"), 1, Defines.ATTN_NORM, 0); + else + GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi.soundindex("player/lava2.wav"), 1, Defines.ATTN_NORM, 0); + else if ((ent.watertype & Defines.CONTENTS_SLIME) != 0) + GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_in.wav"), 1, Defines.ATTN_NORM, 0); + else if ((ent.watertype & Defines.CONTENTS_WATER) != 0) + GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_in.wav"), 1, Defines.ATTN_NORM, 0); + } + + ent.flags |= Defines.FL_INWATER; + ent.damage_debounce_time = 0; + } + } + + public static EntThinkAdapter M_droptofloor = new EntThinkAdapter() { + public boolean think(edict_t ent) { + float[] end = { 0, 0, 0 }; + trace_t trace; + + ent.s.origin[2] += 1; + Math3D.VectorCopy(ent.s.origin, end); + end[2] -= 256; + + trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, end, ent, Defines.MASK_MONSTERSOLID); + + if (trace.fraction == 1 || trace.allsolid) + return true; + + Math3D.VectorCopy(trace.endpos, ent.s.origin); + + GameBase.gi.linkentity(ent); + M.M_CheckGround(ent); + M_CatagorizePosition(ent); + return true; + } + }; + + public static void M_SetEffects(edict_t ent) { + ent.s.effects &= ~(Defines.EF_COLOR_SHELL | Defines.EF_POWERSCREEN); + ent.s.renderfx &= ~(Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE); + + if ((ent.monsterinfo.aiflags & Defines.AI_RESURRECTING) != 0) { + ent.s.effects |= Defines.EF_COLOR_SHELL; + ent.s.renderfx |= Defines.RF_SHELL_RED; + } + + if (ent.health <= 0) + return; + + if (ent.powerarmor_time > GameBase.level.time) { + if (ent.monsterinfo.power_armor_type == Defines.POWER_ARMOR_SCREEN) { + ent.s.effects |= Defines.EF_POWERSCREEN; + } + else if (ent.monsterinfo.power_armor_type == Defines.POWER_ARMOR_SHIELD) { + ent.s.effects |= Defines.EF_COLOR_SHELL; + ent.s.renderfx |= Defines.RF_SHELL_GREEN; + } + } + }; + + public static void M_MoveFrame(edict_t self) { + mmove_t move; + int index; + + move = self.monsterinfo.currentmove; + self.nextthink = GameBase.level.time + Defines.FRAMETIME; + + if ((self.monsterinfo.nextframe != 0) + && (self.monsterinfo.nextframe >= move.firstframe) + && (self.monsterinfo.nextframe <= move.lastframe)) { + self.s.frame = self.monsterinfo.nextframe; + self.monsterinfo.nextframe = 0; + } + else { + if (self.s.frame == move.lastframe) { + if (move.endfunc != null) { + move.endfunc.think(self); + + // regrab move, endfunc is very likely to change it + move = self.monsterinfo.currentmove; + + // check for death + if ((self.svflags & Defines.SVF_DEADMONSTER) != 0) + return; + } + } + + if (self.s.frame < move.firstframe || self.s.frame > move.lastframe) { + self.monsterinfo.aiflags &= ~Defines.AI_HOLD_FRAME; + self.s.frame = move.firstframe; + } + else { + if (0 == (self.monsterinfo.aiflags & Defines.AI_HOLD_FRAME)) { + self.s.frame++; + if (self.s.frame > move.lastframe) + self.s.frame = move.firstframe; + } + } + } + + index = self.s.frame - move.firstframe; + if (move.frame[index].ai != null) + if (0 == (self.monsterinfo.aiflags & Defines.AI_HOLD_FRAME)) + move.frame[index].ai.ai(self, move.frame[index].dist * self.monsterinfo.scale); + else + move.frame[index].ai.ai(self, 0); + + if (move.frame[index].think != null) + move.frame[index].think.think(self); + } + + public static void M_ReactToDamage(edict_t targ, edict_t attacker) { + if ((null != attacker.client) && 0 != (attacker.svflags & Defines.SVF_MONSTER)) + return; + + if (attacker == targ || attacker == targ.enemy) + return; + + // if we are a good guy monster and our attacker is a player + // or another good guy, do not get mad at them + if (0 != (targ.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) { + if (attacker.client != null || (attacker.monsterinfo.aiflags & Defines.AI_GOOD_GUY) != 0) + return; + } + + // we now know that we are not both good guys + + // if attacker is a client, get mad at them because he's good and we're not + if (attacker.client != null) { + targ.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET; + + // this can only happen in coop (both new and old enemies are clients) + // only switch if can't see the current enemy + if (targ.enemy != null && targ.enemy.client != null) { + if (GameUtil.visible(targ, targ.enemy)) { + targ.oldenemy = attacker; + return; + } + targ.oldenemy = targ.enemy; + } + targ.enemy = attacker; + if (0 != (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) + GameUtil.FoundTarget(targ); + return; + } + + // it's the same base (walk/swim/fly) type and a different classname and it's not a tank + // (they spray too much), get mad at them + if (((targ.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == (attacker.flags & (Defines.FL_FLY | Defines.FL_SWIM))) + && (Lib.strcmp(targ.classname, attacker.classname) != 0) + && (Lib.strcmp(attacker.classname, "monster_tank") != 0) + && (Lib.strcmp(attacker.classname, "monster_supertank") != 0) + && (Lib.strcmp(attacker.classname, "monster_makron") != 0) + && (Lib.strcmp(attacker.classname, "monster_jorg") != 0)) { + if (targ.enemy != null && targ.enemy.client != null) + targ.oldenemy = targ.enemy; + targ.enemy = attacker; + if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) + GameUtil.FoundTarget(targ); + } + // if they *meant* to shoot us, then shoot back + else if (attacker.enemy == targ) { + if (targ.enemy != null && targ.enemy.client != null) + targ.oldenemy = targ.enemy; + targ.enemy = attacker; + if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) + GameUtil.FoundTarget(targ); + } + // otherwise get mad at whoever they are mad at (help our buddy) unless it is us! + else if (attacker.enemy != null && attacker.enemy != targ) { + if (targ.enemy != null && targ.enemy.client != null) + targ.oldenemy = targ.enemy; + targ.enemy = attacker.enemy; + if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)) + GameUtil.FoundTarget(targ); + } + } + /** Stops the Flies. */ + public static EntThinkAdapter M_FliesOff = new EntThinkAdapter() { + public boolean think(edict_t self) { + self.s.effects &= ~Defines.EF_FLIES; + self.s.sound = 0; + return true; + } + }; + /** Starts the Flies as setting the animation flag in the entity. */ + public static EntThinkAdapter M_FliesOn = new EntThinkAdapter() { + public boolean think(edict_t self) { + if (self.waterlevel != 0) + return true; + + self.s.effects |= Defines.EF_FLIES; + self.s.sound = GameBase.gi.soundindex("infantry/inflies1.wav"); + self.think = M_FliesOff; + self.nextthink = GameBase.level.time + 60; + return true; + } + }; + /** Adds some flies after a random time */ + public static EntThinkAdapter M_FlyCheck = new EntThinkAdapter() { + public boolean think(edict_t self) { + + if (self.waterlevel != 0) + return true; + + if (Lib.random() > 0.5) + return true; + + self.think = M_FliesOn; + self.nextthink = GameBase.level.time + 5 + 10 * Lib.random(); + return true; + } + }; + +} diff --git a/src/jake2/client/Menu.java b/src/jake2/client/Menu.java new file mode 100644 index 0000000..264d38f --- /dev/null +++ b/src/jake2/client/Menu.java @@ -0,0 +1,4977 @@ +/* + * Menu.java + * Copyright (C) 2004 + * + * $Id: Menu.java,v 1.1 2004-07-07 19:58:51 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.sys.NET; +import jake2.sys.Sys; +import jake2.util.Lib; +import jake2.util.Math3D; + +import java.awt.Dimension; +import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Comparator; + +/** + * Menu + * + * + */ + +abstract class keyfunc_t { + abstract String execute(int key); +} + +public final class Menu extends Key { + + static int m_main_cursor; + static final int NUM_CURSOR_FRAMES = 15; + + static final String menu_in_sound = "misc/menu1.wav"; + static final String menu_move_sound = "misc/menu2.wav"; + static final String menu_out_sound = "misc/menu3.wav"; + + static boolean m_entersound; // play after drawing a frame, so caching + // won't disrupt the sound + + static xcommand_t m_drawfunc; + + static keyfunc_t m_keyfunc; + + // ============================================================================= + /* Support Routines */ + + public final static int MAX_MENU_DEPTH = 8; + + public static class menulayer_t { + xcommand_t draw; + keyfunc_t key; + } + + static class menuframework_s { + int x, y; + int cursor; + + int nitems; + int nslots; + menucommon_s items[] = new menucommon_s[64]; + + String statusbar; + + //void (*cursordraw)( struct _tag_menuframework *m ); + mcallback cursordraw; + + } + + static class mcallback { + public void execute(Object self) { + } + } + + static class menucommon_s { + int type; + String name; + int x, y; + menuframework_s parent; + int cursor_offset; + int localdata[] = { 0, 0, 0, 0 }; + int flags; + int n = -1; //position in an array. + + String statusbar; + + mcallback callback; + mcallback statusbarfunc; + mcallback ownerdraw; + mcallback cursordraw; + } + + static class menufield_s extends menucommon_s { + //char buffer[80]; + StringBuffer buffer; //allow deletion. + + int cursor; + int length; + int visible_length; + int visible_offset; + } + + static class menuslider_s extends menucommon_s { + + float minvalue; + float maxvalue; + float curvalue; + + float range; + } + + static class menulist_s extends menucommon_s { + int curvalue; + String itemnames[]; + } + + static class menuaction_s extends menucommon_s { + + } + + static class menuseparator_s extends menucommon_s { + + } + + public static menulayer_t m_layers[] = new menulayer_t[MAX_MENU_DEPTH]; + public static int m_menudepth; + + static void Banner(String name) { + int w, h; + + Dimension dim = new Dimension(); + Globals.re.DrawGetPicSize(dim, name); + + Globals.re.DrawPic(viddef.width / 2 - dim.width / 2, viddef.height / 2 - 110, name); + } + + static void PushMenu(xcommand_t draw, keyfunc_t key) { //, String(*key) (int k) ) { + int i; + + if (Cvar.VariableValue("maxclients") == 1 && Com.ServerState() != 0) + Cvar.Set("paused", "1"); + + // if this menu is already present, drop back to that level + // to avoid stacking menus by hotkeys + for (i = 0; i < m_menudepth; i++) + if (m_layers[i].draw == draw && m_layers[i].key == key) { + m_menudepth = i; + } + + if (i == m_menudepth) { + if (m_menudepth >= MAX_MENU_DEPTH) + Com.Error(ERR_FATAL, "PushMenu: MAX_MENU_DEPTH"); + + m_layers[m_menudepth].draw = m_drawfunc; + m_layers[m_menudepth].key = m_keyfunc; + m_menudepth++; + } + + m_drawfunc = draw; + m_keyfunc = key; + + m_entersound = true; + + cls.key_dest = key_menu; + } + + static void ForceMenuOff() { + m_drawfunc = null; + m_keyfunc = null; + cls.key_dest = key_game; + m_menudepth = 0; + Key.ClearStates(); + Cvar.Set("paused", "0"); + } + + static void PopMenu() { + S.StartLocalSound(menu_out_sound); + if (m_menudepth < 1) + Com.Error(ERR_FATAL, "PopMenu: depth < 1"); + m_menudepth--; + + m_drawfunc = m_layers[m_menudepth].draw; + m_keyfunc = m_layers[m_menudepth].key; + + if (0 == m_menudepth) + ForceMenuOff(); + } + + static String Default_MenuKey(menuframework_s m, int key) { + String sound = null; + menucommon_s item; + + if (m != null) { + if ((item = ((menucommon_s) Menu_ItemAtCursor(m))) != null) { + if (item.type == MTYPE_FIELD) { + if (Field_Key((menufield_s) item, key)) + return null; + } + } + } + + switch (key) { + case K_ESCAPE : + PopMenu(); + return menu_out_sound; + case K_KP_UPARROW : + case K_UPARROW : + if (m != null) { + m.cursor--; + Menu_AdjustCursor(m, -1); + sound = menu_move_sound; + } + break; + case K_TAB : + if (m != null) { + m.cursor++; + Menu_AdjustCursor(m, 1); + sound = menu_move_sound; + } + break; + case K_KP_DOWNARROW : + case K_DOWNARROW : + if (m != null) { + m.cursor++; + Menu_AdjustCursor(m, 1); + sound = menu_move_sound; + } + break; + case K_KP_LEFTARROW : + case K_LEFTARROW : + if (m != null) { + Menu_SlideItem(m, -1); + sound = menu_move_sound; + } + break; + case K_KP_RIGHTARROW : + case K_RIGHTARROW : + if (m != null) { + Menu_SlideItem(m, 1); + sound = menu_move_sound; + } + break; + + case K_MOUSE1 : + case K_MOUSE2 : + case K_MOUSE3 : + case K_JOY1 : + case K_JOY2 : + case K_JOY3 : + case K_JOY4 : + /* + case K_AUX1 : + case K_AUX2 : + case K_AUX3 : + case K_AUX4 : + case K_AUX5 : + case K_AUX6 : + case K_AUX7 : + case K_AUX8 : + case K_AUX9 : + case K_AUX10 : + case K_AUX11 : + case K_AUX12 : + case K_AUX13 : + case K_AUX14 : + case K_AUX15 : + case K_AUX16 : + case K_AUX17 : + case K_AUX18 : + case K_AUX19 : + case K_AUX20 : + case K_AUX21 : + case K_AUX22 : + case K_AUX23 : + case K_AUX24 : + case K_AUX25 : + case K_AUX26 : + case K_AUX27 : + case K_AUX28 : + case K_AUX29 : + case K_AUX30 : + case K_AUX31 : + case K_AUX32 : + */ + case K_KP_ENTER : + case K_ENTER : + if (m != null) + Menu_SelectItem(m); + sound = menu_move_sound; + break; + } + + return sound; + } + + /* + ================ + DrawCharacter + + Draws one solid graphics character + cx and cy are in 320*240 coordinates, and will be centered on + higher res screens. + ================ + */ + public static void DrawCharacter(int cx, int cy, int num) { + re.DrawChar(cx + ((viddef.width - 320) >> 1), cy + ((viddef.height - 240) >> 1), num); + } + + public static void Print(int cx, int cy, String str) { + //while (*str) + for (int n = 0; n < str.length(); n++) { + DrawCharacter(cx, cy, str.charAt(n) + 128); + //str++; + cx += 8; + } + } + + public static void PrintWhite(int cx, int cy, String str) { + for (int n = 0; n < str.length(); n++) { + DrawCharacter(cx, cy, str.charAt(n)); + //str++; + cx += 8; + } + } + + public static void DrawPic(int x, int y, String pic) { + re.DrawPic(x + ((viddef.width - 320) >> 1), y + ((viddef.height - 240) >> 1), pic); + } + + /* + ============= + DrawCursor + + Draws an animating cursor with the point at + x,y. The pic will extend to the left of x, + and both above and below y. + ============= + */ + static boolean cached; + static void DrawCursor(int x, int y, int f) { + //char cursorname[80]; + String cursorname; + + assert(f >= 0) : "negative time and cursor bug"; + + f = Math.abs(f); + + if (!cached) { + int i; + + for (i = 0; i < NUM_CURSOR_FRAMES; i++) { + cursorname = "m_cursor" + i; + + re.RegisterPic(cursorname); + } + cached = true; + } + + cursorname = "m_cursor" + f; + re.DrawPic(x, y, cursorname); + } + + public static void DrawTextBox(int x, int y, int width, int lines) { + int cx, cy; + int n; + + // draw left side + cx = x; + cy = y; + DrawCharacter(cx, cy, 1); + + for (n = 0; n < lines; n++) { + cy += 8; + DrawCharacter(cx, cy, 4); + } + DrawCharacter(cx, cy + 8, 7); + + // draw middle + cx += 8; + while (width > 0) { + cy = y; + DrawCharacter(cx, cy, 2); + + for (n = 0; n < lines; n++) { + cy += 8; + DrawCharacter(cx, cy, 5); + } + DrawCharacter(cx, cy + 8, 8); + + width -= 1; + cx += 8; + } + + // draw right side + cy = y; + DrawCharacter(cx, cy, 3); + for (n = 0; n < lines; n++) { + cy += 8; + DrawCharacter(cx, cy, 6); + + } + DrawCharacter(cx, cy + 8, 9); + + } + + /* + ======================================================================= + + MAIN MENU + + ======================================================================= + */ + static final int MAIN_ITEMS = 5; + + static xcommand_t Main_Draw = new xcommand_t() { + public void execute() { + Main_Draw(); + } + }; + static void Main_Draw() { + int i; + int w, h; + int ystart; + int xoffset; + int widest = -1; + int totalheight = 0; + String litname; + String[] names = { "m_main_game", "m_main_multiplayer", "m_main_options", "m_main_video", "m_main_quit" }; + Dimension dim = new Dimension(); + + for (i = 0; i < names.length; i++) { + Globals.re.DrawGetPicSize(dim, names[i]); + w = dim.width; + h = dim.height; + + if (w > widest) + widest = w; + totalheight += (h + 12); + } + + ystart = (Globals.viddef.height / 2 - 110); + xoffset = (Globals.viddef.width - widest + 70) / 2; + + for (i = 0; i < names.length; i++) { + if (i != m_main_cursor) + Globals.re.DrawPic(xoffset, ystart + i * 40 + 13, names[i]); + } + + //strcat(litname, "_sel"); + litname = names[m_main_cursor] + "_sel"; + Globals.re.DrawPic(xoffset, ystart + m_main_cursor * 40 + 13, litname); + + DrawCursor(xoffset - 25, ystart + m_main_cursor * 40 + 11, (int) ((Globals.cls.realtime / 100)) % NUM_CURSOR_FRAMES); + + Globals.re.DrawGetPicSize(dim, "m_main_plaque"); + w = dim.width; + h = dim.height; + Globals.re.DrawPic(xoffset - 30 - w, ystart, "m_main_plaque"); + + Globals.re.DrawPic(xoffset - 30 - w, ystart + h + 5, "m_main_logo"); + } + + static keyfunc_t Main_Key = new keyfunc_t() { + public String execute(int key) { + return Main_Key(key); + } + }; + + static String Main_Key(int key) { + String sound = menu_move_sound; + + switch (key) { + case Key.K_ESCAPE : + PopMenu(); + break; + + case Key.K_KP_DOWNARROW : + case Key.K_DOWNARROW : + if (++m_main_cursor >= MAIN_ITEMS) + m_main_cursor = 0; + return sound; + + case Key.K_KP_UPARROW : + case Key.K_UPARROW : + if (--m_main_cursor < 0) + m_main_cursor = MAIN_ITEMS - 1; + return sound; + + case Key.K_KP_ENTER : + case Key.K_ENTER : + m_entersound = true; + + switch (m_main_cursor) { + case 0 : + Menu_Game_f(); + break; + + case 1 : + Menu_Multiplayer_f(); + break; + + case 2 : + Menu_Options_f(); + break; + + case 3 : + Menu_Video_f(); + break; + + case 4 : + Menu_Quit_f(); + break; + } + } + + return null; + } + + static xcommand_t Menu_Main = new xcommand_t() { + public void execute() { + Menu_Main_f(); + } + }; + + static void Menu_Main_f() { + PushMenu(new xcommand_t() { + public void execute() { + Main_Draw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Main_Key(key); + } + }); + } + + /* + ======================================================================= + + MULTIPLAYER MENU + + ======================================================================= + */ + static menuframework_s s_multiplayer_menu = new menuframework_s(); + static menuaction_s s_join_network_server_action = new menuaction_s(); + static menuaction_s s_start_network_server_action = new menuaction_s(); + static menuaction_s s_player_setup_action = new menuaction_s(); + + static void Multiplayer_MenuDraw() { + Banner("m_banner_multiplayer"); + + Menu_AdjustCursor(s_multiplayer_menu, 1); + Menu_Draw(s_multiplayer_menu); + } + + static void PlayerSetupFunc(Object unused) { + Menu_PlayerConfig_f(); + } + + static void JoinNetworkServerFunc(Object unused) { + Menu_JoinServer_f(); + } + + static void StartNetworkServerFunc(Object unused) { + Menu_StartServer_f(); + } + + static void Multiplayer_MenuInit() { + s_multiplayer_menu.x = (int) (viddef.width * 0.50f - 64); + s_multiplayer_menu.nitems = 0; + + s_join_network_server_action.type = MTYPE_ACTION; + s_join_network_server_action.flags = QMF_LEFT_JUSTIFY; + s_join_network_server_action.x = 0; + s_join_network_server_action.y = 0; + s_join_network_server_action.name = " join network server"; + s_join_network_server_action.callback = new mcallback() { + public void execute(Object o) { + JoinNetworkServerFunc(o); + }; + }; + + s_start_network_server_action.type = MTYPE_ACTION; + s_start_network_server_action.flags = QMF_LEFT_JUSTIFY; + s_start_network_server_action.x = 0; + s_start_network_server_action.y = 10; + s_start_network_server_action.name = " start network server"; + s_start_network_server_action.callback = new mcallback() { + public void execute(Object o) { + StartNetworkServerFunc(o); + } + }; + + s_player_setup_action.type = MTYPE_ACTION; + s_player_setup_action.flags = QMF_LEFT_JUSTIFY; + s_player_setup_action.x = 0; + s_player_setup_action.y = 20; + s_player_setup_action.name = " player setup"; + s_player_setup_action.callback = new mcallback() { + public void execute(Object o) { + PlayerSetupFunc(o); + } + }; + + Menu_AddItem(s_multiplayer_menu, s_join_network_server_action); + Menu_AddItem(s_multiplayer_menu, s_start_network_server_action); + Menu_AddItem(s_multiplayer_menu, s_player_setup_action); + + Menu_SetStatusBar(s_multiplayer_menu, null); + + Menu_Center(s_multiplayer_menu); + } + + static String Multiplayer_MenuKey(int key) { + return Default_MenuKey(s_multiplayer_menu, key); + } + + static xcommand_t Menu_Multiplayer = new xcommand_t() { + public void execute() { + Menu_Multiplayer_f(); + } + }; + + static void Menu_Multiplayer_f() { + Multiplayer_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + Multiplayer_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Multiplayer_MenuKey(key); + } + }); + } + + /* + ======================================================================= + + KEYS MENU + + ======================================================================= + */ + static String bindnames[][] = { { "+attack", "attack" }, { + "weapnext", "next weapon" }, { + "+forward", "walk forward" }, { + "+back", "backpedal" }, { + "+left", "turn left" }, { + "+right", "turn right" }, { + "+speed", "run" }, { + "+moveleft", "step left" }, { + "+moveright", "step right" }, { + "+strafe", "sidestep" }, { + "+lookup", "look up" }, { + "+lookdown", "look down" }, { + "centerview", "center view" }, { + "+mlook", "mouse look" }, { + "+klook", "keyboard look" }, { + "+moveup", "up / jump" }, { + "+movedown", "down / crouch" }, { + + "inven", "inventory" }, { + "invuse", "use item" }, { + "invdrop", "drop item" }, { + "invprev", "prev item" }, { + "invnext", "next item" }, { + + "cmd help", "help computer" }, { + null, null } + }; + + int keys_cursor; + static boolean bind_grab; + + static menuframework_s s_keys_menu = new menuframework_s(); + static menuaction_s s_keys_attack_action = new menuaction_s(); + static menuaction_s s_keys_change_weapon_action = new menuaction_s(); + static menuaction_s s_keys_walk_forward_action = new menuaction_s(); + static menuaction_s s_keys_backpedal_action = new menuaction_s(); + static menuaction_s s_keys_turn_left_action = new menuaction_s(); + static menuaction_s s_keys_turn_right_action = new menuaction_s(); + static menuaction_s s_keys_run_action = new menuaction_s(); + static menuaction_s s_keys_step_left_action = new menuaction_s(); + static menuaction_s s_keys_step_right_action = new menuaction_s(); + static menuaction_s s_keys_sidestep_action = new menuaction_s(); + static menuaction_s s_keys_look_up_action = new menuaction_s(); + static menuaction_s s_keys_look_down_action = new menuaction_s(); + static menuaction_s s_keys_center_view_action = new menuaction_s(); + static menuaction_s s_keys_mouse_look_action = new menuaction_s(); + static menuaction_s s_keys_keyboard_look_action = new menuaction_s(); + static menuaction_s s_keys_move_up_action = new menuaction_s(); + static menuaction_s s_keys_move_down_action = new menuaction_s(); + static menuaction_s s_keys_inventory_action = new menuaction_s(); + static menuaction_s s_keys_inv_use_action = new menuaction_s(); + static menuaction_s s_keys_inv_drop_action = new menuaction_s(); + static menuaction_s s_keys_inv_prev_action = new menuaction_s(); + static menuaction_s s_keys_inv_next_action = new menuaction_s(); + + static menuaction_s s_keys_help_computer_action = new menuaction_s(); + + static void UnbindCommand(String command) { + int j; + int l; + String b; + + for (j = 0; j < 256; j++) { + b = keybindings[j]; + if (b == null) + continue; + if (b.equals(command)) + Key.SetBinding(j, ""); + } + } + + static void FindKeysForCommand(String command, int twokeys[]) { + int count; + int j; + int l; + String b; + + twokeys[0] = twokeys[1] = -1; + count = 0; + + for (j = 0; j < 256; j++) { + b = keybindings[j]; + if (b == null) + continue; + + if (b.equals(command)) { + twokeys[count] = j; + count++; + if (count == 2) + break; + } + } + } + + static void KeyCursorDrawFunc(menuframework_s menu) { + if (bind_grab) + re.DrawChar(menu.x, menu.y + menu.cursor * 9, '='); + else + re.DrawChar(menu.x, menu.y + menu.cursor * 9, 12 + ((int) (Sys.Milliseconds() / 250) & 1)); + } + + static void DrawKeyBindingFunc(Object self) { + int keys[] = { 0, 0 }; + menuaction_s a = (menuaction_s) self; + + FindKeysForCommand(bindnames[a.localdata[0]][0], keys); + + if (keys[0] == -1) { + Menu_DrawString(a.x + a.parent.x + 16, a.y + a.parent.y, "???"); + } + else { + int x; + String name; + + name = Key.KeynumToString(keys[0]); + + Menu_DrawString(a.x + a.parent.x + 16, a.y + a.parent.y, name); + + x = strlen(name) * 8; + + if (keys[1] != -1) { + Menu_DrawString(a.x + a.parent.x + 24 + x, a.y + a.parent.y, "or"); + Menu_DrawString(a.x + a.parent.x + 48 + x, a.y + a.parent.y, Key.KeynumToString(keys[1])); + } + } + } + + static void KeyBindingFunc(Object self) { + menuaction_s a = (menuaction_s) self; + int keys[] = { 0, 0 }; + + FindKeysForCommand(bindnames[a.localdata[0]][0], keys); + + if (keys[1] != -1) + UnbindCommand(bindnames[a.localdata[0]][0]); + + bind_grab = true; + + Menu_SetStatusBar(s_keys_menu, "press a key or button for this action"); + } + + static void Keys_MenuInit() { + int y = 0; + int i = 0; + + s_keys_menu.x = (int) (viddef.width * 0.50); + s_keys_menu.nitems = 0; + s_keys_menu.cursordraw = new mcallback() { + public void execute(Object o) { + KeyCursorDrawFunc((menuframework_s) o); + } + }; + + s_keys_attack_action.type = MTYPE_ACTION; + s_keys_attack_action.flags = QMF_GRAYED; + s_keys_attack_action.x = 0; + s_keys_attack_action.y = y; + s_keys_attack_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_attack_action.localdata[0] = i; + s_keys_attack_action.name = bindnames[s_keys_attack_action.localdata[0]][1]; + + s_keys_change_weapon_action.type = MTYPE_ACTION; + s_keys_change_weapon_action.flags = QMF_GRAYED; + s_keys_change_weapon_action.x = 0; + s_keys_change_weapon_action.y = y += 9; + s_keys_change_weapon_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_change_weapon_action.localdata[0] = ++i; + s_keys_change_weapon_action.name = bindnames[s_keys_change_weapon_action.localdata[0]][1]; + + s_keys_walk_forward_action.type = MTYPE_ACTION; + s_keys_walk_forward_action.flags = QMF_GRAYED; + s_keys_walk_forward_action.x = 0; + s_keys_walk_forward_action.y = y += 9; + s_keys_walk_forward_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_walk_forward_action.localdata[0] = ++i; + s_keys_walk_forward_action.name = bindnames[s_keys_walk_forward_action.localdata[0]][1]; + + s_keys_backpedal_action.type = MTYPE_ACTION; + s_keys_backpedal_action.flags = QMF_GRAYED; + s_keys_backpedal_action.x = 0; + s_keys_backpedal_action.y = y += 9; + s_keys_backpedal_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_backpedal_action.localdata[0] = ++i; + s_keys_backpedal_action.name = bindnames[s_keys_backpedal_action.localdata[0]][1]; + + s_keys_turn_left_action.type = MTYPE_ACTION; + s_keys_turn_left_action.flags = QMF_GRAYED; + s_keys_turn_left_action.x = 0; + s_keys_turn_left_action.y = y += 9; + s_keys_turn_left_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_turn_left_action.localdata[0] = ++i; + s_keys_turn_left_action.name = bindnames[s_keys_turn_left_action.localdata[0]][1]; + + s_keys_turn_right_action.type = MTYPE_ACTION; + s_keys_turn_right_action.flags = QMF_GRAYED; + s_keys_turn_right_action.x = 0; + s_keys_turn_right_action.y = y += 9; + s_keys_turn_right_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_turn_right_action.localdata[0] = ++i; + s_keys_turn_right_action.name = bindnames[s_keys_turn_right_action.localdata[0]][1]; + + s_keys_run_action.type = MTYPE_ACTION; + s_keys_run_action.flags = QMF_GRAYED; + s_keys_run_action.x = 0; + s_keys_run_action.y = y += 9; + s_keys_run_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_run_action.localdata[0] = ++i; + s_keys_run_action.name = bindnames[s_keys_run_action.localdata[0]][1]; + + s_keys_step_left_action.type = MTYPE_ACTION; + s_keys_step_left_action.flags = QMF_GRAYED; + s_keys_step_left_action.x = 0; + s_keys_step_left_action.y = y += 9; + s_keys_step_left_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + s_keys_step_left_action.localdata[0] = ++i; + s_keys_step_left_action.name = bindnames[s_keys_step_left_action.localdata[0]][1]; + + s_keys_step_right_action.type = MTYPE_ACTION; + s_keys_step_right_action.flags = QMF_GRAYED; + s_keys_step_right_action.x = 0; + s_keys_step_right_action.y = y += 9; + s_keys_step_right_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_step_right_action.localdata[0] = ++i; + s_keys_step_right_action.name = bindnames[s_keys_step_right_action.localdata[0]][1]; + + s_keys_sidestep_action.type = MTYPE_ACTION; + s_keys_sidestep_action.flags = QMF_GRAYED; + s_keys_sidestep_action.x = 0; + s_keys_sidestep_action.y = y += 9; + s_keys_sidestep_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_sidestep_action.localdata[0] = ++i; + s_keys_sidestep_action.name = bindnames[s_keys_sidestep_action.localdata[0]][1]; + + s_keys_look_up_action.type = MTYPE_ACTION; + s_keys_look_up_action.flags = QMF_GRAYED; + s_keys_look_up_action.x = 0; + s_keys_look_up_action.y = y += 9; + s_keys_look_up_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_look_up_action.localdata[0] = ++i; + s_keys_look_up_action.name = bindnames[s_keys_look_up_action.localdata[0]][1]; + + s_keys_look_down_action.type = MTYPE_ACTION; + s_keys_look_down_action.flags = QMF_GRAYED; + s_keys_look_down_action.x = 0; + s_keys_look_down_action.y = y += 9; + s_keys_look_down_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_look_down_action.localdata[0] = ++i; + s_keys_look_down_action.name = bindnames[s_keys_look_down_action.localdata[0]][1]; + + s_keys_center_view_action.type = MTYPE_ACTION; + s_keys_center_view_action.flags = QMF_GRAYED; + s_keys_center_view_action.x = 0; + s_keys_center_view_action.y = y += 9; + s_keys_center_view_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_center_view_action.localdata[0] = ++i; + s_keys_center_view_action.name = bindnames[s_keys_center_view_action.localdata[0]][1]; + + s_keys_mouse_look_action.type = MTYPE_ACTION; + s_keys_mouse_look_action.flags = QMF_GRAYED; + s_keys_mouse_look_action.x = 0; + s_keys_mouse_look_action.y = y += 9; + s_keys_mouse_look_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_mouse_look_action.localdata[0] = ++i; + s_keys_mouse_look_action.name = bindnames[s_keys_mouse_look_action.localdata[0]][1]; + + s_keys_keyboard_look_action.type = MTYPE_ACTION; + s_keys_keyboard_look_action.flags = QMF_GRAYED; + s_keys_keyboard_look_action.x = 0; + s_keys_keyboard_look_action.y = y += 9; + s_keys_keyboard_look_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_keyboard_look_action.localdata[0] = ++i; + s_keys_keyboard_look_action.name = bindnames[s_keys_keyboard_look_action.localdata[0]][1]; + + s_keys_move_up_action.type = MTYPE_ACTION; + s_keys_move_up_action.flags = QMF_GRAYED; + s_keys_move_up_action.x = 0; + s_keys_move_up_action.y = y += 9; + s_keys_move_up_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_move_up_action.localdata[0] = ++i; + s_keys_move_up_action.name = bindnames[s_keys_move_up_action.localdata[0]][1]; + + s_keys_move_down_action.type = MTYPE_ACTION; + s_keys_move_down_action.flags = QMF_GRAYED; + s_keys_move_down_action.x = 0; + s_keys_move_down_action.y = y += 9; + s_keys_move_down_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_move_down_action.localdata[0] = ++i; + s_keys_move_down_action.name = bindnames[s_keys_move_down_action.localdata[0]][1]; + + s_keys_inventory_action.type = MTYPE_ACTION; + s_keys_inventory_action.flags = QMF_GRAYED; + s_keys_inventory_action.x = 0; + s_keys_inventory_action.y = y += 9; + s_keys_inventory_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_inventory_action.localdata[0] = ++i; + s_keys_inventory_action.name = bindnames[s_keys_inventory_action.localdata[0]][1]; + + s_keys_inv_use_action.type = MTYPE_ACTION; + s_keys_inv_use_action.flags = QMF_GRAYED; + s_keys_inv_use_action.x = 0; + s_keys_inv_use_action.y = y += 9; + s_keys_inv_use_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_inv_use_action.localdata[0] = ++i; + s_keys_inv_use_action.name = bindnames[s_keys_inv_use_action.localdata[0]][1]; + + s_keys_inv_drop_action.type = MTYPE_ACTION; + s_keys_inv_drop_action.flags = QMF_GRAYED; + s_keys_inv_drop_action.x = 0; + s_keys_inv_drop_action.y = y += 9; + s_keys_inv_drop_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_inv_drop_action.localdata[0] = ++i; + s_keys_inv_drop_action.name = bindnames[s_keys_inv_drop_action.localdata[0]][1]; + + s_keys_inv_prev_action.type = MTYPE_ACTION; + s_keys_inv_prev_action.flags = QMF_GRAYED; + s_keys_inv_prev_action.x = 0; + s_keys_inv_prev_action.y = y += 9; + s_keys_inv_prev_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_inv_prev_action.localdata[0] = ++i; + s_keys_inv_prev_action.name = bindnames[s_keys_inv_prev_action.localdata[0]][1]; + + s_keys_inv_next_action.type = MTYPE_ACTION; + s_keys_inv_next_action.flags = QMF_GRAYED; + s_keys_inv_next_action.x = 0; + s_keys_inv_next_action.y = y += 9; + s_keys_inv_next_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_inv_next_action.localdata[0] = ++i; + s_keys_inv_next_action.name = bindnames[s_keys_inv_next_action.localdata[0]][1]; + + s_keys_help_computer_action.type = MTYPE_ACTION; + s_keys_help_computer_action.flags = QMF_GRAYED; + s_keys_help_computer_action.x = 0; + s_keys_help_computer_action.y = y += 9; + s_keys_help_computer_action.ownerdraw = new mcallback() { + public void execute(Object o) { + DrawKeyBindingFunc(o); + } + }; + + s_keys_help_computer_action.localdata[0] = ++i; + s_keys_help_computer_action.name = bindnames[s_keys_help_computer_action.localdata[0]][1]; + + Menu_AddItem(s_keys_menu, s_keys_attack_action); + Menu_AddItem(s_keys_menu, s_keys_change_weapon_action); + Menu_AddItem(s_keys_menu, s_keys_walk_forward_action); + Menu_AddItem(s_keys_menu, s_keys_backpedal_action); + Menu_AddItem(s_keys_menu, s_keys_turn_left_action); + Menu_AddItem(s_keys_menu, s_keys_turn_right_action); + Menu_AddItem(s_keys_menu, s_keys_run_action); + Menu_AddItem(s_keys_menu, s_keys_step_left_action); + Menu_AddItem(s_keys_menu, s_keys_step_right_action); + Menu_AddItem(s_keys_menu, s_keys_sidestep_action); + Menu_AddItem(s_keys_menu, s_keys_look_up_action); + Menu_AddItem(s_keys_menu, s_keys_look_down_action); + Menu_AddItem(s_keys_menu, s_keys_center_view_action); + Menu_AddItem(s_keys_menu, s_keys_mouse_look_action); + Menu_AddItem(s_keys_menu, s_keys_keyboard_look_action); + Menu_AddItem(s_keys_menu, s_keys_move_up_action); + Menu_AddItem(s_keys_menu, s_keys_move_down_action); + + Menu_AddItem(s_keys_menu, s_keys_inventory_action); + Menu_AddItem(s_keys_menu, s_keys_inv_use_action); + Menu_AddItem(s_keys_menu, s_keys_inv_drop_action); + Menu_AddItem(s_keys_menu, s_keys_inv_prev_action); + Menu_AddItem(s_keys_menu, s_keys_inv_next_action); + + Menu_AddItem(s_keys_menu, s_keys_help_computer_action); + + Menu_SetStatusBar(s_keys_menu, "enter to change, backspace to clear"); + Menu_Center(s_keys_menu); + } + + static xcommand_t Keys_MenuDraw = new xcommand_t() { + public void execute() { + Keys_MenuDraw_f(); + } + }; + + static void Keys_MenuDraw_f() { + Menu_AdjustCursor(s_keys_menu, 1); + Menu_Draw(s_keys_menu); + } + + static keyfunc_t Keys_MenuKey = new keyfunc_t() { + public String execute(int key) { + return Keys_MenuKey_f(key); + } + }; + static String Keys_MenuKey_f(int key) { + menuaction_s item = (menuaction_s) Menu_ItemAtCursor(s_keys_menu); + + if (bind_grab) { + if (key != K_ESCAPE && key != '`') { + //char cmd[1024]; + String cmd; + + //Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), bindnames[item.localdata[0]][0]); + cmd = "bind \"" + Key.KeynumToString(key) + "\" \"" + bindnames[item.localdata[0]][0] + "\""; + Cbuf.InsertText(cmd); + } + + Menu_SetStatusBar(s_keys_menu, "enter to change, backspace to clear"); + bind_grab = false; + return menu_out_sound; + } + + switch (key) { + case K_KP_ENTER : + case K_ENTER : + KeyBindingFunc(item); + return menu_in_sound; + case K_BACKSPACE : // delete bindings + case K_DEL : // delete bindings + case K_KP_DEL : + UnbindCommand(bindnames[item.localdata[0]][0]); + return menu_out_sound; + default : + return Default_MenuKey(s_keys_menu, key); + } + } + + static xcommand_t Menu_Keys = new xcommand_t() { + public void execute() { + Menu_Keys_f(); + } + }; + static void Menu_Keys_f() { + Keys_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + Keys_MenuDraw_f(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Keys_MenuKey_f(key); + } + }); + } + + /* + ======================================================================= + + CONTROLS MENU + + ======================================================================= + */ + static cvar_t win_noalttab; + + static menuframework_s s_options_menu = new menuframework_s(); + static menuaction_s s_options_defaults_action = new menuaction_s(); + static menuaction_s s_options_customize_options_action = new menuaction_s(); + static menuslider_s s_options_sensitivity_slider = new menuslider_s(); + static menulist_s s_options_freelook_box = new menulist_s(); + static menulist_s s_options_noalttab_box = new menulist_s(); + static menulist_s s_options_alwaysrun_box = new menulist_s(); + static menulist_s s_options_invertmouse_box = new menulist_s(); + static menulist_s s_options_lookspring_box = new menulist_s(); + static menulist_s s_options_lookstrafe_box = new menulist_s(); + static menulist_s s_options_crosshair_box = new menulist_s(); + static menuslider_s s_options_sfxvolume_slider = new menuslider_s(); + static menulist_s s_options_joystick_box = new menulist_s(); + static menulist_s s_options_cdvolume_box = new menulist_s(); + static menulist_s s_options_quality_list = new menulist_s(); + static menulist_s s_options_compatibility_list = new menulist_s(); + static menuaction_s s_options_console_action = new menuaction_s(); + + static void CrosshairFunc(Object unused) { + Cvar.SetValue("crosshair", s_options_crosshair_box.curvalue); + } + + static void JoystickFunc(Object unused) { + Cvar.SetValue("in_joystick", s_options_joystick_box.curvalue); + } + + static void CustomizeControlsFunc(Object unused) { + Menu_Keys_f(); + } + + static void AlwaysRunFunc(Object unused) { + Cvar.SetValue("cl_run", s_options_alwaysrun_box.curvalue); + } + + static void FreeLookFunc(Object unused) { + Cvar.SetValue("freelook", s_options_freelook_box.curvalue); + } + + static void MouseSpeedFunc(Object unused) { + Cvar.SetValue("sensitivity", s_options_sensitivity_slider.curvalue / 2.0F); + } + + static void NoAltTabFunc(Object unused) { + Cvar.SetValue("win_noalttab", s_options_noalttab_box.curvalue); + } + + static float ClampCvar(float min, float max, float value) { + if (value < min) + return min; + if (value > max) + return max; + return value; + } + + static void ControlsSetMenuItemValues() { + s_options_sfxvolume_slider.curvalue = Cvar.VariableValue("s_volume") * 10; + s_options_cdvolume_box.curvalue = 1 - ((int) Cvar.VariableValue("cd_nocd")); + s_options_quality_list.curvalue = 1 - ((int) Cvar.VariableValue("s_loadas8bit")); + s_options_sensitivity_slider.curvalue = (sensitivity.value) * 2; + + Cvar.SetValue("cl_run", ClampCvar(0, 1, cl_run.value)); + s_options_alwaysrun_box.curvalue = (int) cl_run.value; + + s_options_invertmouse_box.curvalue = m_pitch.value < 0 ? 1 : 0; + + Cvar.SetValue("lookspring", ClampCvar(0, 1, lookspring.value)); + s_options_lookspring_box.curvalue = (int) lookspring.value; + + Cvar.SetValue("lookstrafe", ClampCvar(0, 1, lookstrafe.value)); + s_options_lookstrafe_box.curvalue = (int) lookstrafe.value; + + Cvar.SetValue("freelook", ClampCvar(0, 1, freelook.value)); + s_options_freelook_box.curvalue = (int) freelook.value; + + Cvar.SetValue("crosshair", ClampCvar(0, 3, Globals.crosshair.value)); + s_options_crosshair_box.curvalue = (int) Globals.crosshair.value; + + Cvar.SetValue("in_joystick", ClampCvar(0, 1, in_joystick.value)); + s_options_joystick_box.curvalue = (int) in_joystick.value; + + s_options_noalttab_box.curvalue = (int) win_noalttab.value; + } + + static void ControlsResetDefaultsFunc(Object unused) { + Cbuf.AddText("exec default.cfg\n"); + Cbuf.Execute(); + + ControlsSetMenuItemValues(); + } + + static void InvertMouseFunc(Object unused) { + Cvar.SetValue("m_pitch", -m_pitch.value); + } + + static void LookspringFunc(Object unused) { + Cvar.SetValue("lookspring", 1 - lookspring.value); + } + + static void LookstrafeFunc(Object unused) { + Cvar.SetValue("lookstrafe", 1 - lookstrafe.value); + } + + static void UpdateVolumeFunc(Object unused) { + Cvar.SetValue("s_volume", s_options_sfxvolume_slider.curvalue / 10); + } + + static void UpdateCDVolumeFunc(Object unused) { + Cvar.SetValue("cd_nocd", 1 - s_options_cdvolume_box.curvalue); + } + + static void ConsoleFunc(Object unused) { + /* + ** the proper way to do this is probably to have ToggleConsole_f accept a parameter + */ + + if (cl.attractloop) { + Cbuf.AddText("killserver\n"); + return; + } + + Key.ClearTyping(); + Console.ClearNotify(); + + ForceMenuOff(); + cls.key_dest = key_console; + } + + static void UpdateSoundQualityFunc(Object unused) { + if (s_options_quality_list.curvalue != 0) { + Cvar.SetValue("s_khz", 22); + Cvar.SetValue("s_loadas8bit", 0); + } + else { + Cvar.SetValue("s_khz", 11); + Cvar.SetValue("s_loadas8bit", 1); + } + + Cvar.SetValue("s_primary", s_options_compatibility_list.curvalue); + + DrawTextBox(8, 120 - 48, 36, 3); + Print(16 + 16, 120 - 48 + 8, "Restarting the sound system. This"); + Print(16 + 16, 120 - 48 + 16, "could take up to a minute, so"); + Print(16 + 16, 120 - 48 + 24, "please be patient."); + + // the text box won't show up unless we do a buffer swap + re.EndFrame(); + + CL.Snd_Restart_f.execute(); + } + + static String cd_music_items[] = { "disabled", "enabled", null }; + static String quality_items[] = { "low", "high", null }; + + static String compatibility_items[] = { "max compatibility", "max performance", null }; + + static String yesno_names[] = { "no", "yes", null }; + + static String crosshair_names[] = { "none", "cross", "dot", "angle", null }; + + static void Options_MenuInit() { + + win_noalttab = Cvar.Get("win_noalttab", "0", CVAR_ARCHIVE); + + /* + ** configure controls menu and menu items + */ + s_options_menu.x = viddef.width / 2; + s_options_menu.y = viddef.height / 2 - 58; + s_options_menu.nitems = 0; + + s_options_sfxvolume_slider.type = MTYPE_SLIDER; + s_options_sfxvolume_slider.x = 0; + s_options_sfxvolume_slider.y = 0; + s_options_sfxvolume_slider.name = "effects volume"; + s_options_sfxvolume_slider.callback = new mcallback() { + public void execute(Object o) { + UpdateVolumeFunc(o); + } + }; + s_options_sfxvolume_slider.minvalue = 0; + s_options_sfxvolume_slider.maxvalue = 10; + s_options_sfxvolume_slider.curvalue = Cvar.VariableValue("s_volume") * 10; + + s_options_cdvolume_box.type = MTYPE_SPINCONTROL; + s_options_cdvolume_box.x = 0; + s_options_cdvolume_box.y = 10; + s_options_cdvolume_box.name = "CD music"; + s_options_cdvolume_box.callback = new mcallback() { + public void execute(Object o) { + UpdateCDVolumeFunc(o); + } + }; + s_options_cdvolume_box.itemnames = cd_music_items; + s_options_cdvolume_box.curvalue = 1 - (int) Cvar.VariableValue("cd_nocd"); + + s_options_quality_list.type = MTYPE_SPINCONTROL; + s_options_quality_list.x = 0; + s_options_quality_list.y = 20; + ; + s_options_quality_list.name = "sound quality"; + s_options_quality_list.callback = new mcallback() { + public void execute(Object o) { + UpdateSoundQualityFunc(o); + } + }; + s_options_quality_list.itemnames = quality_items; + s_options_quality_list.curvalue = 1 - (int) Cvar.VariableValue("s_loadas8bit"); + + s_options_compatibility_list.type = MTYPE_SPINCONTROL; + s_options_compatibility_list.x = 0; + s_options_compatibility_list.y = 30; + s_options_compatibility_list.name = "sound compatibility"; + s_options_compatibility_list.callback = new mcallback() { + public void execute(Object o) { + UpdateSoundQualityFunc(o); + } + }; + s_options_compatibility_list.itemnames = compatibility_items; + s_options_compatibility_list.curvalue = (int) Cvar.VariableValue("s_primary"); + + s_options_sensitivity_slider.type = MTYPE_SLIDER; + s_options_sensitivity_slider.x = 0; + s_options_sensitivity_slider.y = 50; + s_options_sensitivity_slider.name = "mouse speed"; + s_options_sensitivity_slider.callback = new mcallback() { + public void execute(Object o) { + MouseSpeedFunc(o); + } + }; + s_options_sensitivity_slider.minvalue = 2; + s_options_sensitivity_slider.maxvalue = 22; + + s_options_alwaysrun_box.type = MTYPE_SPINCONTROL; + s_options_alwaysrun_box.x = 0; + s_options_alwaysrun_box.y = 60; + s_options_alwaysrun_box.name = "always run"; + s_options_alwaysrun_box.callback = new mcallback() { + public void execute(Object o) { + AlwaysRunFunc(o); + } + }; + s_options_alwaysrun_box.itemnames = yesno_names; + + s_options_invertmouse_box.type = MTYPE_SPINCONTROL; + s_options_invertmouse_box.x = 0; + s_options_invertmouse_box.y = 70; + s_options_invertmouse_box.name = "invert mouse"; + s_options_invertmouse_box.callback = new mcallback() { + public void execute(Object o) { + InvertMouseFunc(o); + } + }; + s_options_invertmouse_box.itemnames = yesno_names; + + s_options_lookspring_box.type = MTYPE_SPINCONTROL; + s_options_lookspring_box.x = 0; + s_options_lookspring_box.y = 80; + s_options_lookspring_box.name = "lookspring"; + s_options_lookspring_box.callback = new mcallback() { + public void execute(Object o) { + LookspringFunc(o); + } + }; + s_options_lookspring_box.itemnames = yesno_names; + + s_options_lookstrafe_box.type = MTYPE_SPINCONTROL; + s_options_lookstrafe_box.x = 0; + s_options_lookstrafe_box.y = 90; + s_options_lookstrafe_box.name = "lookstrafe"; + s_options_lookstrafe_box.callback = new mcallback() { + public void execute(Object o) { + LookstrafeFunc(o); + } + }; + s_options_lookstrafe_box.itemnames = yesno_names; + + s_options_freelook_box.type = MTYPE_SPINCONTROL; + s_options_freelook_box.x = 0; + s_options_freelook_box.y = 100; + s_options_freelook_box.name = "free look"; + s_options_freelook_box.callback = new mcallback() { + public void execute(Object o) { + FreeLookFunc(o); + } + }; + s_options_freelook_box.itemnames = yesno_names; + + s_options_crosshair_box.type = MTYPE_SPINCONTROL; + s_options_crosshair_box.x = 0; + s_options_crosshair_box.y = 110; + s_options_crosshair_box.name = "crosshair"; + s_options_crosshair_box.callback = new mcallback() { + public void execute(Object o) { + CrosshairFunc(o); + } + }; + s_options_crosshair_box.itemnames = crosshair_names; + /* + s_options_noalttab_box.type = MTYPE_SPINCONTROL; + s_options_noalttab_box.x = 0; + s_options_noalttab_box.y = 110; + s_options_noalttab_box.name = "disable alt-tab"; + s_options_noalttab_box.callback = NoAltTabFunc; + s_options_noalttab_box.itemnames = yesno_names; + */ + s_options_joystick_box.type = MTYPE_SPINCONTROL; + s_options_joystick_box.x = 0; + s_options_joystick_box.y = 120; + s_options_joystick_box.name = "use joystick"; + s_options_joystick_box.callback = new mcallback() { + public void execute(Object o) { + JoystickFunc(o); + } + }; + s_options_joystick_box.itemnames = yesno_names; + + s_options_customize_options_action.type = MTYPE_ACTION; + s_options_customize_options_action.x = 0; + s_options_customize_options_action.y = 140; + s_options_customize_options_action.name = "customize controls"; + s_options_customize_options_action.callback = new mcallback() { + public void execute(Object o) { + CustomizeControlsFunc(o); + } + }; + + s_options_defaults_action.type = MTYPE_ACTION; + s_options_defaults_action.x = 0; + s_options_defaults_action.y = 150; + s_options_defaults_action.name = "reset defaults"; + s_options_defaults_action.callback = new mcallback() { + public void execute(Object o) { + ControlsResetDefaultsFunc(o); + } + }; + + s_options_console_action.type = MTYPE_ACTION; + s_options_console_action.x = 0; + s_options_console_action.y = 160; + s_options_console_action.name = "go to console"; + s_options_console_action.callback = new mcallback() { + public void execute(Object o) { + ConsoleFunc(o); + } + }; + + ControlsSetMenuItemValues(); + + Menu_AddItem(s_options_menu, s_options_sfxvolume_slider); + + Menu_AddItem(s_options_menu, s_options_cdvolume_box); + Menu_AddItem(s_options_menu, s_options_quality_list); + Menu_AddItem(s_options_menu, s_options_compatibility_list); + Menu_AddItem(s_options_menu, s_options_sensitivity_slider); + Menu_AddItem(s_options_menu, s_options_alwaysrun_box); + Menu_AddItem(s_options_menu, s_options_invertmouse_box); + Menu_AddItem(s_options_menu, s_options_lookspring_box); + Menu_AddItem(s_options_menu, s_options_lookstrafe_box); + Menu_AddItem(s_options_menu, s_options_freelook_box); + Menu_AddItem(s_options_menu, s_options_crosshair_box); +// Menu_AddItem(s_options_menu, s_options_joystick_box); + Menu_AddItem(s_options_menu, s_options_customize_options_action); + Menu_AddItem(s_options_menu, s_options_defaults_action); + Menu_AddItem(s_options_menu, s_options_console_action); + } + + static void Options_MenuDraw() { + Banner("m_banner_options"); + Menu_AdjustCursor(s_options_menu, 1); + Menu_Draw(s_options_menu); + } + + static String Options_MenuKey(int key) { + return Default_MenuKey(s_options_menu, key); + } + + static xcommand_t Menu_Options = new xcommand_t() { + public void execute() { + Menu_Options_f(); + } + }; + + static void Menu_Options_f() { + Options_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + Options_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Options_MenuKey(key); + } + }); + } + + /* + ======================================================================= + + VIDEO MENU + + ======================================================================= + */ + + static xcommand_t Menu_Video = new xcommand_t() { + public void execute() { + Menu_Video_f(); + } + }; + + static void Menu_Video_f() { + VID.MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + VID.MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return VID.MenuKey(key); + } + }); + } + + /* + ============================================================================= + + END GAME MENU + + ============================================================================= + */ + static int credits_start_time; + + static String creditsIndex[] = new String[256]; + static String creditsBuffer; + static String idcredits[] = + { + "+QUAKE II BY ID SOFTWARE", + "", + "+PROGRAMMING", + "John Carmack", + "John Cash", + "Brian Hook", + "", + "+JAVA PORT BY JTEAM", + "CWEI", + "HOZ", + "RST", + "", + "+ART", + "Adrian Carmack", + "Kevin Cloud", + "Paul Steed", + "", + "+LEVEL DESIGN", + "Tim Willits", + "American McGee", + "Christian Antkow", + "Paul Jaquays", + "Brandon James", + "", + "+BIZ", + "Todd Hollenshead", + "Barrett (Bear) Alexander", + "Donna Jackson", + "", + "", + "+SPECIAL THANKS", + "Ben Donges for beta testing", + "", + "", + "", + "", + "", + "", + "+ADDITIONAL SUPPORT", + "", + "+LINUX PORT AND CTF", + "Dave \"Zoid\" Kirsch", + "", + "+CINEMATIC SEQUENCES", + "Ending Cinematic by Blur Studio - ", + "Venice, CA", + "", + "Environment models for Introduction", + "Cinematic by Karl Dolgener", + "", + "Assistance with environment design", + "by Cliff Iwai", + "", + "+SOUND EFFECTS AND MUSIC", + "Sound Design by Soundelux Media Labs.", + "Music Composed and Produced by", + "Soundelux Media Labs. Special thanks", + "to Bill Brown, Tom Ozanich, Brian", + "Celano, Jeff Eisner, and The Soundelux", + "Players.", + "", + "\"Level Music\" by Sonic Mayhem", + "www.sonicmayhem.com", + "", + "\"Quake II Theme Song\"", + "(C) 1997 Rob Zombie. All Rights", + "Reserved.", + "", + "Track 10 (\"Climb\") by Jer Sypult", + "", + "Voice of computers by", + "Carly Staehlin-Taylor", + "", + "+THANKS TO ACTIVISION", + "+IN PARTICULAR:", + "", + "John Tam", + "Steve Rosenthal", + "Marty Stratton", + "Henk Hartong", + "", + "Quake II(tm) (C)1997 Id Software, Inc.", + "All Rights Reserved. Distributed by", + "Activision, Inc. under license.", + "Quake II(tm), the Id Software name,", + "the \"Q II\"(tm) logo and id(tm)", + "logo are trademarks of Id Software,", + "Inc. Activision(R) is a registered", + "trademark of Activision, Inc. All", + "other trademarks and trade names are", + "properties of their respective owners.", + null }; + static String credits[] = idcredits; + static String xatcredits[] = + { + "+QUAKE II MISSION PACK: THE RECKONING", + "+BY", + "+XATRIX ENTERTAINMENT, INC.", + "", + "+DESIGN AND DIRECTION", + "Drew Markham", + "", + "+PRODUCED BY", + "Greg Goodrich", + "", + "+PROGRAMMING", + "Rafael Paiz", + "", + "+LEVEL DESIGN / ADDITIONAL GAME DESIGN", + "Alex Mayberry", + "", + "+LEVEL DESIGN", + "Mal Blackwell", + "Dan Koppel", + "", + "+ART DIRECTION", + "Michael \"Maxx\" Kaufman", + "", + "+COMPUTER GRAPHICS SUPERVISOR AND", + "+CHARACTER ANIMATION DIRECTION", + "Barry Dempsey", + "", + "+SENIOR ANIMATOR AND MODELER", + "Jason Hoover", + "", + "+CHARACTER ANIMATION AND", + "+MOTION CAPTURE SPECIALIST", + "Amit Doron", + "", + "+ART", + "Claire Praderie-Markham", + "Viktor Antonov", + "Corky Lehmkuhl", + "", + "+INTRODUCTION ANIMATION", + "Dominique Drozdz", + "", + "+ADDITIONAL LEVEL DESIGN", + "Aaron Barber", + "Rhett Baldwin", + "", + "+3D CHARACTER ANIMATION TOOLS", + "Gerry Tyra, SA Technology", + "", + "+ADDITIONAL EDITOR TOOL PROGRAMMING", + "Robert Duffy", + "", + "+ADDITIONAL PROGRAMMING", + "Ryan Feltrin", + "", + "+PRODUCTION COORDINATOR", + "Victoria Sylvester", + "", + "+SOUND DESIGN", + "Gary Bradfield", + "", + "+MUSIC BY", + "Sonic Mayhem", + "", + "", + "", + "+SPECIAL THANKS", + "+TO", + "+OUR FRIENDS AT ID SOFTWARE", + "", + "John Carmack", + "John Cash", + "Brian Hook", + "Adrian Carmack", + "Kevin Cloud", + "Paul Steed", + "Tim Willits", + "Christian Antkow", + "Paul Jaquays", + "Brandon James", + "Todd Hollenshead", + "Barrett (Bear) Alexander", + "Dave \"Zoid\" Kirsch", + "Donna Jackson", + "", + "", + "", + "+THANKS TO ACTIVISION", + "+IN PARTICULAR:", + "", + "Marty Stratton", + "Henk \"The Original Ripper\" Hartong", + "Kevin Kraff", + "Jamey Gottlieb", + "Chris Hepburn", + "", + "+AND THE GAME TESTERS", + "", + "Tim Vanlaw", + "Doug Jacobs", + "Steven Rosenthal", + "David Baker", + "Chris Campbell", + "Aaron Casillas", + "Steve Elwell", + "Derek Johnstone", + "Igor Krinitskiy", + "Samantha Lee", + "Michael Spann", + "Chris Toft", + "Juan Valdes", + "", + "+THANKS TO INTERGRAPH COMPUTER SYTEMS", + "+IN PARTICULAR:", + "", + "Michael T. Nicolaou", + "", + "", + "Quake II Mission Pack: The Reckoning", + "(tm) (C)1998 Id Software, Inc. All", + "Rights Reserved. Developed by Xatrix", + "Entertainment, Inc. for Id Software,", + "Inc. Distributed by Activision Inc.", + "under license. Quake(R) is a", + "registered trademark of Id Software,", + "Inc. Quake II Mission Pack: The", + "Reckoning(tm), Quake II(tm), the Id", + "Software name, the \"Q II\"(tm) logo", + "and id(tm) logo are trademarks of Id", + "Software, Inc. Activision(R) is a", + "registered trademark of Activision,", + "Inc. Xatrix(R) is a registered", + "trademark of Xatrix Entertainment,", + "Inc. All other trademarks and trade", + "names are properties of their", + "respective owners.", + null }; + + static String roguecredits[] = + { + "+QUAKE II MISSION PACK 2: GROUND ZERO", + "+BY", + "+ROGUE ENTERTAINMENT, INC.", + "", + "+PRODUCED BY", + "Jim Molinets", + "", + "+PROGRAMMING", + "Peter Mack", + "Patrick Magruder", + "", + "+LEVEL DESIGN", + "Jim Molinets", + "Cameron Lamprecht", + "Berenger Fish", + "Robert Selitto", + "Steve Tietze", + "Steve Thoms", + "", + "+ART DIRECTION", + "Rich Fleider", + "", + "+ART", + "Rich Fleider", + "Steve Maines", + "Won Choi", + "", + "+ANIMATION SEQUENCES", + "Creat Studios", + "Steve Maines", + "", + "+ADDITIONAL LEVEL DESIGN", + "Rich Fleider", + "Steve Maines", + "Peter Mack", + "", + "+SOUND", + "James Grunke", + "", + "+GROUND ZERO THEME", + "+AND", + "+MUSIC BY", + "Sonic Mayhem", + "", + "+VWEP MODELS", + "Brent \"Hentai\" Dill", + "", + "", + "", + "+SPECIAL THANKS", + "+TO", + "+OUR FRIENDS AT ID SOFTWARE", + "", + "John Carmack", + "John Cash", + "Brian Hook", + "Adrian Carmack", + "Kevin Cloud", + "Paul Steed", + "Tim Willits", + "Christian Antkow", + "Paul Jaquays", + "Brandon James", + "Todd Hollenshead", + "Barrett (Bear) Alexander", + "Katherine Anna Kang", + "Donna Jackson", + "Dave \"Zoid\" Kirsch", + "", + "", + "", + "+THANKS TO ACTIVISION", + "+IN PARTICULAR:", + "", + "Marty Stratton", + "Henk Hartong", + "Mitch Lasky", + "Steve Rosenthal", + "Steve Elwell", + "", + "+AND THE GAME TESTERS", + "", + "The Ranger Clan", + "Dave \"Zoid\" Kirsch", + "Nihilistic Software", + "Robert Duffy", + "", + "And Countless Others", + "", + "", + "", + "Quake II Mission Pack 2: Ground Zero", + "(tm) (C)1998 Id Software, Inc. All", + "Rights Reserved. Developed by Rogue", + "Entertainment, Inc. for Id Software,", + "Inc. Distributed by Activision Inc.", + "under license. Quake(R) is a", + "registered trademark of Id Software,", + "Inc. Quake II Mission Pack 2: Ground", + "Zero(tm), Quake II(tm), the Id", + "Software name, the \"Q II\"(tm) logo", + "and id(tm) logo are trademarks of Id", + "Software, Inc. Activision(R) is a", + "registered trademark of Activision,", + "Inc. Rogue(R) is a registered", + "trademark of Rogue Entertainment,", + "Inc. All other trademarks and trade", + "names are properties of their", + "respective owners.", + null }; + + public static void Credits_MenuDraw() { + int i, y; + + /* + ** draw the credits + */ + for (i = 0, y = (int) (viddef.height - ((cls.realtime - credits_start_time) / 40.0F)); + credits[i] != null && y < viddef.height; + y += 10, i++) { + int j, stringoffset = 0; + boolean bold = false; + + if (y <= -8) + continue; + + if (credits[i].length() > 0 && credits[i].charAt(0) == '+') { + bold = true; + stringoffset = 1; + } + else { + bold = false; + stringoffset = 0; + } + + for (j = 0; j + stringoffset < credits[i].length(); j++) { + int x; + + x = (viddef.width - strlen(credits[i]) * 8 - stringoffset * 8) / 2 + (j + stringoffset) * 8; + + if (bold) + re.DrawChar(x, y, credits[i].charAt(j + stringoffset) + 128); + else + re.DrawChar(x, y, credits[i].charAt(j + stringoffset)); + } + } + + if (y < 0) + credits_start_time = cls.realtime; + } + + public static String Credits_Key(int key) { + switch (key) { + case K_ESCAPE : + if (creditsBuffer != null) + //FS.FreeFile(creditsBuffer); + ; + PopMenu(); + break; + } + + return menu_out_sound; + + } + + static xcommand_t Menu_Credits = new xcommand_t() { + public void execute() { + Menu_Credits_f(); + } + }; + static void Menu_Credits_f() { + int n; + int count; + String p; + int isdeveloper = 0; + + byte b[] = FS.LoadFile("credits"); + + if (b != null) { + creditsBuffer = new String(b); + String line[] = Lib.linesplit(creditsBuffer); + + for (n = 0; n < line.length; n++) { + creditsIndex[n] = line[n]; + } + + creditsIndex[n] = null; + credits = creditsIndex; + } + else { + isdeveloper = FS.Developer_searchpath(1); + + if (isdeveloper == 1) // xatrix + credits = xatcredits; + else if (isdeveloper == 2) // ROGUE + credits = roguecredits; + else { + credits = idcredits; + } + + } + + credits_start_time = cls.realtime; + PushMenu(new xcommand_t() { + public void execute() { + Credits_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Credits_Key(key); + } + }); + } + + /* + ============================================================================= + + GAME MENU + + ============================================================================= + */ + + static int m_game_cursor; + + static menuframework_s s_game_menu = new menuframework_s(); + static menuaction_s s_easy_game_action = new menuaction_s(); + static menuaction_s s_medium_game_action = new menuaction_s(); + static menuaction_s s_hard_game_action = new menuaction_s(); + static menuaction_s s_load_game_action = new menuaction_s(); + static menuaction_s s_save_game_action = new menuaction_s(); + static menuaction_s s_credits_action = new menuaction_s(); + static menuseparator_s s_blankline = new menuseparator_s(); + + static void StartGame() { + // disable updates and start the cinematic going + cl.servercount = -1; + ForceMenuOff(); + Cvar.SetValue("deathmatch", 0); + Cvar.SetValue("coop", 0); + + Cvar.SetValue("gamerules", 0); //PGM + + Cbuf.AddText("loading ; killserver ; wait ; newgame\n"); + cls.key_dest = key_game; + } + + static void EasyGameFunc(Object data) { + Cvar.ForceSet("skill", "0"); + StartGame(); + } + + static void MediumGameFunc(Object data) { + Cvar.ForceSet("skill", "1"); + StartGame(); + } + + static void HardGameFunc(Object data) { + Cvar.ForceSet("skill", "2"); + StartGame(); + } + + static void LoadGameFunc(Object unused) { + Menu_LoadGame_f(); + } + + static void SaveGameFunc(Object unused) { + Menu_SaveGame_f(); + } + + static void CreditsFunc(Object unused) { + Menu_Credits_f(); + } + + static String difficulty_names[] = { "easy", "medium", "fuckin shitty hard", null }; + static void Game_MenuInit() { + + s_game_menu.x = (int) (viddef.width * 0.50); + s_game_menu.nitems = 0; + + s_easy_game_action.type = MTYPE_ACTION; + s_easy_game_action.flags = QMF_LEFT_JUSTIFY; + s_easy_game_action.x = 0; + s_easy_game_action.y = 0; + s_easy_game_action.name = "easy"; + s_easy_game_action.callback = new mcallback() { + public void execute(Object o) { + EasyGameFunc(o); + } + }; + + s_medium_game_action.type = MTYPE_ACTION; + s_medium_game_action.flags = QMF_LEFT_JUSTIFY; + s_medium_game_action.x = 0; + s_medium_game_action.y = 10; + s_medium_game_action.name = "medium"; + s_medium_game_action.callback = new mcallback() { + public void execute(Object o) { + MediumGameFunc(o); + } + }; + + s_hard_game_action.type = MTYPE_ACTION; + s_hard_game_action.flags = QMF_LEFT_JUSTIFY; + s_hard_game_action.x = 0; + s_hard_game_action.y = 20; + s_hard_game_action.name = "hard"; + s_hard_game_action.callback = new mcallback() { + public void execute(Object o) { + HardGameFunc(o); + } + }; + + s_blankline.type = MTYPE_SEPARATOR; + + s_load_game_action.type = MTYPE_ACTION; + s_load_game_action.flags = QMF_LEFT_JUSTIFY; + s_load_game_action.x = 0; + s_load_game_action.y = 40; + s_load_game_action.name = "load game"; + s_load_game_action.callback = new mcallback() { + public void execute(Object o) { + LoadGameFunc(o); + } + }; + + s_save_game_action.type = MTYPE_ACTION; + s_save_game_action.flags = QMF_LEFT_JUSTIFY; + s_save_game_action.x = 0; + s_save_game_action.y = 50; + s_save_game_action.name = "save game"; + s_save_game_action.callback = new mcallback() { + public void execute(Object o) { + SaveGameFunc(o); + } + }; + + s_credits_action.type = MTYPE_ACTION; + s_credits_action.flags = QMF_LEFT_JUSTIFY; + s_credits_action.x = 0; + s_credits_action.y = 60; + s_credits_action.name = "credits"; + s_credits_action.callback = new mcallback() { + public void execute(Object o) { + CreditsFunc(o); + } + }; + + Menu_AddItem(s_game_menu, s_easy_game_action); + Menu_AddItem(s_game_menu, s_medium_game_action); + Menu_AddItem(s_game_menu, s_hard_game_action); + Menu_AddItem(s_game_menu, s_blankline); + Menu_AddItem(s_game_menu, s_load_game_action); + Menu_AddItem(s_game_menu, s_save_game_action); + Menu_AddItem(s_game_menu, s_blankline); + Menu_AddItem(s_game_menu, s_credits_action); + + Menu_Center(s_game_menu); + } + + static void Game_MenuDraw() { + Banner("m_banner_game"); + Menu_AdjustCursor(s_game_menu, 1); + Menu_Draw(s_game_menu); + } + + static String Game_MenuKey(int key) { + return Default_MenuKey(s_game_menu, key); + } + + static xcommand_t Menu_Game = new xcommand_t() { + public void execute() { + Menu_Game_f(); + } + }; + + static void Menu_Game_f() { + Game_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + Game_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Game_MenuKey(key); + } + }); + m_game_cursor = 1; + } + + /* + ============================================================================= + + LOADGAME MENU + + ============================================================================= + */ + + public final static int MAX_SAVEGAMES = 15; + + static menuframework_s s_savegame_menu = new menuframework_s(); + static menuframework_s s_loadgame_menu = new menuframework_s(); + + static menuaction_s s_loadgame_actions[] = new menuaction_s[MAX_SAVEGAMES]; + + static { + for (int n = 0; n < MAX_SAVEGAMES; n++) + s_loadgame_actions[n] = new menuaction_s(); + } + + //String m_savestrings[] = new String [MAX_SAVEGAMES][32]; + static String m_savestrings[] = new String[MAX_SAVEGAMES]; + + static { + for (int n = 0; n < MAX_SAVEGAMES; n++) + m_savestrings[n] = ""; + } + + static boolean m_savevalid[] = new boolean[MAX_SAVEGAMES]; + + static void Create_Savestrings() { + int i; + RandomAccessFile f; + //char name[MAX_OSPATH]; + String name; + + for (i = 0; i < MAX_SAVEGAMES; i++) { + name = FS.Gamedir() + "/save/save" + i + "/server.ssv"; + f = fopen(name, "r"); + if (f == null) { + m_savestrings[i] = "<EMPTY>"; + m_savevalid[i] = false; + } + else { + m_savestrings[i] = freadString(f, 32); + fclose(f); + m_savevalid[i] = true; + } + } + } + + static void LoadGameCallback(Object self) { + menuaction_s a = (menuaction_s) self; + + if (m_savevalid[a.localdata[0]]) + Cbuf.AddText("load save" + a.localdata[0] + "\n"); + ForceMenuOff(); + } + + static void LoadGame_MenuInit() { + int i; + + s_loadgame_menu.x = viddef.width / 2 - 120; + s_loadgame_menu.y = viddef.height / 2 - 58; + s_loadgame_menu.nitems = 0; + + Create_Savestrings(); + + for (i = 0; i < MAX_SAVEGAMES; i++) { + s_loadgame_actions[i].name = m_savestrings[i]; + s_loadgame_actions[i].flags = QMF_LEFT_JUSTIFY; + s_loadgame_actions[i].localdata[0] = i; + s_loadgame_actions[i].callback = new mcallback() { + public void execute(Object o) { + LoadGameCallback(o); + } + }; + + s_loadgame_actions[i].x = 0; + s_loadgame_actions[i].y = (i) * 10; + if (i > 0) // separate from autosave + s_loadgame_actions[i].y += 10; + + s_loadgame_actions[i].type = MTYPE_ACTION; + + Menu_AddItem(s_loadgame_menu, s_loadgame_actions[i]); + } + } + + static void LoadGame_MenuDraw() { + Banner("m_banner_load_game"); + // Menu_AdjustCursor( &s_loadgame_menu, 1 ); + Menu_Draw(s_loadgame_menu); + } + + static String LoadGame_MenuKey(int key) { + if (key == K_ESCAPE || key == K_ENTER) { + s_savegame_menu.cursor = s_loadgame_menu.cursor - 1; + if (s_savegame_menu.cursor < 0) + s_savegame_menu.cursor = 0; + } + return Default_MenuKey(s_loadgame_menu, key); + } + + static xcommand_t Menu_LoadGame = new xcommand_t() { + public void execute() { + Menu_LoadGame_f(); + } + }; + static void Menu_LoadGame_f() { + LoadGame_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + LoadGame_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return LoadGame_MenuKey(key); + } + }); + } + + /* + ============================================================================= + + SAVEGAME MENU + + ============================================================================= + */ + //static menuframework_s s_savegame_menu; + static menuaction_s s_savegame_actions[] = new menuaction_s[MAX_SAVEGAMES]; + + static void SaveGameCallback(Object self) { + menuaction_s a = (menuaction_s) self; + + Cbuf.AddText("save save" + a.localdata[0] + "\n"); + ForceMenuOff(); + } + + static void SaveGame_MenuDraw() { + Banner("m_banner_save_game"); + Menu_AdjustCursor(s_savegame_menu, 1); + Menu_Draw(s_savegame_menu); + } + + static void SaveGame_MenuInit() { + int i; + + s_savegame_menu.x = viddef.width / 2 - 120; + s_savegame_menu.y = viddef.height / 2 - 58; + s_savegame_menu.nitems = 0; + + Create_Savestrings(); + + // don't include the autosave slot + for (i = 0; i < MAX_SAVEGAMES - 1; i++) { + s_savegame_actions[i].name = m_savestrings[i + 1]; + s_savegame_actions[i].localdata[0] = i + 1; + s_savegame_actions[i].flags = QMF_LEFT_JUSTIFY; + s_savegame_actions[i].callback = new mcallback() { + public void execute(Object o) { + SaveGameCallback(o); + } + }; + + s_savegame_actions[i].x = 0; + s_savegame_actions[i].y = (i) * 10; + + s_savegame_actions[i].type = MTYPE_ACTION; + + Menu_AddItem(s_savegame_menu, s_savegame_actions[i]); + } + } + + static String SaveGame_MenuKey(int key) { + if (key == K_ENTER || key == K_ESCAPE) { + s_loadgame_menu.cursor = s_savegame_menu.cursor - 1; + if (s_loadgame_menu.cursor < 0) + s_loadgame_menu.cursor = 0; + } + return Default_MenuKey(s_savegame_menu, key); + } + + static xcommand_t Menu_SaveGame = new xcommand_t() { + public void execute() { + Menu_SaveGame_f(); + } + }; + static void Menu_SaveGame_f() { + if (0 == Com.ServerState()) + return; // not playing a game + + SaveGame_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + SaveGame_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return SaveGame_MenuKey(key); + } + }); + Create_Savestrings(); + } + + /* + ============================================================================= + + JOIN SERVER MENU + + ============================================================================= + */ + + static menuframework_s s_joinserver_menu = new menuframework_s(); + static menuseparator_s s_joinserver_server_title = new menuseparator_s(); + static menuaction_s s_joinserver_search_action = new menuaction_s(); + static menuaction_s s_joinserver_address_book_action = new menuaction_s(); + + static netadr_t local_server_netadr[] = new netadr_t[MAX_LOCAL_SERVERS]; + static String local_server_names[] = new String[MAX_LOCAL_SERVERS]; //[80]; + static menuaction_s s_joinserver_server_actions[] = new menuaction_s[MAX_LOCAL_SERVERS]; + + // user readable information + // network address + static { + for (int n = 0; n < MAX_LOCAL_SERVERS; n++) { + local_server_netadr[n] = new netadr_t(); + local_server_names[n] = ""; + s_joinserver_server_actions[n] = new menuaction_s(); + s_joinserver_server_actions[n].n = n; + } + } + + static int m_num_servers; + + static void AddToServerList(netadr_t adr, String info) { + int i; + + if (m_num_servers == MAX_LOCAL_SERVERS) + return; + + String x = info.trim(); + + // ignore if duplicated + for (i = 0; i < m_num_servers; i++) + if (0 == strcmp(x, local_server_names[i])) + return; + + local_server_netadr[m_num_servers] = adr; + local_server_names[m_num_servers] = x; + m_num_servers++; + } + + static void JoinServerFunc(Object self) { + String buffer; + int index; + + index = ((menucommon_s) self).n; + + if (Q_stricmp(local_server_names[index], NO_SERVER_STRING) == 0) + return; + + if (index >= m_num_servers) + return; + + buffer = "connect " + NET.AdrToString(local_server_netadr[index]) + "\n"; + Cbuf.AddText(buffer); + ForceMenuOff(); + } + + static void AddressBookFunc(Object self) { + Menu_AddressBook_f(); + } + + static void NullCursorDraw(Object self) { + } + + static void SearchLocalGames() { + int i; + + m_num_servers = 0; + for (i = 0; i < MAX_LOCAL_SERVERS; i++) + local_server_names[i] = NO_SERVER_STRING; + + DrawTextBox(8, 120 - 48, 36, 3); + Print(16 + 16, 120 - 48 + 8, "Searching for local servers, this"); + Print(16 + 16, 120 - 48 + 16, "could take up to a minute, so"); + Print(16 + 16, 120 - 48 + 24, "please be patient."); + + // the text box won't show up unless we do a buffer swap + re.EndFrame(); + + // send out info packets + CL.PingServers_f.execute(); + } + + static void SearchLocalGamesFunc(Object self) { + SearchLocalGames(); + } + + static void JoinServer_MenuInit() { + int i; + + s_joinserver_menu.x = (int) (viddef.width * 0.50 - 120); + s_joinserver_menu.nitems = 0; + + s_joinserver_address_book_action.type = MTYPE_ACTION; + s_joinserver_address_book_action.name = "address book"; + s_joinserver_address_book_action.flags = QMF_LEFT_JUSTIFY; + s_joinserver_address_book_action.x = 0; + s_joinserver_address_book_action.y = 0; + s_joinserver_address_book_action.callback = new mcallback() { + public void execute(Object o) { + AddressBookFunc(o); + } + }; + + s_joinserver_search_action.type = MTYPE_ACTION; + s_joinserver_search_action.name = "refresh server list"; + s_joinserver_search_action.flags = QMF_LEFT_JUSTIFY; + s_joinserver_search_action.x = 0; + s_joinserver_search_action.y = 10; + s_joinserver_search_action.callback = new mcallback() { + public void execute(Object o) { + SearchLocalGamesFunc(o); + } + }; + s_joinserver_search_action.statusbar = "search for servers"; + + s_joinserver_server_title.type = MTYPE_SEPARATOR; + s_joinserver_server_title.name = "connect to..."; + s_joinserver_server_title.x = 80; + s_joinserver_server_title.y = 30; + + for (i = 0; i < MAX_LOCAL_SERVERS; i++) { + s_joinserver_server_actions[i].type = MTYPE_ACTION; + local_server_names[i] = NO_SERVER_STRING; + s_joinserver_server_actions[i].name = local_server_names[i]; + s_joinserver_server_actions[i].flags = QMF_LEFT_JUSTIFY; + s_joinserver_server_actions[i].x = 0; + s_joinserver_server_actions[i].y = 40 + i * 10; + s_joinserver_server_actions[i].callback = new mcallback() { + public void execute(Object o) { + JoinServerFunc(o); + } + }; + s_joinserver_server_actions[i].statusbar = "press ENTER to connect"; + } + + Menu_AddItem(s_joinserver_menu, s_joinserver_address_book_action); + Menu_AddItem(s_joinserver_menu, s_joinserver_server_title); + Menu_AddItem(s_joinserver_menu, s_joinserver_search_action); + + for (i = 0; i < 8; i++) + Menu_AddItem(s_joinserver_menu, s_joinserver_server_actions[i]); + + Menu_Center(s_joinserver_menu); + + SearchLocalGames(); + } + + static void JoinServer_MenuDraw() { + Banner("m_banner_join_server"); + Menu_Draw(s_joinserver_menu); + } + + static String JoinServer_MenuKey(int key) { + return Default_MenuKey(s_joinserver_menu, key); + } + + static xcommand_t Menu_JoinServer = new xcommand_t() { + public void execute() { + Menu_JoinServer_f(); + } + }; + static void Menu_JoinServer_f() { + JoinServer_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + JoinServer_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return JoinServer_MenuKey(key); + } + }); + } + + /* + ============================================================================= + + START SERVER MENU + + ============================================================================= + */ + static menuframework_s s_startserver_menu = new menuframework_s(); + static String mapnames[]; + static int nummaps; + + static menuaction_s s_startserver_start_action = new menuaction_s(); + static menuaction_s s_startserver_dmoptions_action = new menuaction_s(); + static menufield_s s_timelimit_field = new menufield_s(); + static menufield_s s_fraglimit_field = new menufield_s(); + static menufield_s s_maxclients_field = new menufield_s(); + static menufield_s s_hostname_field = new menufield_s(); + static menulist_s s_startmap_list = new menulist_s(); + static menulist_s s_rules_box = new menulist_s(); + + static void DMOptionsFunc(Object self) { + if (s_rules_box.curvalue == 1) + return; + Menu_DMOptions_f(); + } + + static void RulesChangeFunc(Object self) { + // DM + if (s_rules_box.curvalue == 0) { + s_maxclients_field.statusbar = null; + s_startserver_dmoptions_action.statusbar = null; + } + else if (s_rules_box.curvalue == 1) + // coop // PGM + { + s_maxclients_field.statusbar = "4 maximum for cooperative"; + if (atoi(s_maxclients_field.buffer.toString()) > 4) + s_maxclients_field.buffer = new StringBuffer("4"); + s_startserver_dmoptions_action.statusbar = "N/A for cooperative"; + } + // ===== + // PGM + // ROGUE GAMES + else if (FS.Developer_searchpath(2) == 2) { + if (s_rules_box.curvalue == 2) // tag + { + s_maxclients_field.statusbar = null; + s_startserver_dmoptions_action.statusbar = null; + } + /* + else if(s_rules_box.curvalue == 3) // deathball + { + s_maxclients_field.statusbar = null; + s_startserver_dmoptions_action.statusbar = null; + } + */ + } + // PGM + // ===== + } + + static void StartServerActionFunc(Object self) { + //char startmap[1024]; + String startmap; + int timelimit; + int fraglimit; + int maxclients; + String spot; + + //strcpy(startmap, strchr(mapnames[s_startmap_list.curvalue], '\n') + 1); + String x = mapnames[s_startmap_list.curvalue]; + + int pos = x.indexOf('\n'); + if (pos == -1) + startmap = x; + else + startmap = x.substring(pos + 1, x.length()); + + maxclients = atoi(s_maxclients_field.buffer.toString()); + timelimit = atoi(s_timelimit_field.buffer.toString()); + fraglimit = atoi(s_fraglimit_field.buffer.toString()); + + Cvar.SetValue("maxclients", ClampCvar(0, maxclients, maxclients)); + Cvar.SetValue("timelimit", ClampCvar(0, timelimit, timelimit)); + Cvar.SetValue("fraglimit", ClampCvar(0, fraglimit, fraglimit)); + Cvar.Set("hostname", s_hostname_field.buffer.toString()); + // Cvar.SetValue ("deathmatch", !s_rules_box.curvalue ); + // Cvar.SetValue ("coop", s_rules_box.curvalue ); + + // PGM + if ((s_rules_box.curvalue < 2) || (FS.Developer_searchpath(2) != 2)) { + Cvar.SetValue("deathmatch", 1 - (int) (s_rules_box.curvalue)); + Cvar.SetValue("coop", s_rules_box.curvalue); + Cvar.SetValue("gamerules", 0); + } + else { + Cvar.SetValue("deathmatch", 1); + // deathmatch is always true for rogue games, right? + Cvar.SetValue("coop", 0); + // FIXME - this might need to depend on which game we're running + Cvar.SetValue("gamerules", s_rules_box.curvalue); + } + // PGM + + spot = null; + if (s_rules_box.curvalue == 1) // PGM + { + if (Q_stricmp(startmap, "bunk1") == 0) + spot = "start"; + else if (Q_stricmp(startmap, "mintro") == 0) + spot = "start"; + else if (Q_stricmp(startmap, "fact1") == 0) + spot = "start"; + else if (Q_stricmp(startmap, "power1") == 0) + spot = "pstart"; + else if (Q_stricmp(startmap, "biggun") == 0) + spot = "bstart"; + else if (Q_stricmp(startmap, "hangar1") == 0) + spot = "unitstart"; + else if (Q_stricmp(startmap, "city1") == 0) + spot = "unitstart"; + else if (Q_stricmp(startmap, "boss1") == 0) + spot = "bosstart"; + } + + if (spot != null) { + if (Com.ServerState() != 0) + Cbuf.AddText("disconnect\n"); + Cbuf.AddText("gamemap \"*" + startmap + "$" + spot + "\"\n"); + } + else { + Cbuf.AddText("map " + startmap + "\n"); + } + + ForceMenuOff(); + } + + static String dm_coop_names[] = { "deathmatch", "cooperative", null }; + static String dm_coop_names_rogue[] = { "deathmatch", "cooperative", "tag", + // "deathball", + null }; + + static void StartServer_MenuInit() { + + // ======= + // PGM + // ======= + + byte[] buffer = null; + //char mapsname[1024]; + String mapsname; + String s; + int length; + int i; + RandomAccessFile fp; + + /* + ** load the list of map names + */ + mapsname = FS.Gamedir() + "/maps.lst"; + + if ((fp = fopen(mapsname, "r")) == null) { + buffer = FS.LoadFile("maps.lst"); + if (buffer == null) + //if ((length = FS_LoadFile("maps.lst", (Object *) & buffer)) == -1) + Com.Error(ERR_DROP, "couldn't find maps.lst\n"); + } + else { + try { + int len = (int) fp.length(); + buffer = new byte[len]; + fp.readFully(buffer); + } + catch (Exception e) { + Com.Error(ERR_DROP, "couldn't load maps.lst\n"); + } + } + + s = new String(buffer); + String lines[] = Lib.linesplit(s); + + nummaps = lines.length; + + if (nummaps == 0) + Com.Error(ERR_DROP, "no maps in maps.lst\n"); + + mapnames = new String[nummaps + 1]; + + for (i = 0; i < nummaps; i++) { + String shortname, longname, scratch; + + Com.ParseHelp ph = new Com.ParseHelp(lines[i]); + + shortname = Com.Parse(ph).toUpperCase(); + longname = Com.Parse(ph); + scratch = longname + "\n" + shortname; + mapnames[i] = scratch; + } + mapnames[nummaps] = null; + + if (fp != null) { + fclose(fp); + fp = null; + + } + else { + FS.FreeFile(buffer); + } + + /* + ** initialize the menu stuff + */ + s_startserver_menu.x = (int) (viddef.width * 0.50); + s_startserver_menu.nitems = 0; + + s_startmap_list.type = MTYPE_SPINCONTROL; + s_startmap_list.x = 0; + s_startmap_list.y = 0; + s_startmap_list.name = "initial map"; + s_startmap_list.itemnames = mapnames; + + s_rules_box.type = MTYPE_SPINCONTROL; + s_rules_box.x = 0; + s_rules_box.y = 20; + s_rules_box.name = "rules"; + + // PGM - rogue games only available with rogue DLL. + if (FS.Developer_searchpath(2) == 2) + s_rules_box.itemnames = dm_coop_names_rogue; + else + s_rules_box.itemnames = dm_coop_names; + // PGM + + if (Cvar.VariableValue("coop") != 0) + s_rules_box.curvalue = 1; + else + s_rules_box.curvalue = 0; + s_rules_box.callback = new mcallback() { + public void execute(Object o) { + RulesChangeFunc(o); + } + }; + + s_timelimit_field.type = MTYPE_FIELD; + s_timelimit_field.name = "time limit"; + s_timelimit_field.flags = QMF_NUMBERSONLY; + s_timelimit_field.x = 0; + s_timelimit_field.y = 36; + s_timelimit_field.statusbar = "0 = no limit"; + s_timelimit_field.length = 3; + s_timelimit_field.visible_length = 3; + s_timelimit_field.buffer = new StringBuffer(Cvar.VariableString("timelimit")); + + s_fraglimit_field.type = MTYPE_FIELD; + s_fraglimit_field.name = "frag limit"; + s_fraglimit_field.flags = QMF_NUMBERSONLY; + s_fraglimit_field.x = 0; + s_fraglimit_field.y = 54; + s_fraglimit_field.statusbar = "0 = no limit"; + s_fraglimit_field.length = 3; + s_fraglimit_field.visible_length = 3; + s_fraglimit_field.buffer = new StringBuffer(Cvar.VariableString("fraglimit")); + + /* + ** maxclients determines the maximum number of players that can join + ** the game. If maxclients is only "1" then we should default the menu + ** option to 8 players, otherwise use whatever its current value is. + ** Clamping will be done when the server is actually started. + */ + s_maxclients_field.type = MTYPE_FIELD; + s_maxclients_field.name = "max players"; + s_maxclients_field.flags = QMF_NUMBERSONLY; + s_maxclients_field.x = 0; + s_maxclients_field.y = 72; + s_maxclients_field.statusbar = null; + s_maxclients_field.length = 3; + s_maxclients_field.visible_length = 3; + if (Cvar.VariableValue("maxclients") == 1) + s_maxclients_field.buffer = new StringBuffer("8"); + else + s_maxclients_field.buffer = new StringBuffer(Cvar.VariableString("maxclients")); + + s_hostname_field.type = MTYPE_FIELD; + s_hostname_field.name = "hostname"; + s_hostname_field.flags = 0; + s_hostname_field.x = 0; + s_hostname_field.y = 90; + s_hostname_field.statusbar = null; + s_hostname_field.length = 12; + s_hostname_field.visible_length = 12; + s_hostname_field.buffer = new StringBuffer(Cvar.VariableString("hostname")); + + s_startserver_dmoptions_action.type = MTYPE_ACTION; + s_startserver_dmoptions_action.name = " deathmatch flags"; + s_startserver_dmoptions_action.flags = QMF_LEFT_JUSTIFY; + s_startserver_dmoptions_action.x = 24; + s_startserver_dmoptions_action.y = 108; + s_startserver_dmoptions_action.statusbar = null; + s_startserver_dmoptions_action.callback = new mcallback() { + public void execute(Object o) { + DMOptionsFunc(o); + } + }; + + s_startserver_start_action.type = MTYPE_ACTION; + s_startserver_start_action.name = " begin"; + s_startserver_start_action.flags = QMF_LEFT_JUSTIFY; + s_startserver_start_action.x = 24; + s_startserver_start_action.y = 128; + s_startserver_start_action.callback = new mcallback() { + public void execute(Object o) { + StartServerActionFunc(o); + } + }; + + Menu_AddItem(s_startserver_menu, s_startmap_list); + Menu_AddItem(s_startserver_menu, s_rules_box); + Menu_AddItem(s_startserver_menu, s_timelimit_field); + Menu_AddItem(s_startserver_menu, s_fraglimit_field); + Menu_AddItem(s_startserver_menu, s_maxclients_field); + Menu_AddItem(s_startserver_menu, s_hostname_field); + Menu_AddItem(s_startserver_menu, s_startserver_dmoptions_action); + Menu_AddItem(s_startserver_menu, s_startserver_start_action); + + Menu_Center(s_startserver_menu); + + // call this now to set proper inital state + RulesChangeFunc(null); + } + + static void StartServer_MenuDraw() { + Menu_Draw(s_startserver_menu); + } + + static String StartServer_MenuKey(int key) { + if (key == K_ESCAPE) { + if (mapnames != null) { + int i; + + for (i = 0; i < nummaps; i++) + mapnames[i] = null; + + } + mapnames = null; + nummaps = 0; + } + + return Default_MenuKey(s_startserver_menu, key); + } + + static xcommand_t Menu_StartServer = new xcommand_t() { + public void execute() { + Menu_StartServer_f(); + } + }; + static void Menu_StartServer_f() { + StartServer_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + StartServer_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return StartServer_MenuKey(key); + } + }); + } + + /* + ============================================================================= + + DMOPTIONS BOOK MENU + + ============================================================================= + */ + static String dmoptions_statusbar; //[128]; + + static menuframework_s s_dmoptions_menu = new menuframework_s(); + + static menulist_s s_friendlyfire_box = new menulist_s(); + static menulist_s s_falls_box = new menulist_s(); + static menulist_s s_weapons_stay_box = new menulist_s(); + static menulist_s s_instant_powerups_box = new menulist_s(); + static menulist_s s_powerups_box = new menulist_s(); + static menulist_s s_health_box = new menulist_s(); + static menulist_s s_spawn_farthest_box = new menulist_s(); + static menulist_s s_teamplay_box = new menulist_s(); + static menulist_s s_samelevel_box = new menulist_s(); + static menulist_s s_force_respawn_box = new menulist_s(); + static menulist_s s_armor_box = new menulist_s(); + static menulist_s s_allow_exit_box = new menulist_s(); + static menulist_s s_infinite_ammo_box = new menulist_s(); + static menulist_s s_fixed_fov_box = new menulist_s(); + static menulist_s s_quad_drop_box = new menulist_s(); + + // ROGUE + static menulist_s s_no_mines_box = new menulist_s(); + static menulist_s s_no_nukes_box = new menulist_s(); + static menulist_s s_stack_double_box = new menulist_s(); + static menulist_s s_no_spheres_box = new menulist_s(); + // ROGUE + + static void setvalue(int flags) { + Cvar.SetValue("dmflags", flags); + dmoptions_statusbar = "dmflags = " + flags; + } + + static void DMFlagCallback(Object self) { + menulist_s f = (menulist_s) self; + int flags; + int bit = 0; + + flags = (int) Cvar.VariableValue("dmflags"); + + if (f == s_friendlyfire_box) { + if (f.curvalue != 0) + flags &= ~DF_NO_FRIENDLY_FIRE; + else + flags |= DF_NO_FRIENDLY_FIRE; + setvalue(flags); + return; + } + else if (f == s_falls_box) { + if (f.curvalue != 0) + flags &= ~DF_NO_FALLING; + else + flags |= DF_NO_FALLING; + setvalue(flags); + return; + } + else if (f == s_weapons_stay_box) { + bit = DF_WEAPONS_STAY; + } + else if (f == s_instant_powerups_box) { + bit = DF_INSTANT_ITEMS; + } + else if (f == s_allow_exit_box) { + bit = DF_ALLOW_EXIT; + } + else if (f == s_powerups_box) { + if (f.curvalue != 0) + flags &= ~DF_NO_ITEMS; + else + flags |= DF_NO_ITEMS; + setvalue(flags); + return; + } + else if (f == s_health_box) { + if (f.curvalue != 0) + flags &= ~DF_NO_HEALTH; + else + flags |= DF_NO_HEALTH; + setvalue(flags); + return; + } + else if (f == s_spawn_farthest_box) { + bit = DF_SPAWN_FARTHEST; + } + else if (f == s_teamplay_box) { + if (f.curvalue == 1) { + flags |= DF_SKINTEAMS; + flags &= ~DF_MODELTEAMS; + } + else if (f.curvalue == 2) { + flags |= DF_MODELTEAMS; + flags &= ~DF_SKINTEAMS; + } + else { + flags &= ~(DF_MODELTEAMS | DF_SKINTEAMS); + } + + setvalue(flags); + return; + } + else if (f == s_samelevel_box) { + bit = DF_SAME_LEVEL; + } + else if (f == s_force_respawn_box) { + bit = DF_FORCE_RESPAWN; + } + else if (f == s_armor_box) { + if (f.curvalue != 0) + flags &= ~DF_NO_ARMOR; + else + flags |= DF_NO_ARMOR; + setvalue(flags); + return; + } + else if (f == s_infinite_ammo_box) { + bit = DF_INFINITE_AMMO; + } + else if (f == s_fixed_fov_box) { + bit = DF_FIXED_FOV; + } + else if (f == s_quad_drop_box) { + bit = DF_QUAD_DROP; + } + + // ======= + // ROGUE + else if (FS.Developer_searchpath(2) == 2) { + if (f == s_no_mines_box) { + bit = DF_NO_MINES; + } + else if (f == s_no_nukes_box) { + bit = DF_NO_NUKES; + } + else if (f == s_stack_double_box) { + bit = DF_NO_STACK_DOUBLE; + } + else if (f == s_no_spheres_box) { + bit = DF_NO_SPHERES; + } + } + // ROGUE + // ======= + + if (f != null) { + if (f.curvalue == 0) + flags &= ~bit; + else + flags |= bit; + } + + Cvar.SetValue("dmflags", flags); + + dmoptions_statusbar = "dmflags = " + flags; + + } + + //static String yes_no_names[] = { "no", "yes", 0 }; + static String teamplay_names[] = { "disabled", "by skin", "by model", null }; + + static void DMOptions_MenuInit() { + + int dmflags = (int) Cvar.VariableValue("dmflags"); + int y = 0; + + s_dmoptions_menu.x = (int) (viddef.width * 0.50); + s_dmoptions_menu.nitems = 0; + + s_falls_box.type = MTYPE_SPINCONTROL; + s_falls_box.x = 0; + s_falls_box.y = y; + s_falls_box.name = "falling damage"; + s_falls_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_falls_box.itemnames = yes_no_names; + s_falls_box.curvalue = (dmflags & DF_NO_FALLING) == 0 ? 1 : 0; + + s_weapons_stay_box.type = MTYPE_SPINCONTROL; + s_weapons_stay_box.x = 0; + s_weapons_stay_box.y = y += 10; + s_weapons_stay_box.name = "weapons stay"; + s_weapons_stay_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_weapons_stay_box.itemnames = yes_no_names; + s_weapons_stay_box.curvalue = (dmflags & DF_WEAPONS_STAY) != 0 ? 1 : 0; + + s_instant_powerups_box.type = MTYPE_SPINCONTROL; + s_instant_powerups_box.x = 0; + s_instant_powerups_box.y = y += 10; + s_instant_powerups_box.name = "instant powerups"; + s_instant_powerups_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_instant_powerups_box.itemnames = yes_no_names; + s_instant_powerups_box.curvalue = (dmflags & DF_INSTANT_ITEMS) != 0 ? 1 : 0; + + s_powerups_box.type = MTYPE_SPINCONTROL; + s_powerups_box.x = 0; + s_powerups_box.y = y += 10; + s_powerups_box.name = "allow powerups"; + s_powerups_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_powerups_box.itemnames = yes_no_names; + s_powerups_box.curvalue = (dmflags & DF_NO_ITEMS) == 0 ? 1 : 0; + + s_health_box.type = MTYPE_SPINCONTROL; + s_health_box.x = 0; + s_health_box.y = y += 10; + s_health_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_health_box.name = "allow health"; + s_health_box.itemnames = yes_no_names; + s_health_box.curvalue = (dmflags & DF_NO_HEALTH) == 0 ? 1 : 0; + + s_armor_box.type = MTYPE_SPINCONTROL; + s_armor_box.x = 0; + s_armor_box.y = y += 10; + s_armor_box.name = "allow armor"; + s_armor_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_armor_box.itemnames = yes_no_names; + s_armor_box.curvalue = (dmflags & DF_NO_ARMOR) == 0 ? 1 : 0; + + s_spawn_farthest_box.type = MTYPE_SPINCONTROL; + s_spawn_farthest_box.x = 0; + s_spawn_farthest_box.y = y += 10; + s_spawn_farthest_box.name = "spawn farthest"; + s_spawn_farthest_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_spawn_farthest_box.itemnames = yes_no_names; + s_spawn_farthest_box.curvalue = (dmflags & DF_SPAWN_FARTHEST) != 0 ? 1 : 0; + + s_samelevel_box.type = MTYPE_SPINCONTROL; + s_samelevel_box.x = 0; + s_samelevel_box.y = y += 10; + s_samelevel_box.name = "same map"; + s_samelevel_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_samelevel_box.itemnames = yes_no_names; + s_samelevel_box.curvalue = (dmflags & DF_SAME_LEVEL) != 0 ? 1 : 0; + + s_force_respawn_box.type = MTYPE_SPINCONTROL; + s_force_respawn_box.x = 0; + s_force_respawn_box.y = y += 10; + s_force_respawn_box.name = "force respawn"; + s_force_respawn_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_force_respawn_box.itemnames = yes_no_names; + s_force_respawn_box.curvalue = (dmflags & DF_FORCE_RESPAWN) != 0 ? 1 : 0; + + s_teamplay_box.type = MTYPE_SPINCONTROL; + s_teamplay_box.x = 0; + s_teamplay_box.y = y += 10; + s_teamplay_box.name = "teamplay"; + s_teamplay_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_teamplay_box.itemnames = teamplay_names; + + s_allow_exit_box.type = MTYPE_SPINCONTROL; + s_allow_exit_box.x = 0; + s_allow_exit_box.y = y += 10; + s_allow_exit_box.name = "allow exit"; + s_allow_exit_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_allow_exit_box.itemnames = yes_no_names; + s_allow_exit_box.curvalue = (dmflags & DF_ALLOW_EXIT) != 0 ? 1 : 0; + + s_infinite_ammo_box.type = MTYPE_SPINCONTROL; + s_infinite_ammo_box.x = 0; + s_infinite_ammo_box.y = y += 10; + s_infinite_ammo_box.name = "infinite ammo"; + s_infinite_ammo_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_infinite_ammo_box.itemnames = yes_no_names; + s_infinite_ammo_box.curvalue = (dmflags & DF_INFINITE_AMMO) != 0 ? 1 : 0; + + s_fixed_fov_box.type = MTYPE_SPINCONTROL; + s_fixed_fov_box.x = 0; + s_fixed_fov_box.y = y += 10; + s_fixed_fov_box.name = "fixed FOV"; + s_fixed_fov_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_fixed_fov_box.itemnames = yes_no_names; + s_fixed_fov_box.curvalue = (dmflags & DF_FIXED_FOV) != 0 ? 1 : 0; + + s_quad_drop_box.type = MTYPE_SPINCONTROL; + s_quad_drop_box.x = 0; + s_quad_drop_box.y = y += 10; + s_quad_drop_box.name = "quad drop"; + s_quad_drop_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_quad_drop_box.itemnames = yes_no_names; + s_quad_drop_box.curvalue = (dmflags & DF_QUAD_DROP) != 0 ? 1 : 0; + + s_friendlyfire_box.type = MTYPE_SPINCONTROL; + s_friendlyfire_box.x = 0; + s_friendlyfire_box.y = y += 10; + s_friendlyfire_box.name = "friendly fire"; + s_friendlyfire_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_friendlyfire_box.itemnames = yes_no_names; + s_friendlyfire_box.curvalue = (dmflags & DF_NO_FRIENDLY_FIRE) == 0 ? 1 : 0; + + // ============ + // ROGUE + if (FS.Developer_searchpath(2) == 2) { + s_no_mines_box.type = MTYPE_SPINCONTROL; + s_no_mines_box.x = 0; + s_no_mines_box.y = y += 10; + s_no_mines_box.name = "remove mines"; + s_no_mines_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_no_mines_box.itemnames = yes_no_names; + s_no_mines_box.curvalue = (dmflags & DF_NO_MINES) != 0 ? 1 : 0; + + s_no_nukes_box.type = MTYPE_SPINCONTROL; + s_no_nukes_box.x = 0; + s_no_nukes_box.y = y += 10; + s_no_nukes_box.name = "remove nukes"; + s_no_nukes_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_no_nukes_box.itemnames = yes_no_names; + s_no_nukes_box.curvalue = (dmflags & DF_NO_NUKES) != 0 ? 1 : 0; + + s_stack_double_box.type = MTYPE_SPINCONTROL; + s_stack_double_box.x = 0; + s_stack_double_box.y = y += 10; + s_stack_double_box.name = "2x/4x stacking off"; + s_stack_double_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_stack_double_box.itemnames = yes_no_names; + s_stack_double_box.curvalue = (dmflags & DF_NO_STACK_DOUBLE); + + s_no_spheres_box.type = MTYPE_SPINCONTROL; + s_no_spheres_box.x = 0; + s_no_spheres_box.y = y += 10; + s_no_spheres_box.name = "remove spheres"; + s_no_spheres_box.callback = new mcallback() { + public void execute(Object o) { + DMFlagCallback(o); + } + }; + s_no_spheres_box.itemnames = yes_no_names; + s_no_spheres_box.curvalue = (dmflags & DF_NO_SPHERES) != 0 ? 1 : 0; + + } + // ROGUE + // ============ + + Menu_AddItem(s_dmoptions_menu, s_falls_box); + Menu_AddItem(s_dmoptions_menu, s_weapons_stay_box); + Menu_AddItem(s_dmoptions_menu, s_instant_powerups_box); + Menu_AddItem(s_dmoptions_menu, s_powerups_box); + Menu_AddItem(s_dmoptions_menu, s_health_box); + Menu_AddItem(s_dmoptions_menu, s_armor_box); + Menu_AddItem(s_dmoptions_menu, s_spawn_farthest_box); + Menu_AddItem(s_dmoptions_menu, s_samelevel_box); + Menu_AddItem(s_dmoptions_menu, s_force_respawn_box); + Menu_AddItem(s_dmoptions_menu, s_teamplay_box); + Menu_AddItem(s_dmoptions_menu, s_allow_exit_box); + Menu_AddItem(s_dmoptions_menu, s_infinite_ammo_box); + Menu_AddItem(s_dmoptions_menu, s_fixed_fov_box); + Menu_AddItem(s_dmoptions_menu, s_quad_drop_box); + Menu_AddItem(s_dmoptions_menu, s_friendlyfire_box); + + // ======= + // ROGUE + if (FS.Developer_searchpath(2) == 2) { + Menu_AddItem(s_dmoptions_menu, s_no_mines_box); + Menu_AddItem(s_dmoptions_menu, s_no_nukes_box); + Menu_AddItem(s_dmoptions_menu, s_stack_double_box); + Menu_AddItem(s_dmoptions_menu, s_no_spheres_box); + } + // ROGUE + // ======= + + Menu_Center(s_dmoptions_menu); + + // set the original dmflags statusbar + DMFlagCallback(null); + Menu_SetStatusBar(s_dmoptions_menu, dmoptions_statusbar); + } + + static void DMOptions_MenuDraw() { + Menu_Draw(s_dmoptions_menu); + } + + static String DMOptions_MenuKey(int key) { + return Default_MenuKey(s_dmoptions_menu, key); + } + + static xcommand_t Menu_DMOptions = new xcommand_t() { + public void execute() { + Menu_DMOptions_f(); + } + }; + static void Menu_DMOptions_f() { + DMOptions_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + DMOptions_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return DMOptions_MenuKey(key); + } + }); + } + + /* + ============================================================================= + + DOWNLOADOPTIONS BOOK MENU + + ============================================================================= + */ + static menuframework_s s_downloadoptions_menu = new menuframework_s(); + + static menuseparator_s s_download_title = new menuseparator_s(); + static menulist_s s_allow_download_box = new menulist_s(); + static menulist_s s_allow_download_maps_box = new menulist_s(); + static menulist_s s_allow_download_models_box = new menulist_s(); + static menulist_s s_allow_download_players_box = new menulist_s(); + static menulist_s s_allow_download_sounds_box = new menulist_s(); + + static void DownloadCallback(Object self) { + menulist_s f = (menulist_s) self; + + if (f == s_allow_download_box) { + Cvar.SetValue("allow_download", f.curvalue); + } + + else if (f == s_allow_download_maps_box) { + Cvar.SetValue("allow_download_maps", f.curvalue); + } + + else if (f == s_allow_download_models_box) { + Cvar.SetValue("allow_download_models", f.curvalue); + } + + else if (f == s_allow_download_players_box) { + Cvar.SetValue("allow_download_players", f.curvalue); + } + + else if (f == s_allow_download_sounds_box) { + Cvar.SetValue("allow_download_sounds", f.curvalue); + } + } + + static String yes_no_names[] = { "no", "yes", null }; + static void DownloadOptions_MenuInit() { + + int y = 0; + + s_downloadoptions_menu.x = (int) (viddef.width * 0.50); + s_downloadoptions_menu.nitems = 0; + + s_download_title.type = MTYPE_SEPARATOR; + s_download_title.name = "Download Options"; + s_download_title.x = 48; + s_download_title.y = y; + + s_allow_download_box.type = MTYPE_SPINCONTROL; + s_allow_download_box.x = 0; + s_allow_download_box.y = y += 20; + s_allow_download_box.name = "allow downloading"; + s_allow_download_box.callback = new mcallback() { + public void execute(Object o) { + DownloadCallback(o); + } + }; + s_allow_download_box.itemnames = yes_no_names; + s_allow_download_box.curvalue = (Cvar.VariableValue("allow_download") != 0) ? 1 : 0; + + s_allow_download_maps_box.type = MTYPE_SPINCONTROL; + s_allow_download_maps_box.x = 0; + s_allow_download_maps_box.y = y += 20; + s_allow_download_maps_box.name = "maps"; + s_allow_download_maps_box.callback = new mcallback() { + public void execute(Object o) { + DownloadCallback(o); + } + }; + s_allow_download_maps_box.itemnames = yes_no_names; + s_allow_download_maps_box.curvalue = (Cvar.VariableValue("allow_download_maps") != 0) ? 1 : 0; + + s_allow_download_players_box.type = MTYPE_SPINCONTROL; + s_allow_download_players_box.x = 0; + s_allow_download_players_box.y = y += 10; + s_allow_download_players_box.name = "player models/skins"; + s_allow_download_players_box.callback = new mcallback() { + public void execute(Object o) { + DownloadCallback(o); + } + }; + s_allow_download_players_box.itemnames = yes_no_names; + s_allow_download_players_box.curvalue = (Cvar.VariableValue("allow_download_players") != 0) ? 1 : 0; + + s_allow_download_models_box.type = MTYPE_SPINCONTROL; + s_allow_download_models_box.x = 0; + s_allow_download_models_box.y = y += 10; + s_allow_download_models_box.name = "models"; + s_allow_download_models_box.callback = new mcallback() { + public void execute(Object o) { + DownloadCallback(o); + } + }; + s_allow_download_models_box.itemnames = yes_no_names; + s_allow_download_models_box.curvalue = (Cvar.VariableValue("allow_download_models") != 0) ? 1 : 0; + + s_allow_download_sounds_box.type = MTYPE_SPINCONTROL; + s_allow_download_sounds_box.x = 0; + s_allow_download_sounds_box.y = y += 10; + s_allow_download_sounds_box.name = "sounds"; + s_allow_download_sounds_box.callback = new mcallback() { + public void execute(Object o) { + DownloadCallback(o); + } + }; + s_allow_download_sounds_box.itemnames = yes_no_names; + s_allow_download_sounds_box.curvalue = (Cvar.VariableValue("allow_download_sounds") != 0) ? 1 : 0; + + Menu_AddItem(s_downloadoptions_menu, s_download_title); + Menu_AddItem(s_downloadoptions_menu, s_allow_download_box); + Menu_AddItem(s_downloadoptions_menu, s_allow_download_maps_box); + Menu_AddItem(s_downloadoptions_menu, s_allow_download_players_box); + Menu_AddItem(s_downloadoptions_menu, s_allow_download_models_box); + Menu_AddItem(s_downloadoptions_menu, s_allow_download_sounds_box); + + Menu_Center(s_downloadoptions_menu); + + // skip over title + if (s_downloadoptions_menu.cursor == 0) + s_downloadoptions_menu.cursor = 1; + } + + static void DownloadOptions_MenuDraw() { + Menu_Draw(s_downloadoptions_menu); + } + + static String DownloadOptions_MenuKey(int key) { + return Default_MenuKey(s_downloadoptions_menu, key); + } + + static xcommand_t Menu_DownloadOptions = new xcommand_t() { + public void execute() { + Menu_DownloadOptions_f(); + } + }; + static void Menu_DownloadOptions_f() { + DownloadOptions_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + DownloadOptions_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return DownloadOptions_MenuKey(key); + } + }); + } + /* + ============================================================================= + + ADDRESS BOOK MENU + + ============================================================================= + */ + + static menuframework_s s_addressbook_menu = new menuframework_s(); + static menufield_s s_addressbook_fields[] = new menufield_s[NUM_ADDRESSBOOK_ENTRIES]; + static { + for (int n = 0; n < NUM_ADDRESSBOOK_ENTRIES; n++) + s_addressbook_fields[n] = new menufield_s(); + } + + static void AddressBook_MenuInit() { + int i; + + s_addressbook_menu.x = viddef.width / 2 - 142; + s_addressbook_menu.y = viddef.height / 2 - 58; + s_addressbook_menu.nitems = 0; + + for (i = 0; i < NUM_ADDRESSBOOK_ENTRIES; i++) { + cvar_t adr; + //char buffer[20]; + String buffer; + + //Com_sprintf(buffer, sizeof(buffer), "adr%d", i); + buffer = "adr" + i; + + adr = Cvar.Get(buffer, "", CVAR_ARCHIVE); + + s_addressbook_fields[i].type = MTYPE_FIELD; + s_addressbook_fields[i].name = null; + s_addressbook_fields[i].callback = null; + s_addressbook_fields[i].x = 0; + s_addressbook_fields[i].y = i * 18 + 0; + s_addressbook_fields[i].localdata[0] = i; + s_addressbook_fields[i].cursor = 0; + s_addressbook_fields[i].length = 60; + s_addressbook_fields[i].visible_length = 30; + + s_addressbook_fields[i].buffer = new StringBuffer(adr.string); + + Menu_AddItem(s_addressbook_menu, s_addressbook_fields[i]); + } + } + + static keyfunc_t AddressBook_MenuKey = new keyfunc_t() { + public String execute(int key) { + return AddressBook_MenuKey_f(key); + } + }; + + static String AddressBook_MenuKey_f(int key) { + if (key == K_ESCAPE) { + int index; + //char buffer[20]; + String buffer; + + for (index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++) { + buffer = "adr" + index; + //Com_sprintf(buffer, sizeof(buffer), "adr%d", index); + Cvar.Set(buffer, s_addressbook_fields[index].buffer.toString()); + } + } + return Default_MenuKey(s_addressbook_menu, key); + } + + static xcommand_t AddressBook_MenuDraw = new xcommand_t() { + public void execute() { + AddressBook_MenuDraw_f(); + } + }; + static void AddressBook_MenuDraw_f() { + Banner("m_banner_addressbook"); + Menu_Draw(s_addressbook_menu); + } + + static xcommand_t Menu_AddressBook = new xcommand_t() { + public void execute() { + Menu_AddressBook_f(); + } + }; + static void Menu_AddressBook_f() { + AddressBook_MenuInit(); + PushMenu(new xcommand_t() { + public void execute() { + AddressBook_MenuDraw_f(); + } + }, new keyfunc_t() { + public String execute(int key) { + return AddressBook_MenuKey_f(key); + } + }); + } + /* + ============================================================================= + + PLAYER CONFIG MENU + + ============================================================================= + */ + static menuframework_s s_player_config_menu = new menuframework_s(); + static menufield_s s_player_name_field = new menufield_s(); + static menulist_s s_player_model_box = new menulist_s(); + static menulist_s s_player_skin_box = new menulist_s(); + static menulist_s s_player_handedness_box = new menulist_s(); + static menulist_s s_player_rate_box = new menulist_s(); + static menuseparator_s s_player_skin_title = new menuseparator_s(); + static menuseparator_s s_player_model_title = new menuseparator_s(); + static menuseparator_s s_player_hand_title = new menuseparator_s(); + static menuseparator_s s_player_rate_title = new menuseparator_s(); + static menuaction_s s_player_download_action = new menuaction_s(); + + static class playermodelinfo_s { + int nskins; + String skindisplaynames[]; + //char displayname[MAX_DISPLAYNAME]; + String displayname; + //char directory[MAX_QPATH]; + String directory; + }; + + static playermodelinfo_s s_pmi[] = new playermodelinfo_s[MAX_PLAYERMODELS]; + static String s_pmnames[] = new String[MAX_PLAYERMODELS]; + static int s_numplayermodels; + + static int rate_tbl[] = { 2500, 3200, 5000, 10000, 25000, 0 }; + static String rate_names[] = { "28.8 Modem", "33.6 Modem", "Single ISDN", "Dual ISDN/Cable", "T1/LAN", "User defined", null }; + + static void DownloadOptionsFunc(Object self) { + Menu_DownloadOptions_f(); + } + + static void HandednessCallback(Object unused) { + Cvar.SetValue("hand", s_player_handedness_box.curvalue); + } + + static void RateCallback(Object unused) { + if (s_player_rate_box.curvalue != rate_tbl.length - 1) //sizeof(rate_tbl) / sizeof(* rate_tbl) - 1) + Cvar.SetValue("rate", rate_tbl[s_player_rate_box.curvalue]); + } + + static void ModelCallback(Object unused) { + s_player_skin_box.itemnames = s_pmi[s_player_model_box.curvalue].skindisplaynames; + s_player_skin_box.curvalue = 0; + } + + static boolean IconOfSkinExists(String skin, String pcxfiles[], int npcxfiles) { + int i; + //char scratch[1024]; + String scratch; + + //strcpy(scratch, skin); + scratch = skin; + int pos = scratch.lastIndexOf('.'); + if (pos != -1) + scratch = scratch.substring(0, pos) + "_i.pcx"; + + else + scratch += "_i.pcx"; + + for (i = 0; i < npcxfiles; i++) { + if (strcmp(pcxfiles[i], scratch) == 0) + return true; + } + + return false; + } + + static boolean PlayerConfig_ScanDirectories() { + //char findname[1024]; + String findname; + //char scratch[1024]; + String scratch; + + int ndirs = 0, npms = 0; + int a, b, c; + String dirnames[]; + + String path = null; + + int i; + + //extern String * FS_ListFiles(String , int *, unsigned, unsigned); + + s_numplayermodels = 0; + + /* + ** get a list of directories + */ + do { + path = FS.NextPath(path); + findname = path + "/players/*.*"; + + if ((dirnames = FS.ListFiles(findname, 0, SFF_SUBDIR)) != null) { + ndirs = dirnames.length; + break; + } + } + while (path != null); + + if (dirnames == null) + return false; + + /* + ** go through the subdirectories + */ + npms = ndirs; + if (npms > MAX_PLAYERMODELS) + npms = MAX_PLAYERMODELS; + + for (i = 0; i < npms; i++) { + int k, s; + //String a, b, c; + String pcxnames[]; + String skinnames[]; + int npcxfiles; + int nskins = 0; + + if (dirnames[i] == null) + continue; + + // verify the existence of tris.md2 + scratch = dirnames[i]; + scratch += "/tris.md2"; + if (Sys.FindFirst(scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) == null) { + //free(dirnames[i]); + dirnames[i] = null; + Sys.FindClose(); + continue; + } + Sys.FindClose(); + + // verify the existence of at least one pcx skin + scratch = dirnames[i] + "/*.pcx"; + pcxnames = FS.ListFiles(scratch, 0, 0); + npcxfiles = pcxnames.length; + + if (pcxnames == null) { + + dirnames[i] = null; + continue; + } + + // count valid skins, which consist of a skin with a matching "_i" icon + for (k = 0; k < npcxfiles - 1; k++) { + if (!pcxnames[k].endsWith("_i.pcx")) { + //if (!strstr(pcxnames[k], "_i.pcx")) { + if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles - 1)) { + nskins++; + } + } + } + if (nskins == 0) + continue; + + skinnames = new String[nskins + 1]; //malloc(sizeof(String) * (nskins + 1)); + //memset(skinnames, 0, sizeof(String) * (nskins + 1)); + + // copy the valid skins + for (s = 0, k = 0; k < npcxfiles - 1; k++) { + + if (!strstr(pcxnames[k], "_i.pcx")) { + if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles - 1)) { + a = pcxnames[k].lastIndexOf('/'); + b = pcxnames[k].lastIndexOf('\\'); + + if (a > b) + c = a; + else + c = b; + + scratch = pcxnames[k].substring(c + 1, pcxnames[k].length()); + int pos = scratch.lastIndexOf('.'); + if (pos != -1) + scratch = scratch.substring(0, pos); + + skinnames[s] = scratch; + s++; + } + } + } + + // at this point we have a valid player model + if (s_pmi[s_numplayermodels] == null) + s_pmi[s_numplayermodels] = new playermodelinfo_s(); + + s_pmi[s_numplayermodels].nskins = nskins; + s_pmi[s_numplayermodels].skindisplaynames = skinnames; + + // make short name for the model + a = dirnames[i].lastIndexOf('/'); + b = dirnames[i].lastIndexOf('\\'); + + if (a > b) + c = a; + else + c = b; + + s_pmi[s_numplayermodels].displayname = dirnames[i].substring(c + 1); + s_pmi[s_numplayermodels].directory = dirnames[i].substring(c + 1); + + s_numplayermodels++; + } + + return true; + + } + + static int pmicmpfnc(Object _a, Object _b) { + playermodelinfo_s a = (playermodelinfo_s) _a; + playermodelinfo_s b = (playermodelinfo_s) _b; + + /* + ** sort by male, female, then alphabetical + */ + if (strcmp(a.directory, "male") == 0) + return -1; + else if (strcmp(b.directory, "male") == 0) + return 1; + + if (strcmp(a.directory, "female") == 0) + return -1; + else if (strcmp(b.directory, "female") == 0) + return 1; + + return strcmp(a.directory, b.directory); + } + + static String handedness[] = { "right", "left", "center", null }; + + static boolean PlayerConfig_MenuInit() { + /* + extern cvar_t * name; + extern cvar_t * team; + extern cvar_t * skin; + */ + //har currentdirectory[1024]; + String currentdirectory; + //char currentskin[1024]; + String currentskin; + + int i = 0; + + int currentdirectoryindex = 0; + int currentskinindex = 0; + + cvar_t hand = Cvar.Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); + + PlayerConfig_ScanDirectories(); + + if (s_numplayermodels == 0) + return false; + + if (hand.value < 0 || hand.value > 2) + Cvar.SetValue("hand", 0); + + currentdirectory = skin.string; + + if (currentdirectory.lastIndexOf('/') != -1) { + currentskin = rightFrom(currentdirectory, '/'); + currentdirectory = leftFrom(currentdirectory, '/'); + } + else if (currentdirectory.lastIndexOf('\\') != -1) { + currentskin = rightFrom(currentdirectory, '\\'); + currentdirectory = leftFrom(currentdirectory, '\\'); + } + else { + currentdirectory = "male"; + currentskin = "grunt"; + } + + //qsort(s_pmi, s_numplayermodels, sizeof(s_pmi[0]), pmicmpfnc); + Arrays.sort(s_pmi, 0, s_numplayermodels, new Comparator() { + public int compare(Object o1, Object o2) { + return pmicmpfnc(o1, o2); + } + }); + + //memset(s_pmnames, 0, sizeof(s_pmnames)); + s_pmnames = new String[MAX_PLAYERMODELS]; + + for (i = 0; i < s_numplayermodels; i++) { + s_pmnames[i] = s_pmi[i].displayname; + if (Q_stricmp(s_pmi[i].directory, currentdirectory) == 0) { + int j; + + currentdirectoryindex = i; + + for (j = 0; j < s_pmi[i].nskins; j++) { + if (Q_stricmp(s_pmi[i].skindisplaynames[j], currentskin) == 0) { + currentskinindex = j; + break; + } + } + } + } + + s_player_config_menu.x = viddef.width / 2 - 95; + s_player_config_menu.y = viddef.height / 2 - 97; + s_player_config_menu.nitems = 0; + + s_player_name_field.type = MTYPE_FIELD; + s_player_name_field.name = "name"; + s_player_name_field.callback = null; + s_player_name_field.x = 0; + s_player_name_field.y = 0; + s_player_name_field.length = 20; + s_player_name_field.visible_length = 20; + s_player_name_field.buffer = new StringBuffer(name.string); + s_player_name_field.cursor = strlen(name.string); + + s_player_model_title.type = MTYPE_SEPARATOR; + s_player_model_title.name = "model"; + s_player_model_title.x = -8; + s_player_model_title.y = 60; + + s_player_model_box.type = MTYPE_SPINCONTROL; + s_player_model_box.x = -56; + s_player_model_box.y = 70; + s_player_model_box.callback = new mcallback() { + public void execute(Object o) { + ModelCallback(o); + } + }; + s_player_model_box.cursor_offset = -48; + s_player_model_box.curvalue = currentdirectoryindex; + s_player_model_box.itemnames = s_pmnames; + + s_player_skin_title.type = MTYPE_SEPARATOR; + s_player_skin_title.name = "skin"; + s_player_skin_title.x = -16; + s_player_skin_title.y = 84; + + s_player_skin_box.type = MTYPE_SPINCONTROL; + s_player_skin_box.x = -56; + s_player_skin_box.y = 94; + s_player_skin_box.name = null; + s_player_skin_box.callback = null; + s_player_skin_box.cursor_offset = -48; + s_player_skin_box.curvalue = currentskinindex; + s_player_skin_box.itemnames = s_pmi[currentdirectoryindex].skindisplaynames; + + s_player_hand_title.type = MTYPE_SEPARATOR; + s_player_hand_title.name = "handedness"; + s_player_hand_title.x = 32; + s_player_hand_title.y = 108; + + s_player_handedness_box.type = MTYPE_SPINCONTROL; + s_player_handedness_box.x = -56; + s_player_handedness_box.y = 118; + s_player_handedness_box.name = null; + s_player_handedness_box.cursor_offset = -48; + s_player_handedness_box.callback = new mcallback() { + public void execute(Object o) { + HandednessCallback(o); + } + }; + s_player_handedness_box.curvalue = (int) Cvar.VariableValue("hand"); + s_player_handedness_box.itemnames = handedness; + + for (i = 0; i < rate_tbl.length - 1; i++) + if (Cvar.VariableValue("rate") == rate_tbl[i]) + break; + + s_player_rate_title.type = MTYPE_SEPARATOR; + s_player_rate_title.name = "connect speed"; + s_player_rate_title.x = 56; + s_player_rate_title.y = 156; + + s_player_rate_box.type = MTYPE_SPINCONTROL; + s_player_rate_box.x = -56; + s_player_rate_box.y = 166; + s_player_rate_box.name = null; + s_player_rate_box.cursor_offset = -48; + s_player_rate_box.callback = new mcallback() { + public void execute(Object o) { + RateCallback(o); + } + }; + s_player_rate_box.curvalue = i; + s_player_rate_box.itemnames = rate_names; + + s_player_download_action.type = MTYPE_ACTION; + s_player_download_action.name = "download options"; + s_player_download_action.flags = QMF_LEFT_JUSTIFY; + s_player_download_action.x = -24; + s_player_download_action.y = 186; + s_player_download_action.statusbar = null; + s_player_download_action.callback = new mcallback() { + public void execute(Object o) { + DownloadOptionsFunc(o); + } + }; + + Menu_AddItem(s_player_config_menu, s_player_name_field); + Menu_AddItem(s_player_config_menu, s_player_model_title); + Menu_AddItem(s_player_config_menu, s_player_model_box); + if (s_player_skin_box.itemnames != null) { + Menu_AddItem(s_player_config_menu, s_player_skin_title); + Menu_AddItem(s_player_config_menu, s_player_skin_box); + } + Menu_AddItem(s_player_config_menu, s_player_hand_title); + Menu_AddItem(s_player_config_menu, s_player_handedness_box); + Menu_AddItem(s_player_config_menu, s_player_rate_title); + Menu_AddItem(s_player_config_menu, s_player_rate_box); + Menu_AddItem(s_player_config_menu, s_player_download_action); + + return true; + } + + static int yaw; + + static void PlayerConfig_MenuDraw() { + + refdef_t refdef = new refdef_t(); + //char scratch[MAX_QPATH]; + String scratch; + + //memset(refdef, 0, sizeof(refdef)); + + refdef.x = viddef.width / 2; + refdef.y = viddef.height / 2 - 72; + refdef.width = 144; + refdef.height = 168; + refdef.fov_x = 40; + refdef.fov_y = Math3D.CalcFov(refdef.fov_x, refdef.width, refdef.height); + refdef.time = cls.realtime * 0.001f; + + if (s_pmi[s_player_model_box.curvalue].skindisplaynames != null) { + + int maxframe = 29; + entity_t entity = new entity_t(); + + //memset(entity, 0, sizeof(entity)); + + scratch = "players/" + s_pmi[s_player_model_box.curvalue].directory + "/tris.md2"; + + entity.model = re.RegisterModel(scratch); + + scratch = + "players/" + + s_pmi[s_player_model_box.curvalue].directory + + "/" + + s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] + + ".pcx"; + + entity.skin = re.RegisterSkin(scratch); + entity.flags = RF_FULLBRIGHT; + entity.origin[0] = 80; + entity.origin[1] = 0; + entity.origin[2] = 0; + VectorCopy(entity.origin, entity.oldorigin); + entity.frame = 0; + entity.oldframe = 0; + entity.backlerp = 0.0f; + entity.angles[1] = yaw++; + if (++yaw > 360) + yaw -= 360; + + refdef.areabits = null; + refdef.num_entities = 1; + refdef.entities = new entity_t[] { entity }; + refdef.lightstyles = null; + refdef.rdflags = RDF_NOWORLDMODEL; + + Menu_Draw(s_player_config_menu); + + DrawTextBox( + (int) ((refdef.x) * (320.0F / viddef.width) - 8), + (int) ((viddef.height / 2) * (240.0F / viddef.height) - 77), + refdef.width / 8, + refdef.height / 8); + refdef.height += 4; + + re.RenderFrame(refdef); + + scratch = + "/players/" + + s_pmi[s_player_model_box.curvalue].directory + + "/" + + s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] + + "_i.pcx"; + + re.DrawPic(s_player_config_menu.x - 40, refdef.y, scratch); + } + } + + static String PlayerConfig_MenuKey(int key) { + int i; + + if (key == K_ESCAPE) { + //char scratch[1024]; + String scratch; + + Cvar.Set("name", s_player_name_field.buffer.toString()); + + scratch = + s_pmi[s_player_model_box.curvalue].directory + + "/" + + s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue]; + + Cvar.Set("skin", scratch); + + for (i = 0; i < s_numplayermodels; i++) { + int j; + + for (j = 0; j < s_pmi[i].nskins; j++) { + if (s_pmi[i].skindisplaynames[j] != null) + s_pmi[i].skindisplaynames[j] = null; + } + s_pmi[i].skindisplaynames = null; + s_pmi[i].nskins = 0; + } + } + return Default_MenuKey(s_player_config_menu, key); + } + + static xcommand_t Menu_PlayerConfig = new xcommand_t() { + public void execute() { + Menu_PlayerConfig_f(); + } + }; + static void Menu_PlayerConfig_f() { + if (!PlayerConfig_MenuInit()) { + Menu_SetStatusBar(s_multiplayer_menu, "No valid player models found"); + return; + } + Menu_SetStatusBar(s_multiplayer_menu, null); + PushMenu(new xcommand_t() { + public void execute() { + PlayerConfig_MenuDraw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return PlayerConfig_MenuKey(key); + } + }); + } + + /* + ======================================================================= + + QUIT MENU + + ======================================================================= + */ + + static String Quit_Key(int key) { + switch (key) { + case K_ESCAPE : + case 'n' : + case 'N' : + PopMenu(); + break; + + case 'Y' : + case 'y' : + cls.key_dest = key_console; + CL.Quit_f.execute(); + break; + + default : + break; + } + + return null; + + } + + static void Quit_Draw() { + int w, h; + Dimension d = new Dimension(); + re.DrawGetPicSize(d, "quit"); + w = d.width; + h = d.height; + re.DrawPic((viddef.width - w) / 2, (viddef.height - h) / 2, "quit"); + } + + static xcommand_t Menu_Quit = new xcommand_t() { + public void execute() { + Menu_Quit_f(); + } + }; + + static void Menu_Quit_f() { + PushMenu(new xcommand_t() { + public void execute() { + Quit_Draw(); + } + }, new keyfunc_t() { + public String execute(int key) { + return Quit_Key(key); + } + }); + } + + // ============================================================================= + /* Menu Subsystem */ + + /** + * Init + */ + public static void Init() { + Cmd.AddCommand("menu_main", Menu_Main); + Cmd.AddCommand("menu_game", Menu_Game); + Cmd.AddCommand("menu_loadgame", Menu_LoadGame); + Cmd.AddCommand("menu_savegame", Menu_SaveGame); + Cmd.AddCommand("menu_joinserver", Menu_JoinServer); + Cmd.AddCommand("menu_addressbook", Menu_AddressBook); + Cmd.AddCommand("menu_startserver", Menu_StartServer); + Cmd.AddCommand("menu_dmoptions", Menu_DMOptions); + Cmd.AddCommand("menu_playerconfig", Menu_PlayerConfig); + Cmd.AddCommand("menu_downloadoptions", Menu_DownloadOptions); + Cmd.AddCommand("menu_credits", Menu_Credits); + Cmd.AddCommand("menu_multiplayer", Menu_Multiplayer); + Cmd.AddCommand("menu_video", Menu_Video); + Cmd.AddCommand("menu_options", Menu_Options); + Cmd.AddCommand("menu_keys", Menu_Keys); + Cmd.AddCommand("menu_quit", Menu_Quit); + + for (int i = 0; i < m_layers.length; i++) { + m_layers[i] = new menulayer_t(); + } + } + + /* + ================= + Draw + ================= + */ + static void Draw() { + if (cls.key_dest != key_menu) + return; + + // repaint everything next frame + SCR.DirtyScreen(); + + // dim everything behind it down + if (cl.cinematictime > 0) + re.DrawFill(0, 0, viddef.width, viddef.height, 0); + else + re.DrawFadeScreen(); + + m_drawfunc.execute(); + + // delay playing the enter sound until after the + // menu has been drawn, to avoid delay while + // caching images + if (m_entersound) { + S.StartLocalSound(menu_in_sound); + m_entersound = false; + } + } + + /* + ================= + Keydown + ================= + */ + static void Keydown(int key) { + String s; + + if (m_keyfunc != null) + if ((s = m_keyfunc.execute(key)) != null) + S.StartLocalSound(s); + } + + public static void Action_DoEnter(menuaction_s a) { + if (a.callback != null) + a.callback.execute(a); + } + + public static void Action_Draw(menuaction_s a) { + if ((a.flags & QMF_LEFT_JUSTIFY) != 0) { + if ((a.flags & QMF_GRAYED) != 0) + Menu_DrawStringDark(a.x + a.parent.x + LCOLUMN_OFFSET, a.y + a.parent.y, a.name); + else + Menu_DrawString(a.x + a.parent.x + LCOLUMN_OFFSET, a.y + a.parent.y, a.name); + } + else { + if ((a.flags & QMF_GRAYED) != 0) + Menu_DrawStringR2LDark(a.x + a.parent.x + LCOLUMN_OFFSET, a.y + a.parent.y, a.name); + else + Menu_DrawStringR2L(a.x + a.parent.x + LCOLUMN_OFFSET, a.y + a.parent.y, a.name); + } + if (a.ownerdraw != null) + a.ownerdraw.execute(a); + } + + public static boolean Field_DoEnter(menufield_s f) { + if (f.callback != null) { + f.callback.execute(f); + return true; + } + return false; + } + + public static void Field_Draw(menufield_s f) { + int i; + String tempbuffer; + //[128] = ""; + + if (f.name != null) + Menu_DrawStringR2LDark(f.x + f.parent.x + LCOLUMN_OFFSET, f.y + f.parent.y, f.name); + + //strncpy(tempbuffer, f.buffer + f.visible_offset, f.visible_length); + String s = f.buffer.toString(); + tempbuffer = s.substring(f.visible_offset, s.length()); + re.DrawChar(f.x + f.parent.x + 16, f.y + f.parent.y - 4, 18); + re.DrawChar(f.x + f.parent.x + 16, f.y + f.parent.y + 4, 24); + + re.DrawChar(f.x + f.parent.x + 24 + f.visible_length * 8, f.y + f.parent.y - 4, 20); + re.DrawChar(f.x + f.parent.x + 24 + f.visible_length * 8, f.y + f.parent.y + 4, 26); + + for (i = 0; i < f.visible_length; i++) { + re.DrawChar(f.x + f.parent.x + 24 + i * 8, f.y + f.parent.y - 4, 19); + re.DrawChar(f.x + f.parent.x + 24 + i * 8, f.y + f.parent.y + 4, 25); + } + + Menu_DrawString(f.x + f.parent.x + 24, f.y + f.parent.y, tempbuffer); + + if (Menu_ItemAtCursor(f.parent) == f) { + int offset; + + if (f.visible_offset != 0) + offset = f.visible_length; + else + offset = f.cursor; + + if ((((int) (Sys.Milliseconds() / 250)) & 1) != 0) { + re.DrawChar(f.x + f.parent.x + (offset + 2) * 8 + 8, f.y + f.parent.y, 11); + } + else { + re.DrawChar(f.x + f.parent.x + (offset + 2) * 8 + 8, f.y + f.parent.y, ' '); + } + } + } + + public static boolean Field_Key(menufield_s f, int k) { + char key = (char) k; + + switch (key) { + case K_KP_SLASH : + key = '/'; + break; + case K_KP_MINUS : + key = '-'; + break; + case K_KP_PLUS : + key = '+'; + break; + case K_KP_HOME : + key = '7'; + break; + case K_KP_UPARROW : + key = '8'; + break; + case K_KP_PGUP : + key = '9'; + break; + case K_KP_LEFTARROW : + key = '4'; + break; + case K_KP_5 : + key = '5'; + break; + case K_KP_RIGHTARROW : + key = '6'; + break; + case K_KP_END : + key = '1'; + break; + case K_KP_DOWNARROW : + key = '2'; + break; + case K_KP_PGDN : + key = '3'; + break; + case K_KP_INS : + key = '0'; + break; + case K_KP_DEL : + key = '.'; + break; + } + + if (key > 127) { + switch (key) { + case K_DEL : + default : + return false; + } + } + + /* + ** support pasting from the clipboard + */ + if ((Character.toUpperCase(key) == 'V' && keydown[K_CTRL]) || (((key == K_INS) || (key == K_KP_INS)) && keydown[K_SHIFT])) { + String cbd; + + if ((cbd = Sys.GetClipboardData()) != null) { + //strtok(cbd, "\n\r\b"); + String lines[] = Lib.linesplit(cbd); + if (lines.length > 0 && lines[0].length() != 0) { + //strncpy(f.buffer, cbd, f.length - 1); + f.buffer = new StringBuffer(lines[0]); + f.cursor = f.buffer.length(); + + f.visible_offset = f.cursor - f.visible_length; + + if (f.visible_offset < 0) + f.visible_offset = 0; + } + } + return true; + } + + switch (key) { + case K_KP_LEFTARROW : + case K_LEFTARROW : + case K_BACKSPACE : + if (f.cursor > 0) { + f.buffer.deleteCharAt(f.cursor - 1); + //memmove(f.buffer[f.cursor - 1], f.buffer[f.cursor], strlen(& f.buffer[f.cursor]) + 1); + f.cursor--; + + if (f.visible_offset != 0) { + f.visible_offset--; + } + } + break; + + case K_KP_DEL : + case K_DEL : + //memmove(& f.buffer[f.cursor], & f.buffer[f.cursor + 1], strlen(& f.buffer[f.cursor + 1]) + 1); + f.buffer.deleteCharAt(f.cursor); + break; + + case K_KP_ENTER : + case K_ENTER : + case K_ESCAPE : + case K_TAB : + return false; + + case K_SPACE : + default : + if (!Character.isDigit(key) && (f.flags & QMF_NUMBERSONLY) != 0) + return false; + + if (f.cursor < f.length) { + f.buffer.append(key); + f.cursor++; + + if (f.cursor > f.visible_length) { + f.visible_offset++; + } + } + } + + return true; + } + + public static void Menu_AddItem(menuframework_s menu, menucommon_s item) { + if (menu.nitems == 0) + menu.nslots = 0; + + if (menu.nitems < MAXMENUITEMS) { + menu.items[menu.nitems] = item; + ((menucommon_s) menu.items[menu.nitems]).parent = menu; + menu.nitems++; + } + + menu.nslots = Menu_TallySlots(menu); + } + + /* + ** Menu_AdjustCursor + ** + ** This function takes the given menu, the direction, and attempts + ** to adjust the menu's cursor so that it's at the next available + ** slot. + */ + public static void Menu_AdjustCursor(menuframework_s m, int dir) { + menucommon_s citem; + + /* + ** see if it's in a valid spot + */ + if (m.cursor >= 0 && m.cursor < m.nitems) { + if ((citem = Menu_ItemAtCursor(m)) != null) { + if (citem.type != MTYPE_SEPARATOR) + return; + } + } + + /* + ** it's not in a valid spot, so crawl in the direction indicated until we + ** find a valid spot + */ + if (dir == 1) { + while (true) { + citem = Menu_ItemAtCursor(m); + if (citem != null) + if (citem.type != MTYPE_SEPARATOR) + break; + m.cursor += dir; + if (m.cursor >= m.nitems) + m.cursor = 0; + } + } + else { + while (true) { + citem = Menu_ItemAtCursor(m); + if (citem != null) + if (citem.type != MTYPE_SEPARATOR) + break; + m.cursor += dir; + if (m.cursor < 0) + m.cursor = m.nitems - 1; + } + } + } + + public static void Menu_Center(menuframework_s menu) { + int height; + + height = ((menucommon_s) menu.items[menu.nitems - 1]).y; + height += 10; + + menu.y = (viddef.height - height) / 2; + } + + public static void Menu_Draw(menuframework_s menu) { + int i; + menucommon_s item; + + /* + ** draw contents + */ + for (i = 0; i < menu.nitems; i++) { + switch (((menucommon_s) menu.items[i]).type) { + case MTYPE_FIELD : + Field_Draw((menufield_s) menu.items[i]); + break; + case MTYPE_SLIDER : + Slider_Draw((menuslider_s) menu.items[i]); + break; + case MTYPE_LIST : + MenuList_Draw((menulist_s) menu.items[i]); + break; + case MTYPE_SPINCONTROL : + SpinControl_Draw((menulist_s) menu.items[i]); + break; + case MTYPE_ACTION : + Action_Draw((menuaction_s) menu.items[i]); + break; + case MTYPE_SEPARATOR : + Separator_Draw((menuseparator_s) menu.items[i]); + break; + } + } + + item = Menu_ItemAtCursor(menu); + + if (item != null && item.cursordraw != null) { + item.cursordraw.execute(item); + } + else if (menu.cursordraw != null) { + menu.cursordraw.execute(menu); + } + else if (item != null && item.type != MTYPE_FIELD) { + if ((item.flags & QMF_LEFT_JUSTIFY) != 0) { + re.DrawChar(menu.x + item.x - 24 + item.cursor_offset, menu.y + item.y, 12 + ((int) (Sys.Milliseconds() / 250) & 1)); + } + else { + re.DrawChar(menu.x + item.cursor_offset, menu.y + item.y, 12 + ((int) (Sys.Milliseconds() / 250) & 1)); + } + } + + if (item != null) { + if (item.statusbarfunc != null) + item.statusbarfunc.execute(item); + else if (item.statusbar != null) + Menu_DrawStatusBar(item.statusbar); + else + Menu_DrawStatusBar(menu.statusbar); + + } + else { + Menu_DrawStatusBar(menu.statusbar); + } + } + + public static void Menu_DrawStatusBar(String string) { + if (string != null) { + int l = strlen(string); + int maxrow = viddef.height / 8; + int maxcol = viddef.width / 8; + int col = maxcol / 2 - l / 2; + + re.DrawFill(0, viddef.height - 8, viddef.width, 8, 4); + Menu_DrawString(col * 8, viddef.height - 8, string); + } + else { + re.DrawFill(0, viddef.height - 8, viddef.width, 8, 0); + } + } + + public static void Menu_DrawString(int x, int y, String string) { + int i; + + for (i = 0; i < strlen(string); i++) { + re.DrawChar((x + i * 8), y, string.charAt(i)); + } + } + + public static void Menu_DrawStringDark(int x, int y, String string) { + int i; + + for (i = 0; i < strlen(string); i++) { + re.DrawChar((x + i * 8), y, string.charAt(i) + 128); + } + } + + public static void Menu_DrawStringR2L(int x, int y, String string) { + int i; + + for (i = 0; i < strlen(string); i++) { + re.DrawChar((x - i * 8), y, string.charAt(strlen(string) - i - 1)); + } + } + + public static void Menu_DrawStringR2LDark(int x, int y, String string) { + int i; + + for (i = 0; i < strlen(string); i++) { + re.DrawChar((x - i * 8), y, string.charAt(strlen(string) - i - 1) + 128); + } + } + + public static menucommon_s Menu_ItemAtCursor(menuframework_s m) { + if (m.cursor < 0 || m.cursor >= m.nitems) + return null; + + return (menucommon_s) m.items[m.cursor]; + } + + static boolean Menu_SelectItem(menuframework_s s) { + menucommon_s item = Menu_ItemAtCursor(s); + + if (item != null) { + switch (item.type) { + case MTYPE_FIELD : + return Field_DoEnter((menufield_s) item); + case MTYPE_ACTION : + Action_DoEnter((menuaction_s) item); + return true; + case MTYPE_LIST : + // Menulist_DoEnter( ( menulist_s ) item ); + return false; + case MTYPE_SPINCONTROL : + // SpinControl_DoEnter( ( menulist_s ) item ); + return false; + } + } + return false; + } + + public static void Menu_SetStatusBar(menuframework_s m, String string) { + m.statusbar = string; + } + + public static void Menu_SlideItem(menuframework_s s, int dir) { + menucommon_s item = (menucommon_s) Menu_ItemAtCursor(s); + + if (item != null) { + switch (item.type) { + case MTYPE_SLIDER : + Slider_DoSlide((menuslider_s) item, dir); + break; + case MTYPE_SPINCONTROL : + SpinControl_DoSlide((menulist_s) item, dir); + break; + } + } + } + + public static int Menu_TallySlots(menuframework_s menu) { + int i; + int total = 0; + + for (i = 0; i < menu.nitems; i++) { + if (((menucommon_s) menu.items[i]).type == MTYPE_LIST) { + int nitems = 0; + String n[] = ((menulist_s) menu.items[i]).itemnames; + + while (n[nitems] != null) + nitems++; + + total += nitems; + } + else { + total++; + } + } + + return total; + } + + public static void Menulist_DoEnter(menulist_s l) { + int start; + + start = l.y / 10 + 1; + + l.curvalue = l.parent.cursor - start; + + if (l.callback != null) + l.callback.execute(l); + } + + public static void MenuList_Draw(menulist_s l) { + String n[]; + int y = 0; + + Menu_DrawStringR2LDark(l.x + l.parent.x + LCOLUMN_OFFSET, l.y + l.parent.y, l.name); + + n = l.itemnames; + + re.DrawFill(l.x - 112 + l.parent.x, l.parent.y + l.y + l.curvalue * 10 + 10, 128, 10, 16); + int i = 0; + + while (n[i] != null) { + Menu_DrawStringR2LDark(l.x + l.parent.x + LCOLUMN_OFFSET, l.y + l.parent.y + y + 10, n[i]); + + i++; + y += 10; + } + } + + public static void Separator_Draw(menuseparator_s s) { + if (s.name != null) + Menu_DrawStringR2LDark(s.x + s.parent.x, s.y + s.parent.y, s.name); + } + + public static void Slider_DoSlide(menuslider_s s, int dir) { + s.curvalue += dir; + + if (s.curvalue > s.maxvalue) + s.curvalue = s.maxvalue; + else if (s.curvalue < s.minvalue) + s.curvalue = s.minvalue; + + if (s.callback != null) + s.callback.execute(s); + } + + public static final int SLIDER_RANGE = 10; + + public static void Slider_Draw(menuslider_s s) { + int i; + + Menu_DrawStringR2LDark(s.x + s.parent.x + LCOLUMN_OFFSET, s.y + s.parent.y, s.name); + + s.range = (s.curvalue - s.minvalue) / (float) (s.maxvalue - s.minvalue); + + if (s.range < 0) + s.range = 0; + if (s.range > 1) + s.range = 1; + re.DrawChar(s.x + s.parent.x + RCOLUMN_OFFSET, s.y + s.parent.y, 128); + for (i = 0; i < SLIDER_RANGE; i++) + re.DrawChar(RCOLUMN_OFFSET + s.x + i * 8 + s.parent.x + 8, s.y + s.parent.y, 129); + re.DrawChar(RCOLUMN_OFFSET + s.x + i * 8 + s.parent.x + 8, s.y + s.parent.y, 130); + re.DrawChar((int) (8 + RCOLUMN_OFFSET + s.parent.x + s.x + (SLIDER_RANGE - 1) * 8 * s.range), s.y + s.parent.y, 131); + } + + public static void SpinControl_DoEnter(menulist_s s) { + s.curvalue++; + if (s.itemnames[s.curvalue] == null) + s.curvalue = 0; + + if (s.callback != null) + s.callback.execute(s); + } + + public static void SpinControl_DoSlide(menulist_s s, int dir) { + s.curvalue += dir; + + if (s.curvalue < 0) + s.curvalue = 0; + else if (s.itemnames[s.curvalue] == null) + s.curvalue--; + + if (s.callback != null) + s.callback.execute(s); + } + + public static void SpinControl_Draw(menulist_s s) { + //char buffer[100]; + String buffer; + + if (s.name != null) { + Menu_DrawStringR2LDark(s.x + s.parent.x + LCOLUMN_OFFSET, s.y + s.parent.y, s.name); + } + + if (s.itemnames[s.curvalue].indexOf('\n') == -1) { + Menu_DrawString(RCOLUMN_OFFSET + s.x + s.parent.x, s.y + s.parent.y, s.itemnames[s.curvalue]); + } + else { + String line1, line2; + line1 = Lib.leftFrom(s.itemnames[s.curvalue], '\n'); + Menu_DrawString(RCOLUMN_OFFSET + s.x + s.parent.x, s.y + s.parent.y, line1); + + line2 = Lib.rightFrom(s.itemnames[s.curvalue], '\n'); + + int pos = line2.indexOf('\n'); + if (pos != -1) + line2 = line2.substring(0, pos); + + Menu_DrawString(RCOLUMN_OFFSET + s.x + s.parent.x, s.y + s.parent.y + 10, line2); + } + } +} diff --git a/src/jake2/client/S.java b/src/jake2/client/S.java new file mode 100644 index 0000000..302de14 --- /dev/null +++ b/src/jake2/client/S.java @@ -0,0 +1,33 @@ +/* + * S.java + * Copyright (C) 2003 + * + * $Id: S.java,v 1.1 2004-07-07 19:58:51 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; + +/** + * S + */ +public class S extends SND_DMA { + +} diff --git a/src/jake2/client/SCR.java b/src/jake2/client/SCR.java new file mode 100644 index 0000000..9755008 --- /dev/null +++ b/src/jake2/client/SCR.java @@ -0,0 +1,1420 @@ +/* + * SCR.java + * Copyright (C) 2003 + * + * $Id: SCR.java,v 1.1 2004-07-07 19:58:51 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.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 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 <basename> <rotate> <axis x y z>\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); + + // + // 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 + CDAudio.Stop(); + 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; + int ptrp; + 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); + ptrp = 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(); +// +// 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); + } + + // wird anstelle von der richtigen UpdateScreen benoetigt + public static void UpdateScreen() { + Globals.re.updateScreen(null); + } + + /* + ================= + 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; + } + + + public static void PlayCinematic(String str) { + // TODO: implement PlayCinematic + } + + 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'); + } +}
\ No newline at end of file diff --git a/src/jake2/client/SND_DMA.java b/src/jake2/client/SND_DMA.java new file mode 100644 index 0000000..fba94c1 --- /dev/null +++ b/src/jake2/client/SND_DMA.java @@ -0,0 +1,1247 @@ +/* + * S_DMA.java + * Copyright (C) 2004 + * + * $Id: SND_DMA.java,v 1.1 2004-07-07 19:58:51 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. + +*/ + +// Created on 26.01.2004 by RST. + +package jake2.client; + +import jake2.game.Cmd; +import jake2.game.cvar_t; +import jake2.qcommon.*; +import jake2.util.Vargs; + + + +/** + * SND_DMA + * TODO implement sound system + */ +public class SND_DMA extends SND_MIX { +//// snd_dma.c -- main control for any streaming sound output device +// +// #include "client.h" +// #include "snd_loc.h" +// +// void S_Play(void); +// void S_SoundList(void); +// void S_Update_(); +// void S_StopAllSounds(void); +// +// +//// ======================================================================= +//// Internal sound data & structures +//// ======================================================================= +// +//// only begin attenuating sound volumes when outside the FULLVOLUME range +// #define SOUND_FULLVOLUME 80 +// +// #define SOUND_LOOPATTENUATE 0.003 +// + static int s_registration_sequence; +// +// channel_t channels[MAX_CHANNELS]; +// +// qboolean snd_initialized = false; + static boolean sound_started = false; +// + + +// +// vec3_t listener_origin; +// vec3_t listener_forward; +// vec3_t listener_right; +// vec3_t listener_up; +// + static boolean s_registering; +// + static int soundtime; // sample PAIRS + static int paintedtime; // sample PAIRS +// +//// during registration it is possible to have more sounds +//// than could actually be referenced during gameplay, +//// because we don't want to free anything until we are +//// sure we won't need it. + static final int MAX_SFX = (MAX_SOUNDS*2); + static sfx_t[] known_sfx = new sfx_t[MAX_SFX]; + static { + for (int i = 0; i< known_sfx.length; i++) + known_sfx[i] = new sfx_t(); + } + static int num_sfx; +// +// #define MAX_PLAYSOUNDS 128 +// playsound_t s_playsounds[MAX_PLAYSOUNDS]; +// playsound_t s_freeplays; +// playsound_t s_pendingplays; +// +// int s_beginofs; +// + static cvar_t s_volume; + static cvar_t s_testsound; + static cvar_t s_loadas8bit; + static cvar_t s_khz; + static cvar_t s_show; + static cvar_t s_mixahead; + static cvar_t s_primary; +// +// +// int s_rawend; +// portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; +// +// +// ==================================================================== +// User-setable variables +// ==================================================================== + + + static void SoundInfo_f() + { + if (!sound_started) + { + Com.Printf ("sound system not started\n"); + return; + } + + Com.Printf("%5d stereo\n", new Vargs(1).add(dma.channels - 1)); + Com.Printf("%5d samples\n", new Vargs(1).add(dma.samples)); + Com.Printf("%5d samplepos\n", new Vargs(1).add(dma.samplepos)); + Com.Printf("%5d samplebits\n", new Vargs(1).add(dma.samplebits)); + Com.Printf("%5d submission_chunk\n", new Vargs(1).add(dma.submission_chunk)); + Com.Printf("%5d speed\n", new Vargs(1).add(dma.speed)); + } + + /* + ================ + S_Init + ================ + */ + public static void Init() { + cvar_t cv; + + Com.Printf("\n------- sound initialization -------\n"); + + cv = Cvar.Get("s_initsound", "1", 0); + if (cv.value == 0.0f) + Com.Printf("not initializing.\n"); + else { + s_volume = Cvar.Get("s_volume", "0.7", CVAR_ARCHIVE); + s_khz = Cvar.Get("s_khz", "11", CVAR_ARCHIVE); + s_loadas8bit = Cvar.Get("s_loadas8bit", "1", CVAR_ARCHIVE); + s_mixahead = Cvar.Get("s_mixahead", "0.2", CVAR_ARCHIVE); + s_show = Cvar.Get("s_show", "0", 0); + s_testsound = Cvar.Get("s_testsound", "0", 0); + s_primary = Cvar.Get("s_primary", "0", CVAR_ARCHIVE); // win32 specific + + Cmd.AddCommand("play", new xcommand_t() { + public void execute() { + S.Play(); + } + }); + Cmd.AddCommand("stopsound", new xcommand_t() { + public void execute() { + S.StopAllSounds(); + } + }); + Cmd.AddCommand("soundlist", new xcommand_t() { + public void execute() { + S.SoundList(); + } + }); + Cmd.AddCommand("soundinfo", new xcommand_t() { + public void execute() { + S.SoundInfo_f(); + } + }); + + if (!SNDDMA_Init()) + return; + + S.InitScaletable(); + + sound_started = true; + num_sfx = 0; + + soundtime = 0; + paintedtime = 0; + + Com.Printf("sound sampling rate: " + dma.speed + "\n"); + + S.StopAllSounds(); + } +S.SoundInfo_f(); + Com.Printf("------------------------------------\n"); + } + + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + + static void Shutdown() + { + int i; + sfx_t[] sfx; + + if (!sound_started) + return; + + SNDDMA_Shutdown(); + + sound_started = false; + + Cmd.RemoveCommand("play"); + Cmd.RemoveCommand("stopsound"); + Cmd.RemoveCommand("soundlist"); + Cmd.RemoveCommand("soundinfo"); + + // free all sounds + for (i=0, sfx=known_sfx ; i < num_sfx ; i++) + { + if (sfx[i].name == null) + continue; + + //memset (sfx, 0, sizeof(*sfx)); + sfx[i].clear(); + } + + num_sfx = 0; + } + +// ======================================================================= +// Load a sound +// ======================================================================= + + /* + ================== + S_FindName + + ================== + */ + static sfx_t FindName(String name, boolean create) { + int i; + sfx_t sfx = null; + + if (name == null) + Com.Error(ERR_FATAL, "S_FindName: NULL\n"); + if (name.length() == 0) + Com.Error(ERR_FATAL, "S_FindName: empty name\n"); + + if (strlen(name) >= MAX_QPATH) + Com.Error(ERR_FATAL, "Sound name too long: " + name); + + // see if already loaded + for (i = 0; i < num_sfx; i++) + if (name.equals(known_sfx[i].name)) { + return known_sfx[i]; + } + + if (!create) + return null; + + // find a free sfx + for (i = 0; i < num_sfx; i++) + if (known_sfx[i].name == null) + // registration_sequence < s_registration_sequence) + break; + + if (i == num_sfx) { + if (num_sfx == MAX_SFX) + Com.Error(ERR_FATAL, "S_FindName: out of sfx_t"); + num_sfx++; + } + + sfx = known_sfx[i]; + //memset (sfx, 0, sizeof(*sfx)); + sfx.clear(); + sfx.name = name; + sfx.registration_sequence = s_registration_sequence; + + return sfx; + } +// +// +// /* +// ================== +// S_AliasName +// +// ================== +// */ +// sfx_t *S_AliasName (char *aliasname, char *truename) +// { +// sfx_t *sfx; +// char *s; +// int i; +// +// s = Z_Malloc (MAX_QPATH); +// strcpy (s, truename); +// +// // find a free sfx +// for (i=0 ; i < num_sfx ; i++) +// if (!known_sfx[i].name[0]) +// break; +// +// if (i == num_sfx) +// { +// if (num_sfx == MAX_SFX) +// Com_Error (ERR_FATAL, "S_FindName: out of sfx_t"); +// num_sfx++; +// } +// +// sfx = &known_sfx[i]; +// memset (sfx, 0, sizeof(*sfx)); +// strcpy (sfx->name, aliasname); +// sfx->registration_sequence = s_registration_sequence; +// sfx->truename = s; +// +// return sfx; +// } +// +// + /* + ===================== + S_BeginRegistration + + ===================== + */ + static void BeginRegistration() { + s_registration_sequence++; + s_registering = true; + } + + /* + ================== + S_RegisterSound + + ================== + */ + static sfx_t RegisterSound(String name) { + sfx_t sfx = null; + +// if (!sound_started) +// return null; +// +// sfx = S.FindName(name, true); +// sfx.registration_sequence = s_registration_sequence; +// +// if (!s_registering) +// S.LoadSound(sfx); + + return sfx; + } + + + /* + ===================== + S_EndRegistration + + ===================== + */ + static void EndRegistration () + { +// int i; +// sfx_t *sfx; +// int size; +// +// // free any sounds not from this registration sequence +// for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++) +// { +// if (!sfx->name[0]) +// continue; +// if (sfx->registration_sequence != s_registration_sequence) +// { // don't need this sound +// if (sfx->cache) // it is possible to have a leftover +// Z_Free (sfx->cache); // from a server that didn't finish loading +// memset (sfx, 0, sizeof(*sfx)); +// } +// else +// { // make sure it is paged in +// if (sfx->cache) +// { +// size = sfx->cache->length*sfx->cache->width; +// Com_PageInMemory ((byte *)sfx->cache, size); +// } +// } +// +// } +// +// // load everything in +// for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++) +// { +// if (!sfx->name[0]) +// continue; +// S_LoadSound (sfx); +// } +// +// s_registering = false; + } +// +// +//// ============================================================================= +// +// /* +// ================= +// S_PickChannel +// ================= +// */ +// channel_t *S_PickChannel(int entnum, int entchannel) +// { +// int ch_idx; +// int first_to_die; +// int life_left; +// channel_t *ch; +// +// if (entchannel<0) +// Com_Error (ERR_DROP, "S_PickChannel: entchannel<0"); +// +//// Check for replacement sound, or find the best one to replace +// first_to_die = -1; +// life_left = 0x7fffffff; +// for (ch_idx=0 ; ch_idx < MAX_CHANNELS ; ch_idx++) +// { +// if (entchannel != 0 // channel 0 never overrides +// && channels[ch_idx].entnum == entnum +// && channels[ch_idx].entchannel == entchannel) +// { // always override sound from same entity +// first_to_die = ch_idx; +// break; +// } +// +// // don't let monster sounds override player sounds +// if (channels[ch_idx].entnum == cl.playernum+1 && entnum != cl.playernum+1 && channels[ch_idx].sfx) +// continue; +// +// if (channels[ch_idx].end - paintedtime < life_left) +// { +// life_left = channels[ch_idx].end - paintedtime; +// first_to_die = ch_idx; +// } +// } +// +// if (first_to_die == -1) +// return NULL; +// +// ch = &channels[first_to_die]; +// memset (ch, 0, sizeof(*ch)); +// +// return ch; +// } +// +// /* +// ================= +// S_SpatializeOrigin +// +// Used for spatializing channels and autosounds +// ================= +// */ +// void S_SpatializeOrigin (vec3_t origin, float master_vol, float dist_mult, int *left_vol, int *right_vol) +// { +// vec_t dot; +// vec_t dist; +// vec_t lscale, rscale, scale; +// vec3_t source_vec; +// +// if (cls.state != ca_active) +// { +// *left_vol = *right_vol = 255; +// return; +// } +// +//// calculate stereo seperation and distance attenuation +// VectorSubtract(origin, listener_origin, source_vec); +// +// dist = VectorNormalize(source_vec); +// dist -= SOUND_FULLVOLUME; +// if (dist < 0) +// dist = 0; // close enough to be at full volume +// dist *= dist_mult; // different attenuation levels +// +// dot = DotProduct(listener_right, source_vec); +// +// if (dma.channels == 1 || !dist_mult) +// { // no attenuation = no spatialization +// rscale = 1.0; +// lscale = 1.0; +// } +// else +// { +// rscale = 0.5 * (1.0 + dot); +// lscale = 0.5*(1.0 - dot); +// } +// +// // add in distance effect +// scale = (1.0 - dist) * rscale; +// *right_vol = (int) (master_vol * scale); +// if (*right_vol < 0) +// *right_vol = 0; +// +// scale = (1.0 - dist) * lscale; +// *left_vol = (int) (master_vol * scale); +// if (*left_vol < 0) +// *left_vol = 0; +// } +// +// /* +// ================= +// S_Spatialize +// ================= +// */ +// void S_Spatialize(channel_t *ch) +// { +// vec3_t origin; +// +// // anything coming from the view entity will always be full volume +// if (ch->entnum == cl.playernum+1) +// { +// ch->leftvol = ch->master_vol; +// ch->rightvol = ch->master_vol; +// return; +// } +// +// if (ch->fixed_origin) +// { +// VectorCopy (ch->origin, origin); +// } +// else +// CL_GetEntitySoundOrigin (ch->entnum, origin); +// +// S_SpatializeOrigin (origin, ch->master_vol, ch->dist_mult, &ch->leftvol, &ch->rightvol); +// } +// +// +// /* +// ================= +// S_AllocPlaysound +// ================= +// */ +// playsound_t *S_AllocPlaysound (void) +// { +// playsound_t *ps; +// +// ps = s_freeplays.next; +// if (ps == &s_freeplays) +// return NULL; // no free playsounds +// +// // unlink from freelist +// ps->prev->next = ps->next; +// ps->next->prev = ps->prev; +// +// return ps; +// } +// +// +// /* +// ================= +// S_FreePlaysound +// ================= +// */ +// void S_FreePlaysound (playsound_t *ps) +// { +// // unlink from channel +// ps->prev->next = ps->next; +// ps->next->prev = ps->prev; +// +// // add to free list +// ps->next = s_freeplays.next; +// s_freeplays.next->prev = ps; +// ps->prev = &s_freeplays; +// s_freeplays.next = ps; +// } +// +// +// +// /* +// =============== +// S_IssuePlaysound +// +// Take the next playsound and begin it on the channel +// This is never called directly by S_Play*, but only +// by the update loop. +// =============== +// */ +// void S_IssuePlaysound (playsound_t *ps) +// { +// channel_t *ch; +// sfxcache_t *sc; +// +// if (s_show->value) +// Com_Printf ("Issue %i\n", ps->begin); +// // pick a channel to play on +// ch = S_PickChannel(ps->entnum, ps->entchannel); +// if (!ch) +// { +// S_FreePlaysound (ps); +// return; +// } +// +// // spatialize +// if (ps->attenuation == ATTN_STATIC) +// ch->dist_mult = ps->attenuation * 0.001; +// else +// ch->dist_mult = ps->attenuation * 0.0005; +// ch->master_vol = ps->volume; +// ch->entnum = ps->entnum; +// ch->entchannel = ps->entchannel; +// ch->sfx = ps->sfx; +// VectorCopy (ps->origin, ch->origin); +// ch->fixed_origin = ps->fixed_origin; +// +// S_Spatialize(ch); +// +// ch->pos = 0; +// sc = S_LoadSound (ch->sfx); +// ch->end = paintedtime + sc->length; +// +// // free the playsound +// S_FreePlaysound (ps); +// } +// +// struct sfx_s *S_RegisterSexedSound (entity_state_t *ent, char *base) +// { +// int n; +// char *p; +// struct sfx_s *sfx; +// FILE *f; +// char model[MAX_QPATH]; +// char sexedFilename[MAX_QPATH]; +// char maleFilename[MAX_QPATH]; +// +// // determine what model the client is using +// model[0] = 0; +// n = CS_PLAYERSKINS + ent->number - 1; +// if (cl.configstrings[n][0]) +// { +// p = strchr(cl.configstrings[n], '\\'); +// if (p) +// { +// p += 1; +// strcpy(model, p); +// p = strchr(model, '/'); +// if (p) +// *p = 0; +// } +// } +// // if we can't figure it out, they're male +// if (!model[0]) +// strcpy(model, "male"); +// +// // see if we already know of the model specific sound +// Com_sprintf (sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base+1); +// sfx = S_FindName (sexedFilename, false); +// +// if (!sfx) +// { +// // no, so see if it exists +// FS_FOpenFile (&sexedFilename[1], &f); +// if (f) +// { +// // yes, close the file and register it +// FS_FCloseFile (f); +// sfx = S_RegisterSound (sexedFilename); +// } +// else +// { +// // no, revert to the male sound in the pak0.pak +// Com_sprintf (maleFilename, sizeof(maleFilename), "player/%s/%s", "male", base+1); +// sfx = S_AliasName (sexedFilename, maleFilename); +// } +// } +// +// return sfx; +// } +// +// +//// ======================================================================= +//// Start a sound effect +//// ======================================================================= +// + /* + ==================== + S_StartSound + + Validates the parms and ques the sound up + if pos is NULL, the sound will be dynamically sourced from the entity + Entchannel 0 will never override a playing sound + ==================== + */ + static void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs) + { +// sfxcache_t *sc; +// int vol; +// playsound_t *ps, *sort; +// int start; +// +// if (!sound_started) +// return; +// +// if (!sfx) +// return; +// +// if (sfx->name[0] == '*') +// sfx = S_RegisterSexedSound(&cl_entities[entnum].current, sfx->name); +// +// // make sure the sound is loaded +// sc = S_LoadSound (sfx); +// if (!sc) +// return; // couldn't load the sound's data +// +// vol = fvol*255; +// +// // make the playsound_t +// ps = S_AllocPlaysound (); +// if (!ps) +// return; +// +// if (origin) +// { +// VectorCopy (origin, ps->origin); +// ps->fixed_origin = true; +// } +// else +// ps->fixed_origin = false; +// +// ps->entnum = entnum; +// ps->entchannel = entchannel; +// ps->attenuation = attenuation; +// ps->volume = vol; +// ps->sfx = sfx; +// +// // drift s_beginofs +// start = cl.frame.servertime * 0.001 * dma.speed + s_beginofs; +// if (start < paintedtime) +// { +// start = paintedtime; +// s_beginofs = start - (cl.frame.servertime * 0.001 * dma.speed); +// } +// else if (start > paintedtime + 0.3 * dma.speed) +// { +// start = paintedtime + 0.1 * dma.speed; +// s_beginofs = start - (cl.frame.servertime * 0.001 * dma.speed); +// } +// else +// { +// s_beginofs-=10; +// } +// +// if (!timeofs) +// ps->begin = paintedtime; +// else +// ps->begin = start + timeofs * dma.speed; +// +// // sort into the pending sound list +// for (sort = s_pendingplays.next ; +// sort != &s_pendingplays && sort->begin < ps->begin ; +// sort = sort->next) +// ; +// +// ps->next = sort; +// ps->prev = sort->prev; +// +// ps->next->prev = ps; +// ps->prev->next = ps; + } + +// +// /* +// ================== +// S_StartLocalSound +// ================== +// */ + static void StartLocalSound(String sound) { +// sfx_t *sfx; +// +// if (!sound_started) +// return; +// +// sfx = S_RegisterSound (sound); +// if (!sfx) +// { +// Com_Printf ("S_StartLocalSound: can't cache %s\n", sound); +// return; +// } +// S_StartSound (NULL, cl.playernum+1, 0, sfx, 1, 1, 0); + } +// +// +// /* +// ================== +// S_ClearBuffer +// ================== +// */ +// void S_ClearBuffer (void) +// { +// int clear; +// +// if (!sound_started) +// return; +// +// s_rawend = 0; +// +// if (dma.samplebits == 8) +// clear = 0x80; +// else +// clear = 0; +// +// SNDDMA_BeginPainting (); +// if (dma.buffer) +// memset(dma.buffer, clear, dma.samples * dma.samplebits/8); +// SNDDMA_Submit (); +// } +// + /* + ================== + S_StopAllSounds + ================== + */ + static void StopAllSounds() + { +// int i; +// +// if (!sound_started) +// return; +// +// // clear all the playsounds +// memset(s_playsounds, 0, sizeof(s_playsounds)); +// s_freeplays.next = s_freeplays.prev = &s_freeplays; +// s_pendingplays.next = s_pendingplays.prev = &s_pendingplays; +// +// for (i=0 ; i<MAX_PLAYSOUNDS ; i++) +// { +// s_playsounds[i].prev = &s_freeplays; +// s_playsounds[i].next = s_freeplays.next; +// s_playsounds[i].prev->next = &s_playsounds[i]; +// s_playsounds[i].next->prev = &s_playsounds[i]; +// } +// +// // clear all the channels +// memset(channels, 0, sizeof(channels)); +// +// S_ClearBuffer (); + } +// +// /* +// ================== +// S_AddLoopSounds +// +// Entities with a ->sound field will generated looped sounds +// that are automatically started, stopped, and merged together +// as the entities are sent to the client +// ================== +// */ +// void S_AddLoopSounds (void) +// { +// int i, j; +// int sounds[MAX_EDICTS]; +// int left, right, left_total, right_total; +// channel_t *ch; +// sfx_t *sfx; +// sfxcache_t *sc; +// int num; +// entity_state_t *ent; +// +// if (cl_paused->value) +// return; +// +// if (cls.state != ca_active) +// return; +// +// if (!cl.sound_prepped) +// return; +// +// for (i=0 ; i<cl.frame.num_entities ; i++) +// { +// num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1); +// ent = &cl_parse_entities[num]; +// sounds[i] = ent->sound; +// } +// +// for (i=0 ; i<cl.frame.num_entities ; i++) +// { +// if (!sounds[i]) +// continue; +// +// sfx = cl.sound_precache[sounds[i]]; +// if (!sfx) +// continue; // bad sound effect +// sc = sfx->cache; +// if (!sc) +// continue; +// +// num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1); +// ent = &cl_parse_entities[num]; +// +// // find the total contribution of all sounds of this type +// S_SpatializeOrigin (ent->origin, 255.0, SOUND_LOOPATTENUATE, +// &left_total, &right_total); +// for (j=i+1 ; j<cl.frame.num_entities ; j++) +// { +// if (sounds[j] != sounds[i]) +// continue; +// sounds[j] = 0; // don't check this again later +// +// num = (cl.frame.parse_entities + j)&(MAX_PARSE_ENTITIES-1); +// ent = &cl_parse_entities[num]; +// +// S_SpatializeOrigin (ent->origin, 255.0, SOUND_LOOPATTENUATE, +// &left, &right); +// left_total += left; +// right_total += right; +// } +// +// if (left_total == 0 && right_total == 0) +// continue; // not audible +// +// // allocate a channel +// ch = S_PickChannel(0, 0); +// if (!ch) +// return; +// +// if (left_total > 255) +// left_total = 255; +// if (right_total > 255) +// right_total = 255; +// ch->leftvol = left_total; +// ch->rightvol = right_total; +// ch->autosound = true; // remove next frame +// ch->sfx = sfx; +// ch->pos = paintedtime % sc->length; +// ch->end = paintedtime + sc->length - ch->pos; +// } +// } +// +//// ============================================================================= +// +// /* +// ============ +// S_RawSamples +// +// Cinematic streaming and voice over network +// ============ +// */ +// void S_RawSamples (int samples, int rate, int width, int channels, byte *data) +// { +// int i; +// int src, dst; +// float scale; +// +// if (!sound_started) +// return; +// +// if (s_rawend < paintedtime) +// s_rawend = paintedtime; +// scale = (float)rate / dma.speed; +// +//// Com_Printf ("%i < %i < %i\n", soundtime, paintedtime, s_rawend); +// if (channels == 2 && width == 2) +// { +// if (scale == 1.0) +// { // optimized case +// for (i=0 ; i<samples ; i++) +// { +// dst = s_rawend&(MAX_RAW_SAMPLES-1); +// s_rawend++; +// s_rawsamples[dst].left = +// LittleShort(((short *)data)[i*2]) << 8; +// s_rawsamples[dst].right = +// LittleShort(((short *)data)[i*2+1]) << 8; +// } +// } +// else +// { +// for (i=0 ; ; i++) +// { +// src = i*scale; +// if (src >= samples) +// break; +// dst = s_rawend&(MAX_RAW_SAMPLES-1); +// s_rawend++; +// s_rawsamples[dst].left = +// LittleShort(((short *)data)[src*2]) << 8; +// s_rawsamples[dst].right = +// LittleShort(((short *)data)[src*2+1]) << 8; +// } +// } +// } +// else if (channels == 1 && width == 2) +// { +// for (i=0 ; ; i++) +// { +// src = i*scale; +// if (src >= samples) +// break; +// dst = s_rawend&(MAX_RAW_SAMPLES-1); +// s_rawend++; +// s_rawsamples[dst].left = +// LittleShort(((short *)data)[src]) << 8; +// s_rawsamples[dst].right = +// LittleShort(((short *)data)[src]) << 8; +// } +// } +// else if (channels == 2 && width == 1) +// { +// for (i=0 ; ; i++) +// { +// src = i*scale; +// if (src >= samples) +// break; +// dst = s_rawend&(MAX_RAW_SAMPLES-1); +// s_rawend++; +// s_rawsamples[dst].left = +// ((char *)data)[src*2] << 16; +// s_rawsamples[dst].right = +// ((char *)data)[src*2+1] << 16; +// } +// } +// else if (channels == 1 && width == 1) +// { +// for (i=0 ; ; i++) +// { +// src = i*scale; +// if (src >= samples) +// break; +// dst = s_rawend&(MAX_RAW_SAMPLES-1); +// s_rawend++; +// s_rawsamples[dst].left = +// (((byte *)data)[src]-128) << 16; +// s_rawsamples[dst].right = (((byte *)data)[src]-128) << 16; +// } +// } +// } +// +//// ============================================================================= +// +// /* +// ============ +// S_Update +// +// Called once each time through the main loop +// ============ +// */ + static void Update(float[] origin, float[] forward, float[] right, float[] up) { +// int i; +// int total; +// channel_t *ch; +// channel_t *combine; +// +// if (!sound_started) +// return; +// +// // if the laoding plaque is up, clear everything +// // out to make sure we aren't looping a dirty +// // dma buffer while loading +// if (cls.disable_screen) +// { +// S_ClearBuffer (); +// return; +// } +// +// // rebuild scale tables if volume is modified +// if (s_volume->modified) +// S_InitScaletable (); +// +// VectorCopy(origin, listener_origin); +// VectorCopy(forward, listener_forward); +// VectorCopy(right, listener_right); +// VectorCopy(up, listener_up); +// +// combine = NULL; +// +// // update spatialization for dynamic sounds +// ch = channels; +// for (i=0 ; i<MAX_CHANNELS; i++, ch++) +// { +// if (!ch->sfx) +// continue; +// if (ch->autosound) +// { // autosounds are regenerated fresh each frame +// memset (ch, 0, sizeof(*ch)); +// continue; +// } +// S_Spatialize(ch); // respatialize channel +// if (!ch->leftvol && !ch->rightvol) +// { +// memset (ch, 0, sizeof(*ch)); +// continue; +// } +// } +// +// // add loopsounds +// S_AddLoopSounds (); +// +// // +// // debugging output +// // +// if (s_show->value) +// { +// total = 0; +// ch = channels; +// for (i=0 ; i<MAX_CHANNELS; i++, ch++) +// if (ch->sfx && (ch->leftvol || ch->rightvol) ) +// { +// Com_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); +// total++; +// } +// +// Com_Printf ("----(%i)---- painted: %i\n", total, paintedtime); +// } +// +//// mix some sound +// S_Update_(); + } + +// +// void GetSoundtime(void) +// { +// int samplepos; +// static int buffers; +// static int oldsamplepos; +// int fullsamples; +// +// fullsamples = dma.samples / dma.channels; +// +//// it is possible to miscount buffers if it has wrapped twice between +//// calls to S_Update. Oh well. +// samplepos = SNDDMA_GetDMAPos(); +// +// if (samplepos < oldsamplepos) +// { +// buffers++; // buffer wrapped +// +// if (paintedtime > 0x40000000) +// { // time to chop things off to avoid 32 bit limits +// buffers = 0; +// paintedtime = fullsamples; +// S_StopAllSounds (); +// } +// } +// oldsamplepos = samplepos; +// +// soundtime = buffers*fullsamples + samplepos/dma.channels; +// } +// +// +// void S_Update_(void) +// { +// unsigned endtime; +// int samps; +// +// if (!sound_started) +// return; +// +// SNDDMA_BeginPainting (); +// +// if (!dma.buffer) +// return; +// +//// Updates DMA time +// GetSoundtime(); +// +//// check to make sure that we haven't overshot +// if (paintedtime < soundtime) +// { +// Com_DPrintf ("S_Update_ : overflow\n"); +// paintedtime = soundtime; +// } +// +//// mix ahead of current position +// endtime = soundtime + s_mixahead->value * dma.speed; +//// endtime = (soundtime + 4096) & ~4095; +// +// // mix to an even submission block size +// endtime = (endtime + dma.submission_chunk-1) +// & ~(dma.submission_chunk-1); +// samps = dma.samples >> (dma.channels-1); +// if (endtime - soundtime > samps) +// endtime = soundtime + samps; +// +// S_PaintChannels (endtime); +// +// SNDDMA_Submit (); +// } +// + /* + =============================================================================== + + console functions + + =============================================================================== + */ + + static void Play() + { +// int i; +// char name[256]; +// sfx_t *sfx; +// +// i = 1; +// while (i<Cmd_Argc()) +// { +// if (!strrchr(Cmd_Argv(i), '.')) +// { +// strcpy(name, Cmd_Argv(i)); +// strcat(name, ".wav"); +// } +// else +// strcpy(name, Cmd_Argv(i)); +// sfx = S_RegisterSound(name); +// S_StartSound(NULL, cl.playernum+1, 0, sfx, 1.0, 1.0, 0); +// i++; +// } + } +// + static void SoundList() { +// int i; +// sfx_t *sfx; +// sfxcache_t *sc; +// int size, total; +// +// total = 0; +// for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++) +// { +// if (!sfx->registration_sequence) +// continue; +// sc = sfx->cache; +// if (sc) +// { +// size = sc->length*sc->width*(sc->stereo+1); +// total += size; +// if (sc->loopstart >= 0) +// Com_Printf ("L"); +// else +// Com_Printf (" "); +// Com_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name); +// } +// else +// { +// if (sfx->name[0] == '*') +// Com_Printf(" placeholder : %s\n", sfx->name); +// else +// Com_Printf(" not loaded : %s\n", sfx->name); +// } +// } +// Com_Printf ("Total resident: %i\n", total); + } + +} diff --git a/src/jake2/client/SND_JAVA.java b/src/jake2/client/SND_JAVA.java new file mode 100644 index 0000000..3853e09 --- /dev/null +++ b/src/jake2/client/SND_JAVA.java @@ -0,0 +1,168 @@ +/* + * SND_JAVA.java + * Copyright (C) 2004 + * + * $Id: SND_JAVA.java,v 1.1 2004-07-07 19:58:51 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.cvar_t; +import jake2.qcommon.Cvar; +import jake2.qcommon.FS; + +import java.io.*; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.sound.sampled.*; + +/** + * SND_JAVA + */ +public class SND_JAVA extends Globals { + + static boolean snd_inited= false; + + static cvar_t sndbits; + static cvar_t sndspeed; + static cvar_t sndchannels; + +// static int tryrates[] = { 11025, 22051, 44100, 8000 }; + static class dma_t { + int channels; + int samples; // mono samples in buffer + int submission_chunk; // don't mix less than this # + int samplepos; // in mono samples + int samplebits; + int speed; + byte[] buffer; + } + static SND_DMA.dma_t dma = new dma_t(); + + static SourceDataLine line; + static AudioFormat format; + + + static boolean SNDDMA_Init() { + + if (snd_inited) + return true; + + if (sndbits == null) { + sndbits = Cvar.Get("sndbits", "16", CVAR_ARCHIVE); + sndspeed = Cvar.Get("sndspeed", "0", CVAR_ARCHIVE); + sndchannels = Cvar.Get("sndchannels", "2", CVAR_ARCHIVE); + } + + byte[] sound = FS.LoadFile("sound/misc/menu1.wav"); + AudioInputStream stream; + try { + stream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(sound)); + } catch (UnsupportedAudioFileException e) { + return false; + } catch (IOException e) { + return false; + } + format = stream.getFormat(); + + DataLine.Info dinfo = new DataLine.Info(SourceDataLine.class, format); + //format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits(), 2, 2*format.getFrameSize(), format.getFrameRate(), format.isBigEndian()); + + try { + line = (SourceDataLine)AudioSystem.getLine(dinfo); + } catch (LineUnavailableException e4) { + return false; + } + dma.buffer = new byte[65536]; +// try { +// stream.read(dma.buffer); +// } catch (IOException e3) { +// // TODO Auto-generated catch block +// e3.printStackTrace(); +// } + + dma.channels = format.getChannels(); + dma.samplebits = format.getSampleSizeInBits(); + dma.samples = dma.buffer.length / format.getFrameSize(); + dma.speed = (int)format.getSampleRate(); + dma.samplepos = 0; + dma.submission_chunk = 1; + + try { + line.open(format, 4096); + } catch (LineUnavailableException e5) { + return false; + } + + line.start(); + runLine(); + + snd_inited = true; + return true; + + } + + static int SNDDMA_GetDMAPos() { + dma.samplepos = line.getFramePosition() % dma.samples; + return dma.samplepos; + } + + static void SNDDMA_Shutdown() { + line.stop(); + line.flush(); + line.close(); + line=null; + snd_inited = false; + } + + /* + ============== + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer + =============== + */ + public static void SNDDMA_Submit() { + runLine(); + } + + void SNDDMA_BeginPainting() {} + + private static int pos = 0; + static void runLine() { + + int p = line.getFramePosition() * format.getFrameSize() % dma.buffer.length; + System.out.println("run " + p + " " + pos); + if (p == 0) { + writeLine(); + } + else if (pos - p < 2048 ) writeLine(); + } + + static void writeLine() { + line.write(dma.buffer, pos, 4096); + pos+=4096; + if (pos>=dma.buffer.length) pos = 0; + } + +} diff --git a/src/jake2/client/SND_MEM.java b/src/jake2/client/SND_MEM.java new file mode 100644 index 0000000..a5f7671 --- /dev/null +++ b/src/jake2/client/SND_MEM.java @@ -0,0 +1,374 @@ +/* + * SND_MEM.java + * Copyright (C) 2004 + * + * $Id: SND_MEM.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * SND_MEM + */ +public class SND_MEM extends SND_JAVA { + +//// snd_mem.c: sound caching +// +// #include "client.h" +// #include "snd_loc.h" +// +// int cache_full_cycle; +// +// byte *S_Alloc (int size); +// +// /* +// ================ +// ResampleSfx +// ================ +// */ +// void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data) +// { +// int outcount; +// int srcsample; +// float stepscale; +// int i; +// int sample, samplefrac, fracstep; +// sfxcache_t *sc; +// +// sc = sfx->cache; +// if (!sc) +// return; +// +// stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 +// +// outcount = sc->length / stepscale; +// sc->length = outcount; +// if (sc->loopstart != -1) +// sc->loopstart = sc->loopstart / stepscale; +// +// sc->speed = dma.speed; +// if (s_loadas8bit->value) +// sc->width = 1; +// else +// sc->width = inwidth; +// sc->stereo = 0; +// +//// resample / decimate to the current source rate +// +// if (stepscale == 1 && inwidth == 1 && sc->width == 1) +// { +//// fast special case +// for (i=0 ; i<outcount ; i++) +// ((signed char *)sc->data)[i] +// = (int)( (unsigned char)(data[i]) - 128); +// } +// else +// { +//// general case +// samplefrac = 0; +// fracstep = stepscale*256; +// for (i=0 ; i<outcount ; i++) +// { +// srcsample = samplefrac >> 8; +// samplefrac += fracstep; +// if (inwidth == 2) +// sample = LittleShort ( ((short *)data)[srcsample] ); +// else +// sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; +// if (sc->width == 2) +// ((short *)sc->data)[i] = sample; +// else +// ((signed char *)sc->data)[i] = sample >> 8; +// } +// } +// } +// +//// ============================================================================= +// + /* + ============== + S_LoadSound + ============== + */ + static sfxcache_t LoadSound (sfx_t s) + { +// char namebuffer[MAX_QPATH]; +// byte *data; +// wavinfo_t info; +// int len; +// float stepscale; + sfxcache_t sc = null; +// int size; +// char *name; +// +// if (s->name[0] == '*') +// return NULL; +// +//// see if still in memory +// sc = s->cache; +// if (sc) +// return sc; +// +//// Com_Printf ("S_LoadSound: %x\n", (int)stackbuf); +//// load it in +// if (s->truename) +// name = s->truename; +// else +// name = s->name; +// +// if (name[0] == '#') +// strcpy(namebuffer, &name[1]); +// else +// Com_sprintf (namebuffer, sizeof(namebuffer), "sound/%s", name); +// +//// Com_Printf ("loading %s\n",namebuffer); +// +// size = FS_LoadFile (namebuffer, (void **)&data); +// +// if (!data) +// { +// Com_DPrintf ("Couldn't load %s\n", namebuffer); +// return NULL; +// } +// +// info = GetWavinfo (s->name, data, size); +// if (info.channels != 1) +// { +// Com_Printf ("%s is a stereo sample\n",s->name); +// FS_FreeFile (data); +// return NULL; +// } +// +// stepscale = (float)info.rate / dma.speed; +// len = info.samples / stepscale; +// +// len = len * info.width * info.channels; +// +// sc = s->cache = Z_Malloc (len + sizeof(sfxcache_t)); +// if (!sc) +// { +// FS_FreeFile (data); +// return NULL; +// } +// +// sc->length = info.samples; +// sc->loopstart = info.loopstart; +// sc->speed = info.rate; +// sc->width = info.width; +// sc->stereo = info.channels; +// +// ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); +// +// FS_FreeFile (data); +// + return sc; + } +// +// +// +// /* +// =============================================================================== +// +// WAV loading +// +// =============================================================================== +// */ +// +// +// byte *data_p; +// byte *iff_end; +// byte *last_chunk; +// byte *iff_data; +// int iff_chunk_len; +// +// +// short GetLittleShort(void) +// { +// short val = 0; +// val = *data_p; +// val = val + (*(data_p+1)<<8); +// data_p += 2; +// return val; +// } +// +// int GetLittleLong(void) +// { +// int val = 0; +// val = *data_p; +// val = val + (*(data_p+1)<<8); +// val = val + (*(data_p+2)<<16); +// val = val + (*(data_p+3)<<24); +// data_p += 4; +// return val; +// } +// +// void FindNextChunk(char *name) +// { +// while (1) +// { +// data_p=last_chunk; +// +// if (data_p >= iff_end) +// { // didn't find the chunk +// data_p = NULL; +// return; +// } +// +// data_p += 4; +// iff_chunk_len = GetLittleLong(); +// if (iff_chunk_len < 0) +// { +// data_p = NULL; +// return; +// } +//// if (iff_chunk_len > 1024*1024) +//// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); +// data_p -= 8; +// last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); +// if (!strncmp(data_p, name, 4)) +// return; +// } +// } +// +// void FindChunk(char *name) +// { +// last_chunk = iff_data; +// FindNextChunk (name); +// } +// +// +// void DumpChunks(void) +// { +// char str[5]; +// +// str[4] = 0; +// data_p=iff_data; +// do +// { +// memcpy (str, data_p, 4); +// data_p += 4; +// iff_chunk_len = GetLittleLong(); +// Com_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); +// data_p += (iff_chunk_len + 1) & ~1; +// } while (data_p < iff_end); +// } +// +// /* +// ============ +// GetWavinfo +// ============ +// */ +// wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) +// { +// wavinfo_t info; +// int i; +// int format; +// int samples; +// +// memset (&info, 0, sizeof(info)); +// +// if (!wav) +// return info; +// +// iff_data = wav; +// iff_end = wav + wavlength; +// +//// find "RIFF" chunk +// FindChunk("RIFF"); +// if (!(data_p && !strncmp(data_p+8, "WAVE", 4))) +// { +// Com_Printf("Missing RIFF/WAVE chunks\n"); +// return info; +// } +// +//// get "fmt " chunk +// iff_data = data_p + 12; +//// DumpChunks (); +// +// FindChunk("fmt "); +// if (!data_p) +// { +// Com_Printf("Missing fmt chunk\n"); +// return info; +// } +// data_p += 8; +// format = GetLittleShort(); +// if (format != 1) +// { +// Com_Printf("Microsoft PCM format only\n"); +// return info; +// } +// +// info.channels = GetLittleShort(); +// info.rate = GetLittleLong(); +// data_p += 4+2; +// info.width = GetLittleShort() / 8; +// +//// get cue chunk +// FindChunk("cue "); +// if (data_p) +// { +// data_p += 32; +// info.loopstart = GetLittleLong(); +//// Com_Printf("loopstart=%d\n", sfx->loopstart); +// +// // if the next chunk is a LIST chunk, look for a cue length marker +// FindNextChunk ("LIST"); +// if (data_p) +// { +// if (!strncmp (data_p + 28, "mark", 4)) +// { // this is not a proper parse, but it works with cooledit... +// data_p += 24; +// i = GetLittleLong (); // samples in loop +// info.samples = info.loopstart + i; +//// Com_Printf("looped length: %i\n", i); +// } +// } +// } +// else +// info.loopstart = -1; +// +//// find data chunk +// FindChunk("data"); +// if (!data_p) +// { +// Com_Printf("Missing data chunk\n"); +// return info; +// } +// +// data_p += 4; +// samples = GetLittleLong () / info.width; +// +// if (info.samples) +// { +// if (samples < info.samples) +// Com_Error (ERR_DROP, "Sound %s has a bad loop length", name); +// } +// else +// info.samples = samples; +// +// info.dataofs = data_p - wav; +// +// return info; +// } +// + +} diff --git a/src/jake2/client/SND_MIX.java b/src/jake2/client/SND_MIX.java new file mode 100644 index 0000000..9ba7747 --- /dev/null +++ b/src/jake2/client/SND_MIX.java @@ -0,0 +1,513 @@ +/* + * SND_MIX.java + * Copyright (C) 2004 + * + * $Id: SND_MIX.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * SND_MIX + */ +public class SND_MIX extends SND_MEM { + +//// snd_mix.c -- portable code to mix sounds for snd_dma.c +// +// #include "client.h" +// #include "snd_loc.h" +// +// #define PAINTBUFFER_SIZE 2048 +// portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; +// int snd_scaletable[32][256]; +// int *snd_p, snd_linear_count, snd_vol; +// short *snd_out; +// +// void S_WriteLinearBlastStereo16 (void); +// +// #if !(defined __linux__ && defined __i386__) +// #if !id386 +// +// void S_WriteLinearBlastStereo16 (void) +// { +// int i; +// int val; +// +// for (i=0 ; i<snd_linear_count ; i+=2) +// { +// val = snd_p[i]>>8; +// if (val > 0x7fff) +// snd_out[i] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i] = (short)0x8000; +// else +// snd_out[i] = val; +// +// val = snd_p[i+1]>>8; +// if (val > 0x7fff) +// snd_out[i+1] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i+1] = (short)0x8000; +// else +// snd_out[i+1] = val; +// } +// } +// #else +// __declspec( naked ) void S_WriteLinearBlastStereo16 (void) +// { +// __asm { +// +// push edi +// push ebx +// mov ecx,ds:dword ptr[snd_linear_count] +// mov ebx,ds:dword ptr[snd_p] +// mov edi,ds:dword ptr[snd_out] +// LWLBLoopTop: +// mov eax,ds:dword ptr[-8+ebx+ecx*4] +// sar eax,8 +// cmp eax,07FFFh +// jg LClampHigh +// cmp eax,0FFFF8000h +// jnl LClampDone +// mov eax,0FFFF8000h +// jmp LClampDone +// LClampHigh: +// mov eax,07FFFh +// LClampDone: +// mov edx,ds:dword ptr[-4+ebx+ecx*4] +// sar edx,8 +// cmp edx,07FFFh +// jg LClampHigh2 +// cmp edx,0FFFF8000h +// jnl LClampDone2 +// mov edx,0FFFF8000h +// jmp LClampDone2 +// LClampHigh2: +// mov edx,07FFFh +// LClampDone2: +// shl edx,16 +// and eax,0FFFFh +// or edx,eax +// mov ds:dword ptr[-4+edi+ecx*2],edx +// sub ecx,2 +// jnz LWLBLoopTop +// pop ebx +// pop edi +// ret +// } +// } +// +// #endif +// #endif +// +// void S_TransferStereo16 (unsigned long *pbuf, int endtime) +// { +// int lpos; +// int lpaintedtime; +// +// snd_p = (int *) paintbuffer; +// lpaintedtime = paintedtime; +// +// while (lpaintedtime < endtime) +// { +// // handle recirculating buffer issues +// lpos = lpaintedtime & ((dma.samples>>1)-1); +// +// snd_out = (short *) pbuf + (lpos<<1); +// +// snd_linear_count = (dma.samples>>1) - lpos; +// if (lpaintedtime + snd_linear_count > endtime) +// snd_linear_count = endtime - lpaintedtime; +// +// snd_linear_count <<= 1; +// +// // write a linear blast of samples +// S_WriteLinearBlastStereo16 (); +// +// snd_p += snd_linear_count; +// lpaintedtime += (snd_linear_count>>1); +// } +// } +// +// /* +// =================== +// S_TransferPaintBuffer +// +// =================== +// */ +// void S_TransferPaintBuffer(int endtime) +// { +// int out_idx; +// int count; +// int out_mask; +// int *p; +// int step; +// int val; +// unsigned long *pbuf; +// +// pbuf = (unsigned long *)dma.buffer; +// +// if (s_testsound->value) +// { +// int i; +// int count; +// +// // write a fixed sine wave +// count = (endtime - paintedtime); +// for (i=0 ; i<count ; i++) +// paintbuffer[i].left = paintbuffer[i].right = sin((paintedtime+i)*0.1)*20000*256; +// } +// +// +// if (dma.samplebits == 16 && dma.channels == 2) +// { // optimized case +// S_TransferStereo16 (pbuf, endtime); +// } +// else +// { // general case +// p = (int *) paintbuffer; +// count = (endtime - paintedtime) * dma.channels; +// out_mask = dma.samples - 1; +// out_idx = paintedtime * dma.channels & out_mask; +// step = 3 - dma.channels; +// +// if (dma.samplebits == 16) +// { +// short *out = (short *) pbuf; +// while (count--) +// { +// val = *p >> 8; +// p+= step; +// if (val > 0x7fff) +// val = 0x7fff; +// else if (val < (short)0x8000) +// val = (short)0x8000; +// out[out_idx] = val; +// out_idx = (out_idx + 1) & out_mask; +// } +// } +// else if (dma.samplebits == 8) +// { +// unsigned char *out = (unsigned char *) pbuf; +// while (count--) +// { +// val = *p >> 8; +// p+= step; +// if (val > 0x7fff) +// val = 0x7fff; +// else if (val < (short)0x8000) +// val = (short)0x8000; +// out[out_idx] = (val>>8) + 128; +// out_idx = (out_idx + 1) & out_mask; +// } +// } +// } +// } +// +// +// /* +// =============================================================================== +// +// CHANNEL MIXING +// +// =============================================================================== +// */ +// +// void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime, int offset); +// void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime, int offset); +// +// void S_PaintChannels(int endtime) +// { +// int i; +// int end; +// channel_t *ch; +// sfxcache_t *sc; +// int ltime, count; +// playsound_t *ps; +// +// snd_vol = s_volume->value*256; +// +//// Com_Printf ("%i to %i\n", paintedtime, endtime); +// while (paintedtime < endtime) +// { +// // if paintbuffer is smaller than DMA buffer +// end = endtime; +// if (endtime - paintedtime > PAINTBUFFER_SIZE) +// end = paintedtime + PAINTBUFFER_SIZE; +// +// // start any playsounds +// while (1) +// { +// ps = s_pendingplays.next; +// if (ps == &s_pendingplays) +// break; // no more pending sounds +// if (ps->begin <= paintedtime) +// { +// S_IssuePlaysound (ps); +// continue; +// } +// +// if (ps->begin < end) +// end = ps->begin; // stop here +// break; +// } +// +// // clear the paint buffer +// if (s_rawend < paintedtime) +// { +//// Com_Printf ("clear\n"); +// memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); +// } +// else +// { // copy from the streaming sound source +// int s; +// int stop; +// +// stop = (end < s_rawend) ? end : s_rawend; +// +// for (i=paintedtime ; i<stop ; i++) +// { +// s = i&(MAX_RAW_SAMPLES-1); +// paintbuffer[i-paintedtime] = s_rawsamples[s]; +// } +//// if (i != end) +//// Com_Printf ("partial stream\n"); +//// else +//// Com_Printf ("full stream\n"); +// for ( ; i<end ; i++) +// { +// paintbuffer[i-paintedtime].left = +// paintbuffer[i-paintedtime].right = 0; +// } +// } +// +// +// // paint in the channels. +// ch = channels; +// for (i=0; i<MAX_CHANNELS ; i++, ch++) +// { +// ltime = paintedtime; +// +// while (ltime < end) +// { +// if (!ch->sfx || (!ch->leftvol && !ch->rightvol) ) +// break; +// +// // max painting is to the end of the buffer +// count = end - ltime; +// +// // might be stopped by running out of data +// if (ch->end - ltime < count) +// count = ch->end - ltime; +// +// sc = S_LoadSound (ch->sfx); +// if (!sc) +// break; +// +// if (count > 0 && ch->sfx) +// { +// if (sc->width == 1)// FIXME; 8 bit asm is wrong now +// S_PaintChannelFrom8(ch, sc, count, ltime - paintedtime); +// else +// S_PaintChannelFrom16(ch, sc, count, ltime - paintedtime); +// +// ltime += count; +// } +// +// // if at end of loop, restart +// if (ltime >= ch->end) +// { +// if (ch->autosound) +// { // autolooping sounds always go back to start +// ch->pos = 0; +// ch->end = ltime + sc->length; +// } +// else if (sc->loopstart >= 0) +// { +// ch->pos = sc->loopstart; +// ch->end = ltime + sc->length - ch->pos; +// } +// else +// { // channel just stopped +// ch->sfx = NULL; +// } +// } +// } +// +// } +// +// // transfer out according to DMA format +// S_TransferPaintBuffer(end); +// paintedtime = end; +// } +// } +// + static void InitScaletable() + { +// int i, j; +// int scale; +// +// s_volume->modified = false; +// for (i=0 ; i<32 ; i++) +// { +// scale = i * 8 * 256 * s_volume->value; +// for (j=0 ; j<256 ; j++) +// snd_scaletable[i][j] = ((signed char)j) * scale; +// } + } +// +// +// #if !(defined __linux__ && defined __i386__) +// #if !id386 +// +// void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset) +// { +// int data; +// int *lscale, *rscale; +// unsigned char *sfx; +// int i; +// portable_samplepair_t *samp; +// +// if (ch->leftvol > 255) +// ch->leftvol = 255; +// if (ch->rightvol > 255) +// ch->rightvol = 255; +// +// //ZOID-- >>11 has been changed to >>3, >>11 didn't make much sense +// //as it would always be zero. +// lscale = snd_scaletable[ ch->leftvol >> 3]; +// rscale = snd_scaletable[ ch->rightvol >> 3]; +// sfx = (signed char *)sc->data + ch->pos; +// +// samp = &paintbuffer[offset]; +// +// for (i=0 ; i<count ; i++, samp++) +// { +// data = sfx[i]; +// samp->left += lscale[data]; +// samp->right += rscale[data]; +// } +// +// ch->pos += count; +// } +// +// #else +// +// __declspec( naked ) void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset) +// { +// __asm { +// push esi +// push edi +// push ebx +// push ebp +// mov ebx,ds:dword ptr[4+16+esp] +// mov esi,ds:dword ptr[8+16+esp] +// mov eax,ds:dword ptr[4+ebx] +// mov edx,ds:dword ptr[8+ebx] +// cmp eax,255 +// jna LLeftSet +// mov eax,255 +// LLeftSet: +// cmp edx,255 +// jna LRightSet +// mov edx,255 +// LRightSet: +// and eax,0F8h +// add esi,20 +// and edx,0F8h +// mov edi,ds:dword ptr[16+ebx] +// mov ecx,ds:dword ptr[12+16+esp] +// add esi,edi +// shl eax,7 +// add edi,ecx +// shl edx,7 +// mov ds:dword ptr[16+ebx],edi +// add eax,offset snd_scaletable +// add edx,offset snd_scaletable +// sub ebx,ebx +// mov bl,ds:byte ptr[-1+esi+ecx*1] +// test ecx,1 +// jz LMix8Loop +// mov edi,ds:dword ptr[eax+ebx*4] +// mov ebp,ds:dword ptr[edx+ebx*4] +// add edi,ds:dword ptr[paintbuffer+0-8+ecx*8] +// add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8] +// mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi +// mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp +// mov bl,ds:byte ptr[-2+esi+ecx*1] +// dec ecx +// jz LDone +// LMix8Loop: +// mov edi,ds:dword ptr[eax+ebx*4] +// mov ebp,ds:dword ptr[edx+ebx*4] +// add edi,ds:dword ptr[paintbuffer+0-8+ecx*8] +// add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8] +// mov bl,ds:byte ptr[-2+esi+ecx*1] +// mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi +// mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp +// mov edi,ds:dword ptr[eax+ebx*4] +// mov ebp,ds:dword ptr[edx+ebx*4] +// mov bl,ds:byte ptr[-3+esi+ecx*1] +// add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8] +// add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8] +// mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi +// mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp +// sub ecx,2 +// jnz LMix8Loop +// LDone: +// pop ebp +// pop ebx +// pop edi +// pop esi +// ret +// } +// } +// +// #endif +// #endif +// +// void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count, int offset) +// { +// int data; +// int left, right; +// int leftvol, rightvol; +// signed short *sfx; +// int i; +// portable_samplepair_t *samp; +// +// leftvol = ch->leftvol*snd_vol; +// rightvol = ch->rightvol*snd_vol; +// sfx = (signed short *)sc->data + ch->pos; +// +// samp = &paintbuffer[offset]; +// for (i=0 ; i<count ; i++, samp++) +// { +// data = sfx[i]; +// left = (data * leftvol)>>8; +// right = (data * rightvol)>>8; +// samp->left += left; +// samp->right += right; +// } +// +// ch->pos += count; +// } + +} diff --git a/src/jake2/client/V.java b/src/jake2/client/V.java new file mode 100644 index 0000000..3b9d139 --- /dev/null +++ b/src/jake2/client/V.java @@ -0,0 +1,417 @@ +/* + * V.java + * Copyright (C) 2003 + * + * $Id: V.java,v 1.1 2004-07-07 19:58:52 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.sys.Sys; +import jake2.util.Math3D; +import jake2.util.Vargs; + +import java.io.IOException; + +/** + * V + */ +public final class V extends Globals { + + static cvar_t cl_testblend; + static cvar_t cl_testparticles; + static cvar_t cl_testentities; + static cvar_t cl_testlights; + static cvar_t cl_stats; + + static int r_numdlights; + static dlight_t[] r_dlights = new dlight_t[MAX_DLIGHTS]; + + static int r_numentities; + static entity_t[] r_entities = new entity_t[MAX_ENTITIES]; + + static int r_numparticles; + static particle_t[] r_particles = new particle_t[MAX_PARTICLES]; + + static lightstyle_t[] r_lightstyles = new lightstyle_t[MAX_LIGHTSTYLES]; + static { + for (int i = 0; i < r_dlights.length; i++) + r_dlights[i] = new dlight_t(); + for (int i = 0; i < r_entities.length; i++) + r_entities[i] = new entity_t(); + for (int i = 0; i < r_particles.length; i++) + r_particles[i] = new particle_t(); + for (int i = 0; i < r_lightstyles.length; i++) + r_lightstyles[i] = new lightstyle_t(); + } + + /* + ==================== + V_ClearScene + + Specifies the model that will be used as the world + ==================== + */ + static void ClearScene() { + r_numdlights = 0; + r_numentities = 0; + r_numparticles = 0; + } + + /* + ===================== + V_AddEntity + + ===================== + */ + static void AddEntity(entity_t ent) { + if (r_numentities >= MAX_ENTITIES) + return; + r_entities[r_numentities++].set(ent); + } + + /* + ===================== + V_AddParticle + + ===================== + */ + static void AddParticle(float[] org, int color, float alpha) { + particle_t p; + + if (r_numparticles >= MAX_PARTICLES) + return; + + p = r_particles[r_numparticles++]; + + VectorCopy(org, p.origin); + p.color = color; + p.alpha = alpha; + } + + /* + ===================== + V_AddLight + + ===================== + */ + static void AddLight(float[] org, float intensity, float r, float g, float b) { + dlight_t dl; + + if (r_numdlights >= MAX_DLIGHTS) + return; + dl = r_dlights[r_numdlights++]; + VectorCopy(org, dl.origin); + dl.intensity = intensity; + dl.color[0] = r; + dl.color[1] = g; + dl.color[2] = b; + } + + + /* + ===================== + V_AddLightStyle + + ===================== + */ + static void AddLightStyle(int style, float r, float g, float b) { + lightstyle_t ls; + + if (style < 0 || style > MAX_LIGHTSTYLES) + Com.Error(ERR_DROP, "Bad light style " + style); + ls = r_lightstyles[style]; + + ls.white = r + g + b; + ls.rgb[0] = r; + ls.rgb[1] = g; + ls.rgb[2] = b; + } + + /* + ================ + V_TestParticles + + If cl_testparticles is set, create 4096 particles in the view + ================ + */ + static void TestParticles() { + particle_t p; + int i, j; + float d, r, u; + + r_numparticles = MAX_PARTICLES; + for (i = 0; i < r_numparticles; i++) { + d = i * 0.25f; + r = 4 * ((i & 7) - 3.5f); + u = 4 * (((i >> 3) & 7) - 3.5f); + p = r_particles[i]; + + for (j = 0; j < 3; j++) + p.origin[j] = + cl.refdef.vieworg[j] + + cl.v_forward[j] * d + + cl.v_right[j] * r + + cl.v_up[j] * u; + + p.color = 8; + p.alpha = cl_testparticles.value; + } + } + + /* + ================ + V_TestEntities + + If cl_testentities is set, create 32 player models + ================ + */ + static void TestEntities() { + int i, j; + float f, r; + entity_t ent; + + r_numentities = 32; + //memset (r_entities, 0, sizeof(r_entities)); + for (i = 0; i < r_entities.length; i++) + r_entities[i] = new entity_t(); + + for (i=0 ; i<r_numentities ; i++) + { + ent = r_entities[i]; + + r = 64 * ( (i%4) - 1.5f ); + f = 64 * (i/4) + 128; + + for (j=0 ; j<3 ; j++) + ent.origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f + + cl.v_right[j]*r; + + ent.model = cl.baseclientinfo.model; + ent.skin = cl.baseclientinfo.skin; + } + } + + /* + ================ + V_TestLights + + If cl_testlights is set, create 32 lights models + ================ + */ + static void TestLights() { + int i, j; + float f, r; + dlight_t dl; + + r_numdlights = 32; + //memset (r_dlights, 0, sizeof(r_dlights)); + for (i = 0; i < r_dlights.length; i++) + r_dlights[i] = new dlight_t(); + + for (i=0 ; i<r_numdlights ; i++) + { + dl = r_dlights[i]; + + r = 64 * ( (i%4) - 1.5f ); + f = 64 * (i/4) + 128; + + for (j=0 ; j<3 ; j++) + dl.origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f + + cl.v_right[j]*r; + dl.color[0] = ((i%6)+1) & 1; + dl.color[1] = (((i%6)+1) & 2)>>1; + dl.color[2] = (((i%6)+1) & 4)>>2; + dl.intensity = 200; + } + } + + static xcommand_t Gun_Next_f = new xcommand_t() { + public void execute() { + gun_frame++; + Com.Printf("frame " + gun_frame + "\n"); + } + }; + + static xcommand_t Gun_Prev_f = new xcommand_t() { + public void execute() { + gun_frame--; + if (gun_frame < 0) + gun_frame = 0; + Com.Printf("frame " + gun_frame + "\n"); + } + }; + + static xcommand_t Gun_Model_f = new xcommand_t() { + public void execute() { + if (Cmd.Argc() != 2) { + gun_model = null; + return; + } + String name = "models/" + Cmd.Argv(1) + "/tris.md2"; + gun_model = re.RegisterModel(name); + } + }; + + /* + ================== + V_RenderView + + ================== + */ + static void RenderView(float stereo_separation) { +// extern int entitycmpfnc( const entity_t *, const entity_t * ); +// + if (cls.state != ca_active) return; + + if (!cl.refresh_prepped) return; // still loading + + if (cl_timedemo.value != 0.0f) { + if (cl.timedemo_start == 0) + cl.timedemo_start = Sys.Milliseconds(); + cl.timedemo_frames++; + } + + // an invalid frame will just use the exact previous refdef + // we can't use the old frame if the video mode has changed, though... + if ( cl.frame.valid && (cl.force_refdef || cl_paused.value == 0.0f) ) { + cl.force_refdef = false; + + V.ClearScene(); + + // build a refresh entity list and calc cl.sim* + // this also calls CL_CalcViewValues which loads + // v_forward, etc. + CL.AddEntities(); + + if (cl_testparticles.value != 0.0f) + TestParticles(); + if (cl_testentities.value != 0.0f) + TestEntities(); + if (cl_testlights.value != 0.0f) + TestLights(); + if (cl_testblend.value != 0.0f) { + cl.refdef.blend[0] = 1.0f; + cl.refdef.blend[1] = 0.5f; + cl.refdef.blend[2] = 0.25f; + cl.refdef.blend[3] = 0.5f; + } + + // offset vieworg appropriately if we're doing stereo separation + if ( stereo_separation != 0 ) { + float[] tmp = new float[3]; + + Math3D.VectorScale( cl.v_right, stereo_separation, tmp ); + Math3D.VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg ); + } + + // never let it sit exactly on a node line, because a water plane can + // dissapear when viewed with the eye exactly on it. + // the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis + cl.refdef.vieworg[0] += 1.0/16; + cl.refdef.vieworg[1] += 1.0/16; + cl.refdef.vieworg[2] += 1.0/16; + + cl.refdef.x = scr_vrect.x; + cl.refdef.y = scr_vrect.y; + cl.refdef.width = scr_vrect.width; + cl.refdef.height = scr_vrect.height; + cl.refdef.fov_y = Math3D.CalcFov(cl.refdef.fov_x, cl.refdef.width, cl.refdef.height); + cl.refdef.time = cl.time*0.001f; + + cl.refdef.areabits = cl.frame.areabits; + + if (cl_add_entities.value == 0.0f) + r_numentities = 0; + if (cl_add_particles.value == 0.0f) + r_numparticles = 0; + if (cl_add_lights.value == 0.0f) + r_numdlights = 0; + if (cl_add_blend.value == 0) { + Math3D.VectorClear(cl.refdef.blend); + } + + cl.refdef.num_entities = r_numentities; + cl.refdef.entities = r_entities; + cl.refdef.num_particles = r_numparticles; + cl.refdef.particles = r_particles; + cl.refdef.num_dlights = r_numdlights; + cl.refdef.dlights = r_dlights; + cl.refdef.lightstyles = r_lightstyles; + + cl.refdef.rdflags = cl.frame.playerstate.rdflags; + + // sort entities for better cache locality + // !!! useless in Java !!! + //Arrays.sort(cl.refdef.entities, entitycmpfnc); + } + + re.RenderFrame(cl.refdef); + if (cl_stats.value != 0.0f) + Com.Printf("ent:%i lt:%i part:%i\n", new Vargs(3).add(r_numentities).add( + r_numdlights).add(r_numparticles)); + if ( log_stats.value != 0.0f && ( log_stats_file != null ) ) + try { + log_stats_file.write(r_numentities + "," + r_numdlights + "," + r_numparticles); + } catch (IOException e) {} + + SCR.AddDirtyPoint(scr_vrect.x, scr_vrect.y); + SCR.AddDirtyPoint(scr_vrect.x+scr_vrect.width-1, scr_vrect.y+scr_vrect.height-1); + + SCR.DrawCrosshair(); + } + + /* + ============= + V_Viewpos_f + ============= + */ + static xcommand_t Viewpos_f = new xcommand_t() { + public void execute() { + Com.Printf("(%i %i %i) : %i\n", + new Vargs(4).add((int)cl.refdef.vieworg[0]).add( + (int)cl.refdef.vieworg[1]).add( + (int)cl.refdef.vieworg[2]).add( + (int)cl.refdef.viewangles[YAW])); + } + }; + + public static void Init() { + Cmd.AddCommand("gun_next", Gun_Next_f); + Cmd.AddCommand("gun_prev", Gun_Prev_f); + Cmd.AddCommand("gun_model", Gun_Model_f); + + Cmd.AddCommand("viewpos", Viewpos_f); + + crosshair = Cvar.Get("crosshair", "0", CVAR_ARCHIVE); + + cl_testblend = Cvar.Get("cl_testblend", "0", 0); + cl_testparticles = Cvar.Get("cl_testparticles", "0", 0); + cl_testentities = Cvar.Get("cl_testentities", "0", 0); + cl_testlights = Cvar.Get("cl_testlights", "0", 0); + + cl_stats = Cvar.Get("cl_stats", "0", 0); + } +} diff --git a/src/jake2/client/VID.java b/src/jake2/client/VID.java new file mode 100644 index 0000000..dad6fc0 --- /dev/null +++ b/src/jake2/client/VID.java @@ -0,0 +1,915 @@ +/* + * VID.java + * Copyright (C) 2003 + * + * $Id: VID.java,v 1.1 2004-07-07 19:58:52 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.Defines; +import jake2.Globals; +import jake2.game.Cmd; +import jake2.game.cvar_t; +import jake2.qcommon.*; +import jake2.render.Renderer; +import jake2.sys.*; +import jake2.sys.KBD; +import jake2.sys.RW; +import jake2.util.Vargs; + +import java.awt.Dimension; + +/** + * VID is a video driver. + * + * source: client/vid.h linux/vid_so.c + * + * @author cwei + */ +public class VID extends Globals { + // Main windowed and fullscreen graphics interface module. This module + // is used for both the software and OpenGL rendering versions of the + // Quake refresh engine. + + // Global variables used internally by this module + // Globals.viddef + // global video state; used by other modules + + // Structure containing functions exported from refresh DLL + // Globals.re; + + // Console variables that we need to access from this module + static cvar_t vid_gamma; + static cvar_t vid_ref; // Name of Refresh DLL loaded + static cvar_t vid_xpos; // X coordinate of window position + static cvar_t vid_ypos; // Y coordinate of window position + static cvar_t vid_fullscreen; + + // Global variables used internally by this module + // void *reflib_library; // Handle to refresh DLL + static boolean reflib_active = false; + // const char so_file[] = "/etc/quake2.conf"; + + /* + ========================================================================== + + DLL GLUE + + ========================================================================== + */ + + public static void Printf(int print_level, String fmt, Vargs vargs) { + // static qboolean inupdate; + if (print_level == Defines.PRINT_ALL) + Com.Printf(fmt, vargs); + else + Com.DPrintf(fmt, vargs); + } + + public static void Error(int err_level, String fmt, Vargs vargs) + { + //static qboolean inupdate; + Com.Error(err_level, fmt, vargs); + } + + // ========================================================================== + + /* + ============ + VID_Restart_f + + Console command to re-start the video mode and refresh DLL. We do this + simply by setting the modified flag for the vid_ref variable, which will + cause the entire video mode and refresh DLL to be reset on the next frame. + ============ + */ + static void Restart_f() + { + vid_ref.modified = true; + } + + /* + ** VID_GetModeInfo + */ + static final vidmode_t vid_modes[] = + { + new vidmode_t("Mode 0: 320x240", 320, 240, 0), + new vidmode_t("Mode 1: 400x300", 400, 300, 1), + new vidmode_t("Mode 2: 512x384", 512, 384, 2), + new vidmode_t("Mode 3: 640x480", 640, 480, 3), + new vidmode_t("Mode 4: 800x600", 800, 600, 4), + new vidmode_t("Mode 5: 960x720", 960, 720, 5), + new vidmode_t("Mode 6: 1024x768", 1024, 768, 6), + new vidmode_t("Mode 7: 1152x864", 1152, 864, 7), + new vidmode_t("Mode 8: 1280x1024", 1280, 1024, 8), + new vidmode_t("Mode 9: 1600x1200", 1600, 1200, 9), + new vidmode_t("Mode 10: 2048x1536", 2048, 1536, 10)}; + + static final int NUM_MODES = vid_modes.length; + + public static boolean GetModeInfo(Dimension dim, int mode) { + if (mode < 0 || mode >= NUM_MODES) + return false; + + dim.width = vid_modes[mode].width; + dim.height = vid_modes[mode].height; + return true; + } + + /* + ** VID_NewWindow + */ + public static void NewWindow(int width, int height) { + Globals.viddef.width = width; + Globals.viddef.height = height; + } + + static void FreeReflib() + { + if (Globals.re != null) { + KBD.Close(); + RW.IN_Shutdown(); + } + + Globals.re = null; + reflib_active = false; + } + + /* + ============== + VID_LoadRefresh + ============== + */ + static boolean LoadRefresh( String name ) + { + + if ( reflib_active ) + { + KBD.Close(); + RW.IN_Shutdown(); + + Globals.re.Shutdown(); + FreeReflib(); + } + + Com.Printf( "------- Loading " + name + " -------\n"); + + boolean found = false; + + String[] driverNames = Renderer.getDriverNames(); + for (int i = 0; i < driverNames.length; i++) { + if (driverNames[i].equals(name)) { + found = true; + break; + } + } + + if (!found) { + Com.Printf( "LoadLibrary(\"" + name +"\") failed\n"); + return false; + } + + Com.Printf( "LoadLibrary(\"" + name +"\")\n" ); + refimport_t ri = new refimport_t() { + public void Sys_Error(int err_level, String str) { + VID.Error(err_level, str, null); + } + + public void Sys_Error(int err_level, String str, Vargs vargs) { + VID.Error(err_level, str, vargs); + } + + public void Cmd_AddCommand(String name, xcommand_t cmd) { + Cmd.AddCommand(name, cmd); + } + + public void Cmd_RemoveCommand(String name) { + Cmd.RemoveCommand(name); + } + + public int Cmd_Argc() { + return Cmd.Argc(); + } + + public String Cmd_Argv(int i) { + return Cmd.Argv(i); + } + + public void Cmd_ExecuteText(int exec_when, String text) { + Cbuf.ExecuteText(exec_when, text); + } + + public void Con_Printf(int print_level, String str) { + VID.Printf(print_level, str, null); + } + + public void Con_Printf(int print_level, String str, Vargs vargs) { + VID.Printf(print_level, str, vargs); + } + + public byte[] FS_LoadFile(String name) { + return FS.LoadFile(name); + } + + public int FS_FileLength(String name) { + return FS.FileLength(name); + } + + public void FS_FreeFile(byte[] buf) { + FS.FreeFile(buf); + } + + public String FS_Gamedir() { + return FS.Gamedir(); + } + + public cvar_t Cvar_Get(String name, String value, int flags) { + return Cvar.Get(name, value, flags); + } + + public cvar_t Cvar_Set(String name, String value) { + return Cvar.Set(name, value); + } + + public void Cvar_SetValue(String name, float value) { + Cvar.SetValue(name, value); + } + + public boolean Vid_GetModeInfo(Dimension dim, int mode) { + return VID.GetModeInfo(dim, mode); + } + + public void Vid_MenuInit() { + VID.MenuInit(); + } + + public void Vid_NewWindow(int width, int height) { + VID.NewWindow(width, height); + } + + public void updateScreenCallback() { + SCR.UpdateScreen2(); + } + }; + + Globals.re = Renderer.getDriver( name, ri ); + + if (Globals.re == null) + { + Com.Error(Defines.ERR_FATAL, name + " can't load but registered"); + } + + if (Globals.re.apiVersion() != Defines.API_VERSION) + { + FreeReflib(); + Com.Error(Defines.ERR_FATAL, name + " has incompatible api_version"); + } + + /* Init IN (Mouse) */ +// in_state.IN_CenterView_fp = IN_CenterView; +// in_state.Key_Event_fp = Do_Key_Event; +// in_state.viewangles = cl.viewangles; +// in_state.in_strafe_state = &in_strafe.state; + + IN.Real_IN_Init(); + + if ( !Globals.re.Init((int)vid_xpos.value, (int)vid_ypos.value) ) + { + Globals.re.Shutdown(); + FreeReflib(); + return false; + } + + /* Init KBD */ + KBD.Init(); + + Com.Printf( "------------------------------------\n"); + reflib_active = true; + return true; + } + + /* + ============ + VID_CheckChanges + + This function gets called once just before drawing each frame, and it's sole purpose in life + is to check to see if any of the video mode parameters have changed, and if they have to + update the rendering DLL and/or video mode to match. + ============ + */ + public static void CheckChanges() + { + cvar_t gl_mode; + + if ( vid_ref.modified ) + { + S.StopAllSounds(); + } + + while (vid_ref.modified) + { + /* + ** refresh has changed + */ + vid_ref.modified = false; + vid_fullscreen.modified = true; + Globals.cl.refresh_prepped = false; + Globals.cls.disable_screen = 1.0f; // true; + + if ( !LoadRefresh( vid_ref.string ) ) + { + if ( vid_ref.string.equals("jogl") ) { + Com.Printf("Refresh failed\n"); + gl_mode = Cvar.Get( "gl_mode", "0", 0 ); + if (gl_mode.value != 0.0f) { + Com.Printf("Trying mode 0\n"); + Cvar.SetValue("gl_mode", 0); + if ( !LoadRefresh( vid_ref.string ) ) + Com.Error(Defines.ERR_FATAL, "Couldn't fall back to jogl refresh!"); + } else + Com.Error(Defines.ERR_FATAL, "Couldn't fall back to jogl refresh!"); + } + + Cvar.Set( "vid_ref", "jogl" ); + + /* + * drop the console if we fail to load a refresh + */ + if ( Globals.cls.key_dest != Globals.key_console ) + { + try { + Console.ToggleConsole_f.execute(); + } catch (Exception e) { + } + } + } + Globals.cls.disable_screen = 0.0f; //false; + } + } + + /* + ============ + VID_Init + ============ + */ + public static void Init() + { + /* Create the video variables so we know how to start the graphics drivers */ + vid_ref = Cvar.Get("vid_ref", "jogl", CVAR_ARCHIVE); + vid_xpos = Cvar.Get("vid_xpos", "3", CVAR_ARCHIVE); + vid_ypos = Cvar.Get("vid_ypos", "22", CVAR_ARCHIVE); + vid_fullscreen = Cvar.Get("vid_fullscreen", "0", CVAR_ARCHIVE); + vid_gamma = Cvar.Get( "vid_gamma", "1", CVAR_ARCHIVE ); + + /* Add some console commands that we want to handle */ + Cmd.AddCommand ("vid_restart", new xcommand_t() { + public void execute() { + Restart_f(); + } + }); + + /* Disable the 3Dfx splash screen */ + // putenv("FX_GLIDE_NO_SPLASH=0"); + + /* Start the graphics mode and load refresh DLL */ + CheckChanges(); + } + + /* + ============ + VID_Shutdown + ============ + */ + public static void Shutdown() + { + if ( reflib_active ) + { + KBD.Close(); + RW.IN_Shutdown(); + + Globals.re.Shutdown(); + FreeReflib(); + } + } + + // ========================================================================== + // + // vid_menu.c + // + // ========================================================================== + +// #define REF_SOFT 0 +// #define REF_SOFTX11 1 +// #define REF_MESA3D 2 +// #define REF_3DFXGL 3 +// #define REF_OPENGLX 4 + static final int REF_OPENGL_JOGL = 0; +// #define REF_MESA3DGLX 5 + +// extern cvar_t *vid_ref; +// extern cvar_t *vid_fullscreen; +// extern cvar_t *vid_gamma; +// extern cvar_t *scr_viewsize; + + static cvar_t gl_mode; + static cvar_t gl_driver; + static cvar_t gl_picmip; + static cvar_t gl_ext_palettedtexture; + + static cvar_t sw_mode; + static cvar_t sw_stipplealpha; + + static cvar_t _windowed_mouse; + + /* + ==================================================================== + + MENU INTERACTION + + ==================================================================== + */ + static final int SOFTWARE_MENU = 0; + static final int OPENGL_MENU = 1; + + static Menu.menuframework_s s_software_menu = new Menu.menuframework_s(); + static Menu.menuframework_s s_opengl_menu = new Menu.menuframework_s(); + static Menu.menuframework_s s_current_menu; // referenz + static int s_current_menu_index = 1; // default is the openGL menu + + static Menu.menulist_s[] s_mode_list = new Menu.menulist_s[2]; + static { + s_mode_list[0] = new Menu.menulist_s(); + s_mode_list[1] = new Menu.menulist_s(); + } + static Menu.menulist_s[] s_ref_list = new Menu.menulist_s[2]; + static { + s_ref_list[0] = new Menu.menulist_s(); + s_ref_list[1] = new Menu.menulist_s(); + } + static Menu.menuslider_s s_tq_slider = new Menu.menuslider_s(); + static Menu.menuslider_s[] s_screensize_slider = new Menu.menuslider_s[2]; + static { + s_screensize_slider[0] = new Menu.menuslider_s(); + s_screensize_slider[1] = new Menu.menuslider_s(); + } + static Menu.menuslider_s[] s_brightness_slider = new Menu.menuslider_s[2]; + static { + s_brightness_slider[0] = new Menu.menuslider_s(); + s_brightness_slider[1] = new Menu.menuslider_s(); + } + static Menu.menulist_s[] s_fs_box = new Menu.menulist_s[2]; + static { + s_fs_box[0] = new Menu.menulist_s(); + s_fs_box[1] = new Menu.menulist_s(); + } + static Menu.menulist_s s_stipple_box = new Menu.menulist_s(); + static Menu.menulist_s s_paletted_texture_box = new Menu.menulist_s(); + static Menu.menulist_s s_windowed_mouse = new Menu.menulist_s(); + static Menu.menuaction_s[] s_apply_action = new Menu.menuaction_s[2]; + static { + s_apply_action[0] = new Menu.menuaction_s(); + s_apply_action[1] = new Menu.menuaction_s(); + } + static Menu.menuaction_s[] s_defaults_action= new Menu.menuaction_s[2]; + static { + s_defaults_action[0] = new Menu.menuaction_s(); + s_defaults_action[1] = new Menu.menuaction_s(); + } + + static void DriverCallback( Object unused ) + { + s_ref_list[1 - s_current_menu_index].curvalue = s_ref_list[s_current_menu_index].curvalue; + + if ( s_ref_list[s_current_menu_index].curvalue < 2 ) + { + // we only use opengl today + s_current_menu = s_opengl_menu; // s_software_menu; + s_current_menu_index = 1; // 0; + } + else + { + s_current_menu = s_opengl_menu; + s_current_menu_index = 1; + } + } + + static void ScreenSizeCallback( Object s ) + { + Menu.menuslider_s slider = (Menu.menuslider_s) s; + + Cvar.SetValue( "viewsize", slider.curvalue * 10 ); + } + + static void BrightnessCallback( Object s ) + { + Menu.menuslider_s slider = (Menu.menuslider_s) s; + + if ( s_current_menu_index == 0) + s_brightness_slider[1].curvalue = s_brightness_slider[0].curvalue; + else + s_brightness_slider[0].curvalue = s_brightness_slider[1].curvalue; + + // if ( stricmp( vid_ref.string, "soft" ) == 0 || + // stricmp( vid_ref.string, "softx" ) == 0 ) + if ( vid_ref.string.equalsIgnoreCase("soft") || + vid_ref.string.equalsIgnoreCase("softx") ) + { + float gamma = ( 0.8f - ( slider.curvalue/10.0f - 0.5f ) ) + 0.5f; + + Cvar.SetValue( "vid_gamma", gamma ); + } + } + + static void ResetDefaults( Object unused ) + { + MenuInit(); + } + + static void ApplyChanges( Object unused ) + { + /* + ** make values consistent + */ + s_fs_box[1 - s_current_menu_index].curvalue = s_fs_box[s_current_menu_index].curvalue; + s_brightness_slider[1 - s_current_menu_index].curvalue = s_brightness_slider[s_current_menu_index].curvalue; + s_ref_list[1 - s_current_menu_index].curvalue = s_ref_list[s_current_menu_index].curvalue; + + /* + ** invert sense so greater = brighter, and scale to a range of 0.5 to 1.3 + */ + float gamma = ( 0.8f - ( s_brightness_slider[s_current_menu_index].curvalue/10.0f - 0.5f ) ) + 0.5f; + + Cvar.SetValue( "vid_gamma", gamma ); + Cvar.SetValue( "sw_stipplealpha", s_stipple_box.curvalue ); + Cvar.SetValue( "gl_picmip", 3 - s_tq_slider.curvalue ); + Cvar.SetValue( "vid_fullscreen", s_fs_box[s_current_menu_index].curvalue ); + Cvar.SetValue( "gl_ext_palettedtexture", s_paletted_texture_box.curvalue ); + Cvar.SetValue( "sw_mode", s_mode_list[SOFTWARE_MENU].curvalue ); + Cvar.SetValue( "gl_mode", s_mode_list[OPENGL_MENU].curvalue ); + Cvar.SetValue( "_windowed_mouse", s_windowed_mouse.curvalue); + + switch ( s_ref_list[s_current_menu_index].curvalue ) + { +// case REF_SOFT: +// Cvar_Set( "vid_ref", "soft" ); +// break; +// case REF_SOFTX11: +// Cvar_Set( "vid_ref", "softx" ); +// break; +// +// case REF_MESA3D : +// Cvar_Set( "vid_ref", "gl" ); +// Cvar_Set( "gl_driver", "libMesaGL.so.2" ); +// if (gl_driver->modified) +// vid_ref->modified = true; +// break; +// +// case REF_OPENGLX : +// Cvar_Set( "vid_ref", "glx" ); +// Cvar_Set( "gl_driver", "libGL.so" ); +// if (gl_driver->modified) +// vid_ref->modified = true; +// break; +// +// case REF_MESA3DGLX : +// Cvar_Set( "vid_ref", "glx" ); +// Cvar_Set( "gl_driver", "libMesaGL.so.2" ); +// if (gl_driver->modified) +// vid_ref->modified = true; +// break; +// +// case REF_3DFXGL : +// Cvar_Set( "vid_ref", "gl" ); +// Cvar_Set( "gl_driver", "lib3dfxgl.so" ); +// if (gl_driver->modified) +// vid_ref->modified = true; +// break; + case REF_OPENGL_JOGL : + Cvar.Set( "vid_ref", "jogl" ); + Cvar.Set( "gl_driver", "jogl" ); + if (gl_driver.modified) + vid_ref.modified = true; + break; + } + + Menu.ForceMenuOff(); + } + + static final String[] resolutions = + { + "[320 240 ]", + "[400 300 ]", + "[512 384 ]", + "[640 480 ]", + "[800 600 ]", + "[960 720 ]", + "[1024 768 ]", + "[1152 864 ]", + "[1280 1024]", + "[1600 1200]", + "[2048 1536]", + null + }; + static final String[] refs = + { + // "[software ]", + // "[software X11 ]", + // "[Mesa 3-D 3DFX ]", + // "[3DFXGL Miniport]", + // "[OpenGL glX ]", + // "[Mesa 3-D glX ]", + "[OpenGL jogl ]", + null + }; + static final String[] yesno_names = + { + "no", + "yes", + null + }; + + /* + ** VID_MenuInit + */ + public static void MenuInit() + { + int i; + + if ( gl_driver == null ) + gl_driver = Cvar.Get( "gl_driver", "jogl", 0 ); + if ( gl_picmip == null ) + gl_picmip = Cvar.Get( "gl_picmip", "0", 0 ); + if ( gl_mode == null) + gl_mode = Cvar.Get( "gl_mode", "3", 0 ); + if ( sw_mode == null ) + sw_mode = Cvar.Get( "sw_mode", "0", 0 ); + if ( gl_ext_palettedtexture == null ) + gl_ext_palettedtexture = Cvar.Get( "gl_ext_palettedtexture", "1", CVAR_ARCHIVE ); + + if ( sw_stipplealpha == null ) + sw_stipplealpha = Cvar.Get( "sw_stipplealpha", "0", CVAR_ARCHIVE ); + + if ( _windowed_mouse == null) + _windowed_mouse = Cvar.Get( "_windowed_mouse", "0", CVAR_ARCHIVE ); + + s_mode_list[SOFTWARE_MENU].curvalue = (int)sw_mode.value; + s_mode_list[OPENGL_MENU].curvalue = (int)gl_mode.value; + + if ( SCR.scr_viewsize == null ) + SCR.scr_viewsize = Cvar.Get ("viewsize", "100", CVAR_ARCHIVE); + + s_screensize_slider[SOFTWARE_MENU].curvalue = (int)(SCR.scr_viewsize.value/10); + s_screensize_slider[OPENGL_MENU].curvalue = (int)(SCR.scr_viewsize.value/10); + +// if ( strcmp( vid_ref->string, "soft" ) == 0) +// { +// s_current_menu_index = SOFTWARE_MENU; +// s_ref_list[0].curvalue = s_ref_list[1].curvalue = REF_SOFT; +// } + if ( vid_ref.string.equalsIgnoreCase("jogl")) + { + s_current_menu_index = OPENGL_MENU; + s_ref_list[0].curvalue = s_ref_list[1].curvalue = REF_OPENGL_JOGL; + } +// else if (strcmp( vid_ref->string, "softx" ) == 0 ) +// { +// s_current_menu_index = SOFTWARE_MENU; +// s_ref_list[0].curvalue = s_ref_list[1].curvalue = REF_SOFTX11; +// } +// else if ( strcmp( vid_ref->string, "gl" ) == 0 ) +// { +// s_current_menu_index = OPENGL_MENU; +// if ( strcmp( gl_driver->string, "lib3dfxgl.so" ) == 0 ) +// s_ref_list[s_current_menu_index].curvalue = REF_3DFXGL; +// else +// s_ref_list[s_current_menu_index].curvalue = REF_MESA3D; +// } +// else if ( strcmp( vid_ref->string, "glx" ) == 0 ) +// { +// s_current_menu_index = OPENGL_MENU; +// if ( strcmp( gl_driver->string, "libMesaGL.so.2" ) == 0 ) +// s_ref_list[s_current_menu_index].curvalue = REF_MESA3DGLX; +// else +// s_ref_list[s_current_menu_index].curvalue = REF_OPENGLX; +// } +// + s_software_menu.x = (int)(viddef.width * 0.50f); + s_software_menu.nitems = 0; + s_opengl_menu.x = (int)(viddef.width * 0.50f); + s_opengl_menu.nitems = 0; + + for ( i = 0; i < 2; i++ ) + { + s_ref_list[i].type = MTYPE_SPINCONTROL; + s_ref_list[i].name = "driver"; + s_ref_list[i].x = 0; + s_ref_list[i].y = 0; + s_ref_list[i].callback = new Menu.mcallback() { + public void execute(Object self) { + DriverCallback(self); + } + }; + s_ref_list[i].itemnames = refs; + + s_mode_list[i].type = MTYPE_SPINCONTROL; + s_mode_list[i].name = "video mode"; + s_mode_list[i].x = 0; + s_mode_list[i].y = 10; + s_mode_list[i].itemnames = resolutions; + + s_screensize_slider[i].type = MTYPE_SLIDER; + s_screensize_slider[i].x = 0; + s_screensize_slider[i].y = 20; + s_screensize_slider[i].name = "screen size"; + s_screensize_slider[i].minvalue = 3; + s_screensize_slider[i].maxvalue = 12; + s_screensize_slider[i].callback = new Menu.mcallback() { + public void execute(Object self) { + ScreenSizeCallback(self); + } + }; + s_brightness_slider[i].type = MTYPE_SLIDER; + s_brightness_slider[i].x = 0; + s_brightness_slider[i].y = 30; + s_brightness_slider[i].name = "brightness"; + s_brightness_slider[i].callback = new Menu.mcallback() { + public void execute(Object self) { + BrightnessCallback(self); + } + }; + s_brightness_slider[i].minvalue = 5; + s_brightness_slider[i].maxvalue = 13; + s_brightness_slider[i].curvalue = ( 1.3f - vid_gamma.value + 0.5f ) * 10; + + s_fs_box[i].type = MTYPE_SPINCONTROL; + s_fs_box[i].x = 0; + s_fs_box[i].y = 40; + s_fs_box[i].name = "fullscreen"; + s_fs_box[i].itemnames = yesno_names; + s_fs_box[i].curvalue = (int)vid_fullscreen.value; + + s_defaults_action[i].type = MTYPE_ACTION; + s_defaults_action[i].name = "reset to default"; + s_defaults_action[i].x = 0; + s_defaults_action[i].y = 90; + s_defaults_action[i].callback = new Menu.mcallback() { + public void execute(Object self) { + ResetDefaults(self); + } + }; + + s_apply_action[i].type = MTYPE_ACTION; + s_apply_action[i].name = "apply"; + s_apply_action[i].x = 0; + s_apply_action[i].y = 100; + s_apply_action[i].callback = new Menu.mcallback() { + public void execute(Object self) { + ApplyChanges(self); + } + }; + } + + s_stipple_box.type = MTYPE_SPINCONTROL; + s_stipple_box.x = 0; + s_stipple_box.y = 60; + s_stipple_box.name = "stipple alpha"; + s_stipple_box.curvalue = (int)sw_stipplealpha.value; + s_stipple_box.itemnames = yesno_names; + + s_windowed_mouse.type = MTYPE_SPINCONTROL; + s_windowed_mouse.x = 0; + s_windowed_mouse.y = 72; + s_windowed_mouse.name = "windowed mouse"; + s_windowed_mouse.curvalue = (int)_windowed_mouse.value; + s_windowed_mouse.itemnames = yesno_names; + + s_tq_slider.type = MTYPE_SLIDER; + s_tq_slider.x = 0; + s_tq_slider.y = 60; + s_tq_slider.name = "texture quality"; + s_tq_slider.minvalue = 0; + s_tq_slider.maxvalue = 3; + s_tq_slider.curvalue = 3 - gl_picmip.value; + + s_paletted_texture_box.type = MTYPE_SPINCONTROL; + s_paletted_texture_box.x = 0; + s_paletted_texture_box.y = 70; + s_paletted_texture_box.name = "8-bit textures"; + s_paletted_texture_box.itemnames = yesno_names; + s_paletted_texture_box.curvalue = (int)gl_ext_palettedtexture.value; + + Menu.Menu_AddItem( s_software_menu, s_ref_list[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_software_menu, s_mode_list[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_software_menu, s_screensize_slider[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_software_menu, s_brightness_slider[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_software_menu, s_fs_box[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_software_menu, s_stipple_box ); + Menu.Menu_AddItem( s_software_menu, s_windowed_mouse ); + + Menu.Menu_AddItem( s_opengl_menu, s_ref_list[OPENGL_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_mode_list[OPENGL_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_screensize_slider[OPENGL_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_brightness_slider[OPENGL_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_fs_box[OPENGL_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_tq_slider ); + Menu.Menu_AddItem( s_opengl_menu, s_paletted_texture_box ); + + Menu.Menu_AddItem( s_software_menu, s_defaults_action[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_software_menu, s_apply_action[SOFTWARE_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_defaults_action[OPENGL_MENU] ); + Menu.Menu_AddItem( s_opengl_menu, s_apply_action[OPENGL_MENU] ); + + Menu.Menu_Center( s_software_menu ); + Menu.Menu_Center( s_opengl_menu ); + s_opengl_menu.x -= 8; + s_software_menu.x -= 8; + } + + /* + ================ + VID_MenuDraw + ================ + */ + static void MenuDraw() + { + + if ( s_current_menu_index == 0 ) + s_current_menu = s_software_menu; + else + s_current_menu = s_opengl_menu; + + /* + ** draw the banner + */ + Dimension dim = new Dimension(); + re.DrawGetPicSize( dim, "m_banner_video" ); + re.DrawPic( viddef.width / 2 - dim.width / 2, viddef.height /2 - 110, "m_banner_video" ); + + /* + ** move cursor to a reasonable starting position + */ + Menu.Menu_AdjustCursor( s_current_menu, 1 ); + + /* + ** draw the menu + */ + Menu.Menu_Draw( s_current_menu ); + } + + /* + ================ + VID_MenuKey + ================ + */ + static String MenuKey( int key ) + { + Menu.menuframework_s m = s_current_menu; + final String sound = "misc/menu1.wav"; + + switch ( key ) + { + case K_ESCAPE: + Menu.PopMenu(); + return null; + case K_UPARROW: + m.cursor--; + Menu.Menu_AdjustCursor( m, -1 ); + break; + case K_DOWNARROW: + m.cursor++; + Menu.Menu_AdjustCursor( m, 1 ); + break; + case K_LEFTARROW: + Menu.Menu_SlideItem( m, -1 ); + break; + case K_RIGHTARROW: + Menu.Menu_SlideItem( m, 1 ); + break; + case K_ENTER: + Menu.Menu_SelectItem( m ); + break; + } + + return sound; + } + +} diff --git a/src/jake2/client/centity_t.java b/src/jake2/client/centity_t.java new file mode 100644 index 0000000..0c26bed --- /dev/null +++ b/src/jake2/client/centity_t.java @@ -0,0 +1,39 @@ +/* +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. + +*/ + +// Created on 27.11.2003 by RST. +// $Id: centity_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +import jake2.game.entity_state_t; + +public class centity_t { + entity_state_t baseline= new entity_state_t(null); // delta from this if not from a previous frame + entity_state_t current= new entity_state_t(null); + entity_state_t prev= new entity_state_t(null); // will always be valid, but might just be a copy of current + + int serverframe; // if not current, this ent isn't in the frame + + int trailcount; // for diminishing grenade trails + float[] lerp_origin = { 0, 0, 0 }; // for trails (variable hz) + + int fly_stoptime; +} diff --git a/src/jake2/client/cl_sustain_t.java b/src/jake2/client/cl_sustain_t.java new file mode 100644 index 0000000..6ba3767 --- /dev/null +++ b/src/jake2/client/cl_sustain_t.java @@ -0,0 +1,55 @@ +/* + * cl_sustain_t.java + * Copyright (C) 2004 + * + * $Id: cl_sustain_t.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * cl_sustain_t + */ +public class cl_sustain_t { + static abstract class ThinkAdapter { + abstract void think(cl_sustain_t self); + } + + int id; + int type; + int endtime; + int nextthink; + int thinkinterval; + float[] org = new float[3]; + float[] dir = new float[3]; + int color; + int count; + int magnitude; + + ThinkAdapter think; + + void clear() { + org[0] = org[1] = org[2] = + dir[0] = dir[1] = dir[2] = + id = type = endtime = nextthink = thinkinterval = color = count = magnitude = 0; + think = null; + } +} diff --git a/src/jake2/client/client_state_t.java b/src/jake2/client/client_state_t.java new file mode 100644 index 0000000..3577932 --- /dev/null +++ b/src/jake2/client/client_state_t.java @@ -0,0 +1,137 @@ +/* +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. + +*/ + +// Created on 27.11.2003 by RST. +//$Id: client_state_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +import jake2.Defines; +import jake2.game.cmodel_t; +import jake2.game.usercmd_t; +import jake2.render.image_t; +import jake2.render.model_t; + +import java.io.RandomAccessFile; + +public class client_state_t { + + public client_state_t() { + for (int n = 0; n < Defines.CMD_BACKUP; n++) + cmds[n] = new usercmd_t(); + for (int i = 0; i < frames.length; i++) { + frames[i] = new frame_t(); + } + + for (int n = 0; n < Defines.MAX_CONFIGSTRINGS; n++) + configstrings[n] = new String(); + + for (int n=0; n < Defines.MAX_CLIENTS; n++) + clientinfo[n] = new clientinfo_t(); + } + // + // the client_state_t structure is wiped completely at every + // server map change + // + int timeoutcount; + + int timedemo_frames; + int timedemo_start; + + public boolean refresh_prepped; // false if on new level or new ref dll + boolean sound_prepped; // ambient sounds can start + boolean force_refdef; // vid has changed, so we can't use a paused refdef + + int parse_entities; // index (not anded off) into cl_parse_entities[] + + usercmd_t cmd = new usercmd_t(); + usercmd_t cmds[] = new usercmd_t[Defines.CMD_BACKUP]; // each mesage will send several old cmds + + int cmd_time[] = new int[Defines.CMD_BACKUP]; // time sent, for calculating pings + short predicted_origins[][] = new short[Defines.CMD_BACKUP][3]; // for debug comparing against server + + float predicted_step; // for stair up smoothing + int predicted_step_time; + + float[] predicted_origin ={0,0,0}; // generated by CL_PredictMovement + float[] predicted_angles={0,0,0}; + float[] prediction_error={0,0,0}; + + public frame_t frame = new frame_t(); // received from server + int surpressCount; // number of messages rate supressed + frame_t frames[] = new frame_t[Defines.UPDATE_BACKUP]; + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + public float[] viewangles = { 0, 0, 0 }; + + int time; // this is the time value that the client + // is rendering at. always <= cls.realtime + float lerpfrac; // between oldframe and frame + + refdef_t refdef = new refdef_t(); + + float[] v_forward = { 0, 0, 0 }; + float[] v_right = { 0, 0, 0 }; + float[] v_up = { 0, 0, 0 }; // set when refdef.angles is set + + // + // transient data from server + // + + String layout = ""; // general 2D overlay + int inventory[] = new int[Defines.MAX_ITEMS]; + + // + // non-gameserver infornamtion + // FIXME: move this cinematic stuff into the cin_t structure + RandomAccessFile cinematic_file; + + int cinematictime; // cls.realtime for first cinematic frame + int cinematicframe; + byte cinematicpalette[] = new byte[768]; + boolean cinematicpalette_active; + + // + // server state information + // + boolean attractloop; // running the attract loop, any key will menu + int servercount; // server identification for prespawns + String gamedir =""; + int playernum; + + String configstrings[] = new String[Defines.MAX_CONFIGSTRINGS]; + + // + // locally derived information from server state + // + model_t model_draw[] = new model_t[Defines.MAX_MODELS]; + cmodel_t model_clip[] = new cmodel_t[Defines.MAX_MODELS]; + + sfx_t sound_precache[] = new sfx_t[Defines.MAX_SOUNDS]; + image_t image_precache[] = new image_t[Defines.MAX_IMAGES]; + + clientinfo_t clientinfo[] = new clientinfo_t[Defines.MAX_CLIENTS]; + clientinfo_t baseclientinfo = new clientinfo_t(); + +} diff --git a/src/jake2/client/client_static_t.java b/src/jake2/client/client_static_t.java new file mode 100644 index 0000000..c89a81d --- /dev/null +++ b/src/jake2/client/client_static_t.java @@ -0,0 +1,72 @@ +/* +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. + +*/ + +// Created on 27.11.2003 by RST. +// $Id: client_static_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + + +package jake2.client; + +import jake2.qcommon.netchan_t; +import java.io.*; + +public class client_static_t { + + // was enum connstate_t + public int state; + + // was enum keydest_t + public int key_dest; + + public int framecount; + public int realtime; // always increasing, no clamping, etc + public float frametime; // seconds since last frame + + // screen rendering information + public float disable_screen; // showing loading plaque between levels + // or changing rendering dlls + // if time gets > 30 seconds ahead, break it + public int disable_servercount; // when we receive a frame and cl.servercount + // > cls.disable_servercount, clear disable_screen + + // connection information + public String servername = ""; // name of server from original connect + public float connect_time; // for connection retransmits + + int quakePort; // a 16 bit value that allows quake servers + // to work around address translating routers + public netchan_t netchan = new netchan_t(); + public int serverProtocol; // in case we are doing some kind of version hack + + public int challenge; // from the server to use for connecting + + public RandomAccessFile download; // file transfer from server + public String downloadtempname=""; + public String downloadname=""; + public int downloadnumber; + // was enum dltype_t + public int downloadtype; + public int downloadpercent; + + // demo recording info must be here, so it isn't cleared on level change + public boolean demorecording; + public boolean demowaiting; // don't record until a non-delta message is received + public RandomAccessFile demofile; +} diff --git a/src/jake2/client/clientinfo_t.java b/src/jake2/client/clientinfo_t.java new file mode 100644 index 0000000..4276b08 --- /dev/null +++ b/src/jake2/client/clientinfo_t.java @@ -0,0 +1,53 @@ +/* +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. + +*/ + +// Created on 28.11.2003 by RST. +//$Id: clientinfo_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +import jake2.*; +import jake2.render.*; + +public class clientinfo_t { + String name =""; + String cinfo =""; + image_t skin; // ptr + image_t icon; // ptr + String iconname =""; + model_t model; // ptr + model_t weaponmodel[] = new model_t[Defines.MAX_CLIENTWEAPONMODELS]; // arary of references + +// public void reset() +// { +// set(new clientinfo_t()); +// } + + public void set (clientinfo_t from) + { + name = from.name; + cinfo = from.cinfo; + skin = from.skin; + icon = from.icon; + iconname = from.iconname; + model = from.model; + System.arraycopy(from.weaponmodel,0, weaponmodel, 0 , Defines.MAX_CLIENTWEAPONMODELS); + } +} diff --git a/src/jake2/client/console_t.java b/src/jake2/client/console_t.java new file mode 100644 index 0000000..eff5d2f --- /dev/null +++ b/src/jake2/client/console_t.java @@ -0,0 +1,51 @@ +/* + * console_t.java + * Copyright (C) 2003 + * + * $Id: console_t.java,v 1.1 2004-07-07 19:58:52 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.Defines; + +/** + * console_t + */ +public final class console_t { + boolean initialized; + byte[] text = new byte[Defines.CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int ormask; // high bit mask for colored characters + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float cursorspeed; + + int vislines; + + float[] times = new float[Defines.NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines +} diff --git a/src/jake2/client/cparticle_t.java b/src/jake2/client/cparticle_t.java new file mode 100644 index 0000000..a208077 --- /dev/null +++ b/src/jake2/client/cparticle_t.java @@ -0,0 +1,46 @@ +/* + * cparticle_t.java + * Copyright (C) 2003 + * + * $Id: cparticle_t.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * cparticle_t + * + * @author cwei + */ +public class cparticle_t { + + public cparticle_t next; + public float time; + + public float[] org = {0, 0, 0}; // vec3_t + public float[] vel = {0, 0, 0}; // vec3_t + public float[] accel = {0, 0, 0}; // vec3_t + + public float color; + //public float colorvel; + public float alpha; + public float alphavel; +} diff --git a/src/jake2/client/dlight_t.java b/src/jake2/client/dlight_t.java new file mode 100644 index 0000000..cefb1cb --- /dev/null +++ b/src/jake2/client/dlight_t.java @@ -0,0 +1,31 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: dlight_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +public class dlight_t +{ + public float origin[] = { 0, 0, 0 }; + public float color[] = { 0, 0, 0 }; + public float intensity; +} diff --git a/src/jake2/client/entity_t.java b/src/jake2/client/entity_t.java new file mode 100644 index 0000000..3547e86 --- /dev/null +++ b/src/jake2/client/entity_t.java @@ -0,0 +1,76 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: entity_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +import jake2.render.*; +import jake2.util.Math3D; + +// ok! +public class entity_t implements Cloneable{ + //ptr + public model_t model; // opaque type outside refresh + public float angles[] = { 0, 0, 0 }; + + /* + ** most recent data + */ + public float origin[] = { 0, 0, 0 }; // also used as RF_BEAM's "from" + public int frame; // also used as RF_BEAM's diameter + + /* + ** previous data for lerping + */ + public float oldorigin[] = { 0, 0, 0 }; // also used as RF_BEAM's "to" + public int oldframe; + + /* + ** misc + */ + public float backlerp; // 0.0 = current, 1.0 = old + public int skinnum; // also used as RF_BEAM's palette index + + public int lightstyle; // for flashing entities + public float alpha; // ignore if RF_TRANSLUCENT isn't set + + // reference + public image_t skin; // NULL for inline skin + public int flags; + + + public void set(entity_t src) { + this.model = src.model; + Math3D.VectorCopy(src.angles, this.angles); + Math3D.VectorCopy(src.origin, this.origin); + this.frame = src.frame; + Math3D.VectorCopy(src.oldorigin, this.oldorigin); + this.oldframe = src.oldframe; + this.backlerp = src.backlerp; + this.skinnum = src.skinnum; + this.lightstyle = src.lightstyle; + this.alpha = src.alpha; + this.skin = src.skin; + this.flags = src.flags; + } + +} diff --git a/src/jake2/client/frame_t.java b/src/jake2/client/frame_t.java new file mode 100644 index 0000000..d493363 --- /dev/null +++ b/src/jake2/client/frame_t.java @@ -0,0 +1,61 @@ +/* +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. + +*/ + +// Created on 27.11.2003 by RST. +// $Id: frame_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +import jake2.game.player_state_t; + +import java.util.Arrays; + +public class frame_t implements Cloneable { + + public static final int MAX_MAP_AREAS = 256; + + boolean valid; // cleared if delta parsing was invalid + int serverframe; + int servertime; // server time the message is valid for (in msec) + int deltaframe; + byte areabits[] = new byte [MAX_MAP_AREAS/8]; // portalarea visibility bits + public player_state_t playerstate = new player_state_t(); // mem + int num_entities; + int parse_entities; // non-masked index into cl_parse_entities array + + public void set(frame_t from) { + valid = from.valid; + serverframe = from.serverframe; + deltaframe = from.deltaframe; + num_entities = from.num_entities; + parse_entities = from.parse_entities; + System.arraycopy(from.areabits, 0, areabits, 0, areabits.length); + playerstate.set(from.playerstate); + } + + public void reset() + { + valid = false; + serverframe = servertime = deltaframe = 0; + Arrays.fill(areabits, (byte)0); + playerstate.clear(); + num_entities = parse_entities = 0; + } +} diff --git a/src/jake2/client/kbutton_t.java b/src/jake2/client/kbutton_t.java new file mode 100644 index 0000000..d192bc2 --- /dev/null +++ b/src/jake2/client/kbutton_t.java @@ -0,0 +1,36 @@ +/* + * kbutton_t.java + * Copyright (C) 2004 + * + * $Id: kbutton_t.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * kbutton_t + */ +public class kbutton_t { + int[] down = new int[2]; // key nums holding it down + long downtime; // msec timestamp + long msec; // msec down this frame + public int state; +} diff --git a/src/jake2/client/lightstyle_t.java b/src/jake2/client/lightstyle_t.java new file mode 100644 index 0000000..328e3c1 --- /dev/null +++ b/src/jake2/client/lightstyle_t.java @@ -0,0 +1,30 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: lightstyle_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +public class lightstyle_t +{ + public float rgb[] = { 0, 0, 0 }; // 0.0 - 2.0 + public float white; // highest of rgb +} diff --git a/src/jake2/client/particle_t.java b/src/jake2/client/particle_t.java new file mode 100644 index 0000000..2127b5d --- /dev/null +++ b/src/jake2/client/particle_t.java @@ -0,0 +1,30 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: particle_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +public class particle_t { + public float origin[] = { 0, 0, 0 }; + public int color; + public float alpha; +} diff --git a/src/jake2/client/refdef_t.java b/src/jake2/client/refdef_t.java new file mode 100644 index 0000000..a01ab06 --- /dev/null +++ b/src/jake2/client/refdef_t.java @@ -0,0 +1,47 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: refdef_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +public class refdef_t { + public int x, y, width, height;// in virtual screen coordinates + public float fov_x, fov_y; + public float vieworg[] ={0,0,0}; + public float viewangles[]={0,0,0}; + public float blend[]={0,0,0,0}; // rgba 0-1 full screen blend + public float time; // time is uesed to auto animate + public int rdflags; // RDF_UNDERWATER, etc + + public byte areabits[]; // if not NULL, only areas with set bits will be drawn + + public lightstyle_t lightstyles[]; // [MAX_LIGHTSTYLES] + + public int num_entities; + public entity_t entities[]; + + public int num_dlights; + public dlight_t dlights[]; + + public int num_particles; + public particle_t particles[]; +} diff --git a/src/jake2/client/refexport_t.java b/src/jake2/client/refexport_t.java new file mode 100644 index 0000000..d1872c9 --- /dev/null +++ b/src/jake2/client/refexport_t.java @@ -0,0 +1,107 @@ +/* + * refexport_t.java + * Copyright (C) 2003 + * + * $Id: refexport_t.java,v 1.1 2004-07-07 19:58:52 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.qcommon.xcommand_t; +import jake2.render.image_t; +import jake2.render.model_t; + +import java.awt.Dimension; + +/** + * refexport_t + * + * @author cwei + */ +public interface refexport_t { + // ============================================================================ + // public interface for Renderer implementations + // + // ref.h, refexport_t + // ============================================================================ + // + // these are the functions exported by the refresh module + // + // called when the library is loaded + boolean Init(int vid_xpos, int vid_ypos); + + // called before the library is unloaded + void Shutdown(); + + // All data that will be used in a level should be + // registered before rendering any frames to prevent disk hits, + // but they can still be registered at a later time + // if necessary. + // + // EndRegistration will free any remaining data that wasn't registered. + // Any model_s or skin_s pointers from before the BeginRegistration + // are no longer valid after EndRegistration. + // + // Skins and images need to be differentiated, because skins + // are flood filled to eliminate mip map edge errors, and pics have + // an implicit "pics/" prepended to the name. (a pic name that starts with a + // slash will not use the "pics/" prefix or the ".pcx" postfix) + void BeginRegistration(String map); + model_t RegisterModel(String name); + image_t RegisterSkin(String name); + image_t RegisterPic(String name); + void SetSky(String name, float rotate, /* vec3_t */ + float[] axis); + void EndRegistration(); + + void RenderFrame(refdef_t fd); + + void DrawGetPicSize(Dimension dim /* int *w, *h */, String name); + // will return 0 0 if not found + void DrawPic(int x, int y, String name); + void DrawStretchPic(int x, int y, int w, int h, String name); + void DrawChar(int x, int y, int num); // num is 8 bit ASCII + void DrawTileClear(int x, int y, int w, int h, String name); + void DrawFill(int x, int y, int w, int h, int c); + void DrawFadeScreen(); + + // Draw images for cinematic rendering (which can have a different palette). Note that calls + void DrawStretchRaw(int x, int y, int w, int h, int cols, int rows, byte[] data); + + /* + ** video mode and refresh state management entry points + */ + /* 256 r,g,b values; null = game palette, size = 768 bytes */ + void CinematicSetPalette(final byte[] palette); + void BeginFrame(float camera_separation); + void EndFrame(); + + void AppActivate(boolean activate); + + /** + * + * + */ + void updateScreen(xcommand_t callback); + + int apiVersion(); +} diff --git a/src/jake2/client/refimport_t.java b/src/jake2/client/refimport_t.java new file mode 100644 index 0000000..f465482 --- /dev/null +++ b/src/jake2/client/refimport_t.java @@ -0,0 +1,76 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: refimport_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + + +package jake2.client; + +import java.awt.Dimension; + +import jake2.game.*; +import jake2.qcommon.xcommand_t; +import jake2.util.Vargs; + +public interface refimport_t { + // ref.h + // these are the functions imported by the refresh module + // + void Sys_Error(int err_level, String str); + void Sys_Error(int err_level, String str, Vargs vargs); + + void Cmd_AddCommand(String name, xcommand_t cmd); + void Cmd_RemoveCommand(String name); + int Cmd_Argc(); + String Cmd_Argv(int i); + void Cmd_ExecuteText(int exec_when, String text); + + void Con_Printf(int print_level, String str, Vargs vargs); + + void Con_Printf(int print_level, String str); + + // files will be memory mapped read only + // the returned buffer may be part of a larger pak file, + // or a discrete file from anywhere in the quake search path + // a -1 return means the file does not exist + // NULL can be passed for buf to just determine existance + byte[] FS_LoadFile(String name); + int FS_FileLength(String name); + + void FS_FreeFile(byte[] buf); + // gamedir will be the current directory that generated + // files should be stored to, ie: "f:\quake\id1" + String FS_Gamedir(); + + cvar_t Cvar_Get(String name, String value, int flags); + cvar_t Cvar_Set(String name, String value); + void Cvar_SetValue(String name, float value); + + boolean Vid_GetModeInfo(Dimension dim /* int *w, *h */, int mode); + void Vid_MenuInit(); + void Vid_NewWindow(int width, int height); + + /** + * This is the callback for Renderer. + */ + void updateScreenCallback(); + +}
\ No newline at end of file diff --git a/src/jake2/client/sfx_t.java b/src/jake2/client/sfx_t.java new file mode 100644 index 0000000..97431ab --- /dev/null +++ b/src/jake2/client/sfx_t.java @@ -0,0 +1,41 @@ +/* + * sfx_t.java + * Copyright (C) 2003 + * + * $Id: sfx_t.java,v 1.1 2004-07-07 19:58:52 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. + +*/ + +// Created on 28.11.2003 by RST. + +package jake2.client; + +public class sfx_t { + String name =""; //mem + int registration_sequence; + sfxcache_t cache; //ptr + String truename; //ptr + public void clear() { + name = truename = null; + cache = null; + registration_sequence = 0; + } +} diff --git a/src/jake2/client/sfxcache_t.java b/src/jake2/client/sfxcache_t.java new file mode 100644 index 0000000..69fbcde --- /dev/null +++ b/src/jake2/client/sfxcache_t.java @@ -0,0 +1,34 @@ +/* +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. + +*/ + +// Created on 28.11.2003 by RST. +// $Id: sfxcache_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +public class sfxcache_t { + int length; + int loopstart; + int speed; // not needed, because converted on load? + int width; + int stereo; + byte data[] = new byte[1]; // variable sized + +} diff --git a/src/jake2/client/viddef_t.java b/src/jake2/client/viddef_t.java new file mode 100644 index 0000000..56c92aa --- /dev/null +++ b/src/jake2/client/viddef_t.java @@ -0,0 +1,28 @@ +/* +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. + +*/ + +// Created on 20.11.2003 by RST. +// $Id: viddef_t.java,v 1.1 2004-07-07 19:58:52 hzi Exp $ + +package jake2.client; + +public class viddef_t { + public int width, height; +} diff --git a/src/jake2/client/vidmode_t.java b/src/jake2/client/vidmode_t.java new file mode 100644 index 0000000..69cf055 --- /dev/null +++ b/src/jake2/client/vidmode_t.java @@ -0,0 +1,44 @@ +/* + * vidmode_t.java + * Copyright (C) 2003 + * + * $Id: vidmode_t.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * vidmode_t + * + * @author cwei + */ +public class vidmode_t { + String description; + int width, height; + int mode; + + vidmode_t (String description, int width, int height, int mode) { + this.description = description; + this.width = width; + this.height = height; + this.mode = mode; + } +} diff --git a/src/jake2/client/vrect_t.java b/src/jake2/client/vrect_t.java new file mode 100644 index 0000000..03d15d6 --- /dev/null +++ b/src/jake2/client/vrect_t.java @@ -0,0 +1,39 @@ +/* + * vrect_t.java + * Copyright (C) 2003 + * + * $Id: vrect_t.java,v 1.1 2004-07-07 19:58:52 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; + +/** + * vrect_t + * + * @author cwei + */ +public class vrect_t { + public int x; + public int y; + public int width; + public int height; + vrect_t pnext; +} |