Merge pull request #47 from ByDexterTR/main

v1.2.3.b4
This commit is contained in:
Juzlus 2026-08-03 16:37:29 +02:00 • committed by GitHub
commit cc32a11404
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 449 additions and 208 deletions

View file

@ -31,7 +31,7 @@ namespace src
public override string ModuleName => "[CS2] [ jRandomSkills ]"; public override string ModuleName => "[CS2] [ jRandomSkills ]";
public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)"; public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)";
public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus."; public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus.";
public override string ModuleVersion => "1.2.3.b3"; public override string ModuleVersion => "1.2.3.b4";
public override void Load(bool hotReload) public override void Load(bool hotReload)
{ {
@ -125,7 +125,7 @@ namespace src
SkillsUsedThisMap.TryAdd(skill, 0); SkillsUsedThisMap.TryAdd(skill, 0);
} }
if (SkillUtils.CurseLimitEnabled && SkillUtils.IsCurseSkill(skill)) if (SkillUtils.IsCurseSkill(skill))
{ {
if (methodName == "DisableSkill" && param?.Length > 0 && param[0] is CCSPlayerController curser && curser.IsValid) if (methodName == "DisableSkill" && param?.Length > 0 && param[0] is CCSPlayerController curser && curser.IsValid)
SkillUtils.ReleaseCurse(curser.Index); SkillUtils.ReleaseCurse(curser.Index);

View file

@ -1,4 +1,4 @@
{ {
"none": "Keine", "none": "Keine",
"none_desc": "Du hast keine Fertigkeit", "none_desc": "Du hast keine Fertigkeit",
@ -276,6 +276,7 @@
"jetkick": "Jetkick", "jetkick": "Jetkick",
"jetkick_desc": "Wähle einen Spieler für einen Jetkick", "jetkick_desc": "Wähle einen Spieler für einen Jetkick",
"jetkick_select_info": "Wähle einen Spieler für den Jetkick:",
"jetkick_player_info": "Spieler '{0}' wurde wegge-kickt.", "jetkick_player_info": "Spieler '{0}' wurde wegge-kickt.",
"jetkick_enemy_info": "Jeder Schuss stößt dich nach hinten", "jetkick_enemy_info": "Jeder Schuss stößt dich nach hinten",
"jetkick_disable_info": "Dein Rückstoß ist wieder normal.", "jetkick_disable_info": "Dein Rückstoß ist wieder normal.",

View file

@ -1,4 +1,4 @@
{ {
"none": "None", "none": "None",
"none_desc": "You have no skill", "none_desc": "You have no skill",
@ -269,6 +269,7 @@
"jetkick": "Jetkick", "jetkick": "Jetkick",
"jetkick_desc": "Select a player to jetkick", "jetkick_desc": "Select a player to jetkick",
"jetkick_select_info": "Select a player to jetkick:",
"jetkick_player_info": "Player '{0}' has been jetkicked.", "jetkick_player_info": "Player '{0}' has been jetkicked.",
"jetkick_enemy_info": "Every shot knocks you backwards", "jetkick_enemy_info": "Every shot knocks you backwards",
"jetkick_disable_info": "Your recoil is back to normal.", "jetkick_disable_info": "Your recoil is back to normal.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Aucune", "none": "Aucune",
"none_desc": "Vous n’avez aucune compétence", "none_desc": "Vous n’avez aucune compétence",
@ -276,6 +276,7 @@
"jetkick": "Coup de pied propulsé", "jetkick": "Coup de pied propulsé",
"jetkick_desc": "Sélectionnez un joueur pour lui donner un coup de pied propulsé", "jetkick_desc": "Sélectionnez un joueur pour lui donner un coup de pied propulsé",
"jetkick_select_info": "Sélectionnez un joueur à propulser :",
"jetkick_player_info": "Le joueur '{0}' a reçu un coup de pied propulsé.", "jetkick_player_info": "Le joueur '{0}' a reçu un coup de pied propulsé.",
"jetkick_enemy_info": "Chaque tir vous repousse vers l'arrière", "jetkick_enemy_info": "Chaque tir vous repousse vers l'arrière",
"jetkick_disable_info": "Votre recul est revenu à la normale.", "jetkick_disable_info": "Votre recul est revenu à la normale.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Brak", "none": "Brak",
"none_desc": "Nie posiadasz żadnej supermocy", "none_desc": "Nie posiadasz żadnej supermocy",
@ -276,6 +276,7 @@
"jetkick": "Odrzutowy Kop", "jetkick": "Odrzutowy Kop",
"jetkick_desc": "Wybierz gracza, aby użyć na nim odrzutowego kopa", "jetkick_desc": "Wybierz gracza, aby użyć na nim odrzutowego kopa",
"jetkick_select_info": "Wybierz gracza do odrzutowego kopa:",
"jetkick_player_info": "Gracz '{0}' został odrzucony kopem.", "jetkick_player_info": "Gracz '{0}' został odrzucony kopem.",
"jetkick_enemy_info": "Każdy strzał mocno odrzuca Cię do tyłu", "jetkick_enemy_info": "Każdy strzał mocno odrzuca Cię do tyłu",
"jetkick_disable_info": "Twój odrzut wrócił do normy.", "jetkick_disable_info": "Twój odrzut wrócił do normy.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Nenhuma", "none": "Nenhuma",
"none_desc": "Você não tem nenhuma habilidade", "none_desc": "Você não tem nenhuma habilidade",
@ -276,6 +276,7 @@
"jetkick": "Chute a Jato", "jetkick": "Chute a Jato",
"jetkick_desc": "Selecione um jogador para aplicar um chute a jato", "jetkick_desc": "Selecione um jogador para aplicar um chute a jato",
"jetkick_select_info": "Selecione um jogador para o chute a jato:",
"jetkick_player_info": "O jogador '{0}' recebeu um chute a jato.", "jetkick_player_info": "O jogador '{0}' recebeu um chute a jato.",
"jetkick_enemy_info": "Cada tiro empurra você para trás", "jetkick_enemy_info": "Cada tiro empurra você para trás",
"jetkick_disable_info": "Seu recuo voltou ao normal.", "jetkick_disable_info": "Seu recuo voltou ao normal.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Нет навыка", "none": "Нет навыка",
"none_desc": "У вас нет навыка", "none_desc": "У вас нет навыка",
@ -276,6 +276,7 @@
"jetkick": "Реактивный удар", "jetkick": "Реактивный удар",
"jetkick_desc": "Выберите игрока для реактивного удара", "jetkick_desc": "Выберите игрока для реактивного удара",
"jetkick_select_info": "Выберите игрока для реактивного удара:",
"jetkick_player_info": "Игрок '{0}' получил реактивный удар.", "jetkick_player_info": "Игрок '{0}' получил реактивный удар.",
"jetkick_enemy_info": "Каждый выстрел отбрасывает вас назад", "jetkick_enemy_info": "Каждый выстрел отбрасывает вас назад",
"jetkick_disable_info": "Ваша отдача вернулась к норме.", "jetkick_disable_info": "Ваша отдача вернулась к норме.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Yok", "none": "Yok",
"none_desc": "Hiçbir yeteneğin yok", "none_desc": "Hiçbir yeteneğin yok",
@ -52,13 +52,13 @@
"behind_desc2": "Baş döndürme şansın: %{0}", "behind_desc2": "Baş döndürme şansın: %{0}",
"berserker": "Berserker", "berserker": "Berserker",
"berserker_desc": "Canın azaldıkça daha fazla hasar verir ve daha hızlı hareket edersin", "berserker_desc": "Canın azaldıkça hasarın artar ve daha hızlanırsın",
"blademaster": "Kılıç Ustası", "blademaster": "Kılıç Ustası",
"blademaster_desc": "Bıçak tutarken mermileri sektirme şansın artar", "blademaster_desc": "Bıçak tutarken mermileri sektirme şansın artar",
"blastshot": "Patlama Atışı", "blastshot": "Patlama Atışı",
"blastshot_desc": "MP5 ile Attack2 tuşuna basarak bir HE bombası ateşle", "blastshot_desc": "MP5 ile Sağ Tık'a basarak El bombası fırlat",
"bunnyhop": "Tavşan", "bunnyhop": "Tavşan",
"bunnyhop_desc": "Otomatik \"BunnyHop\" yaparsın", "bunnyhop_desc": "Otomatik \"BunnyHop\" yaparsın",
@ -156,14 +156,14 @@
"fastreload_desc": "[css_useSkill] tuşuna basarak şarjörünü hızlıca değiştir", "fastreload_desc": "[css_useSkill] tuşuna basarak şarjörünü hızlıca değiştir",
"firerain": "Ateş Yağmuru", "firerain": "Ateş Yağmuru",
"firerain_desc": "Molotof yağmuru çağırmak için bir yem at", "firerain_desc": "Molotof yağdırmak için bir decoy at",
"flash": "Flash", "flash": "Flash",
"flash_desc": "Raund başında rastgele oyuncu hızı", "flash_desc": "Raund başında rastgele oyuncu hızı",
"flash_desc2": "Hızın: {0}x", "flash_desc2": "Hızın: {0}x",
"flashlight": "El Feneri", "flashlight": "El Feneri",
"flashlight_desc": "El fenerini açıp kapatmak için [css_useSkill] tuşuna tıkla. Işığı düşmanları kör edebilir", "flashlight_desc": "[css_useSkill] tuşuna basarak El fenerini açarsın/kaparsın (Işığı düşmanları kör edebilir)",
"fortnite": "Fortnite", "fortnite": "Fortnite",
"fortnite_desc": "[css_useSkill] tuşuna basarak barikat kur", "fortnite_desc": "[css_useSkill] tuşuna basarak barikat kur",
@ -223,7 +223,7 @@
"healingsmoke_desc": "Sis bombaların iyileştirir", "healingsmoke_desc": "Sis bombaların iyileştirir",
"hermit": "Münzevi", "hermit": "Münzevi",
"hermit_desc": "Öldürdüğünde mermi ve canın yenilenir", "hermit_desc": "Birini öldürünce mermin ve canın yenilenir",
"holyhandgrenade": "Kutsal El Bombası", "holyhandgrenade": "Kutsal El Bombası",
"holyhandgrenade_desc": "El bomban iki kat hasara ve menzile sahip", "holyhandgrenade_desc": "El bomban iki kat hasara ve menzile sahip",
@ -267,9 +267,10 @@
"jester_on": "AÇIK", "jester_on": "AÇIK",
"jester_off": "KAPALI", "jester_off": "KAPALI",
"jetkick": "Jet Tekmesi", "jetkick": "Jetkick",
"jetkick_desc": "Jet tekmesi yapmak için bir oyuncu seç", "jetkick_desc": "Seçtiğin bir rakibin her atışı onu geriye iter",
"jetkick_player_info": "'{0}' adlı oyuncuya jet tekmesi yapıldı.", "jetkick_select_info": "Jetkick uygulanacak oyuncu:",
"jetkick_player_info": "'{0}' adlı oyuncuya jetkick uygulandı.",
"jetkick_enemy_info": "Her atış seni geriye doğru iter", "jetkick_enemy_info": "Her atış seni geriye doğru iter",
"jetkick_disable_info": "Geri tepmen normale döndü.", "jetkick_disable_info": "Geri tepmen normale döndü.",
@ -297,7 +298,7 @@
"knockback_desc": "Havadayken ateş etmek seni geriye savurur", "knockback_desc": "Havadayken ateş etmek seni geriye savurur",
"lastgasp": "Son Nefes", "lastgasp": "Son Nefes",
"lastgasp_desc": "Öldükten sonra seni öldüren düşmana hasar verirsin", "lastgasp_desc": "Öldükten sonra seni öldürene hasar verirsin",
"lifeswap": "Can Takası", "lifeswap": "Can Takası",
"lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç", "lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç",
@ -340,7 +341,7 @@
"deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün", "deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün",
"deathbomb_explosion": "BOOM!!!", "deathbomb_explosion": "BOOM!!!",
"demoneye": "Şeytan Gözü", "demoneye": "Şeytani Göz",
"demoneye_desc": "Baktığın tüm düşmanlara hasar verirsin", "demoneye_desc": "Baktığın tüm düşmanlara hasar verirsin",
"nightmare": "Kabus", "nightmare": "Kabus",
@ -489,14 +490,14 @@
"swapposition": "Yer Değiştirme", "swapposition": "Yer Değiştirme",
"swapposition_desc": "[css_useSkill] tuşuna basarak vurduğun düşmanla yer değiştir", "swapposition_desc": "[css_useSkill] tuşuna basarak vurduğun düşmanla yer değiştir",
"takeammo": "Mühimmat Al", "takeammo": "Cephane Hırsızı",
"takeammo_desc": "Rastgele bir düşmanın aktif silahının şarjörünü almak için [css_useSkill] tuşuna tıkla", "takeammo_desc": "[css_useSkill] tuşuna basarak rastgele bir düşmanın silahının şarjörünü çalarsın",
"takeammo_hud_info1": "Aktif şarjörü olan bir oyuncu bulunamadı.", "takeammo_hud_info1": "Cephanesi olan bir oyuncu bulunamadı.",
"takeammo_hud_info2": "Düşmanın şarjörü yok.", "takeammo_hud_info2": "Düşmanın cephanesi yok.",
"takeammo_enemy_info": "Bir miktar mühimmat kaybettin.", "takeammo_enemy_info": "Birisi cephaneni çaldı.",
"teamteleport": "Takıma Işınlanma", "teamteleport": "Takıma Işınlanma",
"teamteleport_desc": "Baktığın takım arkadaşına ışınlanmak için [css_useSkill] tuşuna bas", "teamteleport_desc": "[css_useSkill] tuşuna basarak baktığın takım arkadaşına ışınlanırsın",
"teamteleport_noenemy": "Takım arkadaşı bulunamadı", "teamteleport_noenemy": "Takım arkadaşı bulunamadı",
"teamteleport_hud_info": "Bulundu: {0}", "teamteleport_hud_info": "Bulundu: {0}",

View file

@ -206,7 +206,7 @@ namespace src.player
Server.PrintToConsole($"[jRandomSkills] CheckTransmit dying-filter failed: {ex.Message}"); Server.PrintToConsole($"[jRandomSkills] CheckTransmit dying-filter failed: {ex.Message}");
} }
DispatchToActiveSkills("CheckTransmit", infoList); DispatchCheckTransmit([infoList]);
} }
PerfLog.Sample("CheckTransmit", perfStart); PerfLog.Sample("CheckTransmit", perfStart);
} }

