diff --git a/src/ST-API/JsonConverters.cs b/src/ST-API/JsonConverters.cs index 0e50238..377aacd 100644 --- a/src/ST-API/JsonConverters.cs +++ b/src/ST-API/JsonConverters.cs @@ -28,9 +28,9 @@ public static Dictionary ConstructJsonDictFromString(string str) } } -internal class VectorConverter : JsonConverter +internal class Vector_tConverter : JsonConverter { - public override Vector Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Vector_t Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // Ensure that the reader is positioned at the start of an object if (reader.TokenType != JsonTokenType.StartObject) @@ -63,10 +63,10 @@ public override Vector Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS } } - return new Vector { X = x, Y = y, Z = z }; + return new Vector_t { X = x, Y = y, Z = z }; } - public override void Write(Utf8JsonWriter writer, Vector value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, Vector_t value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteNumber("X", value.X); @@ -76,9 +76,9 @@ public override void Write(Utf8JsonWriter writer, Vector value, JsonSerializerOp } } -internal class QAngleConverter : JsonConverter +internal class QAngle_tConverter : JsonConverter { - public override QAngle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override QAngle_t Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // Ensure that the reader is positioned at the start of an object if (reader.TokenType != JsonTokenType.StartObject) @@ -111,10 +111,10 @@ public override QAngle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS } } - return new QAngle { X = X, Y = Y, Z = Z }; + return new QAngle_t { X = X, Y = Y, Z = Z }; } - public override void Write(Utf8JsonWriter writer, QAngle value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, QAngle_t value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteNumber("X", value.X); diff --git a/src/ST-Commands/MapCommands.cs b/src/ST-Commands/MapCommands.cs index 671a15e..14714d7 100644 --- a/src/ST-Commands/MapCommands.cs +++ b/src/ST-Commands/MapCommands.cs @@ -59,7 +59,7 @@ public void Triggers(CCSPlayerController? player, CommandInfo command) player.PrintToChat($"Hooked Trigger -> Start -> {CurrentMap.StartZone} -> Angles {CurrentMap.StartZoneAngles}"); player.PrintToChat($"Hooked Trigger -> End -> {CurrentMap.EndZone}"); int i = 1; - foreach (Vector stage in CurrentMap.StageStartZone) + foreach (Vector_t stage in CurrentMap.StageStartZone) { if (stage.X == 0 && stage.Y == 0 && stage.Z == 0) continue; @@ -71,7 +71,7 @@ public void Triggers(CCSPlayerController? player, CommandInfo command) } i = 1; - foreach (Vector bonus in CurrentMap.BonusStartZone) + foreach (Vector_t bonus in CurrentMap.BonusStartZone) { if (bonus.X == 0 && bonus.Y == 0 && bonus.Z == 0) continue; diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index 221c24a..cb790ff 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -12,8 +12,13 @@ public partial class SurfTimer [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void PlayerReset(CCSPlayerController? player, CommandInfo command) { - if (player == null || player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } Player oPlayer = playerList[player.UserId ?? 0]; if (oPlayer.ReplayRecorder.IsSaving) @@ -25,16 +30,21 @@ public void PlayerReset(CCSPlayerController? player, CommandInfo command) // oPlayer.ReplayRecorder.Reset(); // To-do: players[userid].Timer.Reset() -> teleport player playerList[player.UserId ?? 0].Timer.Reset(); - if (CurrentMap.StartZone != new Vector(0, 0, 0)) - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.StartZone, new QAngle(0, 0, 0), new Vector(0, 0, 0))); + if (!CurrentMap.StartZone.IsZero()) + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!,CurrentMap.StartZone)); } [ConsoleCommand("css_rs", "Reset back to the start of the stage or bonus you're in.")] [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void PlayerResetStage(CCSPlayerController? player, CommandInfo command) { - if (player == null || player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } Player oPlayer = playerList[player.UserId ?? 0]; if (oPlayer.ReplayRecorder.IsSaving) @@ -46,18 +56,18 @@ public void PlayerResetStage(CCSPlayerController? player, CommandInfo command) if (oPlayer.Timer.IsBonusMode) { - if (oPlayer.Timer.Bonus != 0 && CurrentMap.BonusStartZone[oPlayer.Timer.Bonus] != new Vector(0, 0, 0)) - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.BonusStartZone[oPlayer.Timer.Bonus], CurrentMap.BonusStartZoneAngles[oPlayer.Timer.Bonus], new Vector(0, 0, 0))); + if (oPlayer.Timer.Bonus != 0 && !CurrentMap.BonusStartZone[oPlayer.Timer.Bonus].IsZero()) + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value! , CurrentMap.BonusStartZone[oPlayer.Timer.Bonus])); else // Reset back to map start - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.StartZone, new QAngle(0, 0, 0), new Vector(0, 0, 0))); + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!,CurrentMap.StartZone)); } else { - if (oPlayer.Timer.Stage != 0 && CurrentMap.StageStartZone[oPlayer.Timer.Stage] != new Vector(0, 0, 0)) - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.StageStartZone[oPlayer.Timer.Stage], CurrentMap.StageStartZoneAngles[oPlayer.Timer.Stage], new Vector(0, 0, 0))); + if (oPlayer.Timer.Stage != 0 && !CurrentMap.StageStartZone[oPlayer.Timer.Stage].IsZero()) + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!, CurrentMap.StageStartZone[oPlayer.Timer.Stage])); else // Reset back to map start - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.StartZone, new QAngle(0, 0, 0), new Vector(0, 0, 0))); + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!,CurrentMap.StartZone)); } } @@ -66,8 +76,13 @@ public void PlayerResetStage(CCSPlayerController? player, CommandInfo command) [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command) { - if (player == null || player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } int stage; try @@ -109,18 +124,18 @@ public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command) return; } - if (CurrentMap.StageStartZone[stage] != new Vector(0, 0, 0)) + if (!CurrentMap.StageStartZone[stage].IsZero()) { playerList[player.UserId ?? 0].Timer.Reset(); if (stage == 1) { - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.StartZone, CurrentMap.StartZoneAngles, new Vector(0, 0, 0))); + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!, CurrentMap.StartZone)); } else { playerList[player.UserId ?? 0].Timer.Stage = stage; - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.StageStartZone[stage], CurrentMap.StageStartZoneAngles[stage], new Vector(0, 0, 0))); + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!, CurrentMap.StageStartZone[stage])); playerList[player.UserId ?? 0].Timer.IsStageMode = true; } @@ -139,8 +154,13 @@ public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command) [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void PlayerGoToBonus(CCSPlayerController? player, CommandInfo command) { - if (player == null || player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } int bonus; @@ -175,12 +195,12 @@ public void PlayerGoToBonus(CCSPlayerController? player, CommandInfo command) return; } - if (CurrentMap.BonusStartZone[bonus] != new Vector(0, 0, 0)) + if (!CurrentMap.BonusStartZone[bonus].IsZero()) { playerList[player.UserId ?? 0].Timer.Reset(); playerList[player.UserId ?? 0].Timer.IsBonusMode = true; - Server.NextFrame(() => player.PlayerPawn.Value!.Teleport(CurrentMap.BonusStartZone[bonus], CurrentMap.BonusStartZoneAngles[bonus], new Vector(0, 0, 0))); + Server.NextFrame(() => Extensions.Teleport(player.PlayerPawn.Value!, CurrentMap.BonusStartZone[bonus])); } else @@ -321,8 +341,13 @@ Saveloc Commands [ConsoleCommand("css_saveloc", "Save current player location to be practiced")] public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command) { - if (player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } Player p = playerList[player.UserId ?? 0]; if (!p.Timer.IsRunning) @@ -337,9 +362,9 @@ public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command) p.SavedLocations.Add(new SavelocFrame { - Pos = new Vector(player_pos.X, player_pos.Y, player_pos.Z), - Ang = new QAngle(player_angle.X, player_angle.Y, player_angle.Z), - Vel = new Vector(player_velocity.X, player_velocity.Y, player_velocity.Z), + Pos = new Vector_t(player_pos.X, player_pos.Y, player_pos.Z), + Ang = new QAngle_t(player_angle.X, player_angle.Y, player_angle.Z), + Vel = new Vector_t(player_velocity.X, player_velocity.Y, player_velocity.Z), Tick = p.Timer.Ticks }); p.CurrentSavedLocation = p.SavedLocations.Count - 1; @@ -352,8 +377,13 @@ public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command) [ConsoleCommand("css_tele", "Teleport player to current saved location")] public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo command) { - if (player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } Player p = playerList[player.UserId ?? 0]; @@ -387,7 +417,7 @@ public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo comm SavelocFrame location = p.SavedLocations[p.CurrentSavedLocation]; Server.NextFrame(() => { - p.Controller.PlayerPawn.Value!.Teleport(location.Pos, location.Ang, location.Vel); + Extensions.Teleport(p.Controller.PlayerPawn.Value!, location.Pos, location.Ang, location.Vel); p.Timer.Ticks = location.Tick; }); @@ -399,8 +429,13 @@ public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo comm [ConsoleCommand("css_teleprev", "Teleport player to previous saved location")] public void TeleportPlayerLocationPrev(CCSPlayerController? player, CommandInfo command) { - if (player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } Player p = playerList[player.UserId ?? 0]; @@ -429,8 +464,13 @@ public void TeleportPlayerLocationPrev(CCSPlayerController? player, CommandInfo [ConsoleCommand("css_telenext", "Teleport player to next saved location")] public void TeleportPlayerLocationNext(CCSPlayerController? player, CommandInfo command) { - if (player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + if (player == null) return; + if (player.Team == CsTeam.Spectator || player.Team == CsTeam.None) + { + player.ChangeTeam(CsTeam.CounterTerrorist); + player.Respawn(); + } Player p = playerList[player.UserId ?? 0]; diff --git a/src/ST-Events/Players.cs b/src/ST-Events/Players.cs index 3e7d055..08306d2 100644 --- a/src/ST-Events/Players.cs +++ b/src/ST-Events/Players.cs @@ -1,7 +1,6 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes.Registration; -using CounterStrikeSharp.API.Modules.Utils; using MaxMind.GeoIP2; using Microsoft.Extensions.Logging; diff --git a/src/ST-Events/Tick.cs b/src/ST-Events/Tick.cs index 0f8570f..fec417e 100644 --- a/src/ST-Events/Tick.cs +++ b/src/ST-Events/Tick.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API.Modules.Cvars; +using CounterStrikeSharp.API.Modules.Entities.Constants; namespace SurfTimer; @@ -11,6 +12,9 @@ public void OnTick() player.Timer.Tick(); player.ReplayRecorder.Tick(player); player.HUD.Display(); + if (player.Controller.Collision == null) continue; + if ((CollisionGroup)player.Controller.Collision.CollisionGroup == CollisionGroup.COLLISION_GROUP_DEBRIS) continue; + player.Controller.SetCollisionGroup(CollisionGroup.COLLISION_GROUP_DEBRIS); } if (CurrentMap == null) diff --git a/src/ST-Events/TriggerEndTouch.cs b/src/ST-Events/TriggerEndTouch.cs index 59fdedd..73da580 100644 --- a/src/ST-Events/TriggerEndTouch.cs +++ b/src/ST-Events/TriggerEndTouch.cs @@ -43,10 +43,7 @@ internal HookResult OnTriggerEndTouch(CEntityIOOutput output, string name, CEnti { // Get velocities for DB queries // Get the velocity of the player - we will be using this values to compare and write to DB - float velocity_x = player.Controller.PlayerPawn.Value!.AbsVelocity.X; - float velocity_y = player.Controller.PlayerPawn.Value!.AbsVelocity.Y; - float velocity_z = player.Controller.PlayerPawn.Value!.AbsVelocity.Z; - float velocity = (float)Math.Sqrt(velocity_x * velocity_x + velocity_y * velocity_y + velocity_z + velocity_z); + Vector_t velocity = player.Controller.PlayerPawn.Value!.AbsVelocity.ToVector_t(); // Map start zones -- hook into map_start, (s)tage1_start if (trigger.Entity.Name.Contains("map_start") || @@ -65,10 +62,10 @@ internal HookResult OnTriggerEndTouch(CEntityIOOutput output, string name, CEnti } // Prespeed display - player.Controller.PrintToCenter($"Prespeed: {velocity.ToString("0")} u/s"); - player.Stats.ThisRun.StartVelX = velocity_x; // Start pre speed for the Map run - player.Stats.ThisRun.StartVelY = velocity_y; // Start pre speed for the Map run - player.Stats.ThisRun.StartVelZ = velocity_z; // Start pre speed for the Map run + player.Controller.PrintToCenter($"Prespeed: {velocity.velMag():0} u/s"); + player.Stats.ThisRun.StartVelX = velocity.X; // Start pre speed for the Map run + player.Stats.ThisRun.StartVelY = velocity.Y; // Start pre speed for the Map run + player.Stats.ThisRun.StartVelZ = velocity.Z; // Start pre speed for the Map run #if DEBUG player.Controller.PrintToChat($"CS2 Surf DEBUG >> CBaseTrigger_{ChatColors.LightRed}EndTouchFunc{ChatColors.Default} -> {ChatColors.Green}Map Start Zone"); @@ -104,25 +101,25 @@ internal HookResult OnTriggerEndTouch(CEntityIOOutput output, string name, CEnti // player.Controller.PrintToChat($"{ChatColors.Green}Started{ChatColors.Default} Stage timer for stage {ChatColors.Green}{stage}{ChatColors.Default}"); // Show Prespeed for Stages - will be enabled/disabled by the user? - player.Controller.PrintToCenter($"Stage {stage} - Prespeed: {velocity.ToString("0")} u/s"); + player.Controller.PrintToCenter($"Stage {stage} - Prespeed: {velocity.velMag().ToString("0")} u/s"); } else if (player.Timer.IsRunning) { #if DEBUG - Console.WriteLine($"currentCheckpoint.EndVelX {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX} - velocity_x {velocity_x}"); - Console.WriteLine($"currentCheckpoint.EndVelY {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY} - velocity_y {velocity_y}"); - Console.WriteLine($"currentCheckpoint.EndVelZ {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ} - velocity_z {velocity_z}"); + Console.WriteLine($"currentCheckpoint.EndVelX {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX} - velocity.X {velocity.X}"); + Console.WriteLine($"currentCheckpoint.EndVelY {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY} - velocity.Y {velocity.Y}"); + Console.WriteLine($"currentCheckpoint.EndVelZ {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ} - velocity.Z {velocity.Z}"); Console.WriteLine($"currentCheckpoint.Attempts {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].Attempts}"); #endif // Update the Checkpoint object values - player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX = velocity_x; - player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY = velocity_y; - player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ = velocity_z; + player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX = velocity.X; + player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY = velocity.Y; + player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ = velocity.Z; player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndTouch = player.Timer.Ticks; // Show Prespeed for Checkpoints - will be enabled/disabled by the user? - player.Controller.PrintToCenter($"Checkpoint {player.Timer.Checkpoint} - Prespeed: {velocity.ToString("0")} u/s"); + player.Controller.PrintToCenter($"Checkpoint {player.Timer.Checkpoint} - Prespeed: {velocity.velMag():0} u/s"); } } @@ -138,9 +135,9 @@ internal HookResult OnTriggerEndTouch(CEntityIOOutput output, string name, CEnti if (player.Timer.Checkpoint != 0 && player.Timer.Checkpoint <= player.Stats.ThisRun.Checkpoints.Count) { #if DEBUG - Console.WriteLine($"currentCheckpoint.EndVelX {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX} - velocity_x {velocity_x}"); - Console.WriteLine($"currentCheckpoint.EndVelY {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY} - velocity_y {velocity_y}"); - Console.WriteLine($"currentCheckpoint.EndVelZ {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ} - velocity_z {velocity_z}"); + Console.WriteLine($"currentCheckpoint.EndVelX {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX} - velocity.X {velocity.X}"); + Console.WriteLine($"currentCheckpoint.EndVelY {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY} - velocity.Y {velocity.Y}"); + Console.WriteLine($"currentCheckpoint.EndVelZ {player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ} - velocity.Z {velocity.Z}"); #endif if (player.Timer.IsRunning && player.ReplayRecorder.IsRecording) @@ -150,13 +147,13 @@ internal HookResult OnTriggerEndTouch(CEntityIOOutput output, string name, CEnti } // Update the Checkpoint object values - player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX = velocity_x; - player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY = velocity_y; - player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ = velocity_z; + player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelX = velocity.X; + player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelY = velocity.Y; + player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndVelZ = velocity.Z; player.Stats.ThisRun.Checkpoints[player.Timer.Checkpoint].EndTouch = player.Timer.Ticks; // Show Prespeed for stages - will be enabled/disabled by the user? - player.Controller.PrintToCenter($"Checkpoint {Regex.Match(trigger.Entity.Name, "[0-9][0-9]?").Value} - Prespeed: {velocity.ToString("0")} u/s"); + player.Controller.PrintToCenter($"Checkpoint {Regex.Match(trigger.Entity.Name, "[0-9][0-9]?").Value} - Prespeed: {velocity.velMag():0} u/s"); } } @@ -187,10 +184,10 @@ internal HookResult OnTriggerEndTouch(CEntityIOOutput output, string name, CEnti } // Prespeed display - player.Controller.PrintToCenter($"Prespeed: {velocity.ToString("0")} u/s"); - player.Stats.ThisRun.StartVelX = velocity_x; // Start pre speed for the Bonus run - player.Stats.ThisRun.StartVelY = velocity_y; // Start pre speed for the Bonus run - player.Stats.ThisRun.StartVelZ = velocity_z; // Start pre speed for the Bonus run + player.Controller.PrintToCenter($"Prespeed: {velocity.velMag():0)} u/s"); + player.Stats.ThisRun.StartVelX = velocity.X; // Start pre speed for the Bonus run + player.Stats.ThisRun.StartVelY = velocity.Y; // Start pre speed for the Bonus run + player.Stats.ThisRun.StartVelZ = velocity.Z; // Start pre speed for the Bonus run } } diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index 192fbb1..7e3083d 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -44,9 +44,7 @@ internal HookResult OnTriggerStartTouch(CEntityIOOutput output, string name, CEn { // Get velocities for DB queries // Get the velocity of the player - we will be using this values to compare and write to DB - float velocity_x = player.Controller.PlayerPawn.Value!.AbsVelocity.X; - float velocity_y = player.Controller.PlayerPawn.Value!.AbsVelocity.Y; - float velocity_z = player.Controller.PlayerPawn.Value!.AbsVelocity.Z; + Vector_t velocity = player.Controller.PlayerPawn.Value!.AbsVelocity.ToVector_t(); int pStyle = player.Timer.Style; // Map end zones -- hook into map_end @@ -58,9 +56,9 @@ internal HookResult OnTriggerStartTouch(CEntityIOOutput output, string name, CEn player.ReplayRecorder.MapSituations.Add(player.Timer.Ticks); player.Stats.ThisRun.Ticks = player.Timer.Ticks; // End time for the Map run - player.Stats.ThisRun.EndVelX = velocity_x; // End speed for the Map run - player.Stats.ThisRun.EndVelY = velocity_y; // End speed for the Map run - player.Stats.ThisRun.EndVelZ = velocity_z; // End speed for the Map run + player.Stats.ThisRun.EndVelX = velocity.X; // End speed for the Map run + player.Stats.ThisRun.EndVelY = velocity.Y; // End speed for the Map run + player.Stats.ThisRun.EndVelZ = velocity.Z; // End speed for the Map run // MAP END ZONE - Map RUN @@ -123,7 +121,7 @@ internal HookResult OnTriggerStartTouch(CEntityIOOutput output, string name, CEn ============== INSERT INTO `MapTimes` (`player_id`, `map_id`, `style`, `type`, `stage`, `run_time`, `start_vel_x`, `start_vel_y`, `start_vel_z`, `end_vel_x`, `end_vel_y`, `end_vel_z`, `run_date`) VALUES ({player.Profile.ID}, {CurrentMap.ID}, {pStyle}, 0, 0, {player.Stats.ThisRun.Ticks}, - {player.Stats.ThisRun.StartVelX}, {player.Stats.ThisRun.StartVelY}, {player.Stats.ThisRun.StartVelZ}, {velocity_x}, {velocity_y}, {velocity_z}, {(int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()}) + {player.Stats.ThisRun.StartVelX}, {player.Stats.ThisRun.StartVelY}, {player.Stats.ThisRun.StartVelZ}, {velocity.X}, {velocity.Y}, {velocity.Z}, {(int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()}) ON DUPLICATE KEY UPDATE run_time=VALUES(run_time), start_vel_x=VALUES(start_vel_x), start_vel_y=VALUES(start_vel_y), start_vel_z=VALUES(start_vel_z), end_vel_x=VALUES(end_vel_x), end_vel_y=VALUES(end_vel_y), end_vel_z=VALUES(end_vel_z), run_date=VALUES(run_date); "); @@ -374,9 +372,9 @@ internal HookResult OnTriggerStartTouch(CEntityIOOutput output, string name, CEn { Checkpoint cp2 = new Checkpoint(player.Timer.Checkpoint, player.Timer.Ticks, - velocity_x, - velocity_y, - velocity_z, + velocity.X, + velocity.Y, + velocity.Z, -1.0f, -1.0f, -1.0f, @@ -423,9 +421,9 @@ internal HookResult OnTriggerStartTouch(CEntityIOOutput output, string name, CEn // store the checkpoint in the player's current run checkpoints used for Checkpoint functionality Checkpoint cp2 = new Checkpoint(checkpoint, player.Timer.Ticks, - velocity_x, - velocity_y, - velocity_z, + velocity.X, + velocity.Y, + velocity.Z, -1.0f, -1.0f, -1.0f, @@ -479,9 +477,9 @@ internal HookResult OnTriggerStartTouch(CEntityIOOutput output, string name, CEn player.ReplayRecorder.BonusSituations.Add(player.Timer.Ticks); player.Stats.ThisRun.Ticks = player.Timer.Ticks; // End time for the run - player.Stats.ThisRun.EndVelX = velocity_x; // End pre speed for the run - player.Stats.ThisRun.EndVelY = velocity_y; // End pre speed for the run - player.Stats.ThisRun.EndVelZ = velocity_z; // End pre speed for the run + player.Stats.ThisRun.EndVelX = velocity.X; // End pre speed for the run + player.Stats.ThisRun.EndVelY = velocity.Y; // End pre speed for the run + player.Stats.ThisRun.EndVelZ = velocity.Z; // End pre speed for the run bool saveBonusTime = false; string PracticeString = ""; diff --git a/src/ST-Map/Map.cs b/src/ST-Map/Map.cs index bfc37dd..40e621e 100644 --- a/src/ST-Map/Map.cs +++ b/src/ST-Map/Map.cs @@ -60,18 +60,18 @@ internal class Map // Zone Origin Information /* Map Start/End zones */ - public Vector StartZone { get; set; } = new Vector(0, 0, 0); - public QAngle StartZoneAngles { get; set; } = new QAngle(0, 0, 0); - public Vector EndZone { get; set; } = new Vector(0, 0, 0); + public Vector_t StartZone { get; set; } = new Vector_t(0, 0, 0); + public QAngle_t StartZoneAngles { get; set; } = new QAngle_t(0, 0, 0); + public Vector_t EndZone { get; set; } = new Vector_t(0, 0, 0); /* Map Stage zones */ - public Vector[] StageStartZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector(0, 0, 0)).ToArray(); - public QAngle[] StageStartZoneAngles { get; } = Enumerable.Repeat(0, 99).Select(x => new QAngle(0, 0, 0)).ToArray(); + public Vector_t[] StageStartZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector_t(0, 0, 0)).ToArray(); + public QAngle_t[] StageStartZoneAngles { get; } = Enumerable.Repeat(0, 99).Select(x => new QAngle_t(0, 0, 0)).ToArray(); /* Map Bonus zones */ - public Vector[] BonusStartZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector(0, 0, 0)).ToArray(); // To-do: Implement bonuses - public QAngle[] BonusStartZoneAngles { get; } = Enumerable.Repeat(0, 99).Select(x => new QAngle(0, 0, 0)).ToArray(); // To-do: Implement bonuses - public Vector[] BonusEndZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector(0, 0, 0)).ToArray(); // To-do: Implement bonuses + public Vector_t[] BonusStartZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector_t(0, 0, 0)).ToArray(); // To-do: Implement bonuses + public QAngle_t[] BonusStartZoneAngles { get; } = Enumerable.Repeat(0, 99).Select(x => new QAngle_t(0, 0, 0)).ToArray(); // To-do: Implement bonuses + public Vector_t[] BonusEndZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector_t(0, 0, 0)).ToArray(); // To-do: Implement bonuses /* Map Checkpoint zones */ - public Vector[] CheckpointStartZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector(0, 0, 0)).ToArray(); + public Vector_t[] CheckpointStartZone { get; } = Enumerable.Repeat(0, 99).Select(x => new Vector_t(0, 0, 0)).ToArray(); public ReplayManager ReplayManager { get; set; } = null!; @@ -168,8 +168,8 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") teleport.Entity!.Name.Contains("spawn_stage1_start") || teleport.Entity!.Name.Contains("spawn_s1_start"))) { - this.StartZone = new Vector(teleport.AbsOrigin!.X, teleport.AbsOrigin!.Y, teleport.AbsOrigin!.Z); - this.StartZoneAngles = new QAngle(teleport.AbsRotation!.X, teleport.AbsRotation!.Y, teleport.AbsRotation!.Z); + this.StartZone = new Vector_t(teleport.AbsOrigin!.X, teleport.AbsOrigin!.Y, teleport.AbsOrigin!.Z); + this.StartZoneAngles = new QAngle_t(teleport.AbsRotation!.X, teleport.AbsRotation!.Y, teleport.AbsRotation!.Z); foundPlayerSpawn = true; break; } @@ -177,14 +177,14 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") if (!foundPlayerSpawn) { - this.StartZone = new Vector(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); + this.StartZone = new Vector_t(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); } } // Map end zone else if (trigger.Entity!.Name.Contains("map_end")) { - this.EndZone = new Vector(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); + this.EndZone = new Vector_t(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); } // Stage start zones @@ -199,8 +199,8 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") if (teleport.Entity!.Name != null && (IsInZone(trigger.AbsOrigin!, trigger.Collision.BoundingRadius, teleport.AbsOrigin!) || (Regex.Match(teleport.Entity.Name, "^spawn_s([1-9][0-9]?|tage[1-9][0-9]?)_start$").Success && Int32.Parse(Regex.Match(teleport.Entity.Name, "[0-9][0-9]?").Value) == stage))) { - this.StageStartZone[stage] = new Vector(teleport.AbsOrigin!.X, teleport.AbsOrigin!.Y, teleport.AbsOrigin!.Z); - this.StageStartZoneAngles[stage] = new QAngle(teleport.AbsRotation!.X, teleport.AbsRotation!.Y, teleport.AbsRotation!.Z); + this.StageStartZone[stage] = new Vector_t(teleport.AbsOrigin!.X, teleport.AbsOrigin!.Y, teleport.AbsOrigin!.Z); + this.StageStartZoneAngles[stage] = new QAngle_t(teleport.AbsRotation!.X, teleport.AbsRotation!.Y, teleport.AbsRotation!.Z); this.Stages++; // Count stage zones for the map to populate DB foundPlayerSpawn = true; break; @@ -209,7 +209,7 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") if (!foundPlayerSpawn) { - this.StageStartZone[stage] = new Vector(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); + this.StageStartZone[stage] = new Vector_t(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); this.Stages++; } } @@ -217,7 +217,7 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") // Checkpoint start zones (linear maps) else if (Regex.Match(trigger.Entity.Name, "^map_c(p[1-9][0-9]?|heckpoint[1-9][0-9]?)$").Success) { - this.CheckpointStartZone[Int32.Parse(Regex.Match(trigger.Entity.Name, "[0-9][0-9]?").Value)] = new Vector(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); + this.CheckpointStartZone[Int32.Parse(Regex.Match(trigger.Entity.Name, "[0-9][0-9]?").Value)] = new Vector_t(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); this.TotalCheckpoints++; // Might be useful to have this in DB entry } @@ -233,8 +233,8 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") if (teleport.Entity!.Name != null && (IsInZone(trigger.AbsOrigin!, trigger.Collision.BoundingRadius, teleport.AbsOrigin!) || (Regex.Match(teleport.Entity.Name, "^spawn_b([1-9][0-9]?|onus[1-9][0-9]?)_start$").Success && Int32.Parse(Regex.Match(teleport.Entity.Name, "[0-9][0-9]?").Value) == bonus))) { - this.BonusStartZone[bonus] = new Vector(teleport.AbsOrigin!.X, teleport.AbsOrigin!.Y, teleport.AbsOrigin!.Z); - this.BonusStartZoneAngles[bonus] = new QAngle(teleport.AbsRotation!.X, teleport.AbsRotation!.Y, teleport.AbsRotation!.Z); + this.BonusStartZone[bonus] = new Vector_t(teleport.AbsOrigin!.X, teleport.AbsOrigin!.Y, teleport.AbsOrigin!.Z); + this.BonusStartZoneAngles[bonus] = new QAngle_t(teleport.AbsRotation!.X, teleport.AbsRotation!.Y, teleport.AbsRotation!.Z); this.Bonuses++; // Count bonus zones for the map to populate DB foundPlayerSpawn = true; break; @@ -243,14 +243,14 @@ internal void Map_Load_Zones([CallerMemberName] string methodName = "") if (!foundPlayerSpawn) { - this.BonusStartZone[bonus] = new Vector(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); + this.BonusStartZone[bonus] = new Vector_t(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); this.Bonuses++; } } else if (Regex.Match(trigger.Entity.Name, "^b([1-9][0-9]?|onus[1-9][0-9]?)_end$").Success) { - this.BonusEndZone[Int32.Parse(Regex.Match(trigger.Entity.Name, "[0-9][0-9]?").Value)] = new Vector(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); + this.BonusEndZone[Int32.Parse(Regex.Match(trigger.Entity.Name, "[0-9][0-9]?").Value)] = new Vector_t(trigger.AbsOrigin!.X, trigger.AbsOrigin!.Y, trigger.AbsOrigin!.Z); } } } @@ -395,6 +395,13 @@ internal async Task LoadMapRecordRuns([CallerMemberName] string methodName = "") // int totalBonusRuns = 0; this.ConnectedMapTimes.Clear(); + int qType; + int qStage; + int qStyle; + + // Replay Stuff + JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = false, Converters = { new Vector_tConverter(), new QAngle_tConverter() } }; + var runs = await _dataService.GetMapRecordRunsAsync(this.ID); foreach (var run in runs) @@ -494,7 +501,7 @@ internal async Task Get_Record_Run_Checkpoints(int style = 0) /// Base64 encoded string for the replay_frames internal void SetReplayData(int type, int style, int stage, string replayFramesBase64, [CallerMemberName] string methodName = "") { - JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = false, Converters = { new VectorConverter(), new QAngleConverter() } }; + JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = false, Converters = { new Vector_tConverter(), new QAngle_tConverter() } }; // Decompress the Base64 string string json = Compressor.Decompress(replayFramesBase64); @@ -666,7 +673,7 @@ internal void SetReplayData(int type, int style, int stage, string replayFramesB } // Start the new map replay if none existed until now - if (type == 0 && this.ReplayManager.MapWR != null && !this.ReplayManager.MapWR.IsPlaying) + Server.NextFrame(() => { // Console.WriteLine($"CS2 Surf DEBUG >> internal class Map -> internal void SetReplayData -> [MapWR] ResetReplay() and Start()"); this.ReplayManager.MapWR.ResetReplay(); diff --git a/src/ST-Player/Replay/ReplayFrame.cs b/src/ST-Player/Replay/ReplayFrame.cs index a5a8b11..2bf493e 100644 --- a/src/ST-Player/Replay/ReplayFrame.cs +++ b/src/ST-Player/Replay/ReplayFrame.cs @@ -1,4 +1,8 @@ namespace SurfTimer; + +using System; +using System.Numerics; +using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; internal enum ReplayFrameSituation @@ -31,15 +35,14 @@ internal class ReplayFrame public float[] pos { get; set; } = { 0, 0, 0 }; public float[] ang { get; set; } = { 0, 0, 0 }; public ReplayFrameSituation Situation { get; set; } = ReplayFrameSituation.NONE; - public uint Flags { get; set; } + public uint Flags { get; set; } - public Vector GetPos() + public Vector_t GetPos() { - return new Vector(this.pos[0], this.pos[1], this.pos[2]); + return new Vector_t(this.pos[0], this.pos[1], this.pos[2]); } - - public QAngle GetAng() + public QAngle_t GetAng() { - return new QAngle(this.ang[0], this.ang[1], this.ang[2]); + return new QAngle_t(this.ang[0], this.ang[1], this.ang[2]); } } diff --git a/src/ST-Player/Replay/ReplayPlayer.cs b/src/ST-Player/Replay/ReplayPlayer.cs index fa098da..962c3ac 100644 --- a/src/ST-Player/Replay/ReplayPlayer.cs +++ b/src/ST-Player/Replay/ReplayPlayer.cs @@ -105,13 +105,17 @@ public void Start([CallerMemberName] string methodName = "") if (!this.IsPlayable || !this.IsEnabled) return; + Server.NextFrame(() => + { this.FormatBotName(); this.IsPlaying = true; + #if DEBUG _logger.LogDebug("[{ClassName}] {MethodName} -> Starting replay for run {MapTimeID} (Map ID {MapID}) - {RecordPlayerName} (Stage {Stage})", nameof(ReplayPlayer), methodName, this.MapTimeID, this.MapID, this.RecordPlayerName, this.Stage ); #endif + }); } public void Stop([CallerMemberName] string methodName = "") @@ -174,13 +178,13 @@ public void Tick() } // END OF BLASPHEMY - var current_pos = this.Controller!.PlayerPawn.Value!.AbsOrigin!; + var current_pos = Controller!.PlayerPawn.Value!.AbsOrigin!.ToVector_t(); var current_frame_pos = current_frame.GetPos(); var current_frame_ang = current_frame.GetAng(); bool is_on_ground = (current_frame.Flags & (uint)PlayerFlags.FL_ONGROUND) != 0; - Vector velocity = (current_frame_pos - current_pos) * 64; + Vector_t velocity = (current_frame_pos - current_pos) * 64; if (is_on_ground) this.Controller.PlayerPawn.Value.MoveType = MoveType_t.MOVETYPE_WALK; @@ -188,9 +192,9 @@ public void Tick() this.Controller.PlayerPawn.Value.MoveType = MoveType_t.MOVETYPE_NOCLIP; if ((current_pos - current_frame_pos).Length() > 200) - this.Controller.PlayerPawn.Value.Teleport(current_frame_pos, current_frame_ang, new Vector(nint.Zero)); + Extensions.Teleport(Controller.PlayerPawn.Value, current_frame_pos, current_frame_ang, null); else - this.Controller.PlayerPawn.Value.Teleport(new Vector(nint.Zero), current_frame_ang, velocity); + Extensions.Teleport(Controller.PlayerPawn.Value, null , current_frame_ang, velocity); if (!this.IsPaused) diff --git a/src/ST-Player/Replay/ReplayRecorder.cs b/src/ST-Player/Replay/ReplayRecorder.cs index 8fbf2c3..61bacee 100644 --- a/src/ST-Player/Replay/ReplayRecorder.cs +++ b/src/ST-Player/Replay/ReplayRecorder.cs @@ -170,23 +170,20 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C if (this.Frames.Count == 0) { _logger.LogError("[{ClassName}] {MethodName} -> There are no Frames available for trimming for player {Name}", - nameof(ReplayRecorder), methodName, player.Profile.Name - ); + nameof(ReplayRecorder), methodName, player.Profile.Name + ); throw new Exception("There are no Frames available for trimming"); } switch (type) { - case 0: // Trim Map replays - // Map/Bonus runs - var start_enter_index = MapSituations[0]; - var start_exit_index = MapSituations[1]; - var end_enter_index = MapSituations[2]; - // var start_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_ENTER); // This comes out as `-1` somehow, need to edit the IF statement below to use the `start_exit_index` in that case and not care about this variable - // var start_exit_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_EXIT); - // var end_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.END_ZONE_ENTER); + case 0: // Map Run + { + var start_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_ENTER); + var start_exit_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_EXIT); + var end_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.END_ZONE_ENTER); + _logger.LogInformation("[{ClassName}] {MethodName} -> Trimming Map Run replay. Last start enter {start_enter_index} | last start exit {start_exit_index} | end enter {end_enter_index}", - nameof(ReplayRecorder), methodName, start_enter_index, start_exit_index, end_enter_index - ); + nameof(ReplayRecorder), methodName, start_enter_index, start_exit_index, end_enter_index); if (start_enter_index == -1) { @@ -199,32 +196,12 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C if (start_enter_index != -1 && start_exit_index != -1 && end_enter_index != -1) { - // Try different buffer sizes for start index - int startIndex; - if (start_exit_index - (Config.ReplaysPre * 2) >= start_enter_index) - startIndex = start_exit_index - (Config.ReplaysPre * 2); - else if (start_exit_index - Config.ReplaysPre >= start_enter_index) - startIndex = start_exit_index - Config.ReplaysPre; - else if (start_exit_index - (Config.ReplaysPre / 2) >= start_enter_index) - startIndex = start_exit_index - (Config.ReplaysPre / 2); - else - startIndex = start_enter_index; // fallback to minimum allowed - // Try different buffer sizes for end index - int endIndex; - if (end_enter_index + (Config.ReplaysPre * 2) < Frames.Count) - endIndex = end_enter_index + (Config.ReplaysPre * 2); - else if (end_enter_index + Config.ReplaysPre < Frames.Count) - endIndex = end_enter_index + Config.ReplaysPre; - else if (end_enter_index + (Config.ReplaysPre / 2) < Frames.Count) - endIndex = end_enter_index + (Config.ReplaysPre / 2); - else - // endIndex = Frames.Count - 1; // fallback to maximum allowed - endIndex = end_enter_index; // fallback to maximum allowed - // Get the range of frames - new_frames = Frames.GetRange(startIndex, endIndex - startIndex + 1); + int startIndex = CalculateStartIndex(start_enter_index, start_exit_index, Config.ReplaysPre); + int endIndex = CalculateEndIndex(end_enter_index, Frames.Count, Config.ReplaysPre); + new_frames = GetTrimmedFrames(startIndex, endIndex); + _logger.LogInformation("<<< [{ClassName}] {MethodName} -> Trimmed from {StartIndex} to {EndIndex} (new_frames = {NewFramesCount}) - from total {TotalFrames}", - nameof(ReplayRecorder), methodName, startIndex, endIndex, new_frames.Count, this.Frames.Count - ); + nameof(ReplayRecorder), methodName, startIndex, endIndex, new_frames.Count, this.Frames.Count); } else { @@ -233,11 +210,12 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C ); } break; - case 1: // Trim Bonus replays - // Bonus runs - int bonus_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_ENTER); - int bonus_exit_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_EXIT); - int bonus_end_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.END_ZONE_ENTER); + } + case 1: // Bonus Run + { + var bonus_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_ENTER); + var bonus_exit_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.START_ZONE_EXIT); + var bonus_end_enter_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.END_ZONE_ENTER); _logger.LogInformation("[{ClassName}] {MethodName} -> Looking for Bonus Run replay trim indexes. Last start enter {bonus_enter_index}, last start exit {bonus_exit_index}, end enter {bonus_end_enter_index}", nameof(ReplayRecorder), methodName, bonus_enter_index, bonus_exit_index, bonus_end_enter_index ); @@ -252,29 +230,10 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C if (bonus_enter_index != -1 && bonus_exit_index != -1 && bonus_end_enter_index != -1) { - // Try different buffer sizes for start index - int startIndex; - if (bonus_exit_index - (Config.ReplaysPre * 2) >= bonus_enter_index) - startIndex = bonus_exit_index - (Config.ReplaysPre * 2); - else if (bonus_exit_index - Config.ReplaysPre >= bonus_enter_index) - startIndex = bonus_exit_index - Config.ReplaysPre; - else if (bonus_exit_index - (Config.ReplaysPre / 2) >= bonus_enter_index) - startIndex = bonus_exit_index - (Config.ReplaysPre / 2); - else - startIndex = bonus_enter_index; // fallback to minimum allowed - // Try different buffer sizes for end index - int endIndex; - if (bonus_end_enter_index + (Config.ReplaysPre * 2) < Frames.Count) - endIndex = bonus_end_enter_index + (Config.ReplaysPre * 2); - else if (bonus_end_enter_index + Config.ReplaysPre < Frames.Count) - endIndex = bonus_end_enter_index + Config.ReplaysPre; - else if (bonus_end_enter_index + (Config.ReplaysPre / 2) < Frames.Count) - endIndex = bonus_end_enter_index + (Config.ReplaysPre / 2); - else - // endIndex = Frames.Count - 1; // fallback to maximum allowed - endIndex = bonus_end_enter_index; // fallback to maximum allowed - // Get the range of frames - new_frames = Frames.GetRange(startIndex, endIndex - startIndex + 1); + int startIndex = CalculateStartIndex(bonus_enter_index, bonus_exit_index, Config.ReplaysPre); + int endIndex = CalculateEndIndex(bonus_end_enter_index, Frames.Count, Config.ReplaysPre); + new_frames = GetTrimmedFrames(startIndex, endIndex); + _logger.LogInformation("<<< [{ClassName}] {MethodName} -> Trimmed Bonus replay from {startIndex} to {endIndex} ({new_frames}) - from total {OldFrames}", nameof(ReplayRecorder), methodName, startIndex, endIndex, new_frames.Count, this.Frames.Count ); @@ -286,44 +245,47 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C ); } break; - case 2: // Trim Stage replays + } + case 2: // Stage Run + { int stage_end_index; int stage_exit_index; int stage_enter_index; + _logger.LogInformation("[{ClassName}] {MethodName} -> Looking for Stage Run replay trim indexes. Stage {Stage}, available frames {TotalFrames}", nameof(ReplayRecorder), methodName, player.Timer.Stage - 1, Frames.Count ); - // Stage runs - if (lastStage) // Last stage + + if (lastStage) { _logger.LogTrace("Stage replay trimming will use `STAGE_ZONE_X` + `END_ZONE_ENTER`"); - stage_end_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.END_ZONE_ENTER); // Last stage enter (finishing the stage) + stage_end_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.END_ZONE_ENTER); _logger.LogTrace("stage_end_index = {stage_end_index}", - stage_end_index + stage_end_index ); - stage_exit_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_EXIT); // Exiting the previous stage zone (what we are looking for start of the stage run) + stage_exit_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_EXIT); _logger.LogTrace("stage_exit_index = {stage_exit_index}", - stage_exit_index + stage_exit_index ); - stage_enter_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); // Entering the previous stage zone (what we are looking for pre-speed trim) + stage_enter_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); _logger.LogTrace("stage_enter_index = {stage_enter_index}", - stage_enter_index + stage_enter_index ); } - else if (player.Timer.Stage - 1 > 1) // Not first stage + else if (player.Timer.Stage - 1 > 1) { _logger.LogTrace("Stage replay trimming will use `STAGE_ZONE_X`"); - stage_end_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); // Last stage enter (finishing the stage) + stage_end_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); _logger.LogTrace("stage_end_index = {stage_end_index}", - stage_end_index + stage_end_index ); - stage_exit_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_EXIT); // Exiting the previous stage zone (what we are looking for start of the stage run) + stage_exit_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_EXIT); _logger.LogTrace("stage_exit_index = {stage_exit_index}", - stage_exit_index + stage_exit_index ); - stage_enter_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); // Entering the previous stage zone (what we are looking for pre-speed trim) + stage_enter_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); _logger.LogTrace("stage_enter_index = {stage_enter_index}", - stage_enter_index + stage_enter_index ); } else if (player.Timer.Stage - 1 == -1) // Don't crash...? @@ -338,26 +300,26 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C stage_end_index = -1; stage_exit_index = -1; } - else // First stage is always the start of the map so we are looking for START_ZONE_X + else { _logger.LogInformation("Stage replay trimming will use `START_ZONE_X`"); - stage_end_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); // Last stage enter (finishing the stage) + stage_end_index = Frames.FindLastIndex(f => f.Situation == ReplayFrameSituation.STAGE_ZONE_ENTER); _logger.LogTrace("stage_end_index = {stage_end_index}", - stage_end_index + stage_end_index ); - stage_exit_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.START_ZONE_EXIT); // Exiting the previous stage zone (what we are looking for start of the stage run) + stage_exit_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.START_ZONE_EXIT); _logger.LogTrace("stage_exit_index = {stage_exit_index}", - stage_exit_index + stage_exit_index ); - stage_enter_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.START_ZONE_ENTER); // Entering the previous stage zone (what we are looking for pre-speed trim) + stage_enter_index = Frames.FindLastIndex(stage_end_index - 1, f => f.Situation == ReplayFrameSituation.START_ZONE_ENTER); _logger.LogTrace("stage_enter_index = {stage_enter_index}", - stage_enter_index + stage_enter_index ); } - _logger.LogInformation("[{ClassName}] {MethodName} -> Trimming Stage Run replay. Stage {Stage}, enter {stage_enter_index}, stage exit {stage_exit_index}, stage end {stage_end_index}", - nameof(ReplayRecorder), methodName, player.Timer.Stage - 1, stage_enter_index, stage_exit_index, stage_end_index + _logger.LogInformation("[{ClassName}] {MethodName} -> Trimming Stage Run replay. Stage {Stage}, enter {stage_enter_index}, stage exit {stage_exit_index}, stage end {stage_end_index}", + nameof(ReplayRecorder), methodName, player.Timer.Stage - 1, stage_enter_index, stage_exit_index, stage_end_index ); - + if (stage_enter_index == -1) { _logger.LogError("[{ClassName}] {MethodName} -> Player '{Name}' got '-1' for stage_enter_index during Stage ({StageNumber}) replay trimming. Setting 'stage_enter_index' to '0'", @@ -368,29 +330,10 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C if (stage_enter_index != -1 && stage_exit_index != -1 && stage_end_index != -1) { - // Try different buffer sizes for start index - int startIndex; - if (stage_exit_index - (Config.ReplaysPre * 2) >= stage_enter_index) - startIndex = stage_exit_index - (Config.ReplaysPre * 2); - else if (stage_exit_index - Config.ReplaysPre >= stage_enter_index) - startIndex = stage_exit_index - Config.ReplaysPre; - else if (stage_exit_index - (Config.ReplaysPre / 2) >= stage_enter_index) - startIndex = stage_exit_index - (Config.ReplaysPre / 2); - else - startIndex = stage_enter_index; // fallback to minimum allowed - // Try different buffer sizes for end index - int endIndex; - if (stage_end_index + (Config.ReplaysPre * 2) < Frames.Count) - endIndex = stage_end_index + (Config.ReplaysPre * 2); - else if (stage_end_index + Config.ReplaysPre < Frames.Count) - endIndex = stage_end_index + Config.ReplaysPre; - else if (stage_end_index + (Config.ReplaysPre / 2) < Frames.Count) - endIndex = stage_end_index + (Config.ReplaysPre / 2); - else - // endIndex = Frames.Count - 1; // fallback to maximum allowed - endIndex = stage_end_index; // fallback to maximum allowed - // Get the range of frames - new_frames = Frames.GetRange(startIndex, endIndex - startIndex + 1); + int startIndex = CalculateStartIndex(stage_enter_index, stage_exit_index, Config.ReplaysPre); + int endIndex = CalculateEndIndex(stage_end_index, Frames.Count, Config.ReplaysPre); + new_frames = GetTrimmedFrames(startIndex, endIndex); + _logger.LogInformation("<<< [{ClassName}] {MethodName} -> Trimmed Stage replay from {startIndex} to {endIndex} ({new_frames}) - from total {OldFrames}", nameof(ReplayRecorder), methodName, startIndex, endIndex, new_frames.Count, this.Frames.Count ); @@ -402,7 +345,8 @@ public string TrimReplay(Player player, int type = 0, bool lastStage = false, [C ); } break; - } + } + } this.IsSaving = false; string trimmed = JsonSerializer.Serialize(new_frames); @@ -442,4 +386,32 @@ public int LastExitTick(int start_idx = 0) } return 0; } + private int CalculateStartIndex(int start_enter, int start_exit, int buffer) + { + if (start_exit - (buffer * 2) >= start_enter) + return start_exit - (buffer * 2); + else if (start_exit - buffer >= start_enter) + return start_exit - buffer; + else if (start_exit - (buffer / 2) >= start_enter) + return start_exit - (buffer / 2); + else + return start_enter; + } + + private int CalculateEndIndex(int end_enter, int totalFrames, int buffer) + { + if (end_enter + (buffer * 2) < totalFrames) + return end_enter + (buffer * 2); + else if (end_enter + buffer < totalFrames) + return end_enter + buffer; + else if (end_enter + (buffer / 2) < totalFrames) + return end_enter + (buffer / 2); + else + return end_enter; + } + + private List GetTrimmedFrames(int startIndex, int endIndex) + { + return Frames.GetRange(startIndex, endIndex - startIndex + 1); + } } \ No newline at end of file diff --git a/src/ST-Player/Saveloc/SavelocFrame.cs b/src/ST-Player/Saveloc/SavelocFrame.cs index a6bcd4c..7fd9db6 100644 --- a/src/ST-Player/Saveloc/SavelocFrame.cs +++ b/src/ST-Player/Saveloc/SavelocFrame.cs @@ -4,8 +4,8 @@ namespace SurfTimer; internal class SavelocFrame { - public Vector Pos { get; set; } = new Vector(0, 0, 0); - public QAngle Ang { get; set; } = new QAngle(0, 0, 0); - public Vector Vel { get; set; } = new Vector(0, 0, 0); + public Vector_t Pos { get; set; } = new Vector_t(0, 0, 0); + public QAngle_t Ang { get; set; } = new QAngle_t(0, 0, 0); + public Vector_t Vel { get; set; } = new Vector_t(0, 0, 0); public int Tick { get; set; } = 0; } diff --git a/src/ST-UTILS/Extensions.cs b/src/ST-UTILS/Extensions.cs new file mode 100644 index 0000000..493b3b5 --- /dev/null +++ b/src/ST-UTILS/Extensions.cs @@ -0,0 +1,57 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Memory; +using CounterStrikeSharp.API.Modules.Utils; + + +namespace SurfTimer; + +unsafe static class Extensions +{ + public static void Teleport(this CBaseEntity entity, Vector_t? position = null, QAngle_t? angles = null, Vector_t? velocity = null) + { + Guard.IsValidEntity(entity); + + void* pPos = null, pAng = null, pVel = null; + + // Structs are stored on the stack, GC should not break pointers. + + if (position.HasValue) + { + var pos = position.Value; // Remove nullable wrapper + pPos = &pos; + } + + if (angles.HasValue) + { + var ang = angles.Value; + pAng = ∠ + } + + if (velocity.HasValue) + { + var vel = velocity.Value; + pVel = &vel; + } + + VirtualFunction.CreateVoid(entity.Handle, GameData.GetOffset("CBaseEntity_Teleport"))(entity.Handle, (nint)pPos, + (nint)pAng, (nint)pVel); + } + + public static (Vector_t fwd, Vector_t right, Vector_t up) AngleVectors(this QAngle vec) => vec.ToQAngle_t().AngleVectors(); + public static void AngleVectors(this QAngle vec, out Vector_t fwd, out Vector_t right, out Vector_t up) => vec.ToQAngle_t().AngleVectors(out fwd, out right, out up); + + public static Vector_t ToVector_t(this Vector vec) => new(vec.Handle); + public static QAngle_t ToQAngle_t(this QAngle vec) => new(vec.Handle); + + public static void SetCollisionGroup(this CCSPlayerController controller, CollisionGroup collisionGroup) + { + if (!controller.IsValid || controller.Collision==null) return; + controller.Collision.CollisionAttribute.CollisionGroup = (byte)collisionGroup; + controller.Collision.CollisionGroup = (byte)collisionGroup; + + Utilities.SetStateChanged(controller, "CColisionProperity", "m_collisionGroup"); + Utilities.SetStateChanged(controller, "CCollisionProperty", "m_collisionAttribute"); + } +} \ No newline at end of file diff --git a/src/ST-UTILS/Structs/QAngle_t.cs b/src/ST-UTILS/Structs/QAngle_t.cs new file mode 100644 index 0000000..f00c804 --- /dev/null +++ b/src/ST-UTILS/Structs/QAngle_t.cs @@ -0,0 +1,123 @@ +using CounterStrikeSharp.API.Core; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SurfTimer; + +struct QAngle_t : IAdditionOperators, + ISubtractionOperators, + IMultiplyOperators, + IDivisionOperators +{ + public float X, Y, Z; + + public const int SIZE = 3; + + public unsafe float this[int i] + { + readonly get + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + return Unsafe.Read(Unsafe.Add(ptr, i)); + } + } + set + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + Unsafe.Write(Unsafe.Add(ptr, i), value); + } + } + } + + public QAngle_t() + { + } + + public unsafe QAngle_t(nint ptr) : this(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef((void*)ptr), SIZE)) + { + } + + public QAngle_t(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + public QAngle_t(ReadOnlySpan values) + { + if (values.Length < SIZE) + { + throw new ArgumentOutOfRangeException(nameof(values)); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + + public unsafe (Vector_t fwd, Vector_t right, Vector_t up) AngleVectors() + { + Vector_t fwd = default, right = default, up = default; + + nint pFwd = (nint)Unsafe.AsPointer(ref fwd); + nint pRight = (nint)Unsafe.AsPointer(ref right); + nint pUp = (nint)Unsafe.AsPointer(ref up); + + fixed (void* ptr = &this) + { + NativeAPI.AngleVectors((nint)ptr, pFwd, pRight, pUp); + } + + return ( fwd, right, up ); + } + + public unsafe void AngleVectors(out Vector_t fwd, out Vector_t right, out Vector_t up) + { + fixed (void* ptr = &this, pFwd = &fwd, pRight = &right, pUp = &up) + { + NativeAPI.AngleVectors((nint)ptr, (nint)pFwd, (nint)pRight, (nint)pUp); + } + } + + public readonly override string ToString() + { + return $"{X:n2} {Y:n2} {Z:n2}"; + } + + public static QAngle_t operator +(QAngle_t a, QAngle_t b) + { + return new QAngle_t(a.X + b.X, a.Y + b.Y, a.Z + b.Z); + } + + public static QAngle_t operator -(QAngle_t a, QAngle_t b) + { + return new QAngle_t(a.X - b.X, a.Y - b.Y, a.Z - b.Z); + } + + public static QAngle_t operator -(QAngle_t a) + { + return new QAngle_t(-a.X, -a.Y, -a.Z); + } + + public static QAngle_t operator *(QAngle_t a, float b) + { + return new QAngle_t(a.X * b, a.Y * b, a.Z * b); + } + + public static QAngle_t operator /(QAngle_t a, float b) + { + return new QAngle_t(a.X / b, a.Y / b, a.Z / b); + } +} \ No newline at end of file diff --git a/src/ST-UTILS/Structs/Vector_t.cs b/src/ST-UTILS/Structs/Vector_t.cs new file mode 100644 index 0000000..da8606a --- /dev/null +++ b/src/ST-UTILS/Structs/Vector_t.cs @@ -0,0 +1,124 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SurfTimer; + +struct Vector_t : IAdditionOperators, + ISubtractionOperators, + IMultiplyOperators, + IDivisionOperators +{ + public float X, Y, Z; + + public const int SIZE = 3; + + public unsafe float this[int i] + { + readonly get + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + return Unsafe.Read(Unsafe.Add(ptr, i)); + } + } + set + { + if (i < 0 || i > SIZE) + { + throw new IndexOutOfRangeException(); + } + + fixed (void* ptr = &this) + { + Unsafe.Write(Unsafe.Add(ptr, i), value); + } + } + } + + public Vector_t() + { + } + + public unsafe Vector_t(nint ptr) : this(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef((void*)ptr), SIZE)) + { + } + + public Vector_t(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + public Vector_t(ReadOnlySpan values) + { + if (values.Length < SIZE) + { + throw new ArgumentOutOfRangeException(nameof(values)); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + + public readonly float Length() + { + return (float)Math.Sqrt(X * X + Y * Y + Z * Z); + } + + public readonly float Length2D() + { + return (float)Math.Sqrt(X * X + Y * Y); + } + + public readonly float velMag() + { + return (float)Math.Sqrt(X * X + Y * Y + Z + Z ); + } + + public readonly bool IsZero(float tolerance = 0.0001f) + { + return Math.Abs(X) <= tolerance && Math.Abs(Y) <= tolerance && Math.Abs(Z) <= tolerance; + } + public void Scale(float scale) + { + X *= scale; + Y *= scale; + Z *= scale; + } + + public readonly override string ToString() + { + return $"{X:n2} {Y:n2} {Z:n2}"; + } + + public static Vector_t operator +(Vector_t a, Vector_t b) + { + return new Vector_t(a.X + b.X, a.Y + b.Y, a.Z + b.Z); + } + + public static Vector_t operator -(Vector_t a, Vector_t b) + { + return new Vector_t(a.X - b.X, a.Y - b.Y, a.Z - b.Z); + } + + public static Vector_t operator -(Vector_t a) + { + return new Vector_t(-a.X, -a.Y, -a.Z); + } + + public static Vector_t operator *(Vector_t a, float b) + { + return new Vector_t(a.X * b, a.Y * b, a.Z * b); + } + + public static Vector_t operator /(Vector_t a, float b) + { + return new Vector_t(a.X / b, a.Y / b, a.Z / b); + } +} \ No newline at end of file