/// <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_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_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; } } }
/* * ============= * 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_WaterMove /// </summary> static void WaterMove() { // // user intentions // Vector3 pangle = Common.ToVector(ref _Player.v.v_angle); Mathlib.AngleVectors(ref pangle, out _Forward, out _Right, out _Up); Vector3 wishvel = _Forward * _Cmd.forwardmove + _Right * _Cmd.sidemove; if (_Cmd.forwardmove == 0 && _Cmd.sidemove == 0 && _Cmd.upmove == 0) { wishvel.Z -= 60; // drift towards bottom } else { wishvel.Z += _Cmd.upmove; } float wishspeed = wishvel.Length; if (wishspeed > _MaxSpeed.Value) { wishvel *= _MaxSpeed.Value / wishspeed; wishspeed = _MaxSpeed.Value; } wishspeed *= 0.7f; // // water friction // float newspeed, speed = Mathlib.Length(ref _Player.v.velocity); if (speed != 0) { newspeed = (float)(speed - Host.FrameTime * speed * _Friction.Value); if (newspeed < 0) { newspeed = 0; } Mathlib.VectorScale(ref _Player.v.velocity, newspeed / speed, out _Player.v.velocity); } else { newspeed = 0; } // // water acceleration // if (wishspeed == 0) { return; } float addspeed = wishspeed - newspeed; if (addspeed <= 0) { return; } Mathlib.Normalize(ref wishvel); float accelspeed = (float)(_Accelerate.Value * wishspeed * Host.FrameTime); if (accelspeed > addspeed) { accelspeed = addspeed; } wishvel *= accelspeed; _Player.v.velocity.x += wishvel.X; _Player.v.velocity.y += wishvel.Y; _Player.v.velocity.z += wishvel.Z; }