View file

@ -136,6 +136,39 @@ namespace src.player
} }
} }
private static void ReleaseCursesTargeting(uint victimIndex)
{
foreach (uint curserIndex in SkillUtils.GetCursersOf(victimIndex))
{
if (curserIndex == victimIndex) continue;
var curser = Utilities.GetPlayerFromIndex((int)curserIndex);
if (curser == null || !curser.IsValid) continue;
if (PlayerManager.GetPlayerByIndex(curserIndex)?.Skill is not Skills curserSkill || curserSkill == Skills.None)
continue;
try
{
Instance.SkillAction(curserSkill.ToString(), "DisableSkill", [curser]);
}
catch (Exception ex)
{
Server.PrintToConsole($"[jRandomSkills] {curserSkill}.DisableSkill failed while releasing curse on leaving player: {(ex.InnerException ?? ex).Message}");
}
}
}
private static void DispatchCheckTransmit(object[] args)
{
var seen = new HashSet<Skills>();
foreach (var p in Instance.SkillPlayer)
{
if (!seen.Add(p.Skill)) continue;
InvokeSkill(p.Skill, "CheckTransmit", args);
}
}
private static void DispatchOnTakeDamage(DynamicHook h, bool post = false) private static void DispatchOnTakeDamage(DynamicHook h, bool post = false)
{ {
object[] args = [h]; object[] args = [h];
@ -517,6 +550,9 @@ namespace src.player
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]); Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]);
uint leavingIndex = player.Index; uint leavingIndex = player.Index;
ReleaseCursesTargeting(leavingIndex);
foreach (var skill in SkillData.Skills) foreach (var skill in SkillData.Skills)
Instance.SkillAction(skill.Skill.ToString(), "PlayerDisconnect", [leavingIndex]); Instance.SkillAction(skill.Skill.ToString(), "PlayerDisconnect", [leavingIndex]);

View file

