/// <summary> /// SV_ClipMoveToEntity /// Handles selection or creation of a clipping hull, and offseting (and /// eventually rotation) of the end points /// </summary> private Trace_t ClipMoveToEntity(MemoryEdict ent, ref Vector3 start, ref Vector3 mins, ref Vector3 maxs, ref Vector3 end) { var trace = new Trace_t(); // fill in a default trace trace.fraction = 1; trace.allsolid = true; trace.endpos = end; // get the clipping hull Vector3 offset; var hull = HullForEntity(ent, ref mins, ref maxs, out offset); var start_l = start - offset; var 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); }
private void TraceLine(ref Vector3 start, ref Vector3 end, out Vector3 impact) { var trace = new Trace_t(); this.Host.Server.RecursiveHullCheck(this.Host.Client.cl.worldmodel.Hulls[0], 0, 0, 1, ref start, ref end, trace); impact = trace.endpos; // VectorCopy(trace.endpos, impact); }
/// <summary> /// SV_WallFriction /// </summary> private void WallFriction( MemoryEdict ent, Trace_t trace ) { Vector3 forward, right, up, vangle = Utilities.ToVector( ref ent.v.v_angle ); MathLib.AngleVectors( ref vangle, out forward, out right, out up ); var d = Vector3.Dot( trace.plane.normal, forward ); d += 0.5f; if ( d >= 0 ) return; // cut the tangential velocity var vel = Utilities.ToVector( ref ent.v.velocity ); var i = Vector3.Dot( trace.plane.normal, vel ); var into = trace.plane.normal * i; var side = vel - into; ent.v.velocity.x = side.X * ( 1 + d ); ent.v.velocity.y = side.Y * ( 1 + d ); }
/// <summary> /// SV_RecursiveHullCheck /// </summary> public Boolean RecursiveHullCheck(BspHull hull, Int32 num, Single p1f, Single p2f, ref Vector3 p1, ref Vector3 p2, Trace_t trace) { // check for empty if (num < 0) { if (num != ( Int32 )Q1Contents.Solid) { trace.allsolid = false; if (num == ( Int32 )Q1Contents.Empty) { trace.inopen = true; } else { trace.inwater = true; } } else { trace.startsolid = true; } return(true); // empty } if (num < hull.firstclipnode || num > hull.lastclipnode) { Utilities.Error("SV_RecursiveHullCheck: bad node number"); } // // find the point distances // var node_children = hull.clipnodes[num].children; var plane = hull.planes[hull.clipnodes[num].planenum]; Single 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 Single 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; } var midf = p1f + (p2f - p1f) * frac; var mid = p1 + (p2 - p1) * frac; var 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) != ( Int32 )Q1Contents.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) == ( Int32 )Q1Contents.Solid) { // shouldn't really happen, but does occasionally frac -= 0.1f; if (frac < 0) { trace.fraction = midf; trace.endpos = mid; Host.Console.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); }
/// <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> private Int32 FlyMove(MemoryEdict ent, Single time, Trace_t steptrace) { var original_velocity = ent.v.velocity; var primal_velocity = ent.v.velocity; var numbumps = 4; var blocked = 0; var planes = new Vector3[MAX_CLIP_PLANES]; var numplanes = 0; var time_left = time; for (var bumpcount = 0; bumpcount < numbumps; bumpcount++) { if (ent.v.velocity.IsEmpty) { break; } Vector3f end; MathLib.VectorMA(ref ent.v.origin, time_left, ref ent.v.velocity, out end); var 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(Vector3f); 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) { Utilities.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 = ( Int32 )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(Vector3f); return(3); } planes[numplanes] = trace.plane.normal; numplanes++; // // modify original_velocity so it parallels all of the clip planes // var new_velocity = default(Vector3f); Int32 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) { var 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(Vector3f); return(7); } var dir = Vector3.Cross(planes[0], planes[1]); var 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(Vector3f); return(blocked); } } 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 Int32 TryUnstick(MemoryEdict ent, ref Vector3f oldvel) { var oldorg = ent.v.origin; var dir = Utilities.ZeroVector3f; var steptrace = new Trace_t(); for (var 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; var 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 = Utilities.ZeroVector3f; return(7); // still not moving }
/// <summary> /// SV_WalkMove /// Only used by players /// </summary> private void WalkMove(MemoryEdict ent) { // // do a regular slide move unless it looks like you ran into a step // var oldonground = ( Int32 )ent.v.flags & EdictFlags.FL_ONGROUND; ent.v.flags = ( Int32 )ent.v.flags & ~EdictFlags.FL_ONGROUND; var oldorg = ent.v.origin; var oldvel = ent.v.velocity; var steptrace = new Trace_t(); var clip = FlyMove(ent, ( Single )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 (Host.Cvars.NoStep.Get <Boolean>()) { return; } if ((( Int32 )_Player.v.flags & EdictFlags.FL_WATERJUMP) != 0) { return; } var nosteporg = ent.v.origin; var nostepvel = ent.v.velocity; // // try moving up and forward to go up a step // ent.v.origin = oldorg; // back to start pos var upmove = Utilities.ZeroVector3f; var downmove = upmove; upmove.z = STEPSIZE; downmove.z = ( Single )(-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, ( Single )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 var 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 = ( Int32 )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_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 int TryUnstick(MemoryEdict ent, ref Vector3 oldvel) { var oldorg = ent.v.origin; var dir = Utilities.ZeroVector3f; var steptrace = new Trace_t(); for (var 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; } this.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; var clip = this.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 = Utilities.ZeroVector3f; return(7); // still not moving }