Improve illiterate text and FOV handling

This commit is contained in:
Juzlus 2026-08-03 21:58:19 +02:00
parent cc32a11404
commit ada2f4ff7b
36 changed files with 799 additions and 123 deletions

View file

@ -112,10 +112,10 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("correct_form_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("correct_form_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("correct_form_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("correct_form_setskill"));
return;
}
@ -123,10 +123,10 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("player_not_found_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("player_not_found_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("player_not_found_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("player_not_found_setskill"));
return;
}
@ -137,10 +137,10 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("skill_not_found_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("skill_not_found_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("skill_not_found_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("skill_not_found_setskill"));
return;
}
@ -155,11 +155,11 @@ namespace src.command
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("done_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("done_setskill"));
return;
}
SkillUtils.PrintToChat(player, $"{player.GetTranslation("done_setskill")}: {ChatColors.LightRed}{player.GetSkillName(skill.Skill)} {ChatColors.Lime}{player.GetTranslation("for_setskill")} {ChatColors.LightRed}\u202A{targetPlayer.PlayerName}\u202C");
SkillUtils.PrintToChat(player, $"{player.GetTranslationWithoutIlliterate("done_setskill")}: {ChatColors.LightRed}{player.GetSkillName(skill.Skill)} {ChatColors.Lime}{player.GetTranslationWithoutIlliterate("for_setskill")} {ChatColors.LightRed}\u202A{targetPlayer.PlayerName}\u202C");
if (skill.Display)
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(skill.Skill)}", border: "b");
@ -168,11 +168,11 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("error_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("error_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("error_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("error_setskill"));
}
}
@ -234,16 +234,16 @@ namespace src.command
if (string.IsNullOrEmpty(map))
{
command.ReplyToCommand($" {ChatColors.Red}{command.CallingPlayer?.GetTranslation("invalid_map")}");
command.ReplyToCommand($" {ChatColors.Red}{command.CallingPlayer?.GetTranslationWithoutIlliterate("invalid_map")}");
return;
}
Localization.PrintTranslationToChatAll($" {ChatColors.Yellow}{{0}} ({ChatColors.Green}{map}{ChatColors.Yellow})...", ["loading_map"]);
Localization.PrintTranslationToChatAll($" {ChatColors.Yellow}{{0}} ({ChatColors.Green}{map}{ChatColors.Yellow})...", ["loading_map"], false);
if (uint.TryParse(map, out _))
Server.ExecuteCommand($"host_workshop_map {map}");
else if (!Server.IsMapValid(map))
command.ReplyToCommand($" {ChatColors.Red}{command.CallingPlayer?.GetTranslation("invalid_map")}");
command.ReplyToCommand($" {ChatColors.Red}{command.CallingPlayer?.GetTranslationWithoutIlliterate("invalid_map")}");
else
Server.ExecuteCommand($"changelevel {map}");
}
@ -274,12 +274,12 @@ namespace src.command
if (Instance?.GameRules?.WarmupPeriod == true)
{
Server.ExecuteCommand("mp_warmup_end");
Localization.PrintTranslationToChatAll($" {ChatColors.Green}{{0}}", ["game_start"]);
Localization.PrintTranslationToChatAll($" {ChatColors.Green}{{0}}", ["game_start"], false);
}
else
{
Server.ExecuteCommand("mp_restartgame 2");
Instance?.AddTimer(2.0f, () => Localization.PrintTranslationToChatAll($" {ChatColors.Green}{{0}}", ["game_start"]), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
Instance?.AddTimer(2.0f, () => Localization.PrintTranslationToChatAll($" {ChatColors.Green}{{0}}", ["game_start"], false), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
}
@ -348,7 +348,7 @@ namespace src.command
private static void Pause()
{
Localization.PrintTranslationToChatAll($" {(gamePaused ? ChatColors.Green : ChatColors.Red)}{{0}}", [gamePaused ? "unpause" : "pause"]);
Localization.PrintTranslationToChatAll($" {(gamePaused ? ChatColors.Green : ChatColors.Red)}{{0}}", [gamePaused ? "unpause" : "pause"], false);
Server.ExecuteCommand(gamePaused ? "mp_unpause_match" : "mp_pause_match");
gamePaused = !gamePaused;
}
@ -360,7 +360,7 @@ namespace src.command
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
if (!string.IsNullOrEmpty(config.NormalCommands.HealCommand.Permissions) && !AdminManager.PlayerHasPermissions(player, config.NormalCommands.HealCommand.Permissions)) return;
SkillUtils.AddHealth(player.PlayerPawn.Value, 100);
player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("healed")}");
player.PrintToChat($" {ChatColors.Green}{player.GetTranslationWithoutIlliterate("healed")}");
}
[CommandHelper(minArgs: 1, whoCanExecute: CommandUsage.CLIENT_ONLY)]
@ -374,7 +374,7 @@ namespace src.command
if (int.TryParse(command.GetArg(1), out int health))
SkillUtils.AddHealth(pawn, health - pawn.Health, health);
player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("set_health")}");
player.PrintToChat($" {ChatColors.Green}{player.GetTranslationWithoutIlliterate("set_health")}");
}
[CommandHelper(minArgs: 0, whoCanExecute: CommandUsage.CLIENT_ONLY)]
@ -396,7 +396,7 @@ namespace src.command
time = 40;
bomb.C4Blow = Server.CurrentTime + time;
player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("planted_bomb_spawned", [time])}");
player.PrintToChat($" {ChatColors.Green}{player.GetTranslationWithoutIlliterate("planted_bomb_spawned", [time])}");
}
[CommandHelper(minArgs: 0, whoCanExecute: CommandUsage.CLIENT_ONLY)]
@ -415,7 +415,7 @@ namespace src.command
var bot = Utilities.GetPlayers().Where(p => p != null && p.IsValid && p.IsBot && p.PawnIsAlive && (botSlot == -1 || p.Slot == botSlot)).FirstOrDefault();
if (bot == null || bot.PlayerPawn.Value == null || !bot.PlayerPawn.Value.IsValid)
{
player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("bot_placed_not_found")}");
player.PrintToChat($" {ChatColors.Green}{player.GetTranslationWithoutIlliterate("bot_placed_not_found")}");
return;
}
@ -426,7 +426,7 @@ namespace src.command
|| (int.TryParse(command.GetArg(2), out int godmodeInt) && godmodeInt == 1))
bot.PlayerPawn.Value.TakesDamage = false;
player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("bot_placed")}");
player.PrintToChat($" {ChatColors.Green}{player.GetTranslationWithoutIlliterate("bot_placed")}");
}
[CommandHelper(minArgs: 0, whoCanExecute: CommandUsage.CLIENT_ONLY)]
@ -444,7 +444,7 @@ namespace src.command
playerInfo.HideHUD = isDisplayHUD ? int.MaxValue : int.MinValue;
SkillUtils.SetMenuPaused(player, isDisplayHUD);
player.PrintToChat($" {(!isDisplayHUD ? ChatColors.Green : ChatColors.Red)}{player.GetTranslation(!isDisplayHUD ? "hud_on" : "hud_off")}");
player.PrintToChat($" {(!isDisplayHUD ? ChatColors.Green : ChatColors.Red)}{player.GetTranslationWithoutIlliterate(!isDisplayHUD ? "hud_on" : "hud_off")}");
}
[CommandHelper(minArgs: 2, whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
@ -466,7 +466,7 @@ namespace src.command
if (!int.TryParse(command.GetArg(1), out int ctScore) || !int.TryParse(command.GetArg(2), out int tScore))
{
if (player != null && player.IsValid)
SkillUtils.PrintToChat(player, player.GetTranslation("correct_form_setscore"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("correct_form_setscore"));
return;
}
@ -496,11 +496,11 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("correct_form_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("correct_form_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("correct_form_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("correct_form_setskill"));
return;
}
@ -508,11 +508,11 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("player_not_found_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("player_not_found_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("player_not_found_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("player_not_found_setskill"));
return;
}
@ -523,11 +523,11 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("skill_not_found_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("skill_not_found_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("skill_not_found_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("skill_not_found_setskill"));
return;
}
@ -547,11 +547,11 @@ namespace src.command
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("done_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("done_setskill"));
return;
}
SkillUtils.PrintToChat(player, $"{player.GetTranslation("done_setskill")}: {ChatColors.LightRed}{player.GetSkillName(skill.Skill)} {ChatColors.Lime}{player.GetTranslation("for_setskill")} {ChatColors.LightRed}\u202A{targetPlayer.PlayerName}\u202C");
SkillUtils.PrintToChat(player, $"{player.GetTranslationWithoutIlliterate("done_setskill")}: {ChatColors.LightRed}{player.GetSkillName(skill.Skill)} {ChatColors.Lime}{player.GetTranslationWithoutIlliterate("for_setskill")} {ChatColors.LightRed}\u202A{targetPlayer.PlayerName}\u202C");
if (skill.Display)
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(skill.Skill)}", border: "b");
@ -560,11 +560,11 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("error_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("error_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("error_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("error_setskill"));
}
}
@ -607,9 +607,9 @@ namespace src.command
Event.InvalidateFreezeDisabledCache();
if (player != null && player.IsValid)
player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("reload")}");
player.PrintToChat($" {ChatColors.Green}{player.GetTranslationWithoutIlliterate("reload")}");
else
Server.PrintToConsole($" {ChatColors.Green}{Localization.GetTranslation("reload")}");
Server.PrintToConsole($" {ChatColors.Green}{Localization.GetTranslationWithoutIlliterate("reload")}");
foreach (var target in Instance.SkillPlayer)
{
@ -645,10 +645,10 @@ namespace src.command
{
if (player == null)
{
Server.PrintToConsole(Localization.GetTranslation("player_not_found_setskill"));
Server.PrintToConsole(Localization.GetTranslationWithoutIlliterate("player_not_found_setskill"));
return;
}
SkillUtils.PrintToChat(player, player.GetTranslation("player_not_found_setskill"));
SkillUtils.PrintToChat(player, player.GetTranslationWithoutIlliterate("player_not_found_setskill"));
return;
}
}

View file

@ -21,7 +21,7 @@ namespace src.command
vote.SetActive(false);
vote.TimeToNextSameVoting = vote.TimeToNextVoting;
Localization.PrintTranslationToChatAll($" {ChatColors.Red}{{0}}", ["vote_timeout"], [commandName]);
Localization.PrintTranslationToChatAll($" {ChatColors.Red}{{0}}", ["vote_timeout"], false, [commandName]);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
@ -38,7 +38,7 @@ namespace src.command
votes.TryAdd(vote, 0);
string commandName = $"!{VoteTypeCommands.GetCommand(vote.Type)?.Replace("css_", "")}{(!string.IsNullOrEmpty(vote?.Args) ? $" {vote?.Args}" : "")}";
Localization.PrintTranslationToChatAll($" {ChatColors.Lime}{{0}}", ["vote_started"], [commandName]);
Localization.PrintTranslationToChatAll($" {ChatColors.Lime}{{0}}", ["vote_started"], false, [commandName]);
foreach (var player in Utilities.GetPlayers())
player.EmitSound("UIPanorama.tab_mainmenu_news");
@ -61,12 +61,12 @@ namespace src.command
{
if (votes.Keys.Any(v => v.NextVoting() > DateTime.Now))
{
player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_wait")}");
player.PrintToChat($" {ChatColors.Red}{player.GetTranslationWithoutIlliterate("vote_wait")}");
return;
}
else if (votes.Keys.Any(v => v.Type == voteType && v.NextSameVoting() > DateTime.Now))
{
player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_same_wait")}");
player.PrintToChat($" {ChatColors.Red}{player.GetTranslationWithoutIlliterate("vote_same_wait")}");
return;
}
@ -75,12 +75,12 @@ namespace src.command
if (vote == null)
{
player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_not_enough_players")}");
player.PrintToChat($" {ChatColors.Red}{player.GetTranslationWithoutIlliterate("vote_not_enough_players")}");
return;
}
if (!vote.PlayersVoted.TryAdd(player.SteamID, 0))
player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_alredy_voted")}");
player.PrintToChat($" {ChatColors.Red}{player.GetTranslationWithoutIlliterate("vote_alredy_voted")}");
else CheckVote(vote);
}
@ -100,7 +100,7 @@ namespace src.command
else
{
StartVoteTimer(vote!, commandName);
Localization.PrintTranslationToChatAll($" {ChatColors.Yellow}{{0}} '': {ChatColors.Green}{voted}/{playersNeeded}", ["vote_vote"]);
Localization.PrintTranslationToChatAll($" {ChatColors.Yellow}{{0}} '': {ChatColors.Green}{voted}/{playersNeeded}", ["vote_vote"], false);
}
}
}