@ -152,12 +152,24 @@ namespace src.player
// nobody drew now would otherwise never clear state left over from an earlier round. // nobody drew now would otherwise never clear state left over from an earlier round.
// Skills that never ran cannot hold state, so they stay out of the sweep. // Skills that never ran cannot hold state, so they stay out of the sweep.
foreach (var skillName in SkillsUsedThisMap.Keys) foreach (var skillName in SkillsUsedThisMap.Keys)
Instance.SkillAction(skillName, "NewRound"); SafeNewRound(skillName);
ActiveSkillsThisRound.Clear(); ActiveSkillsThisRound.Clear();
tickFailuresLogged.Clear(); tickFailuresLogged.Clear();
} }
} }
private static void SafeNewRound(string skillName)
{
try
{
Instance.SkillAction(skillName, "NewRound");
}
catch (Exception ex)
{
Server.PrintToConsole($"[jRandomSkills] {skillName}.NewRound failed, cleanup continues: {(ex.InnerException ?? ex).Message}");
}
}
public static void OnMapChange() public static void OnMapChange()
{ {
lock (setLock) lock (setLock)
@ -168,10 +180,16 @@ namespace src.player
Fortnite.skillInThisRound = false; Fortnite.skillInThisRound = false;
EntityManager.SuppressKills = true; EntityManager.SuppressKills = true;
try
{
EntityManager.DestroyAllTracked(); EntityManager.DestroyAllTracked();
foreach (var skill in SkillData.Skills) foreach (var skill in SkillData.Skills)
Instance.SkillAction(skill.Skill.ToString(), "NewRound"); SafeNewRound(skill.Skill.ToString());
}
finally
{
EntityManager.SuppressKills = false; EntityManager.SuppressKills = false;
}
ActiveSkillsThisRound.Clear(); ActiveSkillsThisRound.Clear();
SkillsUsedThisMap.Clear(); SkillsUsedThisMap.Clear();

View file

@ -135,7 +135,7 @@ namespace src.player.skills
heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "explosionRadius"); heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "explosionRadius");
if (nades.TryRemove(lastTick, out var source)) if (nades.TryRemove(lastTick, out var source))
heProjectile.Globalname = $"deathbomb_team_{source.Team}_{source.Owner}_{heProjectile.Index}"; heProjectile.Globalname = $"blastshot_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
}); });
} }
@ -150,7 +150,7 @@ namespace src.player.skills
if (nade == null || !nade.IsValid) return; if (nade == null || !nade.IsValid) return;
if (nade.DesignerName != "hegrenade_projectile") return; if (nade.DesignerName != "hegrenade_projectile") return;
if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("deathbomb_team_")) return; if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("blastshot_team_")) return;
var parts = nade.Globalname.Split('_'); var parts = nade.Globalname.Split('_');
if (parts.Length < 4) return; if (parts.Length < 4) return;
@ -169,33 +169,12 @@ namespace src.player.skills
if (owner != null && !owner.IsValid) owner = null; if (owner != null && !owner.IsValid) owner = null;
if (victimPawn.TeamNum == nadeTeam) if (victimPawn.TeamNum == nadeTeam)
{ param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
float reduction = SkillsInfo.GetValue<float>(skillName, "dmgReductionForTeamates");
param2.Damage *= 1f - Math.Clamp(reduction, 0f, 1f);
if (IsFriendlyFireOff())
{
int teamDamage = (int)param2.Damage;
param2.Damage = 0;
if (teamDamage > 0)
SkillUtils.TakeHealth(victimPawn, teamDamage, owner, KillfeedIcons.Explosion);
return;
}
}
if (owner != null && param2.Damage >= victimPawn.Health) if (owner != null && param2.Damage >= victimPawn.Health)
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
} }
private static bool IsFriendlyFireOff()
{
bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue<bool>() ?? false;
bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue<bool>() ?? false;
return !ff && !tae;
}
private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon; private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
public class PlayerSkillInfo public class PlayerSkillInfo

View file

@ -234,6 +234,29 @@ namespace src.player.skills
SkillUtils.CloseMenu(player); SkillUtils.CloseMenu(player);
} }
public static void PlayerDisconnect(uint playerIndex)
{
lock (setLock)
{
Forget(playerIndex);
if (playersToTarget.TryRemove(playerIndex, out uint targetIndex))
Forget(targetIndex);
foreach (var kvp in playersToTarget)
if (kvp.Value == playerIndex)
playersToTarget.TryRemove(kvp.Key, out _);
}
}
private static void Forget(uint index)
{
targetPlayers.TryRemove(index, out _);
lastShot.TryRemove(index, out _);
hitPlayer.TryRemove(index, out _);
targetToPlayer.TryRemove(index, out _);
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#db6c35", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int damageAfterMiss = 5) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#db6c35", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int damageAfterMiss = 5) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{ {
public int DamageAfterMiss { get; set; } = damageAfterMiss; public int DamageAfterMiss { get; set; } = damageAfterMiss;

View file

@ -13,12 +13,12 @@ namespace src.player.skills
private static readonly HashSet<string> disabledWeapons = private static readonly HashSet<string> disabledWeapons =
[ [
"weapon_ak47", "weapon_m4a4", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", "weapon_m4a4", "weapon_m4a1", "weapon_m4a1_silencer",
"weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg553", "weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg556",
"weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7",
"weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08", "weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08",
"weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova", "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova",
"weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249",
"weapon_negev", "weapon_sg556" "weapon_negev"
]; ];
private const string chickenModelPath = "models/chicken/chicken.vmdl"; private const string chickenModelPath = "models/chicken/chicken.vmdl";
private static readonly ConcurrentDictionary<uint, uint> chickens = []; private static readonly ConcurrentDictionary<uint, uint> chickens = [];
@ -139,7 +139,8 @@ namespace src.player.skills
chickenModel.CBodyComponent.SceneNode.GetSkeletonInstance().Scale = 1; chickenModel.CBodyComponent.SceneNode.GetSkeletonInstance().Scale = 1;
Utilities.SetStateChanged(chickenModel, "CBaseEntity", "m_CBodyComponent"); Utilities.SetStateChanged(chickenModel, "CBaseEntity", "m_CBodyComponent");
Server.NextFrame(() => { Server.NextFrame(() =>
{
if (chickenModel == null || !chickenModel.IsValid) return; if (chickenModel == null || !chickenModel.IsValid) return;
chickenModel.AcceptInput("SetScale", chickenModel, chickenModel, "1"); chickenModel.AcceptInput("SetScale", chickenModel, chickenModel, "1");
}); });

View file

@ -123,33 +123,12 @@ namespace src.player.skills
if (owner != null && !owner.IsValid) owner = null; if (owner != null && !owner.IsValid) owner = null;
if (victimPawn.TeamNum == nadeTeam) if (victimPawn.TeamNum == nadeTeam)
{ param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
float reduction = SkillsInfo.GetValue<float>(skillName, "dmgReductionForTeamates");
param2.Damage *= 1f - Math.Clamp(reduction, 0f, 1f);
if (IsFriendlyFireOff())
{
int teamDamage = (int)param2.Damage;
param2.Damage = 0;
if (teamDamage > 0)
SkillUtils.TakeHealth(victimPawn, teamDamage, owner, KillfeedIcons.Explosion);
return;
}
}
if (owner != null && param2.Damage >= victimPawn.Health) if (owner != null && param2.Damage >= victimPawn.Health)
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
} }
private static bool IsFriendlyFireOff()
{
bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue<bool>() ?? false;
bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue<bool>() ?? false;
return !ff && !tae;
}
private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon; private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
private static bool IsDeadPlayerValid(CCSPlayerController? player) private static bool IsDeadPlayerValid(CCSPlayerController? player)

View file

@ -229,33 +229,12 @@ namespace src.player.skills
if (owner != null && !owner.IsValid) owner = null; if (owner != null && !owner.IsValid) owner = null;
if (victimPawn.TeamNum == nadeTeam) if (victimPawn.TeamNum == nadeTeam)
{ param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
float reduction = SkillsInfo.GetValue<float>(skillName, "dmgReductionForTeamates");
param2.Damage *= 1f - Math.Clamp(reduction, 0f, 1f);
if (IsFriendlyFireOff())
{
int teamDamage = (int)param2.Damage;
param2.Damage = 0;
if (teamDamage > 0)
SkillUtils.TakeHealth(victimPawn, teamDamage, owner, KillfeedIcons.Explosion);
return;
}
}
if (owner != null && param2.Damage >= victimPawn.Health) if (owner != null && param2.Damage >= victimPawn.Health)
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
} }
private static bool IsFriendlyFireOff()
{
bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue<bool>() ?? false;
bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue<bool>() ?? false;
return !ff && !tae;
}
private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon; private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
public class PlayerSkillInfo public class PlayerSkillInfo

View file

