diff --git a/jRandomSkills - SRC Files/src/jRandomSkills.cs b/jRandomSkills - SRC Files/src/jRandomSkills.cs index 42b10b3..7e778e3 100644 --- a/jRandomSkills - SRC Files/src/jRandomSkills.cs +++ b/jRandomSkills - SRC Files/src/jRandomSkills.cs @@ -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 Skills { get; } = []; + private static jSkill_SkillInfo[]? _snapshot; + + public static jSkill_SkillInfo[] GetSnapshot() => _snapshot ??= [.. Skills]; + private static Dictionary? _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 diff --git a/jRandomSkills - SRC Files/src/lang/de.json b/jRandomSkills - SRC Files/src/lang/de.json index a161bae..bbe6411 100644 --- a/jRandomSkills - SRC Files/src/lang/de.json +++ b/jRandomSkills - SRC Files/src/lang/de.json @@ -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", diff --git a/jRandomSkills - SRC Files/src/lang/en.json b/jRandomSkills - SRC Files/src/lang/en.json index 7b0426e..b86306c 100644 --- a/jRandomSkills - SRC Files/src/lang/en.json +++ b/jRandomSkills - SRC Files/src/lang/en.json @@ -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", diff --git a/jRandomSkills - SRC Files/src/lang/fr.json b/jRandomSkills - SRC Files/src/lang/fr.json index 4909ab4..722d442 100644 --- a/jRandomSkills - SRC Files/src/lang/fr.json +++ b/jRandomSkills - SRC Files/src/lang/fr.json @@ -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", diff --git a/jRandomSkills - SRC Files/src/lang/pl.json b/jRandomSkills - SRC Files/src/lang/pl.json index 8bd2b86..02b6d82 100644 --- a/jRandomSkills - SRC Files/src/lang/pl.json +++ b/jRandomSkills - SRC Files/src/lang/pl.json @@ -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", diff --git a/jRandomSkills - SRC Files/src/lang/pt-br.json b/jRandomSkills - SRC Files/src/lang/pt-br.json index b1361d8..14fc109 100644 --- a/jRandomSkills - SRC Files/src/lang/pt-br.json +++ b/jRandomSkills - SRC Files/src/lang/pt-br.json @@ -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", diff --git a/jRandomSkills - SRC Files/src/lang/ru.json b/jRandomSkills - SRC Files/src/lang/ru.json index f769502..7e5148e 100644 --- a/jRandomSkills - SRC Files/src/lang/ru.json +++ b/jRandomSkills - SRC Files/src/lang/ru.json @@ -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": "Нанесение урона врагу крадёт его деньги", diff --git a/jRandomSkills - SRC Files/src/lang/tr.json b/jRandomSkills - SRC Files/src/lang/tr.json index 38a67ca..0a6a3ce 100644 --- a/jRandomSkills - SRC Files/src/lang/tr.json +++ b/jRandomSkills - SRC Files/src/lang/tr.json @@ -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", diff --git a/jRandomSkills - SRC Files/src/lang/zh.json b/jRandomSkills - SRC Files/src/lang/zh.json index 2121680..ecccbf0 100644 --- a/jRandomSkills - SRC Files/src/lang/zh.json +++ b/jRandomSkills - SRC Files/src/lang/zh.json @@ -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": "对敌人造成伤害会偷取他们的钱", diff --git a/jRandomSkills - SRC Files/src/player/Debug.cs b/jRandomSkills - SRC Files/src/player/Debug.cs index 285f837..7e743cb 100644 --- a/jRandomSkills - SRC Files/src/player/Debug.cs +++ b/jRandomSkills - SRC Files/src/player/Debug.cs @@ -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((@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((@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 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(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; } diff --git a/jRandomSkills - SRC Files/src/player/ISkill.cs b/jRandomSkills - SRC Files/src/player/ISkill.cs index d3dbffe..d90bc21 100644 --- a/jRandomSkills - SRC Files/src/player/ISkill.cs +++ b/jRandomSkills - SRC Files/src/player/ISkill.cs @@ -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, diff --git a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs index 47169c6..c82dcfe 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs @@ -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 { diff --git a/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs b/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs index f1ef220..8ea432d 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs @@ -15,7 +15,7 @@ namespace src.player Instance.RegisterListener(() => { UpdateGameRules(); - if (Server.TickCount % 2 != 0) return; + if (!SkillUtils.IsHudFrame()) return; if (PerfLog.Enabled && Server.TickCount % 1920 == 0) { @@ -99,25 +99,19 @@ 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; + var randomSkill = skills[Instance.Random.Next(skills.Length)]; - if (skillCount > 0) - { - var skillsArray = skills.ToArray(); - var randomSkill = skillsArray[Instance.Random.Next(skillCount)]; - - infoLine = player.GetTranslationWithoutIlliterate("drawing_skill"); - skillLine = $"{player.GetSkillName(randomSkill.Skill)}"; - } + infoLine = player.GetTranslationWithoutIlliterate("drawing_skill"); + skillLine = $"{player.GetSkillName(randomSkill.Skill)}"; } else { diff --git a/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs b/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs index f85e70b..80a5c72 100644 --- a/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs @@ -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() diff --git a/jRandomSkills - SRC Files/src/player/skills/Chameleon.cs b/jRandomSkills - SRC Files/src/player/skills/Chameleon.cs new file mode 100644 index 0000000..360eda2 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Chameleon.cs @@ -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 holders = []; + private static jRandomSkills? registeredOn; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + + if (ReferenceEquals(registeredOn, Instance)) return; + + registeredOn = Instance; + Instance.RegisterEventHandler(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(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(victimSkill, "requiredPermission"); + if (!string.IsNullOrEmpty(permission) && !AdminManager.PlayerHasPermissions(player, permission)) + return false; + } + + if (!SkillsInfo.GetValue(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) + { + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs b/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs index 5bd73a8..425e7b1 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs @@ -78,6 +78,7 @@ namespace src.player.skills public static void OnTick() { + if (!SkillUtils.IsHudFrame()) return; if (plantingPlayers.IsEmpty) return; float currentTime = Server.CurrentTime; diff --git a/jRandomSkills - SRC Files/src/player/skills/Cypher.cs b/jRandomSkills - SRC Files/src/player/skills/Cypher.cs index c8e9a11..7bc4e51 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Cypher.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Cypher.cs @@ -71,6 +71,7 @@ namespace src.player.skills KillCamera(playerSkill); } playersInfo.TryRemove(player.Index, out _); + SkillUtils.ResetPrintHTML(player); } private static void KillCamera(PlayerSkill playerSkill) diff --git a/jRandomSkills - SRC Files/src/player/skills/Dash.cs b/jRandomSkills - SRC Files/src/player/skills/Dash.cs index 22505ea..f68c06f 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Dash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Dash.cs @@ -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); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Distancer.cs b/jRandomSkills - SRC Files/src/player/skills/Distancer.cs index 92ab05e..8a37270 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Distancer.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Distancer.cs @@ -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; diff --git a/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs b/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs index 6f724fe..6f18a8a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs +++ b/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs @@ -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()) { diff --git a/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs b/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs index f08078b..6f3555a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs @@ -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); } diff --git a/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs b/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs index 12209ba..b9f73b7 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs @@ -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; diff --git a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs index f826a92..1a5f8fe 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs @@ -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); diff --git a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs index 45ebcb8..7cf3f90 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs @@ -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((int)entityIndex); diff --git a/jRandomSkills - SRC Files/src/player/skills/GodMode.cs b/jRandomSkills - SRC Files/src/player/skills/GodMode.cs index 4087bbd..244831a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/GodMode.cs +++ b/jRandomSkills - SRC Files/src/player/skills/GodMode.cs @@ -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); diff --git a/jRandomSkills - SRC Files/src/player/skills/Grapple.cs b/jRandomSkills - SRC Files/src/player/skills/Grapple.cs new file mode 100644 index 0000000..081817b --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Grapple.cs @@ -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 SkillPlayerInfo = []; + private static readonly ConcurrentDictionary 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(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(skillName, "minDistance")) return false; + + skillInfo.Anchor = anchor; + skillInfo.EndTick = Server.TickCount + (int)(SkillsInfo.GetValue(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(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(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((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((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(skillName, "stopDistance")) + { + DestroyRope(skillInfo); + return; + } + + float pullSpeed = SkillsInfo.GetValue(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((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(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 = $"{player.GetTranslation("grapple_pulling_info")}"; + else if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; + } + + 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; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs b/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs index c6c4587..f521c38 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs @@ -139,12 +139,14 @@ namespace src.player.skills public static void OnTick() { + if (activeChickens.IsEmpty) return; + int tickCooldown = SkillsInfo.GetValue(skillName, "tickCooldown"); if (Server.TickCount % tickCooldown == 0) return; float boostFactor = SkillsInfo.GetValue(skillName, "boostFactor"); - var players = PlayerManager.GetTickPlayers().ToArray(); + var players = PlayerManager.GetTickPlayers(); int healAmount = SkillsInfo.GetValue(skillName, "heal"); float healRadius = SkillsInfo.GetValue(skillName, "healRadius"); diff --git a/jRandomSkills - SRC Files/src/player/skills/Iana.cs b/jRandomSkills - SRC Files/src/player/skills/Iana.cs index 52ce05d..5285049 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Iana.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Iana.cs @@ -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); } diff --git a/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs b/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs index 23ded08..f10d426 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs @@ -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; diff --git a/jRandomSkills - SRC Files/src/player/skills/Inheritance.cs b/jRandomSkills - SRC Files/src/player/skills/Inheritance.cs new file mode 100644 index 0000000..9b738a3 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Inheritance.cs @@ -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 holders = []; + private static readonly ConcurrentDictionary fallen = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(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(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(fallenInfo.Skill, "requiredPermission"); + if (!string.IsNullOrEmpty(permission) && !AdminManager.PlayerHasPermissions(player, permission)) + return false; + } + + if (!SkillsInfo.GetValue(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) + { + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Jackal.cs b/jRandomSkills - SRC Files/src/player/skills/Jackal.cs index df00716..97409f4 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jackal.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jackal.cs @@ -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((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((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); diff --git a/jRandomSkills - SRC Files/src/player/skills/Jester.cs b/jRandomSkills - SRC Files/src/player/skills/Jester.cs index 6851cae..9d70d78 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jester.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jester.cs @@ -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()) { diff --git a/jRandomSkills - SRC Files/src/player/skills/Medic.cs b/jRandomSkills - SRC Files/src/player/skills/Medic.cs index 481308a..058a1b6 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Medic.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Medic.cs @@ -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()) { diff --git a/jRandomSkills - SRC Files/src/player/skills/Noclip.cs b/jRandomSkills - SRC Files/src/player/skills/Noclip.cs index 90a4e75..9c8e780 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Noclip.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Noclip.cs @@ -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); diff --git a/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs b/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs index f8ce6ec..fe99125 100644 --- a/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs @@ -19,31 +19,30 @@ namespace src.player.skills { foreach (var player in PlayerManager.GetTickPlayers()) { + if (player == null) continue; + + if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) continue; if (!Instance.IsPlayerValid(player)) continue; - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill == skillName) + var pawn = player.PlayerPawn.Value!; + var weaponServices = pawn.WeaponServices; + if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) continue; + + var weapon = weaponServices.ActiveWeapon.Value; + if (weapon == null || !weapon.IsValid || pawn.CameraServices == null) continue; + + if (pawn.AimPunchServices != null) { - var pawn = player.PlayerPawn.Value!; - var weaponServices = pawn.WeaponServices; - if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) continue; - - var weapon = weaponServices.ActiveWeapon.Value; - if (weapon == null || !weapon.IsValid || pawn.CameraServices == null) continue; - - if (pawn.AimPunchServices != null) - { - pawn.AimPunchServices.PredictableBaseTick = 0; - pawn.AimPunchServices.PredictableBaseTickInterpAmount = 0; - pawn.AimPunchServices.UnpredictableBaseTick = 0; - } - - pawn.CameraServices.CsViewPunchAngleTick = 0; - pawn.CameraServices.CsViewPunchAngleTickRatio = 0f; - - Schema.SetSchemaValue(weapon.Handle, "CBasePlayerWeapon", "m_nNextPrimaryAttackTick", Server.TickCount); - Schema.SetSchemaValue(weapon.Handle, "CBasePlayerWeapon", "m_nNextSecondaryAttackTick", Server.TickCount); + pawn.AimPunchServices.PredictableBaseTick = 0; + pawn.AimPunchServices.PredictableBaseTickInterpAmount = 0; + pawn.AimPunchServices.UnpredictableBaseTick = 0; } + + pawn.CameraServices.CsViewPunchAngleTick = 0; + pawn.CameraServices.CsViewPunchAngleTickRatio = 0f; + + Schema.SetSchemaValue(weapon.Handle, "CBasePlayerWeapon", "m_nNextPrimaryAttackTick", Server.TickCount); + Schema.SetSchemaValue(weapon.Handle, "CBasePlayerWeapon", "m_nNextSecondaryAttackTick", Server.TickCount); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs b/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs index b544c5f..9864f97 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs @@ -21,11 +21,12 @@ 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); + 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) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs b/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs index 29e921a..d5578d4 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs @@ -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) { diff --git a/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs b/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs index f3ac463..95ef445 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs @@ -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); diff --git a/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs b/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs index b9462fe..d4f52ea 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs @@ -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; diff --git a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs index d4185cd..7eea00c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs @@ -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); diff --git a/jRandomSkills - SRC Files/src/player/skills/Retreat.cs b/jRandomSkills - SRC Files/src/player/skills/Retreat.cs index 8f44e51..4ef6ea4 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Retreat.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Retreat.cs @@ -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); diff --git a/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs b/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs new file mode 100644 index 0000000..d59de9a --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs @@ -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 impactBudgets = []; + private static readonly HashSet ownedTracers = []; + private static readonly List tracerPool = []; + private static readonly List 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(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(skillName, "bounces"), 1); + float segmentDistance = SkillsInfo.GetValue(skillName, "segmentDistance"); + float damageFalloff = SkillsInfo.GetValue(skillName, "damageFalloff"); + + var vdata = GetWeaponVData(pawn); + float damage = GetBaseDamage(vdata); + + KillfeedIcons? killfeedIcon = KillfeedIconsExtensions.FromWeaponName(SkillUtils.GetDesignerName(pawn.WeaponServices?.ActiveWeapon?.Value)); + + List 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(skillName, "tracerSpeed"), 100f); + float trail = MathF.Max(SkillsInfo.GetValue(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 path) + { + if (path.Count < 2) return; + if (!SkillsInfo.GetValue(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(skillName, "tracerSpeed"), 100f); + + lock (flightLock) + { + if (flights.Count >= Math.Max(SkillsInfo.GetValue(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(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(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("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((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().GetVData(); + } + catch + { + return null; + } + } + + private static float GetBaseDamage(CCSWeaponBaseVData? vdata) + { + float fallback = SkillsInfo.GetValue(skillName, "fallbackDamage"); + if (vdata == null) return fallback; + + float weaponDamage = vdata.Damage; + if (weaponDamage <= 0) return fallback; + + return weaponDamage * SkillsInfo.GetValue(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(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(skillName, "normalProbeOffset"), 1f); + float tolerance = offset * 2.5f; + + Span 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 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(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(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; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs b/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs index a80826c..19aa0d2 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs @@ -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(skillName, "cooldown")) != 0) return; + if (SkillPlayerInfo.IsEmpty) return; + + int cooldown = SkillsInfo.GetValue(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)) diff --git a/jRandomSkills - SRC Files/src/player/skills/Spectator.cs b/jRandomSkills - SRC Files/src/player/skills/Spectator.cs index 45966d3..5d317d5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Spectator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Spectator.cs @@ -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"); diff --git a/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs b/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs index c85fccc..e64f1dc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs +++ b/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs @@ -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); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs b/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs index afb43ac..a2bc871 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs @@ -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((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((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); } } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs b/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs index e931bcc..110cdd9 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs @@ -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; } diff --git a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs index c863f97..f2c2517 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs @@ -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 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) diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll index 8426491..48f49b6 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll differ diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json index 6cceeea..55054e5 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json @@ -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, diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll index 3f1ce3e..76bb7a6 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll differ diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/de.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/de.json index a161bae..bbe6411 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/de.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/de.json @@ -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", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json index 7b0426e..b86306c 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json @@ -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", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json index 4909ab4..722d442 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json @@ -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", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json index 8bd2b86..02b6d82 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json @@ -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", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json index b1361d8..14fc109 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json @@ -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", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json index f769502..7e5148e 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json @@ -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": "Нанесение урона врагу крадёт его деньги", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json index 38a67ca..0a6a3ce 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json @@ -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", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json index 2121680..ecccbf0 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json @@ -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": "对敌人造成伤害会偷取他们的钱",