diff options
Diffstat (limited to 'src/jake2/server/SV_WORLD.java')
-rw-r--r-- | src/jake2/server/SV_WORLD.java | 654 |
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; + } +} |