Skip to content

Commit c6cacf2

Browse files
authored
Fixing every error I find in the replays + adding saveloc and stage/bonus support (#10)
* Fixed bot noclipin when no replay * Added saveloc/tele support * Added practice mark to messages when in practice mode * Now even when time not running, When tele it will start playing in practice mode * Forgot to add the tele args lol, Fixed now * Fixed error when !tele <num> larger than amount of teles or not a number * Fixed Map stages counting. why wasn't this fix already :( * Added support for more than one replay bot + plugin no longer relies on Will's version of cs2fixes * Displaying time instead of ticks in bot replay now. would like for someone to check if there is a better way, but this works pretty well * Added support for Personal Best replays with !pbreplay <maptime_Id/null> * Updated readme * Removed not needed comments * Works also when no wr exists * FormatTime correctly * Reverted uneeded changes * Temp patch for maps changing bot_quota, need to be addressed * Cleaned code
1 parent d978813 commit c6cacf2

File tree

18 files changed

+459
-101
lines changed

18 files changed

+459
-101
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Bold & Italics = being worked on.
4242
- [ ] Profile implementation (DB)
4343
- [ ] Points/Skill Groups (DB)
4444
- [ ] Player settings (DB)
45-
- [ ] Run replays
45+
- [X] Run replays
46+
- [X] Saveloc/Tele
4647
- [ ] Style implementation (SW, HSW, BW)
4748
- [ ] Paint (?)

cfg/SurfTimer/server_settings.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ sv_staminalandcost 0
3030
sv_timebetweenducks 0
3131

3232
// Some replay bot shit (took so fucking long to debug)
33-
bot_quota 1 // This is gonna be used to change the amount of bots allowed (per stages/bonuses/etc) when stages/bonuses/etc added
33+
// bot_quota 1 No need for this, because the server handles it
3434
bot_quota_mode "normal"
3535
bot_join_after_player 1
3636
bot_join_team CT

src/ST-Commands/PlayerCommands.cs

Lines changed: 192 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command)
4747
return;
4848

4949
int stage = Int32.Parse(command.ArgByIndex(1)) - 1;
50-
if (stage > CurrentMap.Stages - 1)
50+
if (stage > CurrentMap.Stages - 1 && CurrentMap.Stages > 0)
5151
stage = CurrentMap.Stages - 1;
5252

5353
// Must be 1 argument
@@ -84,7 +84,6 @@ public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command)
8484
player.PrintToChat($"{PluginPrefix} {ChatColors.Red}Invalid stage provided. Usage: {ChatColors.Green}!s <stage>");
8585
}
8686

