v1.2.3.b5

## GENERAL
- The HUD loop now runs on the shared 16 Hz frame gate (SkillUtils.IsHudFrame) instead of keeping its own every-other-tick schedule, and fifteen skills that were refreshing their HUD on every single tick were moved onto that same gate.
- Added SkillData.GetSnapshot(), a cached array of the registered skills, so the HUD no longer copies the whole skill bag every frame. The cache is invalidated together with the existing lookup map, so a reload still picks up new skills.
- Added SkillUtils.ForceFullUpdateToAllChunked(), which spreads a full network update across frames (two clients per frame) instead of pushing one for every connected client in a single tick. The original ForceFullUpdateToAll is untouched, so the six skills that already call it keep their current behaviour.
- BlastShot, Cypher and Iana now clear their HUD line in DisableSkill, so a stale line can no longer survive the skill being taken away.

## FIXES
- SoundMaker threw a divide-by-zero on every tick when its cooldown was configured as 0, because the tick gate divides by that value. The cooldown is now clamped to at least 1.
- Regeneration and RadarHack were calling the wrong SkillsInfo.DefaultSkillInfo overload, which silently dropped disableOnFreezeTime, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer and rarity. Every configured field now reaches the base class, so those two skills finally honour their own config.
- Falcon Eye blocked the trigger on the wrong side of the check: picking up a weapon while back in the normal view locked primary and secondary fire, because the pickup handler tested for the player being in their own view instead of in the skill camera.
- Spectator locked primary and secondary fire when its camera could not be created at all, which happens when no enemy is alive to spectate. The player stayed in the normal view with a weapon that would not fire until the round ended.

## SKILL IMPROVEMENTS
- Wallhack:
  -- The glow build and the full network update now start when freeze time ends and are spread across frames, instead of running in a single tick the moment the skill is handed out. On a busy server this used to cost roughly 13 ms in one frame, close to a dropped tick at 64 tick; across four grants in an eight-hour session it no longer reaches the 2 ms profiler threshold at all.
- Jackal:
  -- CheckTransmit resolves the trail relays once per frame rather than once per viewer, so the same entity lookups are no longer repeated for every client on the server.
  -- Removed an opponent list that was gathered in EnableSkill and never used.
- ThrowingKnife:
  -- CheckTransmit builds the knife glow list once per frame, and the viewer index is resolved once per client instead of once per knife.
- Glaz:
  -- The observer target is only resolved for players who do not own the skill themselves, and the lookup now bails out early when there is no observer target at all.
- RadarHack and QuickShot:
  -- The skill check runs before the validity check, so players without the skill are skipped immediately instead of being fully validated first.
- Distancer, HealingChicken and SoundMaker:
  -- These now return straight away while nobody holds the skill, rather than walking the player list every tick.

## DEBUG
- Removed the [HITGROUP] mismatch check. Every recorded case was accounted for: ten were Aimbot rewriting the hit group as designed, and thirteen were non-bullet skill damage, which reports a generic hit group by nature. Dropping the check also removes a dictionary write from every damage event.
- Removed the identity-split tag from the damage log. It was dead by construction, because the victim controller had already been routed before the comparison ran, so it could never report a difference.
- Removed the player-shoot handler, which produced no output on this build.
- [DMG] lines now print the routing fields only when the routed index actually differs from the original one, so the check remains as a tripwire without adding noise to every line.

## TRANSLATIONS
- Turkish: reworded sixty-two entries that padded their sentences with second-person verb endings, dropped the English glosses left in parentheses, and settled every [css_useSkill] line into one shape. Also fixed genuine typos in Dracula, Giant, Thorns and Impostor.
- All eight languages received the strings for the four new skills. (AI)

## NEW SKILLS:
- Chameleon: The first player you kill gives you their skill.
- Grapple Hook: Press [css_useSkill] to fire a hook at the point you are aiming at and pull yourself there.
- Inheritance: When a teammate dies you can take over their skill.
- Ricochet: Your bullets bounce off walls and can still hit enemies.
This commit is contained in:
ByDexter 2026-08-06 04:21:39 +03:00
parent 7b4eff1dba
commit 709ed95614
59 changed files with 2248 additions and 458 deletions

View file

@ -31,7 +31,7 @@ namespace src
public override string ModuleName => "[CS2] [ jRandomSkills ]";
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 ModuleVersion => "1.2.3.b4";
public override string ModuleVersion => "1.2.3.b5";
public override void Load(bool hotReload)
{
@ -386,6 +386,10 @@ namespace src
{
public static ConcurrentBag<jSkill_SkillInfo> Skills { get; } = [];
private static jSkill_SkillInfo[]? _snapshot;
public static jSkill_SkillInfo[] GetSnapshot() => _snapshot ??= [.. Skills];
private static Dictionary<Skills, jSkill_SkillInfo>? _bySkill;
public static jSkill_SkillInfo? GetInfo(Skills skill)
@ -401,7 +405,11 @@ namespace src
return map.TryGetValue(skill, out var info) ? info : null;
}
public static void Invalidate() => _bySkill = null;
public static void Invalidate()
{
_bySkill = null;
_snapshot = null;
}
}
public enum CS2ConsoleColors

View file