View file

@ -248,6 +248,7 @@
"illiterate": "Analphabet",
"illiterate_desc": "Solange du am Leben bist, können deine Feinde nichts lesen",
"illiterate_alert": "Analphabet ist aktiv! Du kannst keine Nachrichten lesen, bis der Besitzer dieser Fähigkeit eliminiert ist",
"illusionist": "Illusionist",
"illusionist_desc": "Klicke [css_useSkill], um ein Abbild zu schicken, das geradeaus läuft",

View file

@ -241,6 +241,7 @@
"illiterate": "Illiterate",
"illiterate_desc": "As long as you are alive, your enemies cannot read",
"illiterate_alert": "Illiterate is active! You cannot read messages until the owner of this skill is eliminated",
"illusionist": "Illusionist",
"illusionist_desc": "Click [css_useSkill] to deploy a replica that walks straight ahead",

View file

@ -247,7 +247,8 @@
"iana_desc": "Cliquez sur [css_useSkill] pour contrôler votre hologramme pendant quelques secondes",
"illiterate": "Illettré",
"illiterate_desc": "Tant que vous êtes en vie, vos ennemis ne mogą pas lire",
"illiterate_desc": "Tant que vous êtes en vie, vos ennemis ne peuvent pas lire",
"illiterate_alert": "Illettré est actif ! Vous ne pouvez pas lire les messages tant que le propriétaire de cette compétence n'est pas éliminé",
"illusionist": "Illusionniste",
"illusionist_desc": "Cliquez sur [css_useSkill] pour déployer une réplique qui marche droit devant",

View file

@ -248,6 +248,7 @@
"illiterate": "Analfabeta",
"illiterate_desc": "Dopóki żyjesz, Twoi wrogowie nie potrafią czytać",
"illiterate_alert": "Analfabeta jest aktywny! Nie możesz czytać wiadomości, dopóki właściciel tej umiejętności nie zostanie wyeliminowany",
"illusionist": "Iluzjonista",
"illusionist_desc": "Naciśnij [css_useSkill], aby wypuścić replikę idącą prosto przed siebie",

View file

@ -248,6 +248,7 @@
"illiterate": "Analfabeto",
"illiterate_desc": "Enquanto você estiver vivo, seus inimigos não conseguem ler",
"illiterate_alert": "Analfabeto está ativo! Você não pode ler mensagens até que o proprietário desta habilidade seja eliminado",
"illusionist": "Ilusionista",
"illusionist_desc": "Clique em [css_useSkill] para enviar uma réplica que caminha em frente",

View file

@ -248,6 +248,7 @@
"illiterate": "Неграмотный",
"illiterate_desc": "Пока вы живы, ваши враги не могут читать (чат)",
"illiterate_alert": "Неграмотный активен! Вы не можете читать сообщения, пока владелец этой способности не будет устранён",
"illusionist": "Иллюзионист",
"illusionist_desc": "Нажмите [css_useSkill], чтобы создать копию, идущую вперёд",

View file

@ -241,6 +241,7 @@
"illiterate": "Cahiliye",
"illiterate_desc": "Yaşadığın sürece rakibin sohbeti (chat) veya oyun yazılarını okuyamaz",
"illiterate_alert": "Cahiliye aktif! Bu yeteneğin sahibi ortadan kalkana kadar mesajları okuyamazsınız.",
"illusionist": "İllüzyonist",
"illusionist_desc": "[css_useSkill] tuşuna basarak düz ilerleyen illüzyon oluştur",

