aboutsummaryrefslogtreecommitdiffstats
path: root/src/jake2/game/PlayerClient.java
diff options
context:
space:
mode:
authorRene Stoeckel <[email protected]>2004-09-22 19:22:16 +0000
committerRene Stoeckel <[email protected]>2004-09-22 19:22:16 +0000
commitc4fcffe436fbfb5b0f3b7be2e5ee103ec74932f7 (patch)
tree7c9439ab1d9f5a4fd61bd57c755069007b23e0b6 /src/jake2/game/PlayerClient.java
parentbcb4ac6eefb425d5b0a90009da506361d5739e75 (diff)
major refactoring in game, server and client package
Diffstat (limited to 'src/jake2/game/PlayerClient.java')
-rw-r--r--src/jake2/game/PlayerClient.java2869
1 files changed, 1434 insertions, 1435 deletions
diff --git a/src/jake2/game/PlayerClient.java b/src/jake2/game/PlayerClient.java
index 980f0cb..852ccfd 100644
--- a/src/jake2/game/PlayerClient.java
+++ b/src/jake2/game/PlayerClient.java
@@ -1,1444 +1,1443 @@
/*
-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.
-
-*/
+ * 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.12.2003 by RST.
-// $Id: PlayerClient.java,v 1.5 2004-09-04 09:01:01 salomo Exp $
-
+// $Id: PlayerClient.java,v 1.6 2004-09-22 19:22:06 salomo Exp $
package jake2.game;
import jake2.Defines;
import jake2.util.Lib;
import jake2.util.Math3D;
-public class PlayerClient extends PlayerHud {
-
- /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
- The normal starting point for a level.
- */
- public static void SP_info_player_start(edict_t self) {
- if (coop.value == 0)
- return;
- if (Q_stricmp(level.mapname, "security") == 0) {
- // invoke one of our gross, ugly, disgusting hacks
- self.think = PlayerClientAdapters.SP_CreateCoopSpots;
- self.nextthink = level.time + FRAMETIME;
- }
- }
-
- /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
- potential spawning position for deathmatch games
- */
- public static void SP_info_player_deathmatch(edict_t self) {
- if (0 == deathmatch.value) {
- G_FreeEdict(self);
- return;
- }
- GameMiscAdapters.SP_misc_teleporter_dest.think(self);
- }
-
- /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32)
- potential spawning position for coop games
- */
-
- public static void SP_info_player_coop(edict_t self) {
- if (0 == coop.value) {
- G_FreeEdict(self);
- return;
- }
-
- if ((Q_stricmp(level.mapname, "jail2") == 0)
- || (Q_stricmp(level.mapname, "jail4") == 0)
- || (Q_stricmp(level.mapname, "mine1") == 0)
- || (Q_stricmp(level.mapname, "mine2") == 0)
- || (Q_stricmp(level.mapname, "mine3") == 0)
- || (Q_stricmp(level.mapname, "mine4") == 0)
- || (Q_stricmp(level.mapname, "lab") == 0)
- || (Q_stricmp(level.mapname, "boss1") == 0)
- || (Q_stricmp(level.mapname, "fact3") == 0)
- || (Q_stricmp(level.mapname, "biggun") == 0)
- || (Q_stricmp(level.mapname, "space") == 0)
- || (Q_stricmp(level.mapname, "command") == 0)
- || (Q_stricmp(level.mapname, "power2") == 0)
- || (Q_stricmp(level.mapname, "strike") == 0)) {
- // invoke one of our gross, ugly, disgusting hacks
- self.think = PlayerClientAdapters.SP_FixCoopSpots;
- self.nextthink = level.time + FRAMETIME;
- }
- }
-
- /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
- The deathmatch intermission point will be at one of these
- Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll'
- */
- public static void SP_info_player_intermission() {
- }
-
- public static void ClientObituary(edict_t self, edict_t inflictor, edict_t attacker) {
- int mod;
- String message;
- String message2;
- boolean ff;
-
- if (coop.value != 0 && attacker.client != null)
- meansOfDeath |= MOD_FRIENDLY_FIRE;
-
- if (deathmatch.value != 0 || coop.value != 0) {
- ff = (meansOfDeath & MOD_FRIENDLY_FIRE) != 0;
- mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
- message = null;
- message2 = "";
-
- switch (mod) {
- case MOD_SUICIDE :
- message = "suicides";
- break;
- case MOD_FALLING :
- message = "cratered";
- break;
- case MOD_CRUSH :
- message = "was squished";
- break;
- case MOD_WATER :
- message = "sank like a rock";
- break;
- case MOD_SLIME :
- message = "melted";
- break;
- case MOD_LAVA :
- message = "does a back flip into the lava";
- break;
- case MOD_EXPLOSIVE :
- case MOD_BARREL :
- message = "blew up";
- break;
- case MOD_EXIT :
- message = "found a way out";
- break;
- case MOD_TARGET_LASER :
- message = "saw the light";
- break;
- case MOD_TARGET_BLASTER :
- message = "got blasted";
- break;
- case MOD_BOMB :
- case MOD_SPLASH :
- case MOD_TRIGGER_HURT :
- message = "was in the wrong place";
- break;
- }
- if (attacker == self) {
- switch (mod) {
- case MOD_HELD_GRENADE :
- message = "tried to put the pin back in";
- break;
- case MOD_HG_SPLASH :
- case MOD_G_SPLASH :
- if (IsNeutral(self))
- message = "tripped on its own grenade";
- else if (IsFemale(self))
- message = "tripped on her own grenade";
- else
- message = "tripped on his own grenade";
- break;
- case MOD_R_SPLASH :
- if (IsNeutral(self))
- message = "blew itself up";
- else if (IsFemale(self))
- message = "blew herself up";
- else
- message = "blew himself up";
- break;
- case MOD_BFG_BLAST :
- message = "should have used a smaller gun";
- break;
- default :
- if (IsNeutral(self))
- message = "killed itself";
- else if (IsFemale(self))
- message = "killed herself";
- else
- message = "killed himself";
- break;
- }
- }
- if (message != null) {
- gi.bprintf(PRINT_MEDIUM, self.client.pers.netname + " " + message + ".\n");
- if (deathmatch.value != 0)
- self.client.resp.score--;
- self.enemy = null;
- return;
- }
-
- self.enemy = attacker;
- if (attacker != null && attacker.client != null) {
- switch (mod) {
- case MOD_BLASTER :
- message = "was blasted by";
- break;
- case MOD_SHOTGUN :
- message = "was gunned down by";
- break;
- case MOD_SSHOTGUN :
- message = "was blown away by";
- message2 = "'s super shotgun";
- break;
- case MOD_MACHINEGUN :
- message = "was machinegunned by";
- break;
- case MOD_CHAINGUN :
- message = "was cut in half by";
- message2 = "'s chaingun";
- break;
- case MOD_GRENADE :
- message = "was popped by";
- message2 = "'s grenade";
- break;
- case MOD_G_SPLASH :
- message = "was shredded by";
- message2 = "'s shrapnel";
- break;
- case MOD_ROCKET :
- message = "ate";
- message2 = "'s rocket";
- break;
- case MOD_R_SPLASH :
- message = "almost dodged";
- message2 = "'s rocket";
- break;
- case MOD_HYPERBLASTER :
- message = "was melted by";
- message2 = "'s hyperblaster";
- break;
- case MOD_RAILGUN :
- message = "was railed by";
- break;
- case MOD_BFG_LASER :
- message = "saw the pretty lights from";
- message2 = "'s BFG";
- break;
- case MOD_BFG_BLAST :
- message = "was disintegrated by";
- message2 = "'s BFG blast";
- break;
- case MOD_BFG_EFFECT :
- message = "couldn't hide from";
- message2 = "'s BFG";
- break;
- case MOD_HANDGRENADE :
- message = "caught";
- message2 = "'s handgrenade";
- break;
- case MOD_HG_SPLASH :
- message = "didn't see";
- message2 = "'s handgrenade";
- break;
- case MOD_HELD_GRENADE :
- message = "feels";
- message2 = "'s pain";
- break;
- case MOD_TELEFRAG :
- message = "tried to invade";
- message2 = "'s personal space";
- break;
- }
- if (message != null) {
- gi.bprintf(
- PRINT_MEDIUM,
- self.client.pers.netname
- + " "
- + message
- + " "
- + attacker.client.pers.netname
- + " "
- + message2
- + "\n");
- if (deathmatch.value != 0) {
- if (ff)
- attacker.client.resp.score--;
- else
- attacker.client.resp.score++;
- }
- return;
- }
- }
- }
-
- gi.bprintf(PRINT_MEDIUM, self.client.pers.netname + " died.\n");
- if (deathmatch.value != 0)
- self.client.resp.score--;
- }
-
- /*
- ==================
- player_die
- ==================
- */
-
- //=======================================================================
-
- /*
- ==============
- InitClientPersistant
-
- This is only called when the game first initializes in single player,
- but is called after each death and level change in deathmatch
- ==============
- */
- public static void InitClientPersistant(gclient_t client) {
- gitem_t item;
-
- client.pers = new client_persistant_t();
-
- item = FindItem("Blaster");
- client.pers.selected_item = ITEM_INDEX(item);
- client.pers.inventory[client.pers.selected_item] = 1;
-
- /* Give shotgun.
- item = FindItem("Shotgun");
- client.pers.selected_item = ITEM_INDEX(item);
- client.pers.inventory[client.pers.selected_item] = 1;
- */
-
- client.pers.weapon = item;
-
- client.pers.health = 100;
- client.pers.max_health = 100;
-
- client.pers.max_bullets = 200;
- client.pers.max_shells = 100;
- client.pers.max_rockets = 50;
- client.pers.max_grenades = 50;
- client.pers.max_cells = 200;
- client.pers.max_slugs = 50;
-
- client.pers.connected = true;
- }
-
- public static void InitClientResp(gclient_t client) {
- //memset(& client.resp, 0, sizeof(client.resp));
- client.resp.clear(); // ok.
- client.resp.enterframe = level.framenum;
- client.resp.coop_respawn.set(client.pers);
- }
-
- /*
- ==================
- SaveClientData
-
- Some information that should be persistant, like health,
- is still stored in the edict structure, so it needs to
- be mirrored out to the client structure before all the
- edicts are wiped.
- ==================
- */
- public static void SaveClientData() {
- int i;
- edict_t ent;
-
- for (i = 0; i < game.maxclients; i++) {
- ent = g_edicts[1 + i];
- if (!ent.inuse)
- continue;
-
- game.clients[i].pers.health = ent.health;
- game.clients[i].pers.max_health = ent.max_health;
- game.clients[i].pers.savedFlags = (ent.flags & (FL_GODMODE | FL_NOTARGET | FL_POWER_ARMOR));
-
- if (coop.value != 0)
- game.clients[i].pers.score = ent.client.resp.score;
- }
- }
-
- public static void FetchClientEntData(edict_t ent) {
- ent.health = ent.client.pers.health;
- ent.max_health = ent.client.pers.max_health;
- ent.flags |= ent.client.pers.savedFlags;
- if (coop.value != 0)
- ent.client.resp.score = ent.client.pers.score;
- }
-
-
- /*
- ================
- PlayersRangeFromSpot
-
- Returns the distance to the nearest player from the given spot
- ================
- */
- static float PlayersRangeFromSpot(edict_t spot) {
- edict_t player;
- float bestplayerdistance;
- float[] v = { 0, 0, 0 };
- int n;
- float playerdistance;
-
- bestplayerdistance = 9999999;
-
- for (n = 1; n <= maxclients.value; n++) {
- player = g_edicts[n];
-
- if (!player.inuse)
- continue;
-
- if (player.health <= 0)
- continue;
-
- VectorSubtract(spot.s.origin, player.s.origin, v);
- playerdistance = VectorLength(v);
-
- if (playerdistance < bestplayerdistance)
- bestplayerdistance = playerdistance;
- }
-
- return bestplayerdistance;
- }
-
- /*
- ================
- SelectRandomDeathmatchSpawnPoint
-
- go to a random point, but NOT the two points closest
- to other players
- ================
- */
- public static edict_t SelectRandomDeathmatchSpawnPoint() {
- edict_t spot, spot1, spot2;
- int count = 0;
- int selection;
- float range, range1, range2;
-
- spot = null;
- range1 = range2 = 99999;
- spot1 = spot2 = null;
-
- EdictIterator es = null;
-
- while ((es = G_Find(es, findByClass, "info_player_deathmatch")) != null) {
- spot = es.o;
- count++;
- range = PlayersRangeFromSpot(spot);
- if (range < range1) {
- range1 = range;
- spot1 = spot;
- }
- else if (range < range2) {
- range2 = range;
- spot2 = spot;
- }
- }
-
- if (count == 0)
- return null;
-
- if (count <= 2) {
- spot1 = spot2 = null;
- }
- else
- count -= 2;
-
- selection = rand() % count;
-
- spot = null;
- es = null;
- do {
- es = G_Find(es, findByClass, "info_player_deathmatch");
- spot = es.o;
- if (spot == spot1 || spot == spot2)
- selection++;
- }
- while (selection-- > 0);
-
- return spot;
- }
-
- /*
- ================
- SelectFarthestDeathmatchSpawnPoint
-
- ================
- */
- static edict_t SelectFarthestDeathmatchSpawnPoint() {
- edict_t bestspot;
- float bestdistance, bestplayerdistance;
- edict_t spot;
-
- spot = null;
- bestspot = null;
- bestdistance = 0;
-
- EdictIterator es = null;
- while ((es = G_Find(es, findByClass, "info_player_deathmatch")).o != null) {
- spot = es.o;
- bestplayerdistance = PlayersRangeFromSpot(spot);
-
- if (bestplayerdistance > bestdistance) {
- bestspot = spot;
- bestdistance = bestplayerdistance;
- }
- }
-
- if (bestspot != null) {
- return bestspot;
- }
-
- // if there is a player just spawned on each and every start spot
- // we have no choice to turn one into a telefrag meltdown
- spot = G_Find(null, findByClass, "info_player_deathmatch").o;
-
- return spot;
- }
-
- public static edict_t SelectDeathmatchSpawnPoint() {
- if (0 != ((int) (dmflags.value) & DF_SPAWN_FARTHEST))
- return SelectFarthestDeathmatchSpawnPoint();
- else
- return SelectRandomDeathmatchSpawnPoint();
- }
-
- public static edict_t SelectCoopSpawnPoint(edict_t ent) {
- int index;
- edict_t spot = null;
- String target;
-
- //index = ent.client - game.clients;
- index = ent.client.index;
-
- // player 0 starts in normal player spawn point
- if (index == 0)
- return null;
-
- spot = null;
- EdictIterator es = null;
-
- // assume there are four coop spots at each spawnpoint
- while (true) {
-
- spot = (es = G_Find(es, findByClass, "info_player_coop")).o;
- if (spot == null)
- return null; // we didn't have enough...
-
- target = spot.targetname;
- if (target == null)
- target = "";
- if (Q_stricmp(game.spawnpoint, target) == 0) { // this is a coop spawn point for one of the clients here
- index--;
- if (0 == index)
- return spot; // this is it
- }
- }
-
- }
-
- /*
- ===========
- SelectSpawnPoint
-
- Chooses a player start, deathmatch start, coop start, etc
- ============
- */
- public static void SelectSpawnPoint(edict_t ent, float[] origin, float[] angles) {
- edict_t spot = null;
-
- if (deathmatch.value != 0)
- spot = SelectDeathmatchSpawnPoint();
- else if (coop.value != 0)
- spot = SelectCoopSpawnPoint(ent);
-
- EdictIterator es = null;
- // find a single player start spot
- if (null == spot) {
- while ((es = G_Find(es, findByClass, "info_player_start")) != null) {
- spot = es.o;
-
- if (game.spawnpoint.length() == 0 && spot.targetname == null)
- break;
-
- if (game.spawnpoint.length() == 0 || spot.targetname == null)
- continue;
-
- if (Q_stricmp(game.spawnpoint, spot.targetname) == 0)
- break;
- }
-
- if (null == spot) {
- if (game.spawnpoint.length() == 0) { // there wasn't a spawnpoint without a target, so use any
- spot = (es = G_Find(es, findByClass, "info_player_start")).o;
- }
- if (null == spot)
- gi.error("Couldn't find spawn point " + game.spawnpoint + "\n");
- }
- }
-
- VectorCopy(spot.s.origin, origin);
- origin[2] += 9;
- VectorCopy(spot.s.angles, angles);
- }
-
- //======================================================================
-
- public static void InitBodyQue() {
- int i;
- edict_t ent;
-
- level.body_que = 0;
- for (i = 0; i < BODY_QUEUE_SIZE; i++) {
- ent = G_Spawn();
- ent.classname = "bodyque";
- }
- }
-
- public static void CopyToBodyQue(edict_t ent) {
- edict_t body;
-
- // grab a body que and cycle to the next one
- int i = (int) maxclients.value + level.body_que + 1;
- body = g_edicts[i];
- level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
-
- // FIXME: send an effect on the removed body
-
- gi.unlinkentity(ent);
-
- gi.unlinkentity(body);
- body.s = ent.s.getClone();
-
- body.s.number = body.index;
-
- body.svflags = ent.svflags;
- VectorCopy(ent.mins, body.mins);
- VectorCopy(ent.maxs, body.maxs);
- VectorCopy(ent.absmin, body.absmin);
- VectorCopy(ent.absmax, body.absmax);
- VectorCopy(ent.size, body.size);
- body.solid = ent.solid;
- body.clipmask = ent.clipmask;
- body.owner = ent.owner;
- body.movetype = ent.movetype;
-
- body.die = PlayerClientAdapters.body_die;
- body.takedamage = DAMAGE_YES;
-
- gi.linkentity(body);
- }
-
- public static void respawn(edict_t self) {
- if (deathmatch.value != 0 || coop.value != 0) {
- // spectator's don't leave bodies
- if (self.movetype != MOVETYPE_NOCLIP)
- CopyToBodyQue(self);
- self.svflags &= ~SVF_NOCLIENT;
- PutClientInServer(self);
-
- // add a teleportation effect
- self.s.event = EV_PLAYER_TELEPORT;
-
- // hold in place briefly
- self.client.ps.pmove.pm_flags = PMF_TIME_TELEPORT;
- self.client.ps.pmove.pm_time = 14;
-
- self.client.respawn_time = level.time;
-
- return;
- }
-
- // restart the entire server
- gi.AddCommandString("menu_loadgame\n");
- }
-
- private static boolean passwdOK(String i1, String i2) {
- if (i1.length() != 0 && !i1.equals("none") && !i1.equals(i2))
- return false;
- return true;
- }
-
- /*
- * only called when pers.spectator changes
- * note that resp.spectator should be the opposite of pers.spectator here
- */
- public static void spectator_respawn(edict_t ent) {
- int i, numspec;
-
- // if the user wants to become a spectator, make sure he doesn't
- // exceed max_spectators
-
- if (ent.client.pers.spectator) {
- String value = Info.Info_ValueForKey(ent.client.pers.userinfo, "spectator");
-
- if (!passwdOK(spectator_password.string, value)) {
- gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n");
- ent.client.pers.spectator = false;
- gi.WriteByte(svc_stufftext);
- gi.WriteString("spectator 0\n");
- gi.unicast(ent, true);
- return;
- }
-
- // count spectators
- for (i = 1, numspec = 0; i <= maxclients.value; i++)
- if (g_edicts[i].inuse && g_edicts[i].client.pers.spectator)
- numspec++;
-
- if (numspec >= maxspectators.value) {
- gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full.");
- ent.client.pers.spectator = false;
- // reset his spectator var
- gi.WriteByte(svc_stufftext);
- gi.WriteString("spectator 0\n");
- gi.unicast(ent, true);
- return;
- }
- }
- else {
- // he was a spectator and wants to join the game
- // he must have the right password
- String value = Info.Info_ValueForKey(ent.client.pers.userinfo, "password");
- if (!passwdOK(spectator_password.string, value)) {
- gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n");
- ent.client.pers.spectator = true;
- gi.WriteByte(svc_stufftext);
- gi.WriteString("spectator 1\n");
- gi.unicast(ent, true);
- return;
- }
- }
-
- // clear client on respawn
- ent.client.resp.score = ent.client.pers.score = 0;
-
- ent.svflags &= ~SVF_NOCLIENT;
- PutClientInServer(ent);
-
- // add a teleportation effect
- if (!ent.client.pers.spectator) {
- // send effect
- gi.WriteByte(svc_muzzleflash);
- //gi.WriteShort(ent - g_edicts);
- gi.WriteShort(ent.index);
-
- gi.WriteByte(MZ_LOGIN);
- gi.multicast(ent.s.origin, MULTICAST_PVS);
-
- // hold in place briefly
- ent.client.ps.pmove.pm_flags = PMF_TIME_TELEPORT;
- ent.client.ps.pmove.pm_time = 14;
- }
-
- ent.client.respawn_time = level.time;
-
- if (ent.client.pers.spectator)
- gi.bprintf(PRINT_HIGH, ent.client.pers.netname + " has moved to the sidelines\n");
- else
- gi.bprintf(PRINT_HIGH, ent.client.pers.netname + " joined the game\n");
- }
-
- //==============================================================
-
- /*
- ===========
- PutClientInServer
-
- Called when a player connects to a server or respawns in
- a deathmatch.
- ============
- */
- public static void PutClientInServer(edict_t ent) {
- float[] mins = { -16, -16, -24 };
- float[] maxs = { 16, 16, 32 };
- int index;
- float[] spawn_origin = { 0, 0, 0 }, spawn_angles = { 0, 0, 0 };
- gclient_t client;
- int i;
- client_persistant_t saved = new client_persistant_t();
- client_respawn_t resp = new client_respawn_t();
-
- // find a spawn point
- // do it before setting health back up, so farthest
- // ranging doesn't count this client
- SelectSpawnPoint(ent, spawn_origin, spawn_angles);
-
- index = ent.index - 1;
- client = ent.client;
-
- // deathmatch wipes most client data every spawn
- if (deathmatch.value != 0) {
- String userinfo;
- //char userinfo[MAX_INFO_STRING];
-
- resp.set(client.resp);
- userinfo = client.pers.userinfo;
-
- //memcpy(userinfo, client.pers.userinfo, sizeof(userinfo));
- InitClientPersistant(client);
- userinfo = ClientUserinfoChanged(ent, userinfo);
- }
- else if (coop.value != 0) {
- // int n;
- //char userinfo[MAX_INFO_STRING];
- String userinfo;
-
- resp.set(client.resp);
- //memcpy(userinfo, client.pers.userinfo, sizeof(userinfo));
- userinfo = client.pers.userinfo;
- // this is kind of ugly, but it's how we want to handle keys in coop
- // for (n = 0; n < game.num_items; n++)
- // {
- // if (itemlist[n].flags & IT_KEY)
- // resp.coop_respawn.inventory[n] = client.pers.inventory[n];
- // }
- resp.coop_respawn.game_helpchanged = client.pers.game_helpchanged;
- resp.coop_respawn.helpchanged = client.pers.helpchanged;
- client.pers.set(resp.coop_respawn);
- userinfo = ClientUserinfoChanged(ent, userinfo);
- if (resp.score > client.pers.score)
- client.pers.score = resp.score;
- }
- else {
- //memset(& resp, 0, sizeof(resp));
- resp.clear();
- }
-
- // clear everything but the persistant data
- saved.set(client.pers);
- //memset(client, 0, sizeof(* client));
- client.clear();
- client.pers.set(saved);
- if (client.pers.health <= 0)
- InitClientPersistant(client);
-
- client.resp.set(resp);
-
- // copy some data from the client to the entity
- FetchClientEntData(ent);
-
- // clear entity values
- ent.groundentity = null;
- ent.client = game.clients[index];
- ent.takedamage = DAMAGE_AIM;
- ent.movetype = MOVETYPE_WALK;
- ent.viewheight = 22;
- ent.inuse = true;
- ent.classname = "player";
- ent.mass = 200;
- ent.solid = SOLID_BBOX;
- ent.deadflag = DEAD_NO;
- ent.air_finished = level.time + 12;
- ent.clipmask = MASK_PLAYERSOLID;
- ent.model = "players/male/tris.md2";
- ent.pain = PlayerClientAdapters.player_pain;
- ent.die = GameAIAdapters.player_die;
- ent.waterlevel = 0;
- ent.watertype = 0;
- ent.flags &= ~FL_NO_KNOCKBACK;
- ent.svflags &= ~SVF_DEADMONSTER;
-
- VectorCopy(mins, ent.mins);
- VectorCopy(maxs, ent.maxs);
- VectorClear(ent.velocity);
-
- // clear playerstate values
- ent.client.ps.clear();
- //memset(& ent.client.ps, 0, sizeof(client.ps));
-
- client.ps.pmove.origin[0] = (short) (spawn_origin[0] * 8);
- client.ps.pmove.origin[1] = (short) (spawn_origin[1] * 8);
- client.ps.pmove.origin[2] = (short) (spawn_origin[2] * 8);
-
- if (deathmatch.value != 0 && 0 != ((int) dmflags.value & DF_FIXED_FOV)) {
- client.ps.fov = 90;
- }
- else {
- client.ps.fov = atoi(Info.Info_ValueForKey(client.pers.userinfo, "fov"));
- if (client.ps.fov < 1)
- client.ps.fov = 90;
- else if (client.ps.fov > 160)
- client.ps.fov = 160;
- }
-
- client.ps.gunindex = gi.modelindex(client.pers.weapon.view_model);
-
- // clear entity state values
- ent.s.effects = 0;
- ent.s.modelindex = 255; // will use the skin specified model
- ent.s.modelindex2 = 255; // custom gun model
- // sknum is player num and weapon number
- // weapon number will be added in changeweapon
- ent.s.skinnum = ent.index - 1;
-
- ent.s.frame = 0;
- VectorCopy(spawn_origin, ent.s.origin);
- ent.s.origin[2] += 1; // make sure off ground
- VectorCopy(ent.s.origin, ent.s.old_origin);
-
- // set the delta angle
- for (i = 0; i < 3; i++) {
- client.ps.pmove.delta_angles[i] = (short) ANGLE2SHORT(spawn_angles[i] - client.resp.cmd_angles[i]);
- }
-
- ent.s.angles[PITCH] = 0;
- ent.s.angles[YAW] = spawn_angles[YAW];
- ent.s.angles[ROLL] = 0;
- VectorCopy(ent.s.angles, client.ps.viewangles);
- VectorCopy(ent.s.angles, client.v_angle);
-
- // spawn a spectator
- if (client.pers.spectator) {
- client.chase_target = null;
-
- client.resp.spectator = true;
-
- ent.movetype = MOVETYPE_NOCLIP;
- ent.solid = SOLID_NOT;
- ent.svflags |= SVF_NOCLIENT;
- ent.client.ps.gunindex = 0;
- gi.linkentity(ent);
- return;
- }
- else
- client.resp.spectator = false;
-
- if (!KillBox(ent)) { // could't spawn in?
- }
-
- gi.linkentity(ent);
-
- // force the current weapon up
- client.newweapon = client.pers.weapon;
- GamePWeapon.ChangeWeapon(ent);
- }
-
- /*
- =====================
- ClientBeginDeathmatch
-
- A client has just connected to the server in
- deathmatch mode, so clear everything out before starting them.
- =====================
- */
- public static void ClientBeginDeathmatch(edict_t ent) {
- G_InitEdict(ent, ent.index);
-
- InitClientResp(ent.client);
-
- // locate ent at a spawn point
- PutClientInServer(ent);
-
- if (level.intermissiontime != 0) {
- MoveClientToIntermission(ent);
- }
- else {
- // send effect
- gi.WriteByte(svc_muzzleflash);
- //gi.WriteShort(ent - g_edicts);
- gi.WriteShort(ent.index);
- gi.WriteByte(MZ_LOGIN);
- gi.multicast(ent.s.origin, MULTICAST_PVS);
- }
-
- gi.bprintf(PRINT_HIGH, ent.client.pers.netname + " entered the game\n");
-
- // make sure all view stuff is valid
- PlayerView.ClientEndServerFrame(ent);
- }
-
- /*
- ===========
- ClientBegin
-
- called when a client has finished connecting, and is ready
- to be placed into the game. This will happen every level load.
- ============
- */
- public static void ClientBegin(edict_t ent) {
- int i;
-
- //ent.client = game.clients + (ent - g_edicts - 1);
- ent.client = game.clients[ent.index - 1];
-
- if (deathmatch.value != 0) {
- ClientBeginDeathmatch(ent);
- return;
- }
-
- // if there is already a body waiting for us (a loadgame), just
- // take it, otherwise spawn one from scratch
- if (ent.inuse == true) {
- // the client has cleared the client side viewangles upon
- // connecting to the server, which is different than the
- // state when the game is saved, so we need to compensate
- // with deltaangles
- for (i = 0; i < 3; i++)
- ent.client.ps.pmove.delta_angles[i] = (short) ANGLE2SHORT(ent.client.ps.viewangles[i]);
- }
- else {
- // a spawn point will completely reinitialize the entity
- // except for the persistant data that was initialized at
- // ClientConnect() time
- G_InitEdict(ent, ent.index);
- ent.classname = "player";
- InitClientResp(ent.client);
- PutClientInServer(ent);
- }
-
- if (level.intermissiontime != 0) {
- MoveClientToIntermission(ent);
- }
- else {
- // send effect if in a multiplayer game
- if (game.maxclients > 1) {
- gi.WriteByte(svc_muzzleflash);
- //gi.WriteShort(ent - g_edicts);
- gi.WriteShort(ent.index);
- gi.WriteByte(MZ_LOGIN);
- gi.multicast(ent.s.origin, MULTICAST_PVS);
-
- gi.bprintf(PRINT_HIGH, ent.client.pers.netname + " entered the game\n");
- }
- }
-
- // make sure all view stuff is valid
- PlayerView.ClientEndServerFrame(ent);
- }
-
- /*
- ===========
- ClientUserInfoChanged
-
- called whenever the player updates a userinfo variable.
-
- The game can override any of the settings in place
- (forcing skins or names, etc) before copying it off.
- ============
- */
- public static String ClientUserinfoChanged(edict_t ent, String userinfo) {
- String s;
- int playernum;
-
- // check for malformed or illegal info strings
- if (!Info.Info_Validate(userinfo)) {
- //strcpy(userinfo, "\\name\\badinfo\\skin\\male/grunt");
- return "\\name\\badinfo\\skin\\male/grunt";
- }
-
- // set name
- s = Info.Info_ValueForKey(userinfo, "name");
-
- //strncpy(ent.client.pers.netname, s, sizeof(ent.client.pers.netname) - 1);
- ent.client.pers.netname = s;
-
- // set spectator
- s = Info.Info_ValueForKey(userinfo, "spectator");
- // spectators are only supported in deathmatch
- if (deathmatch.value != 0 && !s.equals("0"))
- ent.client.pers.spectator = true;
- else
- ent.client.pers.spectator = false;
-
- // set skin
- s = Info.Info_ValueForKey(userinfo, "skin");
-
- playernum = ent.index - 1;
-
- // combine name and skin into a configstring
- gi.configstring(CS_PLAYERSKINS + playernum, ent.client.pers.netname + "\\" + s);
-
- // fov
- if (deathmatch.value != 0 && 0 != ((int) dmflags.value & DF_FIXED_FOV)) {
- ent.client.ps.fov = 90;
- }
- else {
- ent.client.ps.fov = atoi(Info.Info_ValueForKey(userinfo, "fov"));
- if (ent.client.ps.fov < 1)
- ent.client.ps.fov = 90;
- else if (ent.client.ps.fov > 160)
- ent.client.ps.fov = 160;
- }
-
- // handedness
- s = Info.Info_ValueForKey(userinfo, "hand");
- if (s.length() > 0) {
- ent.client.pers.hand = atoi(s);
- }
-
- // save off the userinfo in case we want to check something later
- //strncpy(ent.client.pers.userinfo, userinfo, sizeof(ent.client.pers.userinfo) - 1);
- ent.client.pers.userinfo = userinfo;
-
- return userinfo;
- }
-
- /*
- ===========
- ClientConnect
-
- Called when a player begins connecting to the server.
- The game can refuse entrance to a client by returning false.
- If the client is allowed, the connection process will continue
- and eventually get to ClientBegin()
- Changing levels will NOT cause this to be called again, but
- loadgames will.
- ============
- */
- public static boolean ClientConnect(edict_t ent, String userinfo) {
- String value;
-
- // check to see if they are on the banned IP list
- value = Info.Info_ValueForKey(userinfo, "ip");
- if (GameSVCmds.SV_FilterPacket(value)) {
- userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg", "Banned.");
- return false;
- }
-
- // check for a spectator
- value = Info.Info_ValueForKey(userinfo, "spectator");
- if (deathmatch.value != 0 && value.length() != 0 && 0 != strcmp(value, "0")) {
- int i, numspec;
-
- if (!passwdOK(spectator_password.string, value)) {
- userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg", "Spectator password required or incorrect.");
- return false;
- }
-
- // count spectators
- for (i = numspec = 0; i < maxclients.value; i++)
- if (g_edicts[i + 1].inuse && g_edicts[i + 1].client.pers.spectator)
- numspec++;
-
- if (numspec >= maxspectators.value) {
- userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg", "Server spectator limit is full.");
- return false;
- }
- }
- else {
- // check for a password
- value = Info.Info_ValueForKey(userinfo, "password");
- if (!passwdOK(spectator_password.string, value)) {
- userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg", "Password required or incorrect.");
- return false;
- }
- }
-
- // they can connect
- ent.client = game.clients[ent.index - 1];
-
- // if there is already a body waiting for us (a loadgame), just
- // take it, otherwise spawn one from scratch
- if (ent.inuse == false) {
- // clear the respawning variables
- InitClientResp(ent.client);
- if (!game.autosaved || null == ent.client.pers.weapon)
- InitClientPersistant(ent.client);
- }
-
- userinfo = ClientUserinfoChanged(ent, userinfo);
-
- if (game.maxclients > 1)
- gi.dprintf(ent.client.pers.netname + " connected\n");
-
- ent.svflags = 0; // make sure we start with known default
- ent.client.pers.connected = true;
- return true;
- }
-
- /*
- ===========
- ClientDisconnect
-
- Called when a player drops from the server.
- Will not be called between levels.
- ============
- */
- public static void ClientDisconnect(edict_t ent) {
- int playernum;
-
- if (ent.client == null)
- return;
-
- gi.bprintf(PRINT_HIGH, ent.client.pers.netname + " disconnected\n");
-
- // send effect
- gi.WriteByte(svc_muzzleflash);
- gi.WriteShort(ent.index);
- gi.WriteByte(MZ_LOGOUT);
- gi.multicast(ent.s.origin, MULTICAST_PVS);
-
- gi.unlinkentity(ent);
- ent.s.modelindex = 0;
- ent.solid = SOLID_NOT;
- ent.inuse = false;
- ent.classname = "disconnected";
- ent.client.pers.connected = false;
-
- playernum = ent.index - 1;
- gi.configstring(CS_PLAYERSKINS + playernum, "");
- }
-
- /*
- static int CheckBlock(, int c) {
- int v, i;
- v = 0;
- for (i = 0; i < c; i++)
- v += ((byte *) b)[i];
- return v;
- }
-
- public static void PrintPmove(pmove_t * pm) {
- unsigned c1, c2;
-
- c1 = CheckBlock(& pm.s, sizeof(pm.s));
- c2 = CheckBlock(& pm.cmd, sizeof(pm.cmd));
- Com_Printf("sv %3i:%i %i\n", pm.cmd.impulse, c1, c2);
- }
- */
-
- /*
- ==============
- ClientThink
-
- This will be called once for each client frame, which will
- usually be a couple times for each server frame.
- ==============
- */
- public static void ClientThink(edict_t ent, usercmd_t ucmd) {
- gclient_t client;
- edict_t other;
- int i, j;
- pmove_t pm = null;
-
- level.current_entity = ent;
- client = ent.client;
-
- if (level.intermissiontime != 0) {
- client.ps.pmove.pm_type = PM_FREEZE;
- // can exit intermission after five seconds
- if (level.time > level.intermissiontime + 5.0f && 0 != (ucmd.buttons & BUTTON_ANY))
- level.exitintermission = true;
- return;
- }
-
- PlayerClientAdapters.pm_passent = ent;
-
- if (ent.client.chase_target != null) {
-
- client.resp.cmd_angles[0] = SHORT2ANGLE(ucmd.angles[0]);
- client.resp.cmd_angles[1] = SHORT2ANGLE(ucmd.angles[1]);
- client.resp.cmd_angles[2] = SHORT2ANGLE(ucmd.angles[2]);
-
- }
- else {
-
- // set up for pmove
- //memset(& pm, 0, sizeof(pm));
- pm = new pmove_t();
-
- if (ent.movetype == MOVETYPE_NOCLIP)
- client.ps.pmove.pm_type = PM_SPECTATOR;
- else if (ent.s.modelindex != 255)
- client.ps.pmove.pm_type = PM_GIB;
- else if (ent.deadflag != 0)
- client.ps.pmove.pm_type = PM_DEAD;
- else
- client.ps.pmove.pm_type = PM_NORMAL;
-
- client.ps.pmove.gravity = (short) sv_gravity.value;
- pm.s.set(client.ps.pmove);
-
- for (i = 0; i < 3; i++) {
- pm.s.origin[i] = (short) (ent.s.origin[i] * 8);
- pm.s.velocity[i] = (short) (ent.velocity[i] * 8);
- }
-
- if (client.old_pmove.equals(pm.s)) {
- pm.snapinitial = true;
- // gi.dprintf ("pmove changed!\n");
- }
-
- // this should be a copy
- pm.cmd.set(ucmd);
-
- pm.trace = PlayerClientAdapters.PM_trace; // adds default parms
- pm.pointcontents = gi.pointcontents;
-
- // perform a pmove
- gi.Pmove(pm);
-
- // save results of pmove
- client.ps.pmove.set(pm.s);
- client.old_pmove.set(pm.s);
-
- for (i = 0; i < 3; i++) {
- ent.s.origin[i] = pm.s.origin[i] * 0.125f;
- ent.velocity[i] = pm.s.velocity[i] * 0.125f;
- }
-
- VectorCopy(pm.mins, ent.mins);
- VectorCopy(pm.maxs, ent.maxs);
-
- client.resp.cmd_angles[0] = SHORT2ANGLE(ucmd.angles[0]);
- client.resp.cmd_angles[1] = SHORT2ANGLE(ucmd.angles[1]);
- client.resp.cmd_angles[2] = SHORT2ANGLE(ucmd.angles[2]);
-
- if (ent.groundentity != null && null == pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) {
- gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
- PlayerNoise(ent, ent.s.origin, PNOISE_SELF);
- }
-
- ent.viewheight = (int) pm.viewheight;
- ent.waterlevel = (int) pm.waterlevel;
- ent.watertype = pm.watertype;
- ent.groundentity = pm.groundentity;
- if (pm.groundentity != null)
- ent.groundentity_linkcount = pm.groundentity.linkcount;
-
- if (ent.deadflag != 0) {
- client.ps.viewangles[ROLL] = 40;
- client.ps.viewangles[PITCH] = -15;
- client.ps.viewangles[YAW] = client.killer_yaw;
- }
- else {
- VectorCopy(pm.viewangles, client.v_angle);
- VectorCopy(pm.viewangles, client.ps.viewangles);
- }
-
- gi.linkentity(ent);
-
- if (ent.movetype != MOVETYPE_NOCLIP)
- G_TouchTriggers(ent);
-
- // touch other objects
- for (i = 0; i < pm.numtouch; i++) {
- other = pm.touchents[i];
- for (j = 0; j < i; j++)
- if (pm.touchents[j] == other)
- break;
- if (j != i)
- continue; // duplicated
- if (other.touch == null)
- continue;
- other.touch.touch(other, ent, GameBase.dummyplane, null);
- }
-
- }
-
- client.oldbuttons = client.buttons;
- client.buttons = ucmd.buttons;
- client.latched_buttons |= client.buttons & ~client.oldbuttons;
-
- // save light level the player is standing on for
- // monster sighting AI
- ent.light_level = ucmd.lightlevel;
-
- // fire weapon from final position if needed
- if ((client.latched_buttons & BUTTON_ATTACK) != 0) {
- if (client.resp.spectator) {
-
- client.latched_buttons = 0;
-
- if (client.chase_target != null) {
- client.chase_target = null;
- client.ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
- }
- else
- GetChaseTarget(ent);
-
- }
- else if (!client.weapon_thunk) {
- client.weapon_thunk = true;
- GamePWeapon.Think_Weapon(ent);
- }
- }
-
- if (client.resp.spectator) {
- if (ucmd.upmove >= 10) {
- if (0 == (client.ps.pmove.pm_flags & PMF_JUMP_HELD)) {
- client.ps.pmove.pm_flags |= PMF_JUMP_HELD;
- if (client.chase_target != null)
- ChaseNext(ent);
- else
- GetChaseTarget(ent);
- }
- }
- else
- client.ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
- }
-
- // update chase cam if being followed
- for (i = 1; i <= maxclients.value; i++) {
- other = g_edicts[i];
- if (other.inuse && other.client.chase_target == ent)
- UpdateChaseCam(other);
- }
- }
-
- /*
- ==============
- ClientBeginServerFrame
-
- This will be called once for each server frame, before running
- any other entities in the world.
- ==============
- */
- public static void ClientBeginServerFrame(edict_t ent) {
- gclient_t client;
- int buttonMask;
-
- if (level.intermissiontime != 0)
- return;
-
- client = ent.client;
-
- if (deathmatch.value != 0
- && client.pers.spectator != client.resp.spectator
- && (level.time - client.respawn_time) >= 5) {
- spectator_respawn(ent);
- return;
- }
-
- // run weapon animations if it hasn't been done by a ucmd_t
- if (!client.weapon_thunk && !client.resp.spectator)
- GamePWeapon.Think_Weapon(ent);
- else
- client.weapon_thunk = false;
-
- if (ent.deadflag != 0) {
- // wait for any button just going down
- if (level.time > client.respawn_time) {
- // in deathmatch, only wait for attack button
- if (deathmatch.value != 0)
- buttonMask = BUTTON_ATTACK;
- else
- buttonMask = -1;
-
- if ((client.latched_buttons & buttonMask) != 0
- || (deathmatch.value != 0 && 0 != ((int) dmflags.value & DF_FORCE_RESPAWN))) {
- respawn(ent);
- client.latched_buttons = 0;
- }
- }
- return;
- }
-
- // add player trail so monsters can follow
- if (deathmatch.value != 0)
- if (!visible(ent, PlayerTrail.LastSpot()))
- PlayerTrail.Add(ent.s.old_origin);
-
- client.latched_buttons = 0;
- }
-}
+public class PlayerClient {
+
+ /*
+ * QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) The normal
+ * starting point for a level.
+ */
+ public static void SP_info_player_start(edict_t self) {
+ if (GameBase.coop.value == 0)
+ return;
+ if (Lib.Q_stricmp(GameBase.level.mapname, "security") == 0) {
+ // invoke one of our gross, ugly, disgusting hacks
+ self.think = PlayerClientAdapters.SP_CreateCoopSpots;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+
+ /*
+ * QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) potential
+ * spawning position for deathmatch games
+ */
+ public static void SP_info_player_deathmatch(edict_t self) {
+ if (0 == GameBase.deathmatch.value) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+ GameMisc.SP_misc_teleporter_dest.think(self);
+ }
+
+ /*
+ * QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) potential
+ * spawning position for coop games
+ */
+
+ public static void SP_info_player_coop(edict_t self) {
+ if (0 == GameBase.coop.value) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if ((Lib.Q_stricmp(GameBase.level.mapname, "jail2") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "jail4") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine1") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine2") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine3") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine4") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "lab") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "boss1") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "fact3") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "biggun") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "space") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "command") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "power2") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "strike") == 0)) {
+ // invoke one of our gross, ugly, disgusting hacks
+ self.think = PlayerClientAdapters.SP_FixCoopSpots;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+
+ /*
+ * QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) The
+ * deathmatch intermission point will be at one of these Use 'angles'
+ * instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch
+ * yaw roll'
+ */
+ public static void SP_info_player_intermission() {
+ }
+
+ public static void ClientObituary(edict_t self, edict_t inflictor,
+ edict_t attacker) {
+ int mod;
+ String message;
+ String message2;
+ boolean ff;
+
+ if (GameBase.coop.value != 0 && attacker.client != null)
+ GameBase.meansOfDeath |= Defines.MOD_FRIENDLY_FIRE;
+
+ if (GameBase.deathmatch.value != 0 || GameBase.coop.value != 0) {
+ ff = (GameBase.meansOfDeath & Defines.MOD_FRIENDLY_FIRE) != 0;
+ mod = GameBase.meansOfDeath & ~Defines.MOD_FRIENDLY_FIRE;
+ message = null;
+ message2 = "";
+
+ switch (mod) {
+ case Defines.MOD_SUICIDE:
+ message = "suicides";
+ break;
+ case Defines.MOD_FALLING:
+ message = "cratered";
+ break;
+ case Defines.MOD_CRUSH:
+ message = "was squished";
+ break;
+ case Defines.MOD_WATER:
+ message = "sank like a rock";
+ break;
+ case Defines.MOD_SLIME:
+ message = "melted";
+ break;
+ case Defines.MOD_LAVA:
+ message = "does a back flip into the lava";
+ break;
+ case Defines.MOD_EXPLOSIVE:
+ case Defines.MOD_BARREL:
+ message = "blew up";
+ break;
+ case Defines.MOD_EXIT:
+ message = "found a way out";
+ break;
+ case Defines.MOD_TARGET_LASER:
+ message = "saw the light";
+ break;
+ case Defines.MOD_TARGET_BLASTER:
+ message = "got blasted";
+ break;
+ case Defines.MOD_BOMB:
+ case Defines.MOD_SPLASH:
+ case Defines.MOD_TRIGGER_HURT:
+ message = "was in the wrong place";
+ break;
+ }
+ if (attacker == self) {
+ switch (mod) {
+ case Defines.MOD_HELD_GRENADE:
+ message = "tried to put the pin back in";
+ break;
+ case Defines.MOD_HG_SPLASH:
+ case Defines.MOD_G_SPLASH:
+ if (GameAI.IsNeutral(self))
+ message = "tripped on its own grenade";
+ else if (GameAI.IsFemale(self))
+ message = "tripped on her own grenade";
+ else
+ message = "tripped on his own grenade";
+ break;
+ case Defines.MOD_R_SPLASH:
+ if (GameAI.IsNeutral(self))
+ message = "blew itself up";
+ else if (GameAI.IsFemale(self))
+ message = "blew herself up";
+ else
+ message = "blew himself up";
+ break;
+ case Defines.MOD_BFG_BLAST:
+ message = "should have used a smaller gun";
+ break;
+ default:
+ if (GameAI.IsNeutral(self))
+ message = "killed itself";
+ else if (GameAI.IsFemale(self))
+ message = "killed herself";
+ else
+ message = "killed himself";
+ break;
+ }
+ }
+ if (message != null) {
+ GameBase.gi.bprintf(Defines.PRINT_MEDIUM,
+ self.client.pers.netname + " " + message + ".\n");
+ if (GameBase.deathmatch.value != 0)
+ self.client.resp.score--;
+ self.enemy = null;
+ return;
+ }
+
+ self.enemy = attacker;
+ if (attacker != null && attacker.client != null) {
+ switch (mod) {
+ case Defines.MOD_BLASTER:
+ message = "was blasted by";
+ break;
+ case Defines.MOD_SHOTGUN:
+ message = "was gunned down by";
+ break;
+ case Defines.MOD_SSHOTGUN:
+ message = "was blown away by";
+ message2 = "'s super shotgun";
+ break;
+ case Defines.MOD_MACHINEGUN:
+ message = "was machinegunned by";
+ break;
+ case Defines.MOD_CHAINGUN:
+ message = "was cut in half by";
+ message2 = "'s chaingun";
+ break;
+ case Defines.MOD_GRENADE:
+ message = "was popped by";
+ message2 = "'s grenade";
+ break;
+ case Defines.MOD_G_SPLASH:
+ message = "was shredded by";
+ message2 = "'s shrapnel";
+ break;
+ case Defines.MOD_ROCKET:
+ message = "ate";
+ message2 = "'s rocket";
+ break;
+ case Defines.MOD_R_SPLASH:
+ message = "almost dodged";
+ message2 = "'s rocket";
+ break;
+ case Defines.MOD_HYPERBLASTER:
+ message = "was melted by";
+ message2 = "'s hyperblaster";
+ break;
+ case Defines.MOD_RAILGUN:
+ message = "was railed by";
+ break;
+ case Defines.MOD_BFG_LASER:
+ message = "saw the pretty lights from";
+ message2 = "'s BFG";
+ break;
+ case Defines.MOD_BFG_BLAST:
+ message = "was disintegrated by";
+ message2 = "'s BFG blast";
+ break;
+ case Defines.MOD_BFG_EFFECT:
+ message = "couldn't hide from";
+ message2 = "'s BFG";
+ break;
+ case Defines.MOD_HANDGRENADE:
+ message = "caught";
+ message2 = "'s handgrenade";
+ break;
+ case Defines.MOD_HG_SPLASH:
+ message = "didn't see";
+ message2 = "'s handgrenade";
+ break;
+ case Defines.MOD_HELD_GRENADE:
+ message = "feels";
+ message2 = "'s pain";
+ break;
+ case Defines.MOD_TELEFRAG:
+ message = "tried to invade";
+ message2 = "'s personal space";
+ break;
+ }
+ if (message != null) {
+ GameBase.gi.bprintf(Defines.PRINT_MEDIUM,
+ self.client.pers.netname + " " + message + " "
+ + attacker.client.pers.netname + " "
+ + message2 + "\n");
+ if (GameBase.deathmatch.value != 0) {
+ if (ff)
+ attacker.client.resp.score--;
+ else
+ attacker.client.resp.score++;
+ }
+ return;
+ }
+ }
+ }
+
+ GameBase.gi.bprintf(Defines.PRINT_MEDIUM, self.client.pers.netname
+ + " died.\n");
+ if (GameBase.deathmatch.value != 0)
+ self.client.resp.score--;
+ }
+
+ /*
+ * ================== player_die ==================
+ */
+
+ //=======================================================================
+ /*
+ * ============== InitClientPersistant
+ *
+ * This is only called when the game first initializes in single player, but
+ * is called after each death and level change in deathmatch ==============
+ */
+ public static void InitClientPersistant(gclient_t client) {
+ gitem_t item;
+
+ client.pers = new client_persistant_t();
+
+ item = GameUtil.FindItem("Blaster");
+ client.pers.selected_item = GameUtil.ITEM_INDEX(item);
+ client.pers.inventory[client.pers.selected_item] = 1;
+
+ /*
+ * Give shotgun. item = FindItem("Shotgun"); client.pers.selected_item =
+ * ITEM_INDEX(item); client.pers.inventory[client.pers.selected_item] =
+ * 1;
+ */
+
+ client.pers.weapon = item;
+
+ client.pers.health = 100;
+ client.pers.max_health = 100;
+
+ client.pers.max_bullets = 200;
+ client.pers.max_shells = 100;
+ client.pers.max_rockets = 50;
+ client.pers.max_grenades = 50;
+ client.pers.max_cells = 200;
+ client.pers.max_slugs = 50;
+
+ client.pers.connected = true;
+ }
+
+ public static void InitClientResp(gclient_t client) {
+ //memset(& client.resp, 0, sizeof(client.resp));
+ client.resp.clear(); // ok.
+ client.resp.enterframe = GameBase.level.framenum;
+ client.resp.coop_respawn.set(client.pers);
+ }
+
+ /*
+ * ================== SaveClientData
+ *
+ * Some information that should be persistant, like health, is still stored
+ * in the edict structure, so it needs to be mirrored out to the client
+ * structure before all the edicts are wiped. ==================
+ */
+ public static void SaveClientData() {
+ int i;
+ edict_t ent;
+
+ for (i = 0; i < GameBase.game.maxclients; i++) {
+ ent = GameBase.g_edicts[1 + i];
+ if (!ent.inuse)
+ continue;
+
+ GameBase.game.clients[i].pers.health = ent.health;
+ GameBase.game.clients[i].pers.max_health = ent.max_health;
+ GameBase.game.clients[i].pers.savedFlags = (ent.flags & (Defines.FL_GODMODE
+ | Defines.FL_NOTARGET | Defines.FL_POWER_ARMOR));
+
+ if (GameBase.coop.value != 0)
+ GameBase.game.clients[i].pers.score = ent.client.resp.score;
+ }
+ }
+
+ public static void FetchClientEntData(edict_t ent) {
+ ent.health = ent.client.pers.health;
+ ent.max_health = ent.client.pers.max_health;
+ ent.flags |= ent.client.pers.savedFlags;
+ if (GameBase.coop.value != 0)
+ ent.client.resp.score = ent.client.pers.score;
+ }
+
+ /*
+ * ================ PlayersRangeFromSpot
+ *
+ * Returns the distance to the nearest player from the given spot
+ * ================
+ */
+ static float PlayersRangeFromSpot(edict_t spot) {
+ edict_t player;
+ float bestplayerdistance;
+ float[] v = { 0, 0, 0 };
+ int n;
+ float playerdistance;
+
+ bestplayerdistance = 9999999;
+
+ for (n = 1; n <= GameBase.maxclients.value; n++) {
+ player = GameBase.g_edicts[n];
+
+ if (!player.inuse)
+ continue;
+
+ if (player.health <= 0)
+ continue;
+
+ Math3D.VectorSubtract(spot.s.origin, player.s.origin, v);
+ playerdistance = Math3D.VectorLength(v);
+
+ if (playerdistance < bestplayerdistance)
+ bestplayerdistance = playerdistance;
+ }
+
+ return bestplayerdistance;
+ }
+
+ /*
+ * ================ SelectRandomDeathmatchSpawnPoint
+ *
+ * go to a random point, but NOT the two points closest to other players
+ * ================
+ */
+ public static edict_t SelectRandomDeathmatchSpawnPoint() {
+ edict_t spot, spot1, spot2;
+ int count = 0;
+ int selection;
+ float range, range1, range2;
+
+ spot = null;
+ range1 = range2 = 99999;
+ spot1 = spot2 = null;
+
+ EdictIterator es = null;
+
+ while ((es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_deathmatch")) != null) {
+ spot = es.o;
+ count++;
+ range = PlayersRangeFromSpot(spot);
+ if (range < range1) {
+ range1 = range;
+ spot1 = spot;
+ } else if (range < range2) {
+ range2 = range;
+ spot2 = spot;
+ }
+ }
+
+ if (count == 0)
+ return null;
+
+ if (count <= 2) {
+ spot1 = spot2 = null;
+ } else
+ count -= 2;
+
+ selection = Lib.rand() % count;
+
+ spot = null;
+ es = null;
+ do {
+ es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_deathmatch");
+ spot = es.o;
+ if (spot == spot1 || spot == spot2)
+ selection++;
+ } while (selection-- > 0);
+
+ return spot;
+ }
+
+ /*
+ * ================ SelectFarthestDeathmatchSpawnPoint
+ *
+ * ================
+ */
+ static edict_t SelectFarthestDeathmatchSpawnPoint() {
+ edict_t bestspot;
+ float bestdistance, bestplayerdistance;
+ edict_t spot;
+
+ spot = null;
+ bestspot = null;
+ bestdistance = 0;
+
+ EdictIterator es = null;
+ while ((es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_deathmatch")).o != null) {
+ spot = es.o;
+ bestplayerdistance = PlayersRangeFromSpot(spot);
+
+ if (bestplayerdistance > bestdistance) {
+ bestspot = spot;
+ bestdistance = bestplayerdistance;
+ }
+ }
+
+ if (bestspot != null) {
+ return bestspot;
+ }
+
+ // if there is a player just spawned on each and every start spot
+ // we have no choice to turn one into a telefrag meltdown
+ spot = GameBase.G_Find(null, GameBase.findByClass,
+ "info_player_deathmatch").o;
+
+ return spot;
+ }
+
+ public static edict_t SelectDeathmatchSpawnPoint() {
+ if (0 != ((int) (GameBase.dmflags.value) & Defines.DF_SPAWN_FARTHEST))
+ return SelectFarthestDeathmatchSpawnPoint();
+ else
+ return SelectRandomDeathmatchSpawnPoint();
+ }
+
+ public static edict_t SelectCoopSpawnPoint(edict_t ent) {
+ int index;
+ edict_t spot = null;
+ String target;
+
+ //index = ent.client - game.clients;
+ index = ent.client.index;
+
+ // player 0 starts in normal player spawn point
+ if (index == 0)
+ return null;
+
+ spot = null;
+ EdictIterator es = null;
+
+ // assume there are four coop spots at each spawnpoint
+ while (true) {
+
+ spot = (es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_coop")).o;
+ if (spot == null)
+ return null; // we didn't have enough...
+
+ target = spot.targetname;
+ if (target == null)
+ target = "";
+ if (Lib.Q_stricmp(GameBase.game.spawnpoint, target) == 0) { // this
+ // is a
+ // coop
+ // spawn
+ // point
+ // for
+ // one
+ // of
+ // the
+ // clients
+ // here
+ index--;
+ if (0 == index)
+ return spot; // this is it
+ }
+ }
+
+ }
+
+ /*
+ * =========== SelectSpawnPoint
+ *
+ * Chooses a player start, deathmatch start, coop start, etc ============
+ */
+ public static void SelectSpawnPoint(edict_t ent, float[] origin,
+ float[] angles) {
+ edict_t spot = null;
+
+ if (GameBase.deathmatch.value != 0)
+ spot = SelectDeathmatchSpawnPoint();
+ else if (GameBase.coop.value != 0)
+ spot = SelectCoopSpawnPoint(ent);
+
+ EdictIterator es = null;
+ // find a single player start spot
+ if (null == spot) {
+ while ((es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_start")) != null) {
+ spot = es.o;
+
+ if (GameBase.game.spawnpoint.length() == 0
+ && spot.targetname == null)
+ break;
+
+ if (GameBase.game.spawnpoint.length() == 0
+ || spot.targetname == null)
+ continue;
+
+ if (Lib.Q_stricmp(GameBase.game.spawnpoint, spot.targetname) == 0)
+ break;
+ }
+
+ if (null == spot) {
+ if (GameBase.game.spawnpoint.length() == 0) { // there wasn't a
+ // spawnpoint
+ // without a
+ // target, so use
+ // any
+ spot = (es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_start")).o;
+ }
+ if (null == spot)
+ GameBase.gi.error("Couldn't find spawn point "
+ + GameBase.game.spawnpoint + "\n");
+ }
+ }
+
+ Math3D.VectorCopy(spot.s.origin, origin);
+ origin[2] += 9;
+ Math3D.VectorCopy(spot.s.angles, angles);
+ }
+
+ //======================================================================
+
+ public static void InitBodyQue() {
+ int i;
+ edict_t ent;
+
+ GameBase.level.body_que = 0;
+ for (i = 0; i < Defines.BODY_QUEUE_SIZE; i++) {
+ ent = GameUtil.G_Spawn();
+ ent.classname = "bodyque";
+ }
+ }
+
+ public static void CopyToBodyQue(edict_t ent) {
+ edict_t body;
+
+ // grab a body que and cycle to the next one
+ int i = (int) GameBase.maxclients.value + GameBase.level.body_que + 1;
+ body = GameBase.g_edicts[i];
+ GameBase.level.body_que = (GameBase.level.body_que + 1)
+ % Defines.BODY_QUEUE_SIZE;
+
+ // FIXME: send an effect on the removed body
+
+ GameBase.gi.unlinkentity(ent);
+
+ GameBase.gi.unlinkentity(body);
+ body.s = ent.s.getClone();
+
+ body.s.number = body.index;
+
+ body.svflags = ent.svflags;
+ Math3D.VectorCopy(ent.mins, body.mins);
+ Math3D.VectorCopy(ent.maxs, body.maxs);
+ Math3D.VectorCopy(ent.absmin, body.absmin);
+ Math3D.VectorCopy(ent.absmax, body.absmax);
+ Math3D.VectorCopy(ent.size, body.size);
+ body.solid = ent.solid;
+ body.clipmask = ent.clipmask;
+ body.owner = ent.owner;
+ body.movetype = ent.movetype;
+
+ body.die = PlayerClientAdapters.body_die;
+ body.takedamage = Defines.DAMAGE_YES;
+
+ GameBase.gi.linkentity(body);
+ }
+
+ public static void respawn(edict_t self) {
+ if (GameBase.deathmatch.value != 0 || GameBase.coop.value != 0) {
+ // spectator's don't leave bodies
+ if (self.movetype != Defines.MOVETYPE_NOCLIP)
+ CopyToBodyQue(self);
+ self.svflags &= ~Defines.SVF_NOCLIENT;
+ PutClientInServer(self);
+
+ // add a teleportation effect
+ self.s.event = Defines.EV_PLAYER_TELEPORT;
+
+ // hold in place briefly
+ self.client.ps.pmove.pm_flags = pmove_t.PMF_TIME_TELEPORT;
+ self.client.ps.pmove.pm_time = 14;
+
+ self.client.respawn_time = GameBase.level.time;
+
+ return;
+ }
+
+ // restart the entire server
+ GameBase.gi.AddCommandString("menu_loadgame\n");
+ }
+
+ private static boolean passwdOK(String i1, String i2) {
+ if (i1.length() != 0 && !i1.equals("none") && !i1.equals(i2))
+ return false;
+ return true;
+ }
+
+ /*
+ * only called when pers.spectator changes note that resp.spectator should
+ * be the opposite of pers.spectator here
+ */
+ public static void spectator_respawn(edict_t ent) {
+ int i, numspec;
+
+ // if the user wants to become a spectator, make sure he doesn't
+ // exceed max_spectators
+
+ if (ent.client.pers.spectator) {
+ String value = Info.Info_ValueForKey(ent.client.pers.userinfo,
+ "spectator");
+
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ GameBase.gi.cprintf(ent, Defines.PRINT_HIGH,
+ "Spectator password incorrect.\n");
+ ent.client.pers.spectator = false;
+ GameBase.gi.WriteByte(Defines.svc_stufftext);
+ GameBase.gi.WriteString("spectator 0\n");
+ GameBase.gi.unicast(ent, true);
+ return;
+ }
+
+ // count spectators
+ for (i = 1, numspec = 0; i <= GameBase.maxclients.value; i++)
+ if (GameBase.g_edicts[i].inuse
+ && GameBase.g_edicts[i].client.pers.spectator)
+ numspec++;
+
+ if (numspec >= GameBase.maxspectators.value) {
+ GameBase.gi.cprintf(ent, Defines.PRINT_HIGH,
+ "Server spectator limit is full.");
+ ent.client.pers.spectator = false;
+ // reset his spectator var
+ GameBase.gi.WriteByte(Defines.svc_stufftext);
+ GameBase.gi.WriteString("spectator 0\n");
+ GameBase.gi.unicast(ent, true);
+ return;
+ }
+ } else {
+ // he was a spectator and wants to join the game
+ // he must have the right password
+ String value = Info.Info_ValueForKey(ent.client.pers.userinfo,
+ "password");
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ GameBase.gi.cprintf(ent, Defines.PRINT_HIGH,
+ "Password incorrect.\n");
+ ent.client.pers.spectator = true;
+ GameBase.gi.WriteByte(Defines.svc_stufftext);
+ GameBase.gi.WriteString("spectator 1\n");
+ GameBase.gi.unicast(ent, true);
+ return;
+ }
+ }
+
+ // clear client on respawn
+ ent.client.resp.score = ent.client.pers.score = 0;
+
+ ent.svflags &= ~Defines.SVF_NOCLIENT;
+ PutClientInServer(ent);
+
+ // add a teleportation effect
+ if (!ent.client.pers.spectator) {
+ // send effect
+ GameBase.gi.WriteByte(Defines.svc_muzzleflash);
+ //gi.WriteShort(ent - g_edicts);
+ GameBase.gi.WriteShort(ent.index);
+
+ GameBase.gi.WriteByte(Defines.MZ_LOGIN);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+
+ // hold in place briefly
+ ent.client.ps.pmove.pm_flags = pmove_t.PMF_TIME_TELEPORT;
+ ent.client.ps.pmove.pm_time = 14;
+ }
+
+ ent.client.respawn_time = GameBase.level.time;
+
+ if (ent.client.pers.spectator)
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " has moved to the sidelines\n");
+ else
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " joined the game\n");
+ }
+
+ //==============================================================
+
+ /*
+ * =========== PutClientInServer
+ *
+ * Called when a player connects to a server or respawns in a deathmatch.
+ * ============
+ */
+ public static void PutClientInServer(edict_t ent) {
+ float[] mins = { -16, -16, -24 };
+ float[] maxs = { 16, 16, 32 };
+ int index;
+ float[] spawn_origin = { 0, 0, 0 }, spawn_angles = { 0, 0, 0 };
+ gclient_t client;
+ int i;
+ client_persistant_t saved = new client_persistant_t();
+ client_respawn_t resp = new client_respawn_t();
+
+ // find a spawn point
+ // do it before setting health back up, so farthest
+ // ranging doesn't count this client
+ SelectSpawnPoint(ent, spawn_origin, spawn_angles);
+
+ index = ent.index - 1;
+ client = ent.client;
+
+ // deathmatch wipes most client data every spawn
+ if (GameBase.deathmatch.value != 0) {
+ String userinfo;
+ //char userinfo[MAX_INFO_STRING];
+
+ resp.set(client.resp);
+ userinfo = client.pers.userinfo;
+
+ //memcpy(userinfo, client.pers.userinfo, sizeof(userinfo));
+ InitClientPersistant(client);
+ userinfo = ClientUserinfoChanged(ent, userinfo);
+ } else if (GameBase.coop.value != 0) {
+ // int n;
+ //char userinfo[MAX_INFO_STRING];
+ String userinfo;
+
+ resp.set(client.resp);
+ //memcpy(userinfo, client.pers.userinfo, sizeof(userinfo));
+ userinfo = client.pers.userinfo;
+ // this is kind of ugly, but it's how we want to handle keys in coop
+ // for (n = 0; n < game.num_items; n++)
+ // {
+ // if (itemlist[n].flags & IT_KEY)
+ // resp.coop_respawn.inventory[n] = client.pers.inventory[n];
+ // }
+ resp.coop_respawn.game_helpchanged = client.pers.game_helpchanged;
+ resp.coop_respawn.helpchanged = client.pers.helpchanged;
+ client.pers.set(resp.coop_respawn);
+ userinfo = ClientUserinfoChanged(ent, userinfo);
+ if (resp.score > client.pers.score)
+ client.pers.score = resp.score;
+ } else {
+ //memset(& resp, 0, sizeof(resp));
+ resp.clear();
+ }
+
+ // clear everything but the persistant data
+ saved.set(client.pers);
+ //memset(client, 0, sizeof(* client));
+ client.clear();
+ client.pers.set(saved);
+ if (client.pers.health <= 0)
+ InitClientPersistant(client);
+
+ client.resp.set(resp);
+
+ // copy some data from the client to the entity
+ FetchClientEntData(ent);
+
+ // clear entity values
+ ent.groundentity = null;
+ ent.client = GameBase.game.clients[index];
+ ent.takedamage = Defines.DAMAGE_AIM;
+ ent.movetype = Defines.MOVETYPE_WALK;
+ ent.viewheight = 22;
+ ent.inuse = true;
+ ent.classname = "player";
+ ent.mass = 200;
+ ent.solid = Defines.SOLID_BBOX;
+ ent.deadflag = Defines.DEAD_NO;
+ ent.air_finished = GameBase.level.time + 12;
+ ent.clipmask = Defines.MASK_PLAYERSOLID;
+ ent.model = "players/male/tris.md2";
+ ent.pain = PlayerClientAdapters.player_pain;
+ ent.die = GameAI.player_die;
+ ent.waterlevel = 0;
+ ent.watertype = 0;
+ ent.flags &= ~Defines.FL_NO_KNOCKBACK;
+ ent.svflags &= ~Defines.SVF_DEADMONSTER;
+
+ Math3D.VectorCopy(mins, ent.mins);
+ Math3D.VectorCopy(maxs, ent.maxs);
+ Math3D.VectorClear(ent.velocity);
+
+ // clear playerstate values
+ ent.client.ps.clear();
+ //memset(& ent.client.ps, 0, sizeof(client.ps));
+
+ client.ps.pmove.origin[0] = (short) (spawn_origin[0] * 8);
+ client.ps.pmove.origin[1] = (short) (spawn_origin[1] * 8);
+ client.ps.pmove.origin[2] = (short) (spawn_origin[2] * 8);
+
+ if (GameBase.deathmatch.value != 0
+ && 0 != ((int) GameBase.dmflags.value & Defines.DF_FIXED_FOV)) {
+ client.ps.fov = 90;
+ } else {
+ client.ps.fov = Lib.atoi(Info.Info_ValueForKey(
+ client.pers.userinfo, "fov"));
+ if (client.ps.fov < 1)
+ client.ps.fov = 90;
+ else if (client.ps.fov > 160)
+ client.ps.fov = 160;
+ }
+
+ client.ps.gunindex = GameBase.gi
+ .modelindex(client.pers.weapon.view_model);
+
+ // clear entity state values
+ ent.s.effects = 0;
+ ent.s.modelindex = 255; // will use the skin specified model
+ ent.s.modelindex2 = 255; // custom gun model
+ // sknum is player num and weapon number
+ // weapon number will be added in changeweapon
+ ent.s.skinnum = ent.index - 1;
+
+ ent.s.frame = 0;
+ Math3D.VectorCopy(spawn_origin, ent.s.origin);
+ ent.s.origin[2] += 1; // make sure off ground
+ Math3D.VectorCopy(ent.s.origin, ent.s.old_origin);
+
+ // set the delta angle
+ for (i = 0; i < 3; i++) {
+ client.ps.pmove.delta_angles[i] = (short) Math3D
+ .ANGLE2SHORT(spawn_angles[i] - client.resp.cmd_angles[i]);
+ }
+
+ ent.s.angles[Defines.PITCH] = 0;
+ ent.s.angles[Defines.YAW] = spawn_angles[Defines.YAW];
+ ent.s.angles[Defines.ROLL] = 0;
+ Math3D.VectorCopy(ent.s.angles, client.ps.viewangles);
+ Math3D.VectorCopy(ent.s.angles, client.v_angle);
+
+ // spawn a spectator
+ if (client.pers.spectator) {
+ client.chase_target = null;
+
+ client.resp.spectator = true;
+
+ ent.movetype = Defines.MOVETYPE_NOCLIP;
+ ent.solid = Defines.SOLID_NOT;
+ ent.svflags |= Defines.SVF_NOCLIENT;
+ ent.client.ps.gunindex = 0;
+ GameBase.gi.linkentity(ent);
+ return;
+ } else
+ client.resp.spectator = false;
+
+ if (!GameUtil.KillBox(ent)) { // could't spawn in?
+ }
+
+ GameBase.gi.linkentity(ent);
+
+ // force the current weapon up
+ client.newweapon = client.pers.weapon;
+ GamePWeapon.ChangeWeapon(ent);
+ }
+
+ /*
+ * ===================== ClientBeginDeathmatch
+ *
+ * A client has just connected to the server in deathmatch mode, so clear
+ * everything out before starting them. =====================
+ */
+ public static void ClientBeginDeathmatch(edict_t ent) {
+ GameUtil.G_InitEdict(ent, ent.index);
+
+ InitClientResp(ent.client);
+
+ // locate ent at a spawn point
+ PutClientInServer(ent);
+
+ if (GameBase.level.intermissiontime != 0) {
+ PlayerHud.MoveClientToIntermission(ent);
+ } else {
+ // send effect
+ GameBase.gi.WriteByte(Defines.svc_muzzleflash);
+ //gi.WriteShort(ent - g_edicts);
+ GameBase.gi.WriteShort(ent.index);
+ GameBase.gi.WriteByte(Defines.MZ_LOGIN);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+ }
+
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " entered the game\n");
+
+ // make sure all view stuff is valid
+ PlayerView.ClientEndServerFrame(ent);
+ }
+
+ /*
+ * =========== ClientBegin
+ *
+ * called when a client has finished connecting, and is ready to be placed
+ * into the game. This will happen every level load. ============
+ */
+ public static void ClientBegin(edict_t ent) {
+ int i;
+
+ //ent.client = game.clients + (ent - g_edicts - 1);
+ ent.client = GameBase.game.clients[ent.index - 1];
+
+ if (GameBase.deathmatch.value != 0) {
+ ClientBeginDeathmatch(ent);
+ return;
+ }
+
+ // if there is already a body waiting for us (a loadgame), just
+ // take it, otherwise spawn one from scratch
+ if (ent.inuse == true) {
+ // the client has cleared the client side viewangles upon
+ // connecting to the server, which is different than the
+ // state when the game is saved, so we need to compensate
+ // with deltaangles
+ for (i = 0; i < 3; i++)
+ ent.client.ps.pmove.delta_angles[i] = (short) Math3D
+ .ANGLE2SHORT(ent.client.ps.viewangles[i]);
+ } else {
+ // a spawn point will completely reinitialize the entity
+ // except for the persistant data that was initialized at
+ // ClientConnect() time
+ GameUtil.G_InitEdict(ent, ent.index);
+ ent.classname = "player";
+ InitClientResp(ent.client);
+ PutClientInServer(ent);
+ }
+
+ if (GameBase.level.intermissiontime != 0) {
+ PlayerHud.MoveClientToIntermission(ent);
+ } else {
+ // send effect if in a multiplayer game
+ if (GameBase.game.maxclients > 1) {
+ GameBase.gi.WriteByte(Defines.svc_muzzleflash);
+ //gi.WriteShort(ent - g_edicts);
+ GameBase.gi.WriteShort(ent.index);
+ GameBase.gi.WriteByte(Defines.MZ_LOGIN);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " entered the game\n");
+ }
+ }
+
+ // make sure all view stuff is valid
+ PlayerView.ClientEndServerFrame(ent);
+ }
+
+ /*
+ * =========== ClientUserInfoChanged
+ *
+ * called whenever the player updates a userinfo variable.
+ *
+ * The game can override any of the settings in place (forcing skins or
+ * names, etc) before copying it off. ============
+ */
+ public static String ClientUserinfoChanged(edict_t ent, String userinfo) {
+ String s;
+ int playernum;
+
+ // check for malformed or illegal info strings
+ if (!Info.Info_Validate(userinfo)) {
+ //strcpy(userinfo, "\\name\\badinfo\\skin\\male/grunt");
+ return "\\name\\badinfo\\skin\\male/grunt";
+ }
+
+ // set name
+ s = Info.Info_ValueForKey(userinfo, "name");
+
+ //strncpy(ent.client.pers.netname, s, sizeof(ent.client.pers.netname) -
+ // 1);
+ ent.client.pers.netname = s;
+
+ // set spectator
+ s = Info.Info_ValueForKey(userinfo, "spectator");
+ // spectators are only supported in deathmatch
+ if (GameBase.deathmatch.value != 0 && !s.equals("0"))
+ ent.client.pers.spectator = true;
+ else
+ ent.client.pers.spectator = false;
+
+ // set skin
+ s = Info.Info_ValueForKey(userinfo, "skin");
+
+ playernum = ent.index - 1;
+
+ // combine name and skin into a configstring
+ GameBase.gi.configstring(Defines.CS_PLAYERSKINS + playernum,
+ ent.client.pers.netname + "\\" + s);
+
+ // fov
+ if (GameBase.deathmatch.value != 0
+ && 0 != ((int) GameBase.dmflags.value & Defines.DF_FIXED_FOV)) {
+ ent.client.ps.fov = 90;
+ } else {
+ ent.client.ps.fov = Lib
+ .atoi(Info.Info_ValueForKey(userinfo, "fov"));
+ if (ent.client.ps.fov < 1)
+ ent.client.ps.fov = 90;
+ else if (ent.client.ps.fov > 160)
+ ent.client.ps.fov = 160;
+ }
+
+ // handedness
+ s = Info.Info_ValueForKey(userinfo, "hand");
+ if (s.length() > 0) {
+ ent.client.pers.hand = Lib.atoi(s);
+ }
+
+ // save off the userinfo in case we want to check something later
+ //strncpy(ent.client.pers.userinfo, userinfo,
+ // sizeof(ent.client.pers.userinfo) - 1);
+ ent.client.pers.userinfo = userinfo;
+
+ return userinfo;
+ }
+
+ /*
+ * =========== ClientConnect
+ *
+ * Called when a player begins connecting to the server. The game can refuse
+ * entrance to a client by returning false. If the client is allowed, the
+ * connection process will continue and eventually get to ClientBegin()
+ * Changing levels will NOT cause this to be called again, but loadgames
+ * will. ============
+ */
+ public static boolean ClientConnect(edict_t ent, String userinfo) {
+ String value;
+
+ // check to see if they are on the banned IP list
+ value = Info.Info_ValueForKey(userinfo, "ip");
+ if (GameSVCmds.SV_FilterPacket(value)) {
+ userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg", "Banned.");
+ return false;
+ }
+
+ // check for a spectator
+ value = Info.Info_ValueForKey(userinfo, "spectator");
+ if (GameBase.deathmatch.value != 0 && value.length() != 0
+ && 0 != Lib.strcmp(value, "0")) {
+ int i, numspec;
+
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg",
+ "Spectator password required or incorrect.");
+ return false;
+ }
+
+ // count spectators
+ for (i = numspec = 0; i < GameBase.maxclients.value; i++)
+ if (GameBase.g_edicts[i + 1].inuse
+ && GameBase.g_edicts[i + 1].client.pers.spectator)
+ numspec++;
+
+ if (numspec >= GameBase.maxspectators.value) {
+ userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg",
+ "Server spectator limit is full.");
+ return false;
+ }
+ } else {
+ // check for a password
+ value = Info.Info_ValueForKey(userinfo, "password");
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ userinfo = Info.Info_SetValueForKey1(userinfo, "rejmsg",
+ "Password required or incorrect.");
+ return false;
+ }
+ }
+
+ // they can connect
+ ent.client = GameBase.game.clients[ent.index - 1];
+
+ // if there is already a body waiting for us (a loadgame), just
+ // take it, otherwise spawn one from scratch
+ if (ent.inuse == false) {
+ // clear the respawning variables
+ InitClientResp(ent.client);
+ if (!GameBase.game.autosaved || null == ent.client.pers.weapon)
+ InitClientPersistant(ent.client);
+ }
+
+ userinfo = ClientUserinfoChanged(ent, userinfo);
+
+ if (GameBase.game.maxclients > 1)
+ GameBase.gi.dprintf(ent.client.pers.netname + " connected\n");
+
+ ent.svflags = 0; // make sure we start with known default
+ ent.client.pers.connected = true;
+ return true;
+ }
+
+ /*
+ * =========== ClientDisconnect
+ *
+ * Called when a player drops from the server. Will not be called between
+ * levels. ============
+ */
+ public static void ClientDisconnect(edict_t ent) {
+ int playernum;
+
+ if (ent.client == null)
+ return;
+
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " disconnected\n");
+
+ // send effect
+ GameBase.gi.WriteByte(Defines.svc_muzzleflash);
+ GameBase.gi.WriteShort(ent.index);
+ GameBase.gi.WriteByte(Defines.MZ_LOGOUT);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+
+ GameBase.gi.unlinkentity(ent);
+ ent.s.modelindex = 0;
+ ent.solid = Defines.SOLID_NOT;
+ ent.inuse = false;
+ ent.classname = "disconnected";
+ ent.client.pers.connected = false;
+
+ playernum = ent.index - 1;
+ GameBase.gi.configstring(Defines.CS_PLAYERSKINS + playernum, "");
+ }
+
+ /*
+ * static int CheckBlock(, int c) { int v, i; v = 0; for (i = 0; i < c; i++)
+ * v += ((byte *) b)[i]; return v; }
+ *
+ * public static void PrintPmove(pmove_t * pm) { unsigned c1, c2;
+ *
+ * c1 = CheckBlock(& pm.s, sizeof(pm.s)); c2 = CheckBlock(& pm.cmd,
+ * sizeof(pm.cmd)); Com_Printf("sv %3i:%i %i\n", pm.cmd.impulse, c1, c2); }
+ */
+
+ /*
+ * ============== ClientThink
+ *
+ * This will be called once for each client frame, which will usually be a
+ * couple times for each server frame. ==============
+ */
+ public static void ClientThink(edict_t ent, usercmd_t ucmd) {
+ gclient_t client;
+ edict_t other;
+ int i, j;
+ pmove_t pm = null;
+
+ GameBase.level.current_entity = ent;
+ client = ent.client;
+
+ if (GameBase.level.intermissiontime != 0) {
+ client.ps.pmove.pm_type = Defines.PM_FREEZE;
+ // can exit intermission after five seconds
+ if (GameBase.level.time > GameBase.level.intermissiontime + 5.0f
+ && 0 != (ucmd.buttons & Defines.BUTTON_ANY))
+ GameBase.level.exitintermission = true;
+ return;
+ }
+
+ PlayerClientAdapters.pm_passent = ent;
+
+ if (ent.client.chase_target != null) {
+
+ client.resp.cmd_angles[0] = Math3D.SHORT2ANGLE(ucmd.angles[0]);
+ client.resp.cmd_angles[1] = Math3D.SHORT2ANGLE(ucmd.angles[1]);
+ client.resp.cmd_angles[2] = Math3D.SHORT2ANGLE(ucmd.angles[2]);
+
+ } else {
+
+ // set up for pmove
+ //memset(& pm, 0, sizeof(pm));
+ pm = new pmove_t();
+
+ if (ent.movetype == Defines.MOVETYPE_NOCLIP)
+ client.ps.pmove.pm_type = Defines.PM_SPECTATOR;
+ else if (ent.s.modelindex != 255)
+ client.ps.pmove.pm_type = Defines.PM_GIB;
+ else if (ent.deadflag != 0)
+ client.ps.pmove.pm_type = Defines.PM_DEAD;
+ else
+ client.ps.pmove.pm_type = Defines.PM_NORMAL;
+
+ client.ps.pmove.gravity = (short) GameBase.sv_gravity.value;
+ pm.s.set(client.ps.pmove);
+
+ for (i = 0; i < 3; i++) {
+ pm.s.origin[i] = (short) (ent.s.origin[i] * 8);
+ pm.s.velocity[i] = (short) (ent.velocity[i] * 8);
+ }
+
+ if (client.old_pmove.equals(pm.s)) {
+ pm.snapinitial = true;
+ // gi.dprintf ("pmove changed!\n");
+ }
+
+ // this should be a copy
+ pm.cmd.set(ucmd);
+
+ pm.trace = PlayerClientAdapters.PM_trace; // adds default parms
+ pm.pointcontents = GameBase.gi.pointcontents;
+
+ // perform a pmove
+ GameBase.gi.Pmove(pm);
+
+ // save results of pmove
+ client.ps.pmove.set(pm.s);
+ client.old_pmove.set(pm.s);
+
+ for (i = 0; i < 3; i++) {
+ ent.s.origin[i] = pm.s.origin[i] * 0.125f;
+ ent.velocity[i] = pm.s.velocity[i] * 0.125f;
+ }
+
+ Math3D.VectorCopy(pm.mins, ent.mins);
+ Math3D.VectorCopy(pm.maxs, ent.maxs);
+
+ client.resp.cmd_angles[0] = Math3D.SHORT2ANGLE(ucmd.angles[0]);
+ client.resp.cmd_angles[1] = Math3D.SHORT2ANGLE(ucmd.angles[1]);
+ client.resp.cmd_angles[2] = Math3D.SHORT2ANGLE(ucmd.angles[2]);
+
+ if (ent.groundentity != null && null == pm.groundentity
+ && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) {
+ GameBase.gi.sound(ent, Defines.CHAN_VOICE, GameBase.gi
+ .soundindex("*jump1.wav"), 1, Defines.ATTN_NORM, 0);
+ GameWeapon.PlayerNoise(ent, ent.s.origin, Defines.PNOISE_SELF);
+ }
+
+ ent.viewheight = (int) pm.viewheight;
+ ent.waterlevel = (int) pm.waterlevel;
+ ent.watertype = pm.watertype;
+ ent.groundentity = pm.groundentity;
+ if (pm.groundentity != null)
+ ent.groundentity_linkcount = pm.groundentity.linkcount;
+
+ if (ent.deadflag != 0) {
+ client.ps.viewangles[Defines.ROLL] = 40;
+ client.ps.viewangles[Defines.PITCH] = -15;
+ client.ps.viewangles[Defines.YAW] = client.killer_yaw;
+ } else {
+ Math3D.VectorCopy(pm.viewangles, client.v_angle);
+ Math3D.VectorCopy(pm.viewangles, client.ps.viewangles);
+ }
+
+ GameBase.gi.linkentity(ent);
+
+ if (ent.movetype != Defines.MOVETYPE_NOCLIP)
+ GameBase.G_TouchTriggers(ent);
+
+ // touch other objects
+ for (i = 0; i < pm.numtouch; i++) {
+ other = pm.touchents[i];
+ for (j = 0; j < i; j++)
+ if (pm.touchents[j] == other)
+ break;
+ if (j != i)
+ continue; // duplicated
+ if (other.touch == null)
+ continue;
+ other.touch.touch(other, ent, GameBase.dummyplane, null);
+ }
+
+ }
+
+ client.oldbuttons = client.buttons;
+ client.buttons = ucmd.buttons;
+ client.latched_buttons |= client.buttons & ~client.oldbuttons;
+
+ // save light level the player is standing on for
+ // monster sighting AI
+ ent.light_level = ucmd.lightlevel;
+
+ // fire weapon from final position if needed
+ if ((client.latched_buttons & Defines.BUTTON_ATTACK) != 0) {
+ if (client.resp.spectator) {
+
+ client.latched_buttons = 0;
+
+ if (client.chase_target != null) {
+ client.chase_target = null;
+ client.ps.pmove.pm_flags &= ~pmove_t.PMF_NO_PREDICTION;
+ } else
+ GameAI.GetChaseTarget(ent);
+
+ } else if (!client.weapon_thunk) {
+ client.weapon_thunk = true;
+ GamePWeapon.Think_Weapon(ent);
+ }
+ }
+
+ if (client.resp.spectator) {
+ if (ucmd.upmove >= 10) {
+ if (0 == (client.ps.pmove.pm_flags & pmove_t.PMF_JUMP_HELD)) {
+ client.ps.pmove.pm_flags |= pmove_t.PMF_JUMP_HELD;
+ if (client.chase_target != null)
+ GameAI.ChaseNext(ent);
+ else
+ GameAI.GetChaseTarget(ent);
+ }
+ } else
+ client.ps.pmove.pm_flags &= ~pmove_t.PMF_JUMP_HELD;
+ }
+
+ // update chase cam if being followed
+ for (i = 1; i <= GameBase.maxclients.value; i++) {
+ other = GameBase.g_edicts[i];
+ if (other.inuse && other.client.chase_target == ent)
+ GameAI.UpdateChaseCam(other);
+ }
+ }
+
+ /*
+ * ============== ClientBeginServerFrame
+ *
+ * This will be called once for each server frame, before running any other
+ * entities in the world. ==============
+ */
+ public static void ClientBeginServerFrame(edict_t ent) {
+ gclient_t client;
+ int buttonMask;
+
+ if (GameBase.level.intermissiontime != 0)
+ return;
+
+ client = ent.client;
+
+ if (GameBase.deathmatch.value != 0
+ && client.pers.spectator != client.resp.spectator
+ && (GameBase.level.time - client.respawn_time) >= 5) {
+ spectator_respawn(ent);
+ return;
+ }
+
+ // run weapon animations if it hasn't been done by a ucmd_t
+ if (!client.weapon_thunk && !client.resp.spectator)
+ GamePWeapon.Think_Weapon(ent);
+ else
+ client.weapon_thunk = false;
+
+ if (ent.deadflag != 0) {
+ // wait for any button just going down
+ if (GameBase.level.time > client.respawn_time) {
+ // in deathmatch, only wait for attack button
+ if (GameBase.deathmatch.value != 0)
+ buttonMask = Defines.BUTTON_ATTACK;
+ else
+ buttonMask = -1;
+
+ if ((client.latched_buttons & buttonMask) != 0
+ || (GameBase.deathmatch.value != 0 && 0 != ((int) GameBase.dmflags.value & Defines.DF_FORCE_RESPAWN))) {
+ respawn(ent);
+ client.latched_buttons = 0;
+ }
+ }
+ return;
+ }
+
+ // add player trail so monsters can follow
+ if (GameBase.deathmatch.value != 0)
+ if (!GameUtil.visible(ent, PlayerTrail.LastSpot()))
+ PlayerTrail.Add(ent.s.old_origin);
+
+ client.latched_buttons = 0;
+ }
+} \ No newline at end of file