@ -1,4 +1,4 @@
{
{
"none": "Keine",
"none_desc": "Du hast keine Fertigkeit",
@ -77,6 +77,11 @@
"catapult_desc": "Du hast eine zufällige Chance, einen Gegner nach oben zu schleudern",
"catapult_desc2": "Deine Chance, einen Gegner bei einem Treffer zu schleudern, beträgt: {0} %",
"chameleon": "Chamäleon",
"chameleon_desc": "Der erste Spieler, den du tötest, gibt dir seine Fähigkeit",
"chameleon_copied_info": "Fähigkeit kopiert: {0}",
"chameleon_wrong_team_info": "'{0}' hatte eine Fähigkeit, die dein Team nicht nutzen kann.",
"chicken": "Huhn",
"chicken_desc": "Du erhältst ein Huhn-Modell + 10 % schnellere Bewegung – 50 HP",
@ -220,6 +225,11 @@
"godmode_off": "Unsterblichkeit deaktiviert",
"godmode_on": "Unsterblichkeit aktiviert",
"grapple": "Enterhaken",
"grapple_desc": "Drücke [css_useSkill], um einen Haken auf den anvisierten Punkt zu schießen und dich dorthin zu ziehen",
"grapple_no_anchor_info": "Keine Oberfläche zum Einhaken.",
"grapple_pulling_info": "ZUG",
"grenadier": "Grenadier",
"grenadier_desc": "Du hast unendlich viele Granaten",
@ -259,6 +269,11 @@
"infiniteammo": "Unendliche Munition",
"infiniteammo_desc": "Du erhältst unendliche Munition für alle deine Waffen",
"inheritance": "Erbe",
"inheritance_desc": "Wenn ein Teamkollege stirbt, kannst du seine Fähigkeit übernehmen",
"inheritance_select_info": "Wähle die Fähigkeit, die du erbst:",
"inheritance_player_info": "Du hast die Fähigkeit von '{0}' geerbt.",
"jackal": "Spürhund",
"jackal_desc": "Jeder Gegner hinterlässt eine Spur",
@ -448,6 +463,9 @@
"richboy": "Rich Boy",
"richboy_desc": "Du erhältst zu Beginn der Runde einen zufälligen Geldbetrag",
"ricochet": "Querschläger",
"ricochet_desc": "Deine Kugeln prallen von Wänden ab und können Gegner weiterhin treffen",
"robinhood": "Robin Hood",
"robinhood_desc": "Wenn du einem Gegner Schaden zufügst, stiehlst du ihm sein Geld",

View file

@ -1,4 +1,4 @@
{
{
"none": "None",
"none_desc": "You have no skill",
@ -77,6 +77,11 @@
"catapult_desc": "You have a random chance to launch an enemy upwards",
"catapult_desc2": "Your chance to launch an enemy on hit is: {0}%",
"chameleon": "Chameleon",
"chameleon_desc": "The first player you kill gives you their skill",
"chameleon_copied_info": "You copied the skill: {0}",
"chameleon_wrong_team_info": "'{0}' had a skill your team cannot use.",
"chicken": "Chicken",
"chicken_desc": "You get a chicken model + 10% faster movement - 50 HP",
@ -213,6 +218,11 @@
"godmode_off": "Immortality disabled",
"godmode_on": "Immortality enabled",
"grapple": "Grapple Hook",
"grapple_desc": "Press [css_useSkill] to fire a hook at the point you are aiming at and pull yourself there",
"grapple_no_anchor_info": "No surface to hook onto.",
"grapple_pulling_info": "PULLING",
"grenadier": "Grenadier",
"grenadier_desc": "You have infinite hegrenade",
@ -252,6 +262,11 @@
"infiniteammo": "Infinite Ammo",
"infiniteammo_desc": "You receive infinite ammo for all your weapons",
"inheritance": "Inheritance",
"inheritance_desc": "When a teammate dies you can take over their skill",
"inheritance_select_info": "Choose the skill you inherit:",
"inheritance_player_info": "You inherited the skill of '{0}'.",
"jackal": "Tracker",
"jackal_desc": "Every opponent leaves a trail behind them",
@ -448,6 +463,9 @@
"richboy": "Rich Boy",
"richboy_desc": "You receive a random amount of money at the start of the round",
"ricochet": "Ricochet",
"ricochet_desc": "Your bullets bounce off walls and can still hit enemies",
"robinhood": "Robin Hood",
"robinhood_desc": "Dealing damage to an enemy steals their money",

View file

@ -1,4 +1,4 @@
{
{
"none": "Aucune",
"none_desc": "Vous n’avez aucune compétence",
@ -77,6 +77,11 @@
"catapult_desc": "Vous avez une chance aléatoire de projeter un ennemi en l’air",
"catapult_desc2": "Votre chance de projeter un ennemi à l’impact est : {0}%",
"chameleon": "Caméléon",
"chameleon_desc": "Le premier joueur que vous tuez vous donne sa compétence",
"chameleon_copied_info": "Compétence copiée : {0}",
"chameleon_wrong_team_info": "'{0}' avait une compétence que votre équipe ne peut pas utiliser.",
"chicken": "Poulet",
"chicken_desc": "Vous obtenez un modèle de poulet + 10% de vitesse de déplacement en plus - 50 PV",
@ -220,6 +225,11 @@
"godmode_off": "Immortalité désactivée",
"godmode_on": "Immortalité activée",
"grapple": "Grappin",
"grapple_desc": "Appuyez sur [css_useSkill] pour lancer un grappin sur le point visé et vous y propulser",
"grapple_no_anchor_info": "Aucune surface pour accrocher le grappin.",
"grapple_pulling_info": "TRACTION",
"grenadier": "Grenadier",
"grenadier_desc": "Tu as des grenades HE infinies",
@ -259,6 +269,11 @@
"infiniteammo": "Munitions Infinies",
"infiniteammo_desc": "Vous recevez des munitions infinies pour toutes vos armes",
"inheritance": "Héritage",
"inheritance_desc": "Quand un coéquipier meurt, vous pouvez reprendre sa compétence",
"inheritance_select_info": "Choisissez la compétence à hériter :",
"inheritance_player_info": "Vous avez hérité de la compétence de '{0}'.",
"jackal": "Traqueur",
"jackal_desc": "Chaque adversaire laisse une trace derrière lui",
@ -448,6 +463,9 @@
"richboy": "Gosse de Riche",
"richboy_desc": "Vous recevez une somme d’argent aléatoire au début du round",
"ricochet": "Ricochet",
"ricochet_desc": "Vos balles rebondissent sur les murs et peuvent encore toucher les ennemis",
"robinhood": "Robin des Bois",
"robinhood_desc": "Infliger des dégâts à un ennemi lui vole de l’argent",

View file

@ -1,4 +1,4 @@
{
{
"none": "Brak",
"none_desc": "Nie posiadasz żadnej supermocy",
@ -77,6 +77,11 @@
"catapult_desc": "Masz losową szanse na podrzucenie wroga",
"catapult_desc2": "Twoje szanse na podrzucenie wroga po trafieniu to: {0}%",
"chameleon": "Kameleon",
"chameleon_desc": "Pierwszy zabity gracz oddaje ci swoją umiejętność",
"chameleon_copied_info": "Skopiowano umiejętność: {0}",
"chameleon_wrong_team_info": "'{0}' miał umiejętność, której twoja drużyna nie może użyć.",
"chicken": "Kurczak",
"chicken_desc": "Otrzymujesz model kurczaka + jesteś o 10% szybszy - 50hp",
@ -220,6 +225,11 @@
"godmode_off": "Nieśmiertelność wyłączona",
"godmode_on": "Nieśmiertelność włączona",
"grapple": "Hak",
"grapple_desc": "Naciśnij [css_useSkill], aby wystrzelić hak w celowany punkt i przyciągnąć się do niego",
"grapple_no_anchor_info": "Brak powierzchni do zaczepienia haka.",
"grapple_pulling_info": "PRZYCIĄGANIE",
"grenadier": "Granatnik",
"grenadier_desc": "Masz nieskończone granaty odłamkowe",
@ -259,6 +269,11 @@
"infiniteammo": "Nieskończone Ammo",
"infiniteammo_desc": "Otrzymujesz nieskończoną ilość amunicji do wszystkich broni",
"inheritance": "Spadek",
"inheritance_desc": "Gdy członek drużyny ginie, możesz przejąć jego umiejętność",
"inheritance_select_info": "Wybierz dziedziczoną umiejętność:",
"inheritance_player_info": "Odziedziczyłeś umiejętność gracza '{0}'.",
"jackal": "Stópkarz",
"jackal_desc": "Każdy przeciwnik pozostawia za sobą ślad",
@ -448,6 +463,9 @@
"richboy": "Bogacz",
"richboy_desc": "Na początku rundy otrzymujesz losową kwotę gotówki",
"ricochet": "Rykoszet",
"ricochet_desc": "Twoje pociski odbijają się od ścian i nadal mogą trafić przeciwników",
"robinhood": "Robin Hood",
"robinhood_desc": "Zadanie obrażeń przeciwnikowi powoduje kradzież jego pieniędzy",

View file

@ -1,4 +1,4 @@
{
{
"none": "Nenhuma",
"none_desc": "Você não tem nenhuma habilidade",
@ -77,6 +77,11 @@
"catapult_desc": "Você tem uma chance aleatória de lançar um inimigo para cima",
"catapult_desc2": "Sua chance de lançar um inimigo ao acertá-lo é: {0}%",
"chameleon": "Camaleão",
"chameleon_desc": "O primeiro jogador que você matar lhe dá a habilidade dele",
"chameleon_copied_info": "Habilidade copiada: {0}",
"chameleon_wrong_team_info": "'{0}' tinha uma habilidade que sua equipe não pode usar.",
"chicken": "Galinha",
"chicken_desc": "Você é uma galinha com +10% de movimento mais rápido e -50 HP",
@ -220,6 +225,11 @@
"godmode_off": "Imortalidade desativada",
"godmode_on": "Imortalidade ativada",
"grapple": "Gancho",
"grapple_desc": "Pressione [css_useSkill] para lançar um gancho no ponto mirado e se puxar até lá",
"grapple_no_anchor_info": "Nenhuma superfície para prender o gancho.",
"grapple_pulling_info": "PUXANDO",
"grenadier": "Bomberman",
"grenadier_desc": "Você tem HEs infinitas",
@ -259,6 +269,11 @@
"infiniteammo": "Munição Infinita",
"infiniteammo_desc": "Você recebe munição infinita para todas as suas armas",
"inheritance": "Herança",
"inheritance_desc": "Quando um companheiro de equipe morre, você pode assumir a habilidade dele",
"inheritance_select_info": "Escolha a habilidade que você herda:",
"inheritance_player_info": "Você herdou a habilidade de '{0}'.",
"jackal": "Lesma",
"jackal_desc": "Cada adversário deixa um rastro atrás de si",
@ -448,6 +463,9 @@
"richboy": "Filhinho de Papai",
"richboy_desc": "Você recebe uma quantidade aleatória de dinheiro no início da rodada",
"ricochet": "Ricochete",
"ricochet_desc": "Suas balas ricocheteiam nas paredes e ainda podem acertar inimigos",
"robinhood": "Robin Hood",
"robinhood_desc": "Atirar em um inimigo rouba seu dinheiro",

View file

@ -1,4 +1,4 @@
{
{
"none": "Нет навыка",
"none_desc": "У вас нет навыка",
@ -77,6 +77,11 @@
"catapult_desc": "Есть шанс подбросить врага вверх при попадании",
"catapult_desc2": "Ваш шанс подбросить врага при попадании: {0}%",
"chameleon": "Хамелеон",
"chameleon_desc": "Первый убитый вами игрок отдаёт свой навык",
"chameleon_copied_info": "Навык скопирован: {0}",
"chameleon_wrong_team_info": "У '{0}' был навык, недоступный вашей команде.",
"chicken": "Курица",
"chicken_desc": "Вы получаете модель курицы + 10% к скорости передвижения - 50 HP",
@ -220,6 +225,11 @@
"godmode_off": "Неуязвимость отключена",
"godmode_on": "Неуязвимость включена",
"grapple": "Крюк",
"grapple_desc": "Нажмите [css_useSkill], чтобы выстрелить крюком в точку прицеливания и притянуться к ней",
"grapple_no_anchor_info": "Нет поверхности для зацепа.",
"grapple_pulling_info": "ПРИТЯГИВАНИЕ",
"grenadier": "Гренадёр",
"grenadier_desc": "У вас бесконечные осколочные гранаты",
@ -259,6 +269,11 @@
"infiniteammo": "Бесконечные патроны",
"infiniteammo_desc": "Вы получаете бесконечные патроны для всего оружия",
"inheritance": "Наследие",
"inheritance_desc": "Когда союзник погибает, вы можете перенять его навык",
"inheritance_select_info": "Выберите наследуемый навык:",
"inheritance_player_info": "Вы унаследовали навык игрока '{0}'.",
"jackal": "Трекер",
"jackal_desc": "Каждый противник оставляет за собой след",
@ -448,6 +463,9 @@
"richboy": "Богач",
"richboy_desc": "Вы получаете случайную сумму денег в начале раунда",
"ricochet": "Рикошет",
"ricochet_desc": "Ваши пули отскакивают от стен и могут поразить врагов",
"robinhood": "Робин Гуд",
"robinhood_desc": "Нанесение урона врагу крадёт его деньги",

View file

@ -1,4 +1,4 @@
{
{
"none": "Yok",
"none_desc": "Hiçbir yeteneğin yok",
@ -6,79 +6,84 @@
"aimbot_desc": "Vurduğunda kafadan (HS) sayılır",
"aimlock": "Kitlenme",
"aimlock_desc": "En yakın düşmana nişan almak için [css_useSkill] tuşuna basın",
"aimlock_desc": "[css_useSkill] tuşuna basarak en yakın düşmana kitlen",
"anomaly": "Zaman Büken",
"anomaly_desc": "[css_useSkill] tuşuna basarak zamanı birkaç saniye geriye alırsın",
"anomaly_desc": "[css_useSkill] tuşuna basarak zamanı birkaç saniye geri al",
"antyflash": "Anti-Flash",
"antyflash_desc": "Flaşa kör olmazsın ve flaşların 7 saniye sürer",
"antyflash_desc": "Kör olmazsın ve flaşın 7 saniye sürer",
"antyhead": "Beton Kafa",
"antyhead_desc": "Kafadan hasar almazsın",
"areareaper": "Mekan Kapatan",
"areareaper_desc": "Haritadaki bir bomba alanını tamamen devre dışı bırakabilirsin",
"areareaper_desc": "Haritadaki bir bomba alanını devre dışı bırak",
"areareaper_incorrect_site": "Böyle bir bomba alanı yok uydurma",
"areareaper_no_site": "Haritada bomba alanı bulunamadı",
"areareaper_select_info": "Kapatılacak bomba alanını seç:",
"areareaper_site_disabled": "{0} Bölgesi kapandı - artık buraya bomba kurulamaz!",
"areareaper_used_info": "Yeteneğini zaten kullandın, açgözlülük yapma",
"areareaper_select_info": "Alan seçin:",
"areareaper_site_disabled": "{0} Bölgesi kapandı - artık buraya C4 kurulamaz!",
"areareaper_used_info": "Yeteneğini zaten kullandın!",
"areareaper_teammates_info": "{0} Bölgesi bomba kurmaya kapatıldı.",
"areareaper_bombsite_disabled": "Bu bomba alanı kullanıma kapatıldı!",
"areareaper_bombsite_disabled": "Bu bomba alanı kullanıma kapatıldı.",
"armored": "Zırhlı",
"armored_desc": "Rastgele hasar alma çarpanına sahip olursun",
"armored_desc": "Rastgele hasar alma çarpanın olur",
"armored_desc2": "Hasar alma: {0}x",
"assassin": "Suikastçı",
"assassin_desc": "Düşmanlara arkadan daha fazla hasar verirsin",
"assassin_desc": "Düşmana arkadan daha fazla hasar verirsin",
"astronaut": "Astronot",
"astronaut_desc": "Rastgele bir yerçekimi değerine sahip olursun",
"astronaut_desc": "Rastgele bir yerçekimine sahipsin",
"astronaut_desc2": "Yerçekimin: {0}x",
"moneyswap": "Vergi Memuru",
"moneyswap_desc": "Bir düşmanla paranı takasla",
"moneyswap_enemy_info": "Maliye kapına dayandı",
"moneyswap_player_info": "'{0}' isimli oyuncuyla paraları takas ettin",
"moneyswap_select_info": "Para takasla biriyle:",
"moneyswap_enemy_info": "Maliye kapına dayandı.",
"moneyswap_player_info": "'{0}' ile paranı takasladın.",
"moneyswap_select_info": "Birini seçin:",
"baseball": "Beyzbolcu",
"baseball_desc": "Yanıltıcı bomban (decoy) duvarlardan seker ve çarptığı düşmanı öldürür",
"baseball_desc": "Yanıltıcı bomban duvarlardan seker ve çarptığı düşmanı öldürür",
"behind": "Baş Döndüren",
"behind_desc": "Vurduğunda düşmanı 180 derece döndürme şansın olur",
"behind_desc2": "Baş döndürme şansın: %{0}",
"berserker": "Berserker",
"berserker_desc": "Canın azaldıkça hasarın artar ve daha hızlanırsın",
"berserker_desc": "Canın azaldıkça hasarın ve hızın artar",
"blademaster": "Kılıç Ustası",
"blademaster_desc": "Bıçak tutarken mermileri sektirme şansın artar",
"blademaster_desc": "Bıçak tutarken mermileri savuşturursun",
"blastshot": "Patlama Atışı",
"blastshot_desc": "MP5 ile Sağ Tık'a basarak El bombası fırlat",
"blastshot_desc": "MP5 ile sağ tıklayarak el bombası fırlat",
"bunnyhop": "Tavşan",
"bunnyhop_desc": "Otomatik \"BunnyHop\" yaparsın",
"bunnyhop_desc": "Otomatik BunnyHop yaparsın",
"c4camouflage": "C4 Kamuflaj",
"c4camouflage_desc": "Elinde C4 varken tamamen görünmez olursun",
"c4camouflage_desc": "Elinde C4 varken görünmezsin",
"carefulbullets": "Dikkatli Mermiler",
"carefulbullets_desc": "Düşman mermi kaçırırsa hasar alır",
"carefulbullets_enemy_info": "Mermi kaçırmak canını yakacak",
"carefulbullets_enemy_info": "Mermi kaçırman can yakar.",
"carefulbullets_disable_info": "Artık mermi kaçırmak can yakmayacak.",
"carefulbullets_player_info": "'{0}' artık mermi sıkarken iki kere düşünecek",
"carefulbullets_select_info": "İsabetsiz atışların hasar vereceği oyuncu:",
"carefulbullets_player_info": "'{0}' mermi sıkarken iki kere düşünecek.",
"carefulbullets_select_info": "Birini seçin:",
"catapult": "Mancınık",
"catapult_desc": "Vurduğun rakibi uzaya (yukarı doğru) fırlatma şansın var",
"catapult_desc2": "Fırlatma şansın: %{0}",
"chameleon": "Bukalemun",
"chameleon_desc": "Öldürdüğün ilk oyuncunun yeteneği sana geçer",
"chameleon_copied_info": "Yeteneği kopyaladın: {0}",
"chameleon_wrong_team_info": "'{0}' takımının kullanamayacağı bir yeteneğe sahipti.",
"chicken": "Tavuk",
"chicken_desc": "Tavuk modeli ve %10 daha hızlısın fakat 50 HP",
"chicken_desc": "Tavuk modeli, %10 daha hızlı ama 50 HP",
"chillout": "Ağırdan Al",
"chillout_desc": "Bombayı kurmak çok daha uzun sürer",
@ -87,28 +92,28 @@
"cutter_desc": "Bıçakla tek atarsın",
"cypher": "Gözcü",
"cypher_desc": "Kamera oluşturmak/geçmek için [css_useSkill] tuşuna bas",
"cypher_desc": "[css_useSkill] tuşuna basarak kamera kur ve kameraya geç",
"cypher_nospace": "Kamerayı duvara dik yerleştir",
"darkness": "Karanlık",
"darkness_desc": "Bir düşmanın ekranını karart",
"darkness_enemy_info": "Işıklar sönsün",
"darkness_desc": "Düşmanın ekranını karart",
"darkness_enemy_info": "Işıklar sönsün.",
"darkness_disable_info": "Işıklar geldi.",
"darkness_player_info": "'{0}' isimli oyuncunun dünyasını kararttın",
"darkness_select_info": "Karanlık bir oyuncuyu seç:",
"darkness_player_info": "'{0}' ışıkları söndü.",
"darkness_select_info": "Birini seçin:",
"deactivator": "Silgi",
"deactivator_desc": "Bir düşmanın yeteneğini sil",
"deactivator_enemy_info": "Yeteneğin silindi",
"deactivator_player_info": "'{0}' isimli oyuncunun yeteneği silindi",
"deactivator_select_info": "Yetenek silmek için oyuncu seç:",
"deactivator_enemy_info": "Yeteneğin silindi.",
"deactivator_player_info": "'{0}' isimli oyuncunun yeteneği silindi.",
"deactivator_select_info": "Birini seçin:",
"deaf": "Sağır",
"deaf_desc": "Bir düşmanın kulağını kapat ve sesleri duymaz hale getir",
"deaf_enemy_info": "Kulaklıkların tıkandı",
"deaf_desc": "Bir düşmanı sağır et",
"deaf_enemy_info": "Kulaklıkların tıkandı.",
"deaf_disable_info": "Kulakların açıldı.",
"deaf_player_info": "'{0}' isimli oyuncu için sesler kapatıldı.",
"deaf_select_info": "Sağır bir oyuncuyu seç:",
"deaf_player_info": "'{0}' sesleri kapatıldı.",
"deaf_select_info": "Birini seçin:",
"disarmament": "Silahsızlandırma",
"disarmament_desc": "Vurduğunda rakibin elindeki silahı yere düşürme şansın olur",
@ -121,15 +126,15 @@
"dash_desc": "Atılmak için ikinci kez zıpla",
"dracula": "Drakula",
"dracula_desc": "Rakiplere verdiğin hasarın belirli bir yüzdesi kadar can alırsın",
"dracula_desc": "Rakibe verdiğin hasarın birazı kadar can alırsın",
"duplicator": "Kopyacı",
"duplicator_desc": "Yeteneğini kopyalamak için bir oyuncu seç",
"duplicator_player_info": "'{0}' isimli oyuncunun yeteneği kopyalandı.",
"duplicator_select_info": "Yeteneği kopyalanacak oyuncu:",
"duplicator_desc": "Bir düşmanın yeteneğini kopyala",
"duplicator_player_info": "'{0}' yeteneği kopyalandı.",
"duplicator_select_info": "Birini seçin:",
"dwarf": "Cüce",
"dwarf_desc": "Raund başında rastgele karakter boyutu",
"dwarf_desc": "Rastgele karakter boyutu",
"dwarf_desc2": "Boyutun: {0}x",
"enemyspawn": "Base Baskını",
@ -137,16 +142,16 @@
"expensiveammo": "Pahalı Mermi",
"expensiveammo_desc": "Seçtiğin rakip her ateş ettiğinde para kaybeder",
"expensiveammo_disable_info": "Mermilerin yeniden bedava.",
"expensiveammo_enemy_info": "Artık her atışın sana {0}$ mal oluyor.",
"expensiveammo_player_info": "'{0}' artık her mermisinin parasını ödüyor.",
"expensiveammo_select_info": "Mermisi pahalanacak oyuncu:",
"expensiveammo_disable_info": "Mermi yeniden bedava.",
"expensiveammo_enemy_info": "Her atışın sana {0}$ mal oluyor.",
"expensiveammo_player_info": "'{0}' her mermisinin parasını ödüyor.",
"expensiveammo_select_info": "Birini seçin:",
"explodingbarrel": "Patlayan Varil",
"explodingbarrel_desc": "[css_useSkill] ile vurulunca patlayan bir varil koyarsın",
"explodingbarrel_desc": "[css_useSkill] tuşuna basarak vurulunca patlayan varil koy",
"explosiveshot": "Patlayıcı Mermi",
"explosiveshot_desc": "Ateş ederken rastgele mermilerin patlayabilir",
"explosiveshot_desc": "Ateş ederken mermin patlayabilir",
"explosiveshot_desc2": "Patlayan mermi şansı: %{0}",
"falconeye": "Şahin Gözü",
@ -156,31 +161,31 @@
"fastreload_desc": "[css_useSkill] tuşuna basarak şarjörünü hızlıca değiştir",
"firerain": "Ateş Yağmuru",
"firerain_desc": "Molotof yağdırmak için bir decoy at",
"firerain_desc": "Molotof yağdırmak için yanıltıcı bomba at",
"flash": "Flash",
"flash_desc": "Raund başında rastgele oyuncu hızı",
"flash_desc": "Rastgele oyuncu hızı",
"flash_desc2": "Hızın: {0}x",
"flashlight": "El Feneri",
"flashlight_desc": "[css_useSkill] tuşuna basarak El fenerini açarsın/kaparsın (Işığı düşmanları kör edebilir)",
"flashlight_desc": "[css_useSkill] tuşuna basarak el fenerini aç veya kapat, ışığı düşmanı kör eder",
"fortnite": "Fortnite",
"fortnite_desc": "[css_useSkill] tuşuna basarak barikat kur",
"fragilebomb": "Hassas C4",
"fragilebomb_desc": "Kurulu C4'e ateş etmek C4'ün canını azaltır ve C4 imha edilir.",
"fragilebomb_desc": "Kurulu C4'e ateş etmek canını azaltır ve C4'ü imha eder",
"fragilebomb_bomb_health": "Bombanın canı",
"friendlyfire": "Dost Şifası",
"friendlyfire_desc": "Takım arkadaşlarına ateş etmek onları iyileştirir",
"friendlyfire_desc": "Takım arkadaşına ateş etmek onu iyileştirir",
"frozendecoy": "Buz Kıran",
"frozendecoy_desc": "Yanıltıcı bomba (decoy) yakındaki tüm oyuncuları dondurur",
"frozendecoy_desc": "Yanıltıcı bomban yakındaki herkesi yavaşlatır",
"gambler": "Kumarbaz",
"gambler_desc": "Sunulan listeden bir yetenek seç.",
"gambler_select_info": "Yeteneğini seç:",
"gambler_select_info": "Birini seçin:",
"gambler_more": "#00FF00|${0} yenile",
"gambler_no_money": "Yeterli para yok.",
@ -188,106 +193,116 @@
"ghost_desc": "Tamamen görünmezsin",
"giant": "Dev",
"giant_desc": "Seçtiğin bir rakibi büyütür",
"giant_desc2": "Seçtiğin bir rakibi {0}x büyütür",
"giant_disable_info": "Normal boyutuna geri döndün.",
"giant_enemy_info": "{0}x boyutuna büyütüldün.",
"giant_player_info": "'{0}' oyuncusunu {1}x büyüttün.",
"giant_select_info": "Büyütülecek oyuncu:",
"giant_desc": "Seçtiğin rakibi büyüt",
"giant_desc2": "Rakibin boyutu: {0}x",
"giant_disable_info": "Normal boyutuna döndün.",
"giant_enemy_info": "{0}x boyutuna büyüdün.",
"giant_player_info": "'{0}' boyutunu {1}x büyüttün.",
"giant_select_info": "Birini seçin:",
"glaz": "Glaz",
"glaz_desc": "Sis bombalarının içini görebilirsin",
"glaz_desc": "Sis bombasının içini görürsün",
"glitch": "Glitç",
"glitch_desc": "Seçilen bir düşmanın radarını kapatır",
"glitch_enemy_info": "Radarın kapandı",
"glitch_disable_info": "Radarın açıldı",
"glitch_player_info": "'{0}' isimli oyuncunun radarı devre dışı bırakıldı.",
"glitch_select_info": "Radarı kapatılacak oyuncuyu seç:",
"glitch_desc": "Bir düşmanın radarını kapat",
"glitch_enemy_info": "Radarın kapandı.",
"glitch_disable_info": "Radarın açıldı.",
"glitch_player_info": "'{0}' radarı kapandı.",
"glitch_select_info": "Birini seçin:",
"glue": "Yapıştırıcı",
"glue_desc": "Bombaların duvarlara yapışır",
"glue_desc": "Bomban duvara yapışır",
"godmode": "Ölümsüz",
"godmode_desc": "[css_useSkill] tuşuna basarak kısa süreli ölümsüz ol",
"godmode_off": "Ölümsüzlük kapandı",
"godmode_on": "Ölümsüzlük açıldı",
"grapple": "Kanca",
"grapple_desc": "[css_useSkill] tuşuna basarak baktığın yere kanca at",
"grapple_no_anchor_info": "Kanca atılacak yüzey yok",
"grapple_pulling_info": "ÇEKİLİYOR",
"grenadier": "Bombacı",
"grenadier_desc": "Sınırsız el bombasına sahipsin",
"grenadier_desc": "El bomban sınırsız",
"healingchicken": "Şifacı Tavuk",
"healingchicken_desc": "Yakınındayken tavukların seni iyileştirir",
"healingchicken_desc": "Yakınındayken tavuğun seni iyileştirir",
"healingsmoke": "Şifalı Sis",
"healingsmoke_desc": "Sis bombaların iyileştirir",
"healingsmoke_desc": "Sis bomban iyileştirir",
"hermit": "Münzevi",
"hermit_desc": "Birini öldürünce mermin ve canın yenilenir",
"holyhandgrenade": "Kutsal El Bombası",
"holyhandgrenade_desc": "El bomban iki kat hasara ve menzile sahip",
"holyhandgrenade_desc": "El bombanın hasarı ve menzili iki kat",
"homingnades": "Güdümlü Bomba",
"homingnades_desc": "Bombalarınız (sis hariç) düşmanlara yönelir",
"homingnades_desc": "Bomban (sis hariç) yakın düşmana yönelir",
"hotbomb": "Sıcak C4",
"hotbomb_desc": "Yaşadığın sürece bombacının canı azalıyor",
"hotbomb_hint": "Bomba çok sıcak!",
"hotbomb_disable_info": "Bomba artık yakmıyor.",
"hotbomb_desc": "Yaşadığın sürece bombacının canı azalır",
"hotbomb_hint": "C4 ÇOK SICAK!",
"hotbomb_disable_info": "C4 soğudu.",
"iana": "Hologram",
"iana_desc": "[css_useSkill] tuşuna basarak bir hologram oluştur",
"illiterate": "Cahiliye",
"illiterate_desc": "Yaşadığın sürece rakibin sohbeti (chat) veya oyun yazılarını okuyamaz",
"illiterate_desc": "Yaşadığın sürece rakip hiçbir yazıyı 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",
"impostor": "Sahtekar",
"impostor_desc": "Düşman oyuncu modeline dönüşürsün",
"impostor_desc": "Düşman modeline dönüşürsün",
"infiniteammo": "Sınırsız Mermi",
"infiniteammo_desc": "Tüm silahların sınırsız mermisi olur",
"infiniteammo_desc": "Silahlarının mermisi sınırsız",
"inheritance": "Miras",
"inheritance_desc": "Bir takım arkadaşın öldüğünde yeteneğini devralabilirsin",
"inheritance_select_info": "Birini seçin:",
"inheritance_player_info": "'{0}' yeteneğini devraldın.",
"jackal": "İzci",
"jackal_desc": "Her rakip yürüdüğünde ayak izi bırakır",
"jammer": "Sinyal Kesici",
"jammer_desc": "Bir oyuncunun nişangahını (crosshair) kapatmak için seç",
"jammer_desc": "Bir düşmanın nişangahını kapat",
"jammer_enemy_info": "Nişangahın kapatıldı.",
"jammer_disable_info": "Nişangahın açıldı",
"jammer_disable_info": "Nişangahın açıldı.",
"jammer_player_info": "'{0}' isimli oyuncunun nişangahı kapatıldı.",
"jammer_select_info": "Nişangahı kapatılacak oyuncuyu seç:",
"jammer_select_info": "Birini seçin:",
"jester": "Soytarı",
"jester_desc": "Soytarı modunda hasar alamazsın ve veremezsin. Bu mod birkaç saniyede bir değişir.",
"jester_desc": "Soytarı modunda hasar alamaz ve veremezsin, mod birkaç saniyede bir değişir",
"jester_mode": "Soytarı Modu",
"jester_on": "AÇIK",
"jester_off": "KAPALI",
"jetkick": "Jetkick",
"jetkick_desc": "Seçtiğin bir rakibin her atışı onu geriye iter",
"jetkick_select_info": "Jetkick uygulanacak oyuncu:",
"jetkick_desc": "Seçtiğin rakibi her atışı geriye iter",
"jetkick_select_info": "Birini seçin:",
"jetkick_player_info": "'{0}' adlı oyuncuya jetkick uygulandı.",
"jetkick_enemy_info": "Her atış seni geriye doğru iter",
"jetkick_disable_info": "Geri tepmen normale döndü.",
"jumpban": "Bacaksız",
"jumpban_desc": "Zıplayamayacak bir oyuncu seç",
"jumpban_enemy_info": "Bacakların kesildi",
"jumpban_disable_info": "Bacaklarına kavuştun",
"jumpban_desc": "Zıplayamayacak bir düşman seç",
"jumpban_enemy_info": "Bacakların kesildi.",
"jumpban_disable_info": "Tekrar zıplayabilirsin.",
"jumpban_player_info": "'{0}' artık zıplayamaz.",
"jumpban_select_info": "Zıplaması yasaklanacak oyuncu:",
"jumpban_select_info": "Birini seçin:",
"jumpcurse": "Zıplama Laneti",
"jumpcurse_desc": "Seçtiğin rakip, takım arkadaşları her zıpladığında zıplar",
"jumpcurse_disable_info": "Bacakların yeniden senin.",
"jumpcurse_enemy_info": "Artık takım arkadaşların zıpladıkça sen de zıplıyorsun.",
"jumpcurse_player_info": "'{0}' artık takımıyla birlikte zıplıyor.",
"jumpcurse_select_info": "Zıplama laneti verilecek oyuncu:",
"jumpcurse_desc": "Seçtiğin rakip, takım arkadaşlarıyla zıpladıkça zıplar",
"jumpcurse_disable_info": "Kendin için zıplayabilirsin.",
"jumpcurse_enemy_info": "Takım arkadaşların zıpladıkça sen de zıplıyorsun.",
"jumpcurse_player_info": "'{0}' takımıyla birlikte zıplıyor.",
"jumpcurse_select_info": "Birini seçin:",
"jumpingjack": "Zıpzıp Jack",
"jumpingjack_desc": "Zıplamak canını yeniler",
@ -302,10 +317,10 @@
"lastgasp_desc": "Öldükten sonra seni öldürene hasar verirsin",
"lifeswap": "Can Takası",
"lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç",
"lifeswap_enemy_info": "Birisi canını çaldı",
"lifeswap_player_info": "'{0}' isimli oyuncuyla canlarını takas ettin.",
"lifeswap_select_info": "Can takası yapılacak oyuncu:",
"lifeswap_desc": "Bir düşmanla canını takasla",
"lifeswap_enemy_info": "Birisi canını çaldı.",
"lifeswap_player_info": "'{0}' ile canını takasladın.",
"lifeswap_select_info": "Birini seçin:",
"longknife": "Uzun Bıçak",
"longknife_desc": "Bıçağın uzaktaki rakibe hasar verebilir",
@ -314,46 +329,46 @@
"longzeus_desc": "Zeus uzaktaki rakibe hasar verebilir",
"magneticdecoy": "Manyetik Yanıltıcı",
"magneticdecoy_desc": "Yanıltıcı bombanız yakındaki oyuncuları kendine çeker",
"magneticdecoy_desc": "Yanıltıcı bomban yakındaki herkesi kendine çeker",
"magneto": "Mıknatıs",
"magneto_desc": "Tüm düşman bombaları sizden uzaklaştırılır",
"magneto_desc": "Tüm düşman bombası senden uzaklaşır",
"magnifier": "Büyüteç",
"magnifier_desc": "Düşmanın ekranını yakınlaştırarak görüş alanını daraltır",
"magnifier_desc": "Bir düşmanın görüş alanını daralt",
"magnifier_enemy_info": "Görüşün bozuldu!",
"magnifier_disable_info": "Görüşün düzeldi",
"magnifier_player_info": "'{0}' isimli oyuncunun ekranı yakınlaştırıldı.",
"magnifier_select_info": "Ekranı büyütülecek oyuncuyu seç:",
"magnifier_select_info": "Birini seçin:",
"medic": "Sıhhiye",
"medic_desc": "[css_useSkill] tuşuna basarak kendine anında 50 HP bas",
"miner": "Mayın",
"miner_desc": "El bombanız (HE) sadece yakında bir düşman olduğunda patlar",
"miner_desc": "El bomban sadece yakında düşman varken patlar",
"bankrupt": "İflas",
"bankrupt_desc": "Tüm parasını kaybedecek bir oyuncu seç",
"bankrupt_desc": "Tüm parasını kaybedecek bir düşman seç",
"bankrupt_enemy_info": "İflas ettin!",
"bankrupt_player_info": "'{0}' isimli oyuncu iflas etti.",
"bankrupt_select_info": "İflas edecek oyuncuyu seç:",
"bankrupt_player_info": "'{0}' iflas etti.",
"bankrupt_select_info": "Birini seçin:",
"deathbomb": "Canlı Bomba",
"deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün",
"deathbomb_desc": "Öldüğünde patlayıp etrafındakilere hasar verirsin",
"deathbomb_explosion": "BOOM!!!",
"demoneye": "Şeytani Göz",
"demoneye_desc": "Baktığın tüm düşmanlara hasar verirsin",
"demoneye_desc": "Baktığın tüm düşmana hasar verirsin",
"nightmare": "Kabus",
"nightmare_desc": "Seçtiğin bir rakibe kabus görüntüsü verir",
"nightmare_desc": "Seçtiğin rakibe kabus gördür",
"nightmare_disable_info": "Kabus sona erdi.",
"nightmare_enemy_info": "Gözlerini bir kabus bürüdü.",
"nightmare_player_info": "'{0}' oyuncusuna kabus gördürdün.",
"nightmare_select_info": "Kabus görecek oyuncu:",
"nightmare_select_info": "Birini seçin:",
"ninja": "Ninja",
"ninja_desc": "Görünmezliğini sabit durmak %33, eğilmek %33 ve bıçak tutmak %33 artırır",
"ninja_desc": "Sabit durmak, eğilmek ve bıçak tutmak görünmezliğini %33 artırır",
"nonades": "Bomba Geçirmez",
"nonades_desc": "Bombalar sana hasar vermez",
@ -375,38 +390,38 @@
"paweljumper_desc2": "{0} kez ekstra zıplayabilirsin",
"phoenix": "Anka Kuşu",
"phoenix_desc": "Öldükten sonra rastgele yeniden doğma şansın olur",
"phoenix_desc": "Öldükten sonra yeniden doğma şansın olur",
"phoenix_desc2": "Yeniden doğma şansın: %{0}",
"phoenix_respawn": "Küllerinden yeniden doğdun!",
"psychicdefusing": "Psişik İmha",
"psychicdefusing_desc": "Bombanın yanındayken onu otomatik imha etmeye başlarsın",
"psychicdefusing_desc": "Bombanın yanındayken otomatik imhaya başlarsın",
"psychicdefusing_hud_info": "Bombanın çözülmesi için son {0} saniye",
"pilot": "Pilot",
"pilot_desc": "Sınırlı bir süre uçun. Uçmak için zıplama tuşuna iki kez basın",
"pilot_desc": "Uçmak için zıplama tuşuna iki kez bas",
"pilot_hud_info": "Yakıt",
"planter": "Serbest Kurucu",
"planter_desc": "Bombayı her yere kurabilirsin, patlama süresi 60 saniyedir.",
"planter_desc": "Bombayı her yere kurabilirsin, patlama süresi 60 saniye",
"planter_planting": "Kuruluyor ({0})...",
"poison": "Zehir",
"poison_desc": "Birkaç saniyede bir hasar alacak bir oyuncu seç",
"poison_desc": "Birkaç saniyede bir hasar alacak bir düşman seç",
"poison_enemy_info": "Zehirlendin.",
"poison_disable_info": "Zehire bağışıklık kazandın",
"poison_player_info": "'{0}' isimli oyuncu zehirlendi.",
"poison_select_info": "Zehirlenecek oyuncuyu seç:",
"poison_select_info": "Birini seçin:",
"primaryban": "Tüfek Yasak",
"primaryban_desc": "Tüfek kullanamayacak bir oyuncu seç",
"primaryban_desc": "Tüfek kullanamayacak bir düşman seç",
"primaryban_enemy_info": "Tüfek kullanmayı unuttun",
"primaryban_disable_info": "Tüfek kullanabilirsin",
"primaryban_player_info": "'{0}' artık tüfek kullanamaz.",
"primaryban_select_info": "Tüfek kullanımı yasaklanacak oyuncu:",
"primaryban_select_info": "Birini seçin:",
"prosthesis": "Protez",
"prosthesis_desc": "Kollar ve bacaklar kurşun geçirmezdir",
"prosthesis_desc": "Kolların ve bacakların kurşun geçirmez",
"push": "İtici",
"push_desc": "Düşmana vurduğunda onu geri itme şansın olur",
@ -422,43 +437,46 @@
"radarhack_desc": "Düşmanlar radarda görünür",
"rambo": "Rambo",
"rambo_desc": "Raund başında rastgele miktarda can alırsın",
"rambo_desc": "Rastgele miktarda canın olur",
"randomweapon": "Silah Kumarı",
"randomweapon_desc": "[css_useSkill] tuşuna basarak rastgele bir silah al",
"rezombie": "Zombi",
"rezombie_desc": "Öldükten sonra daha fazla canla ve silahsız zombi olarak doğarsın",
"rezombie_desc": "Öldüğünde silahsız zombi olarak daha çok canla dirilirsin",
"reactivearmor": "Reaktif Zırh",
"reactivearmor_desc": "Zırhın alınan ilk hasarı emmer",
"reactivearmor_desc": "Zırhın ilk hasarı emer",
"regeneration": "Yenilenme",
"regeneration_desc": "Birkaç saniyede bir canın yenilenir",
"replicator": "Klon",
"replicator_desc": "[css_useSkill] tuşuna basarak klon oluştur ve öldürene hasar verir",
"replicator_desc": "[css_useSkill] tuşuna basarak klon oluştur, klonu öldürene hasar verir",
"retreat": "Geri Çekil",
"retreat_desc": "[css_useSkill] tuşuna basarak başlangıç noktasına ışınlan",
"retreat_desc": "[css_useSkill] tuşuna basarak başlangıcına ışınlan",
"returntosender": "İade",
"returntosender_desc": "Düşmana ilk vuruşun onu kendi başlangıç noktasına ışınlar",
"returntosender_desc": "Düşmana ilk vuruşun onu kendi başlangıcına ışınlar",
"richboy": "Zengin Çocuk",
"richboy_desc": "Raund başında rastgele miktarda para alırsın",
"richboy_desc": "Rastgele miktarda paran olur",
"ricochet": "Seken Mermi",
"ricochet_desc": "Mermin duvardan seker ve düşmana isabet edebilir",
"robinhood": "Robin Hood",
"robinhood_desc": "Düşmana hasar vermek onun parasını çalar",
"rubber": "Lastik Mermi",
"rubber_desc": "Mermilerin oyuncuları önemli ölçüde yavaşlatır",
"rubber_desc": "Mermin isabet edeni yavaşlatır",
"saper": "İstihkamcı",
"saper_desc": "Bombayı daha hızlı kurabilir ve imha edebilirsin",
"saper_desc": "Bombayı daha hızlı kurar ve imha edersin",
"secondlife": "İkinci Şans",
"secondlife_desc": "Öldükten sonra aynı miktarda canla yeniden doğarsın",
"secondlife_desc": "Öldüğünde aynı canla yeniden doğarsın",
"shade": "Gölge",
"shade_desc": "Vurduğun düşmanın arkasına ışınlanma şansın olur",
@ -466,24 +484,24 @@
"shade_nospace": "Uygun alan yok",
"shortbomb": "Kısa Fünye",
"shortbomb_desc": "Bomba çok daha hızlı patlar",
"shortbomb_desc": "C4 çok daha hızlı patlar",
"silent": "Sessiz",
"silent_desc": "Ayak seslerini ve zıplamalarını DİĞER oyuncular duyamaz",
"silent_desc": "Ayak ve zıplama sesini kimse duyamaz",
"smoker": "Tiryaki",
"smoker_desc": "Attığın Sis asla bitmez",
"smoker_desc": "Attığın sis asla bitmez",
"sniperelite": "Keskin Nişancı",
"sniperelite_customname": "Keskin Nişancı",
"sniperelite_desc": "[css_useSkill] tuşuna basarak silahını AWP ile değiştir",
"soldier": "Teğmen",
"soldier_desc": "Rastgele bir oranda fazla hasar verirsin",
"soldier_desc": "Rastgele hasar verme çarpanın olur",
"soldier_desc2": "Hasarın: {0}x",
"soundmaker": "Çığlık",
"soundmaker_desc": "Rakiplerinin çığlıklarını duyarsın",
"soundmaker_desc": "Rakibin çığlığını duyarsın",
"spectator": "Röntgen",
"spectator_desc": "[css_useSkill] tuşuna basarak rakibinin gözünden gör",
@ -492,47 +510,47 @@
"swapposition_desc": "[css_useSkill] tuşuna basarak vurduğun düşmanla yer değiştir",
"takeammo": "Cephane Hırsızı",
"takeammo_desc": "[css_useSkill] tuşuna basarak rastgele bir düşmanın silahının şarjörünü çalarsın",
"takeammo_hud_info1": "Cephanesi olan bir oyuncu bulunamadı.",
"takeammo_hud_info2": "Düşmanın cephanesi yok.",
"takeammo_enemy_info": "Birisi cephaneni çaldı.",
"takeammo_desc": "[css_useSkill] tuşuna basarak rastgele bir düşmanın şarjörünü çal",
"takeammo_hud_info1": "Şarjörü olan bir oyuncu bulunamadı.",
"takeammo_hud_info2": "Düşmanın şarjörü yok.",
"takeammo_enemy_info": "Birisi şarjörünü çaldı.",
"teamteleport": "Takıma Işınlanma",
"teamteleport_desc": "[css_useSkill] tuşuna basarak baktığın takım arkadaşına ışınlanırsın",
"teamteleport_desc": "[css_useSkill] tuşuna basarak baktığın takım arkadaşına ışınlan",
"teamteleport_noenemy": "Takım arkadaşı bulunamadı",
"teamteleport_hud_info": "Bulundu: {0}",
"teleporter": "Işınlayıcı",
"teleporter_desc": "Vurulan bir düşmanla yer değiştirme şansınız var",
"teleporter_desc": "Vurduğun düşmanla yer değiştirme şansın olur",
"teleporter_desc2": "Yer değiştirme şansın: %{0}",
"thief": "Hırsız",
"thief_desc": "Seçilen bir oyuncudan yeteneğini çalabilirsin",
"thief_desc": "Bir düşmanın yeteneğini çal",
"thief_enemy_info": "Yeteneğin çalındı.",
"thief_player_info": "'{0}' isimli oyuncunun yeteneği çalındı.",
"thief_select_info": "Yetenek çalınacak oyuncu:",
"thief_select_info": "Birini seçin:",
"thief_incorrect_skill": "Bu yetenek çalınamaz!",
"thirdeye": "Üçüncü Göz",
"thirdeye_desc": "[css_useSkill] tuşuna basarak üçüncü göze(thirdperson) geç",
"thirdeye_desc": "[css_useSkill] tuşuna basarak üçüncü şahıs görünümüne geç",
"thorns": "Dikenler",
"thorns_desc": "Rakibin, sana verdiği hasarın bir kısmını geri alır",
"thorns_desc": "Sana verilen hasarın bir kısmı rakibe döner",
"throwingknife": "Fırlatma Bıçağı",
"throwingknife_desc": "Bıçak fırlatmak için [css_useSkill] tuşuna basın. Ama başkalarına dikkat edin",
"throwingknife_desc": "[css_useSkill] tuşuna basarak bıçak fırlat",
"toxicsmoke": "Zehirli Sis",
"toxicsmoke_desc": "Sis bombaların hasar verir",
"toxicsmoke_desc": "Sis bomban hasar verir",
"tripwire": "Tel Tuzağı",
"tripwire_desc": "[css_useSkill] ile iki duvar arasına tel ger. Tele değen düşman radarında görünür",
"tripwire_desc": "[css_useSkill] tuşuna basarak iki duvar arasına tel ger, değen düşman radarda görünür",
"tripwire_no_wall_info": "İki yanında da yeterince yakın duvar yok.",
"tripwire_placed_info": "Tel gerildi.",
"tripwire_triggered_info": "'{0}' teline takıldı.",
"wallhack": "Wallhack",
"wallhack_desc": "Düşmanları duvarların arkasından görebilirsin",
"wallhack_desc": "Düşmanları duvar arkasından görürsün",
"watchmaker": "Saat Ustası",
"watchmaker_desc": "Atılan her bomba raund süresini değiştirir",
@ -544,21 +562,21 @@
"weaponsswap_hud_info2": "Takas edilecek silahın yok",
"weightless": "Yerçekimsiz",
"weightless_desc": "El bombalarınız yerçekiminden etkilenmez ve dümdüz gider",
"weightless_desc": "El bomban yerçekiminden etkilenmez ve dümdüz gider",
"wildthrow": "Unutkan Bombacı",
"wildthrow_desc": "El bombası atmakta zorluk çekecek bir oyuncu seçin",
"wildthrow_player_info": "'{0}' adlı oyuncu tüm taktiklerini unuttu",
"wildthrow_desc": "El bombası atmakta zorlanacak bir düşman seç",
"wildthrow_player_info": "'{0}' tüm taktiklerini unuttu.",
"wildthrow_enemy_info": "Bomba atarken çok heyecanlanıyorsun.",
"wildthrow_disable_info": "Bombaların düzeldi.",
"wildthrow_select_info": "Bir oyuncu seçin:",
"wildthrow_disable_info": "Bomban tekrardan düzgün.",
"wildthrow_select_info": "Birini seçin:",
"zeus": "Zeus",
"zeus_desc": "Zeus x27 anında şarj olur",
"your_skill": "Yeteneğin",
"enemy_skill": "Düşmanın yeteneği",
"observer_skill": "Oyuncunun yeteneği",
"observer_skill": "Yeteneği",
"welcome_message": "{PLAYER}, {SERVER_NAME} sunucusuna hoş geldin!\nGüncel jRandomSkills versiyonu: {VERSION} ({SKILLS_COUNT} yetenek).\n\nOrijinal yapımcı:\n{AUTHOR1}\nGeliştiren:\n{AUTHOR2}\nResmi Discord: https://discord.gg/9H8EZYBpPF",
"drawing_skill": "Yetenek çarkı dönüyor...",
"disabled_weapon": "Bu silahı kullanamazsın",

View file

@ -71,6 +71,11 @@
"catapult_desc": "你有随机几率将敌人向上弹射",
"catapult_desc2": "你击中敌人将其弹射的几率是:{0}%",
"chameleon": "变色龙",
"chameleon_desc": "你击杀的第一名玩家会把技能传给你",
"chameleon_copied_info": "已复制技能:{0}",
"chameleon_wrong_team_info": "'{0}' 的技能你的队伍无法使用。",
"chicken": "小鸡",
"chicken_desc": "你获得小鸡模型 + 移动速度提高10% - 50生命值",
@ -201,6 +206,11 @@
"godmode_off": "无敌状态已禁用",
"godmode_on": "无敌状态已启用",
"grapple": "钩爪",
"grapple_desc": "按 [css_useSkill] 向准星所指位置发射钩爪并将自己拉过去",
"grapple_no_anchor_info": "没有可以钩住的表面。",
"grapple_pulling_info": "拉动中",
"grenadier": "投弹手",
"grenadier_desc": "你拥有无限高爆手雷",
@ -237,6 +247,11 @@
"infiniteammo": "无限弹药",
"infiniteammo_desc": "你为所有武器获得无限弹药",
"inheritance": "继承",
"inheritance_desc": "当队友阵亡时,你可以接管他的技能",
"inheritance_select_info": "选择要继承的技能:",
"inheritance_player_info": "你继承了 '{0}' 的技能。",
"jackal": "追踪者",
"jackal_desc": "每个对手都会留下痕迹",
@ -418,6 +433,9 @@
"richboy": "富少",
"richboy_desc": "回合开始时你获得随机数量的钱",
"ricochet": "跳弹",
"ricochet_desc": "你的子弹会从墙上弹开,并且仍能击中敌人",
"robinhood": "罗宾汉",
"robinhood_desc": "对敌人造成伤害会偷取他们的钱",

View file

@ -4,7 +4,6 @@ using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
using System.Collections.Concurrent;
using static CounterStrikeSharp.API.Core.Listeners;
using static src.jRandomSkills;
@ -96,33 +95,9 @@ namespace src.player
WriteToDebug($"Map changed: {mapName}.");
});
Instance.RegisterEventHandler<EventPlayerShoot>((@event, info) =>
{
var player = PlayerManager.GetPlayerEvent(@event.Userid);
if (player == null || !player.IsValid) return HookResult.Continue;
WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} fired a shot.");
return HookResult.Continue;
});
Instance.RegisterEventHandler<EventPlayerHurt>((@event, info) =>
{
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
if (victim == null || !victim.IsValid) return HookResult.Continue;
if (!lastNativeHitGroup.TryRemove(victim.Index, out var native)) return HookResult.Continue;
if (native.HitGroup == @event.Hitgroup) return HookResult.Continue;
WriteToDebug($"[HITGROUP] mismatch on {victim.PlayerName}: native={(HitGroup_t)native.HitGroup} event={(HitGroup_t)@event.Hitgroup} " +
$"weapon={@event.Weapon} rawDmg={native.Damage:0.#} appliedDmg={@event.DmgHealth}");
return HookResult.Continue;
});
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamage, HookMode.Pre);
}
private static readonly ConcurrentDictionary<uint, (int HitGroup, float Damage)> lastNativeHitGroup = [];
public static void Unload()
{
try { VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre); }
@ -131,16 +106,6 @@ namespace src.player
lock (_writeLock) { _writer?.Dispose(); _writer = null; }
}
private static string DescribeIdentitySplit(CCSPlayerController victim)
{
var routed = PlayerManager.GetPlayerEvent(victim);
uint routedIndex = routed?.Index ?? victim.Index;
if (routedIndex == victim.Index) return string.Empty;
return $" SPLIT(idx {victim.Index}->{routedIndex}, skill {PlayerManager.GetPlayerByIndex(victim.Index)?.Skill}" +
$"->{PlayerManager.GetPlayerByIndex(routedIndex)?.Skill}, controllingBot={victim.ControllingBot})";
}
private static HookResult OnTakeDamage(DynamicHook h)
{
CEntityInstance param = h.GetParam<CEntityInstance>(0);
@ -165,12 +130,10 @@ namespace src.player
if (playerInfo == null) return HookResult.Continue;
var nativeHitGroup = SkillUtils.GetHitGroup(param2);
lastNativeHitGroup[victim.Index] = ((int)nativeHitGroup, param2.Damage);
WriteToDebug($"{(victim.IsBot ? "Bot" : "Player")} {victim.PlayerName} took damage from {(attacker.IsBot ? "bot" : "player")} {attacker.PlayerName}. " +
$"[dmg={param2.Damage:0.#} hp={victimPawn.Health}/{victimPawn.MaxHealth} armor={victimPawn.ArmorValue} hitgroup={nativeHitGroup} " +
$"takes={victimPawn.TakesDamage} vskill={PlayerManager.GetPlayerByIndex(victim.Index)?.Skill} askill={playerInfo.Skill}" +
$"{DescribeIdentitySplit(victim)}]");
$"takes={victimPawn.TakesDamage} vskill={PlayerManager.GetPlayerByIndex(victim.Index)?.Skill} askill={playerInfo.Skill}]");
return HookResult.Continue;
}

View file

@ -79,6 +79,7 @@ public enum Skills
C4Camouflage,
CarefulBullets,
Catapult,
Chameleon,
Chicken,
ChillOut,
Cutter,
@ -114,6 +115,7 @@ public enum Skills
Glitch,
Glue,
GodMode,
Grapple,
Grenadier,
HealingChicken,
HealingSmoke,
@ -126,6 +128,7 @@ public enum Skills
Illusionist,
Impostor,
InfiniteAmmo,
Inheritance,
Jackal,
Jammer,
Jester,
@ -173,6 +176,7 @@ public enum Skills
Retreat,
ReturnToSender,
RichBoy,
Ricochet,
RobinHood,
Rubber,
Saper,

View file

@ -226,8 +226,11 @@ namespace src.player
if (controller == null || !controller.IsValid) return string.Empty;
uint routedIndex = PlayerManager.GetPlayerEvent(controller)?.Index ?? controller.Index;
return $" on {controller.PlayerName} [idx={controller.Index} skill={PlayerManager.GetPlayerByIndex(controller.Index)?.Skill}" +
$" routedIdx={routedIndex} routedSkill={PlayerManager.GetPlayerByIndex(routedIndex)?.Skill}]";
string routing = routedIndex == controller.Index
? string.Empty
: $" routedIdx={routedIndex} routedSkill={PlayerManager.GetPlayerByIndex(routedIndex)?.Skill}";
return $" on {controller.PlayerName} [idx={controller.Index} skill={PlayerManager.GetPlayerByIndex(controller.Index)?.Skill}{routing}]";
}
catch
{

View file

@ -15,7 +15,7 @@ namespace src.player
Instance.RegisterListener<OnTick>(() =>
{
UpdateGameRules();
if (Server.TickCount % 2 != 0) return;
if (!SkillUtils.IsHudFrame()) return;
if (PerfLog.Enabled && Server.TickCount % 1920 == 0)
{
@ -99,26 +99,20 @@ namespace src.player
bool showDescriptionHUD = skillPlayer.SkillDescriptionHudExpired >= now || Config.LoadedConfig.DisplayAlwaysDescription;
bool isDescription = true;
var skills = SkillData.Skills;
var skills = SkillData.GetSnapshot();
if (skills == null || skills.IsEmpty)
if (skills.Length == 0)
{
infoLine = player.GetTranslationWithoutIlliterate("your_skill");
skillLine = player.GetTranslationWithoutIlliterate("none");
}
else if (skillPlayer.IsDrawing && player.PawnIsAlive)
{
int skillCount = skills.Count;
if (skillCount > 0)
{
var skillsArray = skills.ToArray();
var randomSkill = skillsArray[Instance.Random.Next(skillCount)];
var randomSkill = skills[Instance.Random.Next(skills.Length)];
infoLine = player.GetTranslationWithoutIlliterate("drawing_skill");
skillLine = $"<font color='{randomSkill.Color}'>{player.GetSkillName(randomSkill.Skill)}</font>";
}
}
else
{
if (player.PawnIsAlive)

View file

@ -45,6 +45,7 @@ namespace src.player.skills
{
if (player == null || !player.IsValid) return;
SkillPlayerInfo.TryRemove(player.Index, out _);
SkillUtils.ResetPrintHTML(player);
}
public static void OnTick()

View file

@ -0,0 +1,166 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
using System.Collections.Concurrent;
using static src.jRandomSkills;
namespace src.player.skills
{
public class Chameleon : ISkill
{
private const Skills skillName = Skills.Chameleon;
private static readonly ConcurrentDictionary<uint, byte> holders = [];
private static jRandomSkills? registeredOn;
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
if (ReferenceEquals(registeredOn, Instance)) return;
registeredOn = Instance;
Instance.RegisterEventHandler<EventPlayerDeath>(OnAnyDeath);
}
public static void NewRound()
{
holders.Clear();
}
public static void EnableSkill(CCSPlayerController player)
{
if (player == null || !player.IsValid) return;
holders[player.Index] = 0;
}
public static void DisableSkill(CCSPlayerController player)
{
if (player == null) return;
holders.TryRemove(player.Index, out _);
}
public static void PlayerDisconnect(uint playerIndex)
{
holders.TryRemove(playerIndex, out _);
}
private static HookResult OnAnyDeath(EventPlayerDeath @event, GameEventInfo info)
{
try
{
if (holders.IsEmpty) return HookResult.Continue;
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
if (attacker == null || !attacker.IsValid) return HookResult.Continue;
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
if (victim == null || !victim.IsValid) return HookResult.Continue;
if (attacker.Index == victim.Index) return HookResult.Continue;
if (attacker.Team == victim.Team) return HookResult.Continue;
if (!holders.ContainsKey(attacker.Index)) return HookResult.Continue;
var victimInfo = PlayerManager.GetPlayerByIndex(victim.Index);
if (victimInfo == null || victimInfo.IsDrawing) return HookResult.Continue;
CopySkill(attacker, victim.PlayerName, victimInfo.Skill);
}
catch (Exception ex)
{
Server.PrintToConsole($"[jRandomSkills] Chameleon.OnAnyDeath failed: {ex.Message}");
}
return HookResult.Continue;
}
private static void CopySkill(CCSPlayerController player, string victimName, Skills victimSkill)
{
var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
if (playerInfo == null) return;
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
if (!CanCopy(player, victimSkill, out string? blockReason))
{
if (blockReason != null && playerEvent != null && playerEvent.IsValid)
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation(blockReason, victimName));
return;
}
uint playerIndex = player.Index;
Skills previousSkill = playerInfo.Skill;
if (!holders.TryRemove(playerIndex, out _)) return;
Instance.AddTimer(.1f, () =>
{
var target = Utilities.GetPlayerFromIndex((int)playerIndex);
if (target == null || !target.IsValid) return;
var targetInfo = PlayerManager.GetPlayerByIndex(playerIndex);
if (targetInfo == null || targetInfo.Skill != previousSkill) return;
Instance.SkillAction(previousSkill.ToString(), "DisableSkill", [target]);
targetInfo.Skill = victimSkill;
targetInfo.SpecialSkill = skillName;
targetInfo.SkillChance = null;
if (target.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
if (SkillsInfo.GetValue<bool>(victimSkill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
Instance.AddTimer(Math.Max((float)(Event.GetFreezeTimeEnd() - DateTime.Now).TotalSeconds, 0), () =>
{
var heir = Utilities.GetPlayerFromIndex((int)playerIndex);
if (heir == null || !heir.IsValid) return;
if (PlayerManager.GetPlayerByIndex(playerIndex)?.Skill != victimSkill) return;
Instance.SkillAction(victimSkill.ToString(), "EnableSkill", [heir]);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
else
Instance.SkillAction(victimSkill.ToString(), "EnableSkill", [target]);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
if (playerEvent != null && playerEvent.IsValid)
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("chameleon_copied_info", player.GetSkillName(victimSkill)));
}
private static bool CanCopy(CCSPlayerController player, Skills victimSkill, out string? blockReason)
{
blockReason = null;
if (victimSkill == Skills.None || victimSkill == skillName) return false;
if (SkillData.GetInfo(victimSkill) == null) return false;
bool ctOnly = Event.counterterroristSkills.Any(s => s.Name == victimSkill.ToString());
bool ttOnly = Event.terroristSkills.Any(s => s.Name == victimSkill.ToString());
if ((player.Team == CsTeam.Terrorist && ctOnly) || (player.Team == CsTeam.CounterTerrorist && ttOnly))
{
blockReason = "chameleon_wrong_team_info";
return false;
}
if (!player.IsBot)
{
string permission = SkillsInfo.GetValue<string>(victimSkill, "requiredPermission");
if (!string.IsNullOrEmpty(permission) && !AdminManager.PlayerHasPermissions(player, permission))
return false;
}
if (!SkillsInfo.GetValue<bool>(victimSkill, "needsTeammates")) return true;
return PlayerManager.GetTickPlayers().Any(p =>
p != null && p.IsValid && p.Index != player.Index && p.Team == player.Team
&& p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0);
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5fd98a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
}
}
}

View file

@ -78,6 +78,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
if (plantingPlayers.IsEmpty) return;
float currentTime = Server.CurrentTime;

View file

@ -71,6 +71,7 @@ namespace src.player.skills
KillCamera(playerSkill);
}
playersInfo.TryRemove(player.Index, out _);
SkillUtils.ResetPrintHTML(player);
}
private static void KillCamera(PlayerSkill playerSkill)

View file

@ -77,7 +77,7 @@ namespace src.player.skills
if (playerInfo?.Skill == skillName)
if (SkillPlayerInfo.TryGetValue(eventPlayer!.Index, out var skillInfo))
{
UpdateHUD(player, skillInfo);
if (SkillUtils.IsHudFrame()) UpdateHUD(player, skillInfo);
HandleDash(player, skillInfo);
}
}

View file

@ -25,6 +25,10 @@ namespace src.player.skills
public static void OnTick()
{
if (distancerPlayers.IsEmpty) return;
if (!SkillUtils.IsHudFrame()) return;
if (SkillUtils.IsFreezeTime()) return;
foreach (var playerIndex in distancerPlayers.Keys)
{
@ -41,8 +45,10 @@ namespace src.player.skills
string closetEnemy = "Bot";
double closetDistance = double.MaxValue;
foreach (var enemy in PlayerManager.GetTickPlayers().Where(p => p.Team != player.Team))
foreach (var enemy in PlayerManager.GetTickPlayers())
{
if (enemy == null || enemy.Team == player.Team) continue;
var enemyPawn = enemy.PlayerPawn.Value;
if (enemyPawn == null || !enemyPawn.IsValid) continue;
if (enemyPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || playerPawn.AbsOrigin == null || enemyPawn.AbsOrigin == null) continue;

View file

@ -25,6 +25,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
if (SkillUtils.IsFreezeTime()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{

View file

@ -39,7 +39,7 @@ namespace src.player.skills
var pawn = player!.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.CameraServices == null) return;
if (cameras.TryGetValue(player!.Index, out var cameraInfo) && cameraInfo.Item1 == pawn.CameraServices.ViewEntity.Raw)
if (cameras.TryGetValue(player!.Index, out var cameraInfo) && cameraInfo.Item1 != pawn.CameraServices.ViewEntity.Raw)
BlockWeapon(player, true);
}

View file

@ -54,7 +54,7 @@ namespace src.player.skills
if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo))
continue;
UpdateHUD(player, skillInfo);
if (SkillUtils.IsHudFrame()) UpdateHUD(player, skillInfo);
if (!skillInfo.IsActive || skillInfo.LightEntityIndex == 0)
continue;

View file

@ -34,6 +34,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);

View file

@ -46,10 +46,17 @@ namespace src.player.skills
if (player == null || !player.IsValid) continue;
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
var observedPlayer = PlayerManager.GetTickPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle);
var observerInfo = observedPlayer != null ? PlayerManager.GetPlayerByIndex(observedPlayer.Index) : null;
if (playerInfo?.Skill != skillName)
{
var observerTarget = player.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle ?? nint.Zero;
if (observerTarget == nint.Zero) continue;
var observedPlayer = PlayerManager.GetTickPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == observerTarget);
if (observedPlayer == null) continue;
if (PlayerManager.GetPlayerByIndex(observedPlayer.Index)?.Skill != skillName) continue;
}
if (playerInfo?.Skill != skillName && observerInfo?.Skill != skillName) continue;
foreach (var entityIndex in smokes.Keys)
{
var entity = Utilities.GetEntityFromIndex<CBaseEntity>((int)entityIndex);

View file

@ -43,6 +43,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);

View file

@ -0,0 +1,316 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
using System.Collections.Concurrent;
using System.Drawing;
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
namespace src.player.skills
{
public class Grapple : ISkill
{
private const Skills skillName = Skills.Grapple;
private static readonly ConcurrentDictionary<uint, PlayerSkillInfo> SkillPlayerInfo = [];
private static readonly ConcurrentDictionary<uint, byte> liveRopes = [];
private static readonly object setLock = new();
private static readonly Color terroristRope = Color.FromArgb(255, 138, 84, 34);
private static readonly Color counterTerroristRope = Color.FromArgb(255, 52, 92, 138);
private const double attemptCooldown = .3;
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
}
public static void NewRound()
{
lock (setLock)
{
foreach (var skillInfo in SkillPlayerInfo.Values)
DestroyRope(skillInfo);
SkillPlayerInfo.Clear();
SweepOrphanRopes();
}
}
public static void RoundEnd()
{
foreach (var skillInfo in SkillPlayerInfo.Values)
DestroyRope(skillInfo);
SweepOrphanRopes();
}
public static void EnableSkill(CCSPlayerController player)
{
if (player == null || !player.IsValid) return;
SkillPlayerInfo[player.Index] = new PlayerSkillInfo
{
CanUse = true,
Cooldown = DateTime.MinValue,
};
}
public static void DisableSkill(CCSPlayerController player)
{
if (player == null) return;
if (SkillPlayerInfo.TryRemove(player.Index, out var skillInfo))
DestroyRope(skillInfo);
SkillUtils.ResetPrintHTML(player);
}
public static void PlayerDisconnect(uint playerIndex)
{
if (SkillPlayerInfo.TryRemove(playerIndex, out var skillInfo))
DestroyRope(skillInfo);
}
public static void PlayerDeath(EventPlayerDeath @event)
{
var player = PlayerManager.GetPlayerEvent(@event.Userid);
if (player == null || !player.IsValid) return;
if (SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo))
DestroyRope(skillInfo);
}
public static void UseSkill(CCSPlayerController player)
{
if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) return;
if (skillInfo.Anchor != null)
{
DestroyRope(skillInfo);
return;
}
if (!skillInfo.CanUse) return;
if ((DateTime.Now - skillInfo.LastAttempt).TotalSeconds < attemptCooldown) return;
skillInfo.LastAttempt = DateTime.Now;
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
if (!TryAttach(player, skillInfo))
{
if (playerEvent != null && playerEvent.IsValid)
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("grapple_no_anchor_info"));
return;
}
skillInfo.CanUse = false;
skillInfo.Cooldown = DateTime.Now;
}
private static bool TryAttach(CCSPlayerController player, PlayerSkillInfo skillInfo)
{
if (!RayTrace.IsAvailable) return false;
var pawn = player.PlayerPawn?.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return false;
var result = RayTrace.EyeTrace(player);
if (result == null || !result.Value.DidHit) return false;
if (result.Value.HitPlayer(out _)) return false;
Vector anchor = new(result.Value.EndPosX, result.Value.EndPosY, result.Value.EndPosZ);
Vector eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
if (SkillUtils.GetDistance(eyePos, anchor) < SkillsInfo.GetValue<float>(skillName, "minDistance")) return false;
skillInfo.Anchor = anchor;
skillInfo.EndTick = Server.TickCount + (int)(SkillsInfo.GetValue<float>(skillName, "maxPullSeconds") * 64);
if (!EntityManager.OverBudget())
{
Color ropeColor = player.Team == CsTeam.Terrorist ? terroristRope : counterTerroristRope;
var rope = EntityManager.CreateTrackedBeam(player.Index, eyePos, anchor, ropeColor);
if (rope != null && rope.IsValid)
{
float width = SkillsInfo.GetValue<float>(skillName, "ropeWidth");
rope.Width = width;
rope.EndWidth = width;
Utilities.SetStateChanged(rope, "CBeam", "m_fWidth");
Utilities.SetStateChanged(rope, "CBeam", "m_fEndWidth");
uint ropeIndex = rope.Index;
skillInfo.RopeIndex = ropeIndex;
liveRopes[ropeIndex] = 0;
jRandomSkills.Instance.AddTimer(SkillsInfo.GetValue<float>(skillName, "maxPullSeconds") + 1f,
() => KillRope(ropeIndex),
CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
}
player.EmitSound("SolidMetal.BulletImpact");
return true;
}
private static void DestroyRope(PlayerSkillInfo skillInfo)
{
skillInfo.Anchor = null;
skillInfo.EndTick = 0;
if (skillInfo.RopeIndex == null) return;
uint ropeIndex = skillInfo.RopeIndex.Value;
skillInfo.RopeIndex = null;
KillRope(ropeIndex);
}
private static void KillRope(uint ropeIndex)
{
if (!liveRopes.TryRemove(ropeIndex, out _)) return;
var rope = Utilities.GetEntityFromIndex<CBeam>((int)ropeIndex);
if (rope != null && rope.IsValid)
{
rope.Width = 0;
rope.EndWidth = 0;
rope.Render = Color.FromArgb(0, 0, 0, 0);
Utilities.SetStateChanged(rope, "CBeam", "m_fWidth");
Utilities.SetStateChanged(rope, "CBeam", "m_fEndWidth");
Utilities.SetStateChanged(rope, "CBaseModelEntity", "m_clrRender");
}
EntityManager.DestroyEntity(ropeIndex);
Server.NextFrame(() =>
{
var leftover = Utilities.GetEntityFromIndex<CBeam>((int)ropeIndex);
if (leftover != null && leftover.IsValid)
leftover.Remove();
});
}
private static void SweepOrphanRopes()
{
foreach (uint ropeIndex in liveRopes.Keys)
KillRope(ropeIndex);
}
public static void OnTick()
{
if (SkillPlayerInfo.IsEmpty) return;
bool hudFrame = SkillUtils.IsHudFrame();
foreach (var player in PlayerManager.GetTickPlayers())
{
if (player == null || !player.IsValid) continue;
if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) continue;
if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) continue;
if (skillInfo.Anchor != null)
HandlePull(player, skillInfo);
if (hudFrame)
UpdateHUD(player, skillInfo);
}
}
private static void HandlePull(CCSPlayerController player, PlayerSkillInfo skillInfo)
{
var pawn = player.PlayerPawn?.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
{
DestroyRope(skillInfo);
return;
}
if (Server.TickCount >= skillInfo.EndTick)
{
DestroyRope(skillInfo);
return;
}
Vector anchor = skillInfo.Anchor!;
Vector eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
float dx = anchor.X - eyePos.X;
float dy = anchor.Y - eyePos.Y;
float dz = anchor.Z - eyePos.Z;
float distance = MathF.Sqrt(dx * dx + dy * dy + dz * dz);
if (distance <= SkillsInfo.GetValue<float>(skillName, "stopDistance"))
{
DestroyRope(skillInfo);
return;
}
float pullSpeed = SkillsInfo.GetValue<float>(skillName, "pullSpeed");
float invLength = pullSpeed / distance;
pawn.AbsVelocity.X = dx * invLength;
pawn.AbsVelocity.Y = dy * invLength;
pawn.AbsVelocity.Z = dz * invLength;
if (skillInfo.RopeIndex == null) return;
var rope = Utilities.GetEntityFromIndex<CBeam>((int)skillInfo.RopeIndex.Value);
if (rope == null || !rope.IsValid)
{
liveRopes.TryRemove(skillInfo.RopeIndex.Value, out _);
skillInfo.RopeIndex = null;
return;
}
rope.Teleport(eyePos);
}
private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo)
{
float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue<float>(skillName, "cooldown")) - DateTime.Now).TotalSeconds);
float cooldown = Math.Max(time, 0);
if (cooldown == 0 && !skillInfo.CanUse)
skillInfo.CanUse = true;
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
if (playerInfo == null) return;
if (skillInfo.Anchor != null)
playerInfo.PrintHTML = $"<font color='#00FF00'>{player.GetTranslation("grapple_pulling_info")}</font>";
else if (cooldown == 0)
playerInfo.PrintHTML = null;
else
playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"<font color='#FF0000'>{cooldown}</font>")}";
}
public class PlayerSkillInfo
{
public bool CanUse { get; set; }
public DateTime Cooldown { get; set; }
public DateTime LastAttempt { get; set; }
public Vector? Anchor { get; set; }
public uint? RopeIndex { get; set; }
public int EndTick { get; set; }
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#38e0c4", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, float cooldown = 10f, float maxDistance = 1500f, float minDistance = 150f, float stopDistance = 90f, float pullSpeed = 850f, float maxPullSeconds = 3f, float ropeWidth = 0.8f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
public float Cooldown { get; set; } = cooldown;
public float MaxDistance { get; set; } = maxDistance;
public float MinDistance { get; set; } = minDistance;
public float StopDistance { get; set; } = stopDistance;
public float PullSpeed { get; set; } = pullSpeed;
public float MaxPullSeconds { get; set; } = maxPullSeconds;
public float RopeWidth { get; set; } = ropeWidth;
}
}
}

