/// <summary> /// SV_ClipMoveToEntity /// Handles selection or creation of a clipping hull, and offseting (and /// eventually rotation) of the end points /// </summary> static trace_t ClipMoveToEntity(edict_t ent, ref Vector3 start, ref Vector3 mins, ref Vector3 maxs, ref Vector3 end) { trace_t trace = new trace_t(); // fill in a default trace trace.fraction = 1; trace.allsolid = true; trace.endpos = end; // get the clipping hull Vector3 offset; hull_t hull = HullForEntity(ent, ref mins, ref maxs, out offset); Vector3 start_l = start - offset; Vector3 end_l = end - offset; // trace a line through the apropriate clipping hull RecursiveHullCheck(hull, hull.firstclipnode, 0, 1, ref start_l, ref end_l, trace); // fix trace up by the offset if (trace.fraction != 1) { trace.endpos += offset; } // did we clip the move? if (trace.fraction < 1 || trace.startsolid) { trace.ent = ent; } return(trace); }
/* * =============== * PF_droptofloor * * void() droptofloor * =============== */ static void PF_droptofloor() { edict_t ent = Server.ProgToEdict(Progs.GlobalStruct.self); Vector3 org, mins, maxs; Mathlib.Copy(ref ent.v.origin, out org); Mathlib.Copy(ref ent.v.mins, out mins); Mathlib.Copy(ref ent.v.maxs, out maxs); Vector3 end = org; end.Z -= 256; trace_t trace = Server.Move(ref org, ref mins, ref maxs, ref end, 0, ent); if (trace.fraction == 1 || trace.allsolid) { ReturnFloat(0); } else { Mathlib.Copy(ref trace.endpos, out ent.v.origin); Server.LinkEdict(ent, false); ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND; ent.v.groundentity = Server.EdictToProg(trace.ent); ReturnFloat(1); } }
/* * ================= * PF_traceline * * Used for use tracing and shot targeting * Traces are blocked by bbox and exact bsp entityes, and also slide box entities * if the tryents flag is set. * * traceline (vector1, vector2, tryents) * ================= */ static unsafe void PF_traceline() { float * v1 = GetVector(OFS.OFS_PARM0); float * v2 = GetVector(OFS.OFS_PARM1); int nomonsters = (int)GetFloat(OFS.OFS_PARM2); edict_t ent = GetEdict(OFS.OFS_PARM3); Vector3 vec1, vec2; Copy(v1, out vec1); Copy(v2, out vec2); trace_t trace = Server.Move(ref vec1, ref Common.ZeroVector, ref Common.ZeroVector, ref vec2, nomonsters, ent); Progs.GlobalStruct.trace_allsolid = trace.allsolid ? 1 : 0; Progs.GlobalStruct.trace_startsolid = trace.startsolid ? 1 : 0; Progs.GlobalStruct.trace_fraction = trace.fraction; Progs.GlobalStruct.trace_inwater = trace.inwater ? 1 : 0; Progs.GlobalStruct.trace_inopen = trace.inopen ? 1 : 0; Mathlib.Copy(ref trace.endpos, out Progs.GlobalStruct.trace_endpos); Mathlib.Copy(ref trace.plane.normal, out Progs.GlobalStruct.trace_plane_normal); Progs.GlobalStruct.trace_plane_dist = trace.plane.dist; if (trace.ent != null) { Progs.GlobalStruct.trace_ent = Server.EdictToProg(trace.ent); } else { Progs.GlobalStruct.trace_ent = Server.EdictToProg(Server.sv.edicts[0]); } }
static void TraceLine(ref Vector3 start, ref Vector3 end, out Vector3 impact) { trace_t trace = new trace_t(); Server.RecursiveHullCheck(Client.cl.worldmodel.hulls[0], 0, 0, 1, ref start, ref end, trace); impact = trace.endpos; // VectorCopy(trace.endpos, impact); }
public edict_t ent; // entity the surface is on public void CopyFrom(trace_t src) { this.allsolid = src.allsolid; this.startsolid = src.startsolid; this.inopen = src.inopen; this.inwater = src.inwater; this.fraction = src.fraction; this.endpos = src.endpos; this.plane = src.plane; this.ent = src.ent; }
/// <summary> /// SV_TestEntityPosition /// This could be a lot more efficient... /// </summary> static edict_t TestEntityPosition(edict_t ent) { trace_t trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref ent.v.origin, 0, ent); if (trace.startsolid) { return(sv.edicts[0]); } return(null); }
/// <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> /// SV_WallFriction /// </summary> static void WallFriction(edict_t ent, trace_t trace) { Vector3 forward, right, up, vangle = Common.ToVector(ref ent.v.v_angle); Mathlib.AngleVectors(ref vangle, out forward, out right, out up); float d = Vector3.Dot(trace.plane.normal, forward); d += 0.5f; if (d >= 0) { return; } // cut the tangential velocity Vector3 vel = Common.ToVector(ref ent.v.velocity); float i = Vector3.Dot(trace.plane.normal, vel); Vector3 into = trace.plane.normal * i; Vector3 side = vel - into; ent.v.velocity.x = side.X * (1 + d); ent.v.velocity.y = side.Y * (1 + d); }
/// <summary> /// SV_UserFriction /// </summary> static void UserFriction() { float speed = Mathlib.LengthXY(ref _Player.v.velocity); if (speed == 0) { return; } // if the leading edge is over a dropoff, increase friction Vector3 start, stop; start.X = stop.X = _Player.v.origin.x + _Player.v.velocity.x / speed * 16; start.Y = stop.Y = _Player.v.origin.y + _Player.v.velocity.y / speed * 16; start.Z = _Player.v.origin.z + _Player.v.mins.z; stop.Z = start.Z - 34; trace_t trace = Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref stop, 1, _Player); float friction = _Friction.Value; if (trace.fraction == 1.0) { friction *= _EdgeFriction.Value; } // apply friction float control = speed < _StopSpeed.Value ? _StopSpeed.Value : speed; float newspeed = (float)(speed - Host.FrameTime * control * friction); if (newspeed < 0) { newspeed = 0; } newspeed /= speed; Mathlib.VectorScale(ref _Player.v.velocity, newspeed, out _Player.v.velocity); }
/// <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); }
/// <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 (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; } }
/// <summary> /// SV_Physics_Toss /// Toss, bounce, and fly movement. When onground, do nothing. /// </summary> private static void Physics_Toss(edict_t ent) { // regular thinking if (!RunThink(ent)) { return; } // if onground, return without moving if (((int)ent.v.flags & EdictFlags.FL_ONGROUND) != 0) { return; } CheckVelocity(ent); // add gravity if (ent.v.movetype != Movetypes.MOVETYPE_FLY && ent.v.movetype != Movetypes.MOVETYPE_FLYMISSILE) { AddGravity(ent); } // move angles Mathlib.VectorMA(ref ent.v.angles, (float)Host.FrameTime, ref ent.v.avelocity, out ent.v.angles); // move origin v3f move; Mathlib.VectorScale(ref ent.v.velocity, (float)Host.FrameTime, out move); trace_t trace = PushEntity(ent, ref move); if (trace.fraction == 1) { return; } if (ent.free) { return; } float backoff; if (ent.v.movetype == Movetypes.MOVETYPE_BOUNCE) { backoff = 1.5f; } else { backoff = 1; } ClipVelocity(ref ent.v.velocity, ref trace.plane.normal, out ent.v.velocity, backoff); // stop if on ground if (trace.plane.normal.Z > 0.7f) { if (ent.v.velocity.z < 60 || ent.v.movetype != Movetypes.MOVETYPE_BOUNCE) { ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND; ent.v.groundentity = EdictToProg(trace.ent); ent.v.velocity = default(v3f); ent.v.avelocity = default(v3f); } } // check for in water CheckWaterTransition(ent); }
/// <summary> /// SV_RecursiveHullCheck /// </summary> public static bool RecursiveHullCheck(hull_t hull, int num, float p1f, float p2f, ref Vector3 p1, ref Vector3 p2, trace_t trace) { // check for empty if (num < 0) { if (num != Contents.CONTENTS_SOLID) { trace.allsolid = false; if (num == Contents.CONTENTS_EMPTY) { trace.inopen = true; } else { trace.inwater = true; } } else { trace.startsolid = true; } return(true); // empty } if (num < hull.firstclipnode || num > hull.lastclipnode) { Sys.Error("SV_RecursiveHullCheck: bad node number"); } // // find the point distances // short[] node_children = hull.clipnodes[num].children; mplane_t plane = hull.planes[hull.clipnodes[num].planenum]; float t1, t2; if (plane.type < 3) { t1 = Mathlib.Comp(ref p1, plane.type) - plane.dist; t2 = Mathlib.Comp(ref p2, plane.type) - plane.dist; } else { t1 = Vector3.Dot(plane.normal, p1) - plane.dist; t2 = Vector3.Dot(plane.normal, p2) - plane.dist; } if (t1 >= 0 && t2 >= 0) { return(RecursiveHullCheck(hull, node_children[0], p1f, p2f, ref p1, ref p2, trace)); } if (t1 < 0 && t2 < 0) { return(RecursiveHullCheck(hull, node_children[1], p1f, p2f, ref p1, ref p2, trace)); } // put the crosspoint DIST_EPSILON pixels on the near side float frac; if (t1 < 0) { frac = (t1 + DIST_EPSILON) / (t1 - t2); } else { frac = (t1 - DIST_EPSILON) / (t1 - t2); } if (frac < 0) { frac = 0; } if (frac > 1) { frac = 1; } float midf = p1f + (p2f - p1f) * frac; Vector3 mid = p1 + (p2 - p1) * frac; int side = (t1 < 0) ? 1 : 0; // move up to the node if (!RecursiveHullCheck(hull, node_children[side], p1f, midf, ref p1, ref mid, trace)) { return(false); } if (HullPointContents(hull, node_children[side ^ 1], ref mid) != Contents.CONTENTS_SOLID) { // go past the node return(RecursiveHullCheck(hull, node_children[side ^ 1], midf, p2f, ref mid, ref p2, trace)); } if (trace.allsolid) { return(false); // never got out of the solid area } //================== // the other side of the node is solid, this is the impact point //================== if (side == 0) { trace.plane.normal = plane.normal; trace.plane.dist = plane.dist; } else { trace.plane.normal = -plane.normal; trace.plane.dist = -plane.dist; } while (HullPointContents(hull, hull.firstclipnode, ref mid) == Contents.CONTENTS_SOLID) { // shouldn't really happen, but does occasionally frac -= 0.1f; if (frac < 0) { trace.fraction = midf; trace.endpos = mid; Con.DPrint("backup past 0\n"); return(false); } midf = p1f + (p2f - p1f) * frac; mid = p1 + (p2 - p1) * frac; } trace.fraction = midf; trace.endpos = mid; return(false); }
/* * ============= * PF_aim * * Pick a vector for the player to shoot along * vector aim(entity, missilespeed) * ============= */ static void PF_aim() { edict_t ent = GetEdict(OFS.OFS_PARM0); float speed = GetFloat(OFS.OFS_PARM1); Vector3 start = Common.ToVector(ref ent.v.origin); start.Z += 20; // try sending a trace straight Vector3 dir; Mathlib.Copy(ref Progs.GlobalStruct.v_forward, out dir); Vector3 end = start + dir * 2048; trace_t tr = Server.Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref end, 0, ent); if (tr.ent != null && tr.ent.v.takedamage == Damages.DAMAGE_AIM && (Host.TeamPlay == 0 || ent.v.team <= 0 || ent.v.team != tr.ent.v.team)) { ReturnVector(ref Progs.GlobalStruct.v_forward); return; } // try all possible entities Vector3 bestdir = dir; float bestdist = Server.Aim; edict_t bestent = null; for (int i = 1; i < Server.sv.num_edicts; i++) { edict_t check = Server.sv.edicts[i]; if (check.v.takedamage != Damages.DAMAGE_AIM) { continue; } if (check == ent) { continue; } if (Host.TeamPlay != 0 && ent.v.team > 0 && ent.v.team == check.v.team) { continue; // don't aim at teammate } v3f tmp; Mathlib.VectorAdd(ref check.v.mins, ref check.v.maxs, out tmp); Mathlib.VectorMA(ref check.v.origin, 0.5f, ref tmp, out tmp); Mathlib.Copy(ref tmp, out end); dir = end - start; Mathlib.Normalize(ref dir); float dist = Vector3.Dot(dir, Common.ToVector(ref Progs.GlobalStruct.v_forward)); if (dist < bestdist) { continue; // to far to turn } tr = Server.Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref end, 0, ent); if (tr.ent == check) { // can shoot at this one bestdist = dist; bestent = check; } } if (bestent != null) { v3f dir2, end2; Mathlib.VectorSubtract(ref bestent.v.origin, ref ent.v.origin, out dir2); float dist = Mathlib.DotProduct(ref dir2, ref Progs.GlobalStruct.v_forward); Mathlib.VectorScale(ref Progs.GlobalStruct.v_forward, dist, out end2); end2.z = dir2.z; Mathlib.Normalize(ref end2); ReturnVector(ref end2); } else { ReturnVector(ref bestdir); } }
/// <summary> /// SV_CheckBottom /// </summary> public static bool CheckBottom(edict_t ent) { v3f mins, maxs; Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.mins, out mins); Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.maxs, out maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint Vector3 start; start.Z = mins.z - 1; for (int x = 0; x <= 1; x++) { for (int y = 0; y <= 1; y++) { start.X = (x != 0 ? maxs.x : mins.x); start.Y = (y != 0 ? maxs.y : mins.y); if (PointContents(ref start) != Contents.CONTENTS_SOLID) { goto RealCheck; } } } return(true); // we got out easy RealCheck: // // check it for real... // start.Z = mins.z; // the midpoint must be within 16 of the bottom start.X = (mins.x + maxs.x) * 0.5f; start.Y = (mins.y + maxs.y) * 0.5f; Vector3 stop = start; stop.Z -= 2 * STEPSIZE; trace_t trace = Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref stop, 1, ent); if (trace.fraction == 1.0) { return(false); } float mid = trace.endpos.Z; float bottom = mid; // the corners must be within 16 of the midpoint for (int x = 0; x <= 1; x++) { for (int y = 0; y <= 1; y++) { start.X = stop.X = (x != 0 ? maxs.x : mins.x); start.Y = stop.Y = (y != 0 ? maxs.y : mins.y); trace = Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref stop, 1, ent); if (trace.fraction != 1.0 && trace.endpos.Z > bottom) { bottom = trace.endpos.Z; } if (trace.fraction == 1.0 || mid - trace.endpos.Z > STEPSIZE) { return(false); } } } return(true); }
/// <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; }