@ -1,7 +1,10 @@
using CounterStrikeSharp.API; using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Utils;
using jRandomSkills.src.utils;
using src.utils; using src.utils;
using System.Collections.Concurrent;
using static src.jRandomSkills; using static src.jRandomSkills;
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
@ -13,6 +16,13 @@ namespace src.player.skills
private static readonly QAngle angle = new(5, 10, -4); private static readonly QAngle angle = new(5, 10, -4);
private static int lastTick = 0; private static int lastTick = 0;
private static byte pendingTeam = (byte)CsTeam.None;
private static readonly ConcurrentDictionary<int, (byte Team, uint Owner)> nades = [];
public static void NewRound()
{
nades.Clear();
}
public static void LoadSkill() public static void LoadSkill()
{ {
@ -31,10 +41,12 @@ namespace src.player.skills
border: !PlayerManager.GetTickPlayers().Any(p => p.Team == player.Team && p != player) ? "tb" : "t"); border: !PlayerManager.GetTickPlayers().Any(p => p.Team == player.Team && p != player) ? "tb" : "t");
} }
private static void SpawnExplosion(Vector vector) private static void SpawnExplosion(Vector vector, CCSPlayerController player)
{ {
lastTick = Server.TickCount; lastTick = Server.TickCount;
SkillUtils.CreateHEGrenadeProjectile(vector, angle, new Vector(0, 0, 0), 0); pendingTeam = player.TeamNum;
nades.AddOrUpdate(Server.TickCount, (player.TeamNum, player.Index), (_, _) => (player.TeamNum, player.Index));
SkillUtils.CreateHEGrenadeProjectile(vector, angle, new Vector(0, 0, 0), player.TeamNum);
} }
public static void OnEntitySpawned(CEntityInstance entity) public static void OnEntitySpawned(CEntityInstance entity)
@ -44,6 +56,8 @@ namespace src.player.skills
var heProjectile = entity.As<CBaseCSGrenadeProjectile>(); var heProjectile = entity.As<CBaseCSGrenadeProjectile>();
if (heProjectile == null || !heProjectile.IsValid || heProjectile.AbsRotation == null) return; if (heProjectile == null || !heProjectile.IsValid || heProjectile.AbsRotation == null) return;
int spawnTick = Server.TickCount;
Server.NextFrame(() => Server.NextFrame(() =>
{ {
if (heProjectile == null || !heProjectile.IsValid) return; if (heProjectile == null || !heProjectile.IsValid) return;
@ -51,13 +65,52 @@ namespace src.player.skills
return; return;
heProjectile.TicksAtZeroVelocity = 100; heProjectile.TicksAtZeroVelocity = 100;
heProjectile.TeamNum = (byte)CsTeam.None; heProjectile.TeamNum = pendingTeam;
heProjectile.Damage = SkillsInfo.GetValue<float>(skillName, "damage"); heProjectile.Damage = SkillsInfo.GetValue<float>(skillName, "damage");
heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "damageRadius"); heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "damageRadius");
heProjectile.DetonateTime = 0; heProjectile.DetonateTime = 0;
if (nades.TryRemove(spawnTick, out var source))
heProjectile.Globalname = $"explosiveshot_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
}); });
} }
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;
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("explosiveshot_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;
if (victimPawn.TeamNum == nadeTeam)
param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
if (owner != null && !owner.IsValid) owner = null;
if (owner != null && param2.Damage >= victimPawn.Health)
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
}
private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon; private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
public static void BulletImpact(EventBulletImpact @event) public static void BulletImpact(EventBulletImpact @event)
@ -73,15 +126,16 @@ namespace src.player.skills
if (playerInfo == null || playerInfo.Skill != skillName) return; if (playerInfo == null || playerInfo.Skill != skillName) return;
if (Instance.Random.NextDouble() <= playerInfo.SkillChance) if (Instance.Random.NextDouble() <= playerInfo.SkillChance)
SpawnExplosion(pos); SpawnExplosion(pos, player);
} }
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{ {
public float Damage { get; set; } = damage; public float Damage { get; set; } = damage;
public float DamageRadius { get; set; } = damageRadius; public float DamageRadius { get; set; } = damageRadius;
public float ChanceFrom { get; set; } = chanceFrom; public float ChanceFrom { get; set; } = chanceFrom;
public float ChanceTo { get; set; } = chanceTo; public float ChanceTo { get; set; } = chanceTo;
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
} }
} }
} }

View file

@ -12,6 +12,10 @@ namespace src.player.skills
private const Skills skillName = Skills.FireRain; private const Skills skillName = Skills.FireRain;
private static readonly QAngle angle = new(10, -4, 13); private static readonly QAngle angle = new(10, -4, 13);
private static readonly ConcurrentDictionary<uint, byte> decoys = []; private static readonly ConcurrentDictionary<uint, byte> decoys = [];
private static int rainTick = -1;
private static CCSPlayerPawn? rainThrower;
private static byte rainTeam = (byte)CsTeam.None;
private static readonly ConcurrentDictionary<uint, (uint ThrowerRaw, byte Team)> rainMolotovs = [];
public static void LoadSkill() public static void LoadSkill()
{ {
@ -21,6 +25,7 @@ namespace src.player.skills
public static void NewRound() public static void NewRound()
{ {
KillAllDecoys(); KillAllDecoys();
rainMolotovs.Clear();
} }
private static void KillAllDecoys() private static void KillAllDecoys()
@ -43,58 +48,79 @@ namespace src.player.skills
return; return;
const int grenadeCount = 5; const int grenadeCount = 5;
const float spawnHeight = 800.0f; const float maxSpawnHeight = 400.0f;
const float approachDistance = 600.0f; const float clusterRadius = 110.0f;
const float clusterRadius = 130.0f; const float dropSpeed = 250.0f;
const float flightTime = 0.65f;
const float gravity = 800.0f;
float approachAngle = Random.Shared.NextSingle() * MathF.Tau; rainTick = Server.TickCount;
float dirX = MathF.Cos(approachAngle); rainThrower = pawn;
float dirY = MathF.Sin(approachAngle); rainTeam = player.TeamNum;
Vector skyCenter = new( Vector decoyGround = new(targetPos.X, targetPos.Y, targetPos.Z + 8f);
targetPos.X + dirX * approachDistance, Vector dropVelocity = new(0, 0, -dropSpeed);
targetPos.Y + dirY * approachDistance,
targetPos.Z + spawnHeight
);
float dx = targetPos.X - skyCenter.X;
float dy = targetPos.Y - skyCenter.Y;
float dz = targetPos.Z - skyCenter.Z;
float vx = dx / flightTime;
float vy = dy / flightTime;
float vz = (dz + 0.5f * gravity * flightTime * flightTime) / flightTime;
Vector velocity = new(vx, vy, vz);
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);
for (int i = 0; i < grenadeCount; i++) for (int i = 0; i < grenadeCount; i++)
{ {
float offsetAngle = Random.Shared.NextSingle() * MathF.Tau; float offsetAngle = Random.Shared.NextSingle() * MathF.Tau;
float offsetDist = MathF.Sqrt(Random.Shared.NextSingle()) * clusterRadius; float offsetDist = MathF.Sqrt(Random.Shared.NextSingle()) * clusterRadius;
float offsetX = MathF.Cos(offsetAngle) * offsetDist; Vector ground = new(
float offsetY = MathF.Sin(offsetAngle) * offsetDist; decoyGround.X + MathF.Cos(offsetAngle) * offsetDist,
decoyGround.Y + MathF.Sin(offsetAngle) * offsetDist,
Vector spawnPos = new( decoyGround.Z
skyCenter.X + offsetX,
skyCenter.Y + offsetY,
skyCenter.Z
); );
SkillUtils.CreateMolotovProjectile(spawnPos, launchAngle, velocity, player.TeamNum); 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);
} }
} }
public static void OnEntitySpawned(CEntityInstance entity) public static void OnEntitySpawned(CEntityInstance entity)
{ {
var name = entity.DesignerName; var name = entity.DesignerName;
if (name == "molotov_projectile")
{
if (Server.TickCount != rainTick) return;
var thrower = rainThrower;
if (thrower == null || !thrower.IsValid) return;
var molotov = entity.As<CMolotovProjectile>();
if (molotov == null || !molotov.IsValid) return;
molotov.TeamNum = rainTeam;
molotov.Thrower.Raw = thrower.EntityHandle.Raw;
molotov.OwnerEntity.Raw = thrower.EntityHandle.Raw;
rainMolotovs[molotov.Index] = (thrower.EntityHandle.Raw, rainTeam);
return;
}
if (name == "inferno")
{
var inferno = entity.As<CInferno>();
if (inferno == null || !inferno.IsValid) return;
var ownerHandle = inferno.OwnerEntity;
var source = ownerHandle?.Value;
if (ownerHandle == null || source == null || !source.IsValid) return;
if (!rainMolotovs.TryGetValue(source.Index, out var origin)) return;
inferno.TeamNum = origin.Team;
ownerHandle.Raw = origin.ThrowerRaw;
return;
}
if (name != "decoy_projectile") if (name != "decoy_projectile")
return; return;

View file

@ -112,13 +112,31 @@ namespace src.player.skills
box.Entity!.Name = box.Globalname = $"FortniteWall_{Server.TickCount}"; box.Entity!.Name = box.Globalname = $"FortniteWall_{Server.TickCount}";
box.Collision.SolidType = SolidType_t.SOLID_VPHYSICS; box.Collision.SolidType = SolidType_t.SOLID_VPHYSICS;
box.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(box.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2)); box.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(box.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
box.DispatchSpawn(); box.Health = box.MaxHealth = 1_000_000;
var keys = new CEntityKeyValues();
try
{
keys.SetString("model", SkillsInfo.GetValue<string>(skillName, "propModel"));
keys.SetVector("origin", pos);
keys.SetBool("solid", true);
box.DispatchSpawn(keys);
}
finally
{
keys.Dispose();
}
if (!box.IsValid) return;
box.Teleport(pos, angle, null);
barricades.TryAdd(box.Index, SkillsInfo.GetValue<int>(skillName, "barricadeHealth")); barricades.TryAdd(box.Index, SkillsInfo.GetValue<int>(skillName, "barricadeHealth"));
Server.NextFrame(() => Server.NextFrame(() =>
{ {
if (box == null || !box.IsValid) return; if (box == null || !box.IsValid) return;
box.SetModel(SkillsInfo.GetValue<string>(skillName, "propModel"));
box.Teleport(pos, angle, null); box.Teleport(pos, angle, null);
Utilities.SetStateChanged(box, "CBaseEntity", "m_CBodyComponent");
}); });
} }