View file

@ -139,12 +139,14 @@ namespace src.player.skills
public static void OnTick()
{
if (activeChickens.IsEmpty) return;
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();
var players = PlayerManager.GetTickPlayers();
int healAmount = SkillsInfo.GetValue<int>(skillName, "heal");
float healRadius = SkillsInfo.GetValue<float>(skillName, "healRadius");

View file

@ -90,6 +90,7 @@ namespace src.player.skills
KillClone(playerSkill);
playersInfo.TryRemove(player.Index, out _);
EntityManager.DestroyPlayerEntities(player.Index);
SkillUtils.ResetPrintHTML(player);
}
private static void UpdateWeapons(CCSPlayerController player, PlayerSkill playerSkill)
@ -146,6 +147,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var playerSkill in playersInfo.Values)
UpdateHUD(playerSkill);
}

View file

@ -47,6 +47,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
if (player == null || !player.IsValid) continue;

View file

@ -0,0 +1,201 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
using System.Collections.Concurrent;
using static src.jRandomSkills;
namespace src.player.skills
{
public class Inheritance : ISkill
{
private const Skills skillName = Skills.Inheritance;
private static readonly ConcurrentDictionary<uint, byte> holders = [];
private static readonly ConcurrentDictionary<uint, FallenInfo> fallen = [];
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
}
public static void NewRound()
{
holders.Clear();
fallen.Clear();
foreach (var player in PlayerManager.GetTickPlayers())
SkillUtils.CloseMenu(player);
}
public static void EnableSkill(CCSPlayerController player)
{
if (player == null || !player.IsValid) return;
holders[player.Index] = 0;
RefreshMenu(player);
}
public static void DisableSkill(CCSPlayerController player)
{
if (player == null) return;
holders.TryRemove(player.Index, out _);
SkillUtils.CloseMenu(player);
}
public static void PlayerDisconnect(uint playerIndex)
{
holders.TryRemove(playerIndex, out _);
fallen.TryRemove(playerIndex, out _);
}
public static void PlayerDeath(EventPlayerDeath @event)
{
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
if (victim == null || !victim.IsValid) return;
holders.TryRemove(victim.Index, out _);
var victimInfo = PlayerManager.GetPlayerByIndex(victim.Index);
if (victimInfo == null || victimInfo.IsDrawing) return;
if (victimInfo.Skill == Skills.None || victimInfo.Skill == skillName) return;
if (SkillData.GetInfo(victimInfo.Skill) == null) return;
fallen[victim.Index] = new FallenInfo
{
PlayerName = victim.PlayerName,
Skill = victimInfo.Skill,
Team = victim.Team,
};
foreach (uint holderIndex in holders.Keys)
{
var holder = Utilities.GetPlayerFromIndex((int)holderIndex);
if (holder == null || !holder.IsValid || holder.Team != victim.Team) continue;
RefreshMenu(holder);
}
}
public static void TypeSkill(CCSPlayerController player, string[] commands)
{
if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
if (playerInfo?.Skill != skillName) return;
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
if (playerEvent == null || !playerEvent.IsValid) return;
if (commands == null || commands.Length == 0 || !uint.TryParse(commands[0], out uint fallenIndex))
{
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
return;
}
if (!fallen.TryGetValue(fallenIndex, out var fallenInfo) || !CanInherit(player, fallenInfo))
{
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
return;
}
InheritSkill(player, fallenInfo);
}
private static void InheritSkill(CCSPlayerController player, FallenInfo fallenInfo)
{
var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
if (playerInfo == null) return;
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
if (playerEvent == null || !playerEvent.IsValid) return;
uint playerIndex = player.Index;
Skills inheritedSkill = fallenInfo.Skill;
holders.TryRemove(playerIndex, out _);
SkillUtils.CloseMenu(player);
Instance.AddTimer(.1f, () =>
{
var target = Utilities.GetPlayerFromIndex((int)playerIndex);
if (target == null || !target.IsValid) return;
var targetInfo = PlayerManager.GetPlayerByIndex(playerIndex);
if (targetInfo == null || targetInfo.Skill != skillName) return;
targetInfo.Skill = inheritedSkill;
targetInfo.SpecialSkill = skillName;
SkillUtils.CloseMenu(target);
if (SkillsInfo.GetValue<bool>(inheritedSkill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
Instance?.AddTimer(Math.Max((float)(Event.GetFreezeTimeEnd() - DateTime.Now).TotalSeconds, 0), () =>
{
var heir = Utilities.GetPlayerFromIndex((int)playerIndex);
if (heir == null || !heir.IsValid) return;
if (PlayerManager.GetPlayerByIndex(playerIndex)?.Skill != inheritedSkill) return;
Instance?.SkillAction(inheritedSkill.ToString(), "EnableSkill", [heir]);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
else
Instance?.SkillAction(inheritedSkill.ToString(), "EnableSkill", [target]);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("inheritance_player_info", fallenInfo.PlayerName));
}
private static void RefreshMenu(CCSPlayerController player)
{
if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) return;
ConcurrentBag<(string, string)> menuItems = [];
foreach (var (fallenIndex, fallenInfo) in fallen)
{
if (fallenIndex == player.Index) continue;
if (!CanInherit(player, fallenInfo)) continue;
menuItems.Add(($"\u202A{fallenInfo.PlayerName}\u202C : {player.GetSkillName(fallenInfo.Skill)}", fallenIndex.ToString()));
}
if (menuItems.IsEmpty) return;
if (SkillUtils.HasMenu(player))
SkillUtils.UpdateMenu(player, menuItems);
else
SkillUtils.CreateMenu(player, menuItems);
}
private static bool CanInherit(CCSPlayerController player, FallenInfo fallenInfo)
{
if (fallenInfo.Team != player.Team) return false;
if (!player.IsBot)
{
string permission = SkillsInfo.GetValue<string>(fallenInfo.Skill, "requiredPermission");
if (!string.IsNullOrEmpty(permission) && !AdminManager.PlayerHasPermissions(player, permission))
return false;
}
if (!SkillsInfo.GetValue<bool>(fallenInfo.Skill, "needsTeammates")) return true;
return PlayerManager.GetTickPlayers().Any(p =>
p != null && p.IsValid && p.Index != player.Index && p.Team == player.Team
&& p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0);
}
private class FallenInfo
{
public required string PlayerName { get; set; }
public required Skills Skill { get; set; }
public required CsTeam Team { get; set; }
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c9a227", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = true, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
}
}
}

View file

@ -38,9 +38,27 @@ namespace src.player.skills
playersInAction.Clear();
}
private static readonly List<(CsTeam OwnerTeam, uint RelayEntityIndex)> transmitTrails = [];
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
{
var snapshot = activeTrails.ToArray();
if (activeTrails.IsEmpty) return;
transmitTrails.Clear();
foreach (var (trackedPlayerIndex, relayIndex) in activeTrails)
{
if (relayIndex == null) continue;
var trailOwner = Utilities.GetPlayerFromIndex((int)trackedPlayerIndex);
if (trailOwner == null || !trailOwner.IsValid) continue;
var relayEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)relayIndex);
if (relayEntity == null || !relayEntity.IsValid) continue;
transmitTrails.Add((trailOwner.Team, relayEntity.Index));
}
if (transmitTrails.Count == 0) return;
foreach (var (info, player) in infoList)
{
@ -65,19 +83,12 @@ namespace src.player.skills
}
}
foreach (var (trackedPlayerIndex, relayIndex) in snapshot)
var playerTeam = player.Team;
foreach (var (ownerTeam, relayEntityIndex) in transmitTrails)
{
if (relayIndex == null) continue;
var trailOwner = Utilities.GetPlayerFromIndex((int)trackedPlayerIndex);
if (trailOwner == null || !trailOwner.IsValid) continue;
var relayEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)relayIndex);
if (relayEntity == null || !relayEntity.IsValid) continue;
if (player.Team == trailOwner.Team || !isJackalOwner)
if (info.TransmitEntities.Contains(relayEntity.Index))
info.TransmitEntities.Remove(relayEntity.Index);
if (playerTeam == ownerTeam || !isJackalOwner)
if (info.TransmitEntities.Contains(relayEntityIndex))
info.TransmitEntities.Remove(relayEntityIndex);
}
}
}
@ -135,14 +146,6 @@ namespace src.player.skills
Event.EnableTransmit();
playersInAction.TryAdd(player.Index, 0);
var opponents = PlayerManager.GetTickPlayers()
.Where(p => p != null
&& p.IsValid
&& p.Team != player.Team
&& p.PawnIsAlive
&& (p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist))
.ToArray();
mainSkillTimer ??= Instance.AddTimer(2.5f, () => UpdateAllTrails(),
CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT | CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);