87-
// Test command
8887
[ConsoleCommand("css_spec", "Moves a player automaticlly into spectator mode")]
8988
public void MovePlayerToSpectator(CCSPlayerController? player, CommandInfo command)
9089
{
@@ -94,31 +93,210 @@ public void MovePlayerToSpectator(CCSPlayerController? player, CommandInfo comma
9493
player.ChangeTeam(CsTeam.Spectator);
9594
}
9695

96+
/*
97+
#########################
98+
Reaplay Commands
99+
#########################
100+
*/
97101
[ConsoleCommand("css_replaybotpause", "Pause the replay bot playback")]
98102
[ConsoleCommand("css_rbpause", "Pause the replay bot playback")]
99103
public void PauseReplay(CCSPlayerController? player, CommandInfo command)
100104
{
101-
if (player == null
102-
|| player.Team != CsTeam.Spectator
103-
|| CurrentMap.ReplayBot.Controller == null
104-
|| !CurrentMap.ReplayBot.IsPlaying
105-
|| CurrentMap.ReplayBot.Controller.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum)
105+
if(player == null || player.Team != CsTeam.Spectator)
106106
return;
107107

108-
CurrentMap.ReplayBot.Pause();
108+
foreach(ReplayPlayer rb in CurrentMap.ReplayBots)
109+
{
110+
if(!rb.IsPlayable || !rb.IsPlaying || !playerList[player.UserId ?? 0].IsSpectating(rb.Controller!))
111+
continue;
112+
113+
rb.Pause();
114+
}
109115
}
110116

111117
[ConsoleCommand("css_replaybotflip", "Flips the replay bot between Forward/Backward playback")]
112118
[ConsoleCommand("css_rbflip", "Flips the replay bot between Forward/Backward playback")]
113119
public void ReverseReplay(CCSPlayerController? player, CommandInfo command)
114120
{
115-
if (player == null
116-
|| player.Team != CsTeam.Spectator
117-
|| CurrentMap.ReplayBot.Controller == null
118-
|| !CurrentMap.ReplayBot.IsPlaying
119-
|| CurrentMap.ReplayBot.Controller.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum)
121+
if(player == null || player.Team != CsTeam.Spectator)
122+
return;
123+
124+
foreach(ReplayPlayer rb in CurrentMap.ReplayBots)
125+
{
126+
if(!rb.IsPlayable || !rb.IsPlaying || !playerList[player.UserId ?? 0].IsSpectating(rb.Controller!))
127+
continue;
128+
129+
rb.FrameTickIncrement *= -1;
130+
}
131+
}
132+
133+
[ConsoleCommand("css_pbreplay", "Allows for replay of player's PB")]
134+
public void PbReplay(CCSPlayerController? player, CommandInfo command)
135+
{
136+
if(player == null)
137+
return;
138+
139+
int maptime_id = playerList[player!.UserId ?? 0].Stats.PB[playerList[player.UserId ?? 0].Timer.Style].ID;
140+
if (command.ArgCount > 1)
141+
{
142+
try
143+
{
144+
maptime_id = int.Parse(command.ArgByIndex(1));
145+
}
146+
catch {}
147+
}
148+
149+
if(maptime_id == -1 || !CurrentMap.ConnectedMapTimes.Contains(maptime_id))
150+
{
151+
player.PrintToChat($"{PluginPrefix} {ChatColors.Red}No time was found");
152+
return;
153+
}
154+
155+
for(int i = 0; i < CurrentMap.ReplayBots.Count; i++)
156+
{
157+
if(CurrentMap.ReplayBots[i].Stat_MapTimeID == maptime_id)
158+
{
159+
player.PrintToChat($"{PluginPrefix} {ChatColors.Red}A bot of this run already playing");
160+
return;
161+
}
162+
}
163+
164+
CurrentMap.ReplayBots = CurrentMap.ReplayBots.Prepend(new ReplayPlayer() {
165+
Stat_MapTimeID = maptime_id,
166+
Stat_Prefix = "PB"
167+
}).ToList();
168+
169+
Server.NextFrame(() => {
170+
Server.ExecuteCommand($"bot_quota {CurrentMap.ReplayBots.Count}");
171+
});
172+
}
173+
174+
/*
175+
########################
176+
Saveloc Commands
177+
########################
178+
*/
179+
[ConsoleCommand("css_saveloc", "Save current player location to be practiced")]
180+
public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command)
181+
{
182+
if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0))
183+
return;
184+
185+
Player p = playerList[player.UserId ?? 0];
186+
if (!p.Timer.IsRunning)
187+
{
188+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Cannot save location while not in run");
189+
return;
190+
}
191+
192+
var player_pos = p.Controller.Pawn.Value!.AbsOrigin!;
193+
var player_angle = p.Controller.PlayerPawn.Value!.EyeAngles;
194+
var player_velocity = p.Controller.PlayerPawn.Value!.AbsVelocity;
195+
196+
p.SavedLocations.Add(new SavelocFrame {
197+
Pos = new Vector(player_pos.X, player_pos.Y, player_pos.Z),
198+
Ang = new QAngle(player_angle.X, player_angle.Y, player_angle.Z),
199+
Vel = new Vector(player_velocity.X, player_velocity.Y, player_velocity.Z),
200+
Tick = p.Timer.Ticks
201+
});
202+
p.CurrentSavedLocation = p.SavedLocations.Count-1;
203+
204+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Green}Saved location! {ChatColors.Default} use !tele {p.SavedLocations.Count-1} to teleport to this location");
205+
}
206+
207+
[ConsoleCommand("css_tele", "Teleport player to current saved location")]
208+
public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo command)
209+
{
210+
if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0))
120211
return;
121212