View file

@ -72,6 +72,8 @@ namespace src.player.skills
{ {
if (Server.TickCount % 32 != 0) return; if (Server.TickCount % 32 != 0) return;
ReapplyScales();
foreach (var player in PlayerManager.GetTickPlayers()) foreach (var player in PlayerManager.GetTickPlayers())
{ {
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
@ -155,6 +157,29 @@ namespace src.player.skills
} }
} }
private static void ReapplyScales()
{
lock (setLock)
{
foreach (var kvp in enlargedEnemies)
{
float expected = PlayerManager.GetPlayerByIndex(kvp.Key)?.SkillChance ?? 0f;
if (expected <= 0f) continue;
var target = Utilities.GetPlayerFromIndex((int)kvp.Value);
if (target == null || !target.IsValid) continue;
var pawn = target.PlayerPawn?.Value;
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) continue;
var skeleton = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance();
if (skeleton == null || Math.Abs(skeleton.Scale - expected) < 0.01f) continue;
SkillUtils.ChangePlayerScale(target, expected);
}
}
}
private static void ResetScale(uint targetIndex, bool notify = false) private static void ResetScale(uint targetIndex, bool notify = false)
{ {
var target = Utilities.GetPlayerFromIndex((int)targetIndex); var target = Utilities.GetPlayerFromIndex((int)targetIndex);

View file

@ -1,5 +1,6 @@
using CounterStrikeSharp.API; using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Utils;
using src.utils; using src.utils;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -19,6 +20,7 @@ namespace src.player.skills
} }
private static readonly ConcurrentDictionary<uint, List<ChickenState>> activeChickens = new(); private static readonly ConcurrentDictionary<uint, List<ChickenState>> activeChickens = new();
private static readonly ConcurrentDictionary<uint, uint> chickenOwners = new();
public static void LoadSkill() public static void LoadSkill()
{ {
@ -28,6 +30,29 @@ namespace src.player.skills
public static void NewRound() public static void NewRound()
{ {
activeChickens.Clear(); activeChickens.Clear();
chickenOwners.Clear();
}
public static void OnTakeDamage(DynamicHook h)
{
CEntityInstance param = h.GetParam<CEntityInstance>(0);
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
if (param == null || param2 == null) return;
if (!chickenOwners.TryGetValue(param.Index, out uint 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;
if (attackerPawn.TeamNum != owner.TeamNum) return;
param2.Damage = 0;
} }
public static void EnableSkill(CCSPlayerController player) public static void EnableSkill(CCSPlayerController player)
@ -40,8 +65,11 @@ namespace src.player.skills
if (activeChickens.TryRemove(player.Index, out var chickens)) if (activeChickens.TryRemove(player.Index, out var chickens))
foreach (var state in chickens) foreach (var state in chickens)
if (state.Chicken != null && state.Chicken.IsValid) if (state.Chicken != null && state.Chicken.IsValid)
{
chickenOwners.TryRemove(state.Chicken.Index, out _);
state.Chicken.Remove(); state.Chicken.Remove();
} }
}
private static void SpawnChicken(CCSPlayerController player) private static void SpawnChicken(CCSPlayerController player)
{ {
@ -59,6 +87,7 @@ namespace src.player.skills
if (chicken == null || !chicken.IsValid) continue; if (chicken == null || !chicken.IsValid) continue;
chicken.Render = Color.LightGreen; chicken.Render = Color.LightGreen;
chickenOwners[chicken.Index] = player.Index;
Vector offset = new( Vector offset = new(
(float)(100 * Math.Cos(2 * Math.PI * i / amount)), (float)(100 * Math.Cos(2 * Math.PI * i / amount)),

View file

@ -33,6 +33,21 @@ namespace src.player.skills
} }
} }
public static void PlayerDisconnect(uint playerIndex)
{
lock (setLock)
{
targetedPlayers.TryRemove(playerIndex, out _);
if (playersToTarget.TryRemove(playerIndex, out uint targetIndex))
targetedPlayers.TryRemove(targetIndex, out _);
foreach (var kvp in playersToTarget)
if (kvp.Value == playerIndex)
playersToTarget.TryRemove(kvp.Key, out _);
}
}
public static void OnTick() public static void OnTick()
{ {
if (Server.TickCount % 32 != 0) return; if (Server.TickCount % 32 != 0) return;
@ -75,7 +90,7 @@ namespace src.player.skills
var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex); var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex);
if (enemy == null) if (enemy == null || !enemy.IsValid || enemy.Team == player.Team)
{ {
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")); playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
return; return;

View file

@ -25,7 +25,9 @@ namespace src.player.skills
lock (setLock) lock (setLock)
{ {
RemoveVolume(playerIndex); RemoveVolume(playerIndex);
playersToTarget.TryRemove(playerIndex, out _);
if (playersToTarget.TryRemove(playerIndex, out uint leavingTarget))
RemoveVolume(leavingTarget);
foreach (var kvp in playersToTarget) foreach (var kvp in playersToTarget)
if (kvp.Value == playerIndex) if (kvp.Value == playerIndex)
@ -104,7 +106,7 @@ namespace src.player.skills
} }
var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex); var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex);
if (enemy == null || !enemy.IsValid) if (enemy == null || !enemy.IsValid || enemy.Team == player.Team)
{ {
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")); playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
return; return;

View file

@ -15,12 +15,12 @@ namespace src.player.skills
private static readonly string[] disabledWeapons = private static readonly string[] disabledWeapons =
[ [
"weapon_ak47", "weapon_m4a4", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", "weapon_m4a4", "weapon_m4a1", "weapon_m4a1_silencer",
"weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg553", "weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg556",
"weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7",
"weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08", "weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08",
"weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova", "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova",
"weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249",
"weapon_negev", "weapon_sg556" "weapon_negev"
]; ];
public static void LoadSkill() public static void LoadSkill()

View file

@ -23,7 +23,7 @@ namespace src.player.skills
{ {
"weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90",
"weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47",
"weapon_aug", "weapon_sg553", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_aug", "weapon_sg556", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1",
"weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev"
}; };

View file

@ -20,7 +20,7 @@ namespace src.player.skills
[ [
"weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90",
"weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47",
"weapon_aug", "weapon_sg553", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_aug", "weapon_sg556", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1",
"weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev"
]; ];

View file

@ -185,10 +185,12 @@ namespace src.player.skills
camNode.Flags = (uint)(camNode.Flags & ~(1 << 2)); camNode.Flags = (uint)(camNode.Flags & ~(1 << 2));
camera.SetModel(cameraViewModel); camera.SetModel(cameraViewModel);
camera.Render = Color.FromArgb(1, 255, 255, 255);
camera.Teleport(pos, angle); camera.Teleport(pos, angle);
camera.DispatchSpawn(); camera.DispatchSpawn();
camera.Render = Color.FromArgb(1, 255, 255, 255);
Utilities.SetStateChanged(camera, "CBaseModelEntity", "m_clrRender");
CBaseEntity? target = pawn != null && pawn.IsValid ? pawn : null; CBaseEntity? target = pawn != null && pawn.IsValid ? pawn : null;
var entities = EntityManager.GetPlayerEntities(enemy.Index, "empty_prop"); var entities = EntityManager.GetPlayerEntities(enemy.Index, "empty_prop");
@ -203,10 +205,11 @@ namespace src.player.skills
camera.AcceptInput("SetParent", target, target, "!activator"); camera.AcceptInput("SetParent", target, target, "!activator");
}); });
if (cameras.TryGetValue(player.Index, out var cameraInfo)) uint originalView = cameras.TryGetValue(player.Index, out var cameraInfo)
cameras.AddOrUpdate(player.Index, (cameraInfo.Item1, camera.Index, enemy.Index), (k, v) => (cameraInfo.Item1, camera.Index, enemy.Index)); ? cameraInfo.Item1
else : player.PlayerPawn?.Value?.CameraServices?.ViewEntity.Raw ?? 0;
cameras.AddOrUpdate(player.Index, (pawn.CameraServices.ViewEntity.Raw, camera.Index, enemy.Index), (k, v) => (pawn.CameraServices.ViewEntity.Raw, camera.Index, enemy.Index));
cameras[player.Index] = (originalView, camera.Index, enemy.Index);
return camera.EntityHandle.Raw; return camera.EntityHandle.Raw;
} }