View file

@ -235,6 +235,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
if (SkillUtils.IsFreezeTime()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{

View file

@ -26,6 +26,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
if (SkillUtils.IsFreezeTime()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{

View file

@ -29,6 +29,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);

View file

@ -19,11 +19,11 @@ namespace src.player.skills
{
foreach (var player in PlayerManager.GetTickPlayers())
{
if (!Instance.IsPlayerValid(player)) continue;
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
if (player == null) continue;
if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) continue;
if (!Instance.IsPlayerValid(player)) continue;
if (playerInfo?.Skill == skillName)
{
var pawn = player.PlayerPawn.Value!;
var weaponServices = pawn.WeaponServices;
if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) continue;
@ -45,7 +45,6 @@ namespace src.player.skills
Schema.SetSchemaValue<Int32>(weapon.Handle, "CBasePlayerWeapon", "m_nNextSecondaryAttackTick", Server.TickCount);
}
}
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8a42f5", 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) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{

View file

@ -21,10 +21,11 @@ namespace src.player.skills
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerEvent = PlayerManager.GetPlayerEvent(player);
if (playerEvent == null) continue;
if (PlayerManager.GetPlayerByIndex(playerEvent.Index)?.Skill != skillName) continue;
if (!Instance.IsPlayerValid(playerEvent)) continue;
var playerInfo = PlayerManager.GetPlayerByIndex(playerEvent!.Index);
if (playerInfo?.Skill == skillName)
SetEnemiesVisibleOnRadar(player);
}
}
@ -36,8 +37,10 @@ namespace src.player.skills
// SpottedByMask is indexed by player slot (0-63), not entity index.
int slot = player.Slot;
foreach (var enemy in PlayerManager.GetTickPlayers().FindAll(p => p.Team != player.Team))
foreach (var enemy in PlayerManager.GetTickPlayers())
{
if (enemy == null || enemy.Team == player.Team) continue;
var enemyEvent = PlayerManager.GetPlayerEvent(enemy);
if (enemyEvent == null || !enemyEvent.IsValid) continue;
@ -59,7 +62,7 @@ namespace src.player.skills
public class SkillConfig : SkillsInfo.DefaultSkillInfo
{
public SkillConfig(Skills skill = skillName, bool active = true, string color = "#2effcb", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = utils.Rarity.Common) : base(skill, active, color, onlyTeam, needsTeammates)
public SkillConfig(Skills skill = skillName, bool active = true, string color = "#2effcb", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = utils.Rarity.Common) : base(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
}
}

View file

@ -40,6 +40,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
var players = PlayerManager.GetTickPlayers().ToArray();
foreach (var player in players)
{

View file

@ -25,6 +25,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);

View file

@ -33,7 +33,7 @@ namespace src.player.skills
{
public int HealthToAdd { get; set; }
public float Cooldown { get; set; }
public SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff462e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = utils.Rarity.Common, int healthToAdd = 1, float cooldown = .25f) : base(skill, active, color, onlyTeam, needsTeammates)
public SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff462e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = utils.Rarity.Common, int healthToAdd = 1, float cooldown = .25f) : base(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
HealthToAdd = healthToAdd;
Cooldown = cooldown;

View file

@ -40,6 +40,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);