View file

@ -226,6 +226,7 @@
"illiterate": "文盲",
"illiterate_desc": "只要你还活着,你的敌人就无法阅读",
"illiterate_alert": "文盲已激活!在该技能的拥有者被消灭之前,你无法阅读消息。",
"illusionist": "幻术师",
"illusionist_desc": "点击 [css_useSkill] 部署一个直线行走的幻象",

View file

@ -11,7 +11,7 @@ namespace src.menu
{
public static void DisplaySkillsList(CCSPlayerController player)
{
CenterHtmlMenu menu = new($"[ ★ {player.GetTranslation("skills_menu")} ★ ]", Instance);
CenterHtmlMenu menu = new($"[ ★ {player.GetTranslationWithoutIlliterate("skills_menu")} ★ ]", Instance);
foreach (var skillInfo in SkillData.Skills)
{

View file

@ -21,6 +21,7 @@ public interface ISkill
public static void NewRound() { }
public static void RoundEnd() { }
public static void PlayerDisconnect(uint playerIndex) { }
public static void PlayerMakeSound(UserMessage _) { }
public static void PlayerBlind(EventPlayerBlind _) { }
public static void PlayerHurt(EventPlayerHurt _) { }

View file

@ -35,6 +35,7 @@ namespace src.player
private static readonly ConcurrentDictionary<uint, ConcurrentBag<jSkill_SkillInfo>> playersSkills = [];
public static readonly ConcurrentDictionary<uint, jSkill_SkillInfo> staticSkills = [];
public static readonly ConcurrentDictionary<uint, uint> playersCustomFOV = [];
private static readonly object setLock = new();
public static void Load()
@ -525,7 +526,7 @@ namespace src.player
Localization.PreResolveLanguage(player);
string welcomeMsg = player.GetTranslation("welcome_message", "welcome");
string welcomeMsg = player.GetTranslationWithoutIlliterate("welcome_message", "welcome");
foreach (string line in welcomeMsg.Split("\n"))
player.PrintToChat($" {ChatColors.Green}" + line.Replace("{PLAYER}", $" {ChatColors.Red}\u202A{player.PlayerName}\u202C{ChatColors.Green}", StringComparison.OrdinalIgnoreCase)
.Replace("{SERVER_NAME}", $" {ChatColors.Red}{ConVar.Find("hostname")?.StringValue ?? "Default Server"}{ChatColors.Green}", StringComparison.OrdinalIgnoreCase)
@ -603,8 +604,11 @@ namespace src.player
player.ReplicateConVar("sv_disable_radar", "0");
player.DesiredFOV = 0;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
if (SkillsInfo.GetValue<bool>(Skills.Magnifier, "active") && player.DesiredFOV == SkillsInfo.GetValue<uint>(Skills.Magnifier, "customFOV"))
{
player.DesiredFOV = playersCustomFOV.TryGetValue(player.Index, out uint fov) ? fov : 0;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
}
}
private static HookResult PlayerDeathPre(EventPlayerDeath @event, GameEventInfo info)
@ -678,7 +682,7 @@ namespace src.player
SkillUtils.PrintToChat(victim,
$"{ChatColors.DarkRed}{(attackerInfo.SpecialSkill == Skills.None ? victim.GetSkillName(skillData.Skill) : $"{victim.GetSkillName(specialSkillData.Skill)} -> {victim.GetSkillName(skillData.Skill)}")}{ChatColors.Lime} - {skillDesc}",
title: $"{victim.GetTranslation("enemy_skill")} {ChatColors.DarkRed}\u202A{attacker.PlayerName}\u202C{ChatColors.Lime}");
title: $"{victim.GetTranslationWithoutIlliterate("enemy_skill")} {ChatColors.DarkRed}\u202A{attacker.PlayerName}\u202C{ChatColors.Lime}");
}
}
return HookResult.Continue;
@ -756,7 +760,6 @@ namespace src.player
if (Illiterate.CheckIlliterateSkill(player))
{
headerLine = Illiterate.GetRandomText(headerLine);
centerLine = Illiterate.GetRandomText(centerLine);
extraLine = Illiterate.GetRandomText(extraLine);
}

View file

@ -103,8 +103,8 @@ namespace src.player
if (skills == null || skills.IsEmpty)
{
infoLine = player.GetTranslation("your_skill");
skillLine = player.GetTranslation("none");
infoLine = player.GetTranslationWithoutIlliterate("your_skill");
skillLine = player.GetTranslationWithoutIlliterate("none");
}
else if (skillPlayer.IsDrawing && player.PawnIsAlive)
{
@ -115,7 +115,7 @@ namespace src.player
var skillsArray = skills.ToArray();
var randomSkill = skillsArray[Instance.Random.Next(skillCount)];
infoLine = player.GetTranslation("drawing_skill");
infoLine = player.GetTranslationWithoutIlliterate("drawing_skill");
skillLine = $"<font color='{randomSkill.Color}'>{player.GetSkillName(randomSkill.Skill)}</font>";
}
}
@ -127,7 +127,7 @@ namespace src.player
if (skillInfo != null)
{
infoLine = player.GetTranslation("your_skill");
infoLine = player.GetTranslationWithoutIlliterate("your_skill");
skillLine = $"<font color='{skillInfo.Color}'>{player.GetSkillName(skillInfo.Skill, skillPlayer.SkillChance)}</font>";
if (skillInfo.Skill != Skills.None)
@ -177,7 +177,7 @@ namespace src.player
if (pName.Length > 18)
pName = $"{pName[..17]}...";
var observerSkill = player.GetTranslation("observer_skill");
var observerSkill = player.GetTranslationWithoutIlliterate("observer_skill");
infoLine = string.IsNullOrEmpty(observerSkill) ? pName : $"{observerSkill} {pName}";
if (observedSkill.SpecialSkill == Skills.None || observedSpecialInfo == null)

View file

@ -195,6 +195,7 @@ namespace src.player
SkillsUsedThisMap.Clear();
nextRoundPicks.Clear();
playersCustomFOV.Clear();
playersSkills.Clear();
staticSkills.Clear();
@ -238,10 +239,10 @@ namespace src.player
if (string.IsNullOrEmpty(skillsText)) continue;
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslation("summary"), border: "t");
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("summary"), border: "t");
foreach (string text in skillsText.Split("\n"))
if (!string.IsNullOrEmpty(text))
SkillUtils.PrintToChat(player, text, title: player.GetTranslation("teammate_skills"), border: "");
SkillUtils.PrintToChat(player, text, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "");
SkillUtils.PrintToChat(player, string.Empty, border: "b");
}
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
@ -578,11 +579,11 @@ namespace src.player
if (!string.IsNullOrEmpty(teammateSkills))
{
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslation("teammate_skills"), border: "t");
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "t");
foreach (string text in teammateSkills.Split("\n"))
if (!string.IsNullOrEmpty(text))
SkillUtils.PrintToChat(player, text, title: player.GetTranslation("teammate_skills"), border: "");
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslation("teammate_skills"), border: "b");
SkillUtils.PrintToChat(player, text, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "");
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "b");
}
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}

View file

@ -141,7 +141,6 @@ namespace src.player.skills
public float ExplosionRadius { get; set; } = explosionRadius;
public int ExplosionDamage { get; set; } = explosionDamage;
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
}
}
}

View file