View file

@ -28,11 +28,18 @@ namespace src.player.skills
} }
foreach (var kvp in OriginalWeaponMaxAmmo) foreach (var kvp in OriginalWeaponMaxAmmo)
{
try
{ {
var vdata = VirtualFunctions.GetCSWeaponDataFromKey(-1, kvp.Value.Item2); var vdata = VirtualFunctions.GetCSWeaponDataFromKey(-1, kvp.Value.Item2);
if (vdata != null) if (vdata == null || vdata.Handle == nint.Zero) continue;
vdata.PrimaryReserveAmmoMax = kvp.Value.Item1; vdata.PrimaryReserveAmmoMax = kvp.Value.Item1;
} }
catch (Exception ex)
{
Server.PrintToConsole($"[jRandomSkills] TakeAmmo could not restore ammo for {kvp.Value.Item2}: {ex.Message}");
}
}
OriginalWeaponMaxAmmo.Clear(); OriginalWeaponMaxAmmo.Clear();
} }

View file

@ -95,8 +95,16 @@ namespace src.player.skills
var pawn = eventPlayer.PlayerPawn.Value; var pawn = eventPlayer.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue; if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
if (SkillUtils.GetDistance(smoke.Key, pawn.AbsOrigin) <= smokeRadius) if (SkillUtils.GetDistance(smoke.Key, pawn.AbsOrigin) > smokeRadius) continue;
if (SkillUtils.TakeHealth(pawn, smokeDamage, thrower, KillfeedIcons.Smokegrenade))
int damage = smokeDamage;
if (thrower != null && eventPlayer.Index != thrower.Index && pawn.TeamNum == thrower.TeamNum)
{
damage = (int)MathF.Round(smokeDamage * SkillUtils.GetTeamDamageMultiplier(skillName));
if (damage <= 0) continue;
}
if (SkillUtils.TakeHealth(pawn, damage, thrower, KillfeedIcons.Smokegrenade))
player.EmitSound("Player.DamageBody.Onlooker"); player.EmitSound("Player.DamageBody.Onlooker");
} }
} }
@ -162,12 +170,13 @@ namespace src.player.skills
SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1); SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1);
} }
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{ {
public int SmokeDamage { get; set; } = smokeDamage; public int SmokeDamage { get; set; } = smokeDamage;
public float SmokeRadius { get; set; } = smokeRadius; public float SmokeRadius { get; set; } = smokeRadius;
public int TickCooldown { get; set; } = tickCooldown; public int TickCooldown { get; set; } = tickCooldown;
public int GrenadeLimit { get; set; } = grenadeLimit; public int GrenadeLimit { get; set; } = grenadeLimit;
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
} }
} }
} }

View file

@ -17,7 +17,7 @@ namespace src.player.skills
"weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", "weapon_elite", "weapon_hkp2000", "weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", "weapon_elite", "weapon_hkp2000",
"weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", "weapon_sg556", "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", "weapon_sg556",
"weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a4", "weapon_m4a1_silencer", "weapon_ak47", "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a4", "weapon_m4a1_silencer", "weapon_ak47",
"weapon_aug", "weapon_sg553", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_aug", "weapon_sg556", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1",
"weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" ]; "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" ];
public static void LoadSkill() public static void LoadSkill()

View file

