diff --git a/jRandomSkills - SRC Files/src/jRandomSkills.cs b/jRandomSkills - SRC Files/src/jRandomSkills.cs index 7e778e3..e56b522 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.b5"; + public override string ModuleVersion => "1.2.3.b6"; public override void Load(bool hotReload) { @@ -371,6 +371,18 @@ namespace src public int HideHUD { get; set; } public bool SkillUsed = false; public bool? HudOnDeathBlocked { get; set; } + public HudCacheEntry? HudCache { get; set; } + } + + public sealed class HudCacheEntry + { + public uint PlayerIndex; + public object? Config; + public string? Header; + public string? Center; + public string? Extra; + public bool IsDescription; + public string? Content; } public class jSkill_SkillInfo(Skills skill, string color, bool display) diff --git a/jRandomSkills - SRC Files/src/lang/de.json b/jRandomSkills - SRC Files/src/lang/de.json index bbe6411..d72dadf 100644 --- a/jRandomSkills - SRC Files/src/lang/de.json +++ b/jRandomSkills - SRC Files/src/lang/de.json @@ -60,6 +60,13 @@ "blastshot": "Explosivschuss", "blastshot_desc": "Drücke Attack2 mit der MP5, um eine HE-Granate abzufeuern", + "bounty": "Kopfgeld", + "bounty_desc": "Setze ein Kopfgeld auf einen Gegner aus; wer ihn tötet, bekommt das Geld", + "bounty_select_info": "Wähle einen Spieler:", + "bounty_player_info": "Du hast {1}$ Kopfgeld auf '{0}' ausgesetzt.", + "bounty_enemy_info": "Auf deinen Kopf wurden {0}$ ausgesetzt.", + "bounty_claimed_info": "Du hast '{0}' getötet und {1}$ kassiert.", + "bunnyhop": "Hase", "bunnyhop_desc": "Du erhältst automatisch bunnyhop", @@ -144,6 +151,10 @@ "dwarf_desc": "Zufällige Charaktergröße zu Beginn der Runde", "dwarf_desc2": "Dein Größenmultiplikator beträgt: {0}x", + "empgrenade": "EMP-Granate", + "empgrenade_desc": "Wer von deiner Granate getroffen wird, verliert kurzzeitig Radar und Fadenkreuz", + "empgrenade_enemy_info": "Eine EMP-Granate hat dein Radar und Fadenkreuz lahmgelegt.", + "enemyspawn": "Feindes-Spawn", "enemyspawn_desc": "Klicke auf [css_useSkill], um zum Feindes-Spawn zu teleportieren", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Keine Oberfläche zum Einhaken.", "grapple_pulling_info": "ZUG", + "gravitydecoy": "Schwerkraft-Ablenker", + "gravitydecoy_desc": "Dein Ablenkungsköder verändert die Schwerkraft aller in der Nähe", + "grenadier": "Grenadier", "grenadier_desc": "Du hast unendlich viele Granaten", @@ -239,6 +253,9 @@ "healingchicken": "Heilendes Huhn", "healingchicken_desc": "Deine Hühner heilen dich, solange du in ihrer Nähe bist", + "heavyweight": "Schwergewicht", + "heavyweight_desc": "Fähigkeiten, die dich stoßen oder verlangsamen, wirken nicht auf dich", + "hermit": "Einsiedler", "hermit_desc": "Durch das Töten von Gegnern werden Munition und ein Teil der Gesundheit wiederhergestellt", @@ -360,6 +377,12 @@ "bankrupt_player_info": "Der Spieler ‚{0}‘ ist bankrott.", "bankrupt_select_info": "Wähle einen Spieler aus, der bankrott gehen soll:", + "nemesis": "Nemesis", + "nemesis_desc": "Der von dir markierte Spieler erleidet zusätzlichen Schaden", + "nemesis_select_info": "Wähle einen Spieler:", + "nemesis_player_info": "Du hast '{0}' markiert.", + "nemesis_enemy_info": "Du wurdest als Nemesis markiert und erleidest jetzt von allen zusätzlichen Schaden.", + "nightmare": "Albtraum", "nightmare_desc": "Lass einen ausgewählten Gegner eine schreckliche Vision erleben", "nightmare_disable_info": "Der Albtraum ist vorbei.", @@ -423,6 +446,10 @@ "prosthesis": "Prothese", "prosthesis_desc": "Arme und Beine sind kugelsicher", + "punisher": "Bestrafer", + "punisher_desc": "Du sammelst einen Teil des erlittenen Schadens und legst ihn auf deinen nächsten Treffer", + "punisher_released_info": "Du hast {0} zusätzlichen Schaden verursacht und ebenso viel Leben erhalten.", + "push": "Pusher", "push_desc": "Du hast eine zufällige Chance, einen Gegner zurückzustoßen, wenn du ihn triffst", "push_desc2": "Deine Chancen, den Gegner zurückzustoßen, betragen: {0} %", @@ -460,6 +487,10 @@ "returntosender": "Zurück zum Absender", "returntosender_desc": "Der erste Treffer eines Gegners schickt ihn zurück zu seinem Spawn", + "rewind": "Rückspulen", + "rewind_desc": "Drücke [css_useSkill], um hier eine Marke zu setzen und kurz darauf dorthin zurückzukehren", + "rewind_pending_info": "RÜCKKEHR: {0}", + "richboy": "Rich Boy", "richboy_desc": "Du erhältst zu Beginn der Runde einen zufälligen Geldbetrag", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Stolperdraht platziert.", "tripwire_triggered_info": "'{0}' hat deinen Stolperdraht ausgelöst.", + "truearmor": "Echte Rüstung", + "truearmor_desc": "Ein Teil des Schadens geht auf deine Rüstung statt auf dein Leben", + + "voodoo": "Voodoo", + "voodoo_desc": "Der markierte Gegner erleidet einen Teil des Schadens, den du erleidest", + "voodoo_select_info": "Wähle einen Spieler:", + "voodoo_player_info": "Du hast '{0}' an deinen Voodoo gebunden.", + "wallhack": "Wallhack", "wallhack_desc": "Du kannst Feinde durch Wände hindurch sehen", diff --git a/jRandomSkills - SRC Files/src/lang/en.json b/jRandomSkills - SRC Files/src/lang/en.json index b86306c..4175258 100644 --- a/jRandomSkills - SRC Files/src/lang/en.json +++ b/jRandomSkills - SRC Files/src/lang/en.json @@ -60,6 +60,13 @@ "blastshot": "Blast Shot", "blastshot_desc": "Press Attack2 with the MP5 to fire an HE grenade", + "bounty": "Bounty", + "bounty_desc": "Put a price on an enemy's head; whoever kills them takes the money", + "bounty_select_info": "Choose a player:", + "bounty_player_info": "You put a {1}$ bounty on '{0}'.", + "bounty_enemy_info": "A {0}$ bounty was placed on your head.", + "bounty_claimed_info": "You killed '{0}' and claimed {1}$.", + "bunnyhop": "Bunny", "bunnyhop_desc": "You get auto \"BunnyHop\"", @@ -137,6 +144,10 @@ "dwarf_desc": "Random character size at the start of the round", "dwarf_desc2": "Your size multiplier is: {0}x", + "empgrenade": "EMP Grenade", + "empgrenade_desc": "Anyone hurt by your grenade loses their radar and crosshair for a while", + "empgrenade_enemy_info": "An EMP grenade knocked out your radar and crosshair.", + "enemyspawn": "Enemy Spawn", "enemyspawn_desc": "Click [css_useSkill] to teleport to the enemy spawn", @@ -223,6 +234,9 @@ "grapple_no_anchor_info": "No surface to hook onto.", "grapple_pulling_info": "PULLING", + "gravitydecoy": "Gravity Decoy", + "gravitydecoy_desc": "Your decoy changes the gravity of everyone nearby", + "grenadier": "Grenadier", "grenadier_desc": "You have infinite hegrenade", @@ -232,6 +246,9 @@ "healingsmoke": "Healing Smoke", "healingsmoke_desc": "Your smoke grenades heal", + "heavyweight": "Heavyweight", + "heavyweight_desc": "Skills that push or slow you down have no effect on you", + "hermit": "Hermit", "hermit_desc": "Killing restores ammo and a portion of health", @@ -360,6 +377,12 @@ "demoneye": "Demon Eye", "demoneye_desc": "You deal damage to every enemy you are looking at", + "nemesis": "Nemesis", + "nemesis_desc": "The player you mark takes extra damage", + "nemesis_select_info": "Choose a player:", + "nemesis_player_info": "You marked '{0}'.", + "nemesis_enemy_info": "You have been marked as a nemesis, you now take extra damage from everyone.", + "nightmare": "Nightmare", "nightmare_desc": "Force a chosen enemy to experience a terrifying vision", "nightmare_disable_info": "The nightmare has faded.", @@ -423,6 +446,10 @@ "prosthesis": "Prosthesis", "prosthesis_desc": "Arms and legs are bulletproof", + "punisher": "Punisher", + "punisher_desc": "You bank part of the damage you take and add it to your next hit", + "punisher_released_info": "You dealt {0} extra damage and healed for the same amount.", + "push": "Pusher", "push_desc": "You have a random chance to push an enemy back when hitting them", "push_desc2": "Your chances of pushing back the enemy are: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Return to Sender", "returntosender_desc": "The first hit on an enemy sends them back to their spawn", + "rewind": "Rewind", + "rewind_desc": "Click [css_useSkill] to drop a marker where you stand and return to it shortly after", + "rewind_pending_info": "RETURN: {0}", + "richboy": "Rich Boy", "richboy_desc": "You receive a random amount of money at the start of the round", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Tripwire placed.", "tripwire_triggered_info": "'{0}' triggered your tripwire.", + "truearmor": "True Armor", + "truearmor_desc": "Part of the damage you take is paid by your armor instead of your health", + + "voodoo": "Voodoo", + "voodoo_desc": "The enemy you mark also takes part of the damage you take", + "voodoo_select_info": "Choose a player:", + "voodoo_player_info": "You bound '{0}' to your voodoo.", + "wallhack": "Wallhack", "wallhack_desc": "You can see enemies through walls", diff --git a/jRandomSkills - SRC Files/src/lang/fr.json b/jRandomSkills - SRC Files/src/lang/fr.json index 722d442..bb99783 100644 --- a/jRandomSkills - SRC Files/src/lang/fr.json +++ b/jRandomSkills - SRC Files/src/lang/fr.json @@ -60,6 +60,13 @@ "blastshot": "Tir explosif", "blastshot_desc": "Appuyez sur Attack2 avec le MP5 pour tirer une grenade HE", + "bounty": "Prime", + "bounty_desc": "Mets une prime sur la tête d'un ennemi ; celui qui le tue empoche l'argent", + "bounty_select_info": "Choisis un joueur :", + "bounty_player_info": "Tu as mis une prime de {1}$ sur '{0}'.", + "bounty_enemy_info": "Une prime de {0}$ a été mise sur ta tête.", + "bounty_claimed_info": "Tu as tué '{0}' et empoché {1}$.", + "bunnyhop": "Lapin", "bunnyhop_desc": "Vous obtenez l’auto « BunnyHop »", @@ -144,6 +151,10 @@ "dwarf_desc": "Taille du personnage aléatoire au début du round", "dwarf_desc2": "Votre multiplicateur de taille est : {0}x", + "empgrenade": "Grenade EMP", + "empgrenade_desc": "Ceux que ta grenade blesse perdent leur radar et leur viseur un moment", + "empgrenade_enemy_info": "Une grenade EMP a coupé ton radar et ton viseur.", + "enemyspawn": "Spawn Ennemi", "enemyspawn_desc": "Appuyez sur [css_useSkill] pour vous téléporter au spawn ennemi", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Aucune surface pour accrocher le grappin.", "grapple_pulling_info": "TRACTION", + "gravitydecoy": "Leurre gravitationnel", + "gravitydecoy_desc": "Ton leurre modifie la gravité de tous ceux qui sont à proximité", + "grenadier": "Grenadier", "grenadier_desc": "Tu as des grenades HE infinies", @@ -239,6 +253,9 @@ "healingchicken": "Poulet guérisseur", "healingchicken_desc": "Vos poulets vous soignent lorsque vous êtes à proximité", + "heavyweight": "Poids lourd", + "heavyweight_desc": "Les compétences qui te poussent ou te ralentissent n'ont aucun effet sur toi", + "hermit": "Ermite", "hermit_desc": "Tuer restaure des munitions et une partie de vos PV", @@ -360,6 +377,12 @@ "bankrupt_player_info": "Le joueur « {0} » est en faillite.", "bankrupt_select_info": "Mettre un joueur en faillite :", + "nemesis": "Némésis", + "nemesis_desc": "Le joueur que tu marques subit des dégâts supplémentaires", + "nemesis_select_info": "Choisis un joueur :", + "nemesis_player_info": "Tu as marqué '{0}'.", + "nemesis_enemy_info": "Vous avez été marqué comme némésis, vous subissez désormais des dégâts supplémentaires de la part de tous.", + "nightmare": "Cauchemar", "nightmare_desc": "Faites subir une vision terrifiante à un ennemi choisi", "nightmare_disable_info": "Le cauchemar s'est dissipé.", @@ -423,6 +446,10 @@ "prosthesis": "Prothèses", "prosthesis_desc": "Les bras et les jambes sont pare-balles", + "punisher": "Punisseur", + "punisher_desc": "Tu accumules une partie des dégâts subis et l'ajoutes à ton prochain tir", + "punisher_released_info": "Tu as infligé {0} dégâts en plus et récupéré autant de vie.", + "push": "Pousseur", "push_desc": "Vous avez une chance aléatoire de repousser un ennemi en le touchant", "push_desc2": "Votre chance de repousser l’ennemi est : {0}%", @@ -460,6 +487,10 @@ "returntosender": "Retour à l’Expéditeur", "returntosender_desc": "Le premier coup porté à un ennemi le renvoie à son spawn", + "rewind": "Rembobinage", + "rewind_desc": "Appuie sur [css_useSkill] pour poser un repère ici et y revenir peu après", + "rewind_pending_info": "RETOUR : {0}", + "richboy": "Gosse de Riche", "richboy_desc": "Vous recevez une somme d’argent aléatoire au début du round", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Fil-piège placé.", "tripwire_triggered_info": "'{0}' a déclenché votre fil-piège.", + "truearmor": "Vraie armure", + "truearmor_desc": "Une partie des dégâts est absorbée par ton armure au lieu de ta vie", + + "voodoo": "Vaudou", + "voodoo_desc": "L'ennemi que tu marques subit aussi une partie des dégâts que tu reçois", + "voodoo_select_info": "Choisis un joueur :", + "voodoo_player_info": "Tu as lié '{0}' à ton vaudou.", + "wallhack": "Wallhack", "wallhack_desc": "Vous pouvez voir les ennemis à travers les murs", diff --git a/jRandomSkills - SRC Files/src/lang/pl.json b/jRandomSkills - SRC Files/src/lang/pl.json index fbce5ea..2659a45 100644 --- a/jRandomSkills - SRC Files/src/lang/pl.json +++ b/jRandomSkills - SRC Files/src/lang/pl.json @@ -60,6 +60,13 @@ "blastshot": "Mocne Uderzenie", "blastshot_desc": "Naciśnij Attack2 na MP5, aby wystrzelić granat HE", + "bounty": "Nagroda", + "bounty_desc": "Wyznacz nagrodę za głowę wroga; kto go zabije, dostaje pieniądze", + "bounty_select_info": "Wybierz gracza:", + "bounty_player_info": "Wyznaczyłeś nagrodę {1}$ za '{0}'.", + "bounty_enemy_info": "Za twoją głowę wyznaczono {0}$.", + "bounty_claimed_info": "Zabiłeś '{0}' i zgarnąłeś {1}$.", + "bunnyhop": "Królik", "bunnyhop_desc": "Otrzymujesz auto \"BunnyHopa\"", @@ -144,6 +151,10 @@ "dwarf_desc": "Losowa wielkość postaci na początku rundy", "dwarf_desc2": "Twój mnożnik wielkości to {0}x", + "empgrenade": "Granat EMP", + "empgrenade_desc": "Kto oberwie twoim granatem, na chwilę traci radar i celownik", + "empgrenade_enemy_info": "Granat EMP wyłączył twój radar i celownik.", + "enemyspawn": "Resp Wroga", "enemyspawn_desc": "Kliknij [css_useSkill], aby teleportować się na resp wroga", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Brak powierzchni do zaczepienia haka.", "grapple_pulling_info": "PRZYCIĄGANIE", + "gravitydecoy": "Grawitacyjna wabik", + "gravitydecoy_desc": "Twój wabik zmienia grawitację wszystkich w pobliżu", + "grenadier": "Granatnik", "grenadier_desc": "Masz nieskończone granaty odłamkowe", @@ -239,6 +253,9 @@ "healingchicken": "Lecząca Kura", "healingchicken_desc": "Twoje kury leczą Cię, gdy jesteś w pobliżu", + "heavyweight": "Waga ciężka", + "heavyweight_desc": "Umiejętności, które cię popychają lub spowalniają, nie działają na ciebie", + "hermit": "Pustelnik", "hermit_desc": "Zabijanie przywraca amunicję i część zdrowia", @@ -360,6 +377,12 @@ "moneyswap_player_info": "Wymieniłeś swoje pieniądze z graczem '{0}'.", "moneyswap_select_info": "Zamień się kasą z graczem:", + "nemesis": "Nemezis", + "nemesis_desc": "Oznaczony gracz otrzymuje dodatkowe obrażenia", + "nemesis_select_info": "Wybierz gracza:", + "nemesis_player_info": "Oznaczyłeś '{0}'.", + "nemesis_enemy_info": "Zostałeś oznaczony jako nemezis, teraz otrzymujesz dodatkowe obrażenia od wszystkich.", + "nightmare": "Koszmar", "nightmare_desc": "Wywołaj u wybranego przeciwnika przerażającą wizję", "nightmare_disable_info": "Koszmar minął.", @@ -423,6 +446,10 @@ "prosthesis": "Proteza", "prosthesis_desc": "Ramiona i nogi są kuloodporne", + "punisher": "Karzyciel", + "punisher_desc": "Gromadzisz część otrzymanych obrażeń i dodajesz je do następnego trafienia", + "punisher_released_info": "Zadałeś {0} dodatkowych obrażeń i odzyskałeś tyle samo życia.", + "push": "Odpychacz", "push_desc": "Masz losową szansę na odepchnięcie wroga po trafieniu go", "push_desc2": "Twoje szanse na odepchnięcie wroga to: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Zwrot do Nadawcy", "returntosender_desc": "Pierwsze trafienie wroga powoduje, że wraca on na swój resp", + "rewind": "Przewijanie", + "rewind_desc": "Naciśnij [css_useSkill], aby zostawić znacznik i wkrótce do niego wrócić", + "rewind_pending_info": "POWRÓT: {0}", + "richboy": "Bogacz", "richboy_desc": "Na początku rundy otrzymujesz losową kwotę gotówki", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Linka została rozstawiona.", "tripwire_triggered_info": "'{0}' uruchomił Twoją linkę.", + "truearmor": "Prawdziwy pancerz", + "truearmor_desc": "Część obrażeń pokrywa twój pancerz zamiast zdrowia", + + "voodoo": "Voodoo", + "voodoo_desc": "Oznaczony wróg również otrzymuje część obrażeń, które ty dostajesz", + "voodoo_select_info": "Wybierz gracza:", + "voodoo_player_info": "Związałeś '{0}' swoim voodoo.", + "wallhack": "Wallhack", "wallhack_desc": "Widzisz wrogów przez ściany", diff --git a/jRandomSkills - SRC Files/src/lang/pt-br.json b/jRandomSkills - SRC Files/src/lang/pt-br.json index 14fc109..c477c18 100644 --- a/jRandomSkills - SRC Files/src/lang/pt-br.json +++ b/jRandomSkills - SRC Files/src/lang/pt-br.json @@ -60,6 +60,13 @@ "blastshot": "Tiro Explosivo", "blastshot_desc": "Pressione Attack2 com a MP5 para disparar uma granada HE", + "bounty": "Recompensa", + "bounty_desc": "Coloque um preço na cabeça de um inimigo; quem o matar leva o dinheiro", + "bounty_select_info": "Escolha um jogador:", + "bounty_player_info": "Você colocou {1}$ de recompensa em '{0}'.", + "bounty_enemy_info": "Puseram {0}$ de recompensa na sua cabeça.", + "bounty_claimed_info": "Você matou '{0}' e levou {1}$.", + "bunnyhop": "Coelho", "bunnyhop_desc": "Você ganha \"BunnyHop\" automático", @@ -144,6 +151,10 @@ "dwarf_desc": "Sua altura agora é aleatória", "dwarf_desc2": "Seu multiplicador de altura é: {0}x", + "empgrenade": "Granada EMP", + "empgrenade_desc": "Quem se ferir com sua granada perde o radar e a mira por um tempo", + "empgrenade_enemy_info": "Uma granada EMP derrubou seu radar e sua mira.", + "enemyspawn": "Spawn Inimigo", "enemyspawn_desc": "Clique em [css_useSkill] para se teletransportar para a base inimiga", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Nenhuma superfície para prender o gancho.", "grapple_pulling_info": "PUXANDO", + "gravitydecoy": "Chamariz Gravitacional", + "gravitydecoy_desc": "Seu chamariz muda a gravidade de todos que estão por perto", + "grenadier": "Bomberman", "grenadier_desc": "Você tem HEs infinitas", @@ -239,6 +253,9 @@ "healingchicken": "Galinha Curadora", "healingchicken_desc": "Suas galinhas curam você enquanto você estiver por perto", + "heavyweight": "Peso Pesado", + "heavyweight_desc": "Habilidades que empurram ou te deixam lento não fazem efeito em você", + "hermit": "Sanguessuga", "hermit_desc": "Matar restaura munição e uma parte da sua vida", @@ -360,6 +377,12 @@ "moneyswap_player_info": "Você trocou dinheiro com o jogador '{0}'.", "moneyswap_select_info": "Trocar dinheiro c/ jogador:", + "nemesis": "Nêmesis", + "nemesis_desc": "O jogador que você marcar recebe dano extra", + "nemesis_select_info": "Escolha um jogador:", + "nemesis_player_info": "Você marcou '{0}'.", + "nemesis_enemy_info": "Você foi marcado como nêmesis, agora recebe dano extra de todos.", + "nightmare": "Pesadelo", "nightmare_desc": "Faça um inimigo escolhido ter uma visão aterrorizante", "nightmare_disable_info": "O pesadelo desapareceu.", @@ -423,6 +446,10 @@ "prosthesis": "Prótese", "prosthesis_desc": "Braços e pernas são à prova de balas", + "punisher": "Punidor", + "punisher_desc": "Você guarda parte do dano recebido e soma no seu próximo tiro", + "punisher_released_info": "Você causou {0} de dano extra e recuperou a mesma vida.", + "push": "Empurrão", "push_desc": "Você tem uma chance aleatória de empurrar um inimigo ao acertá-lo", "push_desc2": "Suas chances de empurrar o inimigo são: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Devolução ao Remetente", "returntosender_desc": "O primeiro tiro em um inimigo o envia de volta a sua base", + "rewind": "Retroceder", + "rewind_desc": "Clique em [css_useSkill] para deixar uma marca aqui e voltar a ela logo depois", + "rewind_pending_info": "RETORNO: {0}", + "richboy": "Filhinho de Papai", "richboy_desc": "Você recebe uma quantidade aleatória de dinheiro no início da rodada", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Fio de armadilha colocado.", "tripwire_triggered_info": "'{0}' acionou seu fio.", + "truearmor": "Armadura Real", + "truearmor_desc": "Parte do dano que você recebe sai do colete em vez da vida", + + "voodoo": "Vodu", + "voodoo_desc": "O inimigo que você marcar também recebe parte do dano que você leva", + "voodoo_select_info": "Escolha um jogador:", + "voodoo_player_info": "Você ligou '{0}' ao seu vodu.", + "wallhack": "Wallhack", "wallhack_desc": "Você pode ver inimigos através das paredes", diff --git a/jRandomSkills - SRC Files/src/lang/ru.json b/jRandomSkills - SRC Files/src/lang/ru.json index 7e5148e..19f2012 100644 --- a/jRandomSkills - SRC Files/src/lang/ru.json +++ b/jRandomSkills - SRC Files/src/lang/ru.json @@ -60,6 +60,13 @@ "blastshot": "Взрывной выстрел", "blastshot_desc": "Нажмите Attack2 на MP5, чтобы выстрелить гранатой HE", + "bounty": "Награда", + "bounty_desc": "Назначь награду за голову врага; тот, кто убьёт его, получит деньги", + "bounty_select_info": "Выберите игрока:", + "bounty_player_info": "Вы назначили награду {1}$ за '{0}'.", + "bounty_enemy_info": "За вашу голову назначили {0}$.", + "bounty_claimed_info": "Вы убили '{0}' и получили {1}$.", + "bunnyhop": "Заяц", "bunnyhop_desc": "Вы получаете автоматический «БанниХоп»", @@ -144,6 +151,10 @@ "dwarf_desc": "Случайный размер персонажа в начале раунда", "dwarf_desc2": "Ваш множитель размера: {0}x", + "empgrenade": "ЭМИ-граната", + "empgrenade_desc": "Тот, кто пострадал от вашей гранаты, на время теряет радар и прицел", + "empgrenade_enemy_info": "ЭМИ-граната вывела из строя ваш радар и прицел.", + "enemyspawn": "Спавн врагов", "enemyspawn_desc": "Нажмите [css_useSkill], чтобы телепортироваться на спавн врагов", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Нет поверхности для зацепа.", "grapple_pulling_info": "ПРИТЯГИВАНИЕ", + "gravitydecoy": "Гравитационная обманка", + "gravitydecoy_desc": "Ваша обманка меняет гравитацию всех, кто рядом", + "grenadier": "Гренадёр", "grenadier_desc": "У вас бесконечные осколочные гранаты", @@ -239,6 +253,9 @@ "healingsmoke": "Лечащий дым", "healingsmoke_desc": "Ваши дымовые гранаты лечат", + "heavyweight": "Тяжеловес", + "heavyweight_desc": "Способности, которые толкают или замедляют, на вас не действуют", + "hermit": "Отшельник", "hermit_desc": "Убийства восстанавливают боеприпасы и часть здоровья", @@ -360,6 +377,12 @@ "moneyswap_player_info": "Вы обменялись деньгами с игроком '{0}'.", "moneyswap_select_info": "Обменять деньги с игроком:", + "nemesis": "Немезида", + "nemesis_desc": "Отмеченный вами игрок получает больше урона", + "nemesis_select_info": "Выберите игрока:", + "nemesis_player_info": "Вы отметили '{0}'.", + "nemesis_enemy_info": "Вы отмечены как немезида и теперь получаете больше урона от всех.", + "nightmare": "Кошмар", "nightmare_desc": "Заставьте выбранного противника увидеть кошмарное видение", "nightmare_disable_info": "Кошмар рассеялся.", @@ -423,6 +446,10 @@ "prosthesis": "Протез", "prosthesis_desc": "Руки и ноги защищены от пуль", + "punisher": "Каратель", + "punisher_desc": "Вы копите часть полученного урона и добавляете его к следующему попаданию", + "punisher_released_info": "Вы нанесли {0} дополнительного урона и восстановили столько же здоровья.", + "push": "Толкач", "push_desc": "Есть шанс оттолкнуть врага назад при попадании", "push_desc2": "Ваш шанс оттолкнуть врага: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Вернуть отправителю", "returntosender_desc": "Первое попадание по врагу отправляет его обратно на спавн", + "rewind": "Перемотка", + "rewind_desc": "Нажмите [css_useSkill], чтобы оставить метку здесь и вскоре вернуться к ней", + "rewind_pending_info": "ВОЗВРАТ: {0}", + "richboy": "Богач", "richboy_desc": "Вы получаете случайную сумму денег в начале раунда", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Растяжка установлена.", "tripwire_triggered_info": "'{0}' задел вашу растяжку.", + "truearmor": "Настоящая броня", + "truearmor_desc": "Часть урона принимает броня, а не здоровье", + + "voodoo": "Вуду", + "voodoo_desc": "Отмеченный враг тоже получает часть урона, который получаете вы", + "voodoo_select_info": "Выберите игрока:", + "voodoo_player_info": "Вы привязали '{0}' к своему вуду.", + "wallhack": "Воллхак", "wallhack_desc": "Вы видите врагов сквозь стены", diff --git a/jRandomSkills - SRC Files/src/lang/tr.json b/jRandomSkills - SRC Files/src/lang/tr.json index 0a6a3ce..617a3dd 100644 --- a/jRandomSkills - SRC Files/src/lang/tr.json +++ b/jRandomSkills - SRC Files/src/lang/tr.json @@ -60,6 +60,13 @@ "blastshot": "Patlama Atışı", "blastshot_desc": "MP5 ile sağ tıklayarak el bombası fırlat", + "bounty": "Ödül Avı", + "bounty_desc": "Bir düşmana ödül koy, onu öldüren parayı alır", + "bounty_select_info": "Birini seçin:", + "bounty_player_info": "'{0}' için {1}$ ödül koydun.", + "bounty_enemy_info": "Başına {0}$ ödül kondu.", + "bounty_claimed_info": "'{0}' oyuncusunu öldürdün ve {1}$ kazandın.", + "bunnyhop": "Tavşan", "bunnyhop_desc": "Otomatik BunnyHop yaparsın", @@ -137,6 +144,10 @@ "dwarf_desc": "Rastgele karakter boyutu", "dwarf_desc2": "Boyutun: {0}x", + "empgrenade": "EMP Bombası", + "empgrenade_desc": "El bomban, hasar verdiğin düşmanın radarını ve nişangahını kapatır", + "empgrenade_enemy_info": "Radarın ve nişangahın kapandı.", + "enemyspawn": "Base Baskını", "enemyspawn_desc": "[css_useSkill] tuşuna basarak düşman doğma noktasına ışınlan", @@ -223,6 +234,9 @@ "grapple_no_anchor_info": "Kanca atılacak yüzey yok", "grapple_pulling_info": "ÇEKİLİYOR", + "gravitydecoy": "Yerçekimi Yanıltıcısı", + "gravitydecoy_desc": "Yanıltıcı bomban yakındaki herkesin yerçekimini değiştirir", + "grenadier": "Bombacı", "grenadier_desc": "El bomban sınırsız", @@ -232,6 +246,9 @@ "healingsmoke": "Şifalı Sis", "healingsmoke_desc": "Sis bomban iyileştirir", + "heavyweight": "Ağır Sıklet", + "heavyweight_desc": "Seni iten ve yavaşlatan yetenekler işlemez", + "hermit": "Münzevi", "hermit_desc": "Birini öldürünce mermin ve canın yenilenir", @@ -360,6 +377,12 @@ "demoneye": "Şeytani Göz", "demoneye_desc": "Baktığın tüm düşmana hasar verirsin", + "nemesis": "Ezeli Rakip", + "nemesis_desc": "İşaretlediğin düşman herkesten daha fazla hasar alır", + "nemesis_select_info": "Birini seçin:", + "nemesis_player_info": "'{0}' artık herkesten daha fazla hasar alacak.", + "nemesis_enemy_info": "İşaretlendin, artık herkesten daha fazla hasar alıyorsun.", + "nightmare": "Kabus", "nightmare_desc": "Seçtiğin rakibe kabus gördür", "nightmare_disable_info": "Kabus sona erdi.", @@ -423,6 +446,10 @@ "prosthesis": "Protez", "prosthesis_desc": "Kolların ve bacakların kurşun geçirmez", + "punisher": "İntikam", + "punisher_desc": "Aldığın hasarı biriktirir, sonraki vuruşunda geri verirsin", + "punisher_released_info": "{0} ekstra hasar verdin ve o kadar can kazandın.", + "push": "İtici", "push_desc": "Düşmana vurduğunda onu geri itme şansın olur", "push_desc2": "Geri itme şansın: %{0}", @@ -460,6 +487,10 @@ "returntosender": "İade", "returntosender_desc": "Düşmana ilk vuruşun onu kendi başlangıcına ışınlar", + "rewind": "Geri Sarma", + "rewind_desc": "[css_useSkill] tuşuna basarak iz bırak, birazdan oraya geri dön", + "rewind_pending_info": "DÖNÜŞ: {0}", + "richboy": "Zengin Çocuk", "richboy_desc": "Rastgele miktarda paran olur", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Tel gerildi.", "tripwire_triggered_info": "'{0}' teline takıldı.", + "truearmor": "Gerçek Zırh", + "truearmor_desc": "Aldığın hasarın bir kısmını canın yerine zırhın karşılar", + + "voodoo": "Vudu", + "voodoo_desc": "Aldığın hasarın bir kısmını işaretlediğin düşman da alır", + "voodoo_select_info": "Birini seçin:", + "voodoo_player_info": "'{0}' oyuncusunu kendine bağladın.", + "wallhack": "Wallhack", "wallhack_desc": "Düşmanları duvar arkasından görürsün", diff --git a/jRandomSkills - SRC Files/src/lang/zh.json b/jRandomSkills - SRC Files/src/lang/zh.json index ecccbf0..8726bc7 100644 --- a/jRandomSkills - SRC Files/src/lang/zh.json +++ b/jRandomSkills - SRC Files/src/lang/zh.json @@ -54,6 +54,13 @@ "blademaster": "我的刀能档子弹!", "blademaster_desc": "持刀时,你有很高几率格挡子弹", + "bounty": "悬赏", + "bounty_desc": "对一名敌人悬赏,击杀他的人可以获得赏金", + "bounty_select_info": "选择一名玩家:", + "bounty_player_info": "你对 '{0}' 悬赏了 {1}$。", + "bounty_enemy_info": "有人对你悬赏了 {0}$。", + "bounty_claimed_info": "你击杀了 '{0}',获得 {1}$。", + "bunnyhop": "连跳大佬", "bunnyhop_desc": "你获得自动连跳的能力", @@ -131,6 +138,10 @@ "dwarf_desc": "回合开始时获得随机角色大小", "dwarf_desc2": "你的体型倍率是:{0}x", + "empgrenade": "电磁脉冲手雷", + "empgrenade_desc": "被你的手雷伤到的人会暂时失去雷达和准星", + "empgrenade_enemy_info": "电磁脉冲手雷让你的雷达和准星失效了。", + "enemyspawn": "传送敌人出生点", "enemyspawn_desc": "点击 [css_useSkill] 传送到敌人出生点", @@ -211,12 +222,18 @@ "grapple_no_anchor_info": "没有可以钩住的表面。", "grapple_pulling_info": "拉动中", + "gravitydecoy": "重力诱饵", + "gravitydecoy_desc": "你的诱饵会改变附近所有人的重力", + "grenadier": "投弹手", "grenadier_desc": "你拥有无限高爆手雷", "healingsmoke": "治疗烟雾", "healingsmoke_desc": "你的烟雾弹会治疗", + "heavyweight": "重量级", + "heavyweight_desc": "推开你或让你减速的技能对你无效", + "hermit": "隐士", "hermit_desc": "击杀敌人会恢复弹药和部分生命值", @@ -330,6 +347,12 @@ "deathbomb_desc": "你死亡时会爆炸,杀死附近的玩家", "deathbomb_explosion": "砰!", + "nemesis": "宿敌", + "nemesis_desc": "被你标记的玩家会受到额外伤害", + "nemesis_select_info": "选择一名玩家:", + "nemesis_player_info": "你标记了 '{0}'。", + "nemesis_enemy_info": "你被标记为宿敌,现在会受到所有人的额外伤害。", + "nightmare": "nightmare", "nightmare_desc": "nightmare_desc", "nightmare_disable_info": "nightmare_disable_info", @@ -393,6 +416,10 @@ "prosthesis": "假肢", "prosthesis_desc": "手臂和腿部防弹", + "punisher": "惩罚者", + "punisher_desc": "你会积攒受到伤害的一部分,并加到下一次命中上", + "punisher_released_info": "你造成了 {0} 点额外伤害,并回复了同样的生命值。", + "push": "推力者", "push_desc": "你有随机几率在击中敌人时将其推回", "push_desc2": "你击退敌人的几率为:{0}%", @@ -430,6 +457,10 @@ "returntosender": "发送他们房子!!!", "returntosender_desc": "首次击中敌人会将其送回出生点", + "rewind": "回溯", + "rewind_desc": "按 [css_useSkill] 在原地留下标记,稍后回到那里", + "rewind_pending_info": "回溯:{0}", + "richboy": "富少", "richboy_desc": "回合开始时你获得随机数量的钱", @@ -507,6 +538,14 @@ "tripwire_placed_info": "tripwire_placed_info", "tripwire_triggered_info": "tripwire_triggered_info: {0}", + "truearmor": "真实护甲", + "truearmor_desc": "你受到的一部分伤害由护甲承担,而不是生命值", + + "voodoo": "巫毒", + "voodoo_desc": "被你标记的敌人也会受到你所受伤害的一部分", + "voodoo_select_info": "选择一名玩家:", + "voodoo_player_info": "你把 '{0}' 绑到了你的巫毒上。", + "wallhack": "透视", "wallhack_desc": "你可以通过墙壁看到敌人", diff --git a/jRandomSkills - SRC Files/src/player/Debug.cs b/jRandomSkills - SRC Files/src/player/Debug.cs index 7e743cb..c977778 100644 --- a/jRandomSkills - SRC Files/src/player/Debug.cs +++ b/jRandomSkills - SRC Files/src/player/Debug.cs @@ -45,13 +45,13 @@ namespace src.player var teams = Utilities.FindAllEntitiesByDesignerName("cs_team_manager").Where(t => t != null).ToList(); var tTeam = teams.FirstOrDefault(t => t.TeamNum == (int)CsTeam.Terrorist); var ctTeam = teams.FirstOrDefault(t => t.TeamNum == (int)CsTeam.CounterTerrorist); - WriteToDebug($"Round #{tTeam?.Score + ctTeam?.Score + 1} (CT {ctTeam?.Score} : {tTeam?.Score} TT) started."); + WriteToDebug($"Round #{tTeam?.Score + ctTeam?.Score + 1} (CT {ctTeam?.Score} : {tTeam?.Score} TT) started.{WarmupTag()}"); return HookResult.Continue; }); Instance.RegisterEventHandler((@event, info) => { - WriteToDebug($"Freeze time ended."); + WriteToDebug($"Freeze time ended.{WarmupTag()}"); return HookResult.Continue; }); @@ -60,7 +60,7 @@ namespace src.player var teams = Utilities.FindAllEntitiesByDesignerName("cs_team_manager").Where(t => t != null).ToList(); var tTeam = teams.FirstOrDefault(t => t.TeamNum == (int)CsTeam.Terrorist); var ctTeam = teams.FirstOrDefault(t => t.TeamNum == (int)CsTeam.CounterTerrorist); - WriteToDebug($"Round #{tTeam?.Score + ctTeam?.Score} (CT {ctTeam?.Score} : {tTeam?.Score} TT) ended."); + WriteToDebug($"Round #{tTeam?.Score + ctTeam?.Score} (CT {ctTeam?.Score} : {tTeam?.Score} TT) ended.{WarmupTag()}"); return HookResult.Continue; }); @@ -137,6 +137,13 @@ namespace src.player return HookResult.Continue; } + private static string WarmupTag() + { + var gameRules = Instance?.GameRules; + if (gameRules == null || gameRules.Handle == IntPtr.Zero) return " [gamerules unavailable]"; + return gameRules.WarmupPeriod ? " [WARMUP]" : string.Empty; + } + public static void WriteToDebug(string message) { if (Config.LoadedConfig.DebugMode != true) diff --git a/jRandomSkills - SRC Files/src/player/ISkill.cs b/jRandomSkills - SRC Files/src/player/ISkill.cs index d90bc21..f69d2f5 100644 --- a/jRandomSkills - SRC Files/src/player/ISkill.cs +++ b/jRandomSkills - SRC Files/src/player/ISkill.cs @@ -76,6 +76,7 @@ public enum Skills BladeMaster, BunnyHop, BlastShot, + Bounty, C4Camouflage, CarefulBullets, Catapult, @@ -95,6 +96,7 @@ public enum Skills Dracula, Duplicator, Dwarf, + EmpGrenade, EnemySpawn, ExpensiveAmmo, ExplodingBarrel, @@ -116,9 +118,11 @@ public enum Skills Glue, GodMode, Grapple, + GravityDecoy, Grenadier, HealingChicken, HealingSmoke, + Heavyweight, Hermit, HolyHandGrenade, HomingNades, @@ -148,6 +152,7 @@ public enum Skills Medic, Miner, MoneySwap, + Nemesis, Nightmare, Ninja, NoNades, @@ -163,6 +168,7 @@ public enum Skills Poison, PrimaryBan, Prosthesis, + Punisher, Push, Pyro, QuickShot, @@ -175,6 +181,7 @@ public enum Skills Replicator, Retreat, ReturnToSender, + Rewind, RichBoy, Ricochet, RobinHood, @@ -199,6 +206,8 @@ public enum Skills ThrowingKnife, ToxicSmoke, Tripwire, + TrueArmor, + Voodoo, Wallhack, Watchmaker, WeaponsSwap, diff --git a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs index c82dcfe..9e966c9 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs @@ -33,7 +33,7 @@ namespace src.player public static readonly SkillsInfo.DefaultSkillInfo[] counterterroristSkills = [.. SkillsInfo.LoadedConfig.Where(s => s.OnlyTeam == (int)CsTeam.CounterTerrorist)]; private static readonly SkillsInfo.DefaultSkillInfo[] allTeamsSkills = [.. SkillsInfo.LoadedConfig.Where(s => s.OnlyTeam == 0)]; - private static readonly ConcurrentDictionary> playersSkills = []; + private static readonly ConcurrentDictionary> playersSkills = []; public static readonly ConcurrentDictionary staticSkills = []; public static readonly ConcurrentDictionary playersCustomFOV = []; private static readonly object setLock = new(); @@ -564,6 +564,7 @@ namespace src.player PlayerManager.UnregisterPlayer(player.Index); EntityManager.DestroyPlayerEntities(player.Index); + SkillUtils.ClearHudSuppression(player.Index); return HookResult.Continue; } @@ -602,6 +603,8 @@ namespace src.player var pawn = player.PlayerPawn?.Value; if (pawn == null || !pawn.IsValid) return; + SkillUtils.ClearHudSuppression(player.Index); + pawn.HideHUD = (uint)(pawn.HideHUD & ~(1 << 8)); Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_iHideHUD"); @@ -768,6 +771,21 @@ namespace src.player } var config = Config.LoadedConfig.HtmlHudCustomisation; + + var cache = skillPlayer?.HudCache; + if (cache != null + && cache.Content != null + && cache.PlayerIndex == player.Index + && ReferenceEquals(cache.Config, config) + && cache.IsDescription == isDescription + && string.Equals(cache.Header, headerLine, StringComparison.Ordinal) + && string.Equals(cache.Center, centerLine, StringComparison.Ordinal) + && string.Equals(cache.Extra, extraLine, StringComparison.Ordinal)) + { + player.PrintToCenterHtml(cache.Content); + return; + } + var emptySymbol = $" "; var emptySymbol2 = $" "; @@ -782,6 +800,19 @@ namespace src.player : $"
{emptySymbol}{extraLine}{emptySymbol}"; var hudContent = "" + infoLine + skillLine + remainingLine; + + if (skillPlayer != null) + { + cache ??= skillPlayer.HudCache = new HudCacheEntry(); + cache.PlayerIndex = player.Index; + cache.Config = config; + cache.Header = headerLine; + cache.Center = centerLine; + cache.Extra = extraLine; + cache.IsDescription = isDescription; + cache.Content = hudContent; + } + player.PrintToCenterHtml(hudContent); } } diff --git a/jRandomSkills - SRC Files/src/player/RoundEvents.cs b/jRandomSkills - SRC Files/src/player/RoundEvents.cs index 62a7a38..f43b976 100644 --- a/jRandomSkills - SRC Files/src/player/RoundEvents.cs +++ b/jRandomSkills - SRC Files/src/player/RoundEvents.cs @@ -195,6 +195,8 @@ namespace src.player SkillsUsedThisMap.Clear(); nextRoundPicks.Clear(); + SkillUtils.ClearAllHudSuppression(); + playersCustomFOV.Clear(); playersSkills.Clear(); staticSkills.Clear(); @@ -275,6 +277,7 @@ namespace src.player public required HashSet NeedsTeammates { get; init; } public required HashSet CtOnly { get; init; } public required HashSet TOnly { get; init; } + public required HashSet PistolRoundBanned { get; init; } public required int TerroristCount { get; init; } public required int CounterTerroristCount { get; init; } } @@ -296,6 +299,9 @@ namespace src.player NeedsTeammates = ToSkillSet(SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates).Select(s => s.Name)), CtOnly = ToSkillSet(counterterroristSkills.Select(s => s.Name)), TOnly = ToSkillSet(terroristSkills.Select(s => s.Name)), + PistolRoundBanned = SkillUtils.IsPistolRound() + ? ToSkillSet(SkillsInfo.LoadedConfig.Where(s => s.DisableOnPistolRound).Select(s => s.Name)) + : [], TerroristCount = validPlayers.Count(p => p.Team == CsTeam.Terrorist), CounterTerroristCount = validPlayers.Count(p => p.Team == CsTeam.CounterTerrorist), }; @@ -328,9 +334,12 @@ namespace src.player else skillList.RemoveAll(s => ctx.TOnly.Contains(s.Skill)); - if (gameMode == Config.GameModes.NoRepeat && playersSkills.TryGetValue(player.Index, out ConcurrentBag? skills)) + if (ctx.PistolRoundBanned.Count != 0) + skillList.RemoveAll(s => ctx.PistolRoundBanned.Contains(s.Skill)); + + if (gameMode == Config.GameModes.NoRepeat && playersSkills.TryGetValue(player.Index, out HashSet? skills)) { - skillList.RemoveAll(s => skills.Any(s2 => s2.Skill == s.Skill)); + skillList.RemoveAll(s => skills.Contains(s.Skill)); if (skillList.Count == 0) skills.Clear(); } @@ -338,10 +347,10 @@ namespace src.player if (gameMode == Config.GameModes.NoRepeat) { - if (playersSkills.TryGetValue(player.Index, out ConcurrentBag? value)) - value.Add(randomSkill); + if (playersSkills.TryGetValue(player.Index, out HashSet? value)) + value.Add(randomSkill.Skill); else - playersSkills.TryAdd(player.Index, [randomSkill]); + playersSkills.TryAdd(player.Index, [randomSkill.Skill]); } return randomSkill; @@ -358,6 +367,7 @@ namespace src.player var def = SkillsInfo.LoadedConfig.FirstOrDefault(d => d.Name == name); if (def == null) return false; + if (def.DisableOnPistolRound && SkillUtils.IsPistolRound()) return false; if (def.NeedsTeammates && validPlayers.Count(p => p.Team == player.Team) == 1) return false; if (def.MaxPerServer >= 0 && assignmentCounts.TryGetValue(pick.Skill, out var c) && c >= def.MaxPerServer) return false; @@ -648,9 +658,12 @@ namespace src.player else skillList.RemoveAll(s => terroristSkills.Any(s2 => s2.Name == s.Skill.ToString())); - if (gameMode == Config.GameModes.NoRepeat && playersSkills.TryGetValue(player.Index, out ConcurrentBag? skills)) + if (SkillUtils.IsPistolRound()) + skillList.RemoveAll(s => SkillsInfo.GetValue(s.Skill, "disableOnPistolRound")); + + if (gameMode == Config.GameModes.NoRepeat && playersSkills.TryGetValue(player.Index, out HashSet? skills)) { - skillList.RemoveAll(s => skills.Any(s2 => s2.Skill == s.Skill)); + skillList.RemoveAll(s => skills.Contains(s.Skill)); if (skillList.Count == 0) skills.Clear(); } diff --git a/jRandomSkills - SRC Files/src/player/skills/Assassin.cs b/jRandomSkills - SRC Files/src/player/skills/Assassin.cs index fb170c3..767ce50 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Assassin.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Assassin.cs @@ -8,11 +8,9 @@ namespace src.player.skills public class Assassin : ISkill { private const Skills skillName = Skills.Assassin; - private static readonly string[] nadeWeapons = - [ - "weapon_inferno", "weapon_molotov", "weapon_incgrenade", "weapon_flashbang", - "weapon_smokegrenade", "weapon_decoy", "weapon_hegrenade" - ]; + private const string infernoWeapon = "weapon_inferno"; + + private static bool IsNade(string? weapon) => weapon == infernoWeapon || WeaponPool.IsGrenade(weapon); public static void LoadSkill() { @@ -46,7 +44,7 @@ namespace src.player.skills var weapon = param2.Ability?.Value; if (weapon == null || !weapon.IsValid) return; - if (nadeWeapons.Contains(weapon.DesignerName)) return; + if (IsNade(weapon.DesignerName)) return; if (IsBehind(attacker, victim)) param2.Damage *= SkillsInfo.GetValue(skillName, "damageMultiplier"); diff --git a/jRandomSkills - SRC Files/src/player/skills/Bounty.cs b/jRandomSkills - SRC Files/src/player/skills/Bounty.cs new file mode 100644 index 0000000..48b55d3 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Bounty.cs @@ -0,0 +1,175 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Cvars; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class Bounty : ISkill + { + private const Skills skillName = Skills.Bounty; + + private static readonly ConcurrentDictionary bountyTargets = []; + private static readonly ConcurrentDictionary ownerToTarget = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + bountyTargets.Clear(); + ownerToTarget.Clear(); + + foreach (var player in PlayerManager.GetTickPlayers()) + SkillUtils.CloseMenu(player); + } + + public static void PlayerDisconnect(uint playerIndex) + { + bountyTargets.TryRemove(playerIndex, out _); + ownerToTarget.TryRemove(playerIndex, out _); + + foreach (var kvp in ownerToTarget) + if (kvp.Value == playerIndex) + ownerToTarget.TryRemove(kvp.Key, out _); + } + + public static void EnableSkill(CCSPlayerController player) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null) return; + playerInfo.SkillUsed = false; + + var playerEvent = PlayerManager.GetPlayerFromEvent(player); + if (playerEvent == null || !playerEvent.IsValid) return; + + var enemies = SkillUtils.GetSelectableEnemies(player, true); + if (enemies.Length > 0) + { + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); + SkillUtils.CreateMenu(player, menuItems); + } + else + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + if (ownerToTarget.TryRemove(player.Index, out uint targetIndex)) + bountyTargets.TryRemove(targetIndex, out _); + + SkillUtils.CloseMenu(player); + } + + public static void OnTick() + { + if (Server.TickCount % 32 != 0) return; + + foreach (var player in PlayerManager.GetTickPlayers()) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null || playerInfo.Skill != skillName) continue; + if (!SkillUtils.HasMenu(player)) continue; + + var enemies = SkillUtils.GetSelectableEnemies(player, true); + + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); + SkillUtils.UpdateMenu(player, menuItems); + } + } + + 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 (playerInfo.SkillUsed) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("areareaper_used_info")}"); + return; + } + + if (commands.Length == 0 || !uint.TryParse(commands[0], out uint enemyIndex)) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + return; + } + + var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex); + if (enemy == null || !enemy.IsValid) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + return; + } + + bountyTargets[enemy.Index] = 0; + ownerToTarget[player.Index] = enemy.Index; + playerInfo.SkillUsed = true; + + int reward = SkillsInfo.GetValue(skillName, "reward"); + + playerEvent.PrintToChat($" {ChatColors.Green}{playerEvent.GetTranslation("bounty_player_info", enemy.PlayerName, reward)}"); + + var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy); + if (enemyEvent != null && enemyEvent.IsValid) + enemyEvent.PrintToChat($" {ChatColors.Red}{enemyEvent.GetTranslation("bounty_enemy_info", reward)}"); + } + + public static void PlayerDeath(EventPlayerDeath @event) + { + if (bountyTargets.IsEmpty) return; + + var victim = PlayerManager.GetPlayerEvent(@event.Userid); + if (victim == null || !victim.IsValid) return; + + if (!bountyTargets.TryRemove(victim.Index, out _)) return; + + foreach (var kvp in ownerToTarget) + if (kvp.Value == victim.Index) + ownerToTarget.TryRemove(kvp.Key, out _); + + var killer = PlayerManager.GetPlayerEvent(@event.Attacker); + if (killer == null || !killer.IsValid) return; + if (killer.Index == victim.Index) return; + if (killer.Team == victim.Team) return; + + int reward = SkillsInfo.GetValue(skillName, "reward"); + if (!GiveMoney(killer, reward)) return; + + var killerEvent = PlayerManager.GetPlayerFromEvent(killer); + if (killerEvent != null && killerEvent.IsValid) + killerEvent.PrintToChat($" {ChatColors.Green}{killerEvent.GetTranslation("bounty_claimed_info", victim.PlayerName, reward)}"); + } + + private static int GetMaxMoney() => ConVar.Find("mp_maxmoney")?.GetPrimitiveValue() ?? 16000; + + private static bool GiveMoney(CCSPlayerController player, int amount) + { + var moneyServices = player.InGameMoneyServices; + if (moneyServices == null) return false; + + int maxMoney = GetMaxMoney(); + if (moneyServices.Account >= maxMoney) return false; + + moneyServices.Account = Math.Clamp(moneyServices.Account + amount, 0, maxMoney); + Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices"); + return true; + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f2c14e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int reward = 300) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public int Reward { get; set; } = reward; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs index 89a5437..21d4963 100644 --- a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs +++ b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs @@ -105,35 +105,27 @@ namespace src.player.skills var bomb = PlayerManager.GetTickBomb(); + var hidden = SkillUtils.ResolveHiddenPawns(invisiblePlayers.Keys, null); + if (hidden.Count == 0) return; + foreach (var (info, player) in infoList) { if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue; var targetHandle = player.Pawn.Value?.ObserverServices?.ObserverTarget.Value?.Handle ?? nint.Zero; - foreach (var playerIndex in invisiblePlayers.Keys) + foreach (var target in hidden) { - var playerController = PlayerManager.GetPlayerEvent(Utilities.GetPlayerFromIndex((int)playerIndex)); - if (playerController == null || !playerController.IsValid || playerController.Index == player.Index) - continue; - - if (player.Team == playerController.Team) - continue; - - var playerPawn = playerController.PlayerPawn.Value; - if (playerPawn == null || !playerPawn.IsValid) continue; + if (target.Index == player.Index) continue; + if (player.Team == target.Team) continue; // Only the actively spectated pawn stays transmitted; hiding it breaks the camera. - if (targetHandle != nint.Zero && playerPawn.Handle == targetHandle) - continue; + if (targetHandle != nint.Zero && target.Pawn.Handle == targetHandle) continue; - var entity = Utilities.GetEntityFromIndex((int)playerPawn.Index); - if (entity == null || !entity.IsValid) continue; + if (info.TransmitEntities.Contains(target.Pawn.Index)) + info.TransmitEntities.Remove(target.Pawn.Index); - if (info.TransmitEntities.Contains(entity.Index)) - info.TransmitEntities.Remove(entity.Index); - - SkillUtils.HideCarriedEntities(info, playerPawn); + SkillUtils.HideCarriedEntities(info, target.Pawn); if (bomb == null) continue; diff --git a/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs b/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs index 742cd65..2f29a87 100644 --- a/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs +++ b/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs @@ -12,6 +12,7 @@ namespace src.player.skills public class CarefulBullets : ISkill { private const Skills skillName = Skills.CarefulBullets; + private const string hurtSound = "Player.DamageBody.Victim"; private static readonly ConcurrentDictionary targetPlayers = []; private static readonly ConcurrentDictionary lastShot = []; private static readonly ConcurrentDictionary hitPlayer = []; @@ -153,7 +154,7 @@ namespace src.player.skills { if (lastShot.TryRemove(playerIndex, out bool didHit) && !didHit) { - eventPlayer.ExecuteClientCommand($"play player/player_damagebody_0{Instance.Random.Next(4, 8)}"); + SkillUtils.EmitSoundToPlayer(eventPlayer, hurtSound, SkillsInfo.GetValue(skillName, "soundVolume")); SkillUtils.TakeHealth(playerPawn, SkillsInfo.GetValue(skillName, "damageAfterMiss"), GetSkillOwner(playerIndex), KillfeedIcons.Fist); } @@ -257,8 +258,9 @@ namespace src.player.skills targetToPlayer.TryRemove(index, out _); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#db6c35", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int damageAfterMiss = 5) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#db6c35", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int damageAfterMiss = 5, float soundVolume = .35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int DamageAfterMiss { get; set; } = damageAfterMiss; } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Catapult.cs b/jRandomSkills - SRC Files/src/player/skills/Catapult.cs index 53c17b2..585be94 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Catapult.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Catapult.cs @@ -23,6 +23,8 @@ namespace src.player.skills if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return; var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index); + if (Heavyweight.Resists(victim)) return; + if (attackerInfo?.Skill == skillName && victim!.PawnIsAlive) if (Instance.Random.NextDouble() <= attackerInfo.SkillChance) { diff --git a/jRandomSkills - SRC Files/src/player/skills/Chicken.cs b/jRandomSkills - SRC Files/src/player/skills/Chicken.cs index 3932272..2ce7bb0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Chicken.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Chicken.cs @@ -10,16 +10,7 @@ namespace src.player.skills public class Chicken : ISkill { private const Skills skillName = Skills.Chicken; - private static readonly HashSet disabledWeapons = - [ - "weapon_ak47", "weapon_m4a4", "weapon_m4a1", "weapon_m4a1_silencer", - "weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg556", - "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", - "weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08", - "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova", - "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", - "weapon_negev" - ]; + private static HashSet disabledWeapons => WeaponPool.Rifles; private const string chickenModelPath = "models/chicken/chicken.vmdl"; private static readonly ConcurrentDictionary chickens = []; diff --git a/jRandomSkills - SRC Files/src/player/skills/Cypher.cs b/jRandomSkills - SRC Files/src/player/skills/Cypher.cs index 7bc4e51..e9d6ffc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Cypher.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Cypher.cs @@ -88,7 +88,7 @@ namespace src.player.skills { var cameraProp = Utilities.GetEntityFromIndex((int)playerSkill.CameraProp); if (cameraProp != null && cameraProp.IsValid) - cameraProp.EmitSound("SolidMetal.BulletImpact"); + cameraProp.EmitSound("SolidMetal.BulletImpact", volume: SkillsInfo.GetValue(skillName, "soundVolume")); SkillUtils.SafeKillEntity(playerSkill.CameraProp); playerSkill.CameraProp = null; @@ -414,8 +414,9 @@ namespace src.player.skills public required QAngle LastCameraAngle { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#34ebd5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 30) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#34ebd5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 30, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Dash.cs b/jRandomSkills - SRC Files/src/player/skills/Dash.cs index f68c06f..3a4d54c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Dash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Dash.cs @@ -143,6 +143,8 @@ namespace src.player.skills eventPlayerPawn.AbsVelocity.X = newVelocity.X; eventPlayerPawn.AbsVelocity.Y = newVelocity.Y; eventPlayerPawn.AbsVelocity.Z = newVelocity.Z; + + eventPlayerPawn.EmitSound("Default.WalkJump", volume: SkillsInfo.GetValue(skillName, "soundVolume")); } } @@ -164,8 +166,9 @@ namespace src.player.skills public int JumpReleasedTicks { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42bbfc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float jumpVelocity = 150f, float pushVelocity = 600f, bool anyDirection = true, float cooldown = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42bbfc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float jumpVelocity = 150f, float pushVelocity = 600f, bool anyDirection = true, float cooldown = 2f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float JumpVelocity { get; set; } = jumpVelocity; public float PushVelocity { get; set; } = pushVelocity; public bool AnyDirection { get; set; } = anyDirection; diff --git a/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs b/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs index 6064c0a..70cfa46 100644 --- a/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs +++ b/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs @@ -13,6 +13,7 @@ namespace src.player.skills public class DeathBomb : ISkill { private const Skills skillName = Skills.DeathBomb; + private static readonly string[] voiceLines = ["balkan.radiobotfallback01", "balkan.radiobotfallback02", "balkan.radiobotfallback04"]; private static readonly QAngle angle = new(10, -5, 9); private static readonly ConcurrentDictionary nades = []; @@ -61,9 +62,7 @@ namespace src.player.skills SkillUtils.PrintToChat(_p, $"{ChatColors.DarkRed}\u202A{player.PlayerName}\u202C: {ChatColors.Lime}{_p.GetTranslation("deathbomb_explosion")}", border: !PlayerManager.GetTickPlayers().Any(p => p.Team == player.Team && p != player) ? "tb" : "t"); - var fileNames = new[] { "radiobotfallback01", "radiobotfallback02", "radiobotfallback04" }; - var randomFile = fileNames[new Random().Next(fileNames.Length)]; - player.ExecuteClientCommand($"play vo/agents/balkan/{randomFile}.vsnd"); + player.EmitSound(voiceLines[Random.Shared.Next(voiceLines.Length)], volume: SkillsInfo.GetValue(skillName, "soundVolume")); nades.AddOrUpdate(Server.TickCount, (player.TeamNum, player.Index), (_, _) => (player.TeamNum, player.Index)); } @@ -136,8 +135,9 @@ namespace src.player.skills return player != null && player.IsValid && player.PlayerPawn?.Value != null; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float explosionRadius = 500.0f, int explosionDamage = 999, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float explosionRadius = 500.0f, int explosionDamage = 999, float dmgReductionForTeamates = 0.5f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float ExplosionRadius { get; set; } = explosionRadius; public int ExplosionDamage { get; set; } = explosionDamage; public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates; diff --git a/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs b/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs index 8059a0d..f4847dd 100644 --- a/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs @@ -66,13 +66,14 @@ namespace src.player.skills if (playerSeesEnemy) { SkillUtils.TakeHealth(enemyPawn, damage, player, KillfeedIcons.Fist); - enemyEvent.EmitSound("Player.DamageBody.Onlooker"); + SkillUtils.EmitSoundToPlayer(enemyEvent, "Player.DamageBody.Victim", SkillsInfo.GetValue(skillName, "soundVolume")); } } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c91243", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float secondCooldown = 1f, int damage = 5) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c91243", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float secondCooldown = 1f, int damage = 5, float soundVolume = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float SecondCooldown { get; set; } = secondCooldown; public int Damage { get; set; } = damage; } diff --git a/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs b/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs new file mode 100644 index 0000000..1b24cf5 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs @@ -0,0 +1,154 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class EmpGrenade : ISkill + { + private const Skills skillName = Skills.EmpGrenade; + + private const string ownerTag = "EmpGrenade"; + + private static readonly ConcurrentDictionary jammedUntilTick = []; + private static readonly ConcurrentDictionary playersWithSkill = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + RestoreAll(); + jammedUntilTick.Clear(); + } + + public static void RoundEnd() + { + RestoreAll(); + jammedUntilTick.Clear(); + } + + public static void PlayerDisconnect(uint playerIndex) + { + jammedUntilTick.TryRemove(playerIndex, out _); + playersWithSkill.TryRemove(playerIndex, out _); + } + + public static void PlayerDeath(EventPlayerDeath @event) + { + var victim = PlayerManager.GetPlayerEvent(@event.Userid); + if (victim == null || !victim.IsValid) return; + + if (jammedUntilTick.TryRemove(victim.Index, out _)) + SetHud(victim.Index, false); + } + + public static void PlayerHurt(EventPlayerHurt @event) + { + if (@event.Weapon != "hegrenade") return; + + var attacker = PlayerManager.GetPlayerEvent(@event.Attacker); + if (attacker == null || !attacker.IsValid) return; + + var attackerInfo = PlayerManager.GetPlayerByIndex(attacker.Index); + if (attackerInfo?.Skill != skillName) return; + + var victim = PlayerManager.GetPlayerEvent(@event.Userid); + if (victim == null || !victim.IsValid) return; + + float duration = SkillsInfo.GetValue(skillName, "jamSeconds"); + jammedUntilTick[victim.Index] = Server.TickCount + (int)(duration * 64); + SetHud(victim.Index, true); + + var victimEvent = PlayerManager.GetPlayerFromEvent(victim); + if (victimEvent != null && victimEvent.IsValid) + victimEvent.PrintToChat($" {ChatColors.Red}{victimEvent.GetTranslation("empgrenade_enemy_info")}"); + } + + public static void OnTick() + { + if (jammedUntilTick.IsEmpty) return; + + int now = Server.TickCount; + + foreach (var kvp in jammedUntilTick) + { + if (now < kvp.Value) continue; + if (!jammedUntilTick.TryRemove(kvp.Key, out _)) continue; + + SetHud(kvp.Key, false); + } + } + + private static void RestoreAll() + { + foreach (var playerIndex in jammedUntilTick.Keys) + SetHud(playerIndex, false); + } + + private static void SetHud(uint playerIndex, bool jam) + { + var player = Utilities.GetPlayerFromIndex((int)playerIndex); + if (player == null || !player.IsValid) return; + + ApplyHud(player, jam); + ApplyHud(PlayerManager.GetPlayerFromEvent(player), jam); + } + + private static void ApplyHud(CCSPlayerController? player, bool jam) + { + if (player == null || !player.IsValid) return; + + SkillUtils.SetCrosshairHidden(player, ownerTag, jam); + SkillUtils.SetRadarDisabled(player, ownerTag, jam); + } + + public static void GrenadeThrown(EventGrenadeThrown @event) + { + var player = PlayerManager.GetPlayerEvent(@event.Userid); + if (player == null || !player.IsValid) return; + + if (@event.Weapon != "hegrenade") return; + + var playerInfo = PlayerManager.GetPlayerByIndex(player.Index); + if (playerInfo?.Skill != skillName) return; + + if (playersWithSkill.TryGetValue(player.Index, out int grenadesLeft) && grenadesLeft > 1) + { + playersWithSkill[player.Index] = grenadesLeft - 1; + player!.GiveNamedItem("weapon_hegrenade"); + SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, grenadesLeft - 1); + } + } + + public static void EnableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + int grenadeLimit = SkillsInfo.GetValue(skillName, "grenadeLimit"); + playersWithSkill[player.Index] = grenadeLimit; + + SkillUtils.TryGiveWeapon(player, CsItem.HEGrenade); + SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, grenadeLimit); + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + playersWithSkill.TryRemove(player.Index, out _); + SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, 1); + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3ad6e8", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Uncommon, float jamSeconds = 3f, int grenadeLimit = 2) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float JamSeconds { get; set; } = jamSeconds; + public int GrenadeLimit { get; set; } = grenadeLimit; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/FireRain.cs b/jRandomSkills - SRC Files/src/player/skills/FireRain.cs index b71e0a2..0f38d47 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FireRain.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FireRain.cs @@ -19,12 +19,14 @@ namespace src.player.skills public static void LoadSkill() { SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + DecoyRing.PreloadAssets(); } public static void NewRound() { KillAllDecoys(); rainMolotovs.Clear(); + DecoyRing.ClearAll(skillName); } private static void KillAllDecoys() @@ -276,7 +278,14 @@ namespace src.player.skills if (decoy != null && decoy.IsValid) decoy.AddEntityIOEvent("Kill", decoy, delay: 0.1f); decoys.TryRemove(key, out _); - SpawnRain(player, new Vector(@event.X, @event.Y, @event.Z)); + + Vector impact = new(@event.X, @event.Y, @event.Z); + SpawnRain(player, impact); + + DecoyRing.Show(skillName, key, impact, 130f); + jRandomSkills.Instance.AddTimer(SkillsInfo.GetValue(skillName, "markerSeconds"), + () => DecoyRing.Hide(skillName, key), + CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); } } @@ -286,8 +295,9 @@ namespace src.player.skills SkillUtils.TryGiveWeapon(player, CsItem.DecoyGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffbf47", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Epic) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffbf47", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Epic, float markerSeconds = 4f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float MarkerSeconds { get; set; } = markerSeconds; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs index 1a5f8fe..384eb3e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs @@ -154,7 +154,7 @@ namespace src.player.skills var box = param.As(); if (box == null || !box.IsValid) return; - box.EmitSound("Wood_Plank.BulletImpact", volume: 1f); + box.EmitSound("Wood_Plank.BulletImpact", volume: SkillsInfo.GetValue(skillName, "soundVolume")); if (barricades.TryGetValue(box.Index, out int health)) { @@ -178,8 +178,9 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1b04cc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 5, Rarity rarity = Rarity.Common, float cooldown = 2f, int barricadeHealth = 115, string propModel = "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl") : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1b04cc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 5, Rarity rarity = Rarity.Common, float cooldown = 2f, int barricadeHealth = 115, string propModel = "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl", float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; public int BarricadeHealth { get; set; } = barricadeHealth; public string PropModel { get; set; } = propModel; diff --git a/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs b/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs index 24af06a..1d86e6c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs @@ -16,11 +16,19 @@ namespace src.player.skills public static void LoadSkill() { SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + DecoyRing.PreloadAssets(); } public static void NewRound() { decoys.Clear(); + DecoyRing.ClearAll(skillName); + } + + public static void RoundEnd() + { + decoys.Clear(); + DecoyRing.ClearAll(skillName); } public static void DecoyStarted(EventDecoyStarted @event) @@ -31,7 +39,9 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo?.Skill != skillName) return; - decoys.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0); + Vector pos = new(@event.X, @event.Y, @event.Z); + decoys.TryAdd(pos, 0); + DecoyRing.Show(skillName, (uint)@event.Entityid, pos, SkillsInfo.GetValue(skillName, "triggerRadius")); } public static void DecoyDetonate(EventDecoyDetonate @event) @@ -44,6 +54,8 @@ namespace src.player.skills foreach (var decoy in decoys.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z)) decoys.TryRemove(decoy, out _); + + DecoyRing.Hide(skillName, (uint)@event.Entityid); } public static void OnTick() diff --git a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs index 46f7900..9994fd5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs @@ -13,9 +13,13 @@ namespace src.player.skills public class Ghost : ISkill { private const Skills skillName = Skills.Ghost; - private static readonly string[] allowedWeapons = [ - "weapon_molotov", "weapon_incgrenade", "weapon_flashbang", "weapon_smokegrenade", "weapon_decoy", "weapon_hegrenade", "weapon_knife", "weapon_bayonet", "weapon_c4" - ]; + private static readonly HashSet alwaysAllowed = new(StringComparer.Ordinal) + { + "weapon_knife", "weapon_bayonet", "weapon_c4" + }; + + private static bool IsAllowed(string? weapon) => + weapon != null && (alwaysAllowed.Contains(weapon) || WeaponPool.IsGrenade(weapon)); private static readonly ConcurrentDictionary invisiblePlayers = []; private const string bloodParticle = "particles/blood_impact/blood_impact_high.vpcf"; private const string cameraViewModel = "models/sprays/spray_plane.vmdl"; @@ -61,38 +65,30 @@ namespace src.player.skills var bomb = PlayerManager.GetTickBomb(); uint? bombOwnerIndex = bomb != null && bomb.IsValid ? bomb.OwnerEntity?.Index : null; + var hidden = SkillUtils.ResolveHiddenPawns(invisiblePlayers.Keys, bombOwnerIndex); + if (hidden.Count == 0) return; + foreach (var (info, player) in infoList) { if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue; var targetHandle = player.Pawn.Value?.ObserverServices?.ObserverTarget.Value?.Handle ?? nint.Zero; - foreach (var playerIndex in invisiblePlayers.Keys) + foreach (var target in hidden) { - var playerController = Utilities.GetPlayerFromIndex((int)playerIndex); - if (playerController == null || !playerController.IsValid || playerController.Index == player.Index) - continue; - - if (player.Team == playerController.Team) - continue; - - var playerPawn = playerController.PlayerPawn.Value; - if (playerPawn == null || !playerPawn.IsValid) continue; + if (target.Index == player.Index) continue; + if (player.Team == target.Team) continue; // Only the actively spectated pawn stays transmitted; hiding it breaks the camera. - if (targetHandle != nint.Zero && playerPawn.Handle == targetHandle) - continue; + if (targetHandle != nint.Zero && target.Pawn.Handle == targetHandle) continue; - var entity = Utilities.GetEntityFromIndex((int)playerPawn.Index); - if (entity == null || !entity.IsValid) continue; + if (info.TransmitEntities.Contains(target.Pawn.Index)) + info.TransmitEntities.Remove(target.Pawn.Index); - if (info.TransmitEntities.Contains(entity.Index)) - info.TransmitEntities.Remove(entity.Index); - - SkillUtils.HideCarriedEntities(info, playerPawn); + SkillUtils.HideCarriedEntities(info, target.Pawn); // Hide the bomb as well, but only while this hidden player is the one holding it. - if (bomb == null || bombOwnerIndex != playerController.Index) continue; + if (bomb == null || !target.HoldsBomb) continue; if (info.TransmitEntities.Contains(bomb.Index)) info.TransmitEntities.Remove(bomb.Index); @@ -156,7 +152,7 @@ namespace src.player.skills foreach (var player in PlayerManager.GetTickPlayers()) { var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill == skillName) + if (playerInfo?.Skill == skillName && SkillUtils.IsHudFrame()) UpdateHUD(player); if (player.LifeState != (byte)LifeState_t.LIFE_ALIVE) @@ -206,7 +202,7 @@ namespace src.player.skills if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid && !string.IsNullOrEmpty(weapon.Value.DesignerName)) { string weaponName = weapon.Value.DesignerName; - if (!allowedWeapons.Contains(weaponName)) + if (!IsAllowed(weaponName)) { weapon.Value.NextPrimaryAttackTick = disableWeapon ? int.MaxValue : Server.TickCount; weapon.Value.NextSecondaryAttackTick = disableWeapon ? int.MaxValue : Server.TickCount; @@ -227,7 +223,7 @@ namespace src.player.skills if (playerInfo == null) return; var weapon = pawn.WeaponServices.ActiveWeapon.Value; - if (weapon == null || !weapon.IsValid || allowedWeapons.Contains(weapon.DesignerName)) + if (weapon == null || !weapon.IsValid || IsAllowed(weapon.DesignerName)) { playerInfo.PrintHTML = null; return; diff --git a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs index 7cf3f90..671845b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs @@ -41,6 +41,18 @@ namespace src.player.skills { if (smokes.IsEmpty || playersWithSkill.IsEmpty) return; + List smokeIndexes = new(smokes.Count); + foreach (var entityIndex in smokes.Keys) + { + var entity = Utilities.GetEntityFromIndex((int)entityIndex); + if (entity == null || !entity.IsValid) continue; + smokeIndexes.Add(entity.Index); + } + + if (smokeIndexes.Count == 0) return; + + Dictionary? pawnOwners = null; + foreach (var (info, player) in infoList) { if (player == null || !player.IsValid) continue; @@ -51,23 +63,36 @@ namespace src.player.skills 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; + pawnOwners ??= BuildPawnOwners(); - if (PlayerManager.GetPlayerByIndex(observedPlayer.Index)?.Skill != skillName) continue; + if (!pawnOwners.TryGetValue(observerTarget, out uint observedIndex)) continue; + if (PlayerManager.GetPlayerByIndex(observedIndex)?.Skill != skillName) continue; } - foreach (var entityIndex in smokes.Keys) - { - var entity = Utilities.GetEntityFromIndex((int)entityIndex); - if (entity == null || !entity.IsValid) continue; - - if (info.TransmitEntities.Contains(entity.Index)) - info.TransmitEntities.Remove(entity.Index); - } + foreach (var index in smokeIndexes) + if (info.TransmitEntities.Contains(index)) + info.TransmitEntities.Remove(index); } } + private static Dictionary BuildPawnOwners() + { + var players = PlayerManager.GetTickPlayers(); + Dictionary owners = new(players.Count); + + foreach (var p in players) + { + if (p == null || !p.IsValid) continue; + + nint handle = p.Pawn?.Value?.Handle ?? nint.Zero; + if (handle == nint.Zero) continue; + + owners.TryAdd(handle, p.Index); + } + + return owners; + } + public static void GrenadeThrown(EventGrenadeThrown @event) { var player = PlayerManager.GetPlayerEvent(@event.Userid); diff --git a/jRandomSkills - SRC Files/src/player/skills/Glitch.cs b/jRandomSkills - SRC Files/src/player/skills/Glitch.cs index c8fe078..2291daa 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glitch.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glitch.cs @@ -9,6 +9,7 @@ namespace src.player.skills public class Glitch : ISkill { private const Skills skillName = Skills.Glitch; + private const string ownerTag = "Glitch"; private static readonly ConcurrentDictionary glitchedPlayers = []; private static readonly ConcurrentDictionary playersToTarget = []; private static readonly object setLock = new(); @@ -100,7 +101,7 @@ namespace src.player.skills var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy); if (enemyEvent == null || !enemyEvent.IsValid) return; - enemyEvent.ReplicateConVar("sv_disable_radar", "1"); + SkillUtils.SetRadarDisabled(enemyEvent, ownerTag, true); playerInfo.SkillUsed = true; playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("glitch_player_info", enemy.PlayerName)); @@ -117,7 +118,7 @@ namespace src.player.skills if (!glitchedPlayers.ContainsKey(bot.Index)) return; - player.ReplicateConVar("sv_disable_radar", "1"); + SkillUtils.SetRadarDisabled(player, ownerTag, true); } public static void EnableSkill(CCSPlayerController player) @@ -146,7 +147,7 @@ namespace src.player.skills var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex)); if (target != null && target.IsValid) { - target.ReplicateConVar("sv_disable_radar", "0"); + SkillUtils.SetRadarDisabled(target, ownerTag, false); if (target.PawnIsAlive && !SkillUtils.IsFreezeTime()) target.PrintToChat($" {ChatColors.Green}" + target.GetTranslation("glitch_disable_info")); } @@ -161,7 +162,7 @@ namespace src.player.skills var player = @event.Userid; if (player == null || !player.IsValid) return; - player.ReplicateConVar("sv_disable_radar", "0"); + SkillUtils.SetRadarDisabled(player, ownerTag, false); glitchedPlayers.TryRemove(player.Index, out _); SkillUtils.CloseMenu(player); } diff --git a/jRandomSkills - SRC Files/src/player/skills/Grapple.cs b/jRandomSkills - SRC Files/src/player/skills/Grapple.cs index 081817b..f8c82d7 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Grapple.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Grapple.cs @@ -20,10 +20,13 @@ namespace src.player.skills private static readonly Color counterTerroristRope = Color.FromArgb(255, 52, 92, 138); private const double attemptCooldown = .3; + private const string hookModel = "models/generic/grapplinghook_01/grapplinghook_hook_01_open.vmdl"; + private const string hookSound = "SolidMetal.BulletImpact"; public static void LoadSkill() { SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + jRandomSkills.Instance.AddToManifest(hookModel); } public static void NewRound() @@ -131,6 +134,8 @@ namespace src.player.skills skillInfo.Anchor = anchor; skillInfo.EndTick = Server.TickCount + (int)(SkillsInfo.GetValue(skillName, "maxPullSeconds") * 64); + SpawnHook(player, skillInfo, anchor, result.Value.NormalX, result.Value.NormalY, result.Value.NormalZ); + if (!EntityManager.OverBudget()) { Color ropeColor = player.Team == CsTeam.Terrorist ? terroristRope : counterTerroristRope; @@ -155,15 +160,102 @@ namespace src.player.skills } } - player.EmitSound("SolidMetal.BulletImpact"); return true; } + private static void EmitHookSound(CBaseEntity? source, CCSPlayerController player) + { + float volume = SkillsInfo.GetValue(skillName, "soundVolume"); + + if (source != null && source.IsValid) + { + source.EmitSound(hookSound, volume: volume); + return; + } + + var pawn = player.PlayerPawn?.Value; + if (pawn != null && pawn.IsValid) + pawn.EmitSound(hookSound, volume: volume); + } + + private static void SpawnHook(CCSPlayerController player, PlayerSkillInfo skillInfo, Vector anchor, float normalX, float normalY, float normalZ) + { + float length = MathF.Sqrt(normalX * normalX + normalY * normalY + normalZ * normalZ); + if (length < .0001f) + { + EmitHookSound(null, player); + return; + } + + normalX /= length; + normalY /= length; + normalZ /= length; + + var hook = EntityManager.CreateTrackedPropOverride(player.Index); + if (hook == null || !hook.IsValid) + { + EmitHookSound(null, player); + return; + } + + float pitch = -MathF.Asin(Math.Clamp(-normalZ, -1f, 1f)) * 180f / MathF.PI; + float yaw = MathF.Atan2(-normalY, -normalX) * 180f / MathF.PI; + QAngle angle = new(pitch, yaw, 0); + + float scale = SkillsInfo.GetValue(skillName, "hookScale"); + float embed = SkillsInfo.GetValue(skillName, "hookEmbed") * scale; + Vector pos = new(anchor.X + normalX * embed, anchor.Y + normalY * embed, anchor.Z + normalZ * embed); + + uint hookIndex = hook.Index; + skillInfo.HookIndex = hookIndex; + + Server.NextFrame(() => + { + var prop = Utilities.GetEntityFromIndex((int)hookIndex); + if (prop == null || !prop.IsValid) + { + EmitHookSound(null, player); + return; + } + + var node = prop.CBodyComponent?.SceneNode?.Owner?.Entity; + if (node != null) + node.Flags = (uint)(node.Flags & ~(1 << 2)); + + prop.SetModel(hookModel); + prop.Teleport(pos, angle); + prop.DispatchSpawn(); + + EmitHookSound(prop, player); + + var skeleton = prop.CBodyComponent?.SceneNode?.GetSkeletonInstance(); + if (skeleton != null) + { + skeleton.Scale = scale; + prop.AcceptInput("SetScale", null, null, scale.ToString(System.Globalization.CultureInfo.InvariantCulture)); + } + + Utilities.SetStateChanged(prop, "CBaseEntity", "m_CBodyComponent"); + }); + } + + private static void DestroyHook(PlayerSkillInfo skillInfo) + { + if (skillInfo.HookIndex == null) return; + + uint hookIndex = skillInfo.HookIndex.Value; + skillInfo.HookIndex = null; + + EntityManager.DestroyEntity(hookIndex); + } + private static void DestroyRope(PlayerSkillInfo skillInfo) { skillInfo.Anchor = null; skillInfo.EndTick = 0; + DestroyHook(skillInfo); + if (skillInfo.RopeIndex == null) return; uint ropeIndex = skillInfo.RopeIndex.Value; @@ -176,26 +268,7 @@ namespace src.player.skills { 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(); - }); + EntityManager.DestroyBeam(ropeIndex); } private static void SweepOrphanRopes() @@ -299,11 +372,13 @@ namespace src.player.skills public DateTime LastAttempt { get; set; } public Vector? Anchor { get; set; } public uint? RopeIndex { get; set; } + public uint? HookIndex { 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 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, float hookEmbed = 8f, float hookScale = .4f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; public float MaxDistance { get; set; } = maxDistance; public float MinDistance { get; set; } = minDistance; @@ -311,6 +386,8 @@ namespace src.player.skills public float PullSpeed { get; set; } = pullSpeed; public float MaxPullSeconds { get; set; } = maxPullSeconds; public float RopeWidth { get; set; } = ropeWidth; + public float HookEmbed { get; set; } = hookEmbed; + public float HookScale { get; set; } = hookScale; } } } diff --git a/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs b/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs new file mode 100644 index 0000000..668ff84 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs @@ -0,0 +1,189 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class GravityDecoy : ISkill + { + private const Skills skillName = Skills.GravityDecoy; + + private const float defaultGravity = 1f; + + private static readonly ConcurrentDictionary decoys = []; + private static readonly ConcurrentDictionary playersWithSkill = []; + private static readonly ConcurrentDictionary affected = []; + private static readonly ConcurrentDictionary restoreOnRespawn = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + DecoyRing.PreloadAssets(); + } + + public static void NewRound() + { + RestoreAll(); + decoys.Clear(); + DecoyRing.ClearAll(skillName); + } + + public static void RoundEnd() + { + RestoreAll(); + decoys.Clear(); + DecoyRing.ClearAll(skillName); + } + + public static void DecoyStarted(EventDecoyStarted @event) + { + var player = PlayerManager.GetPlayerEvent(@event.Userid); + if (player == null || !player.IsValid) return; + + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo?.Skill != skillName) return; + + Vector pos = new(@event.X, @event.Y, @event.Z); + decoys.TryAdd(pos, 0); + DecoyRing.Show(skillName, (uint)@event.Entityid, pos, SkillsInfo.GetValue(skillName, "triggerRadius")); + } + + public static void DecoyDetonate(EventDecoyDetonate @event) + { + var player = PlayerManager.GetPlayerEvent(@event.Userid); + if (player == null || !player.IsValid) return; + + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo?.Skill != skillName) return; + + foreach (var decoy in decoys.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z)) + decoys.TryRemove(decoy, out _); + + DecoyRing.Hide(skillName, (uint)@event.Entityid); + + if (decoys.IsEmpty) RestoreAll(); + } + + public static void OnTick() + { + if (decoys.IsEmpty && affected.IsEmpty && restoreOnRespawn.IsEmpty) return; + + if (decoys.IsEmpty && !affected.IsEmpty) RestoreAll(); + + float radius = SkillsInfo.GetValue(skillName, "triggerRadius"); + float gravity = SkillsInfo.GetValue(skillName, "gravityScale"); + + foreach (var player in PlayerManager.GetTickPlayers()) + { + if (player == null || !player.IsValid) continue; + if (player.Team is not (CsTeam.CounterTerrorist or CsTeam.Terrorist)) continue; + + var eventPlayer = PlayerManager.GetPlayerEvent(player); + if (eventPlayer == null || !eventPlayer.IsValid) continue; + + var pawn = eventPlayer.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue; + if (pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) continue; + + if (restoreOnRespawn.TryRemove(eventPlayer.Index, out _)) + pawn.ActualGravityScale = defaultGravity; + + bool inside = !decoys.IsEmpty && decoys.Keys.Any(d => SkillUtils.GetDistance(d, pawn.AbsOrigin) <= radius); + + if (inside) + { + if (!affected.TryAdd(eventPlayer.Index, 0)) continue; + pawn.ActualGravityScale = gravity; + } + else if (affected.TryRemove(eventPlayer.Index, out _)) + pawn.ActualGravityScale = defaultGravity; + } + } + + public static void PlayerDeath(EventPlayerDeath @event) + { + var victim = PlayerManager.GetPlayerEvent(@event.Userid); + if (victim == null || !victim.IsValid) return; + + if (!affected.TryRemove(victim.Index, out _)) return; + + Restore(victim.Index); + restoreOnRespawn[victim.Index] = 0; + } + + public static void PlayerDisconnect(uint playerIndex) + { + affected.TryRemove(playerIndex, out _); + restoreOnRespawn.TryRemove(playerIndex, out _); + playersWithSkill.TryRemove(playerIndex, out _); + } + + private static void RestoreAll() + { + foreach (uint playerIndex in affected.Keys) + Restore(playerIndex); + + foreach (uint playerIndex in restoreOnRespawn.Keys) + Restore(playerIndex); + + affected.Clear(); + restoreOnRespawn.Clear(); + } + + private static void Restore(uint playerIndex) + { + var player = Utilities.GetPlayerFromIndex((int)playerIndex); + var pawn = player?.PlayerPawn?.Value; + + if (pawn != null && pawn.IsValid) + pawn.ActualGravityScale = defaultGravity; + } + + public static void GrenadeThrown(EventGrenadeThrown @event) + { + var player = PlayerManager.GetPlayerEvent(@event.Userid); + if (player == null || !player.IsValid) return; + + if (@event.Weapon != "decoy") return; + + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo?.Skill != skillName) return; + + if (playersWithSkill.TryGetValue(player.Index, out int grenadesLeft) && grenadesLeft > 1) + { + playersWithSkill[player.Index] = grenadesLeft - 1; + player!.GiveNamedItem("weapon_decoy"); + SkillUtils.UpdateGrenadeCount(player, CsItem.DecoyGrenade, grenadesLeft - 1); + } + } + + public static void EnableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + int grenadeLimit = SkillsInfo.GetValue(skillName, "grenadeLimit"); + playersWithSkill[player.Index] = grenadeLimit; + + SkillUtils.TryGiveWeapon(player, CsItem.DecoyGrenade); + SkillUtils.UpdateGrenadeCount(player, CsItem.DecoyGrenade, grenadeLimit); + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + playersWithSkill.TryRemove(player.Index, out _); + SkillUtils.UpdateGrenadeCount(player, CsItem.DecoyGrenade, 1); + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a06cd5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float triggerRadius = 180f, float gravityScale = .5f, int grenadeLimit = 2) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float TriggerRadius { get; set; } = triggerRadius; + public float GravityScale { get; set; } = gravityScale; + public int GrenadeLimit { get; set; } = grenadeLimit; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs b/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs index a88370c..069b5cb 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs @@ -92,7 +92,7 @@ namespace src.player.skills if (SkillUtils.GetDistance(smokePos, pawn.AbsOrigin) <= smokeRadius) if (SkillUtils.AddHealth(pawn, smokeHeal)) - player.EmitSound("Healthshot.Success"); + SkillUtils.EmitSoundToPlayer(player, "Healthshot.Success", SkillsInfo.GetValue(skillName, "soundVolume")); } } @@ -156,8 +156,9 @@ namespace src.player.skills SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1fe070", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeHeal = 1, float smokeRadius = 180, int tickCooldown = 16, int grenadeLimit = 1) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1fe070", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeHeal = 1, float smokeRadius = 180, int tickCooldown = 16, int grenadeLimit = 1, float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int SmokeHeal { get; set; } = smokeHeal; public float SmokeRadius { get; set; } = smokeRadius; public int TickCooldown { get; set; } = tickCooldown; diff --git a/jRandomSkills - SRC Files/src/player/skills/Heavyweight.cs b/jRandomSkills - SRC Files/src/player/skills/Heavyweight.cs new file mode 100644 index 0000000..92e7842 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Heavyweight.cs @@ -0,0 +1,55 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class Heavyweight : ISkill + { + private const Skills skillName = Skills.Heavyweight; + + private static readonly ConcurrentDictionary holders = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + 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 _); + } + + public static bool Resists(CCSPlayerController? player) + { + if (player == null || !player.IsValid || holders.IsEmpty) return false; + + if (holders.ContainsKey(player.Index)) return true; + + uint routedIndex = PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index; + return routedIndex != player.Index && holders.ContainsKey(routedIndex); + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8d6e63", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Uncommon) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Hermit.cs b/jRandomSkills - SRC Files/src/player/skills/Hermit.cs index c8430de..8375144 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Hermit.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Hermit.cs @@ -10,19 +10,7 @@ namespace src.player.skills public class Hermit : ISkill { private const Skills skillName = Skills.Hermit; - private static readonly ConcurrentDictionary ConcurrentDictionary = new( - [ - new KeyValuePair("weapon_glock", 3), new KeyValuePair("weapon_usp_silencer", 2), new KeyValuePair("weapon_hkp2000", 4), new KeyValuePair("weapon_p250", 3), - new KeyValuePair("weapon_cz75", 2), new KeyValuePair("weapon_deagle", 3), new KeyValuePair("weapon_fiveseven", 2), new KeyValuePair("weapon_elite", 2), - new KeyValuePair("weapon_tec9", 3), new KeyValuePair("weapon_revolver", 2), new KeyValuePair("weapon_mac10", 3), new KeyValuePair("weapon_mp9", 2), - new KeyValuePair("weapon_mp7", 3), new KeyValuePair("weapon_mp5", 3), new KeyValuePair("weapon_mp5sd", 3), new KeyValuePair("weapon_ump45", 3), - new KeyValuePair("weapon_p90", 2), new KeyValuePair("weapon_bizon", 2), new KeyValuePair("weapon_ak47", 3), new KeyValuePair("weapon_m4a1", 4), - new KeyValuePair("weapon_m4a1_silencer", 3), new KeyValuePair("weapon_galilar", 4), new KeyValuePair("weapon_famas", 4), new KeyValuePair("weapon_aug", 3), - new KeyValuePair("weapon_sg556", 3), new KeyValuePair("weapon_ssg08", 2), new KeyValuePair("weapon_awp", 2), new KeyValuePair("weapon_scar20", 2), - new KeyValuePair("weapon_g3sg1", 2), new KeyValuePair("weapon_nova", 32), new KeyValuePair("weapon_xm1014", 32), new KeyValuePair("weapon_sawedoff", 32), - new KeyValuePair("weapon_mag7", 3), new KeyValuePair("weapon_m249", 2), new KeyValuePair("weapon_negev", 2) - ]); - private static readonly ConcurrentDictionary maxReserveAmmo = ConcurrentDictionary; + private const int fallbackReserveAmmo = 100; public static void LoadSkill() { @@ -43,9 +31,11 @@ namespace src.player.skills var weapon = pawn.WeaponServices.ActiveWeapon.Value; if (weapon == null || !weapon.IsValid || weapon.VData == null) return; - var maxReserveAmmoClip = maxReserveAmmo.TryGetValue(weapon.DesignerName, out var reserve) ? reserve : 100; + int reserve = weapon.As().GetVData()?.PrimaryReserveAmmoMax ?? 0; + if (reserve <= 0) reserve = fallbackReserveAmmo; + weapon.Clip1 = weapon.VData.MaxClip1; - weapon.ReserveAmmo.Fill(maxReserveAmmoClip); + weapon.ReserveAmmo.Fill(reserve); Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1"); Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo"); diff --git a/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs b/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs index 0f984d4..50c3170 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs @@ -12,6 +12,7 @@ namespace src.player.skills public class HotBomb : ISkill { private const Skills skillName = Skills.HotBomb; + private const string hurtSound = "Player.DamageBody.Victim"; private readonly static ConcurrentDictionary players = []; public static void LoadSkill() @@ -41,7 +42,7 @@ namespace src.player.skills if (!hasC4) continue; SkillUtils.TakeHealth(pawn, damage, owner, KillfeedIcons.C4); - playerEvent?.ExecuteClientCommand($"play player/player_damagebody_0{jRandomSkills.Instance.Random.Next(4, 8)}"); + SkillUtils.EmitSoundToPlayer(playerEvent, hurtSound, SkillsInfo.GetValue(skillName, "soundVolume")); } } @@ -121,8 +122,9 @@ namespace src.player.skills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#baf081", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Common, float cooldown = 1, int damage = 2) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#baf081", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Common, float cooldown = 1, int damage = 2, float soundVolume = .35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; public int Damage { get; set; } = damage; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Iana.cs b/jRandomSkills - SRC Files/src/player/skills/Iana.cs index 5285049..d272ae1 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Iana.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Iana.cs @@ -112,7 +112,7 @@ namespace src.player.skills var player = Utilities.GetPlayerFromIndex((int)playerSkill.PlayerIndex); if (player == null || !player.IsValid) return; - player.EmitSound("SolidMetal.BulletImpact"); + player.EmitSound("SolidMetal.BulletImpact", volume: SkillsInfo.GetValue(skillName, "soundVolume")); CDynamicProp? cloneProp = null; if (playerSkill.CloneProp != null) @@ -468,8 +468,9 @@ namespace src.player.skills public List Weapons { get; set; } = []; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d0d930", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 30, float duration = 10) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d0d930", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 30, float duration = 10, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; public float Duration { get; set; } = duration; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Illiterate.cs b/jRandomSkills - SRC Files/src/player/skills/Illiterate.cs index f2660de..090c06d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Illiterate.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Illiterate.cs @@ -50,11 +50,19 @@ namespace src.player.skills private static void SendAlertToAll() { + holdersTick = int.MinValue; + foreach (var player in PlayerManager.GetTickPlayers()) { if (!CheckIlliterateSkill(player)) continue; - player.PrintToCenterAlert(player.GetTranslationWithoutIlliterate("illiterate_alert")); - } + + var playerEvent = PlayerManager.GetPlayerFromEvent(player); + if (playerEvent == null || !playerEvent.IsValid) continue; + + string alert = playerEvent.GetTranslationWithoutIlliterate("illiterate_alert"); + playerEvent.PrintToCenterAlert(alert); + SkillUtils.PrintToChat(playerEvent, $"{ChatColors.Red}{alert}", ignoreIlliterate: true); + } } public static bool CheckIlliterateSkill(CCSPlayerController? player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs b/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs index f10d426..45ae2a3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs @@ -199,7 +199,7 @@ namespace src.player.skills if (!consumedReplicas.TryAdd(replica.Index, 0)) return; - replica.EmitSound("GlassBottle.BulletImpact", volume: 1f); + replica.EmitSound("GlassBottle.BulletImpact", volume: SkillsInfo.GetValue(skillName, "soundVolume")); if (ActiveTimers.TryRemove((int)replica.Index, out var timer)) timer?.Kill(); var owner = GetReplicaOwner(replica.Index); @@ -230,8 +230,9 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42f5ef", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 30f, float durationRun = 5, float durationCrouch = 12, int yourTeamDamage = 10, int enemyTeamDamage = 20) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42f5ef", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 30f, float durationRun = 5, float durationCrouch = 12, int yourTeamDamage = 10, int enemyTeamDamage = 20, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; public float DurationRun { get; set; } = durationRun; public float DurationCrouch { get; set; } = durationCrouch; diff --git a/jRandomSkills - SRC Files/src/player/skills/Jammer.cs b/jRandomSkills - SRC Files/src/player/skills/Jammer.cs index 3e2e511..69666d3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jammer.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jammer.cs @@ -9,6 +9,7 @@ namespace src.player.skills public class Jammer : ISkill { private const Skills skillName = Skills.Jammer; + private const string ownerTag = "Jammer"; private static readonly ConcurrentDictionary jammedPlayers = []; private static readonly ConcurrentDictionary jammerToTarget = []; private static readonly object setLock = new(); @@ -118,14 +119,7 @@ namespace src.player.skills private static void SetCrosshair(CCSPlayerController player, bool enabled) { - var pawn = player.PlayerPawn.Value; - if (pawn == null || !pawn.IsValid) return; - - pawn.HideHUD = (uint)(enabled - ? (pawn.HideHUD & ~(1 << 8)) - : (pawn.HideHUD | (1 << 8))); - - Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_iHideHUD"); + SkillUtils.SetCrosshairHidden(player, ownerTag, !enabled); } public static void BotTakeover(EventBotTakeover @event) diff --git a/jRandomSkills - SRC Files/src/player/skills/Jester.cs b/jRandomSkills - SRC Files/src/player/skills/Jester.cs index 9d70d78..c14a74d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jester.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jester.cs @@ -12,6 +12,7 @@ namespace src.player.skills public class Jester : ISkill { private const Skills skillName = Skills.Jester; + private const string toggleSound = "Weapon_Taser.ChargeReady"; private static readonly ConcurrentDictionary jesters = []; public static void LoadSkill() @@ -196,7 +197,7 @@ namespace src.player.skills var playerEvent = PlayerManager.GetPlayerFromEvent(player); if (playerEvent != null && playerEvent.IsValid) - playerEvent.ExecuteClientCommand("play sounds/weapons/taser/taser_charge_ready"); + SkillUtils.EmitSoundToPlayer(playerEvent, toggleSound, SkillsInfo.GetValue(skillName, "soundVolume")); } var minTime = SkillsInfo.GetValue(skillName, "minTime"); @@ -275,8 +276,9 @@ namespace src.player.skills public Timer? Timer { get; set; } = null; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8f108f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float minTime = 10f, float maxTime = 25f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8f108f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float minTime = 10f, float maxTime = 25f, float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float MinTime { get; set; } = minTime; public float MaxTime { get; set; } = maxTime; } diff --git a/jRandomSkills - SRC Files/src/player/skills/JetKick.cs b/jRandomSkills - SRC Files/src/player/skills/JetKick.cs index 5dc4e38..94602e8 100644 --- a/jRandomSkills - SRC Files/src/player/skills/JetKick.cs +++ b/jRandomSkills - SRC Files/src/player/skills/JetKick.cs @@ -166,6 +166,8 @@ namespace src.player.skills private static void PushPlayer(CCSPlayerController player) { + if (Heavyweight.Resists(player)) return; + var playerPawn = player.PlayerPawn.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.Health <= 0) return; diff --git a/jRandomSkills - SRC Files/src/player/skills/LastGasp.cs b/jRandomSkills - SRC Files/src/player/skills/LastGasp.cs index 8baa7dd..ae3ba86 100644 --- a/jRandomSkills - SRC Files/src/player/skills/LastGasp.cs +++ b/jRandomSkills - SRC Files/src/player/skills/LastGasp.cs @@ -9,6 +9,7 @@ namespace src.player.skills public class LastGasp : ISkill { private const Skills skillName = Skills.LastGasp; + private const string hurtSound = "Player.DamageBody.Victim"; public static void LoadSkill() { @@ -45,11 +46,12 @@ namespace src.player.skills } SkillUtils.TakeHealth(attackerPawn, damageAfterDeath, victim, KillfeedIcons.Fist); - PlayerManager.GetPlayerFromEvent(attacker)?.ExecuteClientCommand($"play player/player_damagebody_0{jRandomSkills.Instance.Random.Next(4, 8)}"); + SkillUtils.EmitSoundToPlayer(attacker, hurtSound, SkillsInfo.GetValue(skillName, "soundVolume")); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88bdba", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, int damageAfterDeath = 30, bool canKill = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88bdba", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, int damageAfterDeath = 30, bool canKill = true, float soundVolume = .35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int DamageAfterDeath { get; set; } = damageAfterDeath; public bool CanKill { get; set; } = canKill; diff --git a/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs b/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs index 5c04b36..a15c9df 100644 --- a/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs @@ -12,6 +12,9 @@ namespace src.player.skills public class LongKnife : ISkill { private const Skills skillName = Skills.LongKnife; + private const string victimSound = "Player.DamageBody.Victim"; + private const string heavyHitSound = "Weapon_Knife.Hit.Heavy.Flesh"; + private const string lightHitSound = "Weapon_Knife.Hit.Light.Flesh"; private static bool hooked = false; private const int actionCode = 503; @@ -121,12 +124,15 @@ namespace src.player.skills if (!SkillsInfo.GetValue(skillName, "friendlyFire") && player.Team == target.Team) return; - target.PlayerPawn.Value.EmitSound("Player.DamageBody.Onlooker"); + SkillUtils.EmitSoundToPlayer(target, victimSound, SkillsInfo.GetValue(skillName, "soundVolume")); + SkillUtils.EmitSoundToPlayer(player, heavyHit ? heavyHitSound : lightHitSound, SkillsInfo.GetValue(skillName, "hitSoundVolume")); SkillUtils.TakeHealth(target.PlayerPawn.Value, heavyHit ? Instance.Random.Next(45, 55) : Instance.Random.Next(21, 34), player, killfeedIcon ?? KillfeedIcons.Knife); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c9f8ff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maxDistance = 4096f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c9f8ff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maxDistance = 4096f, bool friendlyFire = true, float soundVolume = .35f, float hitSoundVolume = .35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; + public float HitSoundVolume { get; set; } = hitSoundVolume; public float MaxDistance { get; set; } = maxDistance; public bool FriendlyFire { get; set; } = friendlyFire; } diff --git a/jRandomSkills - SRC Files/src/player/skills/MagneticDecoy.cs b/jRandomSkills - SRC Files/src/player/skills/MagneticDecoy.cs index a5a4a1a..116cab5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/MagneticDecoy.cs +++ b/jRandomSkills - SRC Files/src/player/skills/MagneticDecoy.cs @@ -16,11 +16,19 @@ namespace src.player.skills public static void LoadSkill() { SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + DecoyRing.PreloadAssets(); } public static void NewRound() { decoys.Clear(); + DecoyRing.ClearAll(skillName); + } + + public static void RoundEnd() + { + decoys.Clear(); + DecoyRing.ClearAll(skillName); } public static void DecoyStarted(EventDecoyStarted @event) @@ -31,7 +39,9 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo?.Skill != skillName) return; - decoys.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0); + Vector pos = new(@event.X, @event.Y, @event.Z); + decoys.TryAdd(pos, 0); + DecoyRing.Show(skillName, (uint)@event.Entityid, pos, SkillsInfo.GetValue(skillName, "triggerRadius")); } public static void DecoyDetonate(EventDecoyDetonate @event) @@ -44,6 +54,8 @@ namespace src.player.skills foreach (var decoy in decoys.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z)) decoys.TryRemove(decoy, out _); + + DecoyRing.Hide(skillName, (uint)@event.Entityid); } public static void OnTick() diff --git a/jRandomSkills - SRC Files/src/player/skills/Medic.cs b/jRandomSkills - SRC Files/src/player/skills/Medic.cs index 058a1b6..fecc9be 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Medic.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Medic.cs @@ -93,7 +93,7 @@ namespace src.player.skills skillInfo.Cooldown = DateTime.Now; skillInfo.Count -= 1; SkillUtils.AddHealth(playerPawn, SkillsInfo.GetValue(skillName, "healthToAdd")); - player.EmitSound("Healthshot.Success"); + SkillUtils.EmitSoundToPlayer(player, "Healthshot.Success", SkillsInfo.GetValue(skillName, "soundVolume")); } } } @@ -106,8 +106,9 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#10c212", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int healthToAdd = 50, int healthShotLimit = 3, float cooldown = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#10c212", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int healthToAdd = 50, int healthShotLimit = 3, float cooldown = 1f, float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int HealthToAdd { get; set; } = healthToAdd; public int HealthShotLimit { get; set; } = healthShotLimit; public float Cooldown { get; set; } = cooldown; diff --git a/jRandomSkills - SRC Files/src/player/skills/Miner.cs b/jRandomSkills - SRC Files/src/player/skills/Miner.cs index 3c8d452..c8adb83 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Miner.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Miner.cs @@ -68,7 +68,7 @@ namespace src.player.skills position.Z += 60; grenade.Teleport(position); - grenade.EmitSound("IncGrenade.Bounce_M"); + grenade.EmitSound("IncGrenade.Bounce_M", volume: SkillsInfo.GetValue(skillName, "soundVolume")); grenade.DetonateTime = Server.CurrentTime + .5f; Utilities.SetStateChanged(grenade, "CBaseGrenade", "m_flDetonateTime"); @@ -161,8 +161,9 @@ namespace src.player.skills SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, 1); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#adf542", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float detonationRange = 130, int grenadeLimit = 3) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#adf542", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float detonationRange = 130, int grenadeLimit = 3, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float DetonationRange { get; set; } = detonationRange; public int GrenadeLimit { get; set; } = grenadeLimit; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs b/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs new file mode 100644 index 0000000..278211c --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs @@ -0,0 +1,156 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; +using static src.jRandomSkills; + +namespace src.player.skills +{ + public class Nemesis : ISkill + { + private const Skills skillName = Skills.Nemesis; + + private static readonly ConcurrentDictionary markedPlayers = []; + private static readonly ConcurrentDictionary ownerToTarget = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + markedPlayers.Clear(); + ownerToTarget.Clear(); + + foreach (var player in PlayerManager.GetTickPlayers()) + SkillUtils.CloseMenu(player); + } + + public static void PlayerDisconnect(uint playerIndex) + { + markedPlayers.TryRemove(playerIndex, out _); + ownerToTarget.TryRemove(playerIndex, out _); + + foreach (var kvp in ownerToTarget) + if (kvp.Value == playerIndex) + ownerToTarget.TryRemove(kvp.Key, out _); + } + + public static void EnableSkill(CCSPlayerController player) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null) return; + playerInfo.SkillUsed = false; + + var playerEvent = PlayerManager.GetPlayerFromEvent(player); + if (playerEvent == null || !playerEvent.IsValid) return; + + var enemies = SkillUtils.GetSelectableEnemies(player, true); + if (enemies.Length > 0) + { + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); + SkillUtils.CreateMenu(player, menuItems); + } + else + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + if (ownerToTarget.TryRemove(player.Index, out uint targetIndex)) + markedPlayers.TryRemove(targetIndex, out _); + + SkillUtils.CloseMenu(player); + } + + public static void OnTick() + { + if (Server.TickCount % 32 != 0) return; + + foreach (var player in PlayerManager.GetTickPlayers()) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null || playerInfo.Skill != skillName) continue; + if (!SkillUtils.HasMenu(player)) continue; + + var enemies = SkillUtils.GetSelectableEnemies(player, true); + + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); + SkillUtils.UpdateMenu(player, menuItems); + } + } + + 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 (playerInfo.SkillUsed) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("areareaper_used_info")}"); + return; + } + + if (commands.Length == 0 || !uint.TryParse(commands[0], out uint enemyIndex)) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + return; + } + + var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex); + if (enemy == null || !enemy.IsValid) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + return; + } + + markedPlayers[enemy.Index] = 0; + ownerToTarget[player.Index] = enemy.Index; + playerInfo.SkillUsed = true; + + playerEvent.PrintToChat($" {ChatColors.Green}{playerEvent.GetTranslation("nemesis_player_info", enemy.PlayerName)}"); + + var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy); + if (enemyEvent != null && enemyEvent.IsValid) + enemyEvent.PrintToChat($" {ChatColors.Red}{enemyEvent.GetTranslation("nemesis_enemy_info")}"); + } + + public static void OnTakeDamage(DynamicHook h) + { + if (markedPlayers.IsEmpty) return; + + CEntityInstance param = h.GetParam(0); + CTakeDamageInfo param2 = h.GetParam(1); + if (param == null || !param.IsValid || param2 == null) return; + + var victimPawn = param.As(); + if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; + + var victimController = victimPawn.Controller.Value; + if (victimController == null || !victimController.IsValid) return; + + var victim = victimController.As(); + if (victim == null || !victim.IsValid) return; + + uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index; + if (!markedPlayers.ContainsKey(victimIndex)) return; + + param2.Damage *= 1f + SkillsInfo.GetValue(skillName, "extraDamagePercent"); + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b8003a", 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, float extraDamagePercent = .25f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float ExtraDamagePercent { get; set; } = extraDamagePercent; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Nightmare.cs b/jRandomSkills - SRC Files/src/player/skills/Nightmare.cs index 207d968..d564014 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Nightmare.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Nightmare.cs @@ -10,6 +10,7 @@ namespace src.player.skills public class Nightmare : ISkill { private const Skills skillName = Skills.Nightmare; + private const string cursedSound = "UI.ArmsRace.FinalKill_Tone"; private static readonly ConcurrentDictionary playersToTarget = []; private static readonly ConcurrentDictionary targetVolumes = []; private static readonly object setLock = new(); @@ -129,7 +130,10 @@ namespace src.player.skills playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("nightmare_player_info", enemy.PlayerName)); if (enemyEvent != null && enemyEvent.IsValid) + { enemyEvent.PrintToChat($" {ChatColors.Red}" + enemyEvent.GetTranslation("nightmare_enemy_info")); + SkillUtils.EmitSoundToPlayer(enemyEvent, cursedSound, SkillsInfo.GetValue(skillName, "soundVolume")); + } } public static void DisableSkill(CCSPlayerController player) @@ -227,8 +231,9 @@ namespace src.player.skills EntityManager.DestroyEntity(volumeIndex); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5b2c6f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, string postProcessing = "lighting/postprocessing/effects/death_cam_phase1_low_violence.vpost", float fadeTime = .25f, float minExposure = .5f, float maxExposure = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5b2c6f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, string postProcessing = "lighting/postprocessing/effects/death_cam_phase1_low_violence.vpost", float fadeTime = .25f, float minExposure = .5f, float maxExposure = 2f, float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public string PostProcessing { get; set; } = postProcessing; public float FadeTime { get; set; } = fadeTime; public float MinExposure { get; set; } = minExposure; diff --git a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs index 604c32a..cb881b6 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs @@ -63,37 +63,29 @@ namespace src.player.skills var bomb = PlayerManager.GetTickBomb(); uint? bombOwnerIndex = bomb != null && bomb.IsValid ? bomb.OwnerEntity?.Index : null; + var hidden = SkillUtils.ResolveHiddenPawns(invisiblePlayers.Keys, bombOwnerIndex); + if (hidden.Count == 0) return; + foreach (var (info, player) in infoList) { if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue; var targetHandle = player.Pawn.Value?.ObserverServices?.ObserverTarget.Value?.Handle ?? nint.Zero; - foreach (var playerIndex in invisiblePlayers.Keys) + foreach (var target in hidden) { - var playerController = PlayerManager.GetPlayerEvent(Utilities.GetPlayerFromIndex((int)playerIndex)); - if (playerController == null || !playerController.IsValid || playerController.Index == player.Index) - continue; - - if (player.Team == playerController.Team) - continue; - - var playerPawn = playerController.PlayerPawn.Value; - if (playerPawn == null || !playerPawn.IsValid) continue; + if (target.Index == player.Index) continue; + if (player.Team == target.Team) continue; // Only the actively spectated pawn stays transmitted; hiding it breaks the camera. - if (targetHandle != nint.Zero && playerPawn.Handle == targetHandle) - continue; + if (targetHandle != nint.Zero && target.Pawn.Handle == targetHandle) continue; - var entity = Utilities.GetEntityFromIndex((int)playerPawn.Index); - if (entity == null || !entity.IsValid) continue; + if (info.TransmitEntities.Contains(target.Pawn.Index)) + info.TransmitEntities.Remove(target.Pawn.Index); - if (info.TransmitEntities.Contains(entity.Index)) - info.TransmitEntities.Remove(entity.Index); + SkillUtils.HideCarriedEntities(info, target.Pawn); - SkillUtils.HideCarriedEntities(info, playerPawn); - - if (bomb == null || bombOwnerIndex != playerController.Index) continue; + if (bomb == null || !target.HoldsBomb) continue; if (info.TransmitEntities.Contains(bomb.Index)) info.TransmitEntities.Remove(bomb.Index); diff --git a/jRandomSkills - SRC Files/src/player/skills/NoNades.cs b/jRandomSkills - SRC Files/src/player/skills/NoNades.cs index ead6b21..be3d6be 100644 --- a/jRandomSkills - SRC Files/src/player/skills/NoNades.cs +++ b/jRandomSkills - SRC Files/src/player/skills/NoNades.cs @@ -15,15 +15,14 @@ namespace src.player.skills SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } - private static readonly HashSet blockedWeapons = - ["hegrenade", "inferno", "decoy", "flashbang", "smokegrenade", "molotov"]; + private const string infernoDamage = "inferno"; public static bool PlayerHurtPre(EventPlayerHurt @event) { var player = PlayerManager.GetPlayerEvent(@event.Userid); if (!Instance.IsPlayerValid(player)) return false; - if (!blockedWeapons.Contains(@event.Weapon)) return false; + if (@event.Weapon != infernoDamage && !WeaponPool.IsGrenade(@event.Weapon)) return false; if (PlayerManager.GetPlayerByIndex(player!.Index)?.Skill != skillName) return false; SkillUtils.RestoreHealth(player); diff --git a/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs b/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs index 66c8270..b81da67 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs @@ -9,6 +9,7 @@ namespace src.player.skills public class PawelJumper : ISkill { private const Skills skillName = Skills.PawelJumper; + private const string jumpSound = "Default.WalkJump"; private static readonly int?[] J = new int?[64]; private static readonly PlayerButtons[] LB = new PlayerButtons[64]; @@ -73,13 +74,17 @@ namespace src.player.skills { J[player.Slot]++; playerEventPawn.AbsVelocity.Z = 300; + + if (J[player.Slot] > 1) + playerEventPawn.EmitSound(jumpSound, volume: SkillsInfo.GetValue(skillName, "soundVolume")); } LB[player.Slot] = buttons; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFA500", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int extraJumpsMin = 1, int extraJumpsMax = 4) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFA500", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int extraJumpsMin = 1, int extraJumpsMax = 4, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int ExtraJumpsMin { get; set; } = extraJumpsMin; public int ExtraJumpsMax { get; set; } = extraJumpsMax; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Pilot.cs b/jRandomSkills - SRC Files/src/player/skills/Pilot.cs index 0f162d3..39f818c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Pilot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Pilot.cs @@ -15,13 +15,29 @@ namespace src.player.skills public static void LoadSkill() { SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + Instance.AddToManifest(SkillsInfo.GetValue(skillName, "particleName")); } public static void NewRound() { + foreach (var pilotInfo in PlayerPilotInfo.Values) + ClearTrail(pilotInfo); + PlayerPilotInfo.Clear(); } + public static void PlayerDeath(EventPlayerDeath @event) + { + var player = PlayerManager.GetPlayerEvent(@event.Userid); + if (player == null || !player.IsValid) return; + + if (PlayerPilotInfo.TryGetValue(player.Index, out var pilotInfo)) + { + ClearTrail(pilotInfo); + pilotInfo.IsFlying = false; + } + } + public static void OnTick() { foreach (var player in PlayerManager.GetTickPlayers()) @@ -39,7 +55,9 @@ namespace src.player.skills public static void DisableSkill(CCSPlayerController player) { - PlayerPilotInfo.TryRemove(player.Index, out _); + if (PlayerPilotInfo.TryRemove(player.Index, out var pilotInfo)) + ClearTrail(pilotInfo); + SkillUtils.ResetPrintHTML(player); } @@ -96,9 +114,18 @@ namespace src.player.skills ); if (inUse) + { ApplyPilotEffect(player); - else if (pilotInfo.Fuel <= 0) - pilotInfo.IsFlying = false; + StartTrail(player, playerPawn, pilotInfo); + } + else + { + if (pilotInfo.TrailIndex != null) + ClearTrail(pilotInfo); + + if (pilotInfo.Fuel <= 0) + pilotInfo.IsFlying = false; + } pilotInfo.LastButtons = buttons; UpdateHUD(player, pilotInfo); @@ -132,6 +159,62 @@ namespace src.player.skills return "#FF0000"; } + private static void StartTrail(CCSPlayerController player, CCSPlayerPawn pawn, Pilot_PlayerInfo pilotInfo) + { + if (pilotInfo.TrailIndex != null) + { + var existing = Utilities.GetEntityFromIndex((int)pilotInfo.TrailIndex.Value); + if (existing != null && existing.IsValid) return; + pilotInfo.TrailIndex = null; + } + + if (pawn.AbsOrigin == null) return; + + var particle = EntityManager.CreateTrackedParticleSystem(player.Index, SkillsInfo.GetValue(skillName, "particleName")); + if (particle == null || !particle.IsValid) return; + + float offset = SkillsInfo.GetValue(skillName, "particleOffset"); + particle.Teleport(new Vector(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + offset)); + particle.AcceptInput("SetParent", pawn, particle, "!activator"); + particle.AcceptInput("Start"); + + pilotInfo.TrailIndex = particle.Index; + } + + private static void StopTrail(Pilot_PlayerInfo pilotInfo) + { + if (pilotInfo.TrailIndex == null) return; + + uint index = pilotInfo.TrailIndex.Value; + pilotInfo.TrailIndex = null; + + var particle = Utilities.GetEntityFromIndex((int)index); + if (particle != null && particle.IsValid) + { + particle.AcceptInput("Stop"); + particle.AcceptInput("DestroyImmediately"); + } + + EntityManager.DestroyEntity(index); + + if (EntityManager.SuppressKills) return; + + Server.NextFrame(() => + { + var leftover = Utilities.GetEntityFromIndex((int)index); + if (leftover != null && leftover.IsValid) + leftover.Remove(); + }); + } + + private static void ClearTrail(Pilot_PlayerInfo pilotInfo) + { + StopTrail(pilotInfo); + + foreach (var leftover in EntityManager.GetPlayerEntities((uint)pilotInfo.SteamID, "particle_system")) + EntityManager.DestroyEntity(leftover); + } + private static void ApplyPilotEffect(CCSPlayerController player) { var playerPawn = player.PlayerPawn.Value; @@ -168,10 +251,13 @@ namespace src.player.skills public int JumpCount { get; set; } = 0; public float LastJumpTime { get; set; } = 0; public bool IsFlying { get; set; } = false; + public uint? TrailIndex { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1466F5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maximumFuel = 150f, float fuelConsumption = .64f, float refuelling = .1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1466F5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maximumFuel = 150f, float fuelConsumption = .64f, float refuelling = .1f, string particleName = "particles/inferno_fx/incgrenade_thrown_trail.vpcf", float particleOffset = 0f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public string ParticleName { get; set; } = particleName; + public float ParticleOffset { get; set; } = particleOffset; public float MaximumFuel { get; set; } = maximumFuel; public float FuelConsumption { get; set; } = fuelConsumption; public float Refuelling { get; set; } = refuelling; diff --git a/jRandomSkills - SRC Files/src/player/skills/Planter.cs b/jRandomSkills - SRC Files/src/player/skills/Planter.cs index 48de162..ee1acb3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Planter.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Planter.cs @@ -94,6 +94,8 @@ namespace src.player.skills public static void OnTick() { float currentTime = Server.CurrentTime; + bool hudFrame = SkillUtils.IsHudFrame(); + foreach (var player in PlayerManager.GetTickPlayers().Where(p => p.Team == CsTeam.Terrorist)) { if (!Instance.IsPlayerValid(player)) continue; @@ -111,7 +113,7 @@ namespace src.player.skills pawn.InBombZone = true; Schema.SetSchemaValue(pawn.Handle, "CCSPlayerPawn", "m_bInBombZone", true); - if (plantingPlayers.TryGetValue(player.Index, out float plantTime)) + if (hudFrame && plantingPlayers.TryGetValue(player.Index, out float plantTime)) { float remaining = plantTime + 3f - currentTime; playerInfo.PrintHTML = $"{player.GetTranslation("planter_planting", $"{Math.Max(0, remaining):0.0}s")}"; diff --git a/jRandomSkills - SRC Files/src/player/skills/Poison.cs b/jRandomSkills - SRC Files/src/player/skills/Poison.cs index fc433f2..5b46fc5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Poison.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Poison.cs @@ -10,6 +10,7 @@ namespace src.player.skills public class Poison : ISkill { private const Skills skillName = Skills.Poison; + private const string poisonSound = "Player.DamageBody.Victim"; private static readonly ConcurrentDictionary poisonedPlayers = []; private static readonly ConcurrentDictionary playersToTarget = []; private static readonly ConcurrentDictionary targetToPlayer = []; @@ -69,7 +70,7 @@ namespace src.player.skills SkillUtils.TakeHealth(pawn, SkillsInfo.GetValue(skillName, "Damage"), GetSkillOwner(playerIndex), KillfeedIcons.Spray); if (Server.TickCount % cooldown2 == 0) - PlayerManager.GetPlayerFromEvent(player)?.ExecuteClientCommand($"play player/player_damagebody_0{jRandomSkills.Instance.Random.Next(4, 8)}"); + SkillUtils.EmitSoundToPlayer(player, poisonSound, SkillsInfo.GetValue(skillName, "soundVolume")); } } @@ -186,8 +187,9 @@ namespace src.player.skills SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#902eff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = .85f, int damage = 1, int minHealth = 30) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#902eff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = .85f, int damage = 1, int minHealth = 30, float soundVolume = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int Damage { get; set; } = damage; public float Cooldown { get; set; } = cooldown; public int MinHealth { get; set; } = minHealth; diff --git a/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs b/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs index d2e6fdb..d26a8cc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs @@ -12,16 +12,7 @@ namespace src.player.skills private static readonly ConcurrentDictionary bannedPlayers = []; private static readonly ConcurrentDictionary playersToTarget = []; private static readonly object setLock = new(); - private static readonly string[] disabledWeapons = - [ - "weapon_ak47", "weapon_m4a4", "weapon_m4a1", "weapon_m4a1_silencer", - "weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg556", - "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", - "weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08", - "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova", - "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", - "weapon_negev" - ]; + private static HashSet disabledWeapons => WeaponPool.Rifles; public static void LoadSkill() { diff --git a/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs b/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs index a3bc7ce..51a5066 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs @@ -69,7 +69,7 @@ namespace src.player.skills } if (!info.Defusing) - pawn.EmitSound("c4.disarmstart"); + pawn.EmitSound("c4.disarmstart", volume: SkillsInfo.GetValue(skillName, "soundVolume")); info.Defusing = true; info.DefusingTime -= (1f / tickRate); @@ -128,8 +128,9 @@ namespace src.player.skills public float DefusingTime { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maxDefusingRange = 80f, float defusingTime = 10f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maxDefusingRange = 80f, float defusingTime = 10f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float MaxDefusingRange { get; set; } = maxDefusingRange; public float DefusingTime { get; set; } = defusingTime; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Punisher.cs b/jRandomSkills - SRC Files/src/player/skills/Punisher.cs new file mode 100644 index 0000000..3b4bf02 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Punisher.cs @@ -0,0 +1,110 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class Punisher : ISkill + { + private const Skills skillName = Skills.Punisher; + + private static readonly ConcurrentDictionary storedDamage = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + storedDamage.Clear(); + } + + public static void EnableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + storedDamage[player.Index] = 0f; + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null) return; + + storedDamage.TryRemove(player.Index, out _); + SkillUtils.ResetPrintHTML(player); + } + + public static void PlayerDisconnect(uint playerIndex) + { + storedDamage.TryRemove(playerIndex, out _); + } + + public static void PlayerDeath(EventPlayerDeath @event) + { + var victim = PlayerManager.GetPlayerEvent(@event.Userid); + if (victim == null || !victim.IsValid) return; + + if (storedDamage.ContainsKey(victim.Index)) + storedDamage[victim.Index] = 0f; + } + + public static void OnTakeDamage(DynamicHook h) + { + if (storedDamage.IsEmpty) return; + + CEntityInstance param = h.GetParam(0); + CTakeDamageInfo param2 = h.GetParam(1); + if (param == null || !param.IsValid || param2 == null) return; + + var victimPawn = param.As(); + if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; + + var victimController = victimPawn.Controller.Value; + if (victimController == null || !victimController.IsValid) return; + + var victim = victimController.As(); + if (victim == null || !victim.IsValid) return; + + uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index; + + var attacker = param2.Attacker?.Value?.As(); + var attackerController = attacker?.Controller?.Value?.As(); + uint? attackerIndex = attackerController != null && attackerController.IsValid + ? PlayerManager.GetPlayerEvent(attackerController)?.Index ?? attackerController.Index + : null; + + if (attackerIndex.HasValue && attackerIndex.Value != victimIndex && storedDamage.TryGetValue(attackerIndex.Value, out float banked) && banked > 0f) + { + storedDamage[attackerIndex.Value] = 0f; + param2.Damage += banked; + + var puncher = CounterStrikeSharp.API.Utilities.GetPlayerFromIndex((int)attackerIndex.Value); + var puncherPawn = puncher?.PlayerPawn?.Value; + + if (puncherPawn != null && puncherPawn.IsValid && puncherPawn.Health > 0) + SkillUtils.SetHealth(puncherPawn, puncherPawn.Health + (int)MathF.Round(banked)); + + var puncherEvent = PlayerManager.GetPlayerFromEvent(puncher); + if (puncherEvent != null && puncherEvent.IsValid) + puncherEvent.PrintToChat($" {ChatColors.Green}{puncherEvent.GetTranslation("punisher_released_info", (int)MathF.Round(banked))}"); + } + + if (!storedDamage.ContainsKey(victimIndex)) return; + if (attackerIndex.HasValue && attackerIndex.Value == victimIndex) return; + + float ratio = SkillsInfo.GetValue(skillName, "storePercent"); + float cap = SkillsInfo.GetValue(skillName, "maxStored"); + + float stored = storedDamage[victimIndex] + (param2.Damage * ratio); + storedDamage[victimIndex] = MathF.Min(stored, cap); + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb347", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float storePercent = .5f, float maxStored = 100f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float StorePercent { get; set; } = storePercent; + public float MaxStored { get; set; } = maxStored; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Push.cs b/jRandomSkills - SRC Files/src/player/skills/Push.cs index 28bf9ab..d5e9115 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Push.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Push.cs @@ -23,6 +23,8 @@ namespace src.player.skills if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return; + if (Heavyweight.Resists(victim)) return; + var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index); if (playerInfo?.Skill == skillName && victim!.PawnIsAlive) diff --git a/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs b/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs index d5578d4..a7a9b3c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs @@ -13,19 +13,8 @@ namespace src.player.skills private static readonly ConcurrentDictionary SkillPlayerInfo = new(); private static readonly object setLock = new(); - private static readonly HashSet pistols = new(StringComparer.Ordinal) - { - "weapon_deagle", "weapon_revolver", "weapon_glock", "weapon_usp_silencer", - "weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", "weapon_elite", "weapon_hkp2000" - }; - - private static readonly HashSet rifles = new(StringComparer.Ordinal) - { - "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", - "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", - "weapon_aug", "weapon_sg556", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1", - "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" - }; + private static HashSet pistols => WeaponPool.Pistols; + private static HashSet rifles => WeaponPool.Rifles; public static void LoadSkill() { @@ -104,6 +93,19 @@ namespace src.player.skills RemoveAndGiveWeapon(player); } + private static bool RollPistol(bool pistolAvailable, bool rifleAvailable) + { + if (!rifleAvailable) return true; + if (!pistolAvailable) return false; + + float pistolChance = Math.Max(SkillsInfo.GetValue(skillName, "pistolChance"), 0); + float rifleChance = Math.Max(SkillsInfo.GetValue(skillName, "rifleChance"), 0); + float total = pistolChance + rifleChance; + + if (total <= 0) return Instance.Random.Next(2) == 0; + return Instance.Random.NextDouble() * total < pistolChance; + } + private static void RemoveAndGiveWeapon(CCSPlayerController player) { var pawn = player.PlayerPawn.Value; @@ -117,12 +119,15 @@ namespace src.player.skills if (playerWeapons.Count == 0) return; - var available = pistols.Concat(rifles).Where(w => !playerWeapons.Contains(w)).ToArray(); - if (available.Length == 0) + var availablePistols = pistols.Where(w => !playerWeapons.Contains(w)).ToArray(); + var availableRifles = rifles.Where(w => !playerWeapons.Contains(w)).ToArray(); + if (availablePistols.Length == 0 && availableRifles.Length == 0) return; + bool isPistol = RollPistol(availablePistols.Length > 0, availableRifles.Length > 0); + var available = isPistol ? availablePistols : availableRifles; + string newWeapon = available[Instance.Random.Next(available.Length)]; - bool isPistol = pistols.Contains(newWeapon); string? weaponToRemove = null; foreach (var item in pawn.WeaponServices.MyWeapons) @@ -163,9 +168,11 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0873a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 15f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0873a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 15f, float pistolChance = 60f, float rifleChance = 40f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float Cooldown { get; set; } = cooldown; + public float PistolChance { get; set; } = pistolChance; + public float RifleChance { get; set; } = rifleChance; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs index 7eea00c..e07b2ad 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs @@ -152,7 +152,7 @@ namespace src.player.skills if (!consumedReplicas.TryAdd(replica.Index, 0)) return; - replica.EmitSound("GlassBottle.BulletImpact", volume: 1f); + replica.EmitSound("GlassBottle.BulletImpact", volume: SkillsInfo.GetValue(skillName, "soundVolume")); var owner = GetReplicaOwner(replica.Index); EntityManager.DestroyEntity(replica.Index); @@ -183,8 +183,9 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a3000b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 15f, int yourTeamDamage = 10, int enemyTeamDamage = 20) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a3000b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 15f, int yourTeamDamage = 10, int enemyTeamDamage = 20, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; public int YourTeamDamage { get; set; } = yourTeamDamage; public int EnemyTeamDamage { get; set; } = enemyTeamDamage; diff --git a/jRandomSkills - SRC Files/src/player/skills/Rewind.cs b/jRandomSkills - SRC Files/src/player/skills/Rewind.cs new file mode 100644 index 0000000..09713b1 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Rewind.cs @@ -0,0 +1,205 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; +using System.Drawing; + +namespace src.player.skills +{ + public class Rewind : ISkill + { + private const Skills skillName = Skills.Rewind; + + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + foreach (var skillInfo in SkillPlayerInfo.Values) + DestroyMarker(skillInfo); + + SkillPlayerInfo.Clear(); + } + + public static void RoundEnd() + { + foreach (var skillInfo in SkillPlayerInfo.Values) + DestroyMarker(skillInfo); + } + + 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)) + DestroyMarker(skillInfo); + + SkillUtils.ResetPrintHTML(player); + } + + public static void PlayerDisconnect(uint playerIndex) + { + if (SkillPlayerInfo.TryRemove(playerIndex, out var skillInfo)) + DestroyMarker(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)) + { + skillInfo.ReturnTick = 0; + DestroyMarker(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.CanUse || skillInfo.ReturnTick != 0) return; + + var pawn = player.PlayerPawn?.Value; + if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return; + + skillInfo.Mark = new Vector(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z); + skillInfo.MarkAngle = new QAngle(0, pawn.V_angle.Y, 0); + skillInfo.ReturnTick = Server.TickCount + (int)(SkillsInfo.GetValue(skillName, "returnDelay") * 64); + skillInfo.CanUse = false; + skillInfo.Cooldown = DateTime.Now; + + SpawnMarker(player, skillInfo); + SkillUtils.EmitSoundToPlayer(player, "UIPanorama.buttonclick", SkillsInfo.GetValue(skillName, "soundVolume")); + } + + 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.ReturnTick != 0 && Server.TickCount >= skillInfo.ReturnTick) + { + skillInfo.ReturnTick = 0; + Teleport(player, skillInfo); + DestroyMarker(skillInfo); + } + + if (hudFrame) + UpdateHUD(player, skillInfo); + } + } + + private static void Teleport(CCSPlayerController player, PlayerSkillInfo skillInfo) + { + if (skillInfo.Mark == null) return; + + var pawn = player.PlayerPawn?.Value; + if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) + { + skillInfo.Mark = null; + return; + } + + pawn.Teleport(skillInfo.Mark, skillInfo.MarkAngle, new Vector(0, 0, 0)); + skillInfo.Mark = null; + } + + private static void SpawnMarker(CCSPlayerController player, PlayerSkillInfo skillInfo) + { + if (skillInfo.Mark == null) return; + if (EntityManager.OverBudget()) return; + + Color color = Color.White; + try { color = ColorTranslator.FromHtml(SkillsInfo.GetValue(skillName, "color")); } + catch { } + + Vector bottom = new(skillInfo.Mark.X, skillInfo.Mark.Y, skillInfo.Mark.Z + 2); + Vector top = new(skillInfo.Mark.X, skillInfo.Mark.Y, skillInfo.Mark.Z + 68); + + var beam = EntityManager.CreateTrackedBeam(player.Index, bottom, top, color); + if (beam == null || !beam.IsValid) return; + + beam.Width = .6f; + beam.EndWidth = .6f; + + Utilities.SetStateChanged(beam, "CBeam", "m_fWidth"); + Utilities.SetStateChanged(beam, "CBeam", "m_fEndWidth"); + + skillInfo.MarkerIndex = beam.Index; + } + + private static void DestroyMarker(PlayerSkillInfo skillInfo) + { + if (skillInfo.MarkerIndex == null) return; + + uint markerIndex = skillInfo.MarkerIndex.Value; + skillInfo.MarkerIndex = null; + + EntityManager.DestroyBeam(markerIndex); + } + + 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.ReturnTick != 0) + { + int remaining = (int)Math.Ceiling((skillInfo.ReturnTick - Server.TickCount) / 64f); + playerInfo.PrintHTML = $"{player.GetTranslation("rewind_pending_info", Math.Max(remaining, 0))}"; + } + 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 Vector? Mark { get; set; } + public QAngle? MarkAngle { get; set; } + public uint? MarkerIndex { get; set; } + public int ReturnTick { get; set; } + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#4fc3f7", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Uncommon, float returnDelay = 4f, float cooldown = 30f, float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float SoundVolume { get; set; } = soundVolume; + public float ReturnDelay { get; set; } = returnDelay; + public float Cooldown { get; set; } = cooldown; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs b/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs index d59de9a..1982f5f 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ricochet.cs @@ -14,6 +14,7 @@ namespace src.player.skills public class Ricochet : ISkill { private const Skills skillName = Skills.Ricochet; + private const string bounceSound = "FX_RicochetSound.Ricochet_Legacy"; private static readonly ConcurrentDictionary impactBudgets = []; private static readonly HashSet ownedTracers = []; @@ -57,18 +58,10 @@ namespace src.player.skills if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) return; - if (!RayTrace.IsAvailable) - { - Trace("RayTrace module unavailable"); - return; - } + if (!RayTrace.IsAvailable) 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 (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; if (!TryConsumeBudget(player.Index)) return; @@ -76,32 +69,14 @@ namespace src.player.skills Vector3 impact = new(@event.X, @event.Y, @event.Z); Vector3 toImpact = impact - eyePos; - if (toImpact.LengthSquared() < 1f) - { - Trace("impact too close to eye"); - return; - } + if (toImpact.LengthSquared() < 1f) 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 == null || !surface.Value.DidHit) 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; - } + if (!TryResolveNormal(player, surface.Value, direction, out Vector3 normal)) return; Bounce(player, pawn, surface.Value.EndPos, direction, normal); } @@ -118,31 +93,28 @@ namespace src.player.skills KillfeedIcons? killfeedIcon = KillfeedIconsExtensions.FromWeaponName(SkillUtils.GetDesignerName(pawn.WeaponServices?.ActiveWeapon?.Value)); List path = []; + List soundAt = []; for (int i = 0; i < bounces; i++) { direction = Vector3.Reflect(direction, normal); Vector3 start = point + normal * 2f; - if (path.Count == 0) path.Add(start); + if (path.Count == 0) + { + path.Add(start); + soundAt.Add(0); + } 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; - } + if (hit == 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.DidHit) break; if (hit.Value.HitPlayer(out var victim)) { @@ -150,17 +122,14 @@ namespace src.player.skills break; } - if (!TryResolveNormal(player, hit.Value, direction, out normal)) - { - Trace($"bounce {i + 1}: could not resolve normal"); - break; - } + if (!TryResolveNormal(player, hit.Value, direction, out normal)) break; + soundAt.Add(path.Count - 1); point = hit.Value.EndPos; damage *= damageFalloff; } - StartFlight(player, path); + StartFlight(player, path, soundAt); } public static void OnTick() @@ -191,11 +160,12 @@ namespace src.player.skills float tail = MathF.Max(head - trail, flight.Cumulative[segment]); UpdateTracer(flight, PointAt(flight, tail), PointAt(flight, head)); + EmitBounceSound(flight, tail); } } } - private static void StartFlight(CCSPlayerController player, List path) + private static void StartFlight(CCSPlayerController player, List path, List soundAt) { if (path.Count < 2) return; if (!SkillsInfo.GetValue(skillName, "showTracer")) return; @@ -223,10 +193,27 @@ namespace src.player.skills Cumulative = cumulative, TotalLength = total, DeadlineTick = Server.TickCount + (int)(total / speed * 64f) + 128, + SoundAt = [.. soundAt.Where(index => index < points.Length)], }); } } + private static void EmitBounceSound(Flight flight, float tail) + { + if (flight.NextSound >= flight.SoundAt.Length) return; + if (tail < flight.Cumulative[flight.SoundAt[flight.NextSound]]) return; + + while (flight.NextSound < flight.SoundAt.Length && tail >= flight.Cumulative[flight.SoundAt[flight.NextSound]]) + flight.NextSound++; + + if (flight.BeamIndex == null) return; + + var beam = ResolveTracer(flight.BeamIndex.Value); + if (beam == null) return; + + beam.EmitSound(bounceSound, volume: SkillsInfo.GetValue(skillName, "soundVolume")); + } + private static int SegmentAt(Flight flight, float distance) { for (int i = flight.Points.Length - 2; i >= 0; i--) @@ -332,9 +319,8 @@ namespace src.player.skills beam.DispatchSpawn(); return beam; } - catch (Exception ex) + catch { - Trace($"beam create failed: {ex.Message}"); return null; } } @@ -352,7 +338,6 @@ namespace src.player.skills if (beam == null) { ownedTracers.Remove(beamIndex); - Trace($"tracer {beamIndex} vanished before release"); return; } @@ -395,9 +380,6 @@ namespace src.player.skills if (beam != null) HideTracer(beam); } - if (ownedTracers.Count > 0) - Trace($"pool reset: {ownedTracers.Count} tracer(s) released"); - tracerPool.Clear(); ownedTracers.Clear(); } @@ -454,7 +436,6 @@ namespace src.player.skills int finalDamage = (int)MathF.Round(damage); if (finalDamage <= 0) return; - Trace($"hit {victimEvent.PlayerName} for {finalDamage}"); SkillUtils.TakeHealth(victimPawn, finalDamage, attacker, killfeedIcon ?? KillfeedIcons.Leg); } @@ -466,7 +447,6 @@ namespace src.player.skills if (TryRefineNormal(player, result.EndPos, direction, rough, out normal)) return true; - Trace("surface refine failed, using rough normal"); normal = rough; return true; } @@ -600,11 +580,6 @@ namespace src.player.skills 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; @@ -637,12 +612,15 @@ namespace src.player.skills public required float[] Cumulative { get; set; } public required float TotalLength { get; set; } public required int DeadlineTick { get; set; } + public required int[] SoundAt { get; set; } public float Travelled { get; set; } + public int NextSound { 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 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, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int Bounces { get; set; } = bounces; public float SegmentDistance { get; set; } = segmentDistance; public float DamageMultiplier { get; set; } = damageMultiplier; diff --git a/jRandomSkills - SRC Files/src/player/skills/Rubber.cs b/jRandomSkills - SRC Files/src/player/skills/Rubber.cs index 80d1b6f..61c426a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Rubber.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Rubber.cs @@ -30,6 +30,8 @@ namespace src.player.skills if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return; var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index); + if (Heavyweight.Resists(victim)) return; + var victimPawn = victim!.PlayerPawn.Value; if (victimPawn == null || !victimPawn.IsValid) return; diff --git a/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs b/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs index fd8b9eb..1b3d204 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs @@ -16,13 +16,7 @@ namespace src.player.skills private static readonly object setLock = new(); private const string weapon_awp = "weapon_awp"; - private static readonly string[] rifles = - [ - "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", - "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", - "weapon_aug", "weapon_sg556", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1", - "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" - ]; + private static HashSet rifles => WeaponPool.Rifles; public static void LoadSkill() { @@ -81,7 +75,7 @@ namespace src.player.skills foreach (var index in AWPs.ToList()) SkillUtils.SafeKillEntity(index); - if (savedWeapons.TryGetValue(player.Index, out string? savedWeapon) && !string.IsNullOrWhiteSpace(savedWeapon)) + if (!SkillUtils.IsPistolRound() && savedWeapons.TryGetValue(player.Index, out string? savedWeapon) && !string.IsNullOrWhiteSpace(savedWeapon)) Server.NextFrame(() => { if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; diff --git a/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs b/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs index 19aa0d2..5ba9dc0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs @@ -100,9 +100,13 @@ namespace src.player.skills { if (SkillPlayerInfo.IsEmpty) return; + if (Server.TickCount % 60 != 0) return; + int cooldown = SkillsInfo.GetValue(skillName, "cooldown"); if (cooldown < 1) cooldown = 1; - if (Server.TickCount % (60 * cooldown) != 0) return; + if ((Server.TickCount / 60) % cooldown != 0) return; + + float volume = SkillsInfo.GetValue(skillName, "soundVolume"); 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)) @@ -111,18 +115,19 @@ namespace src.player.skills if (entities.Count == 0) { - player.PlayerPawn.Value!.EmitSound(soundEventName, volume: 1f); + player.PlayerPawn.Value!.EmitSound(soundEventName, volume: volume); continue; } var entity = Utilities.GetEntityFromIndex((int)entities[0]); if (entity != null && entity.IsValid) - entity.EmitSound(soundEventName, volume: 1f); + entity.EmitSound(soundEventName, volume: volume); } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e3ed8c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int cooldown = 2) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e3ed8c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int cooldown = 2, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int Cooldown { get; set; } = cooldown; } } diff --git a/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs b/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs index 9a02f1f..7f61d18 100644 --- a/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs +++ b/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs @@ -167,7 +167,7 @@ namespace src.player.skills SetMagazines(player, playerMagazines + enemyMagazines); SetMagazines(enemy, 0); - PlayerManager.GetPlayerFromEvent(enemy)?.ExecuteClientCommand($"play sounds/weapons/g3sg1/g3sg1_clipout"); + player.PlayerPawn?.Value?.EmitSound("BaseCombatCharacter.AmmoPickup", volume: SkillsInfo.GetValue(skillName, "soundVolume")); var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy); if (enemyEvent == null || !enemyEvent.IsValid) return; @@ -299,8 +299,9 @@ namespace src.player.skills public required int Reserve { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5eb8b0", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 30f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5eb8b0", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 30f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float Cooldown { get; set; } = cooldown; } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Thorns.cs b/jRandomSkills - SRC Files/src/player/skills/Thorns.cs index 150e41e..22c5063 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Thorns.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Thorns.cs @@ -40,12 +40,13 @@ namespace src.player.skills damage = Math.Min(damage, SkillsInfo.GetValue(skillName, "maxTakenDamagePerShot")); SkillUtils.TakeHealth(attackerEvent.PlayerPawn.Value, damage, victimEvent, KillfeedIcons.Armor); - attackerEvent.EmitSound("Player.DamageBody.Onlooker"); + SkillUtils.EmitSoundToPlayer(attackerEvent, "Player.DamageBody.Victim", SkillsInfo.GetValue(skillName, "soundVolume")); } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#962631", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float healthTakenScale = .3f, int maxTakenDamagePerShot = 37) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#962631", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float healthTakenScale = .3f, int maxTakenDamagePerShot = 37, float soundVolume = .35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float HealthTakenScale { get; set; } = healthTakenScale; public int MaxTakenDamagePerShot { get; set; } = maxTakenDamagePerShot; } diff --git a/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs b/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs index 57a2320..c1d4c1e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs @@ -105,7 +105,7 @@ namespace src.player.skills } if (SkillUtils.TakeHealth(pawn, damage, thrower, KillfeedIcons.Smokegrenade)) - player.EmitSound("Player.DamageBody.Onlooker"); + SkillUtils.EmitSoundToPlayer(eventPlayer, "Player.DamageBody.Victim", SkillsInfo.GetValue(skillName, "soundVolume")); } } } @@ -170,8 +170,9 @@ namespace src.player.skills SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f, float soundVolume = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int SmokeDamage { get; set; } = smokeDamage; public float SmokeRadius { get; set; } = smokeRadius; public int TickCooldown { get; set; } = tickCooldown; diff --git a/jRandomSkills - SRC Files/src/player/skills/Tripwire.cs b/jRandomSkills - SRC Files/src/player/skills/Tripwire.cs index c901253..7fc5f34 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Tripwire.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Tripwire.cs @@ -12,6 +12,7 @@ namespace src.player.skills public class Tripwire : ISkill { private const Skills skillName = Skills.Tripwire; + private const string triggerSound = "Weapon_Taser.ChargeReady"; private static readonly ConcurrentDictionary wires = []; private static readonly ConcurrentDictionary SkillPlayerInfo = []; @@ -267,7 +268,7 @@ namespace src.player.skills var ownerEvent = PlayerManager.GetPlayerFromEvent(owner); if (ownerEvent != null && ownerEvent.IsValid) { - ownerEvent.ExecuteClientCommand("play sounds/weapons/taser/taser_charge_ready"); + SkillUtils.EmitSoundToPlayer(ownerEvent, triggerSound, SkillsInfo.GetValue(skillName, "soundVolume")); ownerEvent.PrintToChat($" {ChatColors.Red}" + ownerEvent.GetTranslation("tripwire_triggered_info", enemyEvent.PlayerName)); } } @@ -314,8 +315,9 @@ namespace src.player.skills public DateTime LastAttempt { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff3b3b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, float radarDuration = 5f, float triggerRadius = 24f, float wireHeight = 30f, float wireWidth = 0.7f, float maxWallDistance = 400f, float cooldown = 20f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff3b3b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Rare, float radarDuration = 5f, float triggerRadius = 24f, float wireHeight = 30f, float wireWidth = 0.7f, float maxWallDistance = 400f, float cooldown = 20f, float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public float RadarDuration { get; set; } = radarDuration; public float TriggerRadius { get; set; } = triggerRadius; public float WireHeight { get; set; } = wireHeight; diff --git a/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs b/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs new file mode 100644 index 0000000..6219996 --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs @@ -0,0 +1,116 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; +using CounterStrikeSharp.API.Modules.Utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class TrueArmor : ISkill + { + private const Skills skillName = Skills.TrueArmor; + + private static readonly ConcurrentDictionary holders = []; + private static readonly ConcurrentDictionary pending = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + pending.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 _); + pending.TryRemove(player.Index, out _); + } + + public static void PlayerDisconnect(uint playerIndex) + { + holders.TryRemove(playerIndex, out _); + pending.TryRemove(playerIndex, out _); + } + + public static void OnTakeDamage(DynamicHook h) + { + if (holders.IsEmpty) return; + + if (!TryResolveVictim(h, out var victim, out var victimPawn, out var info)) return; + if (!holders.ContainsKey(victim!.Index)) return; + + int armor = victimPawn!.ArmorValue; + if (armor <= 0) return; + + float damage = info!.Damage; + if (damage <= 0) return; + + float ratio = SkillsInfo.GetValue(skillName, "armorShare"); + float absorbed = MathF.Min(damage * ratio, armor); + if (absorbed <= 0) return; + + info.Damage = damage - absorbed; + + pending[victim.Index] = (int)MathF.Round(armor - absorbed); + victimPawn.ArmorValue = 0; + + Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue"); + + Debug.WriteToDebug($"[TrueArmor] {victim.PlayerName}: raw={damage:0.#} absorbed={absorbed:0.#} " + + $"passed={info.Damage:0.#} armor={armor}->{pending[victim.Index]} hp={victimPawn.Health}"); + } + + public static void OnTakeDamagePost(DynamicHook h) + { + if (pending.IsEmpty) return; + + if (!TryResolveVictim(h, out var victim, out var victimPawn, out _)) return; + if (!pending.TryRemove(victim!.Index, out int restored)) return; + + victimPawn!.ArmorValue = restored; + Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue"); + } + + private static bool TryResolveVictim(DynamicHook h, out CCSPlayerController? victim, out CCSPlayerPawn? pawn, out CTakeDamageInfo? info) + { + victim = null; + pawn = null; + info = null; + + CEntityInstance param = h.GetParam(0); + CTakeDamageInfo param2 = h.GetParam(1); + if (param == null || !param.IsValid || param2 == null) return false; + + var victimPawn = param.As(); + if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return false; + + var controller = victimPawn.Controller.Value; + if (controller == null || !controller.IsValid) return false; + + var resolved = controller.As(); + if (resolved == null || !resolved.IsValid) return false; + + var routed = PlayerManager.GetPlayerEvent(resolved); + victim = routed != null && routed.IsValid ? routed : resolved; + pawn = victimPawn; + info = param2; + return true; + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9ad3f5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Uncommon, float armorShare = .33f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float ArmorShare { get; set; } = armorShare; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Voodoo.cs b/jRandomSkills - SRC Files/src/player/skills/Voodoo.cs new file mode 100644 index 0000000..ddaf66c --- /dev/null +++ b/jRandomSkills - SRC Files/src/player/skills/Voodoo.cs @@ -0,0 +1,157 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using jRandomSkills.src.utils; +using src.utils; +using System.Collections.Concurrent; + +namespace src.player.skills +{ + public class Voodoo : ISkill + { + private const Skills skillName = Skills.Voodoo; + + private static readonly ConcurrentDictionary ownerToVictim = []; + private static readonly ConcurrentDictionary reflecting = []; + + public static void LoadSkill() + { + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void NewRound() + { + ownerToVictim.Clear(); + reflecting.Clear(); + + foreach (var player in PlayerManager.GetTickPlayers()) + SkillUtils.CloseMenu(player); + } + + public static void PlayerDisconnect(uint playerIndex) + { + ownerToVictim.TryRemove(playerIndex, out _); + reflecting.TryRemove(playerIndex, out _); + + foreach (var kvp in ownerToVictim) + if (kvp.Value == playerIndex) + ownerToVictim.TryRemove(kvp.Key, out _); + } + + public static void EnableSkill(CCSPlayerController player) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null) return; + playerInfo.SkillUsed = false; + + var playerEvent = PlayerManager.GetPlayerFromEvent(player); + if (playerEvent == null || !playerEvent.IsValid) return; + + var enemies = SkillUtils.GetSelectableEnemies(player, true); + if (enemies.Length > 0) + { + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); + SkillUtils.CreateMenu(player, menuItems); + } + else + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + ownerToVictim.TryRemove(player.Index, out _); + SkillUtils.CloseMenu(player); + } + + public static void OnTick() + { + if (Server.TickCount % 32 != 0) return; + + foreach (var player in PlayerManager.GetTickPlayers()) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null || playerInfo.Skill != skillName) continue; + if (!SkillUtils.HasMenu(player)) continue; + + var enemies = SkillUtils.GetSelectableEnemies(player, true); + + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); + SkillUtils.UpdateMenu(player, menuItems); + } + } + + 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 (playerInfo.SkillUsed) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("areareaper_used_info")}"); + return; + } + + if (commands.Length == 0 || !uint.TryParse(commands[0], out uint enemyIndex)) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + return; + } + + var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex); + if (enemy == null || !enemy.IsValid) + { + playerEvent.PrintToChat($" {ChatColors.Red}{playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index")}"); + return; + } + + ownerToVictim[player.Index] = enemy.Index; + playerInfo.SkillUsed = true; + + playerEvent.PrintToChat($" {ChatColors.Green}{playerEvent.GetTranslation("voodoo_player_info", enemy.PlayerName)}"); + } + + public static void PlayerHurt(EventPlayerHurt @event) + { + if (ownerToVictim.IsEmpty) return; + + var owner = PlayerManager.GetPlayerEvent(@event.Userid); + if (owner == null || !owner.IsValid) return; + + if (!ownerToVictim.TryGetValue(owner.Index, out uint victimIndex)) return; + if (@event.DmgHealth <= 0) return; + + if (!reflecting.TryAdd(owner.Index, 0)) return; + + try + { + var victim = Utilities.GetPlayerFromIndex((int)victimIndex); + if (victim == null || !victim.IsValid) return; + + var victimPawn = victim.PlayerPawn?.Value; + if (victimPawn == null || !victimPawn.IsValid || victimPawn.Health <= 0) return; + if (victimPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; + + int reflected = (int)MathF.Round(@event.DmgHealth * SkillsInfo.GetValue(skillName, "reflectPercent")); + if (reflected < 1) return; + + SkillUtils.TakeHealth(victimPawn, reflected, owner, KillfeedIcons.Fist); + } + finally + { + reflecting.TryRemove(owner.Index, out _); + } + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7b2d8b", 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, float reflectPercent = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float ReflectPercent { get; set; } = reflectPercent; + } + } +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs b/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs index 38e74d8..4780a02 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs @@ -50,15 +50,16 @@ namespace src.player.skills else Localization.PrintTranslationToChatAll($" {ChatColors.LightBlue}{{0}}", ["watchmaker_ct"], true, [roundTime]); - player.EmitSound(SkillsInfo.GetValue(skillName, "SoundEvent")); + SkillUtils.EmitSoundToPlayer(player, SkillsInfo.GetValue(skillName, "SoundEvent"), SkillsInfo.GetValue(skillName, "soundVolume")); var proxy = Utilities.FindAllEntitiesByDesignerName("cs_gamerules").FirstOrDefault(); if (proxy == null) return; Utilities.SetStateChanged(proxy, "CCSGameRulesProxy", "m_pGameRules"); } - public class 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 = Rarity.Common, int changeRoundTime = 7, string soundEvent = "UIPanorama.sidemenu_select") : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class 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 = Rarity.Common, int changeRoundTime = 7, string soundEvent = "UIPanorama.sidemenu_select", float soundVolume = .5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public float SoundVolume { get; set; } = soundVolume; public int ChangeRoundTime { get; set; } = changeRoundTime; public string SoundEvent { get; set; } = soundEvent; } diff --git a/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs b/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs index 872f8d1..89466f0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs +++ b/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs @@ -13,12 +13,7 @@ namespace src.player.skills private static readonly ConcurrentDictionary SkillPlayerInfo = []; private static readonly object setLock = new(); - private static readonly HashSet weapons = [ "weapon_deagle", "weapon_revolver", "weapon_glock", "weapon_usp_silencer", - "weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", "weapon_elite", "weapon_hkp2000", - "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", "weapon_sg556", - "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a4", "weapon_m4a1_silencer", "weapon_ak47", - "weapon_aug", "weapon_sg556", "weapon_ssg08", "weapon_awp", "weapon_scar20", "weapon_g3sg1", - "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" ]; + private static HashSet weapons => WeaponPool.All; public static void LoadSkill() { diff --git a/jRandomSkills - SRC Files/src/utils/Config.cs b/jRandomSkills - SRC Files/src/utils/Config.cs index 270e4f5..b523cf5 100644 --- a/jRandomSkills - SRC Files/src/utils/Config.cs +++ b/jRandomSkills - SRC Files/src/utils/Config.cs @@ -33,6 +33,9 @@ namespace src.utils using (var sr = new StreamReader(fs)) json = sr.ReadToEnd(); newConfig = JsonConvert.DeserializeObject(json) ?? new SettingsModel(); + + if (IsSectionMissing(json, nameof(SettingsModel.Weapons))) + SaveConfig(newConfig); } catch { @@ -45,6 +48,18 @@ namespace src.utils } } + private static bool IsSectionMissing(string json, string section) + { + try + { + return Newtonsoft.Json.Linq.JObject.Parse(json)[section] == null; + } + catch + { + return false; + } + } + public static void SaveConfig(SettingsModel config) { lock (fileLock) @@ -92,6 +107,7 @@ namespace src.utils public string DisableHUDOnDeathPermission { get; set; } public bool DisableSkillsOnRoundEnd { get; set; } public int? CurseSkillPerPlayer { get; set; } + public WeaponPools Weapons { get; set; } public LanguageSystem LanguageSystem { get; set; } public HtmlHudCustomisation HtmlHudCustomisation { get; set; } public ChatMessage ChatMessage { get; set; } @@ -124,6 +140,31 @@ namespace src.utils DisableSkillsOnRoundEnd = false; CurseSkillPerPlayer = null; + Weapons = new WeaponPools + { + Rifle = + [ + "weapon_ak47", "weapon_m4a1", "weapon_m4a1_silencer", + "weapon_famas", "weapon_galilar", "weapon_aug", "weapon_sg556", + "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", + "weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_ssg08", + "weapon_awp", "weapon_scar20", "weapon_g3sg1", "weapon_nova", + "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", + "weapon_negev" + ], + Pistol = + [ + "weapon_deagle", "weapon_revolver", "weapon_glock", "weapon_usp_silencer", + "weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", + "weapon_elite", "weapon_hkp2000" + ], + Grenade = + [ + "weapon_hegrenade", "weapon_flashbang", "weapon_smokegrenade", + "weapon_molotov", "weapon_incgrenade", "weapon_decoy" + ], + }; + LanguageSystem = new LanguageSystem { DefaultLangCode = "en", @@ -204,6 +245,13 @@ namespace src.utils } } + public class WeaponPools + { + public required List Rifle { get; set; } + public required List Pistol { get; set; } + public required List Grenade { get; set; } + } + public class ChatMessage { public required float MaxWidth { get; set; } diff --git a/jRandomSkills - SRC Files/src/utils/DecoyRing.cs b/jRandomSkills - SRC Files/src/utils/DecoyRing.cs new file mode 100644 index 0000000..5e33782 --- /dev/null +++ b/jRandomSkills - SRC Files/src/utils/DecoyRing.cs @@ -0,0 +1,204 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Timers; +using CounterStrikeSharp.API.Modules.Utils; +using src.player; +using System.Collections.Concurrent; +using System.Drawing; +using System.Globalization; + +namespace src.utils +{ + public static class DecoyRing + { + private const string discModel = "models/dev/grenade_trajectory/grenade_target.vmdl"; + + private const float discBaseRadius = 13.462f; + private const int discAlpha = 178; + + private const int outerSegments = 24; + private const int innerSegments = 18; + private const float ringHeight = 3f; + + private const float maxLifetime = 25f; + + private static readonly ConcurrentDictionary rings = []; + + private sealed class Ring + { + public uint? Disc { get; set; } + public List Beams { get; } = []; + } + + public static void PreloadAssets() + { + jRandomSkills.Instance.AddToManifest(discModel); + } + + public static void Show(Skills skill, uint id, Vector center, float radius) + { + if (radius <= 0) return; + + string key = MakeKey(skill, id); + var ring = new Ring(); + if (!rings.TryAdd(key, ring)) return; + + SpawnDisc(center, radius, ring); + + if (!EntityManager.OverBudget()) + { + Color color = GetSkillColor(skill); + SpawnRing(center, radius, outerSegments, color, ring); + SpawnRing(center, radius * .5f, innerSegments, color, ring); + } + + jRandomSkills.Instance.AddTimer(maxLifetime, () => Hide(skill, id), TimerFlags.STOP_ON_MAPCHANGE); + } + + public static void Hide(Skills skill, uint id) + { + if (!rings.TryRemove(MakeKey(skill, id), out var ring)) return; + Destroy(ring); + } + + public static void ClearAll(Skills skill) + { + string prefix = skill + "|"; + + foreach (var key in rings.Keys) + { + if (!key.StartsWith(prefix, StringComparison.Ordinal)) continue; + if (!rings.TryRemove(key, out var ring)) continue; + + Destroy(ring); + } + } + + private static void Destroy(Ring ring) + { + foreach (uint beamIndex in ring.Beams) + EntityManager.DestroyBeam(beamIndex); + + ring.Beams.Clear(); + + if (ring.Disc == null) return; + + uint discIndex = ring.Disc.Value; + ring.Disc = null; + + EntityManager.DestroyEntity(discIndex, 0f); + } + + private static string MakeKey(Skills skill, uint id) + { + return string.Create(CultureInfo.InvariantCulture, $"{skill}|{id}"); + } + + private static Color GetSkillColor(Skills skill) + { + try + { + string hex = SkillsInfo.GetValue(skill, "color"); + if (!string.IsNullOrEmpty(hex)) + return ColorTranslator.FromHtml(hex); + } + catch { } + + return Color.White; + } + + private static void SpawnDisc(Vector center, float radius, Ring ring) + { + var disc = EntityManager.CreateTrackedPropOverride(EntityManager.SystemOwnerIndex); + if (disc == null || !disc.IsValid) return; + + uint discIndex = disc.Index; + ring.Disc = discIndex; + + float scale = radius / discBaseRadius; + Vector pos = new(center.X, center.Y, center.Z + 1f); + + Server.NextFrame(() => + { + var prop = Utilities.GetEntityFromIndex((int)discIndex); + if (prop == null || !prop.IsValid) return; + + var node = prop.CBodyComponent?.SceneNode?.Owner?.Entity; + if (node != null) + node.Flags = (uint)(node.Flags & ~(1 << 2)); + + MakeNonSolid(prop); + + prop.SetModel(discModel); + prop.Teleport(pos, new QAngle(0, 0, 0)); + prop.DispatchSpawn(); + + prop.Render = Color.FromArgb(discAlpha, 255, 255, 255); + Utilities.SetStateChanged(prop, "CBaseModelEntity", "m_clrRender"); + + var skeleton = prop.CBodyComponent?.SceneNode?.GetSkeletonInstance(); + if (skeleton != null) + { + skeleton.Scale = scale; + prop.AcceptInput("SetScale", null, null, scale.ToString(CultureInfo.InvariantCulture)); + } + + Utilities.SetStateChanged(prop, "CBaseEntity", "m_CBodyComponent"); + }); + } + + private static void MakeNonSolid(CDynamicProp prop) + { + var collision = prop.Collision; + if (collision == null) return; + + collision.SolidType = SolidType_t.SOLID_NONE; + collision.SolidFlags = 12; + + var attribute = collision.CollisionAttribute; + if (attribute == null) return; + + attribute.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_NEVER; + attribute.CollisionFunctionMask = 0; + attribute.InteractsAs = 0; + attribute.InteractsWith = 0; + attribute.InteractsExclude = 0; + } + + private static void SpawnRing(Vector center, float radius, int segments, Color color, Ring ring) + { + if (segments < 3 || radius <= 0) return; + + float step = MathF.Tau / segments; + Vector previous = PointOn(center, radius, 0); + + for (int i = 1; i <= segments; i++) + { + Vector next = PointOn(center, radius, step * i); + + var beam = EntityManager.CreateTrackedBeam(EntityManager.SystemOwnerIndex, previous, next, color); + if (beam != null && beam.IsValid) + { + beam.Width = 1.2f; + beam.EndWidth = 1.2f; + + Utilities.SetStateChanged(beam, "CBeam", "m_fWidth"); + Utilities.SetStateChanged(beam, "CBeam", "m_fEndWidth"); + + ring.Beams.Add(beam.Index); + } + + previous = next; + } + } + + private static Vector PointOn(Vector center, float radius, float angle) + { + return new Vector( + center.X + MathF.Cos(angle) * radius, + center.Y + MathF.Sin(angle) * radius, + center.Z + ringHeight); + } + } +} diff --git a/jRandomSkills - SRC Files/src/utils/EntityManager.cs b/jRandomSkills - SRC Files/src/utils/EntityManager.cs index b965cd5..463ed5a 100644 --- a/jRandomSkills - SRC Files/src/utils/EntityManager.cs +++ b/jRandomSkills - SRC Files/src/utils/EntityManager.cs @@ -273,6 +273,40 @@ namespace src.utils return false; } + public static bool DestroyBeam(uint entityIndex) + { + try + { + var beam = Utilities.GetEntityFromIndex((int)entityIndex); + if (beam != null && beam.IsValid) + { + 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"); + } + } + catch (Exception ex) + { + Server.PrintToConsole($"[EntityManager] DestroyBeam {entityIndex}: {ex.Message}"); + } + + bool killed = DestroyEntity(entityIndex); + + if (!SuppressKills) + Server.NextFrame(() => + { + var leftover = Utilities.GetEntityFromIndex((int)entityIndex); + if (leftover != null && leftover.IsValid) + leftover.Remove(); + }); + + return killed; + } + public static bool DestroyEntity(uint? entityIndex) where T : CBaseEntity { if (entityIndex == null) return false; diff --git a/jRandomSkills - SRC Files/src/utils/KillfeedIcons.cs b/jRandomSkills - SRC Files/src/utils/KillfeedIcons.cs index 4292c0c..fb04dbb 100644 --- a/jRandomSkills - SRC Files/src/utils/KillfeedIcons.cs +++ b/jRandomSkills - SRC Files/src/utils/KillfeedIcons.cs @@ -281,7 +281,7 @@ namespace jRandomSkills.src.utils "weapon_sg556" => KillfeedIcons.Sg556, "weapon_famas" => KillfeedIcons.Famas, "weapon_galilar" => KillfeedIcons.Galilar, - "weapon_m4a4" => KillfeedIcons.M4A4, + "weapon_m4a1" => KillfeedIcons.M4A4, "weapon_m4a1_silencer" => KillfeedIcons.M4A1, "weapon_ak47" => KillfeedIcons.Ak47, "weapon_aug" => KillfeedIcons.Aug, diff --git a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs index f2c2517..295acc6 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs @@ -40,7 +40,7 @@ namespace src.utils LazySig>("SnapViewAngles", s => new(s)); // private static readonly int collisionRulesChangedOffset = GameData.GetOffset("CBaseEntity_CollisionRulesChanged"); - public static void PrintToChat(CCSPlayerController player, string? msg, string border = "tb", string? title = null) + public static void PrintToChat(CCSPlayerController player, string? msg, string border = "tb", string? title = null, bool ignoreIlliterate = false) { if (!player.IsValid) return; @@ -49,7 +49,7 @@ namespace src.utils char symbol = config.LineSymbol; if (string.IsNullOrEmpty(title)) title = player.GetTranslation("jRandomSkills"); - if (Illiterate.CheckIlliterateSkill(player)) + if (!ignoreIlliterate && Illiterate.CheckIlliterateSkill(player)) msg = Illiterate.GetRandomText(msg); if (border.Contains('t') && config.LineShow) @@ -60,11 +60,39 @@ namespace src.utils player.PrintToChat($" {MeansureString.GetTextDashed("", maxWidth, symbol, config.LineColor)}"); } + public static void EmitSoundToPlayer(CCSPlayerController? listener, string soundEvent, float volume) + { + var target = PlayerManager.GetPlayerFromEvent(listener); + if (target == null || !target.IsValid) return; + + target.EmitSound(soundEvent, new RecipientFilter(target), volume); + } + public static bool IsFreezeTime() { return jRandomSkills.Instance?.GameRules?.FreezePeriod == true; } + public static bool IsPistolRound() + { + var gameRules = jRandomSkills.Instance?.GameRules; + if (gameRules == null) return false; + + if (gameRules.TotalRoundsPlayed == 0 || gameRules.GameRestart) return true; + + try + { + if (ConVar.Find("mp_halftime")?.GetPrimitiveValue() != true) return false; + + int maxRounds = ConVar.Find("mp_maxrounds")?.GetPrimitiveValue() ?? 0; + return maxRounds > 0 && maxRounds / 2 == gameRules.TotalRoundsPlayed; + } + catch + { + return false; + } + } + public static bool IsHudFrame() => Server.TickCount % 4 == 0; public static void RegisterSkill(Skills skill, string color, bool display = true) @@ -396,7 +424,8 @@ namespace src.utils Skills.Deaf, Skills.ExpensiveAmmo, Skills.Giant, Skills.Glitch, Skills.Jammer, Skills.JumpBan, Skills.JumpCurse, Skills.LifeSwap, Skills.Magnifier, Skills.MoneySwap, Skills.Nightmare, Skills.Poison, - Skills.JetKick, Skills.PrimaryBan, Skills.Thief, Skills.WildThrow + Skills.JetKick, Skills.PrimaryBan, Skills.Thief, Skills.WildThrow, + Skills.Voodoo, Skills.Nemesis, Skills.Bounty ]; private static readonly HashSet curseSkillNames = new(curseSkills.Select(s => s.ToString()), StringComparer.Ordinal); @@ -490,6 +519,73 @@ namespace src.utils else curseCounts[victimIndex] = used - 1; } + private const uint crosshairBit = 1u << 8; + + private static readonly object hudSuppressionLock = new(); + private static readonly Dictionary> crosshairOwners = []; + private static readonly Dictionary> radarOwners = []; + + public static void SetCrosshairHidden(CCSPlayerController? player, string owner, bool hide) + { + if (player == null || !player.IsValid) return; + + var pawn = player.PlayerPawn?.Value; + if (pawn == null || !pawn.IsValid) return; + + if (!TryUpdateOwners(crosshairOwners, player.Index, owner, hide, out bool suppressed)) return; + + if (suppressed) pawn.HideHUD |= crosshairBit; + else pawn.HideHUD &= ~crosshairBit; + + Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_iHideHUD"); + } + + public static void SetRadarDisabled(CCSPlayerController? player, string owner, bool disable) + { + if (player == null || !player.IsValid) return; + if (!TryUpdateOwners(radarOwners, player.Index, owner, disable, out bool suppressed)) return; + + player.ReplicateConVar("sv_disable_radar", suppressed ? "1" : "0"); + } + + public static void ClearHudSuppression(uint playerIndex) + { + lock (hudSuppressionLock) + { + crosshairOwners.Remove(playerIndex); + radarOwners.Remove(playerIndex); + } + } + + public static void ClearAllHudSuppression() + { + lock (hudSuppressionLock) + { + crosshairOwners.Clear(); + radarOwners.Clear(); + } + } + + private static bool TryUpdateOwners(Dictionary> registry, uint playerIndex, string owner, bool claim, out bool suppressed) + { + lock (hudSuppressionLock) + { + if (!registry.TryGetValue(playerIndex, out var owners)) + { + owners = []; + registry[playerIndex] = owners; + } + + bool before = owners.Count > 0; + bool changed = claim ? owners.Add(owner) : owners.Remove(owner); + + suppressed = owners.Count > 0; + if (!suppressed) registry.Remove(playerIndex); + + return changed && before != suppressed; + } + } + public static CCSPlayerController[] GetSelectableEnemies(CCSPlayerController player, bool respectCurseLimit = false) { if (player == null || !player.IsValid) return []; @@ -575,6 +671,26 @@ namespace src.utils return true; } + public readonly record struct HiddenPawn(uint Index, CsTeam Team, CCSPlayerPawn Pawn, bool HoldsBomb); + + public static List ResolveHiddenPawns(ICollection playerIndexes, uint? bombOwnerIndex) + { + List hidden = new(playerIndexes.Count); + + foreach (var playerIndex in playerIndexes) + { + var controller = PlayerManager.GetPlayerEvent(Utilities.GetPlayerFromIndex((int)playerIndex)); + if (controller == null || !controller.IsValid) continue; + + var pawn = controller.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid) continue; + + hidden.Add(new HiddenPawn(controller.Index, controller.Team, pawn, bombOwnerIndex == controller.Index)); + } + + return hidden; + } + public static void HideCarriedEntities(CCheckTransmitInfo info, CCSPlayerPawn? pawn) { if (pawn == null || !pawn.IsValid) return; diff --git a/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs b/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs index 63deed9..9dc501c 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs @@ -21,6 +21,7 @@ namespace src.utils private static SkillsInfoModel? _indexedConfig; private static ConcurrentDictionary _byName = new(); private static readonly ConcurrentDictionary<(Type Type, string Key), MemberInfo?> _memberCache = new(); + private static readonly ConcurrentDictionary<(DefaultSkillInfo Config, string Key, Type Target), object?> _valueCache = new(); public static SkillsInfoModel LoadSkillsInfo() { @@ -92,6 +93,12 @@ namespace src.utils if (!_byName.TryGetValue(skill.ToString()!, out var skillConfig) || skillConfig == null) return default!; + object? cached = _valueCache.GetOrAdd((skillConfig, key, typeof(T)), k => Resolve(k.Config, k.Key, k.Target)); + return cached == null ? default! : (T)cached; + } + + private static object? Resolve(DefaultSkillInfo skillConfig, string key, Type targetType) + { var member = _memberCache.GetOrAdd((skillConfig.GetType(), key), k => { MemberInfo? m = k.Type.GetProperty(k.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); @@ -106,11 +113,10 @@ namespace src.utils _ => null }; - if (value == null) return default!; + if (value == null) return null; - Type targetType = typeof(T); Type? underlyingType = Nullable.GetUnderlyingType(targetType); - return (T)Convert.ChangeType(value, underlyingType ?? targetType); + return Convert.ChangeType(value, underlyingType ?? targetType); } private static void EnsureIndex() @@ -124,6 +130,7 @@ namespace src.utils _byName = dict; _indexedConfig = config; _memberCache.Clear(); + _valueCache.Clear(); } public class SkillsInfoModel : ConcurrentBag @@ -147,10 +154,11 @@ namespace src.utils } } - public class DefaultSkillInfo(Skills skill, bool active = true, string color = "#ffffff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) + public class DefaultSkillInfo(Skills skill, bool active = true, string color = "#ffffff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, bool disableOnPistolRound = false) { public bool NeedsTeammates { get; set; } = needsTeammates; public bool DisableOnFreezeTime { get; set; } = disableOnFreezeTime; + public bool DisableOnPistolRound { get; set; } = disableOnPistolRound; public int OnlyTeam { get; set; } = (int)onlyTeam; public string Color { get; set; } = color; public bool Active { get; set; } = active; diff --git a/jRandomSkills - SRC Files/src/utils/WeaponPool.cs b/jRandomSkills - SRC Files/src/utils/WeaponPool.cs new file mode 100644 index 0000000..95301a8 --- /dev/null +++ b/jRandomSkills - SRC Files/src/utils/WeaponPool.cs @@ -0,0 +1,84 @@ +namespace src.utils +{ + public static class WeaponPool + { + private static readonly object buildLock = new(); + private static bool built; + + private static HashSet rifles = new(StringComparer.Ordinal); + private static HashSet pistols = new(StringComparer.Ordinal); + private static HashSet grenades = new(StringComparer.Ordinal); + private static HashSet all = new(StringComparer.Ordinal); + + private static string[] rifleArray = []; + private static string[] pistolArray = []; + private static string[] grenadeArray = []; + private static string[] allArray = []; + + public static HashSet Rifles { get { Build(); return rifles; } } + public static HashSet Pistols { get { Build(); return pistols; } } + public static HashSet Grenades { get { Build(); return grenades; } } + public static HashSet All { get { Build(); return all; } } + + public static string[] RifleList { get { Build(); return rifleArray; } } + public static string[] PistolList { get { Build(); return pistolArray; } } + public static string[] GrenadeList { get { Build(); return grenadeArray; } } + public static string[] AllList { get { Build(); return allArray; } } + + public static bool IsRifle(string? weapon) => weapon != null && Rifles.Contains(Normalize(weapon)); + public static bool IsPistol(string? weapon) => weapon != null && Pistols.Contains(Normalize(weapon)); + public static bool IsGrenade(string? weapon) => weapon != null && Grenades.Contains(Normalize(weapon)); + public static bool IsWeapon(string? weapon) => weapon != null && All.Contains(Normalize(weapon)); + + public static string Normalize(string weapon) + { + if (string.IsNullOrEmpty(weapon)) return string.Empty; + return weapon.StartsWith("weapon_", StringComparison.Ordinal) ? weapon : "weapon_" + weapon; + } + + public static void Invalidate() + { + lock (buildLock) built = false; + } + + private static void Build() + { + if (built) return; + + lock (buildLock) + { + if (built) return; + + var pools = Config.LoadedConfig?.Weapons; + + rifles = Collect(pools?.Rifle); + pistols = Collect(pools?.Pistol); + grenades = Collect(pools?.Grenade); + + all = new HashSet(rifles, StringComparer.Ordinal); + all.UnionWith(pistols); + + rifleArray = [.. rifles]; + pistolArray = [.. pistols]; + grenadeArray = [.. grenades]; + allArray = [.. all]; + + built = true; + } + } + + private static HashSet Collect(List? source) + { + var set = new HashSet(StringComparer.Ordinal); + if (source == null) return set; + + foreach (var entry in source) + { + if (string.IsNullOrWhiteSpace(entry)) continue; + set.Add(Normalize(entry.Trim())); + } + + return set; + } + } +} diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll index 48f49b6..8e95270 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/config.json b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json index bb77ff6..75e957a 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json @@ -22,6 +22,54 @@ "DisableHUDOnDeathPermission": "@jRandomSkills/death", "DisableSkillsOnRoundEnd": false, "CurseSkillPerPlayer": null, + "Weapons": { + "Rifle": [ + "weapon_ak47", + "weapon_m4a1", + "weapon_m4a1_silencer", + "weapon_famas", + "weapon_galilar", + "weapon_aug", + "weapon_sg556", + "weapon_mp9", + "weapon_mac10", + "weapon_bizon", + "weapon_mp7", + "weapon_ump45", + "weapon_p90", + "weapon_mp5sd", + "weapon_ssg08", + "weapon_awp", + "weapon_scar20", + "weapon_g3sg1", + "weapon_nova", + "weapon_xm1014", + "weapon_mag7", + "weapon_sawedoff", + "weapon_m249", + "weapon_negev" + ], + "Pistol": [ + "weapon_deagle", + "weapon_revolver", + "weapon_glock", + "weapon_usp_silencer", + "weapon_cz75a", + "weapon_fiveseven", + "weapon_p250", + "weapon_tec9", + "weapon_elite", + "weapon_hkp2000" + ], + "Grenade": [ + "weapon_hegrenade", + "weapon_flashbang", + "weapon_smokegrenade", + "weapon_molotov", + "weapon_incgrenade", + "weapon_decoy" + ] + }, "LanguageSystem": { "DefaultLangCode": "en", "DisableGeoLite": false, diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json index 55054e5..d85cf3c 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json @@ -2,6 +2,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#fbff00", "Active": true, @@ -15,6 +16,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#384728", "Active": true, @@ -29,6 +31,7 @@ "GrenadeLimit": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8f6dc9", "Active": true, @@ -43,6 +46,7 @@ "Cooldown": 30.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#c7e03a", "Active": true, @@ -54,10 +58,12 @@ "Rarity": "Common" }, { + "SoundVolume": 0.5, "ChangeRoundTime": 7, "SoundEvent": "UIPanorama.sidemenu_select", "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff462e", "Active": true, @@ -71,6 +77,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#5d00ff", "Active": true, @@ -82,6 +89,37 @@ "Rarity": "Epic" }, { + "ReflectPercent": 0.5, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#7b2d8b", + "Active": true, + "Name": "Voodoo", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": 2, + "Rarity": "Rare" + }, + { + "ArmorShare": 0.33, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#9ad3f5", + "Active": true, + "Name": "TrueArmor", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Uncommon" + }, + { + "SoundVolume": 0.5, "RadarDuration": 5.0, "TriggerRadius": 24.0, "WireHeight": 30.0, @@ -90,6 +128,7 @@ "Cooldown": 20.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff3b3b", "Active": true, @@ -101,6 +140,7 @@ "Rarity": "Rare" }, { + "SoundVolume": 0.3, "SmokeDamage": 2, "SmokeRadius": 180.0, "TickCooldown": 17, @@ -108,6 +148,7 @@ "DmgReductionForTeamates": 0.5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#507529", "Active": true, @@ -123,6 +164,7 @@ "Damage": 9999, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8f108f", "Active": true, @@ -134,10 +176,12 @@ "Rarity": "Common" }, { + "SoundVolume": 0.35, "HealthTakenScale": 0.3, "MaxTakenDamagePerShot": 37, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#962631", "Active": true, @@ -152,6 +196,7 @@ "Distance": 100.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1b04cc", "Active": true, @@ -165,6 +210,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#adaec7", "Active": true, @@ -180,6 +226,7 @@ "ChanceTo": 0.6, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8A2BE2", "Active": true, @@ -196,6 +243,7 @@ "TeleportDistance": 100.0, "NeedsTeammates": true, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#bcf542", "Active": true, @@ -207,9 +255,11 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Cooldown": 30.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#5eb8b0", "Active": true, @@ -225,6 +275,7 @@ "CooldownBeforeUse": 10.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1466F5", "Active": true, @@ -240,6 +291,7 @@ "UseCooldown": 0.5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#42f5da", "Active": true, @@ -251,9 +303,11 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Cooldown": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#e3ed8c", "Active": true, @@ -269,6 +323,7 @@ "ChanceTo": 1.35, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#09ba00", "Active": true, @@ -282,6 +337,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#e0873a", "Active": true, @@ -296,6 +352,7 @@ "GrenadeLimit": 1, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#b5ab8f", "Active": true, @@ -309,6 +366,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#333333", "Active": true, @@ -323,6 +381,7 @@ "DetonationTime": 20, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 2, "Color": "#f5b74c", "Active": true, @@ -339,6 +398,7 @@ "ChanceTo": 0.45, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#4d4d4d", "Active": true, @@ -353,6 +413,7 @@ "StartHealth": 50, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#d41c1c", "Active": true, @@ -366,6 +427,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8A2BE2", "Active": true, @@ -381,6 +443,7 @@ "SlownessModifier": 0.2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8B4513", "Active": true, @@ -395,6 +458,7 @@ "MoneyMultiplier": 35, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#119125", "Active": true, @@ -406,6 +470,7 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Bounces": 3, "SegmentDistance": 1200.0, "DamageMultiplier": 0.5, @@ -421,6 +486,7 @@ "NormalProbeOffset": 6.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffd75e", "Active": true, @@ -436,6 +502,7 @@ "MaxMoney": 15000, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#D4AF37", "Active": true, @@ -454,6 +521,7 @@ "A": 60, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff5C0A", "Active": true, @@ -464,9 +532,27 @@ "MaxPerServer": -1, "Rarity": "Common" }, + { + "SoundVolume": 0.5, + "ReturnDelay": 4.0, + "Cooldown": 30.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#4fc3f7", + "Active": true, + "Name": "Rewind", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Uncommon" + }, { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a68132", "Active": true, @@ -481,6 +567,7 @@ "Cooldown": 15.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a86eff", "Active": true, @@ -492,11 +579,13 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Cooldown": 15.0, "YourTeamDamage": 10, "EnemyTeamDamage": 20, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a3000b", "Active": true, @@ -512,6 +601,7 @@ "Cooldown": 0.25, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff462e", "Active": true, @@ -526,6 +616,7 @@ "Cooldown": 15.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#3cded3", "Active": true, @@ -538,8 +629,11 @@ }, { "Cooldown": 15.0, + "PistolChance": 60.0, + "RifleChance": 40.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#e0873a", "Active": true, @@ -555,6 +649,7 @@ "MaxExtraHealth": 501, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#009905", "Active": true, @@ -568,6 +663,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#2effcb", "Active": true, @@ -581,6 +677,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8a42f5", "Active": true, @@ -596,6 +693,7 @@ "GrenadeLimit": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#3c47de", "Active": true, @@ -613,6 +711,7 @@ "PushVelocity": 400.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1e9ab0", "Active": true, @@ -624,10 +723,28 @@ "Rarity": "Common" }, { + "StorePercent": 0.5, + "MaxStored": 100.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#ffb347", + "Active": true, + "Name": "Punisher", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Common" + }, + { + "SoundVolume": 1.0, "MaxDefusingRange": 80.0, "DefusingTime": 10.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 3, "Color": "#507529", "Active": true, @@ -641,6 +758,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#9c9c9c", "Active": true, @@ -654,6 +772,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffc061", "Active": true, @@ -665,11 +784,13 @@ "Rarity": "Common" }, { + "SoundVolume": 0.3, "Damage": 1, "Cooldown": 0.85, "MinHealth": 30, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#902eff", "Active": true, @@ -684,6 +805,7 @@ "ExtraC4BlowTime": 60, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 2, "Color": "#7d7d7d", "Active": true, @@ -695,11 +817,14 @@ "Rarity": "Common" }, { + "ParticleName": "particles/inferno_fx/incgrenade_thrown_trail.vpcf", + "ParticleOffset": 0.0, "MaximumFuel": 150.0, "FuelConsumption": 0.64, "Refuelling": 0.1, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1466F5", "Active": true, @@ -715,6 +840,7 @@ "ChanceTo": 0.4, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff5C0A", "Active": true, @@ -726,10 +852,12 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "ExtraJumpsMin": 1, "ExtraJumpsMax": 4, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FFA500", "Active": true, @@ -743,6 +871,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#3c47de", "Active": true, @@ -756,6 +885,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff5CD9", "Active": true, @@ -769,6 +899,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8a42f5", "Active": true, @@ -782,6 +913,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FFFFFF", "Active": true, @@ -795,6 +927,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a38c1a", "Active": true, @@ -811,6 +944,7 @@ "Duration": 2.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#44ebd4", "Active": true, @@ -827,6 +961,7 @@ "KnifePercentInvisibility": 0.3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#dedede", "Active": true, @@ -838,12 +973,14 @@ "Rarity": "Common" }, { + "SoundVolume": 0.5, "PostProcessing": "lighting/postprocessing/effects/death_cam_phase1_low_violence.vpost", "FadeTime": 0.25, "MinExposure": 0.5, "MaxExposure": 2.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#5b2c6f", "Active": true, @@ -855,8 +992,24 @@ "Rarity": "Rare" }, { + "ExtraDamagePercent": 0.25, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#b8003a", + "Active": true, + "Name": "Nemesis", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": 2, + "Rarity": "Rare" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#52f54c", "Active": true, @@ -868,10 +1021,12 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "DetonationRange": 130.0, "GrenadeLimit": 3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#adf542", "Active": true, @@ -883,11 +1038,13 @@ "Rarity": "Common" }, { + "SoundVolume": 0.5, "HealthToAdd": 50, "HealthShotLimit": 3, "Cooldown": 1.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#10c212", "Active": true, @@ -902,6 +1059,7 @@ "CustomFOV": 50, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#9ba882", "Active": true, @@ -916,6 +1074,7 @@ "Radius": 100.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#f081ec", "Active": true, @@ -932,6 +1091,7 @@ "GrenadeLimit": 3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#81f0c4", "Active": true, @@ -947,6 +1107,7 @@ "FriendlyFire": false, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#6effc7", "Active": true, @@ -958,10 +1119,13 @@ "Rarity": "Uncommon" }, { + "SoundVolume": 0.35, + "HitSoundVolume": 0.35, "MaxDistance": 4096.0, "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#c9f8ff", "Active": true, @@ -975,6 +1139,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a3651a", "Active": true, @@ -986,10 +1151,12 @@ "Rarity": "Common" }, { + "SoundVolume": 0.35, "DamageAfterDeath": 30, "CanKill": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#88bdba", "Active": true, @@ -1005,6 +1172,7 @@ "MaxSpeed": 1200.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff8c42", "Active": true, @@ -1021,6 +1189,7 @@ "GrenadeLimit": 1, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#57bcff", "Active": true, @@ -1035,6 +1204,7 @@ "HealthToAdd": 3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a86eff", "Active": true, @@ -1049,6 +1219,7 @@ "JumpVelocity": 301.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#7ad1c4", "Active": true, @@ -1062,6 +1233,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#b01e5d", "Active": true, @@ -1076,6 +1248,7 @@ "PushVelocity": 400.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#5a4fd1", "Active": true, @@ -1087,10 +1260,12 @@ "Rarity": "Common" }, { + "SoundVolume": 0.5, "MinTime": 10.0, "MaxTime": 25.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8f108f", "Active": true, @@ -1104,6 +1279,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#42f5a7", "Active": true, @@ -1118,6 +1294,7 @@ "ParticleName": "particles/ui/hud/ui_map_def_utility_trail.vpcf", "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#f542ef", "Active": true, @@ -1131,6 +1308,7 @@ { "NeedsTeammates": true, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#c9a227", "Active": true, @@ -1144,6 +1322,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#0000FF", "Active": true, @@ -1157,6 +1336,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#99140B", "Active": true, @@ -1168,6 +1348,7 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Cooldown": 30.0, "DurationRun": 5.0, "DurationCrouch": 12.0, @@ -1175,6 +1356,7 @@ "EnemyTeamDamage": 20, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#42f5ef", "Active": true, @@ -1188,6 +1370,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1466F5", "Active": true, @@ -1199,10 +1382,12 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Cooldown": 30.0, "Duration": 10.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#d0d930", "Active": true, @@ -1214,10 +1399,12 @@ "Rarity": "Common" }, { + "SoundVolume": 0.35, "Cooldown": 1.0, "Damage": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 3, "Color": "#baf081", "Active": true, @@ -1235,6 +1422,7 @@ "GrenadeLimit": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#384728", "Active": true, @@ -1251,6 +1439,7 @@ "GrenadeLimit": 1, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffdd00", "Active": true, @@ -1266,6 +1455,7 @@ "EffectDuration": 1.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ded678", "Active": true, @@ -1277,12 +1467,28 @@ "Rarity": "Common" }, { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#8d6e63", + "Active": true, + "Name": "Heavyweight", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Uncommon" + }, + { + "SoundVolume": 0.5, "SmokeHeal": 1, "SmokeRadius": 180.0, "TickCooldown": 16, "GrenadeLimit": 1, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1fe070", "Active": true, @@ -1302,6 +1508,7 @@ "BoostFactor": 2.5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#b5ab8f", "Active": true, @@ -1315,6 +1522,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#4a6e21", "Active": true, @@ -1326,6 +1534,24 @@ "Rarity": "Common" }, { + "TriggerRadius": 180.0, + "GravityScale": 0.5, + "GrenadeLimit": 2, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#a06cd5", + "Active": true, + "Name": "GravityDecoy", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Common" + }, + { + "SoundVolume": 1.0, "Cooldown": 10.0, "MaxDistance": 1500.0, "MinDistance": 150.0, @@ -1333,8 +1559,11 @@ "PullSpeed": 850.0, "MaxPullSeconds": 3.0, "RopeWidth": 0.8, + "HookEmbed": 8.0, + "HookScale": 0.4, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#38e0c4", "Active": true, @@ -1350,6 +1579,7 @@ "Duration": 2.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#e0d83a", "Active": true, @@ -1363,6 +1593,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#fff52e", "Active": true, @@ -1376,6 +1607,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#f542ef", "Active": true, @@ -1390,6 +1622,7 @@ "GrenadeLimit": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#5d00ff", "Active": true, @@ -1405,6 +1638,7 @@ "MaxScale": 1.4, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8ad3ff", "Active": true, @@ -1418,6 +1652,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FFFFFF", "Active": true, @@ -1432,6 +1667,7 @@ "RefreshPrice": 150, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#7eff47", "Active": true, @@ -1448,6 +1684,7 @@ "GrenadeLimit": 3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#00eaff", "Active": true, @@ -1462,6 +1699,7 @@ "HealthDamageMultiplier": 0.3, "NeedsTeammates": true, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff0000", "Active": true, @@ -1476,6 +1714,7 @@ "MaxBombHealth": 1000, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 3, "Color": "#5d00ff", "Active": true, @@ -1487,11 +1726,13 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "Cooldown": 2.0, "BarricadeHealth": 115, "PropModel": "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl", "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#1b04cc", "Active": true, @@ -1514,6 +1755,7 @@ "BlindAngle": 10.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a3000b", "Active": true, @@ -1529,6 +1771,7 @@ "ChanceTo": 3.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#A31912", "Active": true, @@ -1540,8 +1783,10 @@ "Rarity": "Common" }, { + "MarkerSeconds": 4.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffbf47", "Active": true, @@ -1555,6 +1800,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffc061", "Active": true, @@ -1569,6 +1815,7 @@ "Distance": 1000.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#d1f542", "Active": true, @@ -1587,6 +1834,7 @@ "DmgReductionForTeamates": 0.5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#9c0000", "Active": true, @@ -1605,6 +1853,7 @@ "DmgReductionForTeamates": 0.5, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#c0392b", "Active": true, @@ -1619,6 +1868,7 @@ "MoneyPerShot": 50, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#e0c341", "Active": true, @@ -1634,6 +1884,7 @@ "CooldownBeforeUse": 10.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff8c92", "Active": true, @@ -1644,11 +1895,28 @@ "MaxPerServer": -1, "Rarity": "Common" }, + { + "JamSeconds": 3.0, + "GrenadeLimit": 2, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#3ad6e8", + "Active": true, + "Name": "EmpGrenade", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Uncommon" + }, { "MinScale": 0.6, "MaxScale": 0.95, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffff00", "Active": true, @@ -1662,6 +1930,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ffb73b", "Active": true, @@ -1676,6 +1945,7 @@ "HealthRegainScale": 0.3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FA050D", "Active": true, @@ -1689,6 +1959,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#00f2ff", "Active": true, @@ -1704,6 +1975,7 @@ "ChanceTo": 0.35, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FF4500", "Active": true, @@ -1715,10 +1987,12 @@ "Rarity": "Common" }, { + "SoundVolume": 0.3, "SecondCooldown": 1.0, "Damage": 5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#c91243", "Active": true, @@ -1730,11 +2004,13 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "ExplosionRadius": 500.0, "ExplosionDamage": 999, "DmgReductionForTeamates": 0.5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#F5CB42", "Active": true, @@ -1748,6 +2024,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#dae01f", "Active": true, @@ -1761,6 +2038,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#919191", "Active": true, @@ -1772,12 +2050,14 @@ "Rarity": "Common" }, { + "SoundVolume": 1.0, "JumpVelocity": 150.0, "PushVelocity": 600.0, "AnyDirection": true, "Cooldown": 2.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#42bbfc", "Active": true, @@ -1795,6 +2075,7 @@ "A": 230, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#383838", "Active": true, @@ -1806,9 +2087,11 @@ "Rarity": "Rare" }, { + "SoundVolume": 1.0, "Cooldown": 30.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#34ebd5", "Active": true, @@ -1822,6 +2105,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#88a31a", "Active": true, @@ -1836,6 +2120,7 @@ "BombArmedTime": 10.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 3, "Color": "#343deb", "Active": true, @@ -1849,6 +2134,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FF8B42", "Active": true, @@ -1862,6 +2148,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#5fd98a", "Active": true, @@ -1877,6 +2164,7 @@ "ChanceTo": 0.4, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#FF4500", "Active": true, @@ -1888,9 +2176,11 @@ "Rarity": "Common" }, { + "SoundVolume": 0.35, "DamageAfterMiss": 5, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#db6c35", "Active": true, @@ -1904,6 +2194,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 2, "Color": "#00911f", "Active": true, @@ -1920,6 +2211,7 @@ "JumpBoost": 2.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#d1430a", "Active": true, @@ -1930,6 +2222,21 @@ "MaxPerServer": -1, "Rarity": "Common" }, + { + "Reward": 300, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, + "OnlyTeam": 0, + "Color": "#f2c14e", + "Active": true, + "Name": "Bounty", + "HudDuration": null, + "DescriptionHudDuration": null, + "RequiredPermission": "", + "MaxPerServer": -1, + "Rarity": "Common" + }, { "ExplosionRadius": 400.0, "ExplosionDamage": 60, @@ -1938,6 +2245,7 @@ "Force": 1000.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#7740c9", "Active": true, @@ -1954,6 +2262,7 @@ "VelocityModifier": 0.85, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#cc7504", "Active": true, @@ -1969,6 +2278,7 @@ "MaxDamageVelocity": 2.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#cc2929", "Active": true, @@ -1984,6 +2294,7 @@ "ChanceTo": 0.4, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#00FF00", "Active": true, @@ -2001,6 +2312,7 @@ "GrenadeLimit": 3, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#2effc7", "Active": true, @@ -2014,6 +2326,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#abab33", "Active": true, @@ -2029,6 +2342,7 @@ "ChanceTo": 0.7, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#7E10AD", "Active": true, @@ -2044,6 +2358,7 @@ "ToleranceDeg": 45.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#d9d9d9", "Active": true, @@ -2059,6 +2374,7 @@ "ChanceTo": 0.85, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#d1430a", "Active": true, @@ -2072,6 +2388,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 3, "Color": "#edf5b5", "Active": true, @@ -2085,6 +2402,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#8B4513", "Active": true, @@ -2100,6 +2418,7 @@ "GrenadeLimit": 2, "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#D6E6FF", "Active": true, @@ -2115,6 +2434,7 @@ "Cooldown": 15.0, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#a86eff", "Active": true, @@ -2130,6 +2450,7 @@ "Duration": 0.3, "NeedsTeammates": false, "DisableOnFreezeTime": true, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#fa7b48", "Active": true, @@ -2143,6 +2464,7 @@ { "NeedsTeammates": false, "DisableOnFreezeTime": false, + "DisableOnPistolRound": false, "OnlyTeam": 0, "Color": "#ff0000", "Active": true, diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll index 76bb7a6..fa6d86b 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 bbe6411..d72dadf 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/de.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/de.json @@ -60,6 +60,13 @@ "blastshot": "Explosivschuss", "blastshot_desc": "Drücke Attack2 mit der MP5, um eine HE-Granate abzufeuern", + "bounty": "Kopfgeld", + "bounty_desc": "Setze ein Kopfgeld auf einen Gegner aus; wer ihn tötet, bekommt das Geld", + "bounty_select_info": "Wähle einen Spieler:", + "bounty_player_info": "Du hast {1}$ Kopfgeld auf '{0}' ausgesetzt.", + "bounty_enemy_info": "Auf deinen Kopf wurden {0}$ ausgesetzt.", + "bounty_claimed_info": "Du hast '{0}' getötet und {1}$ kassiert.", + "bunnyhop": "Hase", "bunnyhop_desc": "Du erhältst automatisch bunnyhop", @@ -144,6 +151,10 @@ "dwarf_desc": "Zufällige Charaktergröße zu Beginn der Runde", "dwarf_desc2": "Dein Größenmultiplikator beträgt: {0}x", + "empgrenade": "EMP-Granate", + "empgrenade_desc": "Wer von deiner Granate getroffen wird, verliert kurzzeitig Radar und Fadenkreuz", + "empgrenade_enemy_info": "Eine EMP-Granate hat dein Radar und Fadenkreuz lahmgelegt.", + "enemyspawn": "Feindes-Spawn", "enemyspawn_desc": "Klicke auf [css_useSkill], um zum Feindes-Spawn zu teleportieren", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Keine Oberfläche zum Einhaken.", "grapple_pulling_info": "ZUG", + "gravitydecoy": "Schwerkraft-Ablenker", + "gravitydecoy_desc": "Dein Ablenkungsköder verändert die Schwerkraft aller in der Nähe", + "grenadier": "Grenadier", "grenadier_desc": "Du hast unendlich viele Granaten", @@ -239,6 +253,9 @@ "healingchicken": "Heilendes Huhn", "healingchicken_desc": "Deine Hühner heilen dich, solange du in ihrer Nähe bist", + "heavyweight": "Schwergewicht", + "heavyweight_desc": "Fähigkeiten, die dich stoßen oder verlangsamen, wirken nicht auf dich", + "hermit": "Einsiedler", "hermit_desc": "Durch das Töten von Gegnern werden Munition und ein Teil der Gesundheit wiederhergestellt", @@ -360,6 +377,12 @@ "bankrupt_player_info": "Der Spieler ‚{0}‘ ist bankrott.", "bankrupt_select_info": "Wähle einen Spieler aus, der bankrott gehen soll:", + "nemesis": "Nemesis", + "nemesis_desc": "Der von dir markierte Spieler erleidet zusätzlichen Schaden", + "nemesis_select_info": "Wähle einen Spieler:", + "nemesis_player_info": "Du hast '{0}' markiert.", + "nemesis_enemy_info": "Du wurdest als Nemesis markiert und erleidest jetzt von allen zusätzlichen Schaden.", + "nightmare": "Albtraum", "nightmare_desc": "Lass einen ausgewählten Gegner eine schreckliche Vision erleben", "nightmare_disable_info": "Der Albtraum ist vorbei.", @@ -423,6 +446,10 @@ "prosthesis": "Prothese", "prosthesis_desc": "Arme und Beine sind kugelsicher", + "punisher": "Bestrafer", + "punisher_desc": "Du sammelst einen Teil des erlittenen Schadens und legst ihn auf deinen nächsten Treffer", + "punisher_released_info": "Du hast {0} zusätzlichen Schaden verursacht und ebenso viel Leben erhalten.", + "push": "Pusher", "push_desc": "Du hast eine zufällige Chance, einen Gegner zurückzustoßen, wenn du ihn triffst", "push_desc2": "Deine Chancen, den Gegner zurückzustoßen, betragen: {0} %", @@ -460,6 +487,10 @@ "returntosender": "Zurück zum Absender", "returntosender_desc": "Der erste Treffer eines Gegners schickt ihn zurück zu seinem Spawn", + "rewind": "Rückspulen", + "rewind_desc": "Drücke [css_useSkill], um hier eine Marke zu setzen und kurz darauf dorthin zurückzukehren", + "rewind_pending_info": "RÜCKKEHR: {0}", + "richboy": "Rich Boy", "richboy_desc": "Du erhältst zu Beginn der Runde einen zufälligen Geldbetrag", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Stolperdraht platziert.", "tripwire_triggered_info": "'{0}' hat deinen Stolperdraht ausgelöst.", + "truearmor": "Echte Rüstung", + "truearmor_desc": "Ein Teil des Schadens geht auf deine Rüstung statt auf dein Leben", + + "voodoo": "Voodoo", + "voodoo_desc": "Der markierte Gegner erleidet einen Teil des Schadens, den du erleidest", + "voodoo_select_info": "Wähle einen Spieler:", + "voodoo_player_info": "Du hast '{0}' an deinen Voodoo gebunden.", + "wallhack": "Wallhack", "wallhack_desc": "Du kannst Feinde durch Wände hindurch sehen", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json index b86306c..4175258 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/en.json @@ -60,6 +60,13 @@ "blastshot": "Blast Shot", "blastshot_desc": "Press Attack2 with the MP5 to fire an HE grenade", + "bounty": "Bounty", + "bounty_desc": "Put a price on an enemy's head; whoever kills them takes the money", + "bounty_select_info": "Choose a player:", + "bounty_player_info": "You put a {1}$ bounty on '{0}'.", + "bounty_enemy_info": "A {0}$ bounty was placed on your head.", + "bounty_claimed_info": "You killed '{0}' and claimed {1}$.", + "bunnyhop": "Bunny", "bunnyhop_desc": "You get auto \"BunnyHop\"", @@ -137,6 +144,10 @@ "dwarf_desc": "Random character size at the start of the round", "dwarf_desc2": "Your size multiplier is: {0}x", + "empgrenade": "EMP Grenade", + "empgrenade_desc": "Anyone hurt by your grenade loses their radar and crosshair for a while", + "empgrenade_enemy_info": "An EMP grenade knocked out your radar and crosshair.", + "enemyspawn": "Enemy Spawn", "enemyspawn_desc": "Click [css_useSkill] to teleport to the enemy spawn", @@ -223,6 +234,9 @@ "grapple_no_anchor_info": "No surface to hook onto.", "grapple_pulling_info": "PULLING", + "gravitydecoy": "Gravity Decoy", + "gravitydecoy_desc": "Your decoy changes the gravity of everyone nearby", + "grenadier": "Grenadier", "grenadier_desc": "You have infinite hegrenade", @@ -232,6 +246,9 @@ "healingsmoke": "Healing Smoke", "healingsmoke_desc": "Your smoke grenades heal", + "heavyweight": "Heavyweight", + "heavyweight_desc": "Skills that push or slow you down have no effect on you", + "hermit": "Hermit", "hermit_desc": "Killing restores ammo and a portion of health", @@ -360,6 +377,12 @@ "demoneye": "Demon Eye", "demoneye_desc": "You deal damage to every enemy you are looking at", + "nemesis": "Nemesis", + "nemesis_desc": "The player you mark takes extra damage", + "nemesis_select_info": "Choose a player:", + "nemesis_player_info": "You marked '{0}'.", + "nemesis_enemy_info": "You have been marked as a nemesis, you now take extra damage from everyone.", + "nightmare": "Nightmare", "nightmare_desc": "Force a chosen enemy to experience a terrifying vision", "nightmare_disable_info": "The nightmare has faded.", @@ -423,6 +446,10 @@ "prosthesis": "Prosthesis", "prosthesis_desc": "Arms and legs are bulletproof", + "punisher": "Punisher", + "punisher_desc": "You bank part of the damage you take and add it to your next hit", + "punisher_released_info": "You dealt {0} extra damage and healed for the same amount.", + "push": "Pusher", "push_desc": "You have a random chance to push an enemy back when hitting them", "push_desc2": "Your chances of pushing back the enemy are: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Return to Sender", "returntosender_desc": "The first hit on an enemy sends them back to their spawn", + "rewind": "Rewind", + "rewind_desc": "Click [css_useSkill] to drop a marker where you stand and return to it shortly after", + "rewind_pending_info": "RETURN: {0}", + "richboy": "Rich Boy", "richboy_desc": "You receive a random amount of money at the start of the round", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Tripwire placed.", "tripwire_triggered_info": "'{0}' triggered your tripwire.", + "truearmor": "True Armor", + "truearmor_desc": "Part of the damage you take is paid by your armor instead of your health", + + "voodoo": "Voodoo", + "voodoo_desc": "The enemy you mark also takes part of the damage you take", + "voodoo_select_info": "Choose a player:", + "voodoo_player_info": "You bound '{0}' to your voodoo.", + "wallhack": "Wallhack", "wallhack_desc": "You can see enemies through walls", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json index 722d442..bb99783 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/fr.json @@ -60,6 +60,13 @@ "blastshot": "Tir explosif", "blastshot_desc": "Appuyez sur Attack2 avec le MP5 pour tirer une grenade HE", + "bounty": "Prime", + "bounty_desc": "Mets une prime sur la tête d'un ennemi ; celui qui le tue empoche l'argent", + "bounty_select_info": "Choisis un joueur :", + "bounty_player_info": "Tu as mis une prime de {1}$ sur '{0}'.", + "bounty_enemy_info": "Une prime de {0}$ a été mise sur ta tête.", + "bounty_claimed_info": "Tu as tué '{0}' et empoché {1}$.", + "bunnyhop": "Lapin", "bunnyhop_desc": "Vous obtenez l’auto « BunnyHop »", @@ -144,6 +151,10 @@ "dwarf_desc": "Taille du personnage aléatoire au début du round", "dwarf_desc2": "Votre multiplicateur de taille est : {0}x", + "empgrenade": "Grenade EMP", + "empgrenade_desc": "Ceux que ta grenade blesse perdent leur radar et leur viseur un moment", + "empgrenade_enemy_info": "Une grenade EMP a coupé ton radar et ton viseur.", + "enemyspawn": "Spawn Ennemi", "enemyspawn_desc": "Appuyez sur [css_useSkill] pour vous téléporter au spawn ennemi", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Aucune surface pour accrocher le grappin.", "grapple_pulling_info": "TRACTION", + "gravitydecoy": "Leurre gravitationnel", + "gravitydecoy_desc": "Ton leurre modifie la gravité de tous ceux qui sont à proximité", + "grenadier": "Grenadier", "grenadier_desc": "Tu as des grenades HE infinies", @@ -239,6 +253,9 @@ "healingchicken": "Poulet guérisseur", "healingchicken_desc": "Vos poulets vous soignent lorsque vous êtes à proximité", + "heavyweight": "Poids lourd", + "heavyweight_desc": "Les compétences qui te poussent ou te ralentissent n'ont aucun effet sur toi", + "hermit": "Ermite", "hermit_desc": "Tuer restaure des munitions et une partie de vos PV", @@ -360,6 +377,12 @@ "bankrupt_player_info": "Le joueur « {0} » est en faillite.", "bankrupt_select_info": "Mettre un joueur en faillite :", + "nemesis": "Némésis", + "nemesis_desc": "Le joueur que tu marques subit des dégâts supplémentaires", + "nemesis_select_info": "Choisis un joueur :", + "nemesis_player_info": "Tu as marqué '{0}'.", + "nemesis_enemy_info": "Vous avez été marqué comme némésis, vous subissez désormais des dégâts supplémentaires de la part de tous.", + "nightmare": "Cauchemar", "nightmare_desc": "Faites subir une vision terrifiante à un ennemi choisi", "nightmare_disable_info": "Le cauchemar s'est dissipé.", @@ -423,6 +446,10 @@ "prosthesis": "Prothèses", "prosthesis_desc": "Les bras et les jambes sont pare-balles", + "punisher": "Punisseur", + "punisher_desc": "Tu accumules une partie des dégâts subis et l'ajoutes à ton prochain tir", + "punisher_released_info": "Tu as infligé {0} dégâts en plus et récupéré autant de vie.", + "push": "Pousseur", "push_desc": "Vous avez une chance aléatoire de repousser un ennemi en le touchant", "push_desc2": "Votre chance de repousser l’ennemi est : {0}%", @@ -460,6 +487,10 @@ "returntosender": "Retour à l’Expéditeur", "returntosender_desc": "Le premier coup porté à un ennemi le renvoie à son spawn", + "rewind": "Rembobinage", + "rewind_desc": "Appuie sur [css_useSkill] pour poser un repère ici et y revenir peu après", + "rewind_pending_info": "RETOUR : {0}", + "richboy": "Gosse de Riche", "richboy_desc": "Vous recevez une somme d’argent aléatoire au début du round", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Fil-piège placé.", "tripwire_triggered_info": "'{0}' a déclenché votre fil-piège.", + "truearmor": "Vraie armure", + "truearmor_desc": "Une partie des dégâts est absorbée par ton armure au lieu de ta vie", + + "voodoo": "Vaudou", + "voodoo_desc": "L'ennemi que tu marques subit aussi une partie des dégâts que tu reçois", + "voodoo_select_info": "Choisis un joueur :", + "voodoo_player_info": "Tu as lié '{0}' à ton vaudou.", + "wallhack": "Wallhack", "wallhack_desc": "Vous pouvez voir les ennemis à travers les murs", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json index fbce5ea..2659a45 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json @@ -60,6 +60,13 @@ "blastshot": "Mocne Uderzenie", "blastshot_desc": "Naciśnij Attack2 na MP5, aby wystrzelić granat HE", + "bounty": "Nagroda", + "bounty_desc": "Wyznacz nagrodę za głowę wroga; kto go zabije, dostaje pieniądze", + "bounty_select_info": "Wybierz gracza:", + "bounty_player_info": "Wyznaczyłeś nagrodę {1}$ za '{0}'.", + "bounty_enemy_info": "Za twoją głowę wyznaczono {0}$.", + "bounty_claimed_info": "Zabiłeś '{0}' i zgarnąłeś {1}$.", + "bunnyhop": "Królik", "bunnyhop_desc": "Otrzymujesz auto \"BunnyHopa\"", @@ -144,6 +151,10 @@ "dwarf_desc": "Losowa wielkość postaci na początku rundy", "dwarf_desc2": "Twój mnożnik wielkości to {0}x", + "empgrenade": "Granat EMP", + "empgrenade_desc": "Kto oberwie twoim granatem, na chwilę traci radar i celownik", + "empgrenade_enemy_info": "Granat EMP wyłączył twój radar i celownik.", + "enemyspawn": "Resp Wroga", "enemyspawn_desc": "Kliknij [css_useSkill], aby teleportować się na resp wroga", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Brak powierzchni do zaczepienia haka.", "grapple_pulling_info": "PRZYCIĄGANIE", + "gravitydecoy": "Grawitacyjna wabik", + "gravitydecoy_desc": "Twój wabik zmienia grawitację wszystkich w pobliżu", + "grenadier": "Granatnik", "grenadier_desc": "Masz nieskończone granaty odłamkowe", @@ -239,6 +253,9 @@ "healingchicken": "Lecząca Kura", "healingchicken_desc": "Twoje kury leczą Cię, gdy jesteś w pobliżu", + "heavyweight": "Waga ciężka", + "heavyweight_desc": "Umiejętności, które cię popychają lub spowalniają, nie działają na ciebie", + "hermit": "Pustelnik", "hermit_desc": "Zabijanie przywraca amunicję i część zdrowia", @@ -360,6 +377,12 @@ "moneyswap_player_info": "Wymieniłeś swoje pieniądze z graczem '{0}'.", "moneyswap_select_info": "Zamień się kasą z graczem:", + "nemesis": "Nemezis", + "nemesis_desc": "Oznaczony gracz otrzymuje dodatkowe obrażenia", + "nemesis_select_info": "Wybierz gracza:", + "nemesis_player_info": "Oznaczyłeś '{0}'.", + "nemesis_enemy_info": "Zostałeś oznaczony jako nemezis, teraz otrzymujesz dodatkowe obrażenia od wszystkich.", + "nightmare": "Koszmar", "nightmare_desc": "Wywołaj u wybranego przeciwnika przerażającą wizję", "nightmare_disable_info": "Koszmar minął.", @@ -423,6 +446,10 @@ "prosthesis": "Proteza", "prosthesis_desc": "Ramiona i nogi są kuloodporne", + "punisher": "Karzyciel", + "punisher_desc": "Gromadzisz część otrzymanych obrażeń i dodajesz je do następnego trafienia", + "punisher_released_info": "Zadałeś {0} dodatkowych obrażeń i odzyskałeś tyle samo życia.", + "push": "Odpychacz", "push_desc": "Masz losową szansę na odepchnięcie wroga po trafieniu go", "push_desc2": "Twoje szanse na odepchnięcie wroga to: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Zwrot do Nadawcy", "returntosender_desc": "Pierwsze trafienie wroga powoduje, że wraca on na swój resp", + "rewind": "Przewijanie", + "rewind_desc": "Naciśnij [css_useSkill], aby zostawić znacznik i wkrótce do niego wrócić", + "rewind_pending_info": "POWRÓT: {0}", + "richboy": "Bogacz", "richboy_desc": "Na początku rundy otrzymujesz losową kwotę gotówki", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Linka została rozstawiona.", "tripwire_triggered_info": "'{0}' uruchomił Twoją linkę.", + "truearmor": "Prawdziwy pancerz", + "truearmor_desc": "Część obrażeń pokrywa twój pancerz zamiast zdrowia", + + "voodoo": "Voodoo", + "voodoo_desc": "Oznaczony wróg również otrzymuje część obrażeń, które ty dostajesz", + "voodoo_select_info": "Wybierz gracza:", + "voodoo_player_info": "Związałeś '{0}' swoim voodoo.", + "wallhack": "Wallhack", "wallhack_desc": "Widzisz wrogów przez ściany", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json index 14fc109..c477c18 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pt-br.json @@ -60,6 +60,13 @@ "blastshot": "Tiro Explosivo", "blastshot_desc": "Pressione Attack2 com a MP5 para disparar uma granada HE", + "bounty": "Recompensa", + "bounty_desc": "Coloque um preço na cabeça de um inimigo; quem o matar leva o dinheiro", + "bounty_select_info": "Escolha um jogador:", + "bounty_player_info": "Você colocou {1}$ de recompensa em '{0}'.", + "bounty_enemy_info": "Puseram {0}$ de recompensa na sua cabeça.", + "bounty_claimed_info": "Você matou '{0}' e levou {1}$.", + "bunnyhop": "Coelho", "bunnyhop_desc": "Você ganha \"BunnyHop\" automático", @@ -144,6 +151,10 @@ "dwarf_desc": "Sua altura agora é aleatória", "dwarf_desc2": "Seu multiplicador de altura é: {0}x", + "empgrenade": "Granada EMP", + "empgrenade_desc": "Quem se ferir com sua granada perde o radar e a mira por um tempo", + "empgrenade_enemy_info": "Uma granada EMP derrubou seu radar e sua mira.", + "enemyspawn": "Spawn Inimigo", "enemyspawn_desc": "Clique em [css_useSkill] para se teletransportar para a base inimiga", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Nenhuma superfície para prender o gancho.", "grapple_pulling_info": "PUXANDO", + "gravitydecoy": "Chamariz Gravitacional", + "gravitydecoy_desc": "Seu chamariz muda a gravidade de todos que estão por perto", + "grenadier": "Bomberman", "grenadier_desc": "Você tem HEs infinitas", @@ -239,6 +253,9 @@ "healingchicken": "Galinha Curadora", "healingchicken_desc": "Suas galinhas curam você enquanto você estiver por perto", + "heavyweight": "Peso Pesado", + "heavyweight_desc": "Habilidades que empurram ou te deixam lento não fazem efeito em você", + "hermit": "Sanguessuga", "hermit_desc": "Matar restaura munição e uma parte da sua vida", @@ -360,6 +377,12 @@ "moneyswap_player_info": "Você trocou dinheiro com o jogador '{0}'.", "moneyswap_select_info": "Trocar dinheiro c/ jogador:", + "nemesis": "Nêmesis", + "nemesis_desc": "O jogador que você marcar recebe dano extra", + "nemesis_select_info": "Escolha um jogador:", + "nemesis_player_info": "Você marcou '{0}'.", + "nemesis_enemy_info": "Você foi marcado como nêmesis, agora recebe dano extra de todos.", + "nightmare": "Pesadelo", "nightmare_desc": "Faça um inimigo escolhido ter uma visão aterrorizante", "nightmare_disable_info": "O pesadelo desapareceu.", @@ -423,6 +446,10 @@ "prosthesis": "Prótese", "prosthesis_desc": "Braços e pernas são à prova de balas", + "punisher": "Punidor", + "punisher_desc": "Você guarda parte do dano recebido e soma no seu próximo tiro", + "punisher_released_info": "Você causou {0} de dano extra e recuperou a mesma vida.", + "push": "Empurrão", "push_desc": "Você tem uma chance aleatória de empurrar um inimigo ao acertá-lo", "push_desc2": "Suas chances de empurrar o inimigo são: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Devolução ao Remetente", "returntosender_desc": "O primeiro tiro em um inimigo o envia de volta a sua base", + "rewind": "Retroceder", + "rewind_desc": "Clique em [css_useSkill] para deixar uma marca aqui e voltar a ela logo depois", + "rewind_pending_info": "RETORNO: {0}", + "richboy": "Filhinho de Papai", "richboy_desc": "Você recebe uma quantidade aleatória de dinheiro no início da rodada", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Fio de armadilha colocado.", "tripwire_triggered_info": "'{0}' acionou seu fio.", + "truearmor": "Armadura Real", + "truearmor_desc": "Parte do dano que você recebe sai do colete em vez da vida", + + "voodoo": "Vodu", + "voodoo_desc": "O inimigo que você marcar também recebe parte do dano que você leva", + "voodoo_select_info": "Escolha um jogador:", + "voodoo_player_info": "Você ligou '{0}' ao seu vodu.", + "wallhack": "Wallhack", "wallhack_desc": "Você pode ver inimigos através das paredes", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json index 7e5148e..19f2012 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/ru.json @@ -60,6 +60,13 @@ "blastshot": "Взрывной выстрел", "blastshot_desc": "Нажмите Attack2 на MP5, чтобы выстрелить гранатой HE", + "bounty": "Награда", + "bounty_desc": "Назначь награду за голову врага; тот, кто убьёт его, получит деньги", + "bounty_select_info": "Выберите игрока:", + "bounty_player_info": "Вы назначили награду {1}$ за '{0}'.", + "bounty_enemy_info": "За вашу голову назначили {0}$.", + "bounty_claimed_info": "Вы убили '{0}' и получили {1}$.", + "bunnyhop": "Заяц", "bunnyhop_desc": "Вы получаете автоматический «БанниХоп»", @@ -144,6 +151,10 @@ "dwarf_desc": "Случайный размер персонажа в начале раунда", "dwarf_desc2": "Ваш множитель размера: {0}x", + "empgrenade": "ЭМИ-граната", + "empgrenade_desc": "Тот, кто пострадал от вашей гранаты, на время теряет радар и прицел", + "empgrenade_enemy_info": "ЭМИ-граната вывела из строя ваш радар и прицел.", + "enemyspawn": "Спавн врагов", "enemyspawn_desc": "Нажмите [css_useSkill], чтобы телепортироваться на спавн врагов", @@ -230,6 +241,9 @@ "grapple_no_anchor_info": "Нет поверхности для зацепа.", "grapple_pulling_info": "ПРИТЯГИВАНИЕ", + "gravitydecoy": "Гравитационная обманка", + "gravitydecoy_desc": "Ваша обманка меняет гравитацию всех, кто рядом", + "grenadier": "Гренадёр", "grenadier_desc": "У вас бесконечные осколочные гранаты", @@ -239,6 +253,9 @@ "healingsmoke": "Лечащий дым", "healingsmoke_desc": "Ваши дымовые гранаты лечат", + "heavyweight": "Тяжеловес", + "heavyweight_desc": "Способности, которые толкают или замедляют, на вас не действуют", + "hermit": "Отшельник", "hermit_desc": "Убийства восстанавливают боеприпасы и часть здоровья", @@ -360,6 +377,12 @@ "moneyswap_player_info": "Вы обменялись деньгами с игроком '{0}'.", "moneyswap_select_info": "Обменять деньги с игроком:", + "nemesis": "Немезида", + "nemesis_desc": "Отмеченный вами игрок получает больше урона", + "nemesis_select_info": "Выберите игрока:", + "nemesis_player_info": "Вы отметили '{0}'.", + "nemesis_enemy_info": "Вы отмечены как немезида и теперь получаете больше урона от всех.", + "nightmare": "Кошмар", "nightmare_desc": "Заставьте выбранного противника увидеть кошмарное видение", "nightmare_disable_info": "Кошмар рассеялся.", @@ -423,6 +446,10 @@ "prosthesis": "Протез", "prosthesis_desc": "Руки и ноги защищены от пуль", + "punisher": "Каратель", + "punisher_desc": "Вы копите часть полученного урона и добавляете его к следующему попаданию", + "punisher_released_info": "Вы нанесли {0} дополнительного урона и восстановили столько же здоровья.", + "push": "Толкач", "push_desc": "Есть шанс оттолкнуть врага назад при попадании", "push_desc2": "Ваш шанс оттолкнуть врага: {0}%", @@ -460,6 +487,10 @@ "returntosender": "Вернуть отправителю", "returntosender_desc": "Первое попадание по врагу отправляет его обратно на спавн", + "rewind": "Перемотка", + "rewind_desc": "Нажмите [css_useSkill], чтобы оставить метку здесь и вскоре вернуться к ней", + "rewind_pending_info": "ВОЗВРАТ: {0}", + "richboy": "Богач", "richboy_desc": "Вы получаете случайную сумму денег в начале раунда", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Растяжка установлена.", "tripwire_triggered_info": "'{0}' задел вашу растяжку.", + "truearmor": "Настоящая броня", + "truearmor_desc": "Часть урона принимает броня, а не здоровье", + + "voodoo": "Вуду", + "voodoo_desc": "Отмеченный враг тоже получает часть урона, который получаете вы", + "voodoo_select_info": "Выберите игрока:", + "voodoo_player_info": "Вы привязали '{0}' к своему вуду.", + "wallhack": "Воллхак", "wallhack_desc": "Вы видите врагов сквозь стены", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json index 0a6a3ce..617a3dd 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/tr.json @@ -60,6 +60,13 @@ "blastshot": "Patlama Atışı", "blastshot_desc": "MP5 ile sağ tıklayarak el bombası fırlat", + "bounty": "Ödül Avı", + "bounty_desc": "Bir düşmana ödül koy, onu öldüren parayı alır", + "bounty_select_info": "Birini seçin:", + "bounty_player_info": "'{0}' için {1}$ ödül koydun.", + "bounty_enemy_info": "Başına {0}$ ödül kondu.", + "bounty_claimed_info": "'{0}' oyuncusunu öldürdün ve {1}$ kazandın.", + "bunnyhop": "Tavşan", "bunnyhop_desc": "Otomatik BunnyHop yaparsın", @@ -137,6 +144,10 @@ "dwarf_desc": "Rastgele karakter boyutu", "dwarf_desc2": "Boyutun: {0}x", + "empgrenade": "EMP Bombası", + "empgrenade_desc": "El bomban, hasar verdiğin düşmanın radarını ve nişangahını kapatır", + "empgrenade_enemy_info": "Radarın ve nişangahın kapandı.", + "enemyspawn": "Base Baskını", "enemyspawn_desc": "[css_useSkill] tuşuna basarak düşman doğma noktasına ışınlan", @@ -223,6 +234,9 @@ "grapple_no_anchor_info": "Kanca atılacak yüzey yok", "grapple_pulling_info": "ÇEKİLİYOR", + "gravitydecoy": "Yerçekimi Yanıltıcısı", + "gravitydecoy_desc": "Yanıltıcı bomban yakındaki herkesin yerçekimini değiştirir", + "grenadier": "Bombacı", "grenadier_desc": "El bomban sınırsız", @@ -232,6 +246,9 @@ "healingsmoke": "Şifalı Sis", "healingsmoke_desc": "Sis bomban iyileştirir", + "heavyweight": "Ağır Sıklet", + "heavyweight_desc": "Seni iten ve yavaşlatan yetenekler işlemez", + "hermit": "Münzevi", "hermit_desc": "Birini öldürünce mermin ve canın yenilenir", @@ -360,6 +377,12 @@ "demoneye": "Şeytani Göz", "demoneye_desc": "Baktığın tüm düşmana hasar verirsin", + "nemesis": "Ezeli Rakip", + "nemesis_desc": "İşaretlediğin düşman herkesten daha fazla hasar alır", + "nemesis_select_info": "Birini seçin:", + "nemesis_player_info": "'{0}' artık herkesten daha fazla hasar alacak.", + "nemesis_enemy_info": "İşaretlendin, artık herkesten daha fazla hasar alıyorsun.", + "nightmare": "Kabus", "nightmare_desc": "Seçtiğin rakibe kabus gördür", "nightmare_disable_info": "Kabus sona erdi.", @@ -423,6 +446,10 @@ "prosthesis": "Protez", "prosthesis_desc": "Kolların ve bacakların kurşun geçirmez", + "punisher": "İntikam", + "punisher_desc": "Aldığın hasarı biriktirir, sonraki vuruşunda geri verirsin", + "punisher_released_info": "{0} ekstra hasar verdin ve o kadar can kazandın.", + "push": "İtici", "push_desc": "Düşmana vurduğunda onu geri itme şansın olur", "push_desc2": "Geri itme şansın: %{0}", @@ -460,6 +487,10 @@ "returntosender": "İade", "returntosender_desc": "Düşmana ilk vuruşun onu kendi başlangıcına ışınlar", + "rewind": "Geri Sarma", + "rewind_desc": "[css_useSkill] tuşuna basarak iz bırak, birazdan oraya geri dön", + "rewind_pending_info": "DÖNÜŞ: {0}", + "richboy": "Zengin Çocuk", "richboy_desc": "Rastgele miktarda paran olur", @@ -549,6 +580,14 @@ "tripwire_placed_info": "Tel gerildi.", "tripwire_triggered_info": "'{0}' teline takıldı.", + "truearmor": "Gerçek Zırh", + "truearmor_desc": "Aldığın hasarın bir kısmını canın yerine zırhın karşılar", + + "voodoo": "Vudu", + "voodoo_desc": "Aldığın hasarın bir kısmını işaretlediğin düşman da alır", + "voodoo_select_info": "Birini seçin:", + "voodoo_player_info": "'{0}' oyuncusunu kendine bağladın.", + "wallhack": "Wallhack", "wallhack_desc": "Düşmanları duvar arkasından görürsün", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json index ecccbf0..8726bc7 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/zh.json @@ -54,6 +54,13 @@ "blademaster": "我的刀能档子弹!", "blademaster_desc": "持刀时,你有很高几率格挡子弹", + "bounty": "悬赏", + "bounty_desc": "对一名敌人悬赏,击杀他的人可以获得赏金", + "bounty_select_info": "选择一名玩家:", + "bounty_player_info": "你对 '{0}' 悬赏了 {1}$。", + "bounty_enemy_info": "有人对你悬赏了 {0}$。", + "bounty_claimed_info": "你击杀了 '{0}',获得 {1}$。", + "bunnyhop": "连跳大佬", "bunnyhop_desc": "你获得自动连跳的能力", @@ -131,6 +138,10 @@ "dwarf_desc": "回合开始时获得随机角色大小", "dwarf_desc2": "你的体型倍率是:{0}x", + "empgrenade": "电磁脉冲手雷", + "empgrenade_desc": "被你的手雷伤到的人会暂时失去雷达和准星", + "empgrenade_enemy_info": "电磁脉冲手雷让你的雷达和准星失效了。", + "enemyspawn": "传送敌人出生点", "enemyspawn_desc": "点击 [css_useSkill] 传送到敌人出生点", @@ -211,12 +222,18 @@ "grapple_no_anchor_info": "没有可以钩住的表面。", "grapple_pulling_info": "拉动中", + "gravitydecoy": "重力诱饵", + "gravitydecoy_desc": "你的诱饵会改变附近所有人的重力", + "grenadier": "投弹手", "grenadier_desc": "你拥有无限高爆手雷", "healingsmoke": "治疗烟雾", "healingsmoke_desc": "你的烟雾弹会治疗", + "heavyweight": "重量级", + "heavyweight_desc": "推开你或让你减速的技能对你无效", + "hermit": "隐士", "hermit_desc": "击杀敌人会恢复弹药和部分生命值", @@ -330,6 +347,12 @@ "deathbomb_desc": "你死亡时会爆炸,杀死附近的玩家", "deathbomb_explosion": "砰!", + "nemesis": "宿敌", + "nemesis_desc": "被你标记的玩家会受到额外伤害", + "nemesis_select_info": "选择一名玩家:", + "nemesis_player_info": "你标记了 '{0}'。", + "nemesis_enemy_info": "你被标记为宿敌,现在会受到所有人的额外伤害。", + "nightmare": "nightmare", "nightmare_desc": "nightmare_desc", "nightmare_disable_info": "nightmare_disable_info", @@ -393,6 +416,10 @@ "prosthesis": "假肢", "prosthesis_desc": "手臂和腿部防弹", + "punisher": "惩罚者", + "punisher_desc": "你会积攒受到伤害的一部分,并加到下一次命中上", + "punisher_released_info": "你造成了 {0} 点额外伤害,并回复了同样的生命值。", + "push": "推力者", "push_desc": "你有随机几率在击中敌人时将其推回", "push_desc2": "你击退敌人的几率为:{0}%", @@ -430,6 +457,10 @@ "returntosender": "发送他们房子!!!", "returntosender_desc": "首次击中敌人会将其送回出生点", + "rewind": "回溯", + "rewind_desc": "按 [css_useSkill] 在原地留下标记,稍后回到那里", + "rewind_pending_info": "回溯:{0}", + "richboy": "富少", "richboy_desc": "回合开始时你获得随机数量的钱", @@ -507,6 +538,14 @@ "tripwire_placed_info": "tripwire_placed_info", "tripwire_triggered_info": "tripwire_triggered_info: {0}", + "truearmor": "真实护甲", + "truearmor_desc": "你受到的一部分伤害由护甲承担,而不是生命值", + + "voodoo": "巫毒", + "voodoo_desc": "被你标记的敌人也会受到你所受伤害的一部分", + "voodoo_select_info": "选择一名玩家:", + "voodoo_player_info": "你把 '{0}' 绑到了你的巫毒上。", + "wallhack": "透视", "wallhack_desc": "你可以通过墙壁看到敌人",