@ -0,0 +1,451 @@
//using CounterStrikeSharp.API;
//using CounterStrikeSharp.API.Core;
//using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
//using CounterStrikeSharp.API.Modules.Utils;
//using jRandomSkills.src.utils;
//using src.utils;
//using System.Collections.Concurrent;
//using System.Drawing;
//namespace src.player.skills
//{
// public class ExplosiveChicken : ISkill
// {
// private const Skills skillName = Skills.ExplosiveChicken;
// private class PlayerInfo
// {
// public uint OwnerIndex { get; set; }
// public uint? ChickenIndex { get; set; }
// public Vector? ChickenLastOrigin { get; set; }
// public int ChickenHealth { get; set; }
// public bool PlayerActive { get; set; }
// public bool CanUse { get; set; }
// public int Cooldown { get; set; }
// public int InfoMessageTime { get; set; }
// public int InfoMessageType { get; set; }
// }
// private static readonly ConcurrentDictionary<uint, PlayerInfo> chickens = [];
// private static readonly ConcurrentDictionary<uint, uint> chickenOwner = [];
// private const string chickenParticle = "particles/critters/chicken/chicken_goop.vpcf";
// private static readonly QAngle angle = new(20, -15, 43);
// private static readonly ConcurrentDictionary<int, (byte Team, uint Owner)> nades = [];
// public static void LoadSkill()
// {
// SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
// jRandomSkills.Instance.AddToManifest(chickenParticle);
// }
// public static void NewRound()
// {
// foreach (var key in chickens.Keys)
// {
// if (chickens.TryRemove(key, out _))
// EntityManager.DestroyPlayerEntities(key);
// }
// chickens.Clear();
// chickenOwner.Clear();
// }
// public static void EnableSkill(CCSPlayerController player)
// {
// if (player == null || !player.IsValid) return;
// int health = SkillsInfo.GetValue<int>(skillName, "health");
// chickens.TryAdd(player.Index, new PlayerInfo
// {
// OwnerIndex = player.Index,
// ChickenIndex = null,
// ChickenLastOrigin = null,
// ChickenHealth = 0,
// PlayerActive = true,
// CanUse = true,
// Cooldown = 0,
// InfoMessageTime = 0,
// InfoMessageType = 0,
// });
// chickens[player.Index].PlayerActive = true;
// }
// public static void DisableSkill(CCSPlayerController player)
// {
// if (player == null || !player.IsValid) return;
// chickens[player.Index].PlayerActive = false;
// }
// public static void UseSkill(CCSPlayerController player)
// {
// var playerPawn = player.PlayerPawn.Value;
// if (playerPawn?.CBodyComponent == null) return;
// if (chickens.TryGetValue(player.Index, out var skillInfo))
// {
// if (!player.IsValid || playerPawn.Health <= 0) return;
// if (skillInfo.CanUse && skillInfo.PlayerActive)
// {
// if (skillInfo.ChickenIndex != null)
// {
// skillInfo.InfoMessageTime = Server.TickCount;
// skillInfo.InfoMessageType = 1;
// return;
// }
// skillInfo.CanUse = false;
// skillInfo.Cooldown = Server.TickCount;
// SpawnChicken(player, skillInfo);
// }
// }
// }
// private static void SpawnChicken(CCSPlayerController player, PlayerInfo skillInfo)
// {
// if (player == null || !player.IsValid) return;
// var pawn = player.PlayerPawn.Value;
// if (pawn == null || !pawn.IsValid || pawn.AbsRotation == null || pawn.AbsOrigin == null || pawn.Health <= 0) return;
// float distance = 40;
// Vector pawnPos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + 10);
// Vector chickenPos = pawn.AbsOrigin + SkillUtils.GetForwardVector(pawn.AbsRotation) * distance;
// chickenPos.Z += 10;
// var trace = RayTrace.TraceShape(player, pawnPos, chickenPos, 0, null);
// if (trace != null && trace.Value.DidHit)
// {
// skillInfo.CanUse = true;
// skillInfo.Cooldown = 0;
// skillInfo.InfoMessageTime = Server.TickCount;
// skillInfo.InfoMessageType = 2;
// return;
// }
// CChicken? chicken = EntityManager.CreateTrackedChicken(player.Index);
// if (chicken == null || !chicken.IsValid) return;
// int health = SkillsInfo.GetValue<int>(skillName, "chickenHealth");
// chicken.Render = Color.Red;
// chicken.MaxHealth = health;
// chicken.Health = health;
// chickenOwner[chicken.Index] = player.Index;
// skillInfo.ChickenIndex = chicken.Index;
// skillInfo.ChickenLastOrigin = chickenPos;
// skillInfo.ChickenHealth = health;
// chicken.Teleport(chickenPos);
// }
// public static void OnTakeDamage(DynamicHook h)
// {
// CEntityInstance param = h.GetParam<CEntityInstance>(0);
// CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
// if (param == null || param.Entity == null || param2 == null) return;
// HandleChickenHit(param, param2);
// HandleExplosionHit(param, param2);
// }
// public static void HandleChickenHit(CEntityInstance param, CTakeDamageInfo param2)
// {
// if (!chickenOwner.TryGetValue(param.Index, out var ownerIndex)) return;
// var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
// if (owner == null || !owner.IsValid) return;
// var attackerEnt = param2.Attacker?.Value;
// if (attackerEnt == null || !attackerEnt.IsValid) return;
// var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle);
// if (!attackerPawn.IsValid || attackerPawn.DesignerName != "player") return;
// var ownerPawn = owner.PlayerPawn.Value;
// if (ownerPawn == null || !ownerPawn.IsValid) return;
// if (attackerPawn.Index != ownerPawn.Index && attackerPawn.TeamNum == owner.TeamNum)
// {
// param2.Damage = 0;
// return;
// }
// var playerInfo = chickens[owner.Index];
// if (playerInfo == null) return;
// playerInfo.ChickenHealth = Math.Max(0, playerInfo.ChickenHealth - (int)param2.Damage);
// if (playerInfo.ChickenHealth > 0)
// CreateHitParticles(param.Index);
// else
// {
// playerInfo.ChickenIndex = null;
// playerInfo.ChickenHealth = SkillsInfo.GetValue<int>(skillName, "chickenHealth");
// playerInfo.ChickenLastOrigin = null;
// chickenOwner.TryRemove(param.Index, out _);
// }
// }
// public static void HandleExplosionHit(CEntityInstance param, CTakeDamageInfo param2)
// {
// var nade = param2.Attacker.Value;
// if (nade == null || !nade.IsValid) return;
// if (nade.DesignerName != "hegrenade_projectile") return;
// if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("explosive_chicken_team_")) return;
// var parts = nade.Globalname.Split('_');
// if (parts.Length < 4) return;
// if (!int.TryParse(parts[2], out int nadeTeam)) return;
// if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
// CCSPlayerPawn victimPawn = new(param.Handle);
// if (victimPawn.DesignerName != "player") return;
// if (victimPawn.Controller?.Value == null) return;
// var victim = victimPawn.Controller.Value.As<CCSPlayerController>();
// if (victim == null || !victim.IsValid || victim.Index == ownerIndex) return;
// var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
// if (owner != null && !owner.IsValid) owner = null;
// if (victimPawn.TeamNum == nadeTeam)
// param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
// if (owner != null && param2.Damage >= victimPawn.Health)
// SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
// }
// private static void CreateHitParticles(uint chickenIndex)
// {
// var chicken = Utilities.GetEntityFromIndex<CChicken>((int)chickenIndex);
// if (chicken == null || !chicken.IsValid || chicken.AbsOrigin == null) return;
// var particle = EntityManager.CreateTrackedParticleSystem(chicken.Index, chickenParticle, autoDestroySeconds: 3f);
// if (particle == null) return;
// Vector pos = new(chicken.AbsOrigin.X, chicken.AbsOrigin.Y, chicken.AbsOrigin.Z + 10);
// particle.Teleport(pos);
// particle.AcceptInput("Start");
// }
// public static void OnTick()
// {
// int tickCooldown = SkillsInfo.GetValue<int>(skillName, "tickCooldown");
// float boostFactor = SkillsInfo.GetValue<float>(skillName, "boostFactor");
// float triggerRadius = SkillsInfo.GetValue<float>(skillName, "triggerRadius");
// float cooldownDuration = SkillsInfo.GetValue<float>(skillName, "cooldown");
// bool processMovement = Server.TickCount % tickCooldown == 0;
// var players = PlayerManager.GetTickPlayers().ToArray();
// foreach (var player in players)
// {
// if (player == null || !player.IsValid) continue;
// var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
// if (playerInfo?.Skill != skillName) continue;
// var pawn = player.PlayerPawn.Value;
// if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || pawn.Health <= 0) continue;
// if (!chickens.TryGetValue(player.Index, out var skillInfo) || skillInfo == null) continue;
// if (!skillInfo.CanUse && skillInfo.ChickenIndex == null)
// {
// float readyAtTick = skillInfo.Cooldown + (cooldownDuration * 64);
// if (Server.TickCount >= readyAtTick)
// {
// skillInfo.CanUse = true;
// skillInfo.Cooldown = 0;
// }
// }
// UpdateHUD(skillInfo);
// if (skillInfo.ChickenIndex == null || !processMovement) continue;
// var chicken = Utilities.GetEntityFromIndex<CChicken>((int)skillInfo.ChickenIndex.Value);
// if (chicken == null || !chicken.IsValid || chicken.AbsOrigin == null)
// {
// skillInfo.ChickenIndex = null;
// skillInfo.ChickenHealth = 0;
// skillInfo.ChickenLastOrigin = null;
// continue;
// }
// Vector currentOrigin = new(chicken.AbsOrigin.X, chicken.AbsOrigin.Y, chicken.AbsOrigin.Z);
// CCSPlayerController? enemy = GetClosetEnemy(currentOrigin, player.TeamNum);
// if (enemy == null || !enemy.IsValid) continue;
// var enemyPawn = enemy.PlayerPawn?.Value;
// if (enemyPawn == null || !enemyPawn.IsValid || enemyPawn.AbsOrigin == null) continue;
// if (chicken.Leader.Raw != enemy.Pawn.Raw)
// chicken.Leader.Raw = enemy.Pawn.Raw;
// player.PrintToChat($"Closest enemy: {(enemy != null ? enemy.PlayerName : "None")}");
// if (skillInfo.ChickenLastOrigin != null)
// {
// float dx = currentOrigin.X - skillInfo.ChickenLastOrigin.X;
// float dy = currentOrigin.Y - skillInfo.ChickenLastOrigin.Y;
// float dist2D = MathF.Sqrt(dx * dx + dy * dy);
// if (dist2D > 0.05f && dist2D < 20.0f)
// {
// Vector newPos = new(
// currentOrigin.X + (dx * boostFactor),
// currentOrigin.Y + (dy * boostFactor),
// currentOrigin.Z
// );
// player.PrintToChat($"Chicken boost");
// chicken.Teleport(newPos, chicken.AbsRotation, chicken.AbsVelocity);
// skillInfo.ChickenLastOrigin = newPos;
// }
// else
// skillInfo.ChickenLastOrigin = currentOrigin;
// }
// else
// skillInfo.ChickenLastOrigin = currentOrigin;
// float pdx = enemyPawn.AbsOrigin.X - skillInfo.ChickenLastOrigin.X;
// float pdy = enemyPawn.AbsOrigin.Y - skillInfo.ChickenLastOrigin.Y;
// float pdz = enemyPawn.AbsOrigin.Z - skillInfo.ChickenLastOrigin.Z;
// float distToPlayer = MathF.Sqrt(pdx * pdx + pdy * pdy + pdz * pdz);
// if (distToPlayer <= triggerRadius)
// ExplodeChicken(player, chicken, skillInfo);
// }
// }
// private static CCSPlayerController? GetClosetEnemy(Vector chickenPos, byte ownerTeam)
// {
// var players = PlayerManager.GetTickPlayers().ToArray();
// CCSPlayerController? closestEnemy = null;
// float closestDistance = float.MaxValue;
// foreach (var player in players)
// {
// if (player == null || !player.IsValid || player.TeamNum == ownerTeam) continue;
// var pawn = player.PlayerPawn?.Value;
// if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || pawn.Health <= 0) continue;
// float distance = SkillUtils.Distance(chickenPos, pawn.AbsOrigin);
// if (distance < closestDistance)
// {
// closestDistance = distance;
// closestEnemy = player;
// }
// }
// return closestEnemy;
// }
// private static void ExplodeChicken(CCSPlayerController owner, CChicken chicken, PlayerInfo skillInfo)
// {
// if (owner == null || !owner.IsValid || chicken == null || !chicken.IsValid || chicken.AbsOrigin == null) return;
// Vector pos = new(chicken.AbsOrigin.X, chicken.AbsOrigin.Y, chicken.AbsOrigin.Z + 10);
// EntityManager.DestroyEntity(chicken.Index, 0f);
// skillInfo.ChickenIndex = null;
// skillInfo.ChickenHealth = 0;
// skillInfo.ChickenLastOrigin = null;
// chickenOwner.TryRemove(chicken.Index, out _);
// jRandomSkills.Instance.AddTickTimer(4, () =>
// {
// SkillUtils.CreateHEGrenadeProjectile(pos, angle, new Vector(0, 0, -10), owner.TeamNum);
// nades.AddOrUpdate(Server.TickCount, (owner.TeamNum, owner.Index), (_, _) => (owner.TeamNum, owner.Index));
// });
// }
// public static void OnEntitySpawned(CEntityInstance entity)
// {
// if (entity.DesignerName != "hegrenade_projectile") return;
// var heProjectile = entity.As<CBaseCSGrenadeProjectile>();
// if (heProjectile == null || !heProjectile.IsValid || heProjectile.AbsRotation == null) return;
// int lastTick = Server.TickCount;
// Server.NextFrame(() =>
// {
// if (heProjectile == null || !heProjectile.IsValid) return;
// if (!(NearlyEquals(angle.X, heProjectile.AbsRotation.X) && NearlyEquals(angle.Y, heProjectile.AbsRotation.Y) && NearlyEquals(angle.Z, heProjectile.AbsRotation.Z)))
// return;
// heProjectile.TicksAtZeroVelocity = 100;
// heProjectile.Damage = SkillsInfo.GetValue<int>(skillName, "explosionDamage");
// heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "explosionRadius");
// heProjectile.DetonateTime = 0;
// if (nades.TryRemove(lastTick, out var source))
// heProjectile.Globalname = $"explosive_chicken_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
// });
// }
// private static void UpdateHUD(PlayerInfo skillInfo)
// {
// if (skillInfo == null) return;
// var player = Utilities.GetPlayerFromIndex((int)skillInfo.OwnerIndex);
// if (player == null || !player.IsValid) return;
// var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
// if (playerInfo == null) return;
// float cooldownDuration = SkillsInfo.GetValue<float>(skillName, "cooldown");
// float cooldown = 0;
// if (!skillInfo.CanUse)
// {
// float readyAtTick = skillInfo.Cooldown + (cooldownDuration * 64);
// float time1 = (readyAtTick - Server.TickCount) / 64;
// cooldown = (int)Math.Ceiling(Math.Max(time1, 0));
// }
// bool chickenActive = skillInfo.ChickenIndex != null;
// if (skillInfo.InfoMessageTime + (64 * 2) > Server.TickCount)
// {
// string? message = skillInfo.InfoMessageType switch
// {
// 1 => player.GetTranslation("chicken_already_active"),
// 2 => player.GetTranslation("chicken_blocked"),
// _ => null
// };
// playerInfo.PrintHTML = message;
// }
// else if (chickenActive)
// playerInfo.PrintHTML = $"{player.GetTranslation("chicken_hud_active", $"<font color='#00FF00'>{skillInfo.ChickenHealth}</font>")}";
// else if (cooldown > 0)
// playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"<font color='#FF0000'>{cooldown}</font>")}";
// else
// playerInfo.PrintHTML = null;
// }
// private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
// public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b5ab8f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Legendary, float cooldown = 30, float triggerRadius = 150.0f, int chickenHealth = 100, int tickCooldown = 16, float boostFactor = 2.5f, float explosionDamage = 100, float explosionRadius = 150.0f, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
// {
// public float Cooldown { get; set; } = cooldown;
// public float TriggerRadius { get; set; } = triggerRadius;
// public int ChickenHealth { get; set; } = chickenHealth;
// public int TickCooldown { get; set; } = tickCooldown;
// public float BoostFactor { get; set; } = boostFactor;
// public float ExplosionDamage { get; set; } = explosionDamage;
// public float ExplosionRadius { get; set; } = explosionRadius;
// public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
// }
// }
//}