View file

@ -26,6 +26,7 @@ namespace src.player.skills
public static void OnTick()
{
if (!SkillUtils.IsHudFrame()) return;
foreach (var player in PlayerManager.GetTickPlayers())
{
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);

View file

@ -0,0 +1,661 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils;
using jRandomSkills.src.utils;
using RayTraceAPI;
using src.utils;
using System.Collections.Concurrent;
using System.Drawing;
using System.Numerics;
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
namespace src.player.skills
{
public class Ricochet : ISkill
{
private const Skills skillName = Skills.Ricochet;
private static readonly ConcurrentDictionary<uint, ImpactBudget> impactBudgets = [];
private static readonly HashSet<uint> ownedTracers = [];
private static readonly List<uint> tracerPool = [];
private static readonly List<Flight> flights = [];
private static readonly object flightLock = new();
private static readonly Color tracerColor = Color.FromArgb(170, 255, 226, 140);
private static readonly ulong worldOnlyMask = (ulong)InteractionLayers.MASK_WORLD_ONLY;
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
}
public static void NewRound()
{
impactBudgets.Clear();
ResetPool(false);
}
public static void RoundEnd()
{
ResetPool(true);
}
public static void PlayerDisconnect(uint playerIndex)
{
impactBudgets.TryRemove(playerIndex, out _);
}
public static void DisableSkill(CCSPlayerController player)
{
if (player == null) return;
impactBudgets.TryRemove(player.Index, out _);
}
public static void BulletImpact(EventBulletImpact @event)
{
var player = PlayerManager.GetPlayerEvent(@event.Userid);
if (player == null || !player.IsValid) return;
if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) return;
if (!RayTrace.IsAvailable)
{
Trace("RayTrace module unavailable");
return;
}
var pawn = player.PlayerPawn?.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
{
Trace("shooter pawn invalid or dead");
return;
}
if (!TryConsumeBudget(player.Index)) return;
Vector3 eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
Vector3 impact = new(@event.X, @event.Y, @event.Z);
Vector3 toImpact = impact - eyePos;
if (toImpact.LengthSquared() < 1f)
{
Trace("impact too close to eye");
return;
}
Vector3 direction = Vector3.Normalize(toImpact);
var surface = RayTrace.TraceShape(player, ToVector(eyePos), ToVector(impact + direction * 8f), worldOnlyMask, 0);
if (surface == null)
{
Trace("surface trace returned null");
return;
}
if (!surface.Value.DidHit)
{
Trace($"surface trace missed (fraction={surface.Value.Fraction:0.###})");
return;
}
if (!TryResolveNormal(player, surface.Value, direction, out Vector3 normal))
{
Trace("could not resolve surface normal");
return;
}
Bounce(player, pawn, surface.Value.EndPos, direction, normal);
}
private static void Bounce(CCSPlayerController player, CCSPlayerPawn pawn, Vector3 point, Vector3 direction, Vector3 normal)
{
int bounces = Math.Max(SkillsInfo.GetValue<int>(skillName, "bounces"), 1);
float segmentDistance = SkillsInfo.GetValue<float>(skillName, "segmentDistance");
float damageFalloff = SkillsInfo.GetValue<float>(skillName, "damageFalloff");
var vdata = GetWeaponVData(pawn);
float damage = GetBaseDamage(vdata);
KillfeedIcons? killfeedIcon = KillfeedIconsExtensions.FromWeaponName(SkillUtils.GetDesignerName(pawn.WeaponServices?.ActiveWeapon?.Value));
List<Vector3> path = [];
for (int i = 0; i < bounces; i++)
{
direction = Vector3.Reflect(direction, normal);
Vector3 start = point + normal * 2f;
if (path.Count == 0) path.Add(start);
Vector3 end = start + direction * segmentDistance;
var hit = RayTrace.TraceShape(player, ToVector(start), ToVector(end));
if (hit == null)
{
Trace($"bounce {i + 1}: segment trace returned null");
break;
}
Vector3 stop = hit.Value.DidHit ? hit.Value.EndPos : end;
path.Add(stop);
if (!hit.Value.DidHit)
{
Trace($"bounce {i + 1}: no surface, len={Vector3.Distance(start, stop):0.#}");
break;
}
if (hit.Value.HitPlayer(out var victim))
{
DealDamage(player, victim, damage, vdata, killfeedIcon);
break;
}
if (!TryResolveNormal(player, hit.Value, direction, out normal))
{
Trace($"bounce {i + 1}: could not resolve normal");
break;
}
point = hit.Value.EndPos;
damage *= damageFalloff;
}
StartFlight(player, path);
}
public static void OnTick()
{
lock (flightLock)
{
if (flights.Count == 0) return;
float speed = MathF.Max(SkillsInfo.GetValue<float>(skillName, "tracerSpeed"), 100f);
float trail = MathF.Max(SkillsInfo.GetValue<float>(skillName, "tracerLength"), 16f);
float step = speed / 64f;
int tick = Server.TickCount;
for (int i = flights.Count - 1; i >= 0; i--)
{
var flight = flights[i];
flight.Travelled += step;
if (flight.Travelled - trail >= flight.TotalLength || tick >= flight.DeadlineTick)
{
ReleaseTracer(flight);
flights.RemoveAt(i);
continue;
}
float head = MathF.Min(flight.Travelled, flight.TotalLength);
int segment = SegmentAt(flight, head);
float tail = MathF.Max(head - trail, flight.Cumulative[segment]);
UpdateTracer(flight, PointAt(flight, tail), PointAt(flight, head));
}
}
}
private static void StartFlight(CCSPlayerController player, List<Vector3> path)
{
if (path.Count < 2) return;
if (!SkillsInfo.GetValue<bool>(skillName, "showTracer")) return;
if (EntityManager.OverBudget()) return;
var points = path.ToArray();
var cumulative = new float[points.Length];
for (int i = 1; i < points.Length; i++)
cumulative[i] = cumulative[i - 1] + Vector3.Distance(points[i - 1], points[i]);
float total = cumulative[^1];
if (total < 1f) return;
float speed = MathF.Max(SkillsInfo.GetValue<float>(skillName, "tracerSpeed"), 100f);
lock (flightLock)
{
if (flights.Count >= Math.Max(SkillsInfo.GetValue<int>(skillName, "maxActiveTracers"), 1)) return;
flights.Add(new Flight
{
OwnerIndex = player.Index,
Points = points,
Cumulative = cumulative,
TotalLength = total,
DeadlineTick = Server.TickCount + (int)(total / speed * 64f) + 128,
});
}
}
private static int SegmentAt(Flight flight, float distance)
{
for (int i = flight.Points.Length - 2; i >= 0; i--)
if (distance >= flight.Cumulative[i]) return i;
return 0;
}
private static Vector3 PointAt(Flight flight, float distance)
{
int i = SegmentAt(flight, distance);
float segmentStart = flight.Cumulative[i];
float segmentLength = flight.Cumulative[i + 1] - segmentStart;
if (segmentLength <= .0001f) return flight.Points[i];
float t = Math.Clamp((distance - segmentStart) / segmentLength, 0f, 1f);
return Vector3.Lerp(flight.Points[i], flight.Points[i + 1], t);
}
private static void UpdateTracer(Flight flight, Vector3 tail, Vector3 head)
{
CBeam? beam = null;
if (flight.BeamIndex != null)
{
beam = ResolveTracer(flight.BeamIndex.Value);
if (beam == null)
{
ownedTracers.Remove(flight.BeamIndex.Value);
flight.BeamIndex = null;
}
}
beam ??= AcquireTracer(flight);
if (beam == null) return;
beam.Teleport(ToVector(tail));
beam.EndPos.X = head.X;
beam.EndPos.Y = head.Y;
beam.EndPos.Z = head.Z;
Utilities.SetStateChanged(beam, "CBeam", "m_vecEndPos");
}
private static CBeam? AcquireTracer(Flight flight)
{
float width = SkillsInfo.GetValue<float>(skillName, "tracerWidth");
while (tracerPool.Count > 0)
{
uint pooledIndex = tracerPool[^1];
tracerPool.RemoveAt(tracerPool.Count - 1);
var pooled = ResolveTracer(pooledIndex);
if (pooled == null)
{
ownedTracers.Remove(pooledIndex);
continue;
}
pooled.Width = width;
pooled.EndWidth = width;
pooled.Render = tracerColor;
Utilities.SetStateChanged(pooled, "CBeam", "m_fWidth");
Utilities.SetStateChanged(pooled, "CBeam", "m_fEndWidth");
Utilities.SetStateChanged(pooled, "CBaseModelEntity", "m_clrRender");
flight.BeamIndex = pooledIndex;
return pooled;
}
if (ownedTracers.Count >= Math.Max(SkillsInfo.GetValue<int>(skillName, "maxActiveTracers"), 1)) return null;
if (EntityManager.OverBudget()) return null;
var beam = CreateTracerBeam(flight.Points[0], width);
if (beam == null) return null;
ownedTracers.Add(beam.Index);
flight.BeamIndex = beam.Index;
return beam;
}
private static CBeam? CreateTracerBeam(Vector3 at, float width)
{
try
{
var beam = Utilities.CreateEntityByName<CBeam>("beam");
if (beam == null || !beam.IsValid) return null;
beam.Render = tracerColor;
beam.Width = width;
beam.EndWidth = width;
beam.Teleport(ToVector(at));
beam.EndPos.X = at.X;
beam.EndPos.Y = at.Y;
beam.EndPos.Z = at.Z;
beam.DispatchSpawn();
return beam;
}
catch (Exception ex)
{
Trace($"beam create failed: {ex.Message}");
return null;
}
}
private static void ReleaseTracer(Flight flight)
{
if (flight.BeamIndex == null) return;
uint beamIndex = flight.BeamIndex.Value;
flight.BeamIndex = null;
if (!ownedTracers.Contains(beamIndex)) return;
var beam = ResolveTracer(beamIndex);
if (beam == null)
{
ownedTracers.Remove(beamIndex);
Trace($"tracer {beamIndex} vanished before release");
return;
}
HideTracer(beam);
tracerPool.Add(beamIndex);
}
private static void HideTracer(CBeam beam)
{
beam.Width = 0;
beam.EndWidth = 0;
beam.Render = Color.FromArgb(0, 0, 0, 0);
Utilities.SetStateChanged(beam, "CBeam", "m_fWidth");
Utilities.SetStateChanged(beam, "CBeam", "m_fEndWidth");
Utilities.SetStateChanged(beam, "CBaseModelEntity", "m_clrRender");
if (beam.AbsOrigin == null) return;
beam.EndPos.X = beam.AbsOrigin.X;
beam.EndPos.Y = beam.AbsOrigin.Y;
beam.EndPos.Z = beam.AbsOrigin.Z;
Utilities.SetStateChanged(beam, "CBeam", "m_vecEndPos");
}
private static void ResetPool(bool hideEntities)
{
lock (flightLock)
{
foreach (var flight in flights)
flight.BeamIndex = null;
flights.Clear();
if (hideEntities)
foreach (uint beamIndex in ownedTracers)
{
var beam = ResolveTracer(beamIndex);
if (beam != null) HideTracer(beam);
}
if (ownedTracers.Count > 0)
Trace($"pool reset: {ownedTracers.Count} tracer(s) released");
tracerPool.Clear();
ownedTracers.Clear();
}
}
private static CBeam? ResolveTracer(uint beamIndex)
{
var beam = Utilities.GetEntityFromIndex<CBeam>((int)beamIndex);
if (beam == null || !beam.IsValid) return null;
return beam.DesignerName == "beam" ? beam : null;
}
private static CCSWeaponBaseVData? GetWeaponVData(CCSPlayerPawn pawn)
{
var activeWeapon = pawn.WeaponServices?.ActiveWeapon?.Value;
if (activeWeapon == null || !activeWeapon.IsValid) return null;
try
{
return activeWeapon.As<CCSWeaponBase>().GetVData<CCSWeaponBaseVData>();
}
catch
{
return null;
}
}
private static float GetBaseDamage(CCSWeaponBaseVData? vdata)
{
float fallback = SkillsInfo.GetValue<float>(skillName, "fallbackDamage");
if (vdata == null) return fallback;
float weaponDamage = vdata.Damage;
if (weaponDamage <= 0) return fallback;
return weaponDamage * SkillsInfo.GetValue<float>(skillName, "damageMultiplier");
}
private static void DealDamage(CCSPlayerController attacker, CCSPlayerController? victim, float damage, CCSWeaponBaseVData? vdata, KillfeedIcons? killfeedIcon)
{
if (victim == null || !victim.IsValid) return;
var victimEvent = PlayerManager.GetPlayerEvent(victim);
if (victimEvent == null || !victimEvent.IsValid) return;
if (victimEvent.Index == attacker.Index || victimEvent.Team == attacker.Team) return;
var victimPawn = victimEvent.PlayerPawn?.Value;
if (victimPawn == null || !victimPawn.IsValid || victimPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
if (vdata != null && victimEvent.PawnArmor > 0 && SkillsInfo.GetValue<bool>(skillName, "respectArmor"))
damage *= vdata.ArmorRatio * .5f;
int finalDamage = (int)MathF.Round(damage);
if (finalDamage <= 0) return;
Trace($"hit {victimEvent.PlayerName} for {finalDamage}");
SkillUtils.TakeHealth(victimPawn, finalDamage, attacker, killfeedIcon ?? KillfeedIcons.Leg);
}
private static bool TryResolveNormal(CCSPlayerController player, CustomTraceResult result, Vector3 direction, out Vector3 normal)
{
normal = Vector3.Zero;
if (!TryRoughNormal(player, result, direction, out Vector3 rough)) return false;
if (TryRefineNormal(player, result.EndPos, direction, rough, out normal)) return true;
Trace("surface refine failed, using rough normal");
normal = rough;
return true;
}
private static bool TryRoughNormal(CCSPlayerController player, CustomTraceResult result, Vector3 direction, out Vector3 rough)
{
rough = result.Normal;
if (rough.LengthSquared() > .0001f)
{
rough = Vector3.Normalize(rough);
if (Vector3.Dot(direction, rough) < -.05f) return true;
}
return TryPlaneFitNormal(player, result.EndPos, direction, out rough);
}
private static bool TryRefineNormal(CCSPlayerController player, Vector3 baseHit, Vector3 direction, Vector3 rough, out Vector3 normal)
{
normal = Vector3.Zero;
Vector3 axis = MathF.Abs(rough.Z) > .9f ? Vector3.UnitX : Vector3.UnitZ;
Vector3 first = Vector3.Cross(rough, axis);
if (first.LengthSquared() < .0001f) return false;
first = Vector3.Normalize(first);
Vector3 second = Vector3.Normalize(Vector3.Cross(rough, first));
float offset = MathF.Max(SkillsInfo.GetValue<float>(skillName, "normalProbeOffset"), 1f);
float tolerance = offset * 2.5f;
Span<Vector3> ring = stackalloc Vector3[4];
int count = 0;
if (SampleSurface(player, baseHit, rough, first * offset, tolerance, out Vector3 firstPlus)) ring[count++] = firstPlus;
if (SampleSurface(player, baseHit, rough, second * offset, tolerance, out Vector3 secondPlus)) ring[count++] = secondPlus;
if (SampleSurface(player, baseHit, rough, first * -offset, tolerance, out Vector3 firstMinus)) ring[count++] = firstMinus;
if (SampleSurface(player, baseHit, rough, second * -offset, tolerance, out Vector3 secondMinus)) ring[count++] = secondMinus;
if (count == 2 && SampleSurface(player, baseHit, rough, Vector3.Zero, tolerance, out Vector3 center))
ring[count++] = center;
if (count < 3) return false;
normal = NewellNormal(ring[..count]);
if (normal.LengthSquared() < .0001f) return false;
normal = Vector3.Normalize(normal);
float dot = Vector3.Dot(direction, normal);
if (MathF.Abs(dot) < .05f) return false;
if (dot > 0f) normal = -normal;
return true;
}
private static bool SampleSurface(CCSPlayerController player, Vector3 baseHit, Vector3 rough, Vector3 tangentOffset, float tolerance, out Vector3 point)
{
point = Vector3.Zero;
Vector3 anchor = baseHit + tangentOffset;
Vector3 start = anchor + rough * 16f;
Vector3 end = anchor - rough * 48f;
var sample = RayTrace.TraceShape(player, ToVector(start), ToVector(end), worldOnlyMask, 0);
if (sample == null || !sample.Value.DidHit) return false;
point = sample.Value.EndPos;
return MathF.Abs(Vector3.Dot(point - baseHit, rough)) <= tolerance;
}
private static Vector3 NewellNormal(ReadOnlySpan<Vector3> points)
{
Vector3 normal = Vector3.Zero;
for (int i = 0; i < points.Length; i++)
{
Vector3 current = points[i];
Vector3 next = points[(i + 1) % points.Length];
normal.X += (current.Y - next.Y) * (current.Z + next.Z);
normal.Y += (current.Z - next.Z) * (current.X + next.X);
normal.Z += (current.X - next.X) * (current.Y + next.Y);
}
return normal;
}
private static bool TryPlaneFitNormal(CCSPlayerController player, Vector3 baseHit, Vector3 direction, out Vector3 normal)
{
normal = Vector3.Zero;
Vector3 axis = MathF.Abs(direction.Z) > .9f ? Vector3.UnitX : Vector3.UnitZ;
Vector3 right = Vector3.Cross(direction, axis);
if (right.LengthSquared() < .0001f) return false;
right = Vector3.Normalize(right);
Vector3 up = Vector3.Normalize(Vector3.Cross(right, direction));
float offset = MathF.Max(SkillsInfo.GetValue<float>(skillName, "normalProbeOffset"), 1f);
float tolerance = offset * 6f;
Vector3 origin = baseHit - direction * 24f;
if (!Probe(player, origin, direction, right * offset, baseHit, tolerance, out Vector3 rightHit)) return false;
if (!Probe(player, origin, direction, right * -offset, baseHit, tolerance, out Vector3 leftHit)) return false;
if (!Probe(player, origin, direction, up * offset, baseHit, tolerance, out Vector3 upHit)) return false;
if (!Probe(player, origin, direction, up * -offset, baseHit, tolerance, out Vector3 downHit)) return false;
normal = Vector3.Cross(rightHit - leftHit, upHit - downHit);
if (normal.LengthSquared() < .0001f) return false;
normal = Vector3.Normalize(normal);
float dot = Vector3.Dot(direction, normal);
if (MathF.Abs(dot) < .05f) return false;
if (dot > 0f) normal = -normal;
return true;
}
private static bool Probe(CCSPlayerController player, Vector3 origin, Vector3 direction, Vector3 offset, Vector3 baseHit, float tolerance, out Vector3 point)
{
point = Vector3.Zero;
Vector3 start = origin + offset;
Vector3 end = start + direction * 96f;
var probe = RayTrace.TraceShape(player, ToVector(start), ToVector(end), worldOnlyMask, 0);
if (probe == null || !probe.Value.DidHit) return false;
point = probe.Value.EndPos;
return Vector3.Distance(point, baseHit) <= tolerance;
}
private static void Trace(string message)
{
Debug.WriteToDebug($"[Ricochet] {message}");
}
private static bool TryConsumeBudget(uint playerIndex)
{
int tick = Server.TickCount;
var budget = impactBudgets.GetOrAdd(playerIndex, _ => new ImpactBudget());
if (budget.Tick != tick)
{
budget.Tick = tick;
budget.Count = 0;
}
if (budget.Count >= Math.Max(SkillsInfo.GetValue<int>(skillName, "maxImpactsPerTick"), 1)) return false;
budget.Count++;
return true;
}
private static Vector ToVector(Vector3 vector) => new(vector.X, vector.Y, vector.Z);
private class ImpactBudget
{
public int Tick { get; set; } = int.MinValue;
public int Count { get; set; }
}
private class Flight
{
public required uint OwnerIndex { get; set; }
public required Vector3[] Points { get; set; }
public required float[] Cumulative { get; set; }
public required float TotalLength { get; set; }
public required int DeadlineTick { get; set; }
public float Travelled { get; set; }
public uint? BeamIndex { get; set; }
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffd75e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare, int bounces = 3, float segmentDistance = 1200f, float damageMultiplier = .5f, float fallbackDamage = 25f, bool respectArmor = true, float damageFalloff = .75f, int maxImpactsPerTick = 2, bool showTracer = true, float tracerWidth = 0.5f, float tracerSpeed = 2600f, float tracerLength = 220f, int maxActiveTracers = 12, float normalProbeOffset = 6f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
{
public int Bounces { get; set; } = bounces;
public float SegmentDistance { get; set; } = segmentDistance;
public float DamageMultiplier { get; set; } = damageMultiplier;
public float FallbackDamage { get; set; } = fallbackDamage;
public bool RespectArmor { get; set; } = respectArmor;
public float DamageFalloff { get; set; } = damageFalloff;
public int MaxImpactsPerTick { get; set; } = maxImpactsPerTick;
public bool ShowTracer { get; set; } = showTracer;
public float TracerWidth { get; set; } = tracerWidth;
public float TracerSpeed { get; set; } = tracerSpeed;
public float TracerLength { get; set; } = tracerLength;
public int MaxActiveTracers { get; set; } = maxActiveTracers;
public float NormalProbeOffset { get; set; } = normalProbeOffset;
}
}
}

View file

@ -50,6 +50,8 @@ namespace src.player.skills
public static void PlayerMakeSound(UserMessage um)
{
if (SkillPlayerInfo.IsEmpty) return;
var soundevent = um.ReadUInt("soundevent_hash");
if (soundevent != soundEventHash) return;
@ -96,7 +98,11 @@ namespace src.player.skills
public static void OnTick()
{
if (Server.TickCount % (60 * SkillsInfo.GetValue<int>(skillName, "cooldown")) != 0) return;
if (SkillPlayerInfo.IsEmpty) return;
int cooldown = SkillsInfo.GetValue<int>(skillName, "cooldown");
if (cooldown < 1) cooldown = 1;
if (Server.TickCount % (60 * cooldown) != 0) return;
foreach (var player in PlayerManager.GetTickPlayers()
.Where(p => p != null && p.IsValid && p.PlayerPawn.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0))

View file

@ -133,7 +133,10 @@ namespace src.player.skills
pawn.CameraServices.ViewEntity.Raw = defaultCam ? orginalCameraRaw : newCameraRaw;
}
else
{
defaultCam = true;
pawn.CameraServices.ViewEntity.Raw = orginalCameraRaw;
}
Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_pCameraServices");

View file

@ -45,7 +45,7 @@ namespace src.player.skills
if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) continue;
GetTeamate(player, skillInfo);
UpdateHUD(player, skillInfo);
if (SkillUtils.IsHudFrame()) UpdateHUD(player, skillInfo);
}
}

