aboutsummaryrefslogtreecommitdiffstats
path: root/src/jake2/server/SV_WORLD.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jake2/server/SV_WORLD.java')
-rw-r--r--src/jake2/server/SV_WORLD.java654
1 files changed, 654 insertions, 0 deletions
diff --git a/src/jake2/server/SV_WORLD.java b/src/jake2/server/SV_WORLD.java
new file mode 100644
index 0000000..6df2512
--- /dev/null
+++ b/src/jake2/server/SV_WORLD.java
@@ -0,0 +1,654 @@
+/*
+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 07.01.2000 by RST.
+// $Id: SV_WORLD.java,v 1.1 2004-07-07 19:59:50 hzi Exp $
+
+package jake2.server;
+
+import jake2.game.edict_t;
+import jake2.game.trace_t;
+
+import jake2.*;
+import jake2.client.*;
+import jake2.game.*;
+import jake2.qcommon.*;
+import jake2.render.*;
+import jake2.util.Vargs;
+
+public class SV_WORLD extends SV_CCMDS {
+
+ // world.c -- world query functions
+
+ //
+ ///*
+ //===============================================================================
+ //
+ //ENTITY AREA CHECKING
+ //
+ //FIXME: this use of "area" is different from the bsp file use
+ //===============================================================================
+ //*/
+
+ //// (type *)STRUCT_FROM_LINK(link_t *link, type, member)
+ //// ent = STRUCT_FROM_LINK(link,entity_t,order)
+
+ //// FIXME: remove this mess!
+ //#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0).m)))
+ //
+ //#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
+
+ //
+
+ public static void initNodes() {
+ for (int n = 0; n < AREA_NODES; n++)
+ sv_areanodes[n] = new areanode_t();
+ }
+
+ public static areanode_t sv_areanodes[] = new areanode_t[AREA_NODES];
+
+ static {
+ initNodes();
+ }
+ public static int sv_numareanodes;
+
+ public static float area_mins[], area_maxs[];
+ public static edict_t area_list[];
+ public static int area_count, area_maxcount;
+ public static int area_type;
+
+ // ClearLink is used for new headnodes
+ public static void ClearLink(link_t l) {
+ l.prev = l.next = l;
+ }
+
+ public static void RemoveLink(link_t l) {
+ l.next.prev = l.prev;
+ l.prev.next = l.next;
+ }
+
+ public static void InsertLinkBefore(link_t l, link_t before) {
+ l.next = before;
+ l.prev = before.prev;
+ l.prev.next = l;
+ l.next.prev = l;
+ }
+
+ /*
+ ===============
+ SV_CreateAreaNode
+
+ Builds a uniformly subdivided tree for the given world size
+ ===============
+ */
+ public static areanode_t SV_CreateAreaNode(int depth, float[] mins, float[] maxs) {
+ areanode_t anode;
+ float[] size = { 0, 0, 0 };
+ float[] mins1 = { 0, 0, 0 }, maxs1 = { 0, 0, 0 }, mins2 = { 0, 0, 0 }, maxs2 = { 0, 0, 0 };
+
+ Com.Printf("SV_CreateAreaNode " + depth + "\n");
+ anode = sv_areanodes[sv_numareanodes];
+
+ // just for debugging (rst)
+ VectorCopy(mins, anode.mins_rst);
+ VectorCopy(maxs, anode.maxs_rst);
+
+ sv_numareanodes++;
+
+ ClearLink(anode.trigger_edicts);
+ ClearLink(anode.solid_edicts);
+
+ if (depth == AREA_DEPTH) {
+ anode.axis = -1;
+ anode.children[0] = anode.children[1] = null;
+ return anode;
+ }
+
+ VectorSubtract(maxs, mins, size);
+ if (size[0] > size[1])
+ anode.axis = 0;
+ else
+ anode.axis = 1;
+
+ anode.dist = 0.5f * (maxs[anode.axis] + mins[anode.axis]);
+ VectorCopy(mins, mins1);
+ VectorCopy(mins, mins2);
+ VectorCopy(maxs, maxs1);
+ VectorCopy(maxs, maxs2);
+
+ maxs1[anode.axis] = mins2[anode.axis] = anode.dist;
+
+ anode.children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2);
+ anode.children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1);
+
+ return anode;
+ }
+
+ /*
+ ===============
+ SV_ClearWorld
+
+ ===============
+ */
+ public static void SV_ClearWorld() {
+ initNodes();
+ sv_numareanodes = 0;
+ SV_CreateAreaNode(0, sv.models[1].mins, sv.models[1].maxs);
+
+ Com.Println("areanodes:" + sv_numareanodes + " (sollten 32 sein).");
+ for (int n = 0; n < sv_numareanodes; n++) {
+ Com.Printf(
+ "|%3i|%2i|%8.2f |%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n",
+ new Vargs()
+ .add(n)
+ .add(sv_areanodes[n].axis)
+ .add(sv_areanodes[n].dist)
+ .add(sv_areanodes[n].mins_rst[0])
+ .add(sv_areanodes[n].mins_rst[1])
+ .add(sv_areanodes[n].mins_rst[2])
+ .add(sv_areanodes[n].maxs_rst[0])
+ .add(sv_areanodes[n].maxs_rst[1])
+ .add(sv_areanodes[n].maxs_rst[2]));
+ }
+ }
+
+ /*
+ ===============
+ SV_UnlinkEdict
+
+ ===============
+ */
+ public static void SV_UnlinkEdict(edict_t ent) {
+ if (null == ent.area.prev)
+ return; // not linked in anywhere
+ RemoveLink(ent.area);
+ ent.area.prev = ent.area.next = null;
+ }
+
+ /*
+ ===============
+ SV_LinkEdict
+
+ ===============
+ */
+ public static final int MAX_TOTAL_ENT_LEAFS = 128;
+
+ public static void SV_LinkEdict(edict_t ent) {
+ areanode_t node;
+ // TODO: make static instead of new
+ int leafs[] = new int[MAX_TOTAL_ENT_LEAFS];
+ int clusters[] = new int[MAX_TOTAL_ENT_LEAFS];
+ int num_leafs;
+ int j, k;
+ int area;
+ int topnode = 0;
+
+ if (ent.area.prev != null)
+ SV_UnlinkEdict(ent); // unlink from old position
+
+ if (ent == ge.edicts[0])
+ return; // don't add the world
+
+ if (!ent.inuse)
+ return;
+
+ // set the size
+ VectorSubtract(ent.maxs, ent.mins, ent.size);
+
+ // encode the size into the entity_state for client prediction
+ if (ent.solid == SOLID_BBOX && 0 == (ent.svflags & SVF_DEADMONSTER)) { // assume that x/y are equal and symetric
+ int i = (int) (ent.maxs[0] / 8);
+ if (i < 1)
+ i = 1;
+ if (i > 31)
+ i = 31;
+
+ // z is not symetric
+ j = (int) ((-ent.mins[2]) / 8);
+ if (j < 1)
+ j = 1;
+ if (j > 31)
+ j = 31;
+
+ // and z maxs can be negative...
+ k = (int) ((ent.maxs[2] + 32) / 8);
+ if (k < 1)
+ k = 1;
+ if (k > 63)
+ k = 63;
+
+ ent.s.solid = (k << 10) | (j << 5) | i;
+ }
+ else if (ent.solid == SOLID_BSP) {
+ ent.s.solid = 31; // a solid_bbox will never create this value
+ }
+ else
+ ent.s.solid = 0;
+
+ // set the abs box
+ if (ent.solid == SOLID_BSP && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0)) { // expand for rotation
+ float max, v;
+
+ max = 0;
+ for (int i = 0; i < 3; i++) {
+ v = Math.abs(ent.mins[i]);
+ if (v > max)
+ max = v;
+ v = Math.abs(ent.maxs[i]);
+ if (v > max)
+ max = v;
+ }
+ for (int i = 0; i < 3; i++) {
+ ent.absmin[i] = ent.s.origin[i] - max;
+ ent.absmax[i] = ent.s.origin[i] + max;
+ }
+ }
+ else { // normal
+ VectorAdd(ent.s.origin, ent.mins, ent.absmin);
+ VectorAdd(ent.s.origin, ent.maxs, ent.absmax);
+ }
+
+ // because movement is clipped an epsilon away from an actual edge,
+ // we must fully check even when bounding boxes don't quite touch
+
+ ent.absmin[0]--;
+ ent.absmin[1]--;
+ ent.absmin[2]--;
+ ent.absmax[0]++;
+ ent.absmax[1]++;
+ ent.absmax[2]++;
+
+ // link to PVS leafs
+ ent.num_clusters = 0;
+ ent.areanum = 0;
+ ent.areanum2 = 0;
+
+ //get all leafs, including solids
+
+ CM.intwrap iw = new CM.intwrap(topnode);
+
+ num_leafs = CM.CM_BoxLeafnums(ent.absmin, ent.absmax, leafs, MAX_TOTAL_ENT_LEAFS, iw);
+
+ topnode = iw.i;
+
+ // set areas
+ for (int i = 0; i < num_leafs; i++) {
+ clusters[i] = CM.CM_LeafCluster(leafs[i]);
+ area = CM.CM_LeafArea(leafs[i]);
+ if (area != 0) { // doors may legally straggle two areas,
+ // but nothing should evern need more than that
+ if (ent.areanum != 0 && ent.areanum != area) {
+ if (ent.areanum2 != 0 && ent.areanum2 != area && sv.state == ss_loading)
+ Com.DPrintf("Object touching 3 areas at " + ent.absmin[0] + " " + ent.absmin[1] + " " + ent.absmin[2] + "\n");
+ ent.areanum2 = area;
+ }
+ else
+ ent.areanum = area;
+ }
+ }
+
+ if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode
+ ent.num_clusters = -1;
+ ent.headnode = topnode;
+ }
+ else {
+ ent.num_clusters = 0;
+ for (int i = 0; i < num_leafs; i++) {
+ if (clusters[i] == -1)
+ continue; // not a visible leaf
+ for (j = 0; j < i; j++)
+ if (clusters[j] == clusters[i])
+ break;
+ if (j == i) {
+ if (ent.num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode
+ ent.num_clusters = -1;
+ ent.headnode = topnode;
+ break;
+ }
+
+ ent.clusternums[ent.num_clusters++] = clusters[i];
+ }
+ }
+ }
+
+ // if first time, make sure old_origin is valid
+ if (0 == ent.linkcount) {
+ VectorCopy(ent.s.origin, ent.s.old_origin);
+ }
+ ent.linkcount++;
+
+ if (ent.solid == SOLID_NOT)
+ return;
+
+ // find the first node that the ent's box crosses
+ node = sv_areanodes[0];
+
+ while (true) {
+ if (node.axis == -1)
+ break;
+ if (ent.absmin[node.axis] > node.dist)
+ node = node.children[0];
+ else if (ent.absmax[node.axis] < node.dist)
+ node = node.children[1];
+ else
+ break; // crosses the node
+ }
+
+ // link it in
+ if (ent.solid == SOLID_TRIGGER)
+ InsertLinkBefore(ent.area, node.trigger_edicts);
+ else
+ InsertLinkBefore(ent.area, node.solid_edicts);
+
+ }
+
+ /*
+ ====================
+ SV_AreaEdicts_r
+
+ ====================
+ */
+ public static void SV_AreaEdicts_r(areanode_t node) {
+ link_t l, next, start;
+ edict_t check;
+ int count;
+
+ count = 0;
+
+ // touch linked edicts
+ if (area_type == AREA_SOLID)
+ start = node.solid_edicts;
+ else
+ start = node.trigger_edicts;
+
+ for (l = start.next; l != start; l = next) {
+ next = l.next;
+ check = //EDICT_FROM_AREA(l);
+ (edict_t) l.o;
+
+ if (check.solid == SOLID_NOT)
+ continue; // deactivated
+ if (check.absmin[0] > area_maxs[0]
+ || check.absmin[1] > area_maxs[1]
+ || check.absmin[2] > area_maxs[2]
+ || check.absmax[0] < area_mins[0]
+ || check.absmax[1] < area_mins[1]
+ || check.absmax[2] < area_mins[2])
+ continue; // not touching
+
+ if (area_count == area_maxcount) {
+ Com.Printf("SV_AreaEdicts: MAXCOUNT\n");
+ return;
+ }
+
+ area_list[area_count] = check;
+ area_count++;
+ }
+
+ if (node.axis == -1)
+ return; // terminal node
+
+ // recurse down both sides
+ if (area_maxs[node.axis] > node.dist)
+ SV_AreaEdicts_r(node.children[0]);
+
+ if (area_mins[node.axis] < node.dist)
+ SV_AreaEdicts_r(node.children[1]);
+ }
+
+ /*
+ ================
+ SV_AreaEdicts
+ ================
+ */
+ public static int SV_AreaEdicts(float[] mins, float[] maxs, edict_t list[], int maxcount, int areatype) {
+ area_mins = mins;
+ area_maxs = maxs;
+ area_list = list;
+ area_count = 0;
+ area_maxcount = maxcount;
+ area_type = areatype;
+
+ SV_AreaEdicts_r(sv_areanodes[0]);
+
+ /*
+ Com.Printf("found edicts in area:" + area_count + "\n");
+ for (int n = 0; n < area_count; n++) {
+ Com.Printf("%4i : %25s", new Vargs().add(n).add(list[n].classname));
+ }
+ */
+ return area_count;
+ }
+
+ //===========================================================================
+
+ /*
+ =============
+ SV_PointContents
+ =============
+ */
+ public static int SV_PointContents(float[] p) {
+ // TODO: make static instead new
+ edict_t touch[] = new edict_t[MAX_EDICTS], hit;
+ int i, num;
+ int contents, c2;
+ int headnode;
+ float angles[];
+
+ // get base contents from world
+ contents = CM.PointContents(p, sv.models[1].headnode);
+
+ // or in contents from all the other entities
+ num = SV_AreaEdicts(p, p, touch, MAX_EDICTS, AREA_SOLID);
+
+ for (i = 0; i < num; i++) {
+ hit = touch[i];
+
+ // might intersect, so do an exact clip
+ headnode = SV_HullForEntity(hit);
+ angles = hit.s.angles;
+ if (hit.solid != SOLID_BSP)
+ angles = vec3_origin; // boxes don't rotate
+
+ c2 = CM.TransformedPointContents(p, headnode, hit.s.origin, hit.s.angles);
+
+ contents |= c2;
+ }
+
+ return contents;
+ }
+
+ /*
+ ================
+ SV_HullForEntity
+
+ Returns a headnode that can be used for testing or clipping an
+ object of mins/maxs size.
+ Offset is filled in to contain the adjustment that must be added to the
+ testing object's origin to get a point to use with the returned hull.
+ ================
+ */
+ public static int SV_HullForEntity(edict_t ent) {
+
+ cmodel_t model;
+
+ // decide which clipping hull to use, based on the size
+ if (ent.solid == SOLID_BSP) { // explicit hulls in the BSP model
+ model = sv.models[ent.s.modelindex];
+
+ if (null == model)
+ Com.Error(ERR_FATAL, "MOVETYPE_PUSH with a non bsp model");
+
+ return model.headnode;
+ }
+
+ // create a temp hull from bounding box sizes
+
+ return CM.HeadnodeForBox(ent.mins, ent.maxs);
+ }
+
+ //===========================================================================
+
+ /*
+ ====================
+ SV_ClipMoveToEntities
+
+ ====================
+ */
+ public static void SV_ClipMoveToEntities(moveclip_t clip) {
+ int i, num;
+ // TODO: make static instead of new
+ edict_t touchlist[] = new edict_t[MAX_EDICTS];
+ edict_t touch;
+ trace_t trace;
+ int headnode;
+ float angles[];
+
+ num = SV_AreaEdicts(clip.boxmins, clip.boxmaxs, touchlist, MAX_EDICTS, AREA_SOLID);
+
+ // 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++) {
+ touch = touchlist[i];
+ if (touch.solid == SOLID_NOT)
+ continue;
+ if (touch == clip.passedict)
+ continue;
+ if (clip.trace.allsolid)
+ return;
+ if (clip.passedict != null) {
+ if (touch.owner == clip.passedict)
+ continue; // don't clip against own missiles
+ if (clip.passedict.owner == touch)
+ continue; // don't clip against owner
+ }
+
+ if (0 == (clip.contentmask & CONTENTS_DEADMONSTER) && 0 != (touch.svflags & SVF_DEADMONSTER))
+ continue;
+
+ // might intersect, so do an exact clip
+ headnode = SV_HullForEntity(touch);
+ angles = touch.s.angles;
+ if (touch.solid != SOLID_BSP)
+ angles = vec3_origin; // boxes don't rotate
+
+ if ((touch.svflags & SVF_MONSTER) != 0)
+ trace =
+ CM.TransformedBoxTrace(
+ clip.start,
+ clip.end,
+ clip.mins2,
+ clip.maxs2,
+ headnode,
+ clip.contentmask,
+ touch.s.origin,
+ angles);
+ else
+ trace =
+ CM.TransformedBoxTrace(
+ clip.start,
+ clip.end,
+ clip.mins,
+ clip.maxs,
+ headnode,
+ clip.contentmask,
+ touch.s.origin,
+ angles);
+
+ if (trace.allsolid || trace.startsolid || trace.fraction < clip.trace.fraction) {
+ trace.ent = touch;
+ if (clip.trace.startsolid) {
+ clip.trace = trace;
+ clip.trace.startsolid = true;
+ }
+ else
+ clip.trace = trace.getClone();
+ }
+ else if (trace.startsolid)
+ clip.trace.startsolid = true;
+ }
+ }
+
+ /*
+ ==================
+ SV_TraceBounds
+ ==================
+ */
+ public static void SV_TraceBounds(float[] start, float[] mins, float[] maxs, float[] end, float[] boxmins, float[] boxmaxs) {
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (end[i] > start[i]) {
+ boxmins[i] = start[i] + mins[i] - 1;
+ boxmaxs[i] = end[i] + maxs[i] + 1;
+ }
+ else {
+ boxmins[i] = end[i] + mins[i] - 1;
+ boxmaxs[i] = start[i] + maxs[i] + 1;
+ }
+ }
+
+ }
+
+ /*
+ ==================
+ SV_Trace
+
+ Moves the given mins/maxs volume through the world from start to end.
+
+ Passedict and edicts owned by passedict are explicitly not checked.
+
+ ==================
+ */
+ public static trace_t SV_Trace(float[] start, float[] mins, float[] maxs, float[] end, edict_t passedict, int contentmask) {
+ moveclip_t clip = new moveclip_t();
+
+ if (mins == null)
+ mins = vec3_origin;
+ if (maxs == null)
+ maxs = vec3_origin;
+
+ //memset ( clip, 0, sizeof ( moveclip_t ) );
+
+ // clip to world
+ clip.trace = CM.BoxTrace(start, end, mins, maxs, 0, contentmask);
+ clip.trace.ent = ge.edicts[0];
+ if (clip.trace.fraction == 0)
+ return clip.trace; // blocked by the world
+
+ clip.contentmask = contentmask;
+ clip.start = start;
+ clip.end = end;
+ clip.mins = mins;
+ clip.maxs = maxs;
+ clip.passedict = passedict;
+
+ VectorCopy(mins, clip.mins2);
+ VectorCopy(maxs, clip.maxs2);
+
+ // create the bounding box of the entire move
+ SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs);
+
+ // clip to other solid entities
+ SV_ClipMoveToEntities(clip);
+
+ return clip.trace;
+ }
+}