View file

@ -10,7 +10,6 @@ namespace src.player.skills
public class FireRain : ISkill
{
private const Skills skillName = Skills.FireRain;
private static readonly QAngle angle = new(10, -4, 13);
private static readonly ConcurrentDictionary<uint, byte> decoys = [];
private static int rainTick = -1;
private static CCSPlayerPawn? rainThrower;
@ -48,40 +47,156 @@ namespace src.player.skills
return;
const int grenadeCount = 5;
const float maxSpawnHeight = 400.0f;
const float clusterRadius = 110.0f;
const float dropSpeed = 250.0f;
const int grenadeGroundCount = 2;
const float spawnHeight = 1500.0f;
const float approachDistance = 600.0f;
rainTick = Server.TickCount;
rainThrower = pawn;
rainTeam = player.TeamNum;
Vector decoyGround = new(targetPos.X, targetPos.Y, targetPos.Z + 8f);
Vector dropVelocity = new(0, 0, -dropSpeed);
float startAngle = Random.Shared.NextSingle() * MathF.Tau;
Vector? skyCenter = null;
bool foundPosition = false;
for (int i = 0; i < 8; i++)
{
float approachAngle = startAngle + i * (MathF.Tau / 8);
float dirX = MathF.Cos(approachAngle);
float dirY = MathF.Sin(approachAngle);
Vector testCenter = new(
targetPos.X + dirX * approachDistance,
targetPos.Y + dirY * approachDistance,
targetPos.Z + spawnHeight
);
var trace = RayTrace.TraceShape(player, targetPos, testCenter, null, 0);
if (!trace.HasValue || !trace.Value.DidHit)
{
skyCenter = testCenter;
foundPosition = true;
break;
}
}
if (!foundPosition || skyCenter == null)
{
CreateMolotovSplash(targetPos, grenadeGroundCount, player.TeamNum);
return;
}
CreateMolotovRaid(targetPos, skyCenter, grenadeCount, player.TeamNum);
}
private static void CreateMolotovSplash(Vector targetPos, int grenadeCount, byte teamNum)
{
const float spreadSpeed = 100.0f;
const float upwardVelocity = 100.0f;
for (int i = 0; i < grenadeCount; i++)
{
float angle = Random.Shared.NextSingle() * MathF.Tau;
float speed = spreadSpeed * (0.7f + Random.Shared.NextSingle() * 0.6f);
float vx = MathF.Cos(angle) * speed;
float vy = MathF.Sin(angle) * speed;
float vz = upwardVelocity + Random.Shared.NextSingle() * 150.0f;
Vector velocity = new(vx, vy, vz);
Vector spawnPos = new(
targetPos.X,
targetPos.Y,
targetPos.Z + 10.0f
);
float yaw = MathF.Atan2(vy, vx) * (180.0f / MathF.PI);
float horizontalSpeed = MathF.Sqrt(vx * vx + vy * vy);
float pitch = -MathF.Atan2(vz, horizontalSpeed) * (180.0f / MathF.PI);
QAngle launchAngle = new(pitch, yaw, 0.0f);
SkillUtils.CreateMolotovProjectile(spawnPos, launchAngle, velocity, teamNum);
}
}
private static void CreateMolotovRaid(Vector targetPos, Vector? skyCenter, int grenadeCount, byte teamNum)
{
if (skyCenter == null)
return;
const float clusterRadius = 130.0f;
const float flightTime = 1.2f;
const float gravity = 800.0f;
const float maxHorizontalVelocity = 700.0f;
Vector adjustedTarget = targetPos;
Vector direction = new(
skyCenter.X - targetPos.X,
skyCenter.Y - targetPos.Y,
0
);
float length = MathF.Sqrt(direction.X * direction.X + direction.Y * direction.Y);
if (length > 0)
{
direction.X /= length;
direction.Y /= length;
const float aimOffset = 150.0f;
adjustedTarget = new Vector(
targetPos.X + direction.X * aimOffset,
targetPos.Y + direction.Y * aimOffset,
targetPos.Z
);
}
float dx = adjustedTarget.X - skyCenter.X;
float dy = adjustedTarget.Y - skyCenter.Y;
float dz = adjustedTarget.Z - skyCenter.Z;
float vx = dx / flightTime;
float vy = dy / flightTime;
float vz = (dz + 0.5f * gravity * flightTime * flightTime) / flightTime;
float horizontalSpeed = MathF.Sqrt(vx * vx + vy * vy);
if (horizontalSpeed > maxHorizontalVelocity)
{
float scale = maxHorizontalVelocity / horizontalSpeed;
vx *= scale;
vy *= scale;
}
Vector velocity = new(vx, vy, vz);
float yaw = MathF.Atan2(vy, vx) * (180.0f / MathF.PI);
float finalHorizontalSpeed = MathF.Sqrt(vx * vx + vy * vy);
float pitch = -MathF.Atan2(vz, finalHorizontalSpeed) * (180.0f / MathF.PI);
QAngle launchAngle = new(pitch, yaw, 0.0f);
for (int i = 0; i < grenadeCount; i++)
{
float offsetAngle = Random.Shared.NextSingle() * MathF.Tau;
float offsetDist = MathF.Sqrt(Random.Shared.NextSingle()) * clusterRadius;
Vector ground = new(
decoyGround.X + MathF.Cos(offsetAngle) * offsetDist,
decoyGround.Y + MathF.Sin(offsetAngle) * offsetDist,
decoyGround.Z
float offsetX = MathF.Cos(offsetAngle) * offsetDist;
float offsetY = MathF.Sin(offsetAngle) * offsetDist;
Vector spawnPos = new(
skyCenter.X + offsetX,
skyCenter.Y + offsetY,
skyCenter.Z
);
var reach = RayTrace.TraceShape(player, decoyGround, ground, null, 0);
if (reach.HasValue && reach.Value.DidHit)
ground = decoyGround;
float height = maxSpawnHeight;
var ceiling = RayTrace.TraceShape(player, ground, new Vector(ground.X, ground.Y, ground.Z + maxSpawnHeight), null, 0);
if (ceiling.HasValue && ceiling.Value.DidHit)
height = Math.Clamp(maxSpawnHeight * ceiling.Value.Fraction - 24f, 24f, maxSpawnHeight);
Vector spawnPos = new(ground.X, ground.Y, ground.Z + height);
SkillUtils.CreateMolotovProjectile(spawnPos, angle, dropVelocity, player.TeamNum);
SkillUtils.CreateMolotovProjectile(spawnPos, launchAngle, velocity, teamNum);
}
}
@ -103,6 +218,14 @@ namespace src.player.skills
molotov.Thrower.Raw = thrower.EntityHandle.Raw;
molotov.OwnerEntity.Raw = thrower.EntityHandle.Raw;
rainMolotovs[molotov.Index] = (thrower.EntityHandle.Raw, rainTeam);
Server.NextWorldUpdate(() =>
{
if (molotov == null || !molotov.IsValid) return;
molotov.DetonateTime += 30f;
Utilities.SetStateChanged(molotov, "CBaseGrenade", "m_flDetonateTime");
});
return;
}