@ -75,6 +75,21 @@ namespace src.player.skills
SkillUtils.CloseMenu(player); SkillUtils.CloseMenu(player);
} }
public static void PlayerDisconnect(uint playerIndex)
{
lock (setLock)
{
infectedPlayers.TryRemove(playerIndex, out _);
if (playersToTarget.TryRemove(playerIndex, out uint targetIndex))
infectedPlayers.TryRemove(targetIndex, out _);
foreach (var kvp in playersToTarget)
if (kvp.Value == playerIndex)
playersToTarget.TryRemove(kvp.Key, out _);
}
}
public static void OnTick() public static void OnTick()
{ {
if (Server.TickCount % 32 != 0) return; if (Server.TickCount % 32 != 0) return;

View file

@ -279,7 +279,6 @@ namespace jRandomSkills.src.utils
"weapon_p90" => KillfeedIcons.P90, "weapon_p90" => KillfeedIcons.P90,
"weapon_mp5sd" => KillfeedIcons.Mp5, "weapon_mp5sd" => KillfeedIcons.Mp5,
"weapon_sg556" => KillfeedIcons.Sg556, "weapon_sg556" => KillfeedIcons.Sg556,
"weapon_sg553" => KillfeedIcons.Sg556,
"weapon_famas" => KillfeedIcons.Famas, "weapon_famas" => KillfeedIcons.Famas,
"weapon_galilar" => KillfeedIcons.Galilar, "weapon_galilar" => KillfeedIcons.Galilar,
"weapon_m4a4" => KillfeedIcons.M4A4, "weapon_m4a4" => KillfeedIcons.M4A4,

View file

@ -249,7 +249,8 @@ namespace src.utils
skeleton.Scale = scale; skeleton.Scale = scale;
playerPawn.AcceptInput("SetScale", null, null, scale.ToString(CultureInfo.InvariantCulture)); playerPawn.AcceptInput("SetScale", null, null, scale.ToString(CultureInfo.InvariantCulture));
Server.NextWorldUpdate(() => { Server.NextWorldUpdate(() =>
{
if (playerPawn == null || !playerPawn.IsValid) return; if (playerPawn == null || !playerPawn.IsValid) return;
Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_CBodyComponent"); Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_CBodyComponent");
}); });
@ -338,6 +339,12 @@ namespace src.utils
return !ff && !tae; // same team + FF off -> engine will zero this damage return !ff && !tae; // same team + FF off -> engine will zero this damage
} }
public static float GetTeamDamageMultiplier(Skills skill)
{
float reduction = SkillsInfo.GetValue<float>(skill, "dmgReductionForTeamates");
return 1f - Math.Clamp(reduction, 0f, 1f);
}
private static readonly ConcurrentDictionary<uint, (uint AttackerIndex, string? Weapon, int ExpiryTick)> pendingKillCredits = []; private static readonly ConcurrentDictionary<uint, (uint AttackerIndex, string? Weapon, int ExpiryTick)> pendingKillCredits = [];
public static void RegisterKillCredit(uint victimIndex, uint attackerIndex, KillfeedIcons? killfeedIcon = null) public static void RegisterKillCredit(uint victimIndex, uint attackerIndex, KillfeedIcons? killfeedIcon = null)
@ -367,7 +374,7 @@ namespace src.utils
"deagle", "revolver", "glock", "usp_silencer", "cz75a", "deagle", "revolver", "glock", "usp_silencer", "cz75a",
"fiveseven", "p250", "tec9", "elite", "hkp2000", "fiveseven", "p250", "tec9", "elite", "hkp2000",
"mp9", "mac10", "bizon", "mp7", "ump45", "p90", "mp5sd", "mp9", "mac10", "bizon", "mp7", "ump45", "p90", "mp5sd",
"famas", "galilar", "m4a1", "m4a1_silencer", "ak47", "aug", "sg553", "famas", "galilar", "m4a1", "m4a1_silencer", "ak47", "aug", "sg556",
"ssg08", "awp", "scar20", "g3sg1", "ssg08", "awp", "scar20", "g3sg1",
"nova", "xm1014", "mag7", "sawedoff", "nova", "xm1014", "mag7", "sawedoff",
"m249", "negev" "m249", "negev"
@ -389,7 +396,7 @@ namespace src.utils
Skills.Deaf, Skills.ExpensiveAmmo, Skills.Giant, Skills.Glitch, Skills.Deaf, Skills.ExpensiveAmmo, Skills.Giant, Skills.Glitch,
Skills.Jammer, Skills.JumpBan, Skills.JumpCurse, Skills.LifeSwap, Skills.Jammer, Skills.JumpBan, Skills.JumpCurse, Skills.LifeSwap,
Skills.Magnifier, Skills.MoneySwap, Skills.Nightmare, Skills.Poison, Skills.Magnifier, Skills.MoneySwap, Skills.Nightmare, Skills.Poison,
Skills.PrimaryBan, Skills.Thief, Skills.WildThrow Skills.JetKick, Skills.PrimaryBan, Skills.Thief, Skills.WildThrow
]; ];
private static readonly HashSet<string> curseSkillNames = new(curseSkills.Select(s => s.ToString()), StringComparer.Ordinal); private static readonly HashSet<string> curseSkillNames = new(curseSkills.Select(s => s.ToString()), StringComparer.Ordinal);
@ -436,15 +443,14 @@ namespace src.utils
public static bool TryClaimCurse(uint curserIndex, uint victimIndex, bool force = false) public static bool TryClaimCurse(uint curserIndex, uint victimIndex, bool force = false)
{ {
if (!CurseLimitEnabled) return true;
int limit = Config.LoadedConfig.CurseSkillPerPlayer!.Value;
lock (curseLock) lock (curseLock)
{ {
ReleaseCurseLocked(curserIndex); ReleaseCurseLocked(curserIndex);
curseCounts.TryGetValue(victimIndex, out int used); curseCounts.TryGetValue(victimIndex, out int used);
if (!force && used >= limit) return false;
if (CurseLimitEnabled && !force && used >= Config.LoadedConfig.CurseSkillPerPlayer!.Value)
return false;
curseCounts[victimIndex] = used + 1; curseCounts[victimIndex] = used + 1;
curserToVictim[curserIndex] = victimIndex; curserToVictim[curserIndex] = victimIndex;
@ -454,15 +460,17 @@ namespace src.utils
public static void ReleaseCurse(uint curserIndex) public static void ReleaseCurse(uint curserIndex)
{ {
if (!CurseLimitEnabled) return;
lock (curseLock) ReleaseCurseLocked(curserIndex); lock (curseLock) ReleaseCurseLocked(curserIndex);
} }
public static uint[] GetCursersOf(uint victimIndex)
{
lock (curseLock)
return [.. curserToVictim.Where(kvp => kvp.Value == victimIndex).Select(kvp => kvp.Key)];
}
public static void ClearCursesFor(uint playerIndex) public static void ClearCursesFor(uint playerIndex)
{ {
if (!CurseLimitEnabled) return;
lock (curseLock) lock (curseLock)
{ {
ReleaseCurseLocked(playerIndex); ReleaseCurseLocked(playerIndex);
@ -1025,7 +1033,7 @@ namespace src.utils
if (roundsInOvertime % overtimeMaxRounds == 0) if (roundsInOvertime % overtimeMaxRounds == 0)
{ {
int currentOvertime = roundsInOvertime / overtimeMaxRounds; int currentOvertime = roundsInOvertime / overtimeMaxRounds;
if ( currentOvertime < overtimeLimit) if (currentOvertime < overtimeLimit)
{ {
gameRulesProxy.SwapTeamsOnRestart = true; gameRulesProxy.SwapTeamsOnRestart = true;
gameRulesProxy.SwitchingTeamsAtRoundReset = true; gameRulesProxy.SwitchingTeamsAtRoundReset = true;

View file

@ -1,4 +1,4 @@
[ [
{ {
"NeedsTeammates": false, "NeedsTeammates": false,
"DisableOnFreezeTime": false, "DisableOnFreezeTime": false,
@ -105,6 +105,7 @@
"SmokeRadius": 180.0, "SmokeRadius": 180.0,
"TickCooldown": 17, "TickCooldown": 17,
"GrenadeLimit": 1, "GrenadeLimit": 1,
"DmgReductionForTeamates": 0.5,
"NeedsTeammates": false, "NeedsTeammates": false,
"DisableOnFreezeTime": false, "DisableOnFreezeTime": false,
"OnlyTeam": 0, "OnlyTeam": 0,
@ -1522,6 +1523,7 @@
"DamageRadius": 210.0, "DamageRadius": 210.0,
"ChanceFrom": 0.15, "ChanceFrom": 0.15,
"ChanceTo": 0.3, "ChanceTo": 0.3,
"DmgReductionForTeamates": 0.5,
"NeedsTeammates": false, "NeedsTeammates": false,
"DisableOnFreezeTime": false, "DisableOnFreezeTime": false,
"OnlyTeam": 0, "OnlyTeam": 0,
@ -1857,9 +1859,9 @@
{ {
"ExplosionRadius": 400.0, "ExplosionRadius": 400.0,
"ExplosionDamage": 60, "ExplosionDamage": 60,
"DmgReductionForTeamates": 0.5,
"Cooldown": 10.0, "Cooldown": 10.0,
"Force": 1000.0, "Force": 1000.0,
"DmgReductionForTeamates": 0.5,
"NeedsTeammates": false, "NeedsTeammates": false,
"DisableOnFreezeTime": false, "DisableOnFreezeTime": false,
"OnlyTeam": 0, "OnlyTeam": 0,

View file

@ -1,4 +1,4 @@
{ {
"none": "Keine", "none": "Keine",
"none_desc": "Du hast keine Fertigkeit", "none_desc": "Du hast keine Fertigkeit",
@ -276,6 +276,7 @@
"jetkick": "Jetkick", "jetkick": "Jetkick",
"jetkick_desc": "Wähle einen Spieler für einen Jetkick", "jetkick_desc": "Wähle einen Spieler für einen Jetkick",
"jetkick_select_info": "Wähle einen Spieler für den Jetkick:",
"jetkick_player_info": "Spieler '{0}' wurde wegge-kickt.", "jetkick_player_info": "Spieler '{0}' wurde wegge-kickt.",
"jetkick_enemy_info": "Jeder Schuss stößt dich nach hinten", "jetkick_enemy_info": "Jeder Schuss stößt dich nach hinten",
"jetkick_disable_info": "Dein Rückstoß ist wieder normal.", "jetkick_disable_info": "Dein Rückstoß ist wieder normal.",

View file

@ -1,4 +1,4 @@
{ {
"none": "None", "none": "None",
"none_desc": "You have no skill", "none_desc": "You have no skill",
@ -269,6 +269,7 @@
"jetkick": "Jetkick", "jetkick": "Jetkick",
"jetkick_desc": "Select a player to jetkick", "jetkick_desc": "Select a player to jetkick",
"jetkick_select_info": "Select a player to jetkick:",
"jetkick_player_info": "Player '{0}' has been jetkicked.", "jetkick_player_info": "Player '{0}' has been jetkicked.",
"jetkick_enemy_info": "Every shot knocks you backwards", "jetkick_enemy_info": "Every shot knocks you backwards",
"jetkick_disable_info": "Your recoil is back to normal.", "jetkick_disable_info": "Your recoil is back to normal.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Aucune", "none": "Aucune",
"none_desc": "Vous n’avez aucune compétence", "none_desc": "Vous n’avez aucune compétence",
@ -276,6 +276,7 @@
"jetkick": "Coup de pied propulsé", "jetkick": "Coup de pied propulsé",
"jetkick_desc": "Sélectionnez un joueur pour lui donner un coup de pied propulsé", "jetkick_desc": "Sélectionnez un joueur pour lui donner un coup de pied propulsé",
"jetkick_select_info": "Sélectionnez un joueur à propulser :",
"jetkick_player_info": "Le joueur '{0}' a reçu un coup de pied propulsé.", "jetkick_player_info": "Le joueur '{0}' a reçu un coup de pied propulsé.",
"jetkick_enemy_info": "Chaque tir vous repousse vers l'arrière", "jetkick_enemy_info": "Chaque tir vous repousse vers l'arrière",
"jetkick_disable_info": "Votre recul est revenu à la normale.", "jetkick_disable_info": "Votre recul est revenu à la normale.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Brak", "none": "Brak",
"none_desc": "Nie posiadasz żadnej supermocy", "none_desc": "Nie posiadasz żadnej supermocy",
@ -276,6 +276,7 @@
"jetkick": "Odrzutowy Kop", "jetkick": "Odrzutowy Kop",
"jetkick_desc": "Wybierz gracza, aby użyć na nim odrzutowego kopa", "jetkick_desc": "Wybierz gracza, aby użyć na nim odrzutowego kopa",
"jetkick_select_info": "Wybierz gracza do odrzutowego kopa:",
"jetkick_player_info": "Gracz '{0}' został odrzucony kopem.", "jetkick_player_info": "Gracz '{0}' został odrzucony kopem.",
"jetkick_enemy_info": "Każdy strzał mocno odrzuca Cię do tyłu", "jetkick_enemy_info": "Każdy strzał mocno odrzuca Cię do tyłu",
"jetkick_disable_info": "Twój odrzut wrócił do normy.", "jetkick_disable_info": "Twój odrzut wrócił do normy.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Nenhuma", "none": "Nenhuma",
"none_desc": "Você não tem nenhuma habilidade", "none_desc": "Você não tem nenhuma habilidade",
@ -276,6 +276,7 @@
"jetkick": "Chute a Jato", "jetkick": "Chute a Jato",
"jetkick_desc": "Selecione um jogador para aplicar um chute a jato", "jetkick_desc": "Selecione um jogador para aplicar um chute a jato",
"jetkick_select_info": "Selecione um jogador para o chute a jato:",
"jetkick_player_info": "O jogador '{0}' recebeu um chute a jato.", "jetkick_player_info": "O jogador '{0}' recebeu um chute a jato.",
"jetkick_enemy_info": "Cada tiro empurra você para trás", "jetkick_enemy_info": "Cada tiro empurra você para trás",
"jetkick_disable_info": "Seu recuo voltou ao normal.", "jetkick_disable_info": "Seu recuo voltou ao normal.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Нет навыка", "none": "Нет навыка",
"none_desc": "У вас нет навыка", "none_desc": "У вас нет навыка",
@ -276,6 +276,7 @@
"jetkick": "Реактивный удар", "jetkick": "Реактивный удар",
"jetkick_desc": "Выберите игрока для реактивного удара", "jetkick_desc": "Выберите игрока для реактивного удара",
"jetkick_select_info": "Выберите игрока для реактивного удара:",
"jetkick_player_info": "Игрок '{0}' получил реактивный удар.", "jetkick_player_info": "Игрок '{0}' получил реактивный удар.",
"jetkick_enemy_info": "Каждый выстрел отбрасывает вас назад", "jetkick_enemy_info": "Каждый выстрел отбрасывает вас назад",
"jetkick_disable_info": "Ваша отдача вернулась к норме.", "jetkick_disable_info": "Ваша отдача вернулась к норме.",

View file

@ -1,4 +1,4 @@
{ {
"none": "Yok", "none": "Yok",
"none_desc": "Hiçbir yeteneğin yok", "none_desc": "Hiçbir yeteneğin yok",
@ -52,13 +52,13 @@
"behind_desc2": "Baş döndürme şansın: %{0}", "behind_desc2": "Baş döndürme şansın: %{0}",
"berserker": "Berserker", "berserker": "Berserker",
"berserker_desc": "Canın azaldıkça daha fazla hasar verir ve daha hızlı hareket edersin", "berserker_desc": "Canın azaldıkça hasarın artar ve daha hızlanırsın",
"blademaster": "Kılıç Ustası", "blademaster": "Kılıç Ustası",
"blademaster_desc": "Bıçak tutarken mermileri sektirme şansın artar", "blademaster_desc": "Bıçak tutarken mermileri sektirme şansın artar",
"blastshot": "Patlama Atışı", "blastshot": "Patlama Atışı",
"blastshot_desc": "MP5 ile Attack2 tuşuna basarak bir HE bombası ateşle", "blastshot_desc": "MP5 ile Sağ Tık'a basarak El bombası fırlat",
"bunnyhop": "Tavşan", "bunnyhop": "Tavşan",
"bunnyhop_desc": "Otomatik \"BunnyHop\" yaparsın", "bunnyhop_desc": "Otomatik \"BunnyHop\" yaparsın",
@ -156,14 +156,14 @@
"fastreload_desc": "[css_useSkill] tuşuna basarak şarjörünü hızlıca değiştir", "fastreload_desc": "[css_useSkill] tuşuna basarak şarjörünü hızlıca değiştir",
"firerain": "Ateş Yağmuru", "firerain": "Ateş Yağmuru",
"firerain_desc": "Molotof yağmuru çağırmak için bir yem at", "firerain_desc": "Molotof yağdırmak için bir decoy at",
"flash": "Flash", "flash": "Flash",
"flash_desc": "Raund başında rastgele oyuncu hızı", "flash_desc": "Raund başında rastgele oyuncu hızı",
"flash_desc2": "Hızın: {0}x", "flash_desc2": "Hızın: {0}x",
"flashlight": "El Feneri", "flashlight": "El Feneri",
"flashlight_desc": "El fenerini açıp kapatmak için [css_useSkill] tuşuna tıkla. Işığı düşmanları kör edebilir", "flashlight_desc": "[css_useSkill] tuşuna basarak El fenerini açarsın/kaparsın (Işığı düşmanları kör edebilir)",
"fortnite": "Fortnite", "fortnite": "Fortnite",
"fortnite_desc": "[css_useSkill] tuşuna basarak barikat kur", "fortnite_desc": "[css_useSkill] tuşuna basarak barikat kur",
@ -223,7 +223,7 @@
"healingsmoke_desc": "Sis bombaların iyileştirir", "healingsmoke_desc": "Sis bombaların iyileştirir",
"hermit": "Münzevi", "hermit": "Münzevi",
"hermit_desc": "Öldürdüğünde mermi ve canın yenilenir", "hermit_desc": "Birini öldürünce mermin ve canın yenilenir",
"holyhandgrenade": "Kutsal El Bombası", "holyhandgrenade": "Kutsal El Bombası",
"holyhandgrenade_desc": "El bomban iki kat hasara ve menzile sahip", "holyhandgrenade_desc": "El bomban iki kat hasara ve menzile sahip",
@ -267,9 +267,10 @@
"jester_on": "AÇIK", "jester_on": "AÇIK",
"jester_off": "KAPALI", "jester_off": "KAPALI",
"jetkick": "Jet Tekmesi", "jetkick": "Jetkick",
"jetkick_desc": "Jet tekmesi yapmak için bir oyuncu seç", "jetkick_desc": "Seçtiğin bir rakibin her atışı onu geriye iter",
"jetkick_player_info": "'{0}' adlı oyuncuya jet tekmesi yapıldı.", "jetkick_select_info": "Jetkick uygulanacak oyuncu:",
"jetkick_player_info": "'{0}' adlı oyuncuya jetkick uygulandı.",
"jetkick_enemy_info": "Her atış seni geriye doğru iter", "jetkick_enemy_info": "Her atış seni geriye doğru iter",
"jetkick_disable_info": "Geri tepmen normale döndü.", "jetkick_disable_info": "Geri tepmen normale döndü.",
@ -297,7 +298,7 @@
"knockback_desc": "Havadayken ateş etmek seni geriye savurur", "knockback_desc": "Havadayken ateş etmek seni geriye savurur",
"lastgasp": "Son Nefes", "lastgasp": "Son Nefes",
"lastgasp_desc": "Öldükten sonra seni öldüren düşmana hasar verirsin", "lastgasp_desc": "Öldükten sonra seni öldürene hasar verirsin",
"lifeswap": "Can Takası", "lifeswap": "Can Takası",
"lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç", "lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç",
@ -340,7 +341,7 @@
"deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün", "deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün",
"deathbomb_explosion": "BOOM!!!", "deathbomb_explosion": "BOOM!!!",
"demoneye": "Şeytan Gözü", "demoneye": "Şeytani Göz",
"demoneye_desc": "Baktığın tüm düşmanlara hasar verirsin", "demoneye_desc": "Baktığın tüm düşmanlara hasar verirsin",
"nightmare": "Kabus", "nightmare": "Kabus",
@ -489,14 +490,14 @@
"swapposition": "Yer Değiştirme", "swapposition": "Yer Değiştirme",
"swapposition_desc": "[css_useSkill] tuşuna basarak vurduğun düşmanla yer değiştir", "swapposition_desc": "[css_useSkill] tuşuna basarak vurduğun düşmanla yer değiştir",
"takeammo": "Mühimmat Al", "takeammo": "Cephane Hırsızı",
"takeammo_desc": "Rastgele bir düşmanın aktif silahının şarjörünü almak için [css_useSkill] tuşuna tıkla", "takeammo_desc": "[css_useSkill] tuşuna basarak rastgele bir düşmanın silahının şarjörünü çalarsın",
"takeammo_hud_info1": "Aktif şarjörü olan bir oyuncu bulunamadı.", "takeammo_hud_info1": "Cephanesi olan bir oyuncu bulunamadı.",
"takeammo_hud_info2": "Düşmanın şarjörü yok.", "takeammo_hud_info2": "Düşmanın cephanesi yok.",
"takeammo_enemy_info": "Bir miktar mühimmat kaybettin.", "takeammo_enemy_info": "Birisi cephaneni çaldı.",
"teamteleport": "Takıma Işınlanma", "teamteleport": "Takıma Işınlanma",
"teamteleport_desc": "Baktığın takım arkadaşına ışınlanmak için [css_useSkill] tuşuna bas", "teamteleport_desc": "[css_useSkill] tuşuna basarak baktığın takım arkadaşına ışınlanırsın",
"teamteleport_noenemy": "Takım arkadaşı bulunamadı", "teamteleport_noenemy": "Takım arkadaşı bulunamadı",
"teamteleport_hud_info": "Bulundu: {0}", "teamteleport_hud_info": "Bulundu: {0}",