/** Turns to the movement direction, and walks the current distance if facing it. */ public static boolean SV_StepDirection(edict_t ent, float yaw, float dist) { float[] move = {0, 0, 0}; float[] oldorigin = {0, 0, 0}; float delta; ent.ideal_yaw = yaw; M.M_ChangeYaw(ent); yaw = (float) (yaw * Math.PI * 2 / 360); move[0] = (float) Math.cos(yaw) * dist; move[1] = (float) Math.sin(yaw) * dist; move[2] = 0; Math3D.VectorCopy(ent.s.origin, oldorigin); if (SV_movestep(ent, move, false)) { delta = ent.s.angles[Defines.YAW] - ent.ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't // take the step Math3D.VectorCopy(oldorigin, ent.s.origin); } GameBase.gi.linkentity(ent); GameBase.G_TouchTriggers(ent); return true; } GameBase.gi.linkentity(ent); GameBase.G_TouchTriggers(ent); return false; }
/** A moving object that doesn't obey physics. */ public static void SV_Physics_Noclip(edict_t ent) { // regular thinking if (!SV_RunThink(ent)) return; Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity, ent.s.angles); Math3D.VectorMA(ent.s.origin, Defines.FRAMETIME, ent.velocity, ent.s.origin); GameBase.gi.linkentity(ent); }
/** GL_DrawParticles */ void GL_DrawParticles(int num_particles) { float origin_x, origin_y, origin_z; Math3D.VectorScale(vup, 1.5f, up); Math3D.VectorScale(vright, 1.5f, right); GL_Bind(r_particletexture.texnum); GL11.glDepthMask(false); // no z buffering GL11.glEnable(GL11.GL_BLEND); GL_TexEnv(GL11.GL_MODULATE); GL11.glBegin(GL11.GL_TRIANGLES); FloatBuffer sourceVertices = particle_t.vertexArray; IntBuffer sourceColors = particle_t.colorArray; float scale; int color; for (int j = 0, i = 0; i < num_particles; i++) { origin_x = sourceVertices.get(j++); origin_y = sourceVertices.get(j++); origin_z = sourceVertices.get(j++); // hack a scale up to keep particles from disapearing scale = (origin_x - r_origin[0]) * vpn[0] + (origin_y - r_origin[1]) * vpn[1] + (origin_z - r_origin[2]) * vpn[2]; scale = (scale < 20) ? 1 : 1 + scale * 0.004f; color = sourceColors.get(i); GL11.glColor4ub( (byte) ((color) & 0xFF), (byte) ((color >> 8) & 0xFF), (byte) ((color >> 16) & 0xFF), (byte) ((color >>> 24))); // first vertex GL11.glTexCoord2f(0.0625f, 0.0625f); GL11.glVertex3f(origin_x, origin_y, origin_z); // second vertex GL11.glTexCoord2f(1.0625f, 0.0625f); GL11.glVertex3f(origin_x + up[0] * scale, origin_y + up[1] * scale, origin_z + up[2] * scale); // third vertex GL11.glTexCoord2f(0.0625f, 1.0625f); GL11.glVertex3f( origin_x + right[0] * scale, origin_y + right[1] * scale, origin_z + right[2] * scale); } GL11.glEnd(); GL11.glDisable(GL11.GL_BLEND); GL11.glColor4f(1, 1, 1, 1); GL11.glDepthMask(true); // back to normal Z buffering GL_TexEnv(GL11.GL_REPLACE); }
/** R_SetupFrame */ void R_SetupFrame() { r_framecount++; // build the transformation matrix for the given view angles Math3D.VectorCopy(r_newrefdef.vieworg, r_origin); Math3D.AngleVectors(r_newrefdef.viewangles, vpn, vright, vup); // current viewcluster mleaf_t leaf; if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) == 0) { r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; leaf = Mod_PointInLeaf(r_origin, r_worldmodel); r_viewcluster = r_viewcluster2 = leaf.cluster; // check above and below so crossing solid water doesn't draw wrong if (leaf.contents == 0) { // look down a bit Math3D.VectorCopy(r_origin, temp); temp[2] -= 16; leaf = Mod_PointInLeaf(temp, r_worldmodel); if ((leaf.contents & Defines.CONTENTS_SOLID) == 0 && (leaf.cluster != r_viewcluster2)) r_viewcluster2 = leaf.cluster; } else { // look up a bit Math3D.VectorCopy(r_origin, temp); temp[2] += 16; leaf = Mod_PointInLeaf(temp, r_worldmodel); if ((leaf.contents & Defines.CONTENTS_SOLID) == 0 && (leaf.cluster != r_viewcluster2)) r_viewcluster2 = leaf.cluster; } } for (int i = 0; i < 4; i++) v_blend[i] = r_newrefdef.blend[i]; c_brush_polys = 0; c_alias_polys = 0; // clear out the portion of the screen that the NOWORLDMODEL defines if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0) { GL11.glEnable(GL11.GL_SCISSOR_TEST); GL11.glClearColor(0.3f, 0.3f, 0.3f, 1.0f); GL11.glScissor( r_newrefdef.x, vid.height - r_newrefdef.height - r_newrefdef.y, r_newrefdef.width, r_newrefdef.height); GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GL11.glClearColor(1.0f, 0.0f, 0.5f, 0.5f); GL11.glDisable(GL11.GL_SCISSOR_TEST); } }
/** R_DrawSpriteModel */ void R_DrawSpriteModel(entity_t e) { float alpha = 1.0F; qfiles.dsprframe_t frame; qfiles.dsprite_t psprite; // don't even bother culling, because it's just a single // polygon without a surface cache psprite = (qfiles.dsprite_t) currentmodel.extradata; e.frame %= psprite.numframes; frame = psprite.frames[e.frame]; if ((e.flags & Defines.RF_TRANSLUCENT) != 0) alpha = e.alpha; if (alpha != 1.0F) GL11.glEnable(GL11.GL_BLEND); GL11.glColor4f(1, 1, 1, alpha); GL_Bind(currentmodel.skins[e.frame].texnum); GL_TexEnv(GL11.GL_MODULATE); if (alpha == 1.0) GL11.glEnable(GL11.GL_ALPHA_TEST); else GL11.glDisable(GL11.GL_ALPHA_TEST); GL11.glBegin(GL11.GL_QUADS); GL11.glTexCoord2f(0, 1); Math3D.VectorMA(e.origin, -frame.origin_y, vup, point); Math3D.VectorMA(point, -frame.origin_x, vright, point); GL11.glVertex3f(point[0], point[1], point[2]); GL11.glTexCoord2f(0, 0); Math3D.VectorMA(e.origin, frame.height - frame.origin_y, vup, point); Math3D.VectorMA(point, -frame.origin_x, vright, point); GL11.glVertex3f(point[0], point[1], point[2]); GL11.glTexCoord2f(1, 0); Math3D.VectorMA(e.origin, frame.height - frame.origin_y, vup, point); Math3D.VectorMA(point, frame.width - frame.origin_x, vright, point); GL11.glVertex3f(point[0], point[1], point[2]); GL11.glTexCoord2f(1, 1); Math3D.VectorMA(e.origin, -frame.origin_y, vup, point); Math3D.VectorMA(point, frame.width - frame.origin_x, vright, point); GL11.glVertex3f(point[0], point[1], point[2]); GL11.glEnd(); GL11.glDisable(GL11.GL_ALPHA_TEST); GL_TexEnv(GL11.GL_REPLACE); if (alpha != 1.0F) GL11.glDisable(GL11.GL_BLEND); GL11.glColor4f(1, 1, 1, 1); }
/** R_SetFrustum */ void R_SetFrustum() { // rotate VPN right by FOV_X/2 degrees Math3D.RotatePointAroundVector(frustum[0].normal, vup, vpn, -(90f - r_newrefdef.fov_x / 2f)); // rotate VPN left by FOV_X/2 degrees Math3D.RotatePointAroundVector(frustum[1].normal, vup, vpn, 90f - r_newrefdef.fov_x / 2f); // rotate VPN up by FOV_X/2 degrees Math3D.RotatePointAroundVector(frustum[2].normal, vright, vpn, 90f - r_newrefdef.fov_y / 2f); // rotate VPN down by FOV_X/2 degrees Math3D.RotatePointAroundVector(frustum[3].normal, vright, vpn, -(90f - r_newrefdef.fov_y / 2f)); for (int i = 0; i < 4; i++) { frustum[i].type = Defines.PLANE_ANYZ; frustum[i].dist = Math3D.DotProduct(r_origin, frustum[i].normal); frustum[i].signbits = (byte) SignbitsForPlane(frustum[i]); } }
/** Bmodel objects don't interact with each other, but push all box objects. */ public static void SV_Physics_Pusher(edict_t ent) { float[] move = {0, 0, 0}; float[] amove = {0, 0, 0}; edict_t part, mv; // if not a team captain, so movement will be handled elsewhere if ((ent.flags & Defines.FL_TEAMSLAVE) != 0) return; // make sure all team slaves can move before commiting // any moves or calling any think functions // if the move is blocked, all moved objects will be backed out // retry: GameBase.pushed_p = 0; for (part = ent; part != null; part = part.teamchain) { if (part.velocity[0] != 0 || part.velocity[1] != 0 || part.velocity[2] != 0 || part.avelocity[0] != 0 || part.avelocity[1] != 0 || part.avelocity[2] != 0) { // object // is // moving Math3D.VectorScale(part.velocity, Defines.FRAMETIME, move); Math3D.VectorScale(part.avelocity, Defines.FRAMETIME, amove); if (!SV_Push(part, move, amove)) break; // move was blocked } } if (GameBase.pushed_p > Defines.MAX_EDICTS) SV_GAME.PF_error(Defines.ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted"); if (part != null) { // the move failed, bump all nextthink times and back out moves for (mv = ent; mv != null; mv = mv.teamchain) { if (mv.nextthink > 0) mv.nextthink += Defines.FRAMETIME; } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (part.blocked != null) part.blocked.blocked(part, GameBase.obstacle); } else { // the move succeeded, so call all think functions for (part = ent; part != null; part = part.teamchain) { SV_RunThink(part); } } }
/** R_CullBox Returns true if the box is completely outside the frustum */ final boolean R_CullBox(float[] mins, float[] maxs) { assert (mins.length == 3 && maxs.length == 3) : "vec3_t bug"; if (r_nocull.value != 0) return false; for (int i = 0; i < 4; i++) { if (Math3D.BoxOnPlaneSide(mins, maxs, frustum[i]) == 2) return true; } return false; }
/** Does not change the entities velocity at all */ public static trace_t SV_PushEntity(edict_t ent, float[] push) { trace_t trace; float[] start = {0, 0, 0}; float[] end = {0, 0, 0}; int mask; Math3D.VectorCopy(ent.s.origin, start); Math3D.VectorAdd(start, push, end); // FIXME: test this // a goto statement was replaced. boolean retry = false; do { if (ent.clipmask != 0) mask = ent.clipmask; else mask = Defines.MASK_SOLID; trace = GameBase.gi.trace(start, ent.mins, ent.maxs, end, ent, mask); Math3D.VectorCopy(trace.endpos, ent.s.origin); GameBase.gi.linkentity(ent); retry = false; if (trace.fraction != 1.0) { SV_Impact(ent, trace); // if the pushed entity went away and the pusher is still there if (!trace.ent.inuse && ent.inuse) { // move the pusher back and try again Math3D.VectorCopy(start, ent.s.origin); GameBase.gi.linkentity(ent); // goto retry; retry = true; } } } while (retry); if (ent.inuse) GameBase.G_TouchTriggers(ent); return trace; }
/** R_DrawBeam */ void R_DrawBeam(entity_t e) { oldorigin[0] = e.oldorigin[0]; oldorigin[1] = e.oldorigin[1]; oldorigin[2] = e.oldorigin[2]; origin[0] = e.origin[0]; origin[1] = e.origin[1]; origin[2] = e.origin[2]; normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; if (Math3D.VectorNormalize(normalized_direction) == 0.0f) return; Math3D.PerpendicularVector(perpvec, normalized_direction); Math3D.VectorScale(perpvec, e.frame / 2, perpvec); for (int i = 0; i < 6; i++) { Math3D.RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0f / NUM_BEAM_SEGS) * i); Math3D.VectorAdd(start_points[i], origin, start_points[i]); Math3D.VectorAdd(start_points[i], direction, end_points[i]); } GL11.glDisable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_BLEND); GL11.glDepthMask(false); float r = (d_8to24table[e.skinnum & 0xFF]) & 0xFF; float g = (d_8to24table[e.skinnum & 0xFF] >> 8) & 0xFF; float b = (d_8to24table[e.skinnum & 0xFF] >> 16) & 0xFF; r *= 1 / 255.0f; g *= 1 / 255.0f; b *= 1 / 255.0f; GL11.glColor4f(r, g, b, e.alpha); GL11.glBegin(GL11.GL_TRIANGLE_STRIP); float[] v; for (int i = 0; i < NUM_BEAM_SEGS; i++) { v = start_points[i]; GL11.glVertex3f(v[0], v[1], v[2]); v = end_points[i]; GL11.glVertex3f(v[0], v[1], v[2]); v = start_points[(i + 1) % NUM_BEAM_SEGS]; GL11.glVertex3f(v[0], v[1], v[2]); v = end_points[(i + 1) % NUM_BEAM_SEGS]; GL11.glVertex3f(v[0], v[1], v[2]); } GL11.glEnd(); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_BLEND); GL11.glDepthMask(true); }
// FIXME: hacked in for E3 demo public static void SV_AddRotationalFriction(edict_t ent) { int n; float adjustment; Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity, ent.s.angles); adjustment = Defines.FRAMETIME * Defines.sv_stopspeed * Defines.sv_friction; for (n = 0; n < 3; n++) { if (ent.avelocity[n] > 0) { ent.avelocity[n] -= adjustment; if (ent.avelocity[n] < 0) ent.avelocity[n] = 0; } else { ent.avelocity[n] += adjustment; if (ent.avelocity[n] > 0) ent.avelocity[n] = 0; } } }
public static void SV_NewChaseDir(edict_t actor, edict_t enemy, float dist) { float deltax, deltay; float d[] = {0, 0, 0}; float tdir, olddir, turnaround; // FIXME: how did we get here with no enemy if (enemy == null) { Com.DPrintf("SV_NewChaseDir without enemy!\n"); return; } olddir = Math3D.anglemod((int) (actor.ideal_yaw / 45) * 45); turnaround = Math3D.anglemod(olddir - 180); deltax = enemy.s.origin[0] - actor.s.origin[0]; deltay = enemy.s.origin[1] - actor.s.origin[1]; if (deltax > 10) d[1] = 0; else if (deltax < -10) d[1] = 180; else d[1] = DI_NODIR; if (deltay < -10) d[2] = 270; else if (deltay > 10) d[2] = 90; else d[2] = DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if (((Lib.rand() & 3) & 1) != 0 || Math.abs(deltay) > Math.abs(deltax)) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; } if (d[1] != DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2] != DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if ((Lib.rand() & 1) != 0) /* randomly determine direction of search */ { for (tdir = 0; tdir <= 315; tdir += 45) if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } else { for (tdir = 315; tdir >= 0; tdir -= 45) if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist)) return; actor.ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!M.M_CheckBottom(actor)) SV_FixCheckBottom(actor); }
// FIXME: since we need to test end position contents here, can we avoid // doing it again later in catagorize position? public static boolean SV_movestep(edict_t ent, float[] move, boolean relink) { float dz; float[] oldorg = {0, 0, 0}; float[] neworg = {0, 0, 0}; float[] end = {0, 0, 0}; trace_t trace = null; // = new trace_t(); int i; float stepsize; float[] test = {0, 0, 0}; int contents; // try the move Math3D.VectorCopy(ent.s.origin, oldorg); Math3D.VectorAdd(ent.s.origin, move, neworg); // flying monsters don't step up if ((ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)) != 0) { // try one move with vertical motion, then one without for (i = 0; i < 2; i++) { Math3D.VectorAdd(ent.s.origin, move, neworg); if (i == 0 && ent.enemy != null) { if (ent.goalentity == null) ent.goalentity = ent.enemy; dz = ent.s.origin[2] - ent.goalentity.s.origin[2]; if (ent.goalentity.client != null) { if (dz > 40) neworg[2] -= 8; if (!((ent.flags & Defines.FL_SWIM) != 0 && (ent.waterlevel < 2))) if (dz < 30) neworg[2] += 8; } else { if (dz > 8) neworg[2] -= 8; else if (dz > 0) neworg[2] -= dz; else if (dz < -8) neworg[2] += 8; else neworg[2] += dz; } } trace = GameBase.gi.trace( ent.s.origin, ent.mins, ent.maxs, neworg, ent, Defines.MASK_MONSTERSOLID); // fly monsters don't enter water voluntarily if ((ent.flags & Defines.FL_FLY) != 0) { if (ent.waterlevel == 0) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent.mins[2] + 1; contents = GameBase.gi.pointcontents.pointcontents(test); if ((contents & Defines.MASK_WATER) != 0) return false; } } // swim monsters don't exit water voluntarily if ((ent.flags & Defines.FL_SWIM) != 0) { if (ent.waterlevel < 2) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent.mins[2] + 1; contents = GameBase.gi.pointcontents.pointcontents(test); if ((contents & Defines.MASK_WATER) == 0) return false; } } if (trace.fraction == 1) { Math3D.VectorCopy(trace.endpos, ent.s.origin); if (relink) { GameBase.gi.linkentity(ent); GameBase.G_TouchTriggers(ent); } return true; } if (ent.enemy == null) break; } return false; } // push down from a step height above the wished position if ((ent.monsterinfo.aiflags & Defines.AI_NOSTEP) == 0) stepsize = GameBase.STEPSIZE; else stepsize = 1; neworg[2] += stepsize; Math3D.VectorCopy(neworg, end); end[2] -= stepsize * 2; trace = GameBase.gi.trace(neworg, ent.mins, ent.maxs, end, ent, Defines.MASK_MONSTERSOLID); if (trace.allsolid) return false; if (trace.startsolid) { neworg[2] -= stepsize; trace = GameBase.gi.trace(neworg, ent.mins, ent.maxs, end, ent, Defines.MASK_MONSTERSOLID); if (trace.allsolid || trace.startsolid) return false; } // don't go in to water if (ent.waterlevel == 0) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent.mins[2] + 1; contents = GameBase.gi.pointcontents.pointcontents(test); if ((contents & Defines.MASK_WATER) != 0) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ((ent.flags & Defines.FL_PARTIALGROUND) != 0) { Math3D.VectorAdd(ent.s.origin, move, ent.s.origin); if (relink) { GameBase.gi.linkentity(ent); GameBase.G_TouchTriggers(ent); } ent.groundentity = null; return true; } return false; // walked off an edge } // check point traces down for dangling corners Math3D.VectorCopy(trace.endpos, ent.s.origin); if (!M.M_CheckBottom(ent)) { if ((ent.flags & Defines.FL_PARTIALGROUND) != 0) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) { GameBase.gi.linkentity(ent); GameBase.G_TouchTriggers(ent); } return true; } Math3D.VectorCopy(oldorg, ent.s.origin); return false; } if ((ent.flags & Defines.FL_PARTIALGROUND) != 0) { ent.flags &= ~Defines.FL_PARTIALGROUND; } ent.groundentity = trace.ent; ent.groundentity_linkcount = trace.ent.linkcount; // the move is ok if (relink) { GameBase.gi.linkentity(ent); GameBase.G_TouchTriggers(ent); } return true; }
/** Toss, bounce, and fly movement. When onground, do nothing. */ public static void SV_Physics_Toss(edict_t ent) { trace_t trace; float[] move = {0, 0, 0}; float backoff; edict_t slave; boolean wasinwater; boolean isinwater; float[] old_origin = {0, 0, 0}; // regular thinking SV_RunThink(ent); // if not a team captain, so movement will be handled elsewhere if ((ent.flags & Defines.FL_TEAMSLAVE) != 0) return; if (ent.velocity[2] > 0) ent.groundentity = null; // check for the groundentity going away if (ent.groundentity != null) if (!ent.groundentity.inuse) ent.groundentity = null; // if onground, return without moving if (ent.groundentity != null) return; Math3D.VectorCopy(ent.s.origin, old_origin); SV_CheckVelocity(ent); // add gravity if (ent.movetype != Defines.MOVETYPE_FLY && ent.movetype != Defines.MOVETYPE_FLYMISSILE) SV_AddGravity(ent); // move angles Math3D.VectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity, ent.s.angles); // move origin Math3D.VectorScale(ent.velocity, Defines.FRAMETIME, move); trace = SV_PushEntity(ent, move); if (!ent.inuse) return; if (trace.fraction < 1) { if (ent.movetype == Defines.MOVETYPE_BOUNCE) backoff = 1.5f; else backoff = 1; GameBase.ClipVelocity(ent.velocity, trace.plane.normal, ent.velocity, backoff); // stop if on ground if (trace.plane.normal[2] > 0.7) { if (ent.velocity[2] < 60 || ent.movetype != Defines.MOVETYPE_BOUNCE) { ent.groundentity = trace.ent; ent.groundentity_linkcount = trace.ent.linkcount; Math3D.VectorCopy(Globals.vec3_origin, ent.velocity); Math3D.VectorCopy(Globals.vec3_origin, ent.avelocity); } } // if (ent.touch) // ent.touch (ent, trace.ent, &trace.plane, trace.surface); } // check for water transition wasinwater = (ent.watertype & Defines.MASK_WATER) != 0; ent.watertype = GameBase.gi.pointcontents.pointcontents(ent.s.origin); isinwater = (ent.watertype & Defines.MASK_WATER) != 0; if (isinwater) ent.waterlevel = 1; else ent.waterlevel = 0; if (!wasinwater && isinwater) GameBase.gi.positioned_sound( old_origin, ent, Defines.CHAN_AUTO, GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); else if (wasinwater && !isinwater) GameBase.gi.positioned_sound( ent.s.origin, ent, Defines.CHAN_AUTO, GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); // move teamslaves for (slave = ent.teamchain; slave != null; slave = slave.teamchain) { Math3D.VectorCopy(ent.s.origin, slave.s.origin); GameBase.gi.linkentity(slave); } }
/** Objects need to be moved back on a failed push, otherwise riders would continue to slide. */ public static boolean SV_Push(edict_t pusher, float[] move, float[] amove) { int i, e; edict_t check, block[]; float[] mins = {0, 0, 0}; float[] maxs = {0, 0, 0}; pushed_t p; float[] org = {0, 0, 0}; float[] org2 = {0, 0, 0}; float[] move2 = {0, 0, 0}; float[] forward = {0, 0, 0}; float[] right = {0, 0, 0}; float[] up = {0, 0, 0}; // clamp the move to 1/8 units, so the position will // be accurate for client side prediction for (i = 0; i < 3; i++) { float temp; temp = move[i] * 8.0f; if (temp > 0.0) temp += 0.5; else temp -= 0.5; move[i] = 0.125f * (int) temp; } // find the bounding box for (i = 0; i < 3; i++) { mins[i] = pusher.absmin[i] + move[i]; maxs[i] = pusher.absmax[i] + move[i]; } // we need this for pushing things later Math3D.VectorSubtract(Globals.vec3_origin, amove, org); Math3D.AngleVectors(org, forward, right, up); // save the pusher's original position GameBase.pushed[GameBase.pushed_p].ent = pusher; Math3D.VectorCopy(pusher.s.origin, GameBase.pushed[GameBase.pushed_p].origin); Math3D.VectorCopy(pusher.s.angles, GameBase.pushed[GameBase.pushed_p].angles); if (pusher.client != null) GameBase.pushed[GameBase.pushed_p].deltayaw = pusher.client.ps.pmove.delta_angles[Defines.YAW]; GameBase.pushed_p++; // move the pusher to it's final position Math3D.VectorAdd(pusher.s.origin, move, pusher.s.origin); Math3D.VectorAdd(pusher.s.angles, amove, pusher.s.angles); GameBase.gi.linkentity(pusher); // see if any solid entities are inside the final position // check= g_edicts + 1; for (e = 1; e < GameBase.num_edicts; e++) { check = GameBase.g_edicts[e]; if (!check.inuse) continue; if (check.movetype == Defines.MOVETYPE_PUSH || check.movetype == Defines.MOVETYPE_STOP || check.movetype == Defines.MOVETYPE_NONE || check.movetype == Defines.MOVETYPE_NOCLIP) continue; if (check.area.prev == null) continue; // not linked in anywhere // if the entity is standing on the pusher, it will definitely be // moved if (check.groundentity != pusher) { // see if the ent needs to be tested if (check.absmin[0] >= maxs[0] || check.absmin[1] >= maxs[1] || check.absmin[2] >= maxs[2] || check.absmax[0] <= mins[0] || check.absmax[1] <= mins[1] || check.absmax[2] <= mins[2]) continue; // see if the ent's bbox is inside the pusher's final position if (SV_TestEntityPosition(check) == null) continue; } if ((pusher.movetype == Defines.MOVETYPE_PUSH) || (check.groundentity == pusher)) { // move this entity GameBase.pushed[GameBase.pushed_p].ent = check; Math3D.VectorCopy(check.s.origin, GameBase.pushed[GameBase.pushed_p].origin); Math3D.VectorCopy(check.s.angles, GameBase.pushed[GameBase.pushed_p].angles); GameBase.pushed_p++; // try moving the contacted entity Math3D.VectorAdd(check.s.origin, move, check.s.origin); if (check.client != null) { // FIXME: doesn't rotate monsters? check.client.ps.pmove.delta_angles[Defines.YAW] += amove[Defines.YAW]; } // figure movement due to the pusher's amove Math3D.VectorSubtract(check.s.origin, pusher.s.origin, org); org2[0] = Math3D.DotProduct(org, forward); org2[1] = -Math3D.DotProduct(org, right); org2[2] = Math3D.DotProduct(org, up); Math3D.VectorSubtract(org2, org, move2); Math3D.VectorAdd(check.s.origin, move2, check.s.origin); // may have pushed them off an edge if (check.groundentity != pusher) check.groundentity = null; block = SV_TestEntityPosition(check); if (block == null) { // pushed ok GameBase.gi.linkentity(check); // impact? continue; } // if it is ok to leave in the old position, do it // this is only relevent for riding entities, not pushed // FIXME: this doesn't acount for rotation Math3D.VectorSubtract(check.s.origin, move, check.s.origin); block = SV_TestEntityPosition(check); if (block == null) { GameBase.pushed_p--; continue; } } // save off the obstacle so we can call the block function GameBase.obstacle = check; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for (int ip = GameBase.pushed_p - 1; ip >= 0; ip--) { p = GameBase.pushed[ip]; Math3D.VectorCopy(p.origin, p.ent.s.origin); Math3D.VectorCopy(p.angles, p.ent.s.angles); if (p.ent.client != null) { p.ent.client.ps.pmove.delta_angles[Defines.YAW] = (short) p.deltayaw; } GameBase.gi.linkentity(p.ent); } return false; } // FIXME: is there a better way to handle this? // see if anything we moved has touched a trigger for (int ip = GameBase.pushed_p - 1; ip >= 0; ip--) GameBase.G_TouchTriggers(GameBase.pushed[ip].ent); return true; }
public static int SV_FlyMove(edict_t ent, float time, int mask) { edict_t hit; int bumpcount, numbumps; float[] dir = {0.0f, 0.0f, 0.0f}; float d; int numplanes; float[][] planes = new float[MAX_CLIP_PLANES][3]; float[] primal_velocity = {0.0f, 0.0f, 0.0f}; float[] original_velocity = {0.0f, 0.0f, 0.0f}; float[] new_velocity = {0.0f, 0.0f, 0.0f}; int i, j; trace_t trace; float[] end = {0.0f, 0.0f, 0.0f}; float time_left; int blocked; numbumps = 4; blocked = 0; Math3D.VectorCopy(ent.velocity, original_velocity); Math3D.VectorCopy(ent.velocity, primal_velocity); numplanes = 0; time_left = time; ent.groundentity = null; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { for (i = 0; i < 3; i++) end[i] = ent.s.origin[i] + time_left * ent.velocity[i]; trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, end, ent, mask); if (trace.allsolid) { // entity is trapped in another solid Math3D.VectorCopy(Globals.vec3_origin, ent.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance Math3D.VectorCopy(trace.endpos, ent.s.origin); Math3D.VectorCopy(ent.velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance hit = trace.ent; if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor if (hit.solid == Defines.SOLID_BSP) { ent.groundentity = hit; ent.groundentity_linkcount = hit.linkcount; } } if (trace.plane.normal[2] == 0.0f) { blocked |= 2; // step } // // run the impact function // SV_Impact(ent, trace); if (!ent.inuse) break; // removed by the impact function time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't // really happen Math3D.VectorCopy(Globals.vec3_origin, ent.velocity); return 3; } Math3D.VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i = 0; i < numplanes; i++) { GameBase.ClipVelocity(original_velocity, planes[i], new_velocity, 1); for (j = 0; j < numplanes; j++) if ((j != i) && !Math3D.VectorEquals(planes[i], planes[j])) { if (Math3D.DotProduct(new_velocity, planes[j]) < 0) break; // not ok } if (j == numplanes) break; } if (i != numplanes) { // go along this plane Math3D.VectorCopy(new_velocity, ent.velocity); } else { // go along the crease if (numplanes != 2) { // gi.dprintf ("clip velocity, numplanes == // %i\n",numplanes); Math3D.VectorCopy(Globals.vec3_origin, ent.velocity); return 7; } Math3D.CrossProduct(planes[0], planes[1], dir); d = Math3D.DotProduct(dir, ent.velocity); Math3D.VectorScale(dir, d, ent.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (Math3D.DotProduct(ent.velocity, primal_velocity) <= 0) { Math3D.VectorCopy(Globals.vec3_origin, ent.velocity); return blocked; } } return blocked; }