public static int BoxOnPlaneSide(ref v3f emins, ref v3f emaxs, mplane_t p) { float mindist, maxdist; switch (p.type) { case 0: mindist = emins.x; maxdist = emaxs.x; break; case 1: mindist = emins.y; maxdist = emaxs.y; break; case 2: mindist = emins.z; maxdist = emaxs.z; break; default: Vector3 mins, maxs; Copy(ref emins, out mins); Copy(ref emaxs, out maxs); return _BoxOnPlaneSide(ref mins, ref maxs, p); } return (p.dist <= mindist ? 1 : (p.dist >= maxdist ? 2 : 3)); }
/// <summary> /// c = a + b * scale; /// </summary> public static void VectorMA(ref v3f a, float scale, ref v3f b, out v3f c) { c.x = a.x + b.x * scale; c.y = a.y + b.y * scale; c.z = a.z + b.z * scale; }
/// <summary> /// SV_WalkMove /// Only used by players /// </summary> static void WalkMove(edict_t ent) { // // do a regular slide move unless it looks like you ran into a step // int oldonground = (int)ent.v.flags & EdictFlags.FL_ONGROUND; ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_ONGROUND; v3f oldorg = ent.v.origin; v3f oldvel = ent.v.velocity; trace_t steptrace = new trace_t(); int clip = FlyMove(ent, (float)Host.FrameTime, steptrace); if ((clip & 2) == 0) { return; // move didn't block on a step } if (oldonground == 0 && ent.v.waterlevel == 0) { return; // don't stair up while jumping } if (ent.v.movetype != Movetypes.MOVETYPE_WALK) { return; // gibbed by a trigger } if (_NoStep.Value != 0) { return; } if (((int)_Player.v.flags & EdictFlags.FL_WATERJUMP) != 0) { return; } v3f nosteporg = ent.v.origin; v3f nostepvel = ent.v.velocity; // // try moving up and forward to go up a step // ent.v.origin = oldorg; // back to start pos v3f upmove = Common.ZeroVector3f; v3f downmove = upmove; upmove.z = STEPSIZE; downmove.z = (float)(-STEPSIZE + oldvel.z * Host.FrameTime); // move up PushEntity(ent, ref upmove); // FIXME: don't link? // move forward ent.v.velocity.x = oldvel.x; ent.v.velocity.y = oldvel.y; ent.v.velocity.z = 0; clip = FlyMove(ent, (float)Host.FrameTime, steptrace); // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls if (clip != 0) { if (Math.Abs(oldorg.y - ent.v.origin.y) < 0.03125 && Math.Abs(oldorg.x - ent.v.origin.x) < 0.03125) { // stepping up didn't make any progress clip = TryUnstick(ent, ref oldvel); } } // extra friction based on view angle if ((clip & 2) != 0) { WallFriction(ent, steptrace); } // move down trace_t downtrace = PushEntity(ent, ref downmove); // FIXME: don't link? if (downtrace.plane.normal.Z > 0.7) { if (ent.v.solid == Solids.SOLID_BSP) { ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND; ent.v.groundentity = EdictToProg(downtrace.ent); } } else { // if the push down didn't end up on good ground, use the move without // the step up. This happens near wall / slope combinations, and can // cause the player to hop up higher on a slope too steep to climb ent.v.origin = nosteporg; ent.v.velocity = nostepvel; } }
public static float LengthXY(ref v3f v) { return((float)Math.Sqrt(v.x * v.x + v.y * v.y)); }
public static void VectorSubtract(ref v3f a, ref v3f b, out v3f c) { c.x = a.x - b.x; c.y = a.y - b.y; c.z = a.z - b.z; }
public static float Normalize(ref v3f v) { float length = (float)Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z); if (length != 0) { float ool = 1 / length; v.x *= ool; v.y *= ool; v.z *= ool; } return length; }
public static void Copy(ref v3f src, out Vector3 dest) { dest.X = src.x; dest.Y = src.y; dest.Z = src.z; }
/// <summary> /// SV_PushMove /// </summary> static void PushMove(edict_t pusher, float movetime) { if (pusher.v.velocity.IsEmpty) { pusher.v.ltime += movetime; return; } v3f move, mins, maxs; Mathlib.VectorScale(ref pusher.v.velocity, movetime, out move); Mathlib.VectorAdd(ref pusher.v.absmin, ref move, out mins); Mathlib.VectorAdd(ref pusher.v.absmax, ref move, out maxs); v3f pushorig = pusher.v.origin; edict_t[] moved_edict = new edict_t[QDef.MAX_EDICTS]; v3f[] moved_from = new v3f[QDef.MAX_EDICTS]; // move the pusher to it's final position Mathlib.VectorAdd(ref pusher.v.origin, ref move, out pusher.v.origin); pusher.v.ltime += movetime; LinkEdict(pusher, false); // see if any solid entities are inside the final position int num_moved = 0; for (int e = 1; e < sv.num_edicts; e++) { edict_t check = sv.edicts[e]; if (check.free) continue; if (check.v.movetype == Movetypes.MOVETYPE_PUSH || check.v.movetype == Movetypes.MOVETYPE_NONE || check.v.movetype == Movetypes.MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definately be moved if (!(((int)check.v.flags & EdictFlags.FL_ONGROUND) != 0 && ProgToEdict(check.v.groundentity) == pusher)) { if (check.v.absmin.x >= maxs.x || check.v.absmin.y >= maxs.y || check.v.absmin.z >= maxs.z || check.v.absmax.x <= mins.x || check.v.absmax.y <= mins.y || check.v.absmax.z <= mins.z) continue; // see if the ent's bbox is inside the pusher's final position if (TestEntityPosition(check) == null) continue; } // remove the onground flag for non-players if (check.v.movetype != Movetypes.MOVETYPE_WALK) check.v.flags = (int)check.v.flags & ~EdictFlags.FL_ONGROUND; v3f entorig = check.v.origin; moved_from[num_moved] = entorig; moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity pusher.v.solid = Solids.SOLID_NOT; PushEntity(check, ref move); pusher.v.solid = Solids.SOLID_BSP; // if it is still inside the pusher, block edict_t block = TestEntityPosition(check); if (block != null) { // fail the move if (check.v.mins.x == check.v.maxs.x) continue; if (check.v.solid == Solids.SOLID_NOT || check.v.solid == Solids.SOLID_TRIGGER) { // corpse check.v.mins.x = check.v.mins.y = 0; check.v.maxs = check.v.mins; continue; } check.v.origin = entorig; LinkEdict(check, true); pusher.v.origin = pushorig; LinkEdict(pusher, false); pusher.v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher.v.blocked != 0) { Progs.GlobalStruct.self = EdictToProg(pusher); Progs.GlobalStruct.other = EdictToProg(check); Progs.Execute(pusher.v.blocked); } // move back any entities we already moved for (int i = 0; i < num_moved; i++) { moved_edict[i].v.origin = moved_from[i]; LinkEdict(moved_edict[i], false); } return; } } }
/// <summary> /// PushEntity /// Does not change the entities velocity at all /// </summary> private static trace_t PushEntity(edict_t ent, ref v3f push) { v3f end; Mathlib.VectorAdd(ref ent.v.origin, ref push, out end); trace_t trace; if (ent.v.movetype == Movetypes.MOVETYPE_FLYMISSILE) trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, MOVE_MISSILE, ent); else if (ent.v.solid == Solids.SOLID_TRIGGER || ent.v.solid == Solids.SOLID_NOT) // only clip against bmodels trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, MOVE_NOMONSTERS, ent); else trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, MOVE_NORMAL, ent); Mathlib.Copy(ref trace.endpos, out ent.v.origin); LinkEdict(ent, true); if (trace.ent != null) Impact(ent, trace.ent); return trace; }
static trace_t Move(ref v3f start, ref v3f mins, ref v3f maxs, ref v3f end, int type, edict_t passedict) { Vector3 vstart, vmins, vmaxs, vend; Mathlib.Copy(ref start, out vstart); Mathlib.Copy(ref mins, out vmins); Mathlib.Copy(ref maxs, out vmaxs); Mathlib.Copy(ref end, out vend); return Move(ref vstart, ref vmins, ref vmaxs, ref vend, type, passedict); }
/// <summary> /// ClipVelocity /// Slide off of the impacting object /// returns the blocked flags (1 = floor, 2 = step / wall) /// </summary> static int ClipVelocity(ref v3f src, ref Vector3 normal, out v3f dest, float overbounce) { int blocked = 0; if (normal.Z > 0) blocked |= 1; // floor if (normal.Z == 0) blocked |= 2; // step float backoff = (src.x * normal.X + src.y * normal.Y + src.z * normal.Z) * overbounce; dest.x = src.x - normal.X * backoff; dest.y = src.y - normal.Y * backoff; dest.z = src.z - normal.Z * backoff; if (dest.x > -STOP_EPSILON && dest.x < STOP_EPSILON) dest.x = 0; if (dest.y > -STOP_EPSILON && dest.y < STOP_EPSILON) dest.y = 0; if (dest.z > -STOP_EPSILON && dest.z < STOP_EPSILON) dest.z = 0; return blocked; }
/// <summary> /// SV_TryUnstick /// Player has come to a dead stop, possibly due to the problem with limited /// float precision at some angle joins in the BSP hull. /// /// Try fixing by pushing one pixel in each direction. /// /// This is a hack, but in the interest of good gameplay... /// </summary> private static int TryUnstick(edict_t ent, ref v3f oldvel) { v3f oldorg = ent.v.origin; v3f dir = common.ZeroVector3f; trace_t steptrace = new trace_t(); for (int i = 0; i < 8; i++) { // try pushing a little in an axial direction switch (i) { case 0: dir.x = 2; dir.y = 0; break; case 1: dir.x = 0; dir.y = 2; break; case 2: dir.x = -2; dir.y = 0; break; case 3: dir.x = 0; dir.y = -2; break; case 4: dir.x = 2; dir.y = 2; break; case 5: dir.x = -2; dir.y = 2; break; case 6: dir.x = 2; dir.y = -2; break; case 7: dir.x = -2; dir.y = -2; break; } PushEntity(ent, ref dir); // retry the original move ent.v.velocity.x = oldvel.x; ent.v.velocity.y = oldvel.y; ent.v.velocity.z = 0; int clip = FlyMove(ent, 0.1f, steptrace); if (Math.Abs(oldorg.y - ent.v.origin.y) > 4 || Math.Abs(oldorg.x - ent.v.origin.x) > 4) { return(clip); } // go back to the original pos and try again ent.v.origin = oldorg; } ent.v.velocity = common.ZeroVector3f; return(7); // still not moving }
static unsafe void Copy(float* src, ref v3f dest) { dest.x = src[0]; dest.y = src[1]; dest.z = src[2]; }
public static void VectorAdd(ref v3f a, ref v3f b, out v3f c) { c.x = a.x + b.x; c.y = a.y + b.y; c.z = a.z + b.z; }
public static void Clamp(ref v3f src, ref Vector3 min, ref Vector3 max, out v3f dest) { dest.x = Math.Max(Math.Min(src.x, max.X), min.X); dest.y = Math.Max(Math.Min(src.y, max.Y), min.Y); dest.z = Math.Max(Math.Min(src.z, max.Z), min.Z); }
/// <summary> /// SV_TryUnstick /// Player has come to a dead stop, possibly due to the problem with limited /// float precision at some angle joins in the BSP hull. /// /// Try fixing by pushing one pixel in each direction. /// /// This is a hack, but in the interest of good gameplay... /// </summary> static int TryUnstick(edict_t ent, ref v3f oldvel) { v3f oldorg = ent.v.origin; v3f dir = Common.ZeroVector3f; trace_t steptrace = new trace_t(); for (int i = 0; i < 8; i++) { // try pushing a little in an axial direction switch (i) { case 0: dir.x = 2; dir.y = 0; break; case 1: dir.x = 0; dir.y = 2; break; case 2: dir.x = -2; dir.y = 0; break; case 3: dir.x = 0; dir.y = -2; break; case 4: dir.x = 2; dir.y = 2; break; case 5: dir.x = -2; dir.y = 2; break; case 6: dir.x = 2; dir.y = -2; break; case 7: dir.x = -2; dir.y = -2; break; } PushEntity(ent, ref dir); // retry the original move ent.v.velocity.x = oldvel.x; ent.v.velocity.y = oldvel.y; ent.v.velocity.z = 0; int clip = FlyMove(ent, 0.1f, steptrace); if (Math.Abs(oldorg.y - ent.v.origin.y) > 4 || Math.Abs(oldorg.x - ent.v.origin.x) > 4) { return clip; } // go back to the original pos and try again ent.v.origin = oldorg; } ent.v.velocity = Common.ZeroVector3f; return 7; // still not moving }
/// <summary> /// G_VECTOR(OFS_RETURN) = value /// </summary> public static unsafe void ReturnVector(ref v3f value) { float* ptr = (float*)Progs.GlobalStructAddr; ptr[OFS.OFS_RETURN + 0] = value.x; ptr[OFS.OFS_RETURN + 1] = value.y; ptr[OFS.OFS_RETURN + 2] = value.z; }
/// <summary> /// SV_movestep /// Called by monster program code. /// The move will be adjusted for slopes and stairs, but if the move isn't /// possible, no move is done, false is returned, and /// pr_global_struct.trace_normal is set to the normal of the blocking wall /// </summary> public static bool MoveStep(edict_t ent, ref v3f move, bool relink) { trace_t trace; // try the move v3f oldorg = ent.v.origin; v3f neworg; Mathlib.VectorAdd(ref ent.v.origin, ref move, out neworg); // flying monsters don't step up if (((int)ent.v.flags & (EdictFlags.FL_SWIM | EdictFlags.FL_FLY)) != 0) { // try one move with vertical motion, then one without for (int i = 0; i < 2; i++) { Mathlib.VectorAdd(ref ent.v.origin, ref move, out neworg); edict_t enemy = ProgToEdict(ent.v.enemy); if (i == 0 && enemy != sv.edicts[0]) { float dz = ent.v.origin.z - enemy.v.origin.z; if (dz > 40) { neworg.z -= 8; } if (dz < 30) { neworg.z += 8; } } trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref neworg, 0, ent); if (trace.fraction == 1) { if (((int)ent.v.flags & EdictFlags.FL_SWIM) != 0 && PointContents(ref trace.endpos) == Contents.CONTENTS_EMPTY) { return(false); // swim monster left water } Mathlib.Copy(ref trace.endpos, out ent.v.origin); if (relink) { LinkEdict(ent, true); } return(true); } if (enemy == sv.edicts[0]) { break; } } return(false); } // push down from a step height above the wished position neworg.z += STEPSIZE; v3f end = neworg; end.z -= STEPSIZE * 2; trace = Move(ref neworg, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent); if (trace.allsolid) { return(false); } if (trace.startsolid) { neworg.z -= STEPSIZE; trace = Move(ref neworg, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent); if (trace.allsolid || trace.startsolid) { return(false); } } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if (((int)ent.v.flags & EdictFlags.FL_PARTIALGROUND) != 0) { Mathlib.VectorAdd(ref ent.v.origin, ref move, out ent.v.origin); if (relink) { LinkEdict(ent, true); } ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_ONGROUND; return(true); } return(false); // walked off an edge } // check point traces down for dangling corners Mathlib.Copy(ref trace.endpos, out ent.v.origin); if (!CheckBottom(ent)) { if (((int)ent.v.flags & EdictFlags.FL_PARTIALGROUND) != 0) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) { LinkEdict(ent, true); } return(true); } ent.v.origin = oldorg; return(false); } if (((int)ent.v.flags & EdictFlags.FL_PARTIALGROUND) != 0) { ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_PARTIALGROUND; } ent.v.groundentity = EdictToProg(trace.ent); // the move is ok if (relink) { LinkEdict(ent, true); } return(true); }
static unsafe void Copy(float *src, ref v3f dest) { dest.x = src[0]; dest.y = src[1]; dest.z = src[2]; }
public static void VectorScale(ref v3f a, float scale, out v3f b) { b.x = a.x * scale; b.y = a.y * scale; b.z = a.z * scale; }
/// <summary> /// SV_SetIdealPitch /// </summary> public static void SetIdealPitch() { if (((int)_Player.v.flags & EdictFlags.FL_ONGROUND) == 0) { return; } double angleval = _Player.v.angles.y * Math.PI * 2 / 360; double sinval = Math.Sin(angleval); double cosval = Math.Cos(angleval); float[] z = new float[MAX_FORWARD]; for (int i = 0; i < MAX_FORWARD; i++) { v3f top = _Player.v.origin; top.x += (float)(cosval * (i + 3) * 12); top.y += (float)(sinval * (i + 3) * 12); top.z += _Player.v.view_ofs.z; v3f bottom = top; bottom.z -= 160; trace_t tr = Move(ref top, ref Common.ZeroVector3f, ref Common.ZeroVector3f, ref bottom, 1, _Player); if (tr.allsolid) { return; // looking at a wall, leave ideal the way is was } if (tr.fraction == 1) { return; // near a dropoff } z[i] = top.z + tr.fraction * (bottom.z - top.z); } float dir = 0; // Uze: int in original code??? int steps = 0; for (int j = 1; j < MAX_FORWARD; j++) { float step = z[j] - z[j - 1]; // Uze: int in original code??? if (step > -QDef.ON_EPSILON && step < QDef.ON_EPSILON) // Uze: comparing int with ON_EPSILON (0.1)??? { continue; } if (dir != 0 && (step - dir > QDef.ON_EPSILON || step - dir < -QDef.ON_EPSILON)) { return; // mixed changes } steps++; dir = step; } if (dir == 0) { _Player.v.idealpitch = 0; return; } if (steps < 2) { return; } _Player.v.idealpitch = -dir * _IdealPitchScale.Value; }
public static bool CheckNaN(ref v3f v, float defValue) { bool flag = false; if (float.IsNaN(v.x)) { flag = true; v.x = defValue; } if (float.IsNaN(v.y)) { flag = true; v.y = defValue; } if (float.IsNaN(v.z)) { flag = true; v.z = defValue; } return flag; }
/// <summary> /// SV_PushMove /// </summary> static void PushMove(edict_t pusher, float movetime) { if (pusher.v.velocity.IsEmpty) { pusher.v.ltime += movetime; return; } v3f move, mins, maxs; Mathlib.VectorScale(ref pusher.v.velocity, movetime, out move); Mathlib.VectorAdd(ref pusher.v.absmin, ref move, out mins); Mathlib.VectorAdd(ref pusher.v.absmax, ref move, out maxs); v3f pushorig = pusher.v.origin; edict_t[] moved_edict = new edict_t[QDef.MAX_EDICTS]; v3f[] moved_from = new v3f[QDef.MAX_EDICTS]; // move the pusher to it's final position Mathlib.VectorAdd(ref pusher.v.origin, ref move, out pusher.v.origin); pusher.v.ltime += movetime; LinkEdict(pusher, false); // see if any solid entities are inside the final position int num_moved = 0; for (int e = 1; e < sv.num_edicts; e++) { edict_t check = sv.edicts[e]; if (check.free) { continue; } if (check.v.movetype == Movetypes.MOVETYPE_PUSH || check.v.movetype == Movetypes.MOVETYPE_NONE || check.v.movetype == Movetypes.MOVETYPE_NOCLIP) { continue; } // if the entity is standing on the pusher, it will definately be moved if (!(((int)check.v.flags & EdictFlags.FL_ONGROUND) != 0 && ProgToEdict(check.v.groundentity) == pusher)) { if (check.v.absmin.x >= maxs.x || check.v.absmin.y >= maxs.y || check.v.absmin.z >= maxs.z || check.v.absmax.x <= mins.x || check.v.absmax.y <= mins.y || check.v.absmax.z <= mins.z) { continue; } // see if the ent's bbox is inside the pusher's final position if (TestEntityPosition(check) == null) { continue; } } // remove the onground flag for non-players if (check.v.movetype != Movetypes.MOVETYPE_WALK) { check.v.flags = (int)check.v.flags & ~EdictFlags.FL_ONGROUND; } v3f entorig = check.v.origin; moved_from[num_moved] = entorig; moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity pusher.v.solid = Solids.SOLID_NOT; PushEntity(check, ref move); pusher.v.solid = Solids.SOLID_BSP; // if it is still inside the pusher, block edict_t block = TestEntityPosition(check); if (block != null) { // fail the move if (check.v.mins.x == check.v.maxs.x) { continue; } if (check.v.solid == Solids.SOLID_NOT || check.v.solid == Solids.SOLID_TRIGGER) { // corpse check.v.mins.x = check.v.mins.y = 0; check.v.maxs = check.v.mins; continue; } check.v.origin = entorig; LinkEdict(check, true); pusher.v.origin = pushorig; LinkEdict(pusher, false); pusher.v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher.v.blocked != 0) { Progs.GlobalStruct.self = EdictToProg(pusher); Progs.GlobalStruct.other = EdictToProg(check); Progs.Execute(pusher.v.blocked); } // move back any entities we already moved for (int i = 0; i < num_moved; i++) { moved_edict[i].v.origin = moved_from[i]; LinkEdict(moved_edict[i], false); } return; } } }
public static float Comp(ref v3f a, int index) { if (index < 0 || index > 2) throw new ArgumentOutOfRangeException("index"); return (index == 0 ? a.x : (index == 1 ? a.y : a.z)); }
/// <summary> /// SV_FlyMove /// The basic solid body movement clip that slides along multiple planes /// Returns the clipflags if the velocity was modified (hit something solid) /// 1 = floor /// 2 = wall / step /// 4 = dead stop /// If steptrace is not NULL, the trace of any vertical wall hit will be stored /// </summary> static int FlyMove(edict_t ent, float time, trace_t steptrace) { v3f original_velocity = ent.v.velocity; v3f primal_velocity = ent.v.velocity; int numbumps = 4; int blocked = 0; Vector3[] planes = new Vector3[MAX_CLIP_PLANES]; int numplanes = 0; float time_left = time; for (int bumpcount = 0; bumpcount < numbumps; bumpcount++) { if (ent.v.velocity.IsEmpty) { break; } v3f end; Mathlib.VectorMA(ref ent.v.origin, time_left, ref ent.v.velocity, out end); trace_t trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent); if (trace.allsolid) { // entity is trapped in another solid ent.v.velocity = default(v3f); return(3); } if (trace.fraction > 0) { // actually covered some distance Mathlib.Copy(ref trace.endpos, out ent.v.origin); original_velocity = ent.v.velocity; numplanes = 0; } if (trace.fraction == 1) { break; // moved the entire distance } if (trace.ent == null) { Sys.Error("SV_FlyMove: !trace.ent"); } if (trace.plane.normal.Z > 0.7) { blocked |= 1; // floor if (trace.ent.v.solid == Solids.SOLID_BSP) { ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND; ent.v.groundentity = EdictToProg(trace.ent); } } if (trace.plane.normal.Z == 0) { blocked |= 2; // step if (steptrace != null) { steptrace.CopyFrom(trace); // save for player extrafriction } } // // run the impact function // Impact(ent, trace.ent); if (ent.free) { 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 ent.v.velocity = default(v3f); return(3); } planes[numplanes] = trace.plane.normal; numplanes++; // // modify original_velocity so it parallels all of the clip planes // v3f new_velocity = default(v3f); int i, j; for (i = 0; i < numplanes; i++) { ClipVelocity(ref original_velocity, ref planes[i], out new_velocity, 1); for (j = 0; j < numplanes; j++) { if (j != i) { float dot = new_velocity.x * planes[j].X + new_velocity.y * planes[j].Y + new_velocity.z * planes[j].Z; if (dot < 0) { break; // not ok } } } if (j == numplanes) { break; } } if (i != numplanes) { // go along this plane ent.v.velocity = new_velocity; } else { // go along the crease if (numplanes != 2) { ent.v.velocity = default(v3f); return(7); } Vector3 dir = Vector3.Cross(planes[0], planes[1]); float d = dir.X * ent.v.velocity.x + dir.Y * ent.v.velocity.y + dir.Z * ent.v.velocity.z; Mathlib.Copy(ref dir, out ent.v.velocity); Mathlib.VectorScale(ref ent.v.velocity, d, out ent.v.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (Mathlib.DotProduct(ref ent.v.velocity, ref primal_velocity) <= 0) { ent.v.velocity = default(v3f); return(blocked); } } return(blocked); }
//static Vector3 PerpendicularVector(ref Vector3 src) //{ // float minelem = 1.0f; // // find the smallest magnitude axially aligned vector // Vector3 tempvec = Vector3.Zero; // if (Math.Abs(src.X) < minelem) // { // minelem = Math.Abs(src.X); // tempvec.X = 1; // } // if (Math.Abs(src.Y) < minelem) // { // minelem = Math.Abs(src.Y); // tempvec = new Vector3(0, 1, 0); // } // else if (Math.Abs(src.Z) < minelem) // { // tempvec = new Vector3(0, 0, 1); // } // // project the point onto the plane defined by src // Vector3 dst = ProjectPointOnPlane(ref tempvec, ref src); // Normalize(ref dst); // return dst; //} //static Vector3 ProjectPointOnPlane(ref Vector3 p, ref Vector3 normal) //{ // float inv_denom = 1.0f / normal.LengthSquared; // float d = Vector3.Dot(normal, p) * inv_denom; // Vector3 n = normal * inv_denom; // return p - d * n; //} public static void Copy(ref v3f src, out Vector3 dest) { dest.X = src.x; dest.Y = src.y; dest.Z = src.z; }
public static void Copy(ref Vector3 src, out v3f dest) { dest.x = src.X; dest.y = src.Y; dest.z = src.Z; }
public static float DotProduct(ref v3f a, ref v3f b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
public static float DotProduct(ref v3f a, ref v3f b) { return(a.x * b.x + a.y * b.y + a.z * b.z); }
public static float LengthXY(ref v3f v) { return (float)Math.Sqrt(v.x * v.x + v.y * v.y); }
// return p - d * n; //} public static void Copy(ref Vector3 src, out v3f dest) { dest.x = src.X; dest.y = src.Y; dest.z = src.Z; }
/// <summary> /// SV_movestep /// Called by monster program code. /// The move will be adjusted for slopes and stairs, but if the move isn't /// possible, no move is done, false is returned, and /// pr_global_struct.trace_normal is set to the normal of the blocking wall /// </summary> public static bool MoveStep(edict_t ent, ref v3f move, bool relink) { trace_t trace; // try the move v3f oldorg = ent.v.origin; v3f neworg; Mathlib.VectorAdd(ref ent.v.origin, ref move, out neworg); // flying monsters don't step up if (((int)ent.v.flags & (EdictFlags.FL_SWIM | EdictFlags.FL_FLY)) != 0) { // try one move with vertical motion, then one without for (int i = 0; i < 2; i++) { Mathlib.VectorAdd(ref ent.v.origin, ref move, out neworg); edict_t enemy = ProgToEdict(ent.v.enemy); if (i == 0 && enemy != sv.edicts[0]) { float dz = ent.v.origin.z - enemy.v.origin.z; if (dz > 40) neworg.z -= 8; if (dz < 30) neworg.z += 8; } trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref neworg, 0, ent); if (trace.fraction == 1) { if (((int)ent.v.flags & EdictFlags.FL_SWIM) != 0 && PointContents(ref trace.endpos) == Contents.CONTENTS_EMPTY) return false; // swim monster left water Mathlib.Copy(ref trace.endpos, out ent.v.origin); if (relink) LinkEdict(ent, true); return true; } if (enemy == sv.edicts[0]) break; } return false; } // push down from a step height above the wished position neworg.z += STEPSIZE; v3f end = neworg; end.z -= STEPSIZE * 2; trace = Move(ref neworg, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent); if (trace.allsolid) return false; if (trace.startsolid) { neworg.z -= STEPSIZE; trace = Move(ref neworg, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent); if (trace.allsolid || trace.startsolid) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if (((int)ent.v.flags & EdictFlags.FL_PARTIALGROUND) != 0) { Mathlib.VectorAdd(ref ent.v.origin, ref move, out ent.v.origin); if (relink) LinkEdict(ent, true); ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_ONGROUND; return true; } return false; // walked off an edge } // check point traces down for dangling corners Mathlib.Copy(ref trace.endpos, out ent.v.origin); if (!CheckBottom(ent)) { if (((int)ent.v.flags & EdictFlags.FL_PARTIALGROUND) != 0) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) LinkEdict(ent, true); return true; } ent.v.origin = oldorg; return false; } if (((int)ent.v.flags & EdictFlags.FL_PARTIALGROUND) != 0) { ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_PARTIALGROUND; } ent.v.groundentity = EdictToProg(trace.ent); // the move is ok if (relink) LinkEdict(ent, true); return true; }