View file

@ -400,14 +400,35 @@ namespace src.player.skills
return (closet, minKnifeInfo);
}
private static readonly List<(uint OwnerIndex, uint GlowEntityIndex)> transmitGlows = [];
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
{
var snapshot = knivesInfo.ToArray();
if (knivesInfo.IsEmpty) return;
transmitGlows.Clear();
foreach (var (knifeOwnerIndex, knifeInfo) in knivesInfo)
{
if (knifeInfo.GlowIndex == null) continue;
var glowEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)knifeInfo.GlowIndex);
if (glowEntity == null || !glowEntity.IsValid)
{
knifeInfo.GlowIndex = null;
continue;
}
transmitGlows.Add((knifeOwnerIndex, glowEntity.Index));
}
if (transmitGlows.Count == 0) return;
foreach (var (info, player) in infoList)
{
if (player == null || !player.IsValid) continue;
uint viewerIndex = PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index;
uint? observedPlayerIndex = null;
var playerPawn = player.Pawn?.Value;
if (playerPawn != null && playerPawn.IsValid)
@ -423,23 +444,13 @@ namespace src.player.skills
}
}
foreach ((uint knifeOwnerIndex, KnifeInfo knifeInfo) in snapshot)
foreach (var (knifeOwnerIndex, glowEntityIndex) in transmitGlows)
{
if (knifeInfo.GlowIndex == null) continue;
if (knifeOwnerIndex == viewerIndex) continue;
if (observedPlayerIndex.HasValue && observedPlayerIndex.Value == knifeOwnerIndex) continue;
var glowEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)knifeInfo.GlowIndex);
if (glowEntity == null || !glowEntity.IsValid)
{
knifeInfo.GlowIndex = null;
continue;
}
bool isOwner = knifeOwnerIndex == (PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index);
bool isObservingOwner = observedPlayerIndex.HasValue && observedPlayerIndex.Value == knifeOwnerIndex;
if (!isOwner && !isObservingOwner)
if (info.TransmitEntities.Contains(glowEntity.Index))
info.TransmitEntities.Remove(glowEntity.Index);
if (info.TransmitEntities.Contains(glowEntityIndex))
info.TransmitEntities.Remove(glowEntityIndex);
}
}
}

View file