View file

@ -42,7 +42,7 @@ namespace src.player.skills
var skill = SkillData.Skills.FirstOrDefault(s => player.GetSkillName(s.Skill).Equals(commands[0], StringComparison.OrdinalIgnoreCase) || s.Skill.ToString().Equals(commands[0], StringComparison.OrdinalIgnoreCase));
if (skill == null)
{
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("skill_not_found_setskill"));
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslationWithoutIlliterate("skill_not_found_setskill"));
return;
}

View file

@ -20,11 +20,13 @@ namespace src.player.skills
}
private static readonly ConcurrentDictionary<uint, List<ChickenState>> activeChickens = new();
private static readonly ConcurrentDictionary<uint, uint> chickenOwners = new();
private static readonly ConcurrentDictionary<uint, (uint ownerIndex, int health)> chickenOwners = new();
private const string chickenParticle = "particles/critters/chicken/chicken_goop.vpcf";
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
jRandomSkills.Instance.AddToManifest(chickenParticle);
}
public static void NewRound()
@ -39,7 +41,10 @@ namespace src.player.skills
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
if (param == null || param2 == null) return;
if (!chickenOwners.TryGetValue(param.Index, out uint ownerIndex)) return;
if (!chickenOwners.TryGetValue(param.Index, out var ownerData)) return;
uint ownerIndex = ownerData.ownerIndex;
int health = ownerData.health;
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
if (owner == null || !owner.IsValid) return;
@ -50,9 +55,33 @@ namespace src.player.skills
var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle);
if (!attackerPawn.IsValid || attackerPawn.DesignerName != "player") return;
if (attackerPawn.TeamNum != owner.TeamNum) return;
var ownerPawn = owner.PlayerPawn.Value;
if (ownerPawn == null || !ownerPawn.IsValid) return;
param2.Damage = 0;
if (attackerPawn.Index != ownerPawn.Index && attackerPawn.TeamNum == owner.TeamNum)
{
param2.Damage = 0;
return;
}
int newHealth = Math.Max(0, health - (int)param2.Damage);
chickenOwners[param.Index] = (ownerIndex, newHealth);
if (newHealth > 0)
CreateHitParticles(param.Index);
}
private static void CreateHitParticles(uint chickenIndex)
{
var chicken = Utilities.GetEntityFromIndex<CChicken>((int)chickenIndex);
if (chicken == null || !chicken.IsValid || chicken.AbsOrigin == null) return;
var particle = EntityManager.CreateTrackedParticleSystem(chicken.Index, chickenParticle, autoDestroySeconds: 3f);
if (particle == null) return;
Vector pos = new(chicken.AbsOrigin.X, chicken.AbsOrigin.Y, chicken.AbsOrigin.Z + 10);
particle.Teleport(pos);
particle.AcceptInput("Start");
}
public static void EnableSkill(CCSPlayerController player)
@ -79,6 +108,7 @@ namespace src.player.skills
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
int amount = SkillsInfo.GetValue<int>(skillName, "amount");
int health = SkillsInfo.GetValue<int>(skillName, "chickenHealth");
var list = new List<ChickenState>();
for (int i = 0; i < amount; i++)
@ -87,7 +117,10 @@ namespace src.player.skills
if (chicken == null || !chicken.IsValid) continue;
chicken.Render = Color.LightGreen;
chickenOwners[chicken.Index] = player.Index;
chicken.MaxHealth = health;
chicken.Health = health;
chickenOwners[chicken.Index] = (player.Index, health);
Vector offset = new(
(float)(100 * Math.Cos(2 * Math.PI * i / amount)),
@ -109,6 +142,8 @@ namespace src.player.skills
int tickCooldown = SkillsInfo.GetValue<int>(skillName, "tickCooldown");
if (Server.TickCount % tickCooldown == 0) return;
float boostFactor = SkillsInfo.GetValue<float>(skillName, "boostFactor");
var players = PlayerManager.GetTickPlayers().ToArray();
int healAmount = SkillsInfo.GetValue<int>(skillName, "heal");
float healRadius = SkillsInfo.GetValue<float>(skillName, "healRadius");
@ -121,7 +156,7 @@ namespace src.player.skills
if (playerInfo?.Skill != skillName) continue;
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || pawn.Health <= 0) continue;
if (!activeChickens.TryGetValue(player.Index, out var chickens)) continue;
@ -149,7 +184,6 @@ namespace src.player.skills
if (dist2D > 0.05f && dist2D < 20.0f)
{
float boostFactor = 2.5f;
Vector newPos = new(
currentOrigin.X + (dx * boostFactor),
currentOrigin.Y + (dy * boostFactor),
@ -185,12 +219,14 @@ namespace src.player.skills
}
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b5ab8f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Legendary, int amount = 3, int heal = 2, int tickCooldown = 16, float healRadius = 150.0f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b5ab8f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Legendary, int amount = 3, int heal = 2, int tickCooldown = 16, float healRadius = 150.0f, int chickenHealth = 50, float boostFactor = 2.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
public int Amount { get; set; } = amount;
public int Heal { get; set; } = heal;
public int TickCooldown { get; set; } = tickCooldown;
public float HealRadius { get; set; } = healRadius;
public int ChickenHealth { get; set; } = chickenHealth;
public float BoostFactor { get; set; } = boostFactor;
}
}
}

