diff --git a/README.md b/README.md index b2de2e8..30601aa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ > [!NOTE] -> The original creator of SharpTimer is deafps, who discontinued support for the project after version 0.2.6. This fork is now maintaned by the community, mainly [rcnoob](https://github.com/rcnoob). +> The original creator of SharpTimer is dea_bb, who discontinued support for the project after version 0.2.6. This fork is now maintaned by the community, mainly [rcnoob](https://github.com/rcnoob). @@ -8,6 +8,10 @@
+

+
+ Badge +

# SharpTimer @@ -80,20 +84,24 @@ SharpTimer is a "simple" Surf/KZ/Bhop/MG/Deathrun/etc. CS2 Timer plugin using Co [**MetaMod**](https://cs2.poggu.me/metamod/installation/) -[**CounterStrikeSharp** *(v215 and up)*](https://github.com/roflmuffin/CounterStrikeSharp/releases) +[**CounterStrikeSharp** *(v281 and up)*](https://github.com/roflmuffin/CounterStrikeSharp/releases) [**SharpTimerModelSetter** *(optional but recommended for custom player models)*](https://github.com/johandrevwyk/STCustomModels) -[**MovementUnlocker** *(optional but recommended)*](https://github.com/Source2ZE/MovementUnlocker) +[**MovementUnlocker** *(optional but recommended for surf and bhop servers)*](https://github.com/Source2ZE/MovementUnlocker) [**RampBugFix** *(optional but recommended for surf servers)*](https://github.com/Interesting-exe/CS2Fixes-RampbugFix/) [**Web panel** *(optional but recommended)*](https://github.com/Letaryat/sharptimer-web-panel) -[**SharpTimer-WallLists** *(optional but recommended)*](https://github.com/M-archand/SharpTimer-WallLists/tree/PointsList) +[**SharpTimer-WallLists** *(optional but recommended)*](https://github.com/M-archand/SharpTimer-WallLists) [**CS2-TeleportAnglesFix** *(optional but recommended)*](https://github.com/M-archand/CS2-TeleportAnglesFix) +[**STFixes** *(optional but recommended)*](https://github.com/rcnoob/STFixes) + +[**Flashing HUD Fix** *(optional but recommended)*](https://github.com/deabb/CS2FlashingHtmlHudFix) + ## Install * Download the [latest release](https://github.com/Letaryat/poor-sharptimer/releases), @@ -146,7 +154,7 @@ SharpTimer is a "simple" Surf/KZ/Bhop/MG/Deathrun/etc. CS2 Timer plugin using Co - [x] Max - [x] Height - [x] Width - - [x] Sync + - [ ] Sync - [ ] Jump Types - [x] Long Jump - [x] BunnyHop @@ -162,4 +170,7 @@ SharpTimer is a "simple" Surf/KZ/Bhop/MG/Deathrun/etc. CS2 Timer plugin using Co - [ ] Strafe Sync Bar on HUD -## Author: [@DEA_BB](https://twitter.com/dea_bb) +## Authors: +[DEA_BB](https://twitter.com/dea_bb) +[Letaryat](https://github.com/Letaryat) +[rcnoob](https://github.com/rcnoob) diff --git a/cfg/SharpTimer/config.cfg b/cfg/SharpTimer/config.cfg index 5187121..fab50bc 100644 --- a/cfg/SharpTimer/config.cfg +++ b/cfg/SharpTimer/config.cfg @@ -23,8 +23,6 @@ sharptimer_remove_legs true sharptimer_remove_collision true // Whether player collision should be removed or not. Default value: true sharptimer_remove_damage true // Whether dealing damage should be disabled or not (will crash with cs2fixes since they hook/detour damage regardless of you using their feature). Default value: true sharptimer_remove_crouch_fatigue true // Whether the player should get no crouch fatigue or not. Default value: true -sharptimer_trigger_push_fix false // When enabled all trigger_push ents will only push once OnStartTouch. Default value: false -sharptimer_hide_all_players false // Whether all players should be hidden or not. Default value: false sharptimer_bhop_block_ticks 16 // Ticks allowed on bhop_block. Default value: 16 sharptimer_spawn_on_respawnpos false // Teleports player to respawnpos on spawn. Default value: false sharptimer_stage_times_enabled true // Whether stage time records are enabled by default or not. Default value: true @@ -101,9 +99,8 @@ sharptimer_enable_noclip false sharptimer_global_rank_points_enabled false // Whether the plugin should reward players with global points for completing maps. MySQL Requierd. Default value: false sharptimer_display_rank_tags_chat true // Whether the plugin should display rank tags infront of players names in chat or not. Default value: true sharptimer_display_rank_tags_scoreboard true // Whether the plugin should display rank tags infront of players names in scoreboard or not. Default value: true -sharptimer_global_rank_free_points_enabled true // Whether the plugin should reward players with free points for completing maps without beating their PB (31xMapTier). Default value: true -sharptimer_global_rank_max_free_rewards 20 // How many times the player should recieve free 'participation' points for finishing the map without a new PB. Default value: 20 -sharptimer_global_rank_min_points_threshold 1000 // Players with Points below this amount will be treated as Unranked. Default value: 1000 +sharptimer_global_rank_min_points_threshold 1 // Players with Points below this amount will be treated as Unranked. Default value: 1 +sharptimer_global_rank_bonus_points_multiplier 0.5 // Multiplier for bonus course completion points. Default value: 0.5 //REPLAYS diff --git a/src/Commands/ChatCommands.cs b/src/Commands/ChatCommands.cs index 8c22cf2..3d34a32 100644 --- a/src/Commands/ChatCommands.cs +++ b/src/Commands/ChatCommands.cs @@ -673,7 +673,7 @@ public async Task RankCommandHandler(CCSPlayerController? player, string steamId foreach (var bonusRespawnPose in bonusRespawnPoses) { var bonusNumber = bonusRespawnPose.Key; - var bonusPbTicks = enableDb ? await GetPreviousPlayerRecordFromDatabase(player, steamId, currentMapName!, playerName, bonusNumber, style) : await GetPreviousPlayerRecord(player, steamId, bonusNumber); + var bonusPbTicks = enableDb ? await GetPreviousPlayerRecordFromDatabase(steamId, currentMapName!, playerName, bonusNumber, style) : await GetPreviousPlayerRecord(steamId, bonusNumber); /// Skip this bonus since the player doesn't have a saved time if (bonusPbTicks <= 0) continue; @@ -697,7 +697,7 @@ public async Task RankCommandHandler(CCSPlayerController? player, string steamId serverPlacement = await GetPlayerServerPlacement(player, steamId, playerName, false, true, false); } - int pbTicks = enableDb ? await GetPreviousPlayerRecordFromDatabase(player, steamId, currentMapName!, playerName, 0, style) : await GetPreviousPlayerRecord(player, steamId, 0); + int pbTicks = enableDb ? await GetPreviousPlayerRecordFromDatabase(steamId, currentMapName!, playerName, 0, style) : await GetPreviousPlayerRecord(steamId, 0); Server.NextFrame(() => { @@ -889,6 +889,7 @@ public void RespawnBonusPlayer(CCSPlayerController? player, CommandInfo command) [ConsoleCommand("css_startpos", "Saves a custom respawn point within the start trigger")] [ConsoleCommand("css_setresp", "Saves a custom respawn point within the start trigger")] + [ConsoleCommand("css_ssp", "Saves a custom respawn point within the start trigger")] [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void SetRespawnCommand(CCSPlayerController? player, CommandInfo command) { @@ -1054,14 +1055,6 @@ public void RespawnPlayerCommand(CCSPlayerController? player, CommandInfo comman } } - - [ConsoleCommand("say !r", "Alias for css_r to teleport to start")] - [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] - public void RespawnPlayerAliasCommand(CCSPlayerController? player, CommandInfo command) - { - RespawnPlayerCommand(player, command); - } - [ConsoleCommand("css_end", "Teleports you to end")] [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] public void EndPlayerCommand(CCSPlayerController? player, CommandInfo command) diff --git a/src/Commands/ConfigConvars.cs b/src/Commands/ConfigConvars.cs index e56aea1..26718b7 100644 --- a/src/Commands/ConfigConvars.cs +++ b/src/Commands/ConfigConvars.cs @@ -84,46 +84,37 @@ public void SharpTimerGlobalRanksConvar(CCSPlayerController? player, CommandInfo globalRanksEnabled = bool.TryParse(args, out bool globalRanksEnabledValue) ? globalRanksEnabledValue : args != "0" && globalRanksEnabled; } - [ConsoleCommand("sharptimer_global_rank_free_points_enabled", "Whether the plugin should reward players with free points for completing maps without beating their PB (31xMapTier). Default value: true")] + [ConsoleCommand("sharptimer_global_rank_min_points_threshold", "Players with Points below this amount will be treated as Unranked. Default value: 1")] [CommandHelper(whoCanExecute: CommandUsage.SERVER_ONLY)] - public void SharpTimerGlobalRanksEnableFreeRewardsConvar(CCSPlayerController? player, CommandInfo command) - { - string args = command.ArgString; - - globalRanksFreePointsEnabled = bool.TryParse(args, out bool globalRanksFreePointsEnabledValue) ? globalRanksFreePointsEnabledValue : args != "0" && globalRanksFreePointsEnabled; - } - - [ConsoleCommand("sharptimer_global_rank_max_free_rewards", "How many times the player should recieve free 'participation' points for finishing the map without a new PB. Default value: 20")] - [CommandHelper(whoCanExecute: CommandUsage.SERVER_ONLY)] - public void SharpTimerGlobalRanksMaxFreeRewardsConvar(CCSPlayerController? player, CommandInfo command) + public void SharpTimerGlobalRanksMinPointsConvar(CCSPlayerController? player, CommandInfo command) { string args = command.ArgString; - if (int.TryParse(args, out int maxFreePoints) && maxFreePoints > 0) + if (int.TryParse(args, out int minPoints) && minPoints > 0) { - maxGlobalFreePoints = maxFreePoints; - SharpTimerConPrint($"SharpTimer free 'participation' rewards set to {maxFreePoints} times."); + minGlobalPointsForRank = minPoints; + SharpTimerConPrint($"SharpTimer min points for rank set to {minPoints} points."); } else { - SharpTimerConPrint("Invalid free 'participation' rewards value. Please provide a positive float."); + SharpTimerConPrint("Invalid min points for rank value. Please provide a positive integer."); } } - [ConsoleCommand("sharptimer_global_rank_min_points_threshold", "Players with Points below this amount will be treated as Unranked. Default value: 1000")] + [ConsoleCommand("sharptimer_global_rank_bonus_points_multiplier", "Multiplier for bonus course completion points. Default value: 0.5")] [CommandHelper(whoCanExecute: CommandUsage.SERVER_ONLY)] - public void SharpTimerGlobalRanksMinPointsConvar(CCSPlayerController? player, CommandInfo command) + public void SharpTimerGlobalRanksBonusPointsConvar(CCSPlayerController? player, CommandInfo command) { string args = command.ArgString; - if (int.TryParse(args, out int minPoints) && minPoints > 0) + if (double.TryParse(args, out double bonusMultiplier) && bonusMultiplier > 0) { - minGlobalPointsForRank = minPoints; - SharpTimerConPrint($"SharpTimer min points for rank set to {minPoints} points."); + globalPointsBonusMultiplier = bonusMultiplier; + SharpTimerConPrint($"SharpTimer bonus points multiplier set to {bonusMultiplier}"); } else { - SharpTimerConPrint("Invalid min points for rank value. Please provide a positive integer."); + SharpTimerConPrint("Invalid bonus points multipler. Please provide a positive integer."); } } @@ -1078,7 +1069,7 @@ public void SharpTimerOnlyAMultiplierConvar(CCSPlayerController? player, Command } } - [ConsoleCommand("sharptimer_style_multiplier_onlyw", "Point modifier for onlys. Default value: 1.33")] + [ConsoleCommand("sharptimer_style_multiplier_onlys", "Point modifier for onlys. Default value: 1.33")] [CommandHelper(whoCanExecute: CommandUsage.SERVER_ONLY)] public void SharpTimerOnlySMultiplierConvar(CCSPlayerController? player, CommandInfo command) { @@ -1095,7 +1086,7 @@ public void SharpTimerOnlySMultiplierConvar(CCSPlayerController? player, Command } } - [ConsoleCommand("sharptimer_style_multiplier_onlyw", "Point modifier for onlyd. Default value: 1.33")] + [ConsoleCommand("sharptimer_style_multiplier_onlyd", "Point modifier for onlyd. Default value: 1.33")] [CommandHelper(whoCanExecute: CommandUsage.SERVER_ONLY)] public void SharpTimerOnlyDMultiplierConvar(CCSPlayerController? player, CommandInfo command) { diff --git a/src/DB/DatabaseUtils.cs b/src/DB/DatabaseUtils.cs index d55f7c1..9969176 100644 --- a/src/DB/DatabaseUtils.cs +++ b/src/DB/DatabaseUtils.cs @@ -761,10 +761,10 @@ DO UPDATE SET upsertCommand!.AddParameterWithValue("@UnixStamp", dBunixStamp); upsertCommand!.AddParameterWithValue("@SteamID", steamId); upsertCommand!.AddParameterWithValue("@Style", style); - if (globalRanksEnabled == true && ((dBtimesFinished <= maxGlobalFreePoints && globalRanksFreePointsEnabled == true) || beatPB)) await SavePlayerPoints(steamId, playerName, playerSlot, playerPoints, dBtimerTicks, beatPB, bonusX, style); + if (globalRanksEnabled == true) await SavePlayerPoints(steamId, playerName, playerSlot, playerPoints, dBtimerTicks, beatPB, bonusX, style, dBtimesFinished); if (style == 0 && (stageTriggerCount != 0 || cpTriggerCount != 0) && bonusX == 0 && enableDb && timerTicks < dBtimerTicks) Server.NextFrame(() => _ = Task.Run(async () => await DumpPlayerStageTimesToJson(player, steamId, playerSlot))); var prevSRID = await GetMapRecordSteamIDFromDatabase(bonusX, 0, style); - var prevSR = await GetPreviousPlayerRecordFromDatabase(player, prevSRID.Item1, currentMapNamee, prevSRID.Item2, bonusX, style); + var prevSR = await GetPreviousPlayerRecordFromDatabase(prevSRID.Item1, currentMapNamee, prevSRID.Item2, bonusX, style); await upsertCommand!.ExecuteNonQueryAsync(); Server.NextFrame(() => SharpTimerDebug($"Saved player {(bonusX != 0 ? $"bonus {bonusX} time" : "time")} to database for {playerName} {timerTicks} {DateTimeOffset.UtcNow.ToUnixTimeSeconds()}")); if (enableDb && IsAllowedPlayer(player)) await RankCommandHandler(player, steamId, playerSlot, playerName, true, style); @@ -812,9 +812,9 @@ DO UPDATE SET upsertCommand!.AddParameterWithValue("@SteamID", steamId); upsertCommand!.AddParameterWithValue("@Style", style); var prevSRID = await GetMapRecordSteamIDFromDatabase(bonusX, 0, style); - var prevSR = await GetPreviousPlayerRecordFromDatabase(player, prevSRID.Item1, currentMapNamee, prevSRID.Item2, bonusX, style); + var prevSR = await GetPreviousPlayerRecordFromDatabase(prevSRID.Item1, currentMapNamee, prevSRID.Item2, bonusX, style); await upsertCommand!.ExecuteNonQueryAsync(); - if (globalRanksEnabled == true) await SavePlayerPoints(steamId, playerName, playerSlot, timerTicks, dBtimerTicks, beatPB, bonusX, style); + if (globalRanksEnabled == true) await SavePlayerPoints(steamId, playerName, playerSlot, timerTicks, dBtimerTicks, beatPB, bonusX, style, dBtimesFinished); if (style == 0 && (stageTriggerCount != 0 || cpTriggerCount != 0) && bonusX == 0) Server.NextFrame(() => _ = Task.Run(async () => await DumpPlayerStageTimesToJson(player, steamId, playerSlot))); Server.NextFrame(() => SharpTimerDebug($"Saved player {(bonusX != 0 ? $"bonus {bonusX} time" : "time")} to database for {playerName} {timerTicks} {DateTimeOffset.UtcNow.ToUnixTimeSeconds()}")); if (IsAllowedPlayer(player)) await RankCommandHandler(player, steamId, playerSlot, playerName, true, style); @@ -1487,12 +1487,12 @@ DO UPDATE SET } } - public void GainPointsMessage(string playerName, int newPoints, int playerPoints) + public void GainPointsMessage(string playerName, double newPoints, double playerPoints) { PrintToChatAll(Localizer["gained_points", playerName, Convert.ToInt32(newPoints - playerPoints), newPoints]); } - public async Task SavePlayerPoints(string steamId, string playerName, int playerSlot, int timerTicks, int oldTicks, bool beatPB = false, int bonusX = 0, int style = 0) + public async Task SavePlayerPoints(string steamId, string playerName, int playerSlot, int timerTicks, int oldTicks, bool beatPB = false, int bonusX = 0, int style = 0, int completions = 0) { SharpTimerDebug($"Trying to set player points in database for {playerName}"); try @@ -1509,7 +1509,6 @@ public async Task SavePlayerPoints(string steamId, string playerName, int player bool isVip = false; string bigGif = "x"; int playerPoints = 0; - float mapTier = 0.1f; using (var connection = await OpenConnectionAsync()) { @@ -1573,13 +1572,49 @@ public async Task SavePlayerPoints(string steamId, string playerName, int player break; } - int newPoints; + double newPoints; - if (!enableStylePoints && style == 0) newPoints = (int)(beatPB == false ? Convert.ToInt32(CalculatePoints(timerTicks, style)! * globalPointsMultiplier!) + playerPoints - : Convert.ToInt32(CalculatePoints(timerTicks, style)! - CalculatePoints(oldTicks, style) * globalPointsMultiplier! + playerPoints + (310 * (bonusX == 0 ? mapTier : mapTier * 0.5)))); - else if (enableStylePoints) newPoints = (int)(beatPB == false ? Convert.ToInt32(CalculatePoints(timerTicks, style)! * globalPointsMultiplier!) + playerPoints - : Convert.ToInt32(CalculatePoints(timerTicks, style)! - CalculatePoints(oldTicks, style) * globalPointsMultiplier! + playerPoints + (310 * (bonusX == 0 ? mapTier : mapTier * 0.5)))); - else newPoints = playerPoints; + // First calculate basic map completion points based on tier + newPoints = CalculateCompletion(); + + // Then calculate max points based on times finished + double maxPoints = CalculateTier(completions); + + // Then grab the top 10 and calculate distributed points + Dictionary sortedRecords = await GetSortedRecordsFromDatabase(10, bonusX, "", style); + int rank = 1; + bool isTop10 = false; + foreach (var kvp in sortedRecords.Take(10)) + { + if (steamId == kvp.Key) + { + newPoints += CalculateTop10(maxPoints, rank); + isTop10 = true; + } + rank++; + } + + // If not in top 10, calculate groups based on percentile + if(!isTop10) + newPoints += CalculateGroups(maxPoints, await GetPlayerMapPercentile(steamId, playerName, bonusX, style)); + + // Apply style multiplier if enabled + if(enableStylePoints) + newPoints *= GetStyleMultiplier(style); + + // Apply bonus multiplier if bonus completion + if(bonusX != 0) + newPoints *= globalPointsBonusMultiplier; + + // Hastily round the new points to prevent 123.4567890123456789 points + newPoints = Math.Round(newPoints); + + // Zero out new points if style points are disabled and player is using styles + if(!enableStylePoints && style != 0) + newPoints = 0; + + // Add final calculation to players points + newPoints += playerPoints; await row.CloseAsync(); // Update or insert the record @@ -1660,13 +1695,49 @@ DO UPDATE SET Server.NextFrame(() => SharpTimerDebug($"No player stats yet")); - int newPoints; + double newPoints; + + // First calculate basic map completion points based on tier + newPoints = CalculateCompletion(); - if (!enableStylePoints && style == 0) newPoints = (int)(beatPB == false ? Convert.ToInt32(CalculatePoints(timerTicks, style)! * globalPointsMultiplier!) + playerPoints - : Convert.ToInt32(CalculatePoints(timerTicks, style)! - CalculatePoints(oldTicks, style) * globalPointsMultiplier! + playerPoints + (310 * (bonusX == 0 ? mapTier : mapTier * 0.5)))); - else if (enableStylePoints) newPoints = (int)(beatPB == false ? Convert.ToInt32(CalculatePoints(timerTicks, style)! * globalPointsMultiplier!) + playerPoints - : Convert.ToInt32(CalculatePoints(timerTicks, style)! - CalculatePoints(oldTicks, style) * globalPointsMultiplier! + playerPoints + (310 * (bonusX == 0 ? mapTier : mapTier * 0.5)))); - else newPoints = playerPoints; + // Then calculate max points based on times finished + double maxPoints = CalculateTier(completions); + + // Then grab the top 10 and calculate distributed points + Dictionary sortedRecords = await GetSortedRecordsFromDatabase(10, bonusX, "", style); + int rank = 1; + bool isTop10 = false; + foreach (var kvp in sortedRecords.Take(10)) + { + if (steamId == kvp.Key) + { + newPoints += CalculateTop10(maxPoints, rank); + isTop10 = true; + } + rank++; + } + + // If not in top 10, calculate groups based on percentile + if(!isTop10) + newPoints += CalculateGroups(maxPoints, await GetPlayerMapPercentile(steamId, playerName, bonusX, style)); + + // Apply style multiplier if enabled + if(enableStylePoints) + newPoints *= GetStyleMultiplier(style); + + // Apply bonus multiplier if bonus completion + if(bonusX != 0) + newPoints *= globalPointsBonusMultiplier; + + // Hastily round the new points to prevent 123.4567890123456789 points + newPoints = Math.Round(newPoints); + + // Zero out new points if style points are disabled and player is using styles + if(!enableStylePoints && style != 0) + newPoints = 0; + + // Add final calculation to players points + newPoints += playerPoints; await row.CloseAsync(); @@ -1732,6 +1803,58 @@ DO UPDATE SET } } + public async Task PlayerCompletions(string steamId, int bonusX = 0, int style = 0) + { + try + { + //if ((bonusX == 0 && !playerTimers[playerSlot].IsTimerRunning) || (bonusX != 0 && !playerTimers[playerSlot].IsBonusTimerRunning)) return; + string currentMapNamee = bonusX == 0 ? currentMapName! : $"{currentMapName}_bonus{bonusX}"; + + using (var connection = await OpenConnectionAsync()) + { + await CreatePlayerRecordsTableAsync(connection); + + string? selectQuery; + DbCommand? selectCommand; + switch (dbType) + { + case DatabaseType.MySQL: + selectQuery = @"SELECT TimesFinished FROM PlayerRecords WHERE MapName = @MapName AND SteamID = @SteamID AND Style = @Style"; + selectCommand = new MySqlCommand(selectQuery, (MySqlConnection)connection); + break; + case DatabaseType.PostgreSQL: + selectQuery = @"SELECT ""TimesFinished"" FROM ""PlayerRecords"" WHERE ""MapName"" = @MapName AND ""SteamID"" = @SteamID AND ""Style"" = @Style"; + selectCommand = new NpgsqlCommand(selectQuery, (NpgsqlConnection)connection); + break; + case DatabaseType.SQLite: + selectQuery = @"SELECT TimesFinished FROM PlayerRecords WHERE MapName = @MapName AND SteamID = @SteamID AND Style = @Style"; + selectCommand = new SQLiteCommand(selectQuery, (SQLiteConnection)connection); + break; + default: + selectQuery = null; + selectCommand = null; + break; + } + // Check if the record already exists or has a higher timer value + selectCommand!.AddParameterWithValue("@MapName", currentMapNamee); + selectCommand!.AddParameterWithValue("@SteamID", steamId); + selectCommand!.AddParameterWithValue("@Style", style); + + var row = await selectCommand!.ExecuteReaderAsync(); + + if (row.Read()) + { + return row.GetInt32("TimesFinished"); + } + } + } + catch(Exception ex) + { + Server.NextFrame(() => SharpTimerError($"Error getting player completions from database for id:{steamId}: {ex}")); + } + return 0; + } + public async Task PrintTop10PlayerPoints(CCSPlayerController player) { try @@ -2184,16 +2307,11 @@ public async Task GetReplayVIPGif(string steamId, int playerSlot) } } - public async Task GetPreviousPlayerRecordFromDatabase(CCSPlayerController? player, string steamId, string currentMapName, string playerName, int bonusX = 0, int style = 0) + public async Task GetPreviousPlayerRecordFromDatabase(string steamId, string currentMapName, string playerName, int bonusX = 0, int style = 0) { SharpTimerDebug($"Trying to get Previous {(bonusX != 0 ? $"bonus {bonusX} time" : "time")} from database for {playerName}"); try { - if (!IsAllowedClient(player)) - { - return 0; - } - string currentMapNamee = bonusX == 0 ? currentMapName : $"{currentMapName}_bonus{bonusX}"; using (IDbConnection connection = await OpenConnectionAsync()) @@ -2471,6 +2589,106 @@ public async Task> GetSortedRecordsFromDatabase } return []; } + public async Task> GetAllSortedRecordsFromDatabase(int limit = 0, int bonusX = 0, int style = 0) + { + SharpTimerDebug($"Trying GetSortedRecords {(bonusX != 0 ? $"bonus {bonusX}" : "")} from database"); + using (var connection = await OpenConnectionAsync()) + { + try + { + await CreatePlayerRecordsTableAsync(connection); + + // Retrieve and sort records for the current map + string? selectQuery; + DbCommand? selectCommand; + if (limit != 0) + { + switch (dbType) + { + case DatabaseType.MySQL: + selectQuery = $@"SELECT SteamID, PlayerName, TimerTicks, MapName FROM PlayerRecords WHERE Style = @Style ORDER BY TimerTicks ASC LIMIT {limit}"; + selectCommand = new MySqlCommand(selectQuery, (MySqlConnection)connection); + break; + case DatabaseType.PostgreSQL: + selectQuery = $@"SELECT ""SteamID"", ""PlayerName"", ""TimerTicks"", ""MapName"" FROM ""PlayerRecords"" WHERE ""Style"" = @Style ORDER BY ""TimerTicks"" ASC LIMIT {limit}"; + selectCommand = new NpgsqlCommand(selectQuery, (NpgsqlConnection)connection); + break; + case DatabaseType.SQLite: + selectQuery = $@"SELECT SteamID, PlayerName, TimerTicks, MapName FROM PlayerRecords WHERE Style = @Style ORDER BY TimerTicks ASC LIMIT {limit}"; + selectCommand = new SQLiteCommand(selectQuery, (SQLiteConnection)connection); + break; + default: + selectQuery = null; + selectCommand = null; + break; + } + } + else + { + switch (dbType) + { + case DatabaseType.MySQL: + selectQuery = @"SELECT SteamID, PlayerName, TimerTicks, MapName FROM PlayerRecords WHERE Style = @Style"; + selectCommand = new MySqlCommand(selectQuery, (MySqlConnection)connection); + break; + case DatabaseType.PostgreSQL: + selectQuery = @"SELECT ""SteamID"", ""PlayerName"", ""TimerTicks"", ""MapName"" FROM ""PlayerRecords"" WHERE ""Style"" = @Style"; + selectCommand = new NpgsqlCommand(selectQuery, (NpgsqlConnection)connection); + break; + case DatabaseType.SQLite: + selectQuery = @"SELECT SteamID, PlayerName, TimerTicks, MapName FROM PlayerRecords WHERE Style = @Style"; + selectCommand = new SQLiteCommand(selectQuery, (SQLiteConnection)connection); + break; + default: + selectQuery = null; + selectCommand = null; + break; + } + } + using (selectCommand) + { + selectCommand!.AddParameterWithValue("@Style", style); + using (var reader = await selectCommand!.ExecuteReaderAsync()) + { + Dictionary> sortedRecords = new Dictionary>(); + while (await reader.ReadAsync()) + { + string steamId = reader.GetString(0); + string playerName = reader.IsDBNull(1) ? "Unknown" : reader.GetString(1); + int timerTicks = reader.GetInt32(2); + string mapname = reader.GetString(3); + if (!sortedRecords.ContainsKey(steamId)) + { + // If steamId doesn't exist, create a new list for the steamId + sortedRecords[steamId] = new List(); + } + sortedRecords[steamId].Add(new PlayerRecord + { + PlayerName = playerName, + SteamID = steamId, + TimerTicks = timerTicks, + MapName = mapname + }); + } + + var sortedList = sortedRecords + .SelectMany(recordEntry => recordEntry.Value) + .OrderBy(record => record.TimerTicks) + .ToList(); + + SharpTimerDebug($"Got GetSortedRecords {(bonusX != 0 ? $"bonus {bonusX}" : "")} from database"); + + return sortedList; + } + } + } + catch (Exception ex) + { + SharpTimerError($"Error getting all sorted records from database: {ex.Message}"); + } + } + return []; + } public async Task> GetSortedStageRecordsFromDatabase(int stage, int limit = 0, int bonusX = 0, string mapName = "") { SharpTimerDebug($"Trying GetSortedStageRecords {(bonusX != 0 ? $"bonus {bonusX}" : "")} from database"); @@ -2650,17 +2868,17 @@ public async Task ImportPlayerPoints() { try { - var sortedRecords = await GetSortedRecordsFromDatabase(); + var sortedRecords = await GetAllSortedRecordsFromDatabase(); - foreach (var kvp in sortedRecords) + foreach (var record in sortedRecords) { - string playerSteamID = kvp.Key; - string playerName = kvp.Value.PlayerName!; - int timerTicks = kvp.Value.TimerTicks; + string playerSteamID = record.SteamID!; + string playerName = record.PlayerName!; + int timerTicks = record.TimerTicks; if (enableDb && globalRanksEnabled == true) { - _ = Task.Run(async () => await SavePlayerPoints(playerSteamID, playerName, -1, timerTicks, 0, false, 0, 0)); + _ = Task.Run(async () => await SavePlayerPoints(playerSteamID, playerName, -1, timerTicks, 0, false, 0, 0, await PlayerCompletions(playerSteamID, 0, 0))); await Task.Delay(100); } } @@ -2898,74 +3116,5 @@ ON CONFLICT (MapName, SteamID, Style) DO UPDATE SharpTimerError($"Error adding JSON times to the database: {ex.Message}"); } } - - [ConsoleCommand("css_databasetojson", " ")] - [RequiresPermissions("@css/root")] - [CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] - public void ExportDatabaseToJsonCommand(CCSPlayerController? player, CommandInfo command) - { - _ = Task.Run(ExportDatabaseToJsonAsync); - } - - public async Task ExportDatabaseToJsonAsync() - { - string recordsDirectoryNamee = "SharpTimer/PlayerRecords"; - string playerRecordsPathh = Path.Combine(gameDir!, "csgo", "cfg", recordsDirectoryNamee); - - try - { - string connectionString = await GetConnectionStringFromConfigFile(); - - using (var connection = new MySqlConnection(connectionString)) - { - await connection.OpenAsync(); - - string selectQuery = "SELECT SteamID, PlayerName, TimerTicks, MapName FROM PlayerRecords"; - using (var selectCommand = new MySqlCommand(selectQuery, connection)) - { - using (var reader = await selectCommand.ExecuteReaderAsync()) - { - while (await reader.ReadAsync()) - { - string steamId = reader.GetString(0); - string playerName = reader.GetString(1); - int timerTicks = reader.GetInt32(2); - string mapName = reader.GetString(3); - - Directory.CreateDirectory(playerRecordsPathh); - - Dictionary records; - string filePath = Path.Combine(playerRecordsPathh, $"{mapName}.json"); - if (File.Exists(filePath)) - { - string existingJson = await File.ReadAllTextAsync(filePath); - records = JsonSerializer.Deserialize>(existingJson) ?? []; - } - else - { - records = []; - } - - records[steamId] = new PlayerRecord - { - PlayerName = playerName, - TimerTicks = timerTicks - }; - - string updatedJson = JsonSerializer.Serialize(records, jsonSerializerOptions); - - await File.WriteAllTextAsync(filePath, updatedJson); - - SharpTimerDebug($"Player records for map {mapName} successfully exported to JSON."); - } - } - } - } - } - catch (Exception ex) - { - SharpTimerError($"Error exporting player records to JSON: {ex.Message}"); - } - } } } \ No newline at end of file diff --git a/src/Features/JumpStats.cs b/src/Features/JumpStats.cs index 2317d71..08fd704 100644 --- a/src/Features/JumpStats.cs +++ b/src/Features/JumpStats.cs @@ -220,7 +220,7 @@ public void OnSyncTick(CCSPlayerController player, PlayerButtons? buttons, QAngl bool left = false; bool right = false; - #pragma warning disable CS0219 // annoyence + #pragma warning disable CS0219 // annoyance bool leftRight = false; #pragma warning restore CS0219 @@ -236,14 +236,15 @@ public void OnSyncTick(CCSPlayerController player, PlayerButtons? buttons, QAngl playerTimer.Rotation.Add(newEyeAngle); playerTimer.TotalSync++; - //Check left goodsync - if (playerTimer.Rotation != null && playerTimer.Rotation.Count > 1 && eyeangle.Y > playerTimer.Rotation[playerTimer.TotalSync - 2].Y && left) - playerTimer.GoodSync++; - - //Check right goodsync - if (playerTimer.Rotation != null && playerTimer.Rotation.Count > 1 && eyeangle.Y < playerTimer.Rotation[playerTimer.TotalSync - 2].Y && right) - playerTimer.GoodSync++; - + if (playerTimer.Rotation != null && playerTimer.Rotation.Count > 1 && playerTimer.TotalSync >= 2) + { + if (eyeangle.Y > playerTimer.Rotation[playerTimer.TotalSync - 2].Y && left) + playerTimer.GoodSync++; + + if (eyeangle.Y < playerTimer.Rotation[playerTimer.TotalSync - 2].Y && right) + playerTimer.GoodSync++; + } + playerTimer.Sync = Math.Round((float)playerTimers[player.Slot].GoodSync / playerTimers[player.Slot].TotalSync * 100, 0); } catch (Exception ex) @@ -443,4 +444,4 @@ public void PrintJS(CCSPlayerController player, PlayerJumpStats playerJumpStat, player.PrintToConsole($" {Localizer["js_msg2", Math.Round(playerJumpStat.jumpFrames.Last().MaxHeight, 2), GetMaxWidth(playerpos, playerJumpStat), playerJumpStat.WTicks, sync]}"); } } -} \ No newline at end of file +} diff --git a/src/Features/Points.cs b/src/Features/Points.cs new file mode 100644 index 0000000..b77b323 --- /dev/null +++ b/src/Features/Points.cs @@ -0,0 +1,145 @@ +using System.Diagnostics.Eventing.Reader; +using System.Net; +using System.Text; +using System.Text.Json; +using System.Xml; +using CounterStrikeSharp.API.Core; + +namespace SharpTimer +{ + public partial class SharpTimer + { + + // Step 1 + // This function calculates basic map completion points + // Start at 25 for T1, then 50, then 100, etc.. + public int CalculateCompletion() + { + if (currentMapTier is not null) + { + switch (currentMapTier) + { + case 1: + return 25; + case 2: + return 50; + case 3: + return 100; + case 4: + return 200; + case 5: + return 400; + case 6: + return 600; + case 7: + return 800; + case 8: + return 1000; + + default: + return 0; + } + }else{ + return 25; + } + } + + // Step 2 + // This function calculates tier ranking + // This is the first step in calculating the players specific ranking on the map + public double CalculateTier(int completions) + { + // Define max WR points for each tier (fallback to t1) + int maxWR; + int tier; + if (currentMapTier is not null) + { + maxWR = 250 * (int)currentMapTier; + tier = (int)currentMapTier; + } + else + { + maxWR = 250; + tier = 1; + } + + switch (tier) + { + case 1: + return Math.Max(maxWR, 58.5 + (1.75 * completions) / 6); + case 2: + return Math.Max(maxWR, 82.15 + (2.8 * completions) / 5); + case 3: + return Math.Max(maxWR, 117 + (3.5 * completions) / 4); + case 4: + return Math.Max(maxWR, 164.25 + (5.74 * completions) / 4); + case 5: + return Math.Max(maxWR, 234 + (7 * completions) / 4); + case 6: + return Math.Max(maxWR, 328 + (14 * completions) / 4); + case 7: + return Math.Max(maxWR, 420 + (21 * completions) / 4); + case 8: + return Math.Max(maxWR, 560 + (30 * completions) / 4); + + default: + return 0; + } + } + + // Step 3 + // This function takes the WR points from above and distributes them among the top 10 + public double CalculateTop10(double points, int position) + { + switch(position) + { + case 1: + return points; + case 2: + return points * 0.8; + case 3: + return points * 0.75; + case 4: + return points * 0.7; + case 5: + return points * 0.65; + case 6: + return points * 0.6; + case 7: + return points * 0.55; + case 8: + return points * 0.5; + case 9: + return points * 0.45; + case 10: + return points * 0.4; + + default: + return 0; + } + } + + // Step 4 + // This function sorts players below top10, but above 50th percentile, into groups + // These groups get less points than top 10, but still get points! + public double CalculateGroups(double points, double percentile) + { + switch(percentile) + { + case double p when p <= 3.125: + return points * 0.25; // Group 1 + case double p when p <= 6.25: + return (points * 0.25) / 1.5; // Group 2 + case double p when p <= 12.5: + return ((points * 0.25) / 1.5) / 1.5; // Group 3 + case double p when p <= 25: + return (((points * 0.25) / 1.5) / 1.5) / 1.5; // Group 4 + case double p when p <= 50: + return (((((points * 0.25) / 1.5) / 1.5) / 1.5) / 1.5); // Group 5 + + default: + return 0; + } + } + } +} \ No newline at end of file diff --git a/src/Features/ReplayUtils.cs b/src/Features/ReplayUtils.cs index bd849b5..05e6d77 100644 --- a/src/Features/ReplayUtils.cs +++ b/src/Features/ReplayUtils.cs @@ -289,7 +289,7 @@ private async Task SpawnReplayBot() Server.ExecuteCommand("bot_zombie 1"); Server.ExecuteCommand("sv_cheats 0"); - AddTimer(0.5f, () => + AddTimer(3.0f, () => { foundReplayBot = false; SharpTimerDebug($"Trying to find replay bot!"); @@ -339,7 +339,7 @@ private void OnReplayBotConnect(CCSPlayerController bot) var botSlot = bot.Slot; var botName = bot.PlayerName; - AddTimer(5.0f, () => + AddTimer(3.0f, () => { OnPlayerConnect(bot, true); connectedReplayBots[botSlot] = new CCSPlayerController(bot.Handle); diff --git a/src/Player/PlayerOnTick.cs b/src/Player/PlayerOnTick.cs index 768c8bb..1447eb2 100644 --- a/src/Player/PlayerOnTick.cs +++ b/src/Player/PlayerOnTick.cs @@ -86,15 +86,13 @@ public void PlayerOnTick() string formattedPlayerPre = Math.Round(ParseVector(playerTimer.PreSpeed ?? "0 0 0").Length2D()).ToString("000"); string playerTime = FormatTime(timerTicks); string playerBonusTime = FormatTime(playerTimer.BonusTimerTicks); - string timerLine = isBonusTimerRunning - ? $" Bonus #{playerTimer.BonusStage} Timer: {playerBonusTime}
" - : isTimerRunning - ? $" Timer: {playerTime} {GetPlayerPlacement(player)}{((playerTimer.CurrentMapStage != 0 && useStageTriggers == true) ? $" {playerTimer.CurrentMapStage}/{stageTriggerCount}" : "")}
" - : playerTimer.IsReplaying - ? $" ◉ REPLAY {FormatTime(playerReplays[playerSlot].CurrentPlaybackFrame)}
" - : ""; - + ? $" Bonus #{playerTimer.BonusStage} Timer: {playerBonusTime}
" + : isTimerRunning + ? $" Timer: {playerTime} ({GetPlayerPlacement(player)}){((playerTimer.CurrentMapStage != 0 && useStageTriggers == true) ? $" {playerTimer.CurrentMapStage}/{stageTriggerCount}" : "")}
" + : playerTimer.IsReplaying + ? $" ◉ REPLAY {FormatTime(playerReplays[playerSlot].CurrentPlaybackFrame)}
" + : ""; string veloLine; if (playerVel <= 349) @@ -141,9 +139,7 @@ public void PlayerOnTick() { veloLine = $" {(playerTimer.IsTester ? playerTimer.TesterSmolGif : "")}Speed: {(playerTimer.IsReplaying ? "{formattedPlayerVel} ({formattedPlayerPre}){(playerTimer.IsTester ? playerTimer.TesterSmolGif : "")}
"; } - - - + string syncLine = $"Sync: {playerTimer.Sync}%
"; string infoLine = ""; @@ -173,41 +169,34 @@ public void PlayerOnTick() if (playerTimer.MovementService!.OldJumpPressed == true) playerTimer.MovementService.OldJumpPressed = false; - // enable hud if isTimerRunning, i added this because of the Weapon Paints Menu update - string hudContent = ""; - bool previousTimerState = false; // Track the previous state of isTimerRunning - - // Check if the timer is running - if (isTimerRunning) - { - // Build the HUD content if the timer is running - hudContent = (hudEnabled ? timerLine + - (VelocityHudEnabled ? veloLine : "") + - (StrafeHudEnabled && !playerTimer.IsReplaying ? syncLine : "") + - infoLine : "") + - (keyEnabled && !playerTimer.IsReplaying ? keysLineNoHtml : "") + - ((playerTimer.IsTester && !playerTimer.IsReplaying) ? $"{(!keyEnabled ? "
" : "")}" + playerTimer.TesterBigGif : "") + - ((playerTimer.IsVip && !playerTimer.IsTester && !playerTimer.IsReplaying) ? $"{(!keyEnabled ? "

" : "")}" + $"

" : "") + - ((playerTimer.IsReplaying && playerTimer.VipReplayGif != "x") ? playerTimer.VipReplayGif : ""); - - // Print the HUD content when timer is running - player.PrintToCenterHtml(hudContent); - } - else - { - // Clear the HUD when the timer stops - if (previousTimerState == true) // Only clear if it was previously running - { - player.PrintToCenterHtml(""); // Clear HUD content - } - } - - // Update the previous timer state - previousTimerState = isTimerRunning; - - +// enable hud if isTimerRunning, updated logic based on Weapon Paints Menu update +string hudContent = ""; +bool previousTimerState = false; // Track the previous state of isTimerRunning +// Check if the timer is running +if (isTimerRunning) +{ + // Build the HUD content if the timer is running + hudContent = (hudEnabled ? timerLine + veloLine + infoLine : "") + + (keyEnabled ? keysLineNoHtml : "") + + ((playerTimer.IsTester && !playerTimer.IsReplaying) ? $"{(!keyEnabled ? "
" : "")}" + playerTimer.TesterBigGif : "") + + ((playerTimer.IsVip && !playerTimer.IsTester && !playerTimer.IsReplaying) ? $"{(!keyEnabled ? "

" : "")}" + $"

" : "") + + ((playerTimer.IsReplaying && playerTimer.VipReplayGif != "x") ? playerTimer.VipReplayGif : ""); + + // Print the HUD content when timer is running + player.PrintToCenterHtml(hudContent); +} +else +{ + // Clear the HUD when the timer stops + if (previousTimerState == true) // Only clear if it was previously running + { + player.PrintToCenterHtml(""); // Clear HUD content + } +} +// Update the previous timer state +previousTimerState = isTimerRunning; if (isTimerRunning) { @@ -364,16 +353,19 @@ public void PlayerOnTick() private string GetMainMapInfoLine(PlayerTimerInfo playerTimer) { - return !playerTimer.IsReplaying - ? $"" + - - $"{playerTimer.CachedPB} " + - $"({playerTimer.CachedMapPlacement})" + - $"{(RankIconsEnabled ? $" | " : "")}" + - $"{((MapNameHudEnabled && currentMapType == null && currentMapTier == null) ? $" | {currentMapName}" : "")}" + - $"" +return !playerTimer.IsReplaying + ? $"" + + $"{playerTimer.CachedPB} " + + $"({playerTimer.CachedMapPlacement})" + + $"{(RankIconsEnabled ? $" | " : "")}" + + $"{(enableStyles ? $" | {GetNamedStyle(playerTimer.currentStyle)}" : "")}" + + $"{((MapTierHudEnabled && currentMapTier != null) ? $" | Tier: {currentMapTier}" : "")}" + + $" | {playerTimer.CachedRank}" + + $"{((MapTypeHudEnabled && currentMapType != null) ? $" | {currentMapType}" : "")}" + + $"{((MapNameHudEnabled && currentMapType == null && currentMapTier == null) ? $" | {currentMapName}" : "")}" + + $"

[CS2SURF.PRO]" + : $"{playerTimer.ReplayHUDString}"; - : $" {playerTimer.ReplayHUDString}"; } private string GetBonusInfoLine(PlayerTimerInfo playerTimer) @@ -426,7 +418,7 @@ public void SpectatorOnTick(CCSPlayerController player) string timerLine = isBonusTimerRunning ? $" Bonus #{playerTimer.BonusStage} Timer: {playerBonusTime}
" : isTimerRunning - ? $" Timer: {playerTime} ({GetPlayerPlacement(target)}){((playerTimer.CurrentMapStage != 0 && useStageTriggers == true) ? $" {playerTimer.CurrentMapStage}/{stageTriggerCount}" : "")}
" + ? $" Timer: {playerTime} ({GetPlayerPlacement(target)}){((playerTimer.CurrentMapStage != 0 && useStageTriggers == true) ? $" {playerTimer.CurrentMapStage}/{stageTriggerCount}" : "")}
" : playerTimer.IsReplaying ? $" ◉ REPLAY {FormatTime(playerReplays[target.Slot].CurrentPlaybackFrame)}
" : ""; diff --git a/src/Player/PlayerUtils.cs b/src/Player/PlayerUtils.cs index a6cdfb5..e49d054 100644 --- a/src/Player/PlayerUtils.cs +++ b/src/Player/PlayerUtils.cs @@ -229,14 +229,8 @@ private void RemovePlayerCollision(CCSPlayerController? player) return (0, string.Empty); } - public async Task GetPreviousPlayerRecord(CCSPlayerController? player, string steamId, int bonusX = 0) + public async Task GetPreviousPlayerRecord(string steamId, int bonusX = 0) { - if (!IsAllowedPlayer(player)) - { - SharpTimerDebug("Player not allowed."); - return 0; - } - string currentMapNamee = bonusX == 0 ? currentMapName! : $"{currentMapName}_bonus{bonusX}"; string mapRecordsFileName = $"{currentMapNamee}.json"; string mapRecordsPath = Path.Combine(playerRecordsPath!, mapRecordsFileName); @@ -297,7 +291,7 @@ public string GetPlayerPlacement(CCSPlayerController? player) } } - public async Task GetPlayerMapPlacementWithTotal(CCSPlayerController? player, string steamId, string playerName, bool getRankImg = false, bool getPlacementOnly = false, int bonusX = 0, int style = 0) + public async Task GetPlayerMapPlacementWithTotal(CCSPlayerController? player, string steamId, string playerName, bool getRankImg = false, bool getPlacementOnly = false, int bonusX = 0, int style = 0, bool getPercentileOnly = false) { try { @@ -306,7 +300,7 @@ public async Task GetPlayerMapPlacementWithTotal(CCSPlayerController? pl string currentMapNamee = bonusX == 0 ? currentMapName! : $"{currentMapName}_bonus{bonusX}"; - int savedPlayerTime = enableDb ? await GetPreviousPlayerRecordFromDatabase(player, steamId, currentMapName!, playerName, bonusX, style) : await GetPreviousPlayerRecord(player, steamId, bonusX); + int savedPlayerTime = enableDb ? await GetPreviousPlayerRecordFromDatabase(steamId, currentMapName!, playerName, bonusX, style) : await GetPreviousPlayerRecord(steamId, bonusX); if (savedPlayerTime == 0) return getRankImg ? UnrankedIcon : UnrankedTitle; @@ -325,6 +319,28 @@ public async Task GetPlayerMapPlacementWithTotal(CCSPlayerController? pl return UnrankedTitle; } } + public async Task GetPlayerMapPercentile(string steamId, string playerName, int bonusX = 0, int style = 0) + { + try + { + string currentMapNamee = bonusX == 0 ? currentMapName! : $"{currentMapName}_bonus{bonusX}"; + + int savedPlayerTime = enableDb ? await GetPreviousPlayerRecordFromDatabase(steamId, currentMapName!, playerName, bonusX, style) : await GetPreviousPlayerRecord(steamId, bonusX); + + Dictionary sortedRecords = enableDb ? await GetSortedRecordsFromDatabase(0, bonusX, "", style) : await GetSortedRecords(); + + int placement = sortedRecords.Count(kv => kv.Value.TimerTicks < savedPlayerTime) + 1; + int totalPlayers = sortedRecords.Count; + double percentage = (double)placement / totalPlayers * 100; + + return percentage; + } + catch (Exception ex) + { + SharpTimerError($"Error in GetPlayerMapPercentile: {ex}"); + return 0; + } + } public async Task GetPlayerStagePlacementWithTotal(CCSPlayerController? player, string steamId, string playerName, int stage, bool getRankImg = false, bool getPlacementOnly = false, int bonusX = 0) { try @@ -463,7 +479,7 @@ public async Task PrintMapTimeToChat(CCSPlayerController player, string steamID, Server.NextFrame(() => { if (IsAllowedPlayer(player) && timesFinished > maxGlobalFreePoints && globalRanksFreePointsEnabled == true && oldticks < newticks) - PrintToChat(player, Localizer["reached_max_free", maxGlobalFreePoints]); + //PrintToChat(player, Localizer["reached_max_free", maxGlobalFreePoints]); if (newSR) { diff --git a/src/Plugin/Classes.cs b/src/Plugin/Classes.cs index 97298c7..4055234 100644 --- a/src/Plugin/Classes.cs +++ b/src/Plugin/Classes.cs @@ -274,6 +274,8 @@ public class IndexedReplayFrames public class PlayerRecord { public string? PlayerName { get; set; } + public string? SteamID { get; set; } + public string? MapName { get; set; } public int TimerTicks { get; set; } } diff --git a/src/Plugin/Globals.cs b/src/Plugin/Globals.cs index 9b7d2ae..daf0222 100644 --- a/src/Plugin/Globals.cs +++ b/src/Plugin/Globals.cs @@ -27,7 +27,7 @@ public partial class SharpTimer public string compileTimeStamp = new DateTime(CompileTimeStamp.CompileTime, DateTimeKind.Utc).ToString(); public override string ModuleName => "SharpTimer"; - public override string ModuleVersion => $"0.3.0t"; + public override string ModuleVersion => $"0.3.1c"; public override string ModuleAuthor => "dea https://github.com/deabb/"; public override string ModuleDescription => "A CS2 Timer Plugin"; @@ -125,10 +125,11 @@ public partial class SharpTimer public bool globalRanksFreePointsEnabled = true; public int maxGlobalFreePoints = 20; public float? globalPointsMultiplier = 1.0f; - public int minGlobalPointsForRank = 1000; + public int minGlobalPointsForRank = 1; + public double globalPointsBonusMultiplier = 0.5; public bool displayChatTags = true; public bool displayScoreboardTags = true; - public string customVIPTag = "[VIP]"; + public string customVIPTag = "[VIP] "; //public string vipGifHost = "https://files.catbox.moe"; public bool useTriggers = true; diff --git a/src/Plugin/Utils.cs b/src/Plugin/Utils.cs index 7486651..78d86fa 100644 --- a/src/Plugin/Utils.cs +++ b/src/Plugin/Utils.cs @@ -287,38 +287,6 @@ private static string FormatSpeedDifferenceFromString(string currentSpeed, strin } } - public double CalculatePoints(int timerTicks, int style) - { - double basePoints = 10000.0; - double timeFactor = 0.0001; - double tierMult = 0.1; - double styleMult = GetStyleMultiplier(style); - - if (currentMapTier != null) - { - tierMult = (double)(currentMapTier * 0.1); - } - - double points = basePoints / (timerTicks * timeFactor); - return points * tierMult * styleMult; - } - - public double CalculatePBPoints(int timerTicks, int style) - { - double basePoints = 10000.0; - double timeFactor = 0.01; - double tierMult = 0.1; - double styleMult = GetStyleMultiplier(style); - - if (currentMapTier != null) - { - tierMult = (double)(currentMapTier * 0.1); - } - - double points = basePoints / (timerTicks * timeFactor); - return points * tierMult * styleMult; - } - string ParseColorToSymbol(string input) { Dictionary colorNameSymbolMap = new(StringComparer.OrdinalIgnoreCase) @@ -842,7 +810,7 @@ private void OnMapStartHandler(string mapName) if (enableReplays && enableSRreplayBot) { - AddTimer(8.0f, () => + AddTimer(10.0f, () => { if (ConVar.Find("mp_force_pick_time")!.GetPrimitiveValue() == 1.0) _ = Task.Run(async () => await SpawnReplayBot());