@ -93,6 +93,7 @@ namespace src.player.skills
public static void NewRound()
{
buildInProgress = false;
buildScheduled = false;
foreach (var kvp in glows)
{
@ -139,10 +140,21 @@ namespace src.player.skills
Event.EnableTransmit();
playersInAction.TryAdd(player.Index, 0);
if (glows.IsEmpty && !buildInProgress)
SetGlowEffectForAll();
if (buildScheduled || buildInProgress || !glows.IsEmpty) return;
SkillUtils.ForceFullUpdateToAll();
buildScheduled = true;
float delay = SkillUtils.IsFreezeTime()
? Math.Max((float)(Event.GetFreezeTimeEnd() - DateTime.Now).TotalSeconds, 0f)
: 0f;
jRandomSkills.Instance.AddTimer(delay, () =>
{
buildScheduled = false;
if (playersInAction.IsEmpty || buildInProgress || !glows.IsEmpty) return;
SetGlowEffectForAll();
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
public static void DisableSkill(CCSPlayerController player)
@ -158,6 +170,7 @@ namespace src.player.skills
private const int GlowsPerFrame = 4;
private static bool buildInProgress;
private static bool buildScheduled;
private static void SetGlowEffectForAll()
{
@ -180,6 +193,7 @@ namespace src.player.skills
if (start >= enemies.Count)
{
buildInProgress = false;
SkillUtils.ForceFullUpdateToAllChunked();
return;
}

View file

@ -65,10 +65,15 @@ namespace src.utils
return jRandomSkills.Instance?.GameRules?.FreezePeriod == true;
}
public static bool IsHudFrame() => Server.TickCount % 4 == 0;
public static void RegisterSkill(Skills skill, string color, bool display = true)
{
if (!SkillData.Skills.Any(s => s.Skill == skill))
{
SkillData.Skills.Add(new jSkill_SkillInfo(skill, color, display));
SkillData.Invalidate();
}
}
public static void UpdateGrenadeCount(CCSPlayerController player, CsItem item, int ammo)
@ -187,11 +192,6 @@ namespace src.utils
SnapViewAngles.Value?.Invoke(pawn, angle);
}
public static CBeam? CreateLine(Vector start, Vector end, Color color, uint ownerPlayerIndex = EntityManager.SystemOwnerIndex)
{
return EntityManager.CreateTrackedBeam(ownerPlayerIndex, start, end, color);
}
public static void SetPlayerCollisions(CCSPlayerController? player, bool enable)
{
return;
@ -684,6 +684,50 @@ namespace src.utils
});
}
public static void ForceFullUpdateToAllChunked(int clientsPerFrame = 2)
{
if (!Config.LoadedConfig.EnableFullForceUpdate) return;
var pending = Utilities.GetPlayers().Where(p => p != null && p.IsValid && !p.IsBot).Select(p => p.Index).ToList();
if (pending.Count == 0) return;
ForceFullUpdateChunk(pending, 0, Math.Max(clientsPerFrame, 1));
}
private static void ForceFullUpdateChunk(List<uint> pending, int start, int clientsPerFrame)
{
if (start >= pending.Count) return;
int end = Math.Min(start + clientsPerFrame, pending.Count);
var toRestore = new List<(uint PlayerIndex, QAngle LastAngle)>();
INetworkGameServer networkGameServer = new INetworkServerService().GetIGameServer();
for (int i = start; i < end; i++)
{
var player = Utilities.GetPlayerFromIndex((int)pending[i]);
if (player == null || !player.IsValid) continue;
ForceFullUpdate(player, toRestore, networkGameServer);
}
if (toRestore.Count > 0)
jRandomSkills.Instance.AddTickTimer(3, () =>
{
foreach (var item in toRestore)
{
var target = Utilities.GetPlayerFromIndex((int)item.PlayerIndex);
if (target == null || !target.IsValid) continue;
var targetPawn = target.PlayerPawn?.Value;
if (targetPawn == null || !targetPawn.IsValid || targetPawn.AbsOrigin == null) continue;
targetPawn.Look(item.LastAngle);
}
});
Server.NextFrame(() => ForceFullUpdateChunk(pending, end, clientsPerFrame));
}
public static bool SetHealth(CCSPlayerPawn? pawn, int newHealth, int? maxHealth = null)
{
if (pawn == null || !pawn.IsValid)

View file

@ -405,6 +405,32 @@
"MaxPerServer": -1,
"Rarity": "Common"
},
{
"Bounces": 3,
"SegmentDistance": 1200.0,
"DamageMultiplier": 0.5,
"FallbackDamage": 25.0,
"RespectArmor": true,
"DamageFalloff": 0.75,
"MaxImpactsPerTick": 2,
"ShowTracer": true,
"TracerWidth": 0.5,
"TracerSpeed": 2600.0,
"TracerLength": 220.0,
"MaxActiveTracers": 12,
"NormalProbeOffset": 6.0,
"NeedsTeammates": false,
"DisableOnFreezeTime": false,
"OnlyTeam": 0,
"Color": "#ffd75e",
"Active": true,
"Name": "Ricochet",
"HudDuration": null,
"DescriptionHudDuration": null,
"RequiredPermission": "",
"MaxPerServer": 2,
"Rarity": "Rare"
},
{
"MinMoney": 5000,
"MaxMoney": 15000,
@ -1102,6 +1128,19 @@
"MaxPerServer": 1,
"Rarity": "Common"
},
{
"NeedsTeammates": true,
"DisableOnFreezeTime": false,
"OnlyTeam": 0,
"Color": "#c9a227",
"Active": true,
"Name": "Inheritance",
"HudDuration": null,
"DescriptionHudDuration": null,
"RequiredPermission": "",
"MaxPerServer": 2,
"Rarity": "Rare"
},
{
"NeedsTeammates": false,
"DisableOnFreezeTime": false,
@ -1286,6 +1325,26 @@
"MaxPerServer": -1,
"Rarity": "Common"
},
{
"Cooldown": 10.0,
"MaxDistance": 1500.0,
"MinDistance": 150.0,
"StopDistance": 90.0,
"PullSpeed": 850.0,
"MaxPullSeconds": 3.0,
"RopeWidth": 0.8,
"NeedsTeammates": false,
"DisableOnFreezeTime": true,
"OnlyTeam": 0,
"Color": "#38e0c4",
"Active": true,
"Name": "Grapple",
"HudDuration": null,
"DescriptionHudDuration": null,
"RequiredPermission": "",
"MaxPerServer": -1,
"Rarity": "Rare"
},
{
"Cooldown": 30.0,
"Duration": 2.0,
@ -1800,6 +1859,19 @@
"MaxPerServer": -1,
"Rarity": "Common"
},
{
"NeedsTeammates": false,
"DisableOnFreezeTime": false,
"OnlyTeam": 0,
"Color": "#5fd98a",
"Active": true,
"Name": "Chameleon",
"HudDuration": null,
"DescriptionHudDuration": null,
"RequiredPermission": "",
"MaxPerServer": 2,
"Rarity": "Rare"
},
{
"ChanceFrom": 0.2,
"ChanceTo": 0.4,

View file

@ -1,4 +1,4 @@
{
{
"none": "Keine",
"none_desc": "Du hast keine Fertigkeit",
@ -77,6 +77,11 @@
"catapult_desc": "Du hast eine zufällige Chance, einen Gegner nach oben zu schleudern",
"catapult_desc2": "Deine Chance, einen Gegner bei einem Treffer zu schleudern, beträgt: {0} %",
"chameleon": "Chamäleon",
"chameleon_desc": "Der erste Spieler, den du tötest, gibt dir seine Fähigkeit",
"chameleon_copied_info": "Fähigkeit kopiert: {0}",
"chameleon_wrong_team_info": "'{0}' hatte eine Fähigkeit, die dein Team nicht nutzen kann.",
"chicken": "Huhn",
"chicken_desc": "Du erhältst ein Huhn-Modell + 10 % schnellere Bewegung – 50 HP",
@ -220,6 +225,11 @@
"godmode_off": "Unsterblichkeit deaktiviert",
"godmode_on": "Unsterblichkeit aktiviert",
"grapple": "Enterhaken",
"grapple_desc": "Drücke [css_useSkill], um einen Haken auf den anvisierten Punkt zu schießen und dich dorthin zu ziehen",
"grapple_no_anchor_info": "Keine Oberfläche zum Einhaken.",
"grapple_pulling_info": "ZUG",
"grenadier": "Grenadier",
"grenadier_desc": "Du hast unendlich viele Granaten",
@ -259,6 +269,11 @@
"infiniteammo": "Unendliche Munition",
"infiniteammo_desc": "Du erhältst unendliche Munition für alle deine Waffen",
"inheritance": "Erbe",
"inheritance_desc": "Wenn ein Teamkollege stirbt, kannst du seine Fähigkeit übernehmen",
"inheritance_select_info": "Wähle die Fähigkeit, die du erbst:",
"inheritance_player_info": "Du hast die Fähigkeit von '{0}' geerbt.",
"jackal": "Spürhund",
"jackal_desc": "Jeder Gegner hinterlässt eine Spur",
@ -448,6 +463,9 @@
"richboy": "Rich Boy",
"richboy_desc": "Du erhältst zu Beginn der Runde einen zufälligen Geldbetrag",
"ricochet": "Querschläger",
"ricochet_desc": "Deine Kugeln prallen von Wänden ab und können Gegner weiterhin treffen",
"robinhood": "Robin Hood",
"robinhood_desc": "Wenn du einem Gegner Schaden zufügst, stiehlst du ihm sein Geld",

View file

@ -1,4 +1,4 @@
{
{
"none": "None",
"none_desc": "You have no skill",
@ -77,6 +77,11 @@
"catapult_desc": "You have a random chance to launch an enemy upwards",
"catapult_desc2": "Your chance to launch an enemy on hit is: {0}%",
"chameleon": "Chameleon",
"chameleon_desc": "The first player you kill gives you their skill",
"chameleon_copied_info": "You copied the skill: {0}",
"chameleon_wrong_team_info": "'{0}' had a skill your team cannot use.",
"chicken": "Chicken",
"chicken_desc": "You get a chicken model + 10% faster movement - 50 HP",
@ -213,6 +218,11 @@
"godmode_off": "Immortality disabled",
"godmode_on": "Immortality enabled",
"grapple": "Grapple Hook",
"grapple_desc": "Press [css_useSkill] to fire a hook at the point you are aiming at and pull yourself there",
"grapple_no_anchor_info": "No surface to hook onto.",
"grapple_pulling_info": "PULLING",
"grenadier": "Grenadier",
"grenadier_desc": "You have infinite hegrenade",
@ -252,6 +262,11 @@
"infiniteammo": "Infinite Ammo",
"infiniteammo_desc": "You receive infinite ammo for all your weapons",
"inheritance": "Inheritance",
"inheritance_desc": "When a teammate dies you can take over their skill",
"inheritance_select_info": "Choose the skill you inherit:",
"inheritance_player_info": "You inherited the skill of '{0}'.",
"jackal": "Tracker",
"jackal_desc": "Every opponent leaves a trail behind them",
@ -448,6 +463,9 @@
"richboy": "Rich Boy",
"richboy_desc": "You receive a random amount of money at the start of the round",
"ricochet": "Ricochet",
"ricochet_desc": "Your bullets bounce off walls and can still hit enemies",
"robinhood": "Robin Hood",
"robinhood_desc": "Dealing damage to an enemy steals their money",

View file

@ -1,4 +1,4 @@
{
{
"none": "Aucune",
"none_desc": "Vous n’avez aucune compétence",
@ -77,6 +77,11 @@
"catapult_desc": "Vous avez une chance aléatoire de projeter un ennemi en l’air",
"catapult_desc2": "Votre chance de projeter un ennemi à l’impact est : {0}%",
"chameleon": "Caméléon",
"chameleon_desc": "Le premier joueur que vous tuez vous donne sa compétence",
"chameleon_copied_info": "Compétence copiée : {0}",
"chameleon_wrong_team_info": "'{0}' avait une compétence que votre équipe ne peut pas utiliser.",
"chicken": "Poulet",
"chicken_desc": "Vous obtenez un modèle de poulet + 10% de vitesse de déplacement en plus - 50 PV",
@ -220,6 +225,11 @@
"godmode_off": "Immortalité désactivée",
"godmode_on": "Immortalité activée",
"grapple": "Grappin",
"grapple_desc": "Appuyez sur [css_useSkill] pour lancer un grappin sur le point visé et vous y propulser",
"grapple_no_anchor_info": "Aucune surface pour accrocher le grappin.",
"grapple_pulling_info": "TRACTION",
"grenadier": "Grenadier",
"grenadier_desc": "Tu as des grenades HE infinies",
@ -259,6 +269,11 @@
"infiniteammo": "Munitions Infinies",
"infiniteammo_desc": "Vous recevez des munitions infinies pour toutes vos armes",
"inheritance": "Héritage",
"inheritance_desc": "Quand un coéquipier meurt, vous pouvez reprendre sa compétence",
"inheritance_select_info": "Choisissez la compétence à hériter :",
"inheritance_player_info": "Vous avez hérité de la compétence de '{0}'.",
"jackal": "Traqueur",
"jackal_desc": "Chaque adversaire laisse une trace derrière lui",
@ -448,6 +463,9 @@
"richboy": "Gosse de Riche",
"richboy_desc": "Vous recevez une somme d’argent aléatoire au début du round",
"ricochet": "Ricochet",
"ricochet_desc": "Vos balles rebondissent sur les murs et peuvent encore toucher les ennemis",
"robinhood": "Robin des Bois",
"robinhood_desc": "Infliger des dégâts à un ennemi lui vole de l’argent",

View file

@ -1,4 +1,4 @@
{
{
"none": "Brak",
"none_desc": "Nie posiadasz żadnej supermocy",
@ -77,6 +77,11 @@
"catapult_desc": "Masz losową szanse na podrzucenie wroga",
"catapult_desc2": "Twoje szanse na podrzucenie wroga po trafieniu to: {0}%",
"chameleon": "Kameleon",
"chameleon_desc": "Pierwszy zabity gracz oddaje ci swoją umiejętność",
"chameleon_copied_info": "Skopiowano umiejętność: {0}",
"chameleon_wrong_team_info": "'{0}' miał umiejętność, której twoja drużyna nie może użyć.",
"chicken": "Kurczak",
"chicken_desc": "Otrzymujesz model kurczaka + jesteś o 10% szybszy - 50hp",
@ -220,6 +225,11 @@
"godmode_off": "Nieśmiertelność wyłączona",
"godmode_on": "Nieśmiertelność włączona",
"grapple": "Hak",
"grapple_desc": "Naciśnij [css_useSkill], aby wystrzelić hak w celowany punkt i przyciągnąć się do niego",
"grapple_no_anchor_info": "Brak powierzchni do zaczepienia haka.",
"grapple_pulling_info": "PRZYCIĄGANIE",
"grenadier": "Granatnik",
"grenadier_desc": "Masz nieskończone granaty odłamkowe",
@ -259,6 +269,11 @@
"infiniteammo": "Nieskończone Ammo",
"infiniteammo_desc": "Otrzymujesz nieskończoną ilość amunicji do wszystkich broni",
"inheritance": "Spadek",
"inheritance_desc": "Gdy członek drużyny ginie, możesz przejąć jego umiejętność",
"inheritance_select_info": "Wybierz dziedziczoną umiejętność:",
"inheritance_player_info": "Odziedziczyłeś umiejętność gracza '{0}'.",
"jackal": "Stópkarz",
"jackal_desc": "Każdy przeciwnik pozostawia za sobą ślad",
@ -448,6 +463,9 @@
"richboy": "Bogacz",
"richboy_desc": "Na początku rundy otrzymujesz losową kwotę gotówki",
"ricochet": "Rykoszet",
"ricochet_desc": "Twoje pociski odbijają się od ścian i nadal mogą trafić przeciwników",
"robinhood": "Robin Hood",
"robinhood_desc": "Zadanie obrażeń przeciwnikowi powoduje kradzież jego pieniędzy",

View file

@ -1,4 +1,4 @@
{
{
"none": "Nenhuma",
"none_desc": "Você não tem nenhuma habilidade",
@ -77,6 +77,11 @@
"catapult_desc": "Você tem uma chance aleatória de lançar um inimigo para cima",
"catapult_desc2": "Sua chance de lançar um inimigo ao acertá-lo é: {0}%",
"chameleon": "Camaleão",
"chameleon_desc": "O primeiro jogador que você matar lhe dá a habilidade dele",
"chameleon_copied_info": "Habilidade copiada: {0}",
"chameleon_wrong_team_info": "'{0}' tinha uma habilidade que sua equipe não pode usar.",
"chicken": "Galinha",
"chicken_desc": "Você é uma galinha com +10% de movimento mais rápido e -50 HP",
@ -220,6 +225,11 @@
"godmode_off": "Imortalidade desativada",
"godmode_on": "Imortalidade ativada",
"grapple": "Gancho",
"grapple_desc": "Pressione [css_useSkill] para lançar um gancho no ponto mirado e se puxar até lá",
"grapple_no_anchor_info": "Nenhuma superfície para prender o gancho.",
"grapple_pulling_info": "PUXANDO",
"grenadier": "Bomberman",
"grenadier_desc": "Você tem HEs infinitas",
@ -259,6 +269,11 @@
"infiniteammo": "Munição Infinita",
"infiniteammo_desc": "Você recebe munição infinita para todas as suas armas",
"inheritance": "Herança",
"inheritance_desc": "Quando um companheiro de equipe morre, você pode assumir a habilidade dele",
"inheritance_select_info": "Escolha a habilidade que você herda:",
"inheritance_player_info": "Você herdou a habilidade de '{0}'.",
"jackal": "Lesma",
"jackal_desc": "Cada adversário deixa um rastro atrás de si",
@ -448,6 +463,9 @@
"richboy": "Filhinho de Papai",
"richboy_desc": "Você recebe uma quantidade aleatória de dinheiro no início da rodada",
"ricochet": "Ricochete",
"ricochet_desc": "Suas balas ricocheteiam nas paredes e ainda podem acertar inimigos",
"robinhood": "Robin Hood",
"robinhood_desc": "Atirar em um inimigo rouba seu dinheiro",

View file

@ -1,4 +1,4 @@
{
{
"none": "Нет навыка",
"none_desc": "У вас нет навыка",
@ -77,6 +77,11 @@
"catapult_desc": "Есть шанс подбросить врага вверх при попадании",
"catapult_desc2": "Ваш шанс подбросить врага при попадании: {0}%",
"chameleon": "Хамелеон",
"chameleon_desc": "Первый убитый вами игрок отдаёт свой навык",
"chameleon_copied_info": "Навык скопирован: {0}",
"chameleon_wrong_team_info": "У '{0}' был навык, недоступный вашей команде.",
"chicken": "Курица",
"chicken_desc": "Вы получаете модель курицы + 10% к скорости передвижения - 50 HP",
@ -220,6 +225,11 @@
"godmode_off": "Неуязвимость отключена",
"godmode_on": "Неуязвимость включена",
"grapple": "Крюк",
"grapple_desc": "Нажмите [css_useSkill], чтобы выстрелить крюком в точку прицеливания и притянуться к ней",
"grapple_no_anchor_info": "Нет поверхности для зацепа.",
"grapple_pulling_info": "ПРИТЯГИВАНИЕ",
"grenadier": "Гренадёр",
"grenadier_desc": "У вас бесконечные осколочные гранаты",
@ -259,6 +269,11 @@
"infiniteammo": "Бесконечные патроны",
"infiniteammo_desc": "Вы получаете бесконечные патроны для всего оружия",
"inheritance": "Наследие",
"inheritance_desc": "Когда союзник погибает, вы можете перенять его навык",
"inheritance_select_info": "Выберите наследуемый навык:",
"inheritance_player_info": "Вы унаследовали навык игрока '{0}'.",
"jackal": "Трекер",
"jackal_desc": "Каждый противник оставляет за собой след",
@ -448,6 +463,9 @@
"richboy": "Богач",
"richboy_desc": "Вы получаете случайную сумму денег в начале раунда",
"ricochet": "Рикошет",
"ricochet_desc": "Ваши пули отскакивают от стен и могут поразить врагов",
"robinhood": "Робин Гуд",
"robinhood_desc": "Нанесение урона врагу крадёт его деньги",

View file

@ -1,4 +1,4 @@
{
{
"none": "Yok",
"none_desc": "Hiçbir yeteneğin yok",
@ -6,79 +6,84 @@
"aimbot_desc": "Vurduğunda kafadan (HS) sayılır",
"aimlock": "Kitlenme",
"aimlock_desc": "En yakın düşmana nişan almak için [css_useSkill] tuşuna basın",
"aimlock_desc": "[css_useSkill] tuşuna basarak en yakın düşmana kitlen",
"anomaly": "Zaman Büken",
"anomaly_desc": "[css_useSkill] tuşuna basarak zamanı birkaç saniye geriye alırsın",
"anomaly_desc": "[css_useSkill] tuşuna basarak zamanı birkaç saniye geri al",
"antyflash": "Anti-Flash",
"antyflash_desc": "Flaşa kör olmazsın ve flaşların 7 saniye sürer",
"antyflash_desc": "Kör olmazsın ve flaşın 7 saniye sürer",
"antyhead": "Beton Kafa",
"antyhead_desc": "Kafadan hasar almazsın",
"areareaper": "Mekan Kapatan",
"areareaper_desc": "Haritadaki bir bomba alanını tamamen devre dışı bırakabilirsin",
"areareaper_desc": "Haritadaki bir bomba alanını devre dışı bırak",
"areareaper_incorrect_site": "Böyle bir bomba alanı yok uydurma",
"areareaper_no_site": "Haritada bomba alanı bulunamadı",
"areareaper_select_info": "Kapatılacak bomba alanını seç:",
"areareaper_site_disabled": "{0} Bölgesi kapandı - artık buraya bomba kurulamaz!",
"areareaper_used_info": "Yeteneğini zaten kullandın, açgözlülük yapma",
"areareaper_select_info": "Alan seçin:",
"areareaper_site_disabled": "{0} Bölgesi kapandı - artık buraya C4 kurulamaz!",
"areareaper_used_info": "Yeteneğini zaten kullandın!",
"areareaper_teammates_info": "{0} Bölgesi bomba kurmaya kapatıldı.",
"areareaper_bombsite_disabled": "Bu bomba alanı kullanıma kapatıldı!",
"areareaper_bombsite_disabled": "Bu bomba alanı kullanıma kapatıldı.",
"armored": "Zırhlı",
"armored_desc": "Rastgele hasar alma çarpanına sahip olursun",
"armored_desc": "Rastgele hasar alma çarpanın olur",
"armored_desc2": "Hasar alma: {0}x",
"assassin": "Suikastçı",
"assassin_desc": "Düşmanlara arkadan daha fazla hasar verirsin",
"assassin_desc": "Düşmana arkadan daha fazla hasar verirsin",
"astronaut": "Astronot",
"astronaut_desc": "Rastgele bir yerçekimi değerine sahip olursun",
"astronaut_desc": "Rastgele bir yerçekimine sahipsin",
"astronaut_desc2": "Yerçekimin: {0}x",
"moneyswap": "Vergi Memuru",
"moneyswap_desc": "Bir düşmanla paranı takasla",
"moneyswap_enemy_info": "Maliye kapına dayandı",
"moneyswap_player_info": "'{0}' isimli oyuncuyla paraları takas ettin",
"moneyswap_select_info": "Para takasla biriyle:",
"moneyswap_enemy_info": "Maliye kapına dayandı.",
"moneyswap_player_info": "'{0}' ile paranı takasladın.",
"moneyswap_select_info": "Birini seçin:",
"baseball": "Beyzbolcu",
"baseball_desc": "Yanıltıcı bomban (decoy) duvarlardan seker ve çarptığı düşmanı öldürür",
"baseball_desc": "Yanıltıcı bomban duvarlardan seker ve çarptığı düşmanı öldürür",
"behind": "Baş Döndüren",
"behind_desc": "Vurduğunda düşmanı 180 derece döndürme şansın olur",
"behind_desc2": "Baş döndürme şansın: %{0}",
"berserker": "Berserker",
"berserker_desc": "Canın azaldıkça hasarın artar ve daha hızlanırsın",
"berserker_desc": "Canın azaldıkça hasarın ve hızın artar",
"blademaster": "Kılıç Ustası",
"blademaster_desc": "Bıçak tutarken mermileri sektirme şansın artar",
"blademaster_desc": "Bıçak tutarken mermileri savuşturursun",
"blastshot": "Patlama Atışı",
"blastshot_desc": "MP5 ile Sağ Tık'a basarak El bombası fırlat",
"blastshot_desc": "MP5 ile sağ tıklayarak el bombası fırlat",
"bunnyhop": "Tavşan",
"bunnyhop_desc": "Otomatik \"BunnyHop\" yaparsın",
"bunnyhop_desc": "Otomatik BunnyHop yaparsın",
"c4camouflage": "C4 Kamuflaj",
"c4camouflage_desc": "Elinde C4 varken tamamen görünmez olursun",
"c4camouflage_desc": "Elinde C4 varken görünmezsin",
"carefulbullets": "Dikkatli Mermiler",
"carefulbullets_desc": "Düşman mermi kaçırırsa hasar alır",
"carefulbullets_enemy_info": "Mermi kaçırmak canını yakacak",
"carefulbullets_enemy_info": "Mermi kaçırman can yakar.",
"carefulbullets_disable_info": "Artık mermi kaçırmak can yakmayacak.",
"carefulbullets_player_info": "'{0}' artık mermi sıkarken iki kere düşünecek",
"carefulbullets_select_info": "İsabetsiz atışların hasar vereceği oyuncu:",
"carefulbullets_player_info": "'{0}' mermi sıkarken iki kere düşünecek.",
"carefulbullets_select_info": "Birini seçin:",
"catapult": "Mancınık",
"catapult_desc": "Vurduğun rakibi uzaya (yukarı doğru) fırlatma şansın var",
"catapult_desc2": "Fırlatma şansın: %{0}",
"chameleon": "Bukalemun",
"chameleon_desc": "Öldürdüğün ilk oyuncunun yeteneği sana geçer",
"chameleon_copied_info": "Yeteneği kopyaladın: {0}",
"chameleon_wrong_team_info": "'{0}' takımının kullanamayacağı bir yeteneğe sahipti.",
"chicken": "Tavuk",
"chicken_desc": "Tavuk modeli ve %10 daha hızlısın fakat 50 HP",
"chicken_desc": "Tavuk modeli, %10 daha hızlı ama 50 HP",
"chillout": "Ağırdan Al",
"chillout_desc": "Bombayı kurmak çok daha uzun sürer",
@ -87,28 +92,28 @@
"cutter_desc": "Bıçakla tek atarsın",
"cypher": "Gözcü",
"cypher_desc": "Kamera oluşturmak/geçmek için [css_useSkill] tuşuna bas",
"cypher_desc": "[css_useSkill] tuşuna basarak kamera kur ve kameraya geç",
"cypher_nospace": "Kamerayı duvara dik yerleştir",
"darkness": "Karanlık",
"darkness_desc": "Bir düşmanın ekranını karart",
"darkness_enemy_info": "Işıklar sönsün",
"darkness_desc": "Düşmanın ekranını karart",
"darkness_enemy_info": "Işıklar sönsün.",
"darkness_disable_info": "Işıklar geldi.",
"darkness_player_info": "'{0}' isimli oyuncunun dünyasını kararttın",
"darkness_select_info": "Karanlık bir oyuncuyu seç:",
"darkness_player_info": "'{0}' ışıkları söndü.",
"darkness_select_info": "Birini seçin:",
"deactivator": "Silgi",
"deactivator_desc": "Bir düşmanın yeteneğini sil",
"deactivator_enemy_info": "Yeteneğin silindi",
"deactivator_player_info": "'{0}' isimli oyuncunun yeteneği silindi",
"deactivator_select_info": "Yetenek silmek için oyuncu seç:",
"deactivator_enemy_info": "Yeteneğin silindi.",
"deactivator_player_info": "'{0}' isimli oyuncunun yeteneği silindi.",
"deactivator_select_info": "Birini seçin:",
"deaf": "Sağır",
"deaf_desc": "Bir düşmanın kulağını kapat ve sesleri duymaz hale getir",
"deaf_enemy_info": "Kulaklıkların tıkandı",
"deaf_desc": "Bir düşmanı sağır et",
"deaf_enemy_info": "Kulaklıkların tıkandı.",
"deaf_disable_info": "Kulakların açıldı.",
"deaf_player_info": "'{0}' isimli oyuncu için sesler kapatıldı.",
"deaf_select_info": "Sağır bir oyuncuyu seç:",
"deaf_player_info": "'{0}' sesleri kapatıldı.",
"deaf_select_info": "Birini seçin:",
"disarmament": "Silahsızlandırma",
"disarmament_desc": "Vurduğunda rakibin elindeki silahı yere düşürme şansın olur",
@ -121,15 +126,15 @@
"dash_desc": "Atılmak için ikinci kez zıpla",
"dracula": "Drakula",
"dracula_desc": "Rakiplere verdiğin hasarın belirli bir yüzdesi kadar can alırsın",
"dracula_desc": "Rakibe verdiğin hasarın birazı kadar can alırsın",
"duplicator": "Kopyacı",
"duplicator_desc": "Yeteneğini kopyalamak için bir oyuncu seç",
"duplicator_player_info": "'{0}' isimli oyuncunun yeteneği kopyalandı.",
"duplicator_select_info": "Yeteneği kopyalanacak oyuncu:",
"duplicator_desc": "Bir düşmanın yeteneğini kopyala",
"duplicator_player_info": "'{0}' yeteneği kopyalandı.",
"duplicator_select_info": "Birini seçin:",
"dwarf": "Cüce",
"dwarf_desc": "Raund başında rastgele karakter boyutu",
"dwarf_desc": "Rastgele karakter boyutu",
"dwarf_desc2": "Boyutun: {0}x",
"enemyspawn": "Base Baskını",
@ -137,16 +142,16 @@
"expensiveammo": "Pahalı Mermi",
"expensiveammo_desc": "Seçtiğin rakip her ateş ettiğinde para kaybeder",
"expensiveammo_disable_info": "Mermilerin yeniden bedava.",
"expensiveammo_enemy_info": "Artık her atışın sana {0}$ mal oluyor.",
"expensiveammo_player_info": "'{0}' artık her mermisinin parasını ödüyor.",
"expensiveammo_select_info": "Mermisi pahalanacak oyuncu:",
"expensiveammo_disable_info": "Mermi yeniden bedava.",
"expensiveammo_enemy_info": "Her atışın sana {0}$ mal oluyor.",
"expensiveammo_player_info": "'{0}' her mermisinin parasını ödüyor.",
"expensiveammo_select_info": "Birini seçin:",
"explodingbarrel": "Patlayan Varil",
"explodingbarrel_desc": "[css_useSkill] ile vurulunca patlayan bir varil koyarsın",
"explodingbarrel_desc": "[css_useSkill] tuşuna basarak vurulunca patlayan varil koy",
"explosiveshot": "Patlayıcı Mermi",
"explosiveshot_desc": "Ateş ederken rastgele mermilerin patlayabilir",
"explosiveshot_desc": "Ateş ederken mermin patlayabilir",
"explosiveshot_desc2": "Patlayan mermi şansı: %{0}",
"falconeye": "Şahin Gözü",
@ -156,31 +161,31 @@
"fastreload_desc": "[css_useSkill] tuşuna basarak şarjörünü hızlıca değiştir",
"firerain": "Ateş Yağmuru",
"firerain_desc": "Molotof yağdırmak için bir decoy at",
"firerain_desc": "Molotof yağdırmak için yanıltıcı bomba at",
"flash": "Flash",
"flash_desc": "Raund başında rastgele oyuncu hızı",
"flash_desc": "Rastgele oyuncu hızı",
"flash_desc2": "Hızın: {0}x",
"flashlight": "El Feneri",
"flashlight_desc": "[css_useSkill] tuşuna basarak El fenerini açarsın/kaparsın (Işığı düşmanları kör edebilir)",
"flashlight_desc": "[css_useSkill] tuşuna basarak el fenerini aç veya kapat, ışığı düşmanı kör eder",
"fortnite": "Fortnite",
"fortnite_desc": "[css_useSkill] tuşuna basarak barikat kur",
"fragilebomb": "Hassas C4",
"fragilebomb_desc": "Kurulu C4'e ateş etmek C4'ün canını azaltır ve C4 imha edilir.",
"fragilebomb_desc": "Kurulu C4'e ateş etmek canını azaltır ve C4'ü imha eder",
"fragilebomb_bomb_health": "Bombanın canı",
"friendlyfire": "Dost Şifası",
"friendlyfire_desc": "Takım arkadaşlarına ateş etmek onları iyileştirir",
"friendlyfire_desc": "Takım arkadaşına ateş etmek onu iyileştirir",
"frozendecoy": "Buz Kıran",
"frozendecoy_desc": "Yanıltıcı bomba (decoy) yakındaki tüm oyuncuları dondurur",
"frozendecoy_desc": "Yanıltıcı bomban yakındaki herkesi yavaşlatır",
"gambler": "Kumarbaz",
"gambler_desc": "Sunulan listeden bir yetenek seç.",
"gambler_select_info": "Yeteneğini seç:",
"gambler_select_info": "Birini seçin:",
"gambler_more": "#00FF00|${0} yenile",
"gambler_no_money": "Yeterli para yok.",
@ -188,106 +193,116 @@
"ghost_desc": "Tamamen görünmezsin",
"giant": "Dev",
"giant_desc": "Seçtiğin bir rakibi büyütür",
"giant_desc2": "Seçtiğin bir rakibi {0}x büyütür",
"giant_disable_info": "Normal boyutuna geri döndün.",
"giant_enemy_info": "{0}x boyutuna büyütüldün.",
"giant_player_info": "'{0}' oyuncusunu {1}x büyüttün.",
"giant_select_info": "Büyütülecek oyuncu:",
"giant_desc": "Seçtiğin rakibi büyüt",
"giant_desc2": "Rakibin boyutu: {0}x",
"giant_disable_info": "Normal boyutuna döndün.",
"giant_enemy_info": "{0}x boyutuna büyüdün.",
"giant_player_info": "'{0}' boyutunu {1}x büyüttün.",
"giant_select_info": "Birini seçin:",
"glaz": "Glaz",
"glaz_desc": "Sis bombalarının içini görebilirsin",
"glaz_desc": "Sis bombasının içini görürsün",
"glitch": "Glitç",
"glitch_desc": "Seçilen bir düşmanın radarını kapatır",
"glitch_enemy_info": "Radarın kapandı",
"glitch_disable_info": "Radarın açıldı",
"glitch_player_info": "'{0}' isimli oyuncunun radarı devre dışı bırakıldı.",
"glitch_select_info": "Radarı kapatılacak oyuncuyu seç:",
"glitch_desc": "Bir düşmanın radarını kapat",
"glitch_enemy_info": "Radarın kapandı.",
"glitch_disable_info": "Radarın açıldı.",
"glitch_player_info": "'{0}' radarı kapandı.",
"glitch_select_info": "Birini seçin:",
"glue": "Yapıştırıcı",
"glue_desc": "Bombaların duvarlara yapışır",
"glue_desc": "Bomban duvara yapışır",
"godmode": "Ölümsüz",
"godmode_desc": "[css_useSkill] tuşuna basarak kısa süreli ölümsüz ol",
"godmode_off": "Ölümsüzlük kapandı",
"godmode_on": "Ölümsüzlük açıldı",
"grapple": "Kanca",
"grapple_desc": "[css_useSkill] tuşuna basarak baktığın yere kanca at",
"grapple_no_anchor_info": "Kanca atılacak yüzey yok",
"grapple_pulling_info": "ÇEKİLİYOR",
"grenadier": "Bombacı",
"grenadier_desc": "Sınırsız el bombasına sahipsin",
"grenadier_desc": "El bomban sınırsız",
"healingchicken": "Şifacı Tavuk",
"healingchicken_desc": "Yakınındayken tavukların seni iyileştirir",
"healingchicken_desc": "Yakınındayken tavuğun seni iyileştirir",
"healingsmoke": "Şifalı Sis",
"healingsmoke_desc": "Sis bombaların iyileştirir",
"healingsmoke_desc": "Sis bomban iyileştirir",
"hermit": "Münzevi",
"hermit_desc": "Birini öldürünce mermin ve canın yenilenir",
"holyhandgrenade": "Kutsal El Bombası",
"holyhandgrenade_desc": "El bomban iki kat hasara ve menzile sahip",
"holyhandgrenade_desc": "El bombanın hasarı ve menzili iki kat",
"homingnades": "Güdümlü Bomba",
"homingnades_desc": "Bombalarınız (sis hariç) düşmanlara yönelir",
"homingnades_desc": "Bomban (sis hariç) yakın düşmana yönelir",
"hotbomb": "Sıcak C4",
"hotbomb_desc": "Yaşadığın sürece bombacının canı azalıyor",
"hotbomb_hint": "Bomba çok sıcak!",
"hotbomb_disable_info": "Bomba artık yakmıyor.",
"hotbomb_desc": "Yaşadığın sürece bombacının canı azalır",
"hotbomb_hint": "C4 ÇOK SICAK!",
"hotbomb_disable_info": "C4 soğudu.",
"iana": "Hologram",
"iana_desc": "[css_useSkill] tuşuna basarak bir hologram oluştur",
"illiterate": "Cahiliye",
"illiterate_desc": "Yaşadığın sürece rakibin sohbeti (chat) veya oyun yazılarını okuyamaz",
"illiterate_desc": "Yaşadığın sürece rakip hiçbir yazıyı 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",
"impostor": "Sahtekar",
"impostor_desc": "Düşman oyuncu modeline dönüşürsün",
"impostor_desc": "Düşman modeline dönüşürsün",
"infiniteammo": "Sınırsız Mermi",
"infiniteammo_desc": "Tüm silahların sınırsız mermisi olur",
"infiniteammo_desc": "Silahlarının mermisi sınırsız",
"inheritance": "Miras",
"inheritance_desc": "Bir takım arkadaşın öldüğünde yeteneğini devralabilirsin",
"inheritance_select_info": "Birini seçin:",
"inheritance_player_info": "'{0}' yeteneğini devraldın.",
"jackal": "İzci",
"jackal_desc": "Her rakip yürüdüğünde ayak izi bırakır",
"jammer": "Sinyal Kesici",
"jammer_desc": "Bir oyuncunun nişangahını (crosshair) kapatmak için seç",
"jammer_desc": "Bir düşmanın nişangahını kapat",
"jammer_enemy_info": "Nişangahın kapatıldı.",
"jammer_disable_info": "Nişangahın açıldı",
"jammer_disable_info": "Nişangahın açıldı.",
"jammer_player_info": "'{0}' isimli oyuncunun nişangahı kapatıldı.",
"jammer_select_info": "Nişangahı kapatılacak oyuncuyu seç:",
"jammer_select_info": "Birini seçin:",
"jester": "Soytarı",
"jester_desc": "Soytarı modunda hasar alamazsın ve veremezsin. Bu mod birkaç saniyede bir değişir.",
"jester_desc": "Soytarı modunda hasar alamaz ve veremezsin, mod birkaç saniyede bir değişir",
"jester_mode": "Soytarı Modu",
"jester_on": "AÇIK",
"jester_off": "KAPALI",
"jetkick": "Jetkick",
"jetkick_desc": "Seçtiğin bir rakibin her atışı onu geriye iter",
"jetkick_select_info": "Jetkick uygulanacak oyuncu:",
"jetkick_desc": "Seçtiğin rakibi her atışı geriye iter",
"jetkick_select_info": "Birini seçin:",
"jetkick_player_info": "'{0}' adlı oyuncuya jetkick uygulandı.",
"jetkick_enemy_info": "Her atış seni geriye doğru iter",
"jetkick_disable_info": "Geri tepmen normale döndü.",
"jumpban": "Bacaksız",
"jumpban_desc": "Zıplayamayacak bir oyuncu seç",
"jumpban_enemy_info": "Bacakların kesildi",
"jumpban_disable_info": "Bacaklarına kavuştun",
"jumpban_desc": "Zıplayamayacak bir düşman seç",
"jumpban_enemy_info": "Bacakların kesildi.",
"jumpban_disable_info": "Tekrar zıplayabilirsin.",
"jumpban_player_info": "'{0}' artık zıplayamaz.",
"jumpban_select_info": "Zıplaması yasaklanacak oyuncu:",
"jumpban_select_info": "Birini seçin:",
"jumpcurse": "Zıplama Laneti",
"jumpcurse_desc": "Seçtiğin rakip, takım arkadaşları her zıpladığında zıplar",
"jumpcurse_disable_info": "Bacakların yeniden senin.",
"jumpcurse_enemy_info": "Artık takım arkadaşların zıpladıkça sen de zıplıyorsun.",
"jumpcurse_player_info": "'{0}' artık takımıyla birlikte zıplıyor.",
"jumpcurse_select_info": "Zıplama laneti verilecek oyuncu:",
"jumpcurse_desc": "Seçtiğin rakip, takım arkadaşlarıyla zıpladıkça zıplar",
"jumpcurse_disable_info": "Kendin için zıplayabilirsin.",
"jumpcurse_enemy_info": "Takım arkadaşların zıpladıkça sen de zıplıyorsun.",
"jumpcurse_player_info": "'{0}' takımıyla birlikte zıplıyor.",
"jumpcurse_select_info": "Birini seçin:",
"jumpingjack": "Zıpzıp Jack",
"jumpingjack_desc": "Zıplamak canını yeniler",
@ -302,10 +317,10 @@
"lastgasp_desc": "Öldükten sonra seni öldürene hasar verirsin",
"lifeswap": "Can Takası",
"lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç",
"lifeswap_enemy_info": "Birisi canını çaldı",
"lifeswap_player_info": "'{0}' isimli oyuncuyla canlarını takas ettin.",
"lifeswap_select_info": "Can takası yapılacak oyuncu:",
"lifeswap_desc": "Bir düşmanla canını takasla",
"lifeswap_enemy_info": "Birisi canını çaldı.",
"lifeswap_player_info": "'{0}' ile canını takasladın.",
"lifeswap_select_info": "Birini seçin:",
"longknife": "Uzun Bıçak",
"longknife_desc": "Bıçağın uzaktaki rakibe hasar verebilir",
@ -314,46 +329,46 @@
"longzeus_desc": "Zeus uzaktaki rakibe hasar verebilir",
"magneticdecoy": "Manyetik Yanıltıcı",
"magneticdecoy_desc": "Yanıltıcı bombanız yakındaki oyuncuları kendine çeker",
"magneticdecoy_desc": "Yanıltıcı bomban yakındaki herkesi kendine çeker",
"magneto": "Mıknatıs",
"magneto_desc": "Tüm düşman bombaları sizden uzaklaştırılır",
"magneto_desc": "Tüm düşman bombası senden uzaklaşır",
"magnifier": "Büyüteç",
"magnifier_desc": "Düşmanın ekranını yakınlaştırarak görüş alanını daraltır",
"magnifier_desc": "Bir düşmanın görüş alanını daralt",
"magnifier_enemy_info": "Görüşün bozuldu!",
"magnifier_disable_info": "Görüşün düzeldi",
"magnifier_player_info": "'{0}' isimli oyuncunun ekranı yakınlaştırıldı.",
"magnifier_select_info": "Ekranı büyütülecek oyuncuyu seç:",
"magnifier_select_info": "Birini seçin:",
"medic": "Sıhhiye",
"medic_desc": "[css_useSkill] tuşuna basarak kendine anında 50 HP bas",
"miner": "Mayın",
"miner_desc": "El bombanız (HE) sadece yakında bir düşman olduğunda patlar",
"miner_desc": "El bomban sadece yakında düşman varken patlar",
"bankrupt": "İflas",
"bankrupt_desc": "Tüm parasını kaybedecek bir oyuncu seç",
"bankrupt_desc": "Tüm parasını kaybedecek bir düşman seç",
"bankrupt_enemy_info": "İflas ettin!",
"bankrupt_player_info": "'{0}' isimli oyuncu iflas etti.",
"bankrupt_select_info": "İflas edecek oyuncuyu seç:",
"bankrupt_player_info": "'{0}' iflas etti.",
"bankrupt_select_info": "Birini seçin:",
"deathbomb": "Canlı Bomba",
"deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün",
"deathbomb_desc": "Öldüğünde patlayıp etrafındakilere hasar verirsin",
"deathbomb_explosion": "BOOM!!!",
"demoneye": "Şeytani Göz",
"demoneye_desc": "Baktığın tüm düşmanlara hasar verirsin",
"demoneye_desc": "Baktığın tüm düşmana hasar verirsin",
"nightmare": "Kabus",
"nightmare_desc": "Seçtiğin bir rakibe kabus görüntüsü verir",
"nightmare_desc": "Seçtiğin rakibe kabus gördür",
"nightmare_disable_info": "Kabus sona erdi.",
"nightmare_enemy_info": "Gözlerini bir kabus bürüdü.",
"nightmare_player_info": "'{0}' oyuncusuna kabus gördürdün.",
"nightmare_select_info": "Kabus görecek oyuncu:",
"nightmare_select_info": "Birini seçin:",
"ninja": "Ninja",
"ninja_desc": "Görünmezliğini sabit durmak %33, eğilmek %33 ve bıçak tutmak %33 artırır",
"ninja_desc": "Sabit durmak, eğilmek ve bıçak tutmak görünmezliğini %33 artırır",
"nonades": "Bomba Geçirmez",
"nonades_desc": "Bombalar sana hasar vermez",
@ -375,38 +390,38 @@
"paweljumper_desc2": "{0} kez ekstra zıplayabilirsin",
"phoenix": "Anka Kuşu",
"phoenix_desc": "Öldükten sonra rastgele yeniden doğma şansın olur",
"phoenix_desc": "Öldükten sonra yeniden doğma şansın olur",
"phoenix_desc2": "Yeniden doğma şansın: %{0}",
"phoenix_respawn": "Küllerinden yeniden doğdun!",
"psychicdefusing": "Psişik İmha",
"psychicdefusing_desc": "Bombanın yanındayken onu otomatik imha etmeye başlarsın",
"psychicdefusing_desc": "Bombanın yanındayken otomatik imhaya başlarsın",
"psychicdefusing_hud_info": "Bombanın çözülmesi için son {0} saniye",
"pilot": "Pilot",
"pilot_desc": "Sınırlı bir süre uçun. Uçmak için zıplama tuşuna iki kez basın",
"pilot_desc": "Uçmak için zıplama tuşuna iki kez bas",
"pilot_hud_info": "Yakıt",
"planter": "Serbest Kurucu",
"planter_desc": "Bombayı her yere kurabilirsin, patlama süresi 60 saniyedir.",
"planter_desc": "Bombayı her yere kurabilirsin, patlama süresi 60 saniye",
"planter_planting": "Kuruluyor ({0})...",
"poison": "Zehir",
"poison_desc": "Birkaç saniyede bir hasar alacak bir oyuncu seç",
"poison_desc": "Birkaç saniyede bir hasar alacak bir düşman seç",
"poison_enemy_info": "Zehirlendin.",
"poison_disable_info": "Zehire bağışıklık kazandın",
"poison_player_info": "'{0}' isimli oyuncu zehirlendi.",
"poison_select_info": "Zehirlenecek oyuncuyu seç:",
"poison_select_info": "Birini seçin:",
"primaryban": "Tüfek Yasak",
"primaryban_desc": "Tüfek kullanamayacak bir oyuncu seç",
"primaryban_desc": "Tüfek kullanamayacak bir düşman seç",
"primaryban_enemy_info": "Tüfek kullanmayı unuttun",
"primaryban_disable_info": "Tüfek kullanabilirsin",
"primaryban_player_info": "'{0}' artık tüfek kullanamaz.",
"primaryban_select_info": "Tüfek kullanımı yasaklanacak oyuncu:",
"primaryban_select_info": "Birini seçin:",
"prosthesis": "Protez",
"prosthesis_desc": "Kollar ve bacaklar kurşun geçirmezdir",
"prosthesis_desc": "Kolların ve bacakların kurşun geçirmez",
"push": "İtici",
"push_desc": "Düşmana vurduğunda onu geri itme şansın olur",
@ -422,43 +437,46 @@
"radarhack_desc": "Düşmanlar radarda görünür",
"rambo": "Rambo",
"rambo_desc": "Raund başında rastgele miktarda can alırsın",
"rambo_desc": "Rastgele miktarda canın olur",
"randomweapon": "Silah Kumarı",
"randomweapon_desc": "[css_useSkill] tuşuna basarak rastgele bir silah al",
"rezombie": "Zombi",
"rezombie_desc": "Öldükten sonra daha fazla canla ve silahsız zombi olarak doğarsın",
"rezombie_desc": "Öldüğünde silahsız zombi olarak daha çok canla dirilirsin",
"reactivearmor": "Reaktif Zırh",
"reactivearmor_desc": "Zırhın alınan ilk hasarı emmer",
"reactivearmor_desc": "Zırhın ilk hasarı emer",
"regeneration": "Yenilenme",
"regeneration_desc": "Birkaç saniyede bir canın yenilenir",
"replicator": "Klon",
"replicator_desc": "[css_useSkill] tuşuna basarak klon oluştur ve öldürene hasar verir",
"replicator_desc": "[css_useSkill] tuşuna basarak klon oluştur, klonu öldürene hasar verir",
"retreat": "Geri Çekil",
"retreat_desc": "[css_useSkill] tuşuna basarak başlangıç noktasına ışınlan",
"retreat_desc": "[css_useSkill] tuşuna basarak başlangıcına ışınlan",
"returntosender": "İade",
"returntosender_desc": "Düşmana ilk vuruşun onu kendi başlangıç noktasına ışınlar",
"returntosender_desc": "Düşmana ilk vuruşun onu kendi başlangıcına ışınlar",
"richboy": "Zengin Çocuk",
"richboy_desc": "Raund başında rastgele miktarda para alırsın",
"richboy_desc": "Rastgele miktarda paran olur",
"ricochet": "Seken Mermi",
"ricochet_desc": "Mermin duvardan seker ve düşmana isabet edebilir",
"robinhood": "Robin Hood",
"robinhood_desc": "Düşmana hasar vermek onun parasını çalar",
"rubber": "Lastik Mermi",
"rubber_desc": "Mermilerin oyuncuları önemli ölçüde yavaşlatır",
"rubber_desc": "Mermin isabet edeni yavaşlatır",
"saper": "İstihkamcı",
"saper_desc": "Bombayı daha hızlı kurabilir ve imha edebilirsin",
"saper_desc": "Bombayı daha hızlı kurar ve imha edersin",
"secondlife": "İkinci Şans",
"secondlife_desc": "Öldükten sonra aynı miktarda canla yeniden doğarsın",
"secondlife_desc": "Öldüğünde aynı canla yeniden doğarsın",
"shade": "Gölge",
"shade_desc": "Vurduğun düşmanın arkasına ışınlanma şansın olur",
@ -466,24 +484,24 @@
"shade_nospace": "Uygun alan yok",
"shortbomb": "Kısa Fünye",
"shortbomb_desc": "Bomba çok daha hızlı patlar",
"shortbomb_desc": "C4 çok daha hızlı patlar",
"silent": "Sessiz",
"silent_desc": "Ayak seslerini ve zıplamalarını DİĞER oyuncular duyamaz",
"silent_desc": "Ayak ve zıplama sesini kimse duyamaz",
"smoker": "Tiryaki",
"smoker_desc": "Attığın Sis asla bitmez",
"smoker_desc": "Attığın sis asla bitmez",
"sniperelite": "Keskin Nişancı",
"sniperelite_customname": "Keskin Nişancı",
"sniperelite_desc": "[css_useSkill] tuşuna basarak silahını AWP ile değiştir",
"soldier": "Teğmen",
"soldier_desc": "Rastgele bir oranda fazla hasar verirsin",
"soldier_desc": "Rastgele hasar verme çarpanın olur",
"soldier_desc2": "Hasarın: {0}x",
"soundmaker": "Çığlık",
"soundmaker_desc": "Rakiplerinin çığlıklarını duyarsın",
"soundmaker_desc": "Rakibin çığlığını duyarsın",
"spectator": "Röntgen",
"spectator_desc": "[css_useSkill] tuşuna basarak rakibinin gözünden gör",
@ -492,47 +510,47 @@
"swapposition_desc": "[css_useSkill] tuşuna basarak vurduğun düşmanla yer değiştir",
"takeammo": "Cephane Hırsızı",
"takeammo_desc": "[css_useSkill] tuşuna basarak rastgele bir düşmanın silahının şarjörünü çalarsın",
"takeammo_hud_info1": "Cephanesi olan bir oyuncu bulunamadı.",
"takeammo_hud_info2": "Düşmanın cephanesi yok.",
"takeammo_enemy_info": "Birisi cephaneni çaldı.",
"takeammo_desc": "[css_useSkill] tuşuna basarak rastgele bir düşmanın şarjörünü çal",
"takeammo_hud_info1": "Şarjörü olan bir oyuncu bulunamadı.",
"takeammo_hud_info2": "Düşmanın şarjörü yok.",
"takeammo_enemy_info": "Birisi şarjörünü çaldı.",
"teamteleport": "Takıma Işınlanma",
"teamteleport_desc": "[css_useSkill] tuşuna basarak baktığın takım arkadaşına ışınlanırsın",
"teamteleport_desc": "[css_useSkill] tuşuna basarak baktığın takım arkadaşına ışınlan",
"teamteleport_noenemy": "Takım arkadaşı bulunamadı",
"teamteleport_hud_info": "Bulundu: {0}",
"teleporter": "Işınlayıcı",
"teleporter_desc": "Vurulan bir düşmanla yer değiştirme şansınız var",
"teleporter_desc": "Vurduğun düşmanla yer değiştirme şansın olur",
"teleporter_desc2": "Yer değiştirme şansın: %{0}",
"thief": "Hırsız",
"thief_desc": "Seçilen bir oyuncudan yeteneğini çalabilirsin",
"thief_desc": "Bir düşmanın yeteneğini çal",
"thief_enemy_info": "Yeteneğin çalındı.",
"thief_player_info": "'{0}' isimli oyuncunun yeteneği çalındı.",
"thief_select_info": "Yetenek çalınacak oyuncu:",
"thief_select_info": "Birini seçin:",
"thief_incorrect_skill": "Bu yetenek çalınamaz!",
"thirdeye": "Üçüncü Göz",
"thirdeye_desc": "[css_useSkill] tuşuna basarak üçüncü göze(thirdperson) geç",
"thirdeye_desc": "[css_useSkill] tuşuna basarak üçüncü şahıs görünümüne geç",
"thorns": "Dikenler",
"thorns_desc": "Rakibin, sana verdiği hasarın bir kısmını geri alır",
"thorns_desc": "Sana verilen hasarın bir kısmı rakibe döner",
"throwingknife": "Fırlatma Bıçağı",
"throwingknife_desc": "Bıçak fırlatmak için [css_useSkill] tuşuna basın. Ama başkalarına dikkat edin",
"throwingknife_desc": "[css_useSkill] tuşuna basarak bıçak fırlat",
"toxicsmoke": "Zehirli Sis",
"toxicsmoke_desc": "Sis bombaların hasar verir",
"toxicsmoke_desc": "Sis bomban hasar verir",
"tripwire": "Tel Tuzağı",
"tripwire_desc": "[css_useSkill] ile iki duvar arasına tel ger. Tele değen düşman radarında görünür",
"tripwire_desc": "[css_useSkill] tuşuna basarak iki duvar arasına tel ger, değen düşman radarda görünür",
"tripwire_no_wall_info": "İki yanında da yeterince yakın duvar yok.",
"tripwire_placed_info": "Tel gerildi.",
"tripwire_triggered_info": "'{0}' teline takıldı.",
"wallhack": "Wallhack",
"wallhack_desc": "Düşmanları duvarların arkasından görebilirsin",
"wallhack_desc": "Düşmanları duvar arkasından görürsün",
"watchmaker": "Saat Ustası",
"watchmaker_desc": "Atılan her bomba raund süresini değiştirir",
@ -544,21 +562,21 @@
"weaponsswap_hud_info2": "Takas edilecek silahın yok",
"weightless": "Yerçekimsiz",
"weightless_desc": "El bombalarınız yerçekiminden etkilenmez ve dümdüz gider",
"weightless_desc": "El bomban yerçekiminden etkilenmez ve dümdüz gider",
"wildthrow": "Unutkan Bombacı",
"wildthrow_desc": "El bombası atmakta zorluk çekecek bir oyuncu seçin",
"wildthrow_player_info": "'{0}' adlı oyuncu tüm taktiklerini unuttu",
"wildthrow_desc": "El bombası atmakta zorlanacak bir düşman seç",
"wildthrow_player_info": "'{0}' tüm taktiklerini unuttu.",
"wildthrow_enemy_info": "Bomba atarken çok heyecanlanıyorsun.",
"wildthrow_disable_info": "Bombaların düzeldi.",
"wildthrow_select_info": "Bir oyuncu seçin:",
"wildthrow_disable_info": "Bomban tekrardan düzgün.",
"wildthrow_select_info": "Birini seçin:",
"zeus": "Zeus",
"zeus_desc": "Zeus x27 anında şarj olur",
"your_skill": "Yeteneğin",
"enemy_skill": "Düşmanın yeteneği",
"observer_skill": "Oyuncunun yeteneği",
"observer_skill": "Yeteneği",
"welcome_message": "{PLAYER}, {SERVER_NAME} sunucusuna hoş geldin!\nGüncel jRandomSkills versiyonu: {VERSION} ({SKILLS_COUNT} yetenek).\n\nOrijinal yapımcı:\n{AUTHOR1}\nGeliştiren:\n{AUTHOR2}\nResmi Discord: https://discord.gg/9H8EZYBpPF",
"drawing_skill": "Yetenek çarkı dönüyor...",
"disabled_weapon": "Bu silahı kullanamazsın",

View file

@ -71,6 +71,11 @@
"catapult_desc": "你有随机几率将敌人向上弹射",
"catapult_desc2": "你击中敌人将其弹射的几率是:{0}%",
"chameleon": "变色龙",
"chameleon_desc": "你击杀的第一名玩家会把技能传给你",
"chameleon_copied_info": "已复制技能:{0}",
"chameleon_wrong_team_info": "'{0}' 的技能你的队伍无法使用。",
"chicken": "小鸡",
"chicken_desc": "你获得小鸡模型 + 移动速度提高10% - 50生命值",
@ -201,6 +206,11 @@
"godmode_off": "无敌状态已禁用",
"godmode_on": "无敌状态已启用",
"grapple": "钩爪",
"grapple_desc": "按 [css_useSkill] 向准星所指位置发射钩爪并将自己拉过去",
"grapple_no_anchor_info": "没有可以钩住的表面。",
"grapple_pulling_info": "拉动中",
"grenadier": "投弹手",
"grenadier_desc": "你拥有无限高爆手雷",
@ -237,6 +247,11 @@
"infiniteammo": "无限弹药",
"infiniteammo_desc": "你为所有武器获得无限弹药",
"inheritance": "继承",
"inheritance_desc": "当队友阵亡时,你可以接管他的技能",
"inheritance_select_info": "选择要继承的技能:",
"inheritance_player_info": "你继承了 '{0}' 的技能。",
"jackal": "追踪者",
"jackal_desc": "每个对手都会留下痕迹",
@ -418,6 +433,9 @@
"richboy": "富少",
"richboy_desc": "回合开始时你获得随机数量的钱",
"ricochet": "跳弹",
"ricochet_desc": "你的子弹会从墙上弹开,并且仍能击中敌人",
"robinhood": "罗宾汉",
"robinhood_desc": "对敌人造成伤害会偷取他们的钱",