122-
CurrentMap.ReplayBot.FrameTickIncrement *= -1;
213+
Player p = playerList[player.UserId ?? 0];
214+
215+
if(p.SavedLocations.Count == 0)
216+
{
217+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}No saved locations");
218+
return;
219+
}
220+
221+
if(!p.Timer.IsRunning)
222+
p.Timer.Start();
223+
224+
if (!p.Timer.IsPracticeMode)
225+
{
226+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Timer now on practice");
227+
p.Timer.IsPracticeMode = true;
228+
}
229+
230+
if(command.ArgCount > 1)
231+
try
232+
{
233+
int tele_n = int.Parse(command.ArgByIndex(1));
234+
if (tele_n < p.SavedLocations.Count)
235+
p.CurrentSavedLocation = tele_n;
236+
}
237+
catch { }
238+
SavelocFrame location = p.SavedLocations[p.CurrentSavedLocation];
239+
Server.NextFrame(() => {
240+
p.Controller.PlayerPawn.Value!.Teleport(location.Pos, location.Ang, location.Vel);
241+
p.Timer.Ticks = location.Tick;
242+
});
243+
244+
p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}");
245+
}
246+
247+
[ConsoleCommand("css_teleprev", "Teleport player to previous saved location")]
248+
public void TeleportPlayerLocationPrev(CCSPlayerController? player, CommandInfo command)
249+
{
250+
if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0))
251+
return;
252+
253+
Player p = playerList[player.UserId ?? 0];
254+
255+
if(p.SavedLocations.Count == 0)
256+
{
257+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}No saved locations");
258+
return;
259+
}
260+
261+
if(p.CurrentSavedLocation == 0)
262+
{
263+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Already at first location");
264+
}
265+
else
266+
{
267+
p.CurrentSavedLocation--;
268+
}
269+
270+
TeleportPlayerLocation(player, command);
271+
272+
p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}");
273+
}
274+
275+
[ConsoleCommand("css_telenext", "Teleport player to next saved location")]
276+
public void TeleportPlayerLocationNext(CCSPlayerController? player, CommandInfo command)
277+
{
278+
if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0))
279+
return;
280+
281+
Player p = playerList[player.UserId ?? 0];
282+
283+
if(p.SavedLocations.Count == 0)
284+
{
285+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}No saved locations");
286+
return;
287+
}
288+
289+
if(p.CurrentSavedLocation == p.SavedLocations.Count-1)
290+
{
291+
p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Already at last location");
292+
}
293+
else
294+
{
295+
p.CurrentSavedLocation++;
296+
}
297+
298+
TeleportPlayerLocation(player, command);
299+
300+
p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}");
123301
}
124302
}

src/ST-Events/Players.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,36 @@ namespace SurfTimer;
99