View file

@ -35,6 +35,7 @@ namespace src.player.skills
public static void EnableSkill(CCSPlayerController _)
{
isActive = true;
SendAlertToAll();
}
public static void Enable()
@ -47,6 +48,15 @@ namespace src.player.skills
isActive = false;
}
private static void SendAlertToAll()
{
foreach (var player in PlayerManager.GetTickPlayers())
{
if (!CheckIlliterateSkill(player)) continue;
player.PrintToCenterAlert(player.GetTranslationWithoutIlliterate("illiterate_alert"));
}
}
public static bool CheckIlliterateSkill(CCSPlayerController? player)
{
if (!isActive || player == null || !player.IsValid) return false;

View file

@ -9,7 +9,7 @@ namespace src.player.skills
public class Magnifier : ISkill
{
private const Skills skillName = Skills.Magnifier;
private static readonly ConcurrentDictionary<uint, uint> playersFOV = [];
private static readonly ConcurrentDictionary<uint, byte> playersWithFov = [];
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
public static void LoadSkill()
@ -19,8 +19,9 @@ namespace src.player.skills
public static void PlayerDisconnect(uint playerIndex)
{
playersFOV.TryRemove(playerIndex, out _);
playersWithFov.TryRemove(playerIndex, out _);
playersToTarget.TryRemove(playerIndex, out _);
Event.playersCustomFOV.TryRemove(playerIndex, out _);
foreach (var kvp in playersToTarget)
if (kvp.Value == playerIndex)
@ -29,14 +30,14 @@ namespace src.player.skills
public static void NewRound()
{
foreach (var playerIndex in playersFOV.Keys)
foreach (var playerIndex in playersWithFov.Keys)
{
var player = Utilities.GetPlayerFromIndex((int)playerIndex);
if (player == null || !player.IsValid) continue;
DisableSkill(player);
}
playersFOV.Clear();
playersWithFov.Clear();
playersToTarget.Clear();
foreach (var player in PlayerManager.GetTickPlayers())
@ -91,13 +92,21 @@ namespace src.player.skills
return;
}
playersFOV.AddOrUpdate(enemy.Index, enemy.DesiredFOV, (k, v) => enemy.DesiredFOV);
uint magnifierFov = SkillsInfo.GetValue<uint>(skillName, "customFOV");
playersWithFov.TryAdd(enemy.Index, 0);
uint enemyFOV = enemy.DesiredFOV;
if (enemyFOV == 0)
Event.playersCustomFOV.TryRemove(enemy.Index, out _);
else if (enemyFOV != magnifierFov)
Event.playersCustomFOV.AddOrUpdate(enemy.Index, enemy.DesiredFOV, (k, v) => enemy.DesiredFOV);
playersToTarget[player.Index] = enemy.Index;
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
if (enemyEvent == null || !enemyEvent.IsValid) return;
enemyEvent.DesiredFOV = SkillsInfo.GetValue<uint>(skillName, "customFOV");
enemyEvent.DesiredFOV = magnifierFov;
Utilities.SetStateChanged(enemyEvent, "CBasePlayerController", "m_iDesiredFOV");
playerInfo.SkillUsed = true;
@ -113,9 +122,15 @@ namespace src.player.skills
var player = @event.Userid;
if (player == null || !player.IsValid) return;
if (!playersFOV.ContainsKey(bot.Index)) return;
if (!playersWithFov.ContainsKey(bot.Index)) return;
player.DesiredFOV = SkillsInfo.GetValue<uint>(skillName, "customFOV");
uint magnifierFov = SkillsInfo.GetValue<uint>(skillName, "customFOV");
uint playerFOV = player.DesiredFOV;
if (playerFOV != 0 && playerFOV != magnifierFov)
Event.playersCustomFOV.AddOrUpdate(player.Index, player.DesiredFOV, (k, v) => player.DesiredFOV);
player.DesiredFOV = magnifierFov;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
}
@ -147,16 +162,22 @@ namespace src.player.skills
var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex));
if (target != null && target.IsValid)
{
if (playersFOV.TryGetValue(targetIndex, out uint fov))
if (target.DesiredFOV == SkillsInfo.GetValue<uint>(skillName, "customFOV"))
{
target.DesiredFOV = fov;
target.DesiredFOV = Event.playersCustomFOV.TryGetValue(target.Index, out uint fov) ? fov : 0;
Utilities.SetStateChanged(target, "CBasePlayerController", "m_iDesiredFOV");
}
if (target.PawnIsAlive && !SkillUtils.IsFreezeTime())
target.PrintToChat($" {ChatColors.Green}" + target.GetTranslation("magnifier_disable_info"));
}
playersFOV.TryRemove(targetIndex, out _);
playersWithFov.TryRemove(targetIndex, out _);
}
if (player.DesiredFOV != 0)
{
player.DesiredFOV = Event.playersCustomFOV.TryGetValue(player.Index, out uint fov) ? fov : 0;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
}
SkillUtils.ResetPrintHTML(player);
@ -167,8 +188,12 @@ namespace src.player.skills
var player = @event.Userid;
if (player == null || !player.IsValid) return;
player.DesiredFOV = 0;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
if (player.DesiredFOV == SkillsInfo.GetValue<uint>(skillName, "customFOV"))
{
player.DesiredFOV = Event.playersCustomFOV.TryGetValue(player.Index, out uint fov) ? fov : 0;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
}
SkillUtils.ResetPrintHTML(player);
}

