public 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 == q_shared.MOVETYPE_FLYMISSILE) { trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, q_shared.MOVE_MISSILE, ent); } else if (ent.v.solid == q_shared.SOLID_TRIGGER || ent.v.solid == q_shared.SOLID_NOT) { // only clip against bmodels trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, q_shared.MOVE_NOMONSTERS, ent); } else { trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, q_shared.MOVE_NORMAL, ent); } Mathlib.Copy(ref trace.endpos, out ent.v.origin); SV_LinkEdict(ent, true); if (trace.ent != null) { SV_Impact(ent, trace.ent); } return(trace); }
public static void SV_ClientThink() { if (sv_player.v.movetype == q_shared.MOVETYPE_NONE) { return; } onground = ((int)sv_player.v.flags & q_shared.FL_ONGROUND) != 0; DropPunchAngle(); // // if dead, behave differently // if (sv_player.v.health <= 0) { return; } // // angles // show 1/3 the pitch angle and all the roll angle cmd = host_client.cmd; v3f v_angle; Mathlib.VectorAdd(ref sv_player.v.v_angle, ref sv_player.v.punchangle, out v_angle); Vector3 pang = ToVector(ref sv_player.v.angles); Vector3 pvel = ToVector(ref sv_player.v.velocity); sv_player.v.angles.z = game_engine.V_CalcRoll(ref pang, ref pvel) * 4; if (sv_player.v.fixangle == 0) { sv_player.v.angles.x = -v_angle.x / 3; sv_player.v.angles.y = v_angle.y; } if (((int)sv_player.v.flags & q_shared.FL_WATERJUMP) != 0) { SV_WaterJump(); return; } // // walk // if ((sv_player.v.waterlevel >= 2) && (sv_player.v.movetype != q_shared.MOVETYPE_NOCLIP)) { SV_WaterMove(); return; } SV_AirMove(); }
static void PF_aim() { edict_t ent = G_EDICT(q_shared.OFS_PARM0); float speed = G_FLOAT(q_shared.OFS_PARM1); Vector3 start = ToVector(ref ent.v.origin); start.Z += 20; // try sending a trace straight Vector3 dir; Mathlib.Copy(ref pr_global_struct.v_forward, out dir); Vector3 end = start + dir * 2048; trace_t tr = SV_Move(ref start, ref q_shared.ZeroVector, ref q_shared.ZeroVector, ref end, 0, ent); if (tr.ent != null && tr.ent.v.takedamage == q_shared.DAMAGE_AIM && (teamplay.value == 0 || ent.v.team <= 0 || ent.v.team != tr.ent.v.team)) { G_VECTOR(ref pr_global_struct.v_forward); return; } // try all possible entities Vector3 bestdir = dir; float bestdist = sv_aim.value; edict_t bestent = null; for (int i = 1; i < sv.num_edicts; i++) { edict_t check = sv.edicts[i]; if (check.v.takedamage != q_shared.DAMAGE_AIM) { continue; } if (check == ent) { continue; } if (teamplay.value != 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, ToVector(ref pr_global_struct.v_forward)); if (dist < bestdist) { continue; // to far to turn } tr = SV_Move(ref start, ref q_shared.ZeroVector, ref q_shared.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 pr_global_struct.v_forward); Mathlib.VectorScale(ref pr_global_struct.v_forward, dist, out end2); end2.z = dir2.z; Mathlib.Normalize(ref end2); G_VECTOR(ref end2); } else { G_VECTOR(ref bestdir); } }
public static void SV_StartSound(edict_t entity, int channel, string sample, int volume, float attenuation) { if (volume < 0 || volume > 255) { Sys_Error("SV_StartSound: volume = {0}", volume); } if (attenuation < 0 || attenuation > 4) { Sys_Error("SV_StartSound: attenuation = {0}", attenuation); } if (channel < 0 || channel > 7) { Sys_Error("SV_StartSound: channel = {0}", channel); } if (sv.datagram.Length > q_shared.MAX_DATAGRAM - 16) { return; } // find precache number for sound int sound_num; for (sound_num = 1; sound_num < q_shared.MAX_SOUNDS && sv.sound_precache[sound_num] != null; sound_num++) { if (sample == sv.sound_precache[sound_num]) { break; } } if (sound_num == q_shared.MAX_SOUNDS || String.IsNullOrEmpty(sv.sound_precache[sound_num])) { Con_Printf("SV_StartSound: {0} not precacheed\n", sample); return; } int ent = NUM_FOR_EDICT(entity); channel = (ent << 3) | channel; int field_mask = 0; if (volume != q_shared.DEFAULT_SOUND_PACKET_VOLUME) { field_mask |= q_shared.SND_VOLUME; } if (attenuation != q_shared.DEFAULT_SOUND_PACKET_ATTENUATION) { field_mask |= q_shared.SND_ATTENUATION; } // directed messages go only to the entity the are targeted on sv.datagram.MSG_WriteByte(q_shared.svc_sound); sv.datagram.MSG_WriteByte(field_mask); if ((field_mask & q_shared.SND_VOLUME) != 0) { sv.datagram.MSG_WriteByte(volume); } if ((field_mask & q_shared.SND_ATTENUATION) != 0) { sv.datagram.MSG_WriteByte((int)(attenuation * 64)); } sv.datagram.MSG_WriteShort(channel); sv.datagram.MSG_WriteByte(sound_num); v3f v; Mathlib.VectorAdd(ref entity.v.mins, ref entity.v.maxs, out v); Mathlib.VectorMA(ref entity.v.origin, 0.5f, ref v, out v); sv.datagram.MSG_WriteCoord(v.x); sv.datagram.MSG_WriteCoord(v.y); sv.datagram.MSG_WriteCoord(v.z); }
public static void SV_LinkEdict(edict_t ent, bool touch_triggers) { if (ent.area.Prev != null) { SV_UnlinkEdict(ent); // unlink from old position } if (ent == sv.edicts[0]) { return; // don't add the world } if (ent.free) { return; } // set the abs box Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.mins, out ent.v.absmin); Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.maxs, out ent.v.absmax); // // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded // if (((int)ent.v.flags & q_shared.FL_ITEM) != 0) { ent.v.absmin.x -= 15; ent.v.absmin.y -= 15; ent.v.absmax.x += 15; ent.v.absmax.y += 15; } else { // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent.v.absmin.x -= 1; ent.v.absmin.y -= 1; ent.v.absmin.z -= 1; ent.v.absmax.x += 1; ent.v.absmax.y += 1; ent.v.absmax.z += 1; } // link to PVS leafs ent.num_leafs = 0; if (ent.v.modelindex != 0) { SV_FindTouchedLeafs(ent, sv.worldmodel.nodes[0]); } if (ent.v.solid == q_shared.SOLID_NOT) { return; } // find the first node that the ent's box crosses areanode_t node = sv_areanodes[0]; while (true) { if (node.axis == -1) { break; } if (Mathlib.Comp(ref ent.v.absmin, node.axis) > node.dist) { node = node.children[0]; } else if (Mathlib.Comp(ref ent.v.absmax, node.axis) < node.dist) { node = node.children[1]; } else { break; // crosses the node } } // link it in if (ent.v.solid == q_shared.SOLID_TRIGGER) { ent.area.InsertBefore(node.trigger_edicts); } else { ent.area.InsertBefore(node.solid_edicts); } // if touch_triggers, touch all entities at this node and decend for more if (touch_triggers) { SV_TouchLinks(ent, sv_areanodes[0]); } }
public static void SV_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[q_shared.MAX_EDICTS]; v3f[] moved_from = new v3f[q_shared.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; SV_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 == q_shared.MOVETYPE_PUSH || check.v.movetype == q_shared.MOVETYPE_NONE || check.v.movetype == q_shared.MOVETYPE_NOCLIP) { continue; } // if the entity is standing on the pusher, it will definately be moved if (!(((int)check.v.flags & q_shared.FL_ONGROUND) != 0 && PROG_TO_EDICT(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 (SV_TestEntityPosition(check) == null) { continue; } } // remove the onground flag for non-players if (check.v.movetype != q_shared.MOVETYPE_WALK) { check.v.flags = (int)check.v.flags & ~q_shared.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 = q_shared.SOLID_NOT; PushEntity(check, ref move); pusher.v.solid = q_shared.SOLID_BSP; // if it is still inside the pusher, block edict_t block = SV_TestEntityPosition(check); if (block != null) { // fail the move if (check.v.mins.x == check.v.maxs.x) { continue; } if (check.v.solid == q_shared.SOLID_NOT || check.v.solid == q_shared.SOLID_TRIGGER) { // corpse check.v.mins.x = check.v.mins.y = 0; check.v.maxs = check.v.mins; continue; } check.v.origin = entorig; SV_LinkEdict(check, true); pusher.v.origin = pushorig; SV_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) { pr_global_struct.self = EDICT_TO_PROG(pusher); pr_global_struct.other = EDICT_TO_PROG(check); PR_ExecuteProgram(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]; SV_LinkEdict(moved_edict[i], false); } return; } } }
public static bool SV_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 & (q_shared.FL_SWIM | q_shared.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 = PROG_TO_EDICT(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 & q_shared.FL_SWIM) != 0 && SV_PointContents(ref trace.endpos) == q_shared.CONTENTS_EMPTY) { return(false); // swim monster left water } Mathlib.Copy(ref trace.endpos, out ent.v.origin); if (relink) { SV_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 += q_shared.STEPSIZE; v3f end = neworg; end.z -= q_shared.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 -= q_shared.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 & q_shared.FL_PARTIALGROUND) != 0) { Mathlib.VectorAdd(ref ent.v.origin, ref move, out ent.v.origin); if (relink) { SV_LinkEdict(ent, true); } ent.v.flags = (int)ent.v.flags & ~q_shared.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 (!SV_CheckBottom(ent)) { if (((int)ent.v.flags & q_shared.FL_PARTIALGROUND) != 0) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) { SV_LinkEdict(ent, true); } return(true); } ent.v.origin = oldorg; return(false); } if (((int)ent.v.flags & q_shared.FL_PARTIALGROUND) != 0) { ent.v.flags = (int)ent.v.flags & ~q_shared.FL_PARTIALGROUND; } ent.v.groundentity = EDICT_TO_PROG(trace.ent); // the move is ok if (relink) { SV_LinkEdict(ent, true); } return(true); }
public static bool SV_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 (SV_PointContents(ref start) != q_shared.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 * q_shared.STEPSIZE; trace_t trace = SV_Move(ref start, ref q_shared.ZeroVector, ref q_shared.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 = SV_Move(ref start, ref q_shared.ZeroVector, ref q_shared.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 > q_shared.STEPSIZE) { return(false); } } } return(true); }