1010
public partial class SurfTimer
1111
{
12-
[GameEventHandler(HookMode.Post)]
12+
[GameEventHandler]
1313
public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
1414
{
1515
var controller = @event.Userid;
16-
if(!controller.IsValid)
16+
if(!controller.IsValid || !controller.IsBot)
1717
return HookResult.Continue;
1818

19-
if (controller.IsBot && CurrentMap.ReplayBot.Controller == null)
19+
for (int i = 0; i < CurrentMap.ReplayBots.Count; i++)
2020
{
21-
CurrentMap.ReplayBot.Controller = controller;
22-
// CurrentMap.ReplayBot.Controller.PlayerName = $"[REPLAY] {CurrentMap.Name}";
21+
if(CurrentMap.ReplayBots[i].IsPlayable)
22+
continue;
2323

24-
Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); // WHY COLORS NOT WORKING AHHHHH!!!!!
24+
int repeats = -1;
25+
if(CurrentMap.ReplayBots[i].Stat_Prefix == "PB")
26+
repeats = 3;
27+
28+
CurrentMap.ReplayBots[i].SetController(controller, repeats);
29+
Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data...");
2530
AddTimer(2f, () => {
26-
CurrentMap.ReplayBot.Controller.RemoveWeapons();
31+
if(!CurrentMap.ReplayBots[i].IsPlayable)
32+
return;
33+
34+
CurrentMap.ReplayBots[i].Controller!.RemoveWeapons();
2735

28-
CurrentMap.ReplayBot.LoadReplayData(DB!, CurrentMap);
36+
CurrentMap.ReplayBots[i].LoadReplayData(DB!);
2937

30-
CurrentMap.ReplayBot.Start();
38+
CurrentMap.ReplayBots[i].Start();
3139
});
40+
41+
return HookResult.Continue;
3242
}
3343

3444
return HookResult.Continue;
@@ -157,8 +167,9 @@ public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo
157167
{
158168
var player = @event.Userid;
159169

160-
if (CurrentMap.ReplayBot.Controller != null&& CurrentMap.ReplayBot.Controller.Equals(player))
161-
CurrentMap.ReplayBot.Reset();
170+
for (int i = 0; i < CurrentMap.ReplayBots.Count; i++)
171+
if (CurrentMap.ReplayBots[i].IsPlayable && CurrentMap.ReplayBots[i].Controller!.Equals(player))
172+
CurrentMap.ReplayBots[i].Reset();
162173

163174
if (player.IsBot || !player.IsValid)
164175
{

src/ST-Events/Tick.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CounterStrikeSharp.API.Core;
2-
using CounterStrikeSharp.API.Modules.Utils;
1+
using CounterStrikeSharp.API.Modules.Cvars;
32

43
namespace SurfTimer;
54

@@ -14,7 +13,25 @@ public void OnTick()
1413
player.HUD.Display();
1514
}
1615

17-
// Replay BOT Ticks
18-
CurrentMap?.ReplayBot.Tick(); // When CurrentMap null the ? operator will terminate safely the operation
16+
if (CurrentMap == null)
17+
return;
18+
19+
// Need to disable maps from executing their cfgs. Currently idk how (But seriusly it a security issue)
20+
ConVar? bot_quota = ConVar.Find("bot_quota");
21+
if (bot_quota != null)
22+
{
23+
int cbq = bot_quota.GetPrimitiveValue<int>();
24+
if(cbq != CurrentMap.ReplayBots.Count)
25+
{
26+
bot_quota.SetValue(CurrentMap.ReplayBots.Count);
27+
}
28+
}
29+
30+
for(int i = 0; i < CurrentMap!.ReplayBots.Count; i++)
31+
{
32+
CurrentMap.ReplayBots[i].Tick();
33+
if (CurrentMap.ReplayBots[i].RepeatCount == 0)
34+
CurrentMap.KickReplayBot(i);
35+
}
1936
}
2037
}

src/ST-Events/TriggerEndTouch.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using CounterStrikeSharp.API.Core;
33
using CounterStrikeSharp.API.Modules.Utils;
44
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
5-
using CounterStrikeSharp.API;
65

76
namespace SurfTimer;
87

@@ -45,11 +44,12 @@ internal HookResult OnTriggerEndTouch(DynamicHook handler)
4544
if(player.ReplayRecorder.IsRecording)
4645
{
4746
// Saveing 2 seconds before leaving the start zone
48-
player.ReplayRecorder.Frames.RemoveRange(0, Math.Max(0, player.ReplayRecorder.Frames.Count - (64*2))); // Would like for someone to fact check the math :)
47+
player.ReplayRecorder.Frames.RemoveRange(0, Math.Max(0, player.ReplayRecorder.Frames.Count - (64*2))); // Todo make a plugin convar for the time saved before start of run
4948
}
5049

5150
// MAP START ZONE
5251
player.Timer.Start();
52+
player.ReplayRecorder.CurrentSituation = ReplayFrameSituation.START_RUN;
5353

5454
/* Revisit
5555
// Wonky Prespeed check

0 commit comments

Comments
 (0)