View file

@ -46,9 +46,9 @@ namespace src.player.skills
Instance.GameRules.RoundTime += player.Team == CsTeam.Terrorist ? roundTime : -roundTime;
if (player.Team == CsTeam.Terrorist)
Localization.PrintTranslationToChatAll($" {ChatColors.Orange}{{0}}", ["watchmaker_tt"], [roundTime]);
Localization.PrintTranslationToChatAll($" {ChatColors.Orange}{{0}}", ["watchmaker_tt"], true, [roundTime]);
else
Localization.PrintTranslationToChatAll($" {ChatColors.LightBlue}{{0}}", ["watchmaker_ct"], [roundTime]);
Localization.PrintTranslationToChatAll($" {ChatColors.LightBlue}{{0}}", ["watchmaker_ct"], true, [roundTime]);
player.EmitSound(SkillsInfo.GetValue<string>(skillName, "SoundEvent"));

View file

@ -25,14 +25,14 @@ namespace src.utils
if (targetresult.Players.Count == 0)
{
if (!ignoreMessage && command.CallingPlayer != null)
command.ReplyToCommand(command.CallingPlayer.GetTranslation("no_player"));
command.ReplyToCommand(command.CallingPlayer.GetTranslationWithoutIlliterate("no_player"));
return (new List<CCSPlayerController>(), string.Empty);
}
else if (singletarget && targetresult.Players.Count > 1)
{
if (command.CallingPlayer != null)
command.ReplyToCommand(command.CallingPlayer.GetTranslation("duplicate_player"));
command.ReplyToCommand(command.CallingPlayer.GetTranslationWithoutIlliterate("duplicate_player"));
return (new List<CCSPlayerController>(), string.Empty);
}

View file

@ -132,7 +132,7 @@ namespace src.utils
}
var value = Math.Round((double)(chance ?? 1), 2);
var skillNameText = GetTranslation(SkillKey(skill), langCode, player, value);
var skillNameText = GetTranslation(SkillKey(skill), langCode, player, true, value);
if (skillNameText.Contains('%')) skillNameText = skillNameText.Replace(value.ToString(), Math.Round(value * 100, 0).ToString());
return skillNameText;
}
@ -173,7 +173,7 @@ namespace src.utils
var skillName = SkillDesc2Key(skill);
var value = Math.Round((double)(chance ?? 1), 2);
var desc2 = GetTranslation(skillName, langCode, player, value);
var desc2 = GetTranslation(skillName, langCode, player, true, value);
var skilLDescription = desc2 == skillName
? player.GetTranslation(SkillDescKey(skill))
@ -181,7 +181,7 @@ namespace src.utils
return skilLDescription;
}
public static void PrintTranslationToChatAll(string message, string[]? key, params object[][]? args)
public static void PrintTranslationToChatAll(string message, string[]? key, bool useIlliterate = true, params object[][]? args)
{
foreach (var player in Utilities.GetPlayers().Where(p => !p.IsBot))
{
@ -196,7 +196,7 @@ namespace src.utils
for (int i = 0; i < key.Length; i++)
{
object[] currentArgs = args != null && i < args.Length ? args[i] : [];
string translation = GetTranslation(key[i], langCode, null, currentArgs);
string translation = GetTranslation(key[i], langCode, null, useIlliterate, currentArgs);
translations.Add(translation);
}
player.PrintToChat(string.Format(message, [.. translations]));
@ -206,10 +206,21 @@ namespace src.utils
public static string GetTranslation(this CCSPlayerController player, string key, params object[] args)
{
string langCode = GetLangCode(player);
return GetTranslation(key, langCode, player, args);
return GetTranslation(key, langCode, player, true, args);
}
public static string GetTranslation(string key, string? langCode = null, CCSPlayerController? player = null, params object[] args)
public static string GetTranslationWithoutIlliterate(this CCSPlayerController player, string key, params object[] args)
{
string langCode = GetLangCode(player);
return GetTranslation(key, langCode, player, false, args);
}
public static string GetTranslationWithoutIlliterate(string key)
{
return GetTranslation(key, null, null, false);
}
public static string GetTranslation(string key, string? langCode = null, CCSPlayerController? player = null, bool useIlliterate = true, params object[] args)
{
langCode ??= defaultLangCode;
if (_translations.TryGetValue(langCode, out var langDict) && langDict.TryGetValue(key, out var translation))
@ -219,13 +230,13 @@ namespace src.utils
{
string output = args.Length == 0 ? translation : string.Format(translation, args);
if (Illiterate.CheckIlliterateSkill(player))
if (useIlliterate && Illiterate.CheckIlliterateSkill(player))
return Illiterate.GetRandomText(output)!;
return output;
}
if (langCode != defaultLangCode)
return GetTranslation(key, null, player, args);
return GetTranslation(key, null, player, useIlliterate, args);
return key;
}

View file

@ -878,7 +878,7 @@ namespace src.utils
if (manager == null) return;
var config = Config.LoadedConfig.HtmlHudCustomisation;
var your_skill = player.GetTranslation("your_skill");
var your_skill = player.GetTranslationWithoutIlliterate("your_skill");
var emptySymbol = $"<font class='fontSize-{(string.IsNullOrEmpty(your_skill) ? "l" : "ml")}'> </font>";
string infoLine = string.IsNullOrEmpty(your_skill)
@ -897,9 +897,9 @@ namespace src.utils
var hudContent = infoLine + skillLine + remainingLine;
string controllsLine =
$"{emptySymbol}<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor1}'>{player.GetTranslation($"menu_controlls_scroll")}</font>"
+ $"<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor2}'>{player.GetTranslation($"menu_controlls_padding")}</font>"
+ $"<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor3}'>{player.GetTranslation($"menu_controlls_select")}</font>{emptySymbol}";
$"{emptySymbol}<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor1}'>{player.GetTranslationWithoutIlliterate($"menu_controlls_scroll")}</font>"
+ $"<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor2}'>{player.GetTranslationWithoutIlliterate($"menu_controlls_padding")}</font>"
+ $"<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor3}'>{player.GetTranslationWithoutIlliterate($"menu_controlls_select")}</font>{emptySymbol}";
string itemText = $"<font class='fontSize-{config.WSADMenuItemLineSize}' color='{config.WSADMenuItemLineColor}'>{{0}}</font><br>";
string itemHoverText = $"<font class='fontSize-{config.WSADMenuItemLineSize}'><font color='purple'>[ </font><font color='{config.WSADMenuItemHoverLineColor}'>{{0}}</font><font color='purple'> ]</font></font><br>";