/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Modifications Copyright 2003-2004 Bytonic Software Copyright 2010 Google Inc. */ /** Father of all GameObjects. */ package com.googlecode.gwtquake.shared.game; import java.util.StringTokenizer; import com.googlecode.gwtquake.*; import com.googlecode.gwtquake.shared.client.*; import com.googlecode.gwtquake.shared.common.Com; import com.googlecode.gwtquake.shared.common.CommandBuffer; import com.googlecode.gwtquake.shared.common.ConsoleVariables; import com.googlecode.gwtquake.shared.common.Constants; import com.googlecode.gwtquake.shared.game.PlayerMove.PointContentsAdapter; import com.googlecode.gwtquake.shared.server.*; import com.googlecode.gwtquake.shared.util.*; public class GameBase { public static Plane dummyplane = new Plane(); public static GameState game = new GameState(); public static LevelLocals level = new LevelLocals(); public static SpawnTemp st = new SpawnTemp(); public static int sm_meat_index; public static int snd_fry; public static int meansOfDeath; public static int num_edicts; public static Entity g_edicts[] = new Entity[Constants.MAX_EDICTS]; static { for (int n = 0; n < Constants.MAX_EDICTS; n++) g_edicts[n] = new Entity(n); } public static ConsoleVariable deathmatch = new ConsoleVariable(); public static ConsoleVariable coop = new ConsoleVariable(); public static ConsoleVariable dmflags = new ConsoleVariable(); public static ConsoleVariable skill; // = new cvar_t(); public static ConsoleVariable fraglimit = new ConsoleVariable(); public static ConsoleVariable timelimit = new ConsoleVariable(); public static ConsoleVariable password = new ConsoleVariable(); public static ConsoleVariable spectator_password = new ConsoleVariable(); public static ConsoleVariable needpass = new ConsoleVariable(); public static ConsoleVariable maxclients = new ConsoleVariable(); public static ConsoleVariable maxspectators = new ConsoleVariable(); public static ConsoleVariable maxentities = new ConsoleVariable(); public static ConsoleVariable g_select_empty = new ConsoleVariable(); public static ConsoleVariable filterban = new ConsoleVariable(); public static ConsoleVariable sv_maxvelocity = new ConsoleVariable(); public static ConsoleVariable sv_gravity = new ConsoleVariable(); public static ConsoleVariable sv_rollspeed = new ConsoleVariable(); public static ConsoleVariable sv_rollangle = new ConsoleVariable(); public static ConsoleVariable gun_x = new ConsoleVariable(); public static ConsoleVariable gun_y = new ConsoleVariable(); public static ConsoleVariable gun_z = new ConsoleVariable(); public static ConsoleVariable run_pitch = new ConsoleVariable(); public static ConsoleVariable run_roll = new ConsoleVariable(); public static ConsoleVariable bob_up = new ConsoleVariable(); public static ConsoleVariable bob_pitch = new ConsoleVariable(); public static ConsoleVariable bob_roll = new ConsoleVariable(); public static ConsoleVariable sv_cheats = new ConsoleVariable(); public static ConsoleVariable flood_msgs = new ConsoleVariable(); public static ConsoleVariable flood_persecond = new ConsoleVariable(); public static ConsoleVariable flood_waitdelay = new ConsoleVariable(); public static ConsoleVariable sv_maplist = new ConsoleVariable(); public final static float STOP_EPSILON = 0.1f; /** * Slide off of the impacting object returns the blocked flags (1 = floor, 2 = * step / wall). */ public static int ClipVelocity(float[] in, float[] normal, float[] out, float overbounce) { float backoff; float change; int i, blocked; blocked = 0; if (normal[2] > 0) blocked |= 1; // floor if (normal[2] == 0.0f) blocked |= 2; // step backoff = Math3D.DotProduct(in, normal) * overbounce; for (i = 0; i < 3; i++) { change = normal[i] * backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } return blocked; } /** * Searches all active entities for the next one that holds the matching * string at fieldofs (use the FOFS() macro) in the structure. * * Searches beginning at the edict after from, or the beginning if null null * will be returned if the end of the list is reached. * */ public static EntityIterator G_Find(EntityIterator from, EntityFilter eff, String s) { if (from == null) from = new EntityIterator(0); else from.i++; for (; from.i < num_edicts; from.i++) { from.o = g_edicts[from.i]; if (from.o.classname == null) { Com.Printf("edict with classname = null" + from.o.index); } if (!from.o.inuse) continue; if (eff.matches(from.o, s)) return from; } return null; } // comfort version (rst) public static Entity G_FindEdict(EntityIterator from, EntityFilter eff, String s) { EntityIterator ei = G_Find(from, eff, s); if (ei == null) return null; else return ei.o; } /** * Returns entities that have origins within a spherical area. */ public static EntityIterator findradius(EntityIterator from, float[] org, float rad) { float[] eorg = { 0, 0, 0 }; int j; if (from == null) from = new EntityIterator(0); else from.i++; for (; from.i < num_edicts; from.i++) { from.o = g_edicts[from.i]; if (!from.o.inuse) continue; if (from.o.solid == Constants.SOLID_NOT) continue; for (j = 0; j < 3; j++) eorg[j] = org[j] - (from.o.s.origin[j] + (from.o.mins[j] + from.o.maxs[j]) * 0.5f); if (Math3D.VectorLength(eorg) > rad) continue; return from; } return null; } /** * Searches all active entities for the next one that holds the matching * string at fieldofs (use the FOFS() macro) in the structure. * * Searches beginning at the edict after from, or the beginning if null null * will be returned if the end of the list is reached. */ public static int MAXCHOICES = 8; public static Entity G_PickTarget(String targetname) { int num_choices = 0; Entity choice[] = new Entity[MAXCHOICES]; if (targetname == null) { ServerGame.PF_dprintf("G_PickTarget called with null targetname\n"); return null; } EntityIterator es = null; while ((es = G_Find(es, findByTarget, targetname)) != null) { choice[num_choices++] = es.o; if (num_choices == MAXCHOICES) break; } if (num_choices == 0) { ServerGame.PF_dprintf("G_PickTarget: target " + targetname + " not found\n"); return null; } return choice[Lib.rand() % num_choices]; } public static float[] VEC_UP = { 0, -1, 0 }; public static float[] MOVEDIR_UP = { 0, 0, 1 }; public static float[] VEC_DOWN = { 0, -2, 0 }; public static float[] MOVEDIR_DOWN = { 0, 0, -1 }; public static void G_SetMovedir(float[] angles, float[] movedir) { if (Math3D.VectorEquals(angles, VEC_UP)) { Math3D.VectorCopy(MOVEDIR_UP, movedir); } else if (Math3D.VectorEquals(angles, VEC_DOWN)) { Math3D.VectorCopy(MOVEDIR_DOWN, movedir); } else { Math3D.AngleVectors(angles, movedir, null, null); } Math3D.VectorClear(angles); } public static String G_CopyString(String in) { return new String(in); } /** * G_TouchTriggers */ static Entity touch[] = new Entity[Constants.MAX_EDICTS]; public static void G_TouchTriggers(Entity ent) { int i, num; Entity hit; // dead things don't activate triggers! if ((ent.client != null || (ent.svflags & Constants.SVF_MONSTER) != 0) && (ent.health <= 0)) return; num = World.SV_AreaEdicts(ent.absmin, ent.absmax, touch, Constants.MAX_EDICTS, Constants.AREA_TRIGGERS); // be careful, it is possible to have an entity in this // list removed before we get to it (killtriggered) for (i = 0; i < num; i++) { hit = touch[i]; if (!hit.inuse) continue; if (hit.touch == null) continue; hit.touch.touch(hit, ent, dummyplane, null); } } public static Pushed pushed[] = new Pushed[Constants.MAX_EDICTS]; static { for (int n = 0; n < Constants.MAX_EDICTS; n++) pushed[n] = new Pushed(); } public static int pushed_p; public static Entity obstacle; public static int c_yes, c_no; public static int STEPSIZE = 18; /** * G_RunEntity */ public static void G_RunEntity(Entity ent) { if (ent.prethink != null) ent.prethink.think(ent); switch ((int) ent.movetype) { case Constants.MOVETYPE_PUSH: case Constants.MOVETYPE_STOP: SV.SV_Physics_Pusher(ent); break; case Constants.MOVETYPE_NONE: SV.SV_Physics_None(ent); break; case Constants.MOVETYPE_NOCLIP: SV.SV_Physics_Noclip(ent); break; case Constants.MOVETYPE_STEP: SV.SV_Physics_Step(ent); break; case Constants.MOVETYPE_TOSS: case Constants.MOVETYPE_BOUNCE: case Constants.MOVETYPE_FLY: case Constants.MOVETYPE_FLYMISSILE: SV.SV_Physics_Toss(ent); break; default: Com.Error(Constants.ERR_FATAL, "SV_Physics: bad movetype " + (int) ent.movetype); } } public static void ClearBounds(float[] mins, float[] maxs) { mins[0] = mins[1] = mins[2] = 99999; maxs[0] = maxs[1] = maxs[2] = -99999; } public static void AddPointToBounds(float[] v, float[] mins, float[] maxs) { int i; float val; for (i = 0; i < 3; i++) { val = v[i]; if (val < mins[i]) mins[i] = val; if (val > maxs[i]) maxs[i] = val; } } public static EntityFilter findByTarget = new EntityFilter() { public boolean matches(Entity e, String s) { if (e.targetname == null) return false; return e.targetname.equalsIgnoreCase(s); } }; public static EntityFilter findByClass = new EntityFilter() { public boolean matches(Entity e, String s) { return e.classname.equalsIgnoreCase(s); } }; public static void ShutdownGame() { ServerGame.PF_dprintf("==== ShutdownGame ====\n"); } /** * ClientEndServerFrames. */ public static void ClientEndServerFrames() { int i; Entity ent; // calc the player views now that all pushing // and damage has been added for (i = 0; i < maxclients.value; i++) { ent = g_edicts[1 + i]; if (!ent.inuse || null == ent.client) continue; PlayerView.ClientEndServerFrame(ent); } } /** * Returns the created target changelevel. */ public static Entity CreateTargetChangeLevel(String map) { Entity ent; ent = GameUtil.G_Spawn(); ent.classname = "target_changelevel"; level.nextmap = map; ent.map = level.nextmap; return ent; } /** * The timelimit or fraglimit has been exceeded. */ public static void EndDMLevel() { Entity ent; //char * s, * t, * f; //static const char * seps = " ,\n\r"; String s, t, f; String seps = " ,\n\r"; // stay on same level flag if (((int) dmflags.value & Constants.DF_SAME_LEVEL) != 0) { PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.mapname)); return; } // see if it's in the map list if (sv_maplist.string.length() > 0) { s = sv_maplist.string; f = null; StringTokenizer tk = new StringTokenizer(s, seps); while (tk.hasMoreTokens()){ t = tk.nextToken(); // store first map if (f == null) f = t; if (t.equalsIgnoreCase(level.mapname)) { // it's in the list, go to the next one if (!tk.hasMoreTokens()) { // end of list, go to first one if (f == null) // there isn't a first one, same level PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.mapname)); else PlayerHud.BeginIntermission(CreateTargetChangeLevel(f)); } else PlayerHud.BeginIntermission(CreateTargetChangeLevel(tk.nextToken())); return; } } } //not in the map list if (level.nextmap.length() > 0) // go to a specific map PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.nextmap)); else { // search for a changelevel EntityIterator edit = null; edit = G_Find(edit, findByClass, "target_changelevel"); if (edit == null) { // the map designer didn't include a // changelevel, // so create a fake ent that goes back to the same level PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.mapname)); return; } ent = edit.o; PlayerHud.BeginIntermission(ent); } } /** * CheckNeedPass. */ public static void CheckNeedPass() { int need; // if password or spectator_password has changed, update needpass // as needed if (password.modified || spectator_password.modified) { password.modified = spectator_password.modified = false; need = 0; if ((password.string.length() > 0) && 0 != Lib.Q_stricmp(password.string, "none")) need |= 1; if ((spectator_password.string.length() > 0) && 0 != Lib.Q_stricmp(spectator_password.string, "none")) need |= 2; ConsoleVariables.Set("needpass", "" + need); } } /** * CheckDMRules. */ public static void CheckDMRules() { int i; GameClient cl; if (level.intermissiontime != 0) return; if (0 == deathmatch.value) return; if (timelimit.value != 0) { if (level.time >= timelimit.value * 60) { ServerSend.SV_BroadcastPrintf(Constants.PRINT_HIGH, "Timelimit hit.\n"); EndDMLevel(); return; } } if (fraglimit.value != 0) { for (i = 0; i < maxclients.value; i++) { cl = game.clients[i]; if (!g_edicts[i + 1].inuse) continue; if (cl.resp.score >= fraglimit.value) { ServerSend.SV_BroadcastPrintf(Constants.PRINT_HIGH, "Fraglimit hit.\n"); EndDMLevel(); return; } } } } /** * Exits a level. */ public static void ExitLevel() { int i; Entity ent; String command = "gamemap \"" + level.changemap + "\"\n"; CommandBuffer.AddText(command); level.changemap = null; level.exitintermission = false; level.intermissiontime = 0; ClientEndServerFrames(); // clear some things before going to next level for (i = 0; i < maxclients.value; i++) { ent = g_edicts[1 + i]; if (!ent.inuse) continue; if (ent.health > ent.client.pers.max_health) ent.health = ent.client.pers.max_health; } } /** * G_RunFrame * * Advances the world by Defines.FRAMETIME (0.1) seconds. */ public static void G_RunFrame() { int i; Entity ent; level.framenum++; level.time = level.framenum * Constants.FRAMETIME; // choose a client for monsters to target this frame GameAI.AI_SetSightClient(); // exit intermissions if (level.exitintermission) { ExitLevel(); return; } // // treat each object in turn // even the world gets a chance to think // for (i = 0; i < num_edicts; i++) { ent = g_edicts[i]; if (!ent.inuse) continue; level.current_entity = ent; Math3D.VectorCopy(ent.s.origin, ent.s.old_origin); // if the ground entity moved, make sure we are still on it if ((ent.groundentity != null) && (ent.groundentity.linkcount != ent.groundentity_linkcount)) { ent.groundentity = null; if (0 == (ent.flags & (Constants.FL_SWIM | Constants.FL_FLY)) && (ent.svflags & Constants.SVF_MONSTER) != 0) { ClientMonsterMethods.M_CheckGround(ent); } } if (i > 0 && i <= maxclients.value) { PlayerClient.ClientBeginServerFrame(ent); continue; } G_RunEntity(ent); } // see if it is time to end a deathmatch CheckDMRules(); // see if needpass needs updated CheckNeedPass(); // build the playerstate_t structures for all players ClientEndServerFrames(); } public static PlayerMove.PointContentsAdapter pointcontents = new PlayerMove.PointContentsAdapter() { public int pointcontents(float[] o) { return 0; } }; }