1.2.3.b2
This commit is contained in:
parent
0e86f54e0a
commit
98f6e65b01
98 changed files with 3391 additions and 397 deletions
38
.github/en.md
vendored
38
.github/en.md
vendored
|
|
@ -48,7 +48,7 @@ Join the 3v3 test server and try out the jRandomSkills plugin:
|
|||
Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.html).
|
||||
|
||||
|
||||
## ✨ Current Skills (124)
|
||||
## ✨ Current Skills (131)
|
||||
<details>
|
||||
<summary>The table below lists all available skills in the game, along with their descriptions.</summary>
|
||||
|
||||
|
|
@ -86,6 +86,8 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Duplicator | Choose a player to copy their skill | - |
|
||||
| Dwarf | Random character size at the start of the round | (60 - 95)% |
|
||||
| Enemy Spawn | Click [css_useSkill] to teleport to the enemy spawn | 15 s |
|
||||
| Expensive Ammo | A chosen enemy loses money with every shot | - |
|
||||
| Exploding Barrel | Place a barrel that explodes when shot | - |
|
||||
| Explosive Shot | Random chance to fire an explosive bullet while shooting | (15 - 30)% |
|
||||
| Falcon Eye | Click [css_useSkill] to activate a bird's-eye view camera| - |
|
||||
| Fastreload | Click [css_useSkill] to reload the weapon you are currently holding | - |
|
||||
|
|
@ -96,6 +98,7 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Freezing Decoy | Your decoy freezes all nearby players | - |
|
||||
| Gambler | Select a skill from the list provided | - |
|
||||
| Ghost | You are completely invisible | - |
|
||||
| Giant | Enlarges a chosen enemy | (110 - 140)% |
|
||||
| Glaz | You can see through smoke grenades | - |
|
||||
| Glitch | Disables the radar for a chosen enemy | - |
|
||||
| Glue | Your grenades stick to walls | - |
|
||||
|
|
@ -115,8 +118,10 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Jammer | Choose a player to disable their crosshair | - |
|
||||
| Jester | In jester mode, you cannot get or take any damage. This mode changes every few seconds | (10 - 25) s |
|
||||
| Legless | Choose a player who cannot jump | - |
|
||||
| Jump Curse | A chosen enemy jumps whenever their teammates jump | - |
|
||||
| Jumping Jack | Jumping restores health | - |
|
||||
| Killer Flash | Anyone fully blinded by your flashbang dies (including you) | - |
|
||||
| Knockback | Firing while airborne pushes you backwards | - |
|
||||
| Life Swap | Choose a player to swap health with | - |
|
||||
| Long Knife | A primary knife attack deals damage regardless of distance | - |
|
||||
| Long Zeus | Zeus deals damage regardless of distance | - |
|
||||
|
|
@ -126,6 +131,7 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Medic | Click [css_useSkill] to use a healing charge that restores 50 health | 1 s |
|
||||
| Bomb Miner | Your HE grenade only explode when there is an enemy nearby | - |
|
||||
| Taxman | Choose a player to swap money with | - |
|
||||
| Nightmare | Gives a chosen enemy a nightmarish vision | - |
|
||||
| Ninja | Standing still increases your invisibility by 33%, crouching by 33%, and holding a knife by 33% | - |
|
||||
| No-Nades | Grenades deal no damage to you | - |
|
||||
| Focus | No recoil while shooting | - |
|
||||
|
|
@ -172,6 +178,7 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Thorns | Your opponent will receive a portion of the damage that they inflicted on you | - |
|
||||
| Throwing Knife | Click [css_useSkill] to throw a knife. But watch out for others | - |
|
||||
| Toxic Smoke | Your smoke grenades deal damage | - |
|
||||
| Tripwire | String a wire between two walls; enemies touching it show on your radar | - |
|
||||
| Wallhack | You can see enemies through walls | - |
|
||||
| Watchmaker | Every grenade throw alters the round time | - |
|
||||
| Weapon Swap | Click [css_useSkill] to swap weapons with a random enemy | 30 s |
|
||||
|
|
@ -366,6 +373,33 @@ This plugin uses content from the following projects:
|
|||
|
||||
## 📋 Changelog
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.3.b1</b></summary>
|
||||
|
||||
- #### General (Performance and Stability)
|
||||
- ###### `CServerSideClient.ForceFullUpdate` now bails on a null client handle.
|
||||
- ###### Added a per-tick player/bomb cache.
|
||||
- ###### Migrated 82 skills from per-skill `Utilities.GetPlayers()` to the one shared per-tick scan, and the HUD loop reuses it. Much lower per-tick cost at high player counts (verified 10v10).
|
||||
- ###### Print a single notice when the RayTrace is not installed.
|
||||
|
||||
- #### Skill improvements:
|
||||
- ###### Second Chance / Phoenix / Re-Zombie:
|
||||
- ###### Intercept the lethal hit before it is applied. Previously they reacted after the death was already committed, so one-shots, fall damage and skill-based kills were missed.
|
||||
- ###### Killer Flash:
|
||||
- ###### Kills through real damage instead of forcing a suicide, so revive skills can intercept.
|
||||
- ###### Baseball:
|
||||
- ###### Fixed a rare server crash from decoy grenades (decoy's entity index could be reused by another entity).
|
||||
- ###### Aimbot:
|
||||
- ###### Guard the hit-group native pointer before writing (avoids writing to freed/null memory).
|
||||
- ###### Wallhack:
|
||||
- ###### Detach the glow when the target dies or disconnects.
|
||||
- ###### Spectator / Cypher / FalconEye / ThirdEye:
|
||||
- ###### null-guard the camera SceneNode walk and re-validate the target before teleport/parent.
|
||||
|
||||
**Full update contributed by [@ByDexterTR](https://github.com/ByDexterTR) in pull request [#44](https://github.com/Juzlus/jRandomSkills/pull/44). Thanks to ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.2.b9</b></summary>
|
||||
|
||||
|
|
@ -399,7 +433,7 @@ This plugin uses content from the following projects:
|
|||
- ###### Ghost / Ninja / C4 Camouflage:
|
||||
- ###### Improved hidden-player processing for better performance.
|
||||
|
||||
**Full update contributed by @ByDexterTR in pull request #41. Thanks to ByDexterTR!**
|
||||
**Full update contributed by [@ByDexterTR](https://github.com/ByDexterTR) in pull request [#41](https://github.com/Juzlus/jRandomSkills/pull/41). Thanks to ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
|
|
|
|||
29
.github/pl.md
vendored
29
.github/pl.md
vendored
|
|
@ -364,6 +364,33 @@ Plugin korzysta z zawartości następujących projektów:
|
|||
|
||||
## 📋 Lista Zmian
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.3.b1</b></summary>
|
||||
|
||||
- #### Ogólne (Wydajność i stabilność)
|
||||
- ###### `CServerSideClient.ForceFullUpdate` przerywa teraz działanie, jeśli klient handle jest pusty.
|
||||
- ###### Dodano współdzieloną pamięć podręczną graczy i bomby odświeżaną co tick.
|
||||
- ###### 82 moce zostały przeniesione z własnych wywołań `Utilities.GetPlayers()` na jedno współdzielone skanowanie wykonywane raz na tick. Pętla HUD również z niego korzysta, co znacznie zmniejsza obciążenie serwera przy większej liczbie graczy (potwierdzone w 10v10).
|
||||
- ###### Wyświetlanie informacji o braku zainstalowanego RayTrace.
|
||||
|
||||
- #### Poprawki mocy:
|
||||
- ###### Druga Szansa / Feniks / Re-Zombie:
|
||||
- ###### Przechwytują śmiertelne obrażenia przed ich zastosowaniem. Wcześniej reagowały dopiero po śmierci gracza, przez co nie działały przy natychmiastowych zabójstwach, obrażeniach od upadku oraz zabójstwach zadawanych przez moce.
|
||||
- ###### Zabójczy Flash:
|
||||
- ###### Zabija teraz poprzez zadanie rzeczywistych obrażeń zamiast wymuszania samobójstwa, dzięki czemu moce wskrzeszające mogą zadziałać.
|
||||
- ###### Bejsbolista:
|
||||
- ###### Naprawiono rzadki crash serwera związany z granatem wabikiem (indeks encji wabika mógł zostać ponownie wykorzystany przez inną encję).
|
||||
- ###### Aimbot:
|
||||
- ###### Dodano sprawdzanie poprawności wskaźnika hit-group przed zapisem, co zapobiega zapisywaniu do zwolnionej lub pustej pamięci.
|
||||
- ###### Wallhack:
|
||||
- ###### Efekt poświaty jest teraz usuwany, gdy cel zginie lub opuści serwer.
|
||||
- ###### Obserwator / Kamera / Oko Sokoła / Trzecie Oko:
|
||||
- ###### Dodano zabezpieczenia przed pustymi wskaźnikami kamery oraz ponowną weryfikację celu przed teleportacją lub przypisaniem kamery.
|
||||
|
||||
**Pełna aktualizacja została przygotowana przez [@ByDexterTR](https://github.com/ByDexterTR) w ramach pull requesta [#44](https://github.com/Juzlus/jRandomSkills/pull/44). Dziękujemy ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.2.b8</b></summary>
|
||||
|
||||
|
|
@ -397,7 +424,7 @@ Plugin korzysta z zawartości następujących projektów:
|
|||
- ###### Duszek / Ninja / C4 Kamuflaż:
|
||||
- ###### Zoptymalizowano przetwarzanie ukrytych graczy, poprawiając wydajność.
|
||||
|
||||
**Pełna aktualizacja została przygotowana przez @ByDexterTR w ramach pull requesta #41. Dziękujemy ByDexterTR!**
|
||||
**Pełna aktualizacja została przygotowana przez [@ByDexterTR](https://github.com/ByDexterTR) w ramach pull requesta [#44](https://github.com/Juzlus/jRandomSkills/pull/44). Dziękujemy ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -31,6 +31,8 @@ project.fragment.lock.json
|
|||
|
||||
# Logs, caches, temp
|
||||
*.log
|
||||
debug_*.txt
|
||||
perf_*.txt
|
||||
*.cache
|
||||
*.tmp
|
||||
*.temp
|
||||
|
|
|
|||
29
README-PL.md
29
README-PL.md
|
|
@ -381,6 +381,33 @@ Plugin korzysta z zawartości następujących projektów:
|
|||
|
||||
## 📋 Lista Zmian
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.3.b1</b></summary>
|
||||
|
||||
- #### Ogólne (Wydajność i stabilność)
|
||||
- ###### `CServerSideClient.ForceFullUpdate` przerywa teraz działanie, jeśli klient handle jest pusty.
|
||||
- ###### Dodano współdzieloną pamięć podręczną graczy i bomby odświeżaną co tick.
|
||||
- ###### 82 moce zostały przeniesione z własnych wywołań `Utilities.GetPlayers()` na jedno współdzielone skanowanie wykonywane raz na tick. Pętla HUD również z niego korzysta, co znacznie zmniejsza obciążenie serwera przy większej liczbie graczy (potwierdzone w 10v10).
|
||||
- ###### Wyświetlanie informacji o braku zainstalowanego RayTrace.
|
||||
|
||||
- #### Poprawki mocy:
|
||||
- ###### Druga Szansa / Feniks / Re-Zombie:
|
||||
- ###### Przechwytują śmiertelne obrażenia przed ich zastosowaniem. Wcześniej reagowały dopiero po śmierci gracza, przez co nie działały przy natychmiastowych zabójstwach, obrażeniach od upadku oraz zabójstwach zadawanych przez moce.
|
||||
- ###### Zabójczy Flash:
|
||||
- ###### Zabija teraz poprzez zadanie rzeczywistych obrażeń zamiast wymuszania samobójstwa, dzięki czemu moce wskrzeszające mogą zadziałać.
|
||||
- ###### Bejsbolista:
|
||||
- ###### Naprawiono rzadki crash serwera związany z granatem wabikiem (indeks encji wabika mógł zostać ponownie wykorzystany przez inną encję).
|
||||
- ###### Aimbot:
|
||||
- ###### Dodano sprawdzanie poprawności wskaźnika hit-group przed zapisem, co zapobiega zapisywaniu do zwolnionej lub pustej pamięci.
|
||||
- ###### Wallhack:
|
||||
- ###### Efekt poświaty jest teraz usuwany, gdy cel zginie lub opuści serwer.
|
||||
- ###### Obserwator / Kamera / Oko Sokoła / Trzecie Oko:
|
||||
- ###### Dodano zabezpieczenia przed pustymi wskaźnikami kamery oraz ponowną weryfikację celu przed teleportacją lub przypisaniem kamery.
|
||||
|
||||
**Pełna aktualizacja została przygotowana przez [@ByDexterTR](https://github.com/ByDexterTR) w ramach pull requesta [#44](https://github.com/Juzlus/jRandomSkills/pull/44). Dziękujemy ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.2.b8</b></summary>
|
||||
|
||||
|
|
@ -414,7 +441,7 @@ Plugin korzysta z zawartości następujących projektów:
|
|||
- ###### Duszek / Ninja / C4 Kamuflaż:
|
||||
- ###### Zoptymalizowano przetwarzanie ukrytych graczy, poprawiając wydajność.
|
||||
|
||||
**Pełna aktualizacja została przygotowana przez @ByDexterTR w ramach pull requesta #41. Dziękujemy ByDexterTR!**
|
||||
**Pełna aktualizacja została przygotowana przez [@ByDexterTR](https://github.com/ByDexterTR) w ramach pull requesta [#44](https://github.com/Juzlus/jRandomSkills/pull/44). Dziękujemy ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -48,7 +48,7 @@ Join the 3v3 test server and try out the jRandomSkills plugin:
|
|||
Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.html).
|
||||
|
||||
|
||||
## ✨ Current Skills (124)
|
||||
## ✨ Current Skills (131)
|
||||
<details>
|
||||
<summary>The table below lists all available skills in the game, along with their descriptions.</summary>
|
||||
|
||||
|
|
@ -86,6 +86,8 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Duplicator | Choose a player to copy their skill | - |
|
||||
| Dwarf | Random character size at the start of the round | (60 - 95)% |
|
||||
| Enemy Spawn | Click [css_useSkill] to teleport to the enemy spawn | 15 s |
|
||||
| Expensive Ammo | A chosen enemy loses money with every shot | - |
|
||||
| Exploding Barrel | Place a barrel that explodes when shot | - |
|
||||
| Explosive Shot | Random chance to fire an explosive bullet while shooting | (15 - 30)% |
|
||||
| Falcon Eye | Click [css_useSkill] to activate a bird's-eye view camera| - |
|
||||
| Fastreload | Click [css_useSkill] to reload the weapon you are currently holding | - |
|
||||
|
|
@ -96,6 +98,7 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Freezing Decoy | Your decoy freezes all nearby players | - |
|
||||
| Gambler | Select a skill from the list provided | - |
|
||||
| Ghost | You are completely invisible | - |
|
||||
| Giant | Enlarges a chosen enemy | (110 - 140)% |
|
||||
| Glaz | You can see through smoke grenades | - |
|
||||
| Glitch | Disables the radar for a chosen enemy | - |
|
||||
| Glue | Your grenades stick to walls | - |
|
||||
|
|
@ -115,8 +118,10 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Jammer | Choose a player to disable their crosshair | - |
|
||||
| Jester | In jester mode, you cannot get or take any damage. This mode changes every few seconds | (10 - 25) s |
|
||||
| Legless | Choose a player who cannot jump | - |
|
||||
| Jump Curse | A chosen enemy jumps whenever their teammates jump | - |
|
||||
| Jumping Jack | Jumping restores health | - |
|
||||
| Killer Flash | Anyone fully blinded by your flashbang dies (including you) | - |
|
||||
| Knockback | Firing while airborne pushes you backwards | - |
|
||||
| Life Swap | Choose a player to swap health with | - |
|
||||
| Long Knife | A primary knife attack deals damage regardless of distance | - |
|
||||
| Long Zeus | Zeus deals damage regardless of distance | - |
|
||||
|
|
@ -126,6 +131,7 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Medic | Click [css_useSkill] to use a healing charge that restores 50 health | 1 s |
|
||||
| Bomb Miner | Your HE grenade only explode when there is an enemy nearby | - |
|
||||
| Taxman | Choose a player to swap money with | - |
|
||||
| Nightmare | Gives a chosen enemy a nightmarish vision | - |
|
||||
| Ninja | Standing still increases your invisibility by 33%, crouching by 33%, and holding a knife by 33% | - |
|
||||
| No-Nades | Grenades deal no damage to you | - |
|
||||
| Focus | No recoil while shooting | - |
|
||||
|
|
@ -172,6 +178,7 @@ Buying a server on pukawka? Use my [referral code](https://pukawka.pl/pp,juzlus.
|
|||
| Thorns | Your opponent will receive a portion of the damage that they inflicted on you | - |
|
||||
| Throwing Knife | Click [css_useSkill] to throw a knife. But watch out for others | - |
|
||||
| Toxic Smoke | Your smoke grenades deal damage | - |
|
||||
| Tripwire | String a wire between two walls; enemies touching it show on your radar | - |
|
||||
| Wallhack | You can see enemies through walls | - |
|
||||
| Watchmaker | Every grenade throw alters the round time | - |
|
||||
| Weapon Swap | Click [css_useSkill] to swap weapons with a random enemy | 30 s |
|
||||
|
|
@ -383,6 +390,33 @@ This plugin uses content from the following projects:
|
|||
|
||||
## 📋 Changelog
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.3.b1</b></summary>
|
||||
|
||||
- #### General (Performance and Stability)
|
||||
- ###### `CServerSideClient.ForceFullUpdate` now bails on a null client handle.
|
||||
- ###### Added a per-tick player/bomb cache.
|
||||
- ###### Migrated 82 skills from per-skill `Utilities.GetPlayers()` to the one shared per-tick scan, and the HUD loop reuses it. Much lower per-tick cost at high player counts (verified 10v10).
|
||||
- ###### Print a single notice when the RayTrace is not installed.
|
||||
|
||||
- #### Skill improvements:
|
||||
- ###### Second Chance / Phoenix / Re-Zombie:
|
||||
- ###### Intercept the lethal hit before it is applied. Previously they reacted after the death was already committed, so one-shots, fall damage and skill-based kills were missed.
|
||||
- ###### Killer Flash:
|
||||
- ###### Kills through real damage instead of forcing a suicide, so revive skills can intercept.
|
||||
- ###### Baseball:
|
||||
- ###### Fixed a rare server crash from decoy grenades (decoy's entity index could be reused by another entity).
|
||||
- ###### Aimbot:
|
||||
- ###### Guard the hit-group native pointer before writing (avoids writing to freed/null memory).
|
||||
- ###### Wallhack:
|
||||
- ###### Detach the glow when the target dies or disconnects.
|
||||
- ###### Spectator / Cypher / FalconEye / ThirdEye:
|
||||
- ###### null-guard the camera SceneNode walk and re-validate the target before teleport/parent.
|
||||
|
||||
**Full update contributed by [@ByDexterTR](https://github.com/ByDexterTR) in pull request [#44](https://github.com/Juzlus/jRandomSkills/pull/44). Thanks to ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.2.b9</b></summary>
|
||||
|
||||
|
|
@ -416,7 +450,7 @@ This plugin uses content from the following projects:
|
|||
- ###### Ghost / Ninja / C4 Camouflage:
|
||||
- ###### Improved hidden-player processing for better performance.
|
||||
|
||||
**Full update contributed by @ByDexterTR in pull request #41. Thanks to ByDexterTR!**
|
||||
**Full update contributed by [@ByDexterTR](https://github.com/ByDexterTR) in pull request [#41](https://github.com/Juzlus/jRandomSkills/pull/41). Thanks to ByDexterTR!**
|
||||
|
||||
</details>
|
||||
|
||||
|
|
|
|||
|
|
@ -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.b1";
|
||||
public override string ModuleVersion => "1.2.3.b2";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
|
|
@ -61,6 +61,10 @@ namespace src
|
|||
{
|
||||
src.player.PerfLog.Info("===== PLUGIN UNLOAD (clean shutdown/reload) =====");
|
||||
Debug.WriteToDebug("===== PLUGIN UNLOAD (clean shutdown/reload) =====");
|
||||
|
||||
Event.Unload();
|
||||
Debug.Unload();
|
||||
|
||||
base.Unload(hotReload);
|
||||
}
|
||||
|
||||
|
|
@ -333,9 +337,11 @@ namespace src
|
|||
public bool IsDrawing { get; set; }
|
||||
public DateTime SkillHudExpired { get; set; }
|
||||
public DateTime SkillDescriptionHudExpired { get; set; }
|
||||
public DateTime HudSuppressedUntil { get; set; }
|
||||
public string? PrintHTML { get; set; }
|
||||
public bool DisplayHUD { get; set; }
|
||||
public bool SkillUsed = false;
|
||||
public bool? HudOnDeathBlocked { get; set; }
|
||||
}
|
||||
|
||||
public class jSkill_SkillInfo(Skills skill, string color, bool display)
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Feindes-Spawn",
|
||||
"enemyspawn_desc": "Klicke auf [css_useSkill], um zum Feindes-Spawn zu teleportieren",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Explosiver Schuss",
|
||||
"explosiveshot_desc": "Zufällige Chance, beim Schießen eine explosive Kugel abzufeuern",
|
||||
"explosiveshot_desc2": "Deine Chance, eine explosive Kugel abzufeuern: {0} %",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Geist",
|
||||
"ghost_desc": "Du bist vollständig unsichtbar",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Du kannst durch Rauchgranaten hindurchsehen",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "Der Spieler ‚{0}‘ kann nicht mehr springen.",
|
||||
"jumpban_select_info": "Spieler vom Springen ausschließen:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Jumping Jack",
|
||||
"jumpingjack_desc": "Durch Springen wird die Gesundheit wiederhergestellt",
|
||||
|
||||
"killerflash": "Killer Flash",
|
||||
"killerflash_desc": "Jeder, der durch deine Blendgranate vollständig geblendet wird, stirbt (einschließlich dir selbst)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
|
||||
"lifeswap": "Lebensaustausch",
|
||||
"lifeswap_desc": "Wähle einen Spieler aus, mit dem du deine Gesundheit austauschen möchtest",
|
||||
"lifeswap_enemy_info": "Jemand hat sich deine Gesundheit ausgeliehen",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Du explodierst beim Tod und tötest Spieler in deiner Nähe",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Wenn du stillstehst, erhöht sich deine Unsichtbarkeit um 33 %, wenn du dich duckst um 33 % und wenn du ein Messer hältst um 33 %",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Giftiger Rauch",
|
||||
"toxicsmoke_desc": "Deine Rauchgranaten verursachen Schaden",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Du kannst Feinde durch Wände hindurch sehen",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Enemy Spawn",
|
||||
"enemyspawn_desc": "Click [css_useSkill] to teleport to the enemy spawn",
|
||||
|
||||
"expensiveammo": "Expensive Ammo",
|
||||
"expensiveammo_desc": "A chosen enemy loses money with every shot",
|
||||
"expensiveammo_disable_info": "Your bullets are free again.",
|
||||
"expensiveammo_enemy_info": "Every shot you fire now costs you {0}$.",
|
||||
"expensiveammo_player_info": "Player '{0}' now pays for every bullet.",
|
||||
"expensiveammo_select_info": "Select player whose ammo becomes expensive:",
|
||||
|
||||
"explodingbarrel": "Exploding Barrel",
|
||||
"explodingbarrel_desc": "Click [css_useSkill] to place a barrel that explodes when shot",
|
||||
|
||||
"explosiveshot": "Explosive Shot",
|
||||
"explosiveshot_desc": "Random chance to fire an explosive bullet while shooting",
|
||||
"explosiveshot_desc2": "Your chance to fire an explosive bullet: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Ghost",
|
||||
"ghost_desc": "You are completely invisible",
|
||||
|
||||
"giant": "Giant",
|
||||
"giant_desc": "Enlarge an enemy of your choice",
|
||||
"giant_desc2": "Enlarge an enemy of your choice to {0}x",
|
||||
"giant_disable_info": "You are back to your normal size.",
|
||||
"giant_enemy_info": "You have been enlarged to {0}x.",
|
||||
"giant_player_info": "You enlarged player '{0}' to {1}x.",
|
||||
"giant_select_info": "Enlarge player:",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "You can see through smoke grenades",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "Player '{0}' can no longer jump.",
|
||||
"jumpban_select_info": "Ban player from jumping:",
|
||||
|
||||
"jumpcurse": "Jump Curse",
|
||||
"jumpcurse_desc": "A chosen enemy jumps whenever their teammates jump",
|
||||
"jumpcurse_disable_info": "Your legs are your own again.",
|
||||
"jumpcurse_enemy_info": "You now jump whenever your teammates jump.",
|
||||
"jumpcurse_player_info": "Player '{0}' now jumps with their team.",
|
||||
"jumpcurse_select_info": "Select player for the jump curse:",
|
||||
|
||||
"jumpingjack": "Jumping Jack",
|
||||
"jumpingjack_desc": "Jumping restores health",
|
||||
|
||||
"killerflash": "Killer Flash",
|
||||
"killerflash_desc": "Anyone fully blinded by your flashbang dies (including you)",
|
||||
|
||||
"knockback": "Knockback",
|
||||
"knockback_desc": "Firing while airborne pushes you backwards",
|
||||
|
||||
"lifeswap": "Life Swap",
|
||||
"lifeswap_desc": "Choose a player to swap health with",
|
||||
"lifeswap_enemy_info": "Someone borrowed your health.",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "You explode upon death, killing nearby players",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "Nightmare",
|
||||
"nightmare_desc": "Give a chosen enemy a nightmarish vision",
|
||||
"nightmare_disable_info": "The nightmare has faded.",
|
||||
"nightmare_enemy_info": "A nightmare clouds your vision.",
|
||||
"nightmare_player_info": "You cast a nightmare on player '{0}'.",
|
||||
"nightmare_select_info": "Select player for the nightmare:",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Standing still increases your invisibility by 33%, crouching by 33%, and holding a knife by 33%",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Toxic Smoke",
|
||||
"toxicsmoke_desc": "Your smoke grenades deal damage",
|
||||
|
||||
"tripwire": "Tripwire",
|
||||
"tripwire_desc": "Click [css_useSkill] to string a wire between two walls. Enemies touching it show on your radar",
|
||||
"tripwire_limit_info": "You can only have {0} tripwires at a time.",
|
||||
"tripwire_no_wall_info": "No walls close enough on both sides.",
|
||||
"tripwire_placed_info": "Tripwire placed.",
|
||||
"tripwire_triggered_info": "'{0}' tripped your wire.",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "You can see enemies through walls",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Spawn Ennemi",
|
||||
"enemyspawn_desc": "Appuyez sur [css_useSkill] pour vous téléporter au spawn ennemi",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Tir Explosif",
|
||||
"explosiveshot_desc": "Chance aléatoire de tirer une balle explosive en tirant",
|
||||
"explosiveshot_desc2": "Votre chance de tirer une balle explosive est : {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Fantôme",
|
||||
"ghost_desc": "Vous êtes complètement invisible",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Vous pouvez voir à travers les fumigènes",
|
||||
|
||||
|
|
@ -241,12 +259,21 @@
|
|||
"jumpban_player_info": "Le joueur « {0} » ne peut plus sauter.",
|
||||
"jumpban_select_info": "Interdire de sauter au joueur :",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Sauteur",
|
||||
"jumpingjack_desc": "Sauter vous rend de la vie",
|
||||
|
||||
"killerflash": "Flash Mortel",
|
||||
"killerflash_desc": "Toute personne totalement aveuglée par votre flashbang meurt (y compris vous)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
"lifeswap": "Échange de Vie",
|
||||
"lifeswap_desc": "Choisissez un joueur avec qui échanger votre santé",
|
||||
"lifeswap_enemy_info": "Quelqu’un a emprunté votre santé.",
|
||||
|
|
@ -288,6 +315,13 @@
|
|||
"deathbomb_desc": "Vous explosez à votre mort, tuant les joueurs proches",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Rester immobile augmente votre invisibilité de 33%, s’accroupir de 33%, et tenir un couteau de 33%",
|
||||
|
||||
|
|
@ -450,6 +484,13 @@
|
|||
"toxicsmoke": "Fumée Toxique",
|
||||
"toxicsmoke_desc": "Vos grenades fumigènes infligent des dégâts",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Vous pouvez voir les ennemis à travers les murs",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Resp Wroga",
|
||||
"enemyspawn_desc": "Kliknij [css_useSkill], aby teleportować się na resp wroga",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Strzał Wybuchowy",
|
||||
"explosiveshot_desc": "Losowa szansa wystrzelenia pocisku wybuchowego podczas strzelania",
|
||||
"explosiveshot_desc2": "Twoja szansa na wystrzelenie pocisku wybuchowego: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Duszek",
|
||||
"ghost_desc": "Jesteś całkowicie niewidzialny",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Nie widzisz granatów dymnych",
|
||||
|
||||
|
|
@ -241,12 +259,21 @@
|
|||
"jumpban_player_info": "Gracz '{0}' nie może teraz skakać.",
|
||||
"jumpban_select_info": "Zablokuj skok dla gracza:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Pajacyk",
|
||||
"jumpingjack_desc": "Skakanie przywraca zdrowie",
|
||||
|
||||
"killerflash": "Zabójczy Flash",
|
||||
"killerflash_desc": "Każdy całkowicie oślepiony twoim granatem umiera (również ty)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
"lifeswap": "Zamiana Żyć",
|
||||
"lifeswap_desc": "Wybierasz gracza, z którym chcesz wymienić się zdrowiem",
|
||||
"lifeswap_enemy_info": "Ktoś pożyczył sobie twoje zdrowie.",
|
||||
|
|
@ -288,6 +315,13 @@
|
|||
"deathbomb_desc": "Po śmierci eksplodujesz i zabijasz graczy znajdujących się w zasięgu",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Stojąc nieruchomo zwiększasz swoją niewidzialność o 33%, kucając o 33%, a trzymając nóż o 33%",
|
||||
|
||||
|
|
@ -450,6 +484,13 @@
|
|||
"toxicsmoke": "Toksyczny Dym",
|
||||
"toxicsmoke_desc": "Twoje granaty dymny zadają obrażenia",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Widzisz wrogów przez ściany",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Spawn Inimigo",
|
||||
"enemyspawn_desc": "Clique em [css_useSkill] para se teletransportar para a base inimiga",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Tiro Explosivo",
|
||||
"explosiveshot_desc": "Chance aleatória de disparar uma bala explosiva enquanto atira",
|
||||
"explosiveshot_desc2": "Sua chance de disparar uma bala explosiva: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Fantasma",
|
||||
"ghost_desc": "Você é completamente invisível",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Smoke Transparente",
|
||||
"glaz_desc": "Smokes tornam-se transparentes para você",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "O jogador '{0}' não pode mais pular.",
|
||||
"jumpban_select_info": "Banir pulo do jogador:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Pula-Pula",
|
||||
"jumpingjack_desc": "Pular restaura vida",
|
||||
|
||||
"killerflash": "Flash Mortal",
|
||||
"killerflash_desc": "Qualquer pessoa cega pela sua flash morre (incluindo você)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
|
||||
"lifeswap": "Troca de Vida",
|
||||
"lifeswap_desc": "Escolha um jogador para trocar de vida",
|
||||
"lifeswap_enemy_info": "Alguém pegou sua vida emprestada.",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Você explode ao morrer, matando os jogadores próximos",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Ficar parado aumenta sua invisibilidade em 33%, agachar em 33%, e segurar uma faca em 33%",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Smoke Tóxica",
|
||||
"toxicsmoke_desc": "Suas smokes causam dano",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Você pode ver inimigos através das paredes",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Спавн врагов",
|
||||
"enemyspawn_desc": "Нажмите [css_useSkill], чтобы телепортироваться на спавн врагов",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Взрывной выстрел",
|
||||
"explosiveshot_desc": "Случайный шанс выстрелить взрывной пулей при стрельбе",
|
||||
"explosiveshot_desc2": "Ваш шанс выстрелить взрывной пулей: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Призрак",
|
||||
"ghost_desc": "Вы полностью невидимы",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Глаз",
|
||||
"glaz_desc": "Вы видите сквозь дымовые гранаты",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "Игрок '{0}' больше не может прыгать.",
|
||||
"jumpban_select_info": "Запретить прыжки игроку:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Прыгающий Джек",
|
||||
"jumpingjack_desc": "Прыжки восстанавливают здоровье",
|
||||
|
||||
"killerflash": "Смертельная флешка",
|
||||
"killerflash_desc": "Любой, полностью ослеплённый вашей флешкой, погибает (включая вас)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
|
||||
"lifeswap": "Обмен жизнями",
|
||||
"lifeswap_desc": "Выберите игрока для обмена здоровьем",
|
||||
"lifeswap_enemy_info": "Кто-то занял ваше здоровье.",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Вы взрываетесь при смерти, убивая ближайших игроков",
|
||||
"deathbomb_explosion": "АЛЛАХУ АКБАР!!!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ниндзя",
|
||||
"ninja_desc": "Стоя на месте, ваша невидимость увеличивается на 33%, в приседе на 33%, с ножом на 33%",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Токсичный дым",
|
||||
"toxicsmoke_desc": "Ваши дымовые гранаты наносят урон",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Воллхак",
|
||||
"wallhack_desc": "Вы видите врагов сквозь стены",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Base Baskını",
|
||||
"enemyspawn_desc": "[css_useSkill] tuşuna basarak düşman doğma noktasına ışınlan",
|
||||
|
||||
"expensiveammo": "Pahalı Mermi",
|
||||
"expensiveammo_desc": "Seçtiğin rakip her ateş ettiğinde para kaybeder",
|
||||
"expensiveammo_disable_info": "Mermilerin yeniden bedava.",
|
||||
"expensiveammo_enemy_info": "Artık her atışın sana {0}$ mal oluyor.",
|
||||
"expensiveammo_player_info": "'{0}' artık her mermisinin parasını ödüyor.",
|
||||
"expensiveammo_select_info": "Mermisi pahalanacak oyuncu:",
|
||||
|
||||
"explodingbarrel": "Patlayan Varil",
|
||||
"explodingbarrel_desc": "[css_useSkill] ile vurulunca patlayan bir varil koyarsın",
|
||||
|
||||
"explosiveshot": "Patlayıcı Mermi",
|
||||
"explosiveshot_desc": "Ateş ederken rastgele mermilerin patlayabilir",
|
||||
"explosiveshot_desc2": "Patlayan mermi şansı: %{0}",
|
||||
|
|
@ -159,12 +169,20 @@
|
|||
"gambler": "Kumarbaz",
|
||||
"gambler_desc": "Sunulan listeden bir yetenek seç.",
|
||||
"gambler_select_info": "Yeteneğini seç:",
|
||||
"gambler_more": "#00FF00|${0} karşılığında yeni yetenekler",
|
||||
"gambler_more": "#00FF00|${0} yenile",
|
||||
"gambler_no_money": "Yeterli para yok.",
|
||||
|
||||
"ghost": "Hayalet",
|
||||
"ghost_desc": "Tamamen görünmezsin",
|
||||
|
||||
"giant": "Dev",
|
||||
"giant_desc": "Seçtiğin bir rakibi büyütür",
|
||||
"giant_desc2": "Seçtiğin bir rakibi {0}x büyütür",
|
||||
"giant_disable_info": "Normal boyutuna geri döndün.",
|
||||
"giant_enemy_info": "{0}x boyutuna büyütüldün.",
|
||||
"giant_player_info": "'{0}' oyuncusunu {1}x büyüttün.",
|
||||
"giant_select_info": "Büyütülecek oyuncu:",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Sis bombalarının içini görebilirsin",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "'{0}' artık zıplayamaz.",
|
||||
"jumpban_select_info": "Zıplaması yasaklanacak oyuncu:",
|
||||
|
||||
"jumpcurse": "Zıplama Laneti",
|
||||
"jumpcurse_desc": "Seçtiğin rakip, takım arkadaşları her zıpladığında zıplar",
|
||||
"jumpcurse_disable_info": "Bacakların yeniden senin.",
|
||||
"jumpcurse_enemy_info": "Artık takım arkadaşların zıpladıkça sen de zıplıyorsun.",
|
||||
"jumpcurse_player_info": "'{0}' artık takımıyla birlikte zıplıyor.",
|
||||
"jumpcurse_select_info": "Zıplama laneti verilecek oyuncu:",
|
||||
|
||||
"jumpingjack": "Zıpzıp Jack",
|
||||
"jumpingjack_desc": "Zıplamak canını yeniler",
|
||||
|
||||
"killerflash": "Ölümcül Flaş",
|
||||
"killerflash_desc": "Flaşına tamamen kör olan herkes ölür (sen dahil)",
|
||||
|
||||
"knockback": "Savurma",
|
||||
"knockback_desc": "Havadayken ateş etmek seni geriye savurur",
|
||||
|
||||
"lifeswap": "Can Takası",
|
||||
"lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç",
|
||||
"lifeswap_enemy_info": "Birisi canını çaldı",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün",
|
||||
"deathbomb_explosion": "BOOM!!!",
|
||||
|
||||
"nightmare": "Kabus",
|
||||
"nightmare_desc": "Seçtiğin bir rakibe kabus görüntüsü verir",
|
||||
"nightmare_disable_info": "Kabus sona erdi.",
|
||||
"nightmare_enemy_info": "Gözlerini bir kabus bürüdü.",
|
||||
"nightmare_player_info": "'{0}' oyuncusuna kabus gördürdün.",
|
||||
"nightmare_select_info": "Kabus görecek oyuncu:",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Görünmezliğini sabit durmak %33, eğilmek %33 ve bıçak tutmak %33 artırır",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Zehirli Sis",
|
||||
"toxicsmoke_desc": "Sis bombaların hasar verir",
|
||||
|
||||
"tripwire": "Tel Tuzağı",
|
||||
"tripwire_desc": "[css_useSkill] ile iki duvar arasına tel ger. Tele değen düşman radarında görünür",
|
||||
"tripwire_limit_info": "Aynı anda en fazla {0} tel gerebilirsin.",
|
||||
"tripwire_no_wall_info": "İki yanında da yeterince yakın duvar yok.",
|
||||
"tripwire_placed_info": "Tel gerildi.",
|
||||
"tripwire_triggered_info": "'{0}' teline takıldı.",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Düşmanları duvarların arkasından görebilirsin",
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -106,6 +106,24 @@ namespace src.player
|
|||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamage, HookMode.Pre);
|
||||
}
|
||||
|
||||
public static void Unload()
|
||||
{
|
||||
try { VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre); }
|
||||
catch { }
|
||||
|
||||
lock (_writeLock) { _writer?.Dispose(); _writer = null; }
|
||||
}
|
||||
|
||||
private static string DescribeIdentitySplit(CCSPlayerController victim)
|
||||
{
|
||||
var routed = PlayerManager.GetPlayerEvent(victim);
|
||||
uint routedIndex = routed?.Index ?? victim.Index;
|
||||
if (routedIndex == victim.Index) return string.Empty;
|
||||
|
||||
return $" SPLIT(idx {victim.Index}->{routedIndex}, skill {PlayerManager.GetPlayerByIndex(victim.Index)?.Skill}" +
|
||||
$"->{PlayerManager.GetPlayerByIndex(routedIndex)?.Skill}, controllingBot={victim.ControllingBot})";
|
||||
}
|
||||
|
||||
private static HookResult OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
||||
|
|
@ -129,7 +147,10 @@ namespace src.player
|
|||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
if (playerInfo == null) return HookResult.Continue;
|
||||
|
||||
WriteToDebug($"{(victim.IsBot ? "Bot" : "Player")} {victim.PlayerName} took damage from {(attacker.IsBot ? "bot" : "player")} {attacker.PlayerName}.");
|
||||
WriteToDebug($"{(victim.IsBot ? "Bot" : "Player")} {victim.PlayerName} took damage from {(attacker.IsBot ? "bot" : "player")} {attacker.PlayerName}. " +
|
||||
$"[dmg={param2.Damage:0.#} hp={victimPawn.Health}/{victimPawn.MaxHealth} armor={victimPawn.ArmorValue} hitgroup={SkillUtils.GetHitGroup(param2)} " +
|
||||
$"takes={victimPawn.TakesDamage} vskill={PlayerManager.GetPlayerByIndex(victim.Index)?.Skill} askill={playerInfo.Skill}" +
|
||||
$"{DescribeIdentitySplit(victim)}]");
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ public interface ISkill
|
|||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList _) { }
|
||||
|
||||
public static void NewRound() { }
|
||||
public static void RoundEnd() { }
|
||||
public static void PlayerMakeSound(UserMessage _) { }
|
||||
public static void PlayerBlind(EventPlayerBlind _) { }
|
||||
public static void PlayerHurt(EventPlayerHurt _) { }
|
||||
public static bool PlayerHurtPre(EventPlayerHurt _) { return false; }
|
||||
public static void PlayerDeath(EventPlayerDeath _) { }
|
||||
public static void PlayerJump(EventPlayerJump _) { }
|
||||
public static void SwitchTeam(EventSwitchTeam _, GameEventInfo __) { }
|
||||
|
|
@ -88,6 +90,8 @@ public enum Skills
|
|||
Duplicator,
|
||||
Dwarf,
|
||||
EnemySpawn,
|
||||
ExpensiveAmmo,
|
||||
ExplodingBarrel,
|
||||
ExplosiveShot,
|
||||
FalconEye,
|
||||
FastReload,
|
||||
|
|
@ -98,6 +102,7 @@ public enum Skills
|
|||
FrozenDecoy,
|
||||
Gambler,
|
||||
Ghost,
|
||||
Giant,
|
||||
Glaz,
|
||||
Glitch,
|
||||
Glue,
|
||||
|
|
@ -117,8 +122,10 @@ public enum Skills
|
|||
Jammer,
|
||||
Jester,
|
||||
JumpBan,
|
||||
JumpCurse,
|
||||
JumpingJack,
|
||||
KillerFlash,
|
||||
Knockback,
|
||||
LifeSwap,
|
||||
LongKnife,
|
||||
LongZeus,
|
||||
|
|
@ -128,6 +135,7 @@ public enum Skills
|
|||
Medic,
|
||||
Miner,
|
||||
MoneySwap,
|
||||
Nightmare,
|
||||
Ninja,
|
||||
NoNades,
|
||||
NoRecoil,
|
||||
|
|
@ -174,6 +182,7 @@ public enum Skills
|
|||
Thorns,
|
||||
ThrowingKnife,
|
||||
ToxicSmoke,
|
||||
Tripwire,
|
||||
Wallhack,
|
||||
Watchmaker,
|
||||
WeaponsSwap,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,10 @@ namespace src.player
|
|||
Instance.RegisterEventHandler<EventRoundStart>(RoundStart);
|
||||
Instance.RegisterEventHandler<EventRoundEnd>(RoundEnd);
|
||||
|
||||
Instance.RegisterEventHandler<EventPlayerDeath>(PlayerDeathPre, HookMode.Pre);
|
||||
Instance.RegisterEventHandler<EventPlayerDeath>(PlayerDeath);
|
||||
Instance.RegisterEventHandler<EventPlayerBlind>(PlayerBlind);
|
||||
Instance.RegisterEventHandler<EventPlayerHurt>(PlayerHurtPre, HookMode.Pre);
|
||||
Instance.RegisterEventHandler<EventPlayerHurt>(PlayerHurt);
|
||||
Instance.RegisterEventHandler<EventPlayerJump>(PlayerJump);
|
||||
Instance.RegisterEventHandler<EventBotTakeover>(BotTakeover);
|
||||
|
|
@ -89,6 +91,22 @@ namespace src.player
|
|||
// Keeping the plugin alive is safer than installing a stale global hook at load time.
|
||||
}
|
||||
|
||||
public static void Unload()
|
||||
{
|
||||
TryUnhook(() => VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre));
|
||||
TryUnhook(() => VirtualFunctions.CBaseTrigger_StartTouchFunc.Unhook(OnTriggerEnter, HookMode.Post));
|
||||
TryUnhook(() => VirtualFunctions.CBaseTrigger_EndTouchFunc.Unhook(OnTriggerExit, HookMode.Pre));
|
||||
TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre));
|
||||
TryUnhook(() => Instance.UnhookUserMessage(208, PlayerMakeSound));
|
||||
TryUnhook(() => Instance.RemoveListener<CheckTransmit>(CheckTransmit));
|
||||
}
|
||||
|
||||
private static void TryUnhook(Action unhook)
|
||||
{
|
||||
try { unhook(); }
|
||||
catch (Exception ex) { Server.PrintToConsole($"[jRandomSkills] unhook failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
private static jSkill_SkillInfo ChooseSkillByRarityAndMax(List<jSkill_SkillInfo> candidates, Dictionary<Skills, int> assignmentCounts, Config.GameModes gameMode)
|
||||
{
|
||||
if (candidates == null || candidates.Count == 0) return noneSkill;
|
||||
|
|
@ -180,12 +198,53 @@ namespace src.player
|
|||
continue;
|
||||
}
|
||||
|
||||
InvokeSkill(p.Skill, "OnTakeDamage", args);
|
||||
InvokeOnTakeDamage(p.Skill, h, args);
|
||||
}
|
||||
|
||||
if (deferred == null) return;
|
||||
foreach (var skill in deferred)
|
||||
InvokeOnTakeDamage(skill, h, args);
|
||||
}
|
||||
|
||||
private static void InvokeOnTakeDamage(Skills skill, DynamicHook h, object[] args)
|
||||
{
|
||||
if (Config.LoadedConfig.DebugMode != true)
|
||||
{
|
||||
InvokeSkill(skill, "OnTakeDamage", args);
|
||||
return;
|
||||
}
|
||||
|
||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
||||
float before = info == null ? 0f : info.Damage;
|
||||
|
||||
InvokeSkill(skill, "OnTakeDamage", args);
|
||||
|
||||
float after = info == null ? 0f : info.Damage;
|
||||
if (Math.Abs(before - after) > 0.01f)
|
||||
Debug.WriteToDebug($"[DMG] {skill} changed damage {before:0.#} -> {after:0.#}{DescribeDamageTarget(h)}");
|
||||
}
|
||||
|
||||
private static string DescribeDamageTarget(DynamicHook h)
|
||||
{
|
||||
try
|
||||
{
|
||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
||||
if (victimEntity == null || !victimEntity.IsValid) return string.Empty;
|
||||
|
||||
var pawn = victimEntity.As<CCSPlayerPawn>();
|
||||
if (pawn == null || !pawn.IsValid || pawn.DesignerName != "player") return string.Empty;
|
||||
|
||||
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
||||
if (controller == null || !controller.IsValid) return string.Empty;
|
||||
|
||||
uint routedIndex = PlayerManager.GetPlayerEvent(controller)?.Index ?? controller.Index;
|
||||
return $" on {controller.PlayerName} [idx={controller.Index} skill={PlayerManager.GetPlayerByIndex(controller.Index)?.Skill}" +
|
||||
$" routedIdx={routedIndex} routedSkill={PlayerManager.GetPlayerByIndex(routedIndex)?.Skill}]";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static HookResult PlayerMakeSound(UserMessage um)
|
||||
|
|
@ -314,6 +373,63 @@ namespace src.player
|
|||
}
|
||||
}
|
||||
|
||||
private static HookResult PlayerHurtPre(EventPlayerHurt @event, GameEventInfo info)
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (@event.DmgHealth <= 0 && @event.DmgArmor <= 0) return HookResult.Continue;
|
||||
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (victim == null || !victim.IsValid) return HookResult.Continue;
|
||||
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim.Index);
|
||||
if (victimInfo == null || victimInfo.IsDrawing) return HookResult.Continue;
|
||||
|
||||
bool suppressed = AskSkillSuppressesHit(victimInfo.Skill, @event);
|
||||
|
||||
if (!suppressed)
|
||||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
if (attacker != null && attacker.IsValid && attacker.Index != victim.Index)
|
||||
{
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||
if (attackerInfo != null && !attackerInfo.IsDrawing && attackerInfo.Skill != victimInfo.Skill)
|
||||
suppressed = AskSkillSuppressesHit(attackerInfo.Skill, @event);
|
||||
}
|
||||
}
|
||||
|
||||
if (!suppressed) return HookResult.Continue;
|
||||
|
||||
if (@event.DmgArmor > 0)
|
||||
{
|
||||
var pawn = victim.PlayerPawn?.Value;
|
||||
if (pawn != null && pawn.IsValid)
|
||||
{
|
||||
pawn.ArmorValue += @event.DmgArmor;
|
||||
Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
}
|
||||
}
|
||||
|
||||
@event.DmgHealth = 0;
|
||||
@event.DmgArmor = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteToDebug($"PlayerHurtPre failed: {ex.Message}");
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AskSkillSuppressesHit(Skills skill, EventPlayerHurt @event)
|
||||
{
|
||||
if (skill == Skills.None) return false;
|
||||
return (bool?)Instance.SkillAction(skill.ToString(), "PlayerHurtPre", [@event]) == true;
|
||||
}
|
||||
|
||||
private static HookResult PlayerHurt(EventPlayerHurt @event, GameEventInfo info)
|
||||
{
|
||||
lock (setLock)
|
||||
|
|
@ -546,6 +662,9 @@ namespace src.player
|
|||
DisplayHUD = true,
|
||||
SkillUsed = false,
|
||||
};
|
||||
|
||||
UpdateSkillHudExpired(playerInfo, Skills.None);
|
||||
|
||||
PlayerManager.Register(playerInfo);
|
||||
}
|
||||
}
|
||||
|
|
@ -609,14 +728,7 @@ namespace src.player
|
|||
var skillPlayer = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (skillPlayer == null) return HookResult.Continue;
|
||||
|
||||
var temp = skillPlayer.SkillHudExpired;
|
||||
skillPlayer.SkillHudExpired = DateTime.MinValue;
|
||||
|
||||
Instance.AddTimer(5f, () =>
|
||||
{
|
||||
if (skillPlayer.SkillHudExpired == DateTime.MinValue)
|
||||
skillPlayer.SkillHudExpired = temp;
|
||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
skillPlayer.HudSuppressedUntil = DateTime.Now.AddSeconds(5);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
|
@ -653,6 +765,7 @@ namespace src.player
|
|||
{
|
||||
bool isWarmup = Instance.GameRules == null || Instance.GameRules.WarmupPeriod == true;
|
||||
isTransmitRegistered = false;
|
||||
SkillUtils.ClearKillCredits();
|
||||
Instance.AddTimer(.1f, () => DisableAll(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p != null && p.IsValid && !p.IsHLTV && p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist))
|
||||
|
|
@ -785,6 +898,7 @@ namespace src.player
|
|||
private static HookResult RoundEnd(EventRoundEnd @event, GameEventInfo info)
|
||||
{
|
||||
Illiterate.Disable();
|
||||
DispatchToActiveSkills("RoundEnd");
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
|
|
@ -833,6 +947,40 @@ namespace src.player
|
|||
}
|
||||
}
|
||||
|
||||
private static HookResult PlayerDeathPre(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
try
|
||||
{
|
||||
var victim = @event.Userid;
|
||||
if (victim == null || !victim.IsValid) return HookResult.Continue;
|
||||
|
||||
if (!SkillUtils.TryConsumeKillCredit(victim.Index, out uint attackerIndex, out string? weapon))
|
||||
return HookResult.Continue;
|
||||
|
||||
var attacker = Utilities.GetPlayerFromIndex((int)attackerIndex);
|
||||
if (attacker == null || !attacker.IsValid || attacker.Index == victim.Index)
|
||||
return HookResult.Continue;
|
||||
|
||||
@event.Attacker = attacker;
|
||||
|
||||
if (!string.IsNullOrEmpty(weapon))
|
||||
@event.Weapon = weapon;
|
||||
|
||||
var matchStats = attacker.ActionTrackingServices?.MatchStats;
|
||||
if (matchStats != null)
|
||||
{
|
||||
matchStats.Kills++;
|
||||
Utilities.SetStateChanged(attacker, "CCSPlayerController", "m_pActionTrackingServices");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteToDebug($"PlayerDeathPre kill credit failed: {ex.Message}");
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private static HookResult PlayerDeath(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
long perfStart = PerfLog.Start();
|
||||
|
|
@ -955,26 +1103,65 @@ namespace src.player
|
|||
|
||||
private static readonly Dictionary<uint, jSkill_SkillInfo> nextRoundPicks = [];
|
||||
|
||||
private static jSkill_SkillInfo PickSkillForPlayer(CCSPlayerController player, jSkill_PlayerInfo skillPlayer, List<CCSPlayerController> validPlayers, Dictionary<Skills, int> assignmentCounts, Config.GameModes gameMode)
|
||||
private sealed class PickContext
|
||||
{
|
||||
List<jSkill_SkillInfo> skillList = [.. SkillData.Skills];
|
||||
skillList.RemoveAll(s => s?.Skill == Skills.None);
|
||||
if (!player.IsBot)
|
||||
skillList.RemoveAll(s => !string.IsNullOrEmpty(SkillsInfo.GetValue<string>(s.Skill, "requiredPermission")) && !AdminManager.PlayerHasPermissions(player, SkillsInfo.GetValue<string>(s.Skill, "requiredPermission")));
|
||||
public required List<jSkill_SkillInfo> BaseList { get; init; }
|
||||
public required Dictionary<Skills, string> RequiredPermissions { get; init; }
|
||||
public required HashSet<Skills> NeedsTeammates { get; init; }
|
||||
public required HashSet<Skills> CtOnly { get; init; }
|
||||
public required HashSet<Skills> TOnly { get; init; }
|
||||
public required int TerroristCount { get; init; }
|
||||
public required int CounterTerroristCount { get; init; }
|
||||
}
|
||||
|
||||
private static PickContext BuildPickContext(List<CCSPlayerController> validPlayers)
|
||||
{
|
||||
Dictionary<Skills, string> perms = [];
|
||||
foreach (var s in SkillData.Skills)
|
||||
{
|
||||
if (s == null || s.Skill == Skills.None) continue;
|
||||
string perm = SkillsInfo.GetValue<string>(s.Skill, "requiredPermission");
|
||||
if (!string.IsNullOrEmpty(perm)) perms[s.Skill] = perm;
|
||||
}
|
||||
|
||||
return new PickContext
|
||||
{
|
||||
BaseList = [.. SkillData.Skills.Where(s => s != null && s.Skill != Skills.None)],
|
||||
RequiredPermissions = perms,
|
||||
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)),
|
||||
TerroristCount = validPlayers.Count(p => p.Team == CsTeam.Terrorist),
|
||||
CounterTerroristCount = validPlayers.Count(p => p.Team == CsTeam.CounterTerrorist),
|
||||
};
|
||||
}
|
||||
|
||||
private static HashSet<Skills> ToSkillSet(IEnumerable<string> names)
|
||||
{
|
||||
HashSet<Skills> set = [];
|
||||
foreach (var name in names)
|
||||
if (Enum.TryParse<Skills>(name, out var skill)) set.Add(skill);
|
||||
return set;
|
||||
}
|
||||
|
||||
private static jSkill_SkillInfo PickSkillForPlayer(CCSPlayerController player, jSkill_PlayerInfo skillPlayer, PickContext ctx, Dictionary<Skills, int> assignmentCounts, Config.GameModes gameMode)
|
||||
{
|
||||
List<jSkill_SkillInfo> skillList = [.. ctx.BaseList];
|
||||
|
||||
if (!player.IsBot && ctx.RequiredPermissions.Count != 0)
|
||||
skillList.RemoveAll(s => ctx.RequiredPermissions.TryGetValue(s.Skill, out var perm) && !AdminManager.PlayerHasPermissions(player, perm));
|
||||
|
||||
if (gameMode != Config.GameModes.FullRandom)
|
||||
skillList.RemoveAll(s => s?.Skill == skillPlayer?.Skill || s?.Skill == skillPlayer?.SpecialSkill);
|
||||
|
||||
if (validPlayers.Count(p => p.Team == player.Team) == 1)
|
||||
{
|
||||
SkillsInfo.DefaultSkillInfo[] skillsNeedsTeammates = [.. SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates)];
|
||||
skillList.RemoveAll(s => skillsNeedsTeammates.Any(s2 => s2.Name == s.Skill.ToString()));
|
||||
}
|
||||
int teamCount = player.Team == CsTeam.Terrorist ? ctx.TerroristCount : ctx.CounterTerroristCount;
|
||||
if (teamCount == 1)
|
||||
skillList.RemoveAll(s => ctx.NeedsTeammates.Contains(s.Skill));
|
||||
|
||||
if (player.Team == CsTeam.Terrorist)
|
||||
skillList.RemoveAll(s => counterterroristSkills.Any(s2 => s2.Name == s.Skill.ToString()));
|
||||
skillList.RemoveAll(s => ctx.CtOnly.Contains(s.Skill));
|
||||
else
|
||||
skillList.RemoveAll(s => terroristSkills.Any(s2 => s2.Name == s.Skill.ToString()));
|
||||
skillList.RemoveAll(s => ctx.TOnly.Contains(s.Skill));
|
||||
|
||||
if (gameMode == Config.GameModes.NoRepeat && playersSkills.TryGetValue(player.Index, out ConcurrentBag<jSkill_SkillInfo>? skills))
|
||||
{
|
||||
|
|
@ -1029,13 +1216,15 @@ namespace src.player
|
|||
.Where(p => p != null && p.IsValid && !p.IsHLTV)
|
||||
.Where(p => { try { return p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist; } catch { return false; } }).ToList();
|
||||
|
||||
var ctx = BuildPickContext(validPlayers);
|
||||
|
||||
Dictionary<Skills, int> assignmentCounts = [];
|
||||
foreach (var player in validPlayers)
|
||||
{
|
||||
var skillPlayer = PlayerManager.GetPlayerByIndex(player.Index);
|
||||
if (skillPlayer == null) continue;
|
||||
|
||||
var pick = PickSkillForPlayer(player, skillPlayer, validPlayers, assignmentCounts, gameMode);
|
||||
var pick = PickSkillForPlayer(player, skillPlayer, ctx, assignmentCounts, gameMode);
|
||||
nextRoundPicks[player.Index] = pick;
|
||||
|
||||
if (pick.Skill != Skills.None)
|
||||
|
|
@ -1116,6 +1305,8 @@ namespace src.player
|
|||
else assignmentCounts[sp.Skill] = 1;
|
||||
}
|
||||
|
||||
PickContext? pickContext = null;
|
||||
|
||||
foreach (var player in validPlayers)
|
||||
{
|
||||
if (player == null) continue;
|
||||
|
|
@ -1126,6 +1317,7 @@ namespace src.player
|
|||
if (skillPlayer == null) continue;
|
||||
|
||||
skillPlayer.IsDrawing = false;
|
||||
skillPlayer.HudOnDeathBlocked = null;
|
||||
if (player.PlayerPawn.Value == null || !player.PlayerPawn.IsValid)
|
||||
{
|
||||
skillPlayer.Skill = Skills.None;
|
||||
|
|
@ -1142,7 +1334,10 @@ namespace src.player
|
|||
if (nextRoundPicks.TryGetValue(player.Index, out var pre) && IsPickStillValid(pre, player, validPlayers, assignmentCounts))
|
||||
randomSkill = pre;
|
||||
else
|
||||
randomSkill = PickSkillForPlayer(player, skillPlayer, validPlayers, assignmentCounts, gameMode);
|
||||
{
|
||||
pickContext ??= BuildPickContext(validPlayers);
|
||||
randomSkill = PickSkillForPlayer(player, skillPlayer, pickContext, assignmentCounts, gameMode);
|
||||
}
|
||||
}
|
||||
else if (gameMode == Config.GameModes.TeamSkills)
|
||||
randomSkill = player.Team == CsTeam.Terrorist ? tSkill : ctSkill;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,11 @@ namespace src.player
|
|||
long perfStart = PerfLog.Start();
|
||||
// Shared per-tick controller snapshot: the skill OnTick loop already scans the
|
||||
// player list this frame, so reuse that native scan instead of running a second one.
|
||||
var now = DateTime.Now;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (player != null && player.IsValid)
|
||||
UpdatePlayerHud(player);
|
||||
UpdatePlayerHud(player, now);
|
||||
}
|
||||
PerfLog.Sample("OnTick(hud)", perfStart);
|
||||
});
|
||||
|
|
@ -70,7 +71,7 @@ namespace src.player
|
|||
Instance.GameRules.GameRestart = Instance.GameRules.RestartRoundTime < Server.CurrentTime;
|
||||
}
|
||||
|
||||
private static void UpdatePlayerHud(CCSPlayerController player)
|
||||
private static void UpdatePlayerHud(CCSPlayerController player, DateTime now)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.IsBot) return;
|
||||
|
||||
|
|
@ -81,9 +82,11 @@ namespace src.player
|
|||
var skillPlayer = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index);
|
||||
if (skillPlayer == null || !skillPlayer.DisplayHUD) return;
|
||||
|
||||
var now = DateTime.Now;
|
||||
if (skillPlayer.HudSuppressedUntil > now) return;
|
||||
|
||||
if (player.PawnIsAlive && skillPlayer.SkillHudExpired < now) return;
|
||||
if (player.PawnIsAlive && skillPlayer.SkillHudExpired < now && string.IsNullOrEmpty(skillPlayer.PrintHTML)) return;
|
||||
|
||||
if (SkillUtils.HasMenu(player)) return;
|
||||
|
||||
string infoLine = string.Empty;
|
||||
string skillLine = string.Empty;
|
||||
|
|
@ -135,17 +138,19 @@ namespace src.player
|
|||
}
|
||||
else
|
||||
{
|
||||
if ((player.Team is CsTeam.Spectator or CsTeam.None && Config.LoadedConfig.DisableSpectateHUD)
|
||||
|| AdminManager.PlayerHasPermissions(player, Config.LoadedConfig.DisableHUDOnDeathPermission))
|
||||
if (player.Team is CsTeam.Spectator or CsTeam.None && Config.LoadedConfig.DisableSpectateHUD)
|
||||
return;
|
||||
|
||||
skillPlayer.HudOnDeathBlocked ??= AdminManager.PlayerHasPermissions(player, Config.LoadedConfig.DisableHUDOnDeathPermission);
|
||||
if (skillPlayer.HudOnDeathBlocked == true) return;
|
||||
|
||||
var pawn = player.Pawn.Value;
|
||||
if (pawn?.ObserverServices == null) return;
|
||||
|
||||
var observerTarget = pawn.ObserverServices.ObserverTarget?.Value;
|
||||
if (observerTarget == null || !observerTarget.IsValid) return;
|
||||
|
||||
var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p =>
|
||||
var observedPlayer = PlayerManager.GetTickPlayers().FirstOrDefault(p =>
|
||||
p != null && p.IsValid && p.Pawn?.Value?.Handle == observerTarget.Handle);
|
||||
|
||||
if (observedPlayer == null) return;
|
||||
|
|
@ -185,7 +190,6 @@ namespace src.player
|
|||
}
|
||||
|
||||
if (string.IsNullOrEmpty(skillLine)) return;
|
||||
if (SkillUtils.HasMenu(player)) return;
|
||||
|
||||
Event.UpdateSkillHUD(player, infoLine, skillLine, remainingLine, isDescription);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,17 +13,17 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
int hitgroup = @event.Hitgroup;
|
||||
|
||||
if (victim == null || !victim.IsValid || attacker == null || !attacker.IsValid || attacker == victim) return;
|
||||
if (victim == null || !victim.IsValid || attacker == null || !attacker.IsValid || attacker == victim) return false;
|
||||
if (@event.Hitgroup != (int)HitGroup_t.HITGROUP_HEAD) return false;
|
||||
if (PlayerManager.GetPlayerByIndex(victim.Index)?.Skill != skillName) return false;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(victim.Index);
|
||||
if (playerInfo?.Skill == skillName && hitgroup == (int)HitGroup_t.HITGROUP_HEAD)
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8B4513", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
||||
|
|
@ -7,32 +8,48 @@ namespace src.player.skills
|
|||
public class Assassin : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Assassin;
|
||||
private static readonly string[] nades = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade"];
|
||||
private static readonly string[] nadeWeapons =
|
||||
[
|
||||
"weapon_inferno", "weapon_molotov", "weapon_incgrenade", "weapon_flashbang",
|
||||
"weapon_smokegrenade", "weapon_decoy", "weapon_hegrenade"
|
||||
];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static void OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
var damage = @event.DmgHealth;
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var weapon = @event.Weapon;
|
||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
||||
|
||||
if (victim == null || !victim.IsValid || attacker == null || !attacker.IsValid || attacker == victim) return;
|
||||
if (string.IsNullOrEmpty(weapon) || nades.Contains(weapon)) return;
|
||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
||||
return;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
||||
|
||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||
return;
|
||||
|
||||
if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null)
|
||||
return;
|
||||
|
||||
var attacker = PlayerManager.GetPlayerEvent(attackerPawn.Controller.Value.As<CCSPlayerController>());
|
||||
var victim = PlayerManager.GetPlayerEvent(victimPawn.Controller.Value.As<CCSPlayerController>());
|
||||
if (attacker == null || !attacker.IsValid || victim == null || !victim.IsValid) return;
|
||||
if (attacker.Index == victim.Index) return;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
|
||||
var weapon = param2.Ability?.Value;
|
||||
if (weapon == null || !weapon.IsValid) return;
|
||||
if (nadeWeapons.Contains(weapon.DesignerName)) return;
|
||||
|
||||
if (IsBehind(attacker, victim))
|
||||
{
|
||||
var victimPawn = victim.PlayerPawn.Value;
|
||||
if (victimPawn != null && victimPawn.IsValid)
|
||||
SkillUtils.TakeHealth(victimPawn, (int)(damage * (SkillsInfo.GetValue<float>(skillName, "damageMultiplier") - 1)));
|
||||
}
|
||||
param2.Damage *= SkillsInfo.GetValue<float>(skillName, "damageMultiplier");
|
||||
}
|
||||
|
||||
private static bool IsBehind(CCSPlayerController attacker, CCSPlayerController victim)
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ namespace src.player.skills
|
|||
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (player == null || !player.IsValid || !SkillUtils.HasMenu(player)) continue;
|
||||
if (player == null || !player.IsValid) continue;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
ConcurrentBag<(string, string)> menuItems = [];
|
||||
|
|
|
|||
|
|
@ -20,10 +20,22 @@ namespace src.player.skills
|
|||
|
||||
public static void NewRound()
|
||||
{
|
||||
decoys.Clear();
|
||||
KillAllDecoys();
|
||||
playersWithSkill.Clear();
|
||||
}
|
||||
|
||||
private static void KillAllDecoys()
|
||||
{
|
||||
foreach (var decoyIndex in decoys.Keys.ToArray())
|
||||
{
|
||||
var decoy = Utilities.GetEntityFromIndex<CDecoyProjectile>((int)decoyIndex);
|
||||
if (decoy != null && decoy.IsValid && decoy.DesignerName == "decoy_projectile")
|
||||
decoy.AddEntityIOEvent("Kill", decoy, delay: 0.1f);
|
||||
}
|
||||
|
||||
decoys.Clear();
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
{
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
|
|
@ -45,7 +57,7 @@ namespace src.player.skills
|
|||
var attackerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(attacker)?.Index ?? attacker.Index));
|
||||
if (attackerInfo?.Skill != skillName) return;
|
||||
|
||||
SkillUtils.TakeHealth(victim!.PlayerPawn.Value, SkillsInfo.GetValue<int>(skillName, "damageDeal"));
|
||||
SkillUtils.TakeHealth(victim!.PlayerPawn.Value, SkillsInfo.GetValue<int>(skillName, "damageDeal"), attacker, "decoy");
|
||||
}
|
||||
|
||||
public static void OnEntitySpawned(CEntityInstance entity)
|
||||
|
|
@ -94,6 +106,7 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 8 != 0) return;
|
||||
|
||||
var keys = decoys.Keys.ToArray();
|
||||
if (keys.Length == 0) return;
|
||||
|
||||
foreach (var decoyIndex in keys)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,20 +45,19 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var damage = @event.DmgHealth;
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
var weapon = @event.Weapon;
|
||||
int hitgroup = @event.Hitgroup;
|
||||
|
||||
if (victim == null || !victim.IsValid || attacker == null || !attacker.IsValid || attacker == victim) return;
|
||||
if (victim == null || !victim.IsValid || attacker == null || !attacker.IsValid || attacker == victim) return false;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(victim.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
if (playerInfo?.Skill != skillName) return false;
|
||||
|
||||
if (string.IsNullOrEmpty(weapon) || noReflectionWeapon.Contains(weapon)) return;
|
||||
if (string.IsNullOrEmpty(weapon) || noReflectionWeapon.Contains(weapon)) return false;
|
||||
|
||||
float chance = (hitgroup == (int)HitGroup_t.HITGROUP_LEFTLEG || hitgroup == (int)HitGroup_t.HITGROUP_RIGHTLEG)
|
||||
? SkillsInfo.GetValue<float>(skillName, "legReflectionChance")
|
||||
|
|
@ -66,10 +65,10 @@ namespace src.player.skills
|
|||
|
||||
var victimPawn = victim.PlayerPawn?.Value;
|
||||
if (victimPawn == null || !victimPawn.IsValid || Instance.Random.NextDouble() > chance)
|
||||
return;
|
||||
return false;
|
||||
|
||||
var weaponServices = victimPawn.WeaponServices;
|
||||
if (weaponServices == null) return;
|
||||
if (weaponServices == null) return false;
|
||||
|
||||
if (weaponServices.ActiveWeapon == null
|
||||
|| !weaponServices.ActiveWeapon.IsValid
|
||||
|
|
@ -77,9 +76,10 @@ namespace src.player.skills
|
|||
|| !weaponServices.ActiveWeapon.Value.IsValid
|
||||
|| (weaponServices.ActiveWeapon.Value.DesignerName != "weapon_knife"
|
||||
&& weaponServices.ActiveWeapon.Value.DesignerName != "weapon_bayonet"))
|
||||
return;
|
||||
return false;
|
||||
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc7504", 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 torseReflectionChance = .95f, float legReflectionChance = .70f, float velocityModifier = .85f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,16 @@ namespace src.player.skills
|
|||
private const Skills skillName = Skills.BunnyHop;
|
||||
private static readonly ConcurrentDictionary<uint, int> playersLastJump = [];
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
playersLastJump.Clear();
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
playersLastJump.TryRemove(playerIndex, out _);
|
||||
}
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
|
|
|
|||
|
|
@ -133,6 +133,8 @@ namespace src.player.skills
|
|||
if (info.TransmitEntities.Contains(entity.Index))
|
||||
info.TransmitEntities.Remove(entity.Index);
|
||||
|
||||
SkillUtils.HideCarriedEntities(info, playerPawn);
|
||||
|
||||
if (bomb == null) continue;
|
||||
|
||||
if (info.TransmitEntities.Contains(bomb.Index))
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace src.player.skills
|
|||
private static readonly ConcurrentDictionary<uint, bool> lastShot = [];
|
||||
private static readonly ConcurrentDictionary<uint, int> hitPlayer = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> targetToPlayer = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -36,6 +37,7 @@ namespace src.player.skills
|
|||
lastShot.Clear();
|
||||
hitPlayer.Clear();
|
||||
playersToTarget.Clear();
|
||||
targetToPlayer.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,10 +46,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => (e.PlayerName, e.Index.ToString()))];
|
||||
|
|
@ -88,6 +91,7 @@ namespace src.player.skills
|
|||
|
||||
targetPlayers.TryAdd(enemy.Index, 0);
|
||||
playersToTarget[player.Index] = enemy.Index;
|
||||
targetToPlayer[enemy.Index] = player.Index;
|
||||
playerInfo.SkillUsed = true;
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
|
||||
|
|
@ -131,13 +135,14 @@ namespace src.player.skills
|
|||
return;
|
||||
|
||||
uint playerIndex = player.Index;
|
||||
int impactTick = Server.TickCount;
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (playerPawn == null || !playerPawn.IsValid) return;
|
||||
|
||||
bool ishitPlayer = hitPlayer.TryGetValue(playerIndex, out int tick) && tick + 10 >= Server.TickCount;
|
||||
bool ishitPlayer = hitPlayer.TryGetValue(playerIndex, out int tick) && tick + 1 >= impactTick;
|
||||
|
||||
if (!lastShot.ContainsKey(playerIndex))
|
||||
{
|
||||
|
|
@ -148,7 +153,8 @@ 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.TakeHealth(playerPawn, SkillsInfo.GetValue<int>(skillName, "damageAfterMiss"));
|
||||
|
||||
SkillUtils.TakeHealth(playerPawn, SkillsInfo.GetValue<int>(skillName, "damageAfterMiss"), GetSkillOwner(playerIndex), "planted_c4");
|
||||
}
|
||||
lastShot.TryRemove(playerIndex, out _);
|
||||
});
|
||||
|
|
@ -158,6 +164,14 @@ namespace src.player.skills
|
|||
});
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetSkillOwner(uint targetIndex)
|
||||
{
|
||||
if (!targetToPlayer.TryGetValue(targetIndex, out uint ownerIndex)) return null;
|
||||
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||
return owner != null && owner.IsValid ? owner : null;
|
||||
}
|
||||
|
||||
public static void OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
||||
|
|
@ -188,12 +202,14 @@ namespace src.player.skills
|
|||
targetPlayers.TryRemove(player.Index, out _);
|
||||
lastShot.TryRemove(player.Index, out _);
|
||||
hitPlayer.TryRemove(player.Index, out _);
|
||||
targetToPlayer.TryRemove(player.Index, out _);
|
||||
|
||||
if (playersToTarget.TryRemove(player.Index, out uint targetIndex))
|
||||
{
|
||||
targetPlayers.TryRemove(targetIndex, out _);
|
||||
lastShot.TryRemove(targetIndex, out _);
|
||||
hitPlayer.TryRemove(targetIndex, out _);
|
||||
targetToPlayer.TryRemove(targetIndex, out _);
|
||||
|
||||
var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex));
|
||||
if (target != null && target.IsValid && target.PawnIsAlive && !SkillUtils.IsFreezeTime())
|
||||
|
|
@ -212,6 +228,8 @@ namespace src.player.skills
|
|||
lastShot.TryRemove(player.Index, out _);
|
||||
hitPlayer.TryRemove(player.Index, out _);
|
||||
|
||||
targetToPlayer.TryRemove(player.Index, out _);
|
||||
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,13 @@ namespace src.player.skills
|
|||
"weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249",
|
||||
"weapon_negev", "weapon_sg556"
|
||||
];
|
||||
private const string chickenModelPath = "models/chicken/chicken.vmdl";
|
||||
private static readonly ConcurrentDictionary<uint, uint> chickens = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
jRandomSkills.Instance.AddToManifest(chickenModelPath);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
|
|
@ -127,7 +129,7 @@ namespace src.player.skills
|
|||
return;
|
||||
|
||||
chickenModel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(chickenModel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
|
||||
chickenModel.SetModel("models/chicken/chicken.vmdl");
|
||||
chickenModel.SetModel(chickenModelPath);
|
||||
chickenModel.Render = Color.FromArgb(255, 255, 255, 255);
|
||||
chickenModel.Teleport(playerPawn.AbsOrigin, playerPawn.AbsRotation, null);
|
||||
chickenModel.DispatchSpawn();
|
||||
|
|
|
|||
|
|
@ -46,17 +46,25 @@ namespace src.player.skills
|
|||
|
||||
plantingPlayers.TryAdd(player!.Index, Server.CurrentTime);
|
||||
|
||||
var anyChillOut = PlayerManager.GetAllPlayers().FirstOrDefault(p => p.Skill == skillName);
|
||||
if (anyChillOut != null)
|
||||
{
|
||||
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
|
||||
if (bombEntities.Count != 0)
|
||||
{
|
||||
var bomb = bombEntities.FirstOrDefault();
|
||||
if (bomb != null)
|
||||
if (!IsAnyOwnerAlive()) return;
|
||||
|
||||
var bomb = PlayerManager.GetTickBomb();
|
||||
if (bomb == null || !bomb.IsValid) return;
|
||||
|
||||
bomb.ArmedTime = Server.CurrentTime + SkillsInfo.GetValue<float>(skillName, "bombArmedTime");
|
||||
}
|
||||
|
||||
private static bool IsAnyOwnerAlive()
|
||||
{
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (player == null || !player.IsValid || !player.PawnIsAlive) continue;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index);
|
||||
if (playerInfo?.Skill == skillName) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void BombPlanted(EventBombPlanted @event)
|
||||
|
|
@ -70,6 +78,8 @@ namespace src.player.skills
|
|||
|
||||
public static void OnTick()
|
||||
{
|
||||
if (plantingPlayers.IsEmpty) return;
|
||||
|
||||
float currentTime = Server.CurrentTime;
|
||||
float extraTime = SkillsInfo.GetValue<float>(skillName, "bombArmedTime");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using static src.jRandomSkills;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
|
|
@ -14,19 +14,35 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static void OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
var damage = @event.DmgHealth;
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
var weapon = @event.Weapon;
|
||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
||||
return;
|
||||
|
||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
||||
|
||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||
return;
|
||||
|
||||
if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null)
|
||||
return;
|
||||
|
||||
var attacker = PlayerManager.GetPlayerEvent(attackerPawn.Controller.Value.As<CCSPlayerController>());
|
||||
if (attacker == null || !attacker.IsValid) return;
|
||||
if (attacker.Index == victimPawn.Controller.Value.Index) return;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
|
||||
if (weapon == "knife" || weapon == "bayonet")
|
||||
SkillUtils.TakeHealth(victim!.PlayerPawn.Value, 9999);
|
||||
var weapon = param2.Ability?.Value;
|
||||
if (weapon == null || !weapon.IsValid) return;
|
||||
if (weapon.DesignerName != "weapon_knife" && weapon.DesignerName != "weapon_bayonet") return;
|
||||
|
||||
param2.Damage = 9999f;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -54,10 +54,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => (e.PlayerName, e.Index.ToString()))];
|
||||
|
|
|
|||
|
|
@ -27,10 +27,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [];
|
||||
|
|
|
|||
|
|
@ -59,10 +59,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => (e.PlayerName, e.Index.ToString()))];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
|
@ -12,7 +13,7 @@ namespace src.player.skills
|
|||
{
|
||||
private const Skills skillName = Skills.DeathBomb;
|
||||
private static readonly QAngle angle = new(10, -5, 9);
|
||||
private static readonly ConcurrentDictionary<int, byte> nades = [];
|
||||
private static readonly ConcurrentDictionary<int, (byte Team, uint Owner)> nades = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
|
|
@ -63,7 +64,7 @@ namespace src.player.skills
|
|||
var randomFile = fileNames[new Random().Next(fileNames.Length)];
|
||||
player.ExecuteClientCommand($"play vo/agents/balkan/{randomFile}.vsnd");
|
||||
|
||||
nades.AddOrUpdate(Server.TickCount, player.TeamNum, (_, _) => player.TeamNum);
|
||||
nades.AddOrUpdate(Server.TickCount, (player.TeamNum, player.Index), (_, _) => (player.TeamNum, player.Index));
|
||||
}
|
||||
|
||||
public static void OnEntitySpawned(CEntityInstance entity)
|
||||
|
|
@ -86,8 +87,8 @@ namespace src.player.skills
|
|||
heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "explosionRadius");
|
||||
heProjectile.DetonateTime = 0;
|
||||
|
||||
if (nades.TryRemove(lastTick, out byte teamNum))
|
||||
heProjectile.Globalname = $"deathbomb_team_{teamNum}_{heProjectile.Index}";
|
||||
if (nades.TryRemove(lastTick, out var source))
|
||||
heProjectile.Globalname = $"deathbomb_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -104,16 +105,48 @@ namespace src.player.skills
|
|||
if (nade.DesignerName != "hegrenade_projectile") return;
|
||||
if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("deathbomb_team_")) return;
|
||||
|
||||
if (!int.TryParse(nade.Globalname.Split('_')[2], out int nadeTeam)) return;
|
||||
var parts = nade.Globalname.Split('_');
|
||||
if (parts.Length < 4) return;
|
||||
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
||||
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
||||
|
||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
||||
|
||||
if (victimPawn.DesignerName != "player") return;
|
||||
if (victimPawn == null || victimPawn.Controller?.Value == null) return;
|
||||
if (victimPawn.TeamNum != nadeTeam) return;
|
||||
if (victimPawn.Controller?.Value == null) return;
|
||||
|
||||
var victim = victimPawn.Controller.Value.As<CCSPlayerController>();
|
||||
if (victim == null || !victim.IsValid || victim.Index == ownerIndex) return;
|
||||
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||
if (owner != null && !owner.IsValid) owner = null;
|
||||
|
||||
if (victimPawn.TeamNum == nadeTeam)
|
||||
{
|
||||
float reduction = SkillsInfo.GetValue<float>(skillName, "dmgReductionForTeamates");
|
||||
param2.Damage *= 1f - Math.Clamp(reduction, 0f, 1f);
|
||||
|
||||
if (IsFriendlyFireOff())
|
||||
{
|
||||
int teamDamage = (int)param2.Damage;
|
||||
param2.Damage = 0;
|
||||
|
||||
if (teamDamage > 0)
|
||||
SkillUtils.TakeHealth(victimPawn, teamDamage, owner, "hegrenade");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (owner != null && param2.Damage >= victimPawn.Health)
|
||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, "hegrenade");
|
||||
}
|
||||
|
||||
private static bool IsFriendlyFireOff()
|
||||
{
|
||||
bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue<bool>() ?? false;
|
||||
bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue<bool>() ?? false;
|
||||
return !ff && !tae;
|
||||
}
|
||||
|
||||
private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
|
||||
|
|
@ -123,7 +156,7 @@ 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 = .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) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public float ExplosionRadius { get; set; } = explosionRadius;
|
||||
public int ExplosionDamage { get; set; } = explosionDamage;
|
||||
|
|
|
|||
|
|
@ -27,18 +27,41 @@ namespace src.player.skills
|
|||
HealAttacker(attacker!, @event.DmgHealth);
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
var pawn = player.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid) return;
|
||||
|
||||
pawn.MaxHealth = 100;
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iMaxHealth");
|
||||
|
||||
if (pawn.Health > 100)
|
||||
{
|
||||
pawn.Health = 100;
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
|
||||
}
|
||||
}
|
||||
|
||||
private static void HealAttacker(CCSPlayerController attacker, float damage)
|
||||
{
|
||||
var attackerPawn = attacker.PlayerPawn.Value;
|
||||
if (attackerPawn == null) return;
|
||||
var attackerPawn = attacker.PlayerPawn?.Value;
|
||||
if (attackerPawn == null || !attackerPawn.IsValid) return;
|
||||
|
||||
int newHealth = (int)(attackerPawn.Health + (damage * SkillsInfo.GetValue<float>(skillName, "healthRegainScale")));
|
||||
if (attackerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || attackerPawn.Health <= 0) return;
|
||||
|
||||
attackerPawn.MaxHealth = Math.Max(newHealth, 100);
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iMaxHealth");
|
||||
int extraHealth = (int)(damage * SkillsInfo.GetValue<float>(skillName, "healthRegainScale"));
|
||||
if (extraHealth <= 0) return;
|
||||
|
||||
attackerPawn.Health = newHealth;
|
||||
attackerPawn.Health += extraHealth;
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iHealth");
|
||||
|
||||
if (attackerPawn.MaxHealth < attackerPawn.Health)
|
||||
{
|
||||
attackerPawn.MaxHealth = attackerPawn.Health;
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iMaxHealth");
|
||||
}
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FA050D", 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 healthRegainScale = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -27,10 +27,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Index != player.Index && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [];
|
||||
|
|
|
|||
171
jRandomSkills - SRC Files/src/player/skills/ExpensiveAmmo.cs
Normal file
171
jRandomSkills - SRC Files/src/player/skills/ExpensiveAmmo.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class ExpensiveAmmo : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.ExpensiveAmmo;
|
||||
private static readonly ConcurrentDictionary<uint, byte> cursedPlayers = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
cursedPlayers.TryRemove(playerIndex, out _);
|
||||
playersToTarget.TryRemove(playerIndex, out _);
|
||||
|
||||
foreach (var kvp in playersToTarget)
|
||||
if (kvp.Value == playerIndex)
|
||||
playersToTarget.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
cursedPlayers.Clear();
|
||||
playersToTarget.Clear();
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
public static void WeaponFire(EventWeaponFire @event)
|
||||
{
|
||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (!cursedPlayers.ContainsKey(player.Index)) return;
|
||||
|
||||
var moneyServices = player.InGameMoneyServices;
|
||||
if (moneyServices == null) return;
|
||||
|
||||
int cost = SkillsInfo.GetValue<int>(skillName, "moneyPerShot");
|
||||
if (cost <= 0) return;
|
||||
|
||||
moneyServices.Account = Math.Max(moneyServices.Account - cost, 0);
|
||||
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
}
|
||||
|
||||
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 = GetSelectableEnemies(player);
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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;
|
||||
}
|
||||
|
||||
string enemyId = commands[0];
|
||||
|
||||
if (!uint.TryParse(enemyId, 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 || enemy.PlayerPawn.Value == null || !enemy.PlayerPawn.Value.IsValid)
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
|
||||
return;
|
||||
}
|
||||
|
||||
cursedPlayers.TryAdd(enemy.Index, 0);
|
||||
playersToTarget[player.Index] = enemy.Index;
|
||||
playerInfo.SkillUsed = true;
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
|
||||
if (enemyEvent == null || !enemyEvent.IsValid) return;
|
||||
|
||||
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("expensiveammo_player_info", enemy.PlayerName));
|
||||
enemyEvent.PrintToChat($" {ChatColors.Red}" + enemyEvent.GetTranslation("expensiveammo_enemy_info", SkillsInfo.GetValue<int>(skillName, "moneyPerShot")));
|
||||
}
|
||||
|
||||
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 = GetSelectableEnemies(player);
|
||||
if (enemies.Length > 0)
|
||||
{
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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 (playersToTarget.TryRemove(player.Index, out uint targetIndex))
|
||||
{
|
||||
cursedPlayers.TryRemove(targetIndex, out _);
|
||||
|
||||
var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex));
|
||||
if (target != null && target.IsValid && target.PawnIsAlive && !SkillUtils.IsFreezeTime())
|
||||
target.PrintToChat($" {ChatColors.Green}" + target.GetTranslation("expensiveammo_disable_info"));
|
||||
}
|
||||
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
public static void PlayerDeath(EventPlayerDeath @event)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
cursedPlayers.TryRemove(player.Index, out _);
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
private static CCSPlayerController[] GetSelectableEnemies(CCSPlayerController player)
|
||||
{
|
||||
return [.. PlayerManager.GetTickPlayers()
|
||||
.Where(p => p != null && p.IsValid)
|
||||
.Select(PlayerManager.GetPlayerEvent)
|
||||
.Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None)
|
||||
.Cast<CCSPlayerController>()];
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0c341", 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 moneyPerShot = 50) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public int MoneyPerShot { get; set; } = moneyPerShot;
|
||||
}
|
||||
}
|
||||
}
|
||||
276
jRandomSkills - SRC Files/src/player/skills/ExplodingBarrel.cs
Normal file
276
jRandomSkills - SRC Files/src/player/skills/ExplodingBarrel.cs
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class ExplodingBarrel : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.ExplodingBarrel;
|
||||
private static readonly QAngle angle = new(7, -3, 11);
|
||||
private static readonly ConcurrentDictionary<uint, PlayerSkillInfo> SkillPlayerInfo = [];
|
||||
private static readonly ConcurrentDictionary<uint, byte> consumedBarrels = [];
|
||||
private static readonly ConcurrentDictionary<int, (byte Team, uint Owner)> nades = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
jRandomSkills.Instance.AddToManifest(SkillsInfo.GetValue<string>(skillName, "propModel"));
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
ClearAllBarrels();
|
||||
lock (setLock)
|
||||
{
|
||||
SkillPlayerInfo.Clear();
|
||||
consumedBarrels.Clear();
|
||||
nades.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearAllBarrels()
|
||||
{
|
||||
var entities = Utilities.FindAllEntitiesByDesignerName<CDynamicProp>("prop_dynamic_override");
|
||||
foreach (var entity in entities)
|
||||
if (entity != null && entity.IsValid && entity.Entity != null && (entity.Entity.Name?.StartsWith("Barrel_") ?? false))
|
||||
EntityManager.DestroyEntity(entity.Index);
|
||||
}
|
||||
|
||||
public static void OnTick()
|
||||
{
|
||||
if (SkillPlayerInfo.IsEmpty || Server.TickCount % 8 != 0) return;
|
||||
|
||||
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)
|
||||
UpdateHUD(player, skillInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
SkillPlayerInfo.TryAdd(player.Index, new PlayerSkillInfo
|
||||
{
|
||||
SteamID = player.Index,
|
||||
CanUse = true,
|
||||
Cooldown = DateTime.MinValue,
|
||||
});
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null) return;
|
||||
SkillPlayerInfo.TryRemove(player.Index, out _);
|
||||
EntityManager.DestroyPlayerEntities(player.Index);
|
||||
SkillUtils.ResetPrintHTML(player);
|
||||
}
|
||||
|
||||
private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo)
|
||||
{
|
||||
float cooldown = 0;
|
||||
if (skillInfo != null)
|
||||
{
|
||||
float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue<float>(skillName, "cooldown")) - DateTime.Now).TotalSeconds);
|
||||
cooldown = Math.Max(time, 0);
|
||||
|
||||
if (cooldown == 0 && skillInfo?.CanUse == false)
|
||||
skillInfo.CanUse = true;
|
||||
}
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo == null) return;
|
||||
|
||||
if (cooldown == 0)
|
||||
playerInfo.PrintHTML = null;
|
||||
else
|
||||
playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"<font color='#FF0000'>{cooldown}</font>")}";
|
||||
}
|
||||
|
||||
public static void UseSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
||||
|
||||
var playerPawn = player.PlayerPawn?.Value;
|
||||
if (playerPawn?.CBodyComponent == null) return;
|
||||
|
||||
if (SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo) && skillInfo.CanUse)
|
||||
{
|
||||
skillInfo.CanUse = false;
|
||||
skillInfo.Cooldown = DateTime.Now;
|
||||
CreateBarrel(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateBarrel(CCSPlayerController player)
|
||||
{
|
||||
var playerPawn = player.PlayerPawn?.Value;
|
||||
if (playerPawn == null || !playerPawn.IsValid || playerPawn.AbsOrigin == null || playerPawn.AbsRotation == null)
|
||||
return;
|
||||
|
||||
var barrel = EntityManager.CreateTrackedPropOverride(player.Index);
|
||||
if (barrel == null || !barrel.IsValid)
|
||||
return;
|
||||
|
||||
consumedBarrels.TryRemove(barrel.Index, out _);
|
||||
|
||||
float distance = 50;
|
||||
Vector pos = playerPawn.AbsOrigin + SkillUtils.GetForwardVector(playerPawn.AbsRotation) * distance;
|
||||
QAngle rotation = new(playerPawn.AbsRotation.X, playerPawn.AbsRotation.Y, playerPawn.AbsRotation.Z);
|
||||
|
||||
barrel.Entity!.Name = barrel.Globalname = $"Barrel_{Server.TickCount}_{player.TeamNum}_{player.Index}";
|
||||
barrel.Collision.SolidType = SolidType_t.SOLID_VPHYSICS;
|
||||
barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
|
||||
barrel.DispatchSpawn();
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (barrel == null || !barrel.IsValid) return;
|
||||
barrel.SetModel(SkillsInfo.GetValue<string>(skillName, "propModel"));
|
||||
barrel.Teleport(pos, rotation, null);
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnEntitySpawned(CEntityInstance entity)
|
||||
{
|
||||
if (entity.DesignerName != "hegrenade_projectile") return;
|
||||
|
||||
var heProjectile = entity.As<CBaseCSGrenadeProjectile>();
|
||||
if (heProjectile == null || !heProjectile.IsValid || heProjectile.AbsRotation == null) return;
|
||||
|
||||
int lastTick = Server.TickCount;
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (heProjectile == null || !heProjectile.IsValid) return;
|
||||
if (!(NearlyEquals(angle.X, heProjectile.AbsRotation.X) && NearlyEquals(angle.Y, heProjectile.AbsRotation.Y) && NearlyEquals(angle.Z, heProjectile.AbsRotation.Z)))
|
||||
return;
|
||||
|
||||
heProjectile.TicksAtZeroVelocity = 100;
|
||||
heProjectile.Damage = SkillsInfo.GetValue<int>(skillName, "explosionDamage");
|
||||
heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "explosionRadius");
|
||||
heProjectile.DetonateTime = 0;
|
||||
|
||||
if (nades.TryRemove(lastTick, out var source))
|
||||
heProjectile.Globalname = $"barrel_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
||||
|
||||
if (param == null || param.Entity == null || param2 == null) return;
|
||||
|
||||
if (HandleBarrelHit(param)) return;
|
||||
HandleNadeDamage(param, param2);
|
||||
}
|
||||
|
||||
private static bool HandleBarrelHit(CEntityInstance param)
|
||||
{
|
||||
if (string.IsNullOrEmpty(param.Entity!.Name)) return false;
|
||||
if (!param.Entity.Name.StartsWith("Barrel_")) return false;
|
||||
|
||||
var barrel = param.As<CDynamicProp>();
|
||||
if (barrel == null || !barrel.IsValid) return true;
|
||||
if (!consumedBarrels.TryAdd(barrel.Index, 0)) return true;
|
||||
|
||||
var nameParts = param.Entity.Name.Split('_');
|
||||
if (nameParts.Length < 4) return true;
|
||||
if (!int.TryParse(nameParts[2], out int teamNum)) return true;
|
||||
if (!uint.TryParse(nameParts[3], out uint ownerIndex)) return true;
|
||||
|
||||
if (barrel.AbsOrigin == null) return true;
|
||||
|
||||
float x = barrel.AbsOrigin.X;
|
||||
float y = barrel.AbsOrigin.Y;
|
||||
float z = barrel.AbsOrigin.Z + 32;
|
||||
|
||||
EntityManager.DestroyEntity(barrel.Index, 0f);
|
||||
|
||||
jRandomSkills.Instance.AddTickTimer(4, () =>
|
||||
{
|
||||
SkillUtils.CreateHEGrenadeProjectile(new Vector(x, y, z), angle, new Vector(0, 0, -10), teamNum);
|
||||
nades.AddOrUpdate(Server.TickCount, ((byte)teamNum, ownerIndex), (_, _) => ((byte)teamNum, ownerIndex));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void HandleNadeDamage(CEntityInstance param, CTakeDamageInfo param2)
|
||||
{
|
||||
var nade = param2.Attacker?.Value;
|
||||
if (nade == null || !nade.IsValid) return;
|
||||
if (nade.DesignerName != "hegrenade_projectile") return;
|
||||
if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("barrel_team_")) return;
|
||||
|
||||
var parts = nade.Globalname.Split('_');
|
||||
if (parts.Length < 4) return;
|
||||
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
||||
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
||||
|
||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
||||
if (victimPawn.DesignerName != "player" || victimPawn.Controller?.Value == null) return;
|
||||
|
||||
var victim = victimPawn.Controller.Value.As<CCSPlayerController>();
|
||||
if (victim == null || !victim.IsValid || victim.Index == ownerIndex) return;
|
||||
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||
if (owner != null && !owner.IsValid) owner = null;
|
||||
|
||||
if (victimPawn.TeamNum == nadeTeam)
|
||||
{
|
||||
float reduction = SkillsInfo.GetValue<float>(skillName, "dmgReductionForTeamates");
|
||||
param2.Damage *= 1f - Math.Clamp(reduction, 0f, 1f);
|
||||
|
||||
if (IsFriendlyFireOff())
|
||||
{
|
||||
int teamDamage = (int)param2.Damage;
|
||||
param2.Damage = 0;
|
||||
|
||||
if (teamDamage > 0)
|
||||
SkillUtils.TakeHealth(victimPawn, teamDamage, owner, "planted_c4");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (owner != null && param2.Damage >= victimPawn.Health)
|
||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, "planted_c4");
|
||||
}
|
||||
|
||||
private static bool IsFriendlyFireOff()
|
||||
{
|
||||
bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue<bool>() ?? false;
|
||||
bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue<bool>() ?? false;
|
||||
return !ff && !tae;
|
||||
}
|
||||
|
||||
private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon;
|
||||
|
||||
public class PlayerSkillInfo
|
||||
{
|
||||
public ulong SteamID { get; set; }
|
||||
public bool CanUse { get; set; }
|
||||
public DateTime Cooldown { get; set; }
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c0392b", 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 = 20f, float explosionRadius = 210f, int explosionDamage = 50, string propModel = "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl", float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public float Cooldown { get; set; } = cooldown;
|
||||
public float ExplosionRadius { get; set; } = explosionRadius;
|
||||
public int ExplosionDamage { get; set; } = explosionDamage;
|
||||
public string PropModel { get; set; } = propModel;
|
||||
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,11 +11,13 @@ namespace src.player.skills
|
|||
public class FalconEye : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.FalconEye;
|
||||
private const string cameraViewModel = "models/sprays/spray_plane.vmdl";
|
||||
private static readonly ConcurrentDictionary<uint, (uint, uint)> cameras = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
Instance.AddToManifest(cameraViewModel);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
|
|
@ -127,7 +129,7 @@ namespace src.player.skills
|
|||
if (camNode != null)
|
||||
camNode.Flags = (uint)(camNode.Flags & ~(1 << 2));
|
||||
|
||||
camera.SetModel("models/sprays/spray_plane.vmdl");
|
||||
camera.SetModel(cameraViewModel);
|
||||
camera.Render = Color.FromArgb(0, 255, 255, 255);
|
||||
camera.Teleport(pos, new QAngle(90, 0, 0));
|
||||
camera.DispatchSpawn();
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ namespace src.player.skills
|
|||
if (info.TransmitEntities.Contains(entity.Index))
|
||||
info.TransmitEntities.Remove(entity.Index);
|
||||
|
||||
SkillUtils.HideCarriedEntities(info, playerPawn);
|
||||
|
||||
// Hide the bomb as well, but only while this hidden player is the one holding it.
|
||||
if (bomb == null || bombOwnerIndex != playerController.Index) continue;
|
||||
|
||||
|
|
|
|||
194
jRandomSkills - SRC Files/src/player/skills/Giant.cs
Normal file
194
jRandomSkills - SRC Files/src/player/skills/Giant.cs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
using static src.jRandomSkills;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class Giant : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Giant;
|
||||
private static readonly ConcurrentDictionary<uint, uint> enlargedEnemies = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
foreach (var targetIndex in enlargedEnemies.Values.ToArray())
|
||||
ResetScale(targetIndex);
|
||||
|
||||
enlargedEnemies.Clear();
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
if (enlargedEnemies.TryRemove(playerIndex, out uint targetIndex))
|
||||
ResetScale(targetIndex);
|
||||
|
||||
foreach (var kvp in enlargedEnemies)
|
||||
if (kvp.Value == playerIndex)
|
||||
enlargedEnemies.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo == null) return;
|
||||
playerInfo.SkillUsed = false;
|
||||
|
||||
float minScale = SkillsInfo.GetValue<float>(skillName, "minScale");
|
||||
float maxScale = SkillsInfo.GetValue<float>(skillName, "maxScale");
|
||||
playerInfo.SkillChance = (float)Math.Round((float)Instance.Random.NextDouble() * (maxScale - minScale) + minScale, 2);
|
||||
|
||||
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
|
||||
if (playerEvent == null || !playerEvent.IsValid) return;
|
||||
|
||||
var enemies = GetSelectableEnemies(player);
|
||||
if (enemies.Length > 0)
|
||||
{
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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 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 = GetSelectableEnemies(player);
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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 || enemy.Team == player.Team)
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayerManager.GetPlayerByIndex(enemy.Index)?.Skill == Skills.Chicken)
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
|
||||
return;
|
||||
}
|
||||
|
||||
float newSize = playerInfo.SkillChance ?? 1f;
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
if (enlargedEnemies.TryRemove(player.Index, out uint previousTarget))
|
||||
ResetScale(previousTarget, notify: true);
|
||||
|
||||
SkillUtils.ChangePlayerScale(enemy, newSize);
|
||||
enlargedEnemies[player.Index] = enemy.Index;
|
||||
}
|
||||
|
||||
playerInfo.SkillUsed = true;
|
||||
SkillUtils.CloseMenu(player);
|
||||
|
||||
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("giant_player_info", enemy.PlayerName, newSize));
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
|
||||
if (enemyEvent == null || !enemyEvent.IsValid) return;
|
||||
|
||||
enemyEvent.PrintToChat($" {ChatColors.Red}" + enemyEvent.GetTranslation("giant_enemy_info", newSize));
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
if (enlargedEnemies.TryRemove(player.Index, out uint targetIndex))
|
||||
ResetScale(targetIndex, notify: true);
|
||||
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResetScale(uint targetIndex, bool notify = false)
|
||||
{
|
||||
var target = Utilities.GetPlayerFromIndex((int)targetIndex);
|
||||
if (target == null || !target.IsValid) return;
|
||||
|
||||
var pawn = target.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid || pawn.CBodyComponent == null) return;
|
||||
|
||||
SkillUtils.ChangePlayerScale(target, 1);
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_CBodyComponent");
|
||||
|
||||
if (!notify) return;
|
||||
|
||||
var targetEvent = PlayerManager.GetPlayerFromEvent(target);
|
||||
if (targetEvent != null && targetEvent.IsValid && targetEvent.PawnIsAlive && !SkillUtils.IsFreezeTime())
|
||||
targetEvent.PrintToChat($" {ChatColors.Green}" + targetEvent.GetTranslation("giant_disable_info"));
|
||||
}
|
||||
|
||||
private static CCSPlayerController[] GetSelectableEnemies(CCSPlayerController player)
|
||||
{
|
||||
return [.. PlayerManager.GetTickPlayers()
|
||||
.Where(p => p != null && p.IsValid)
|
||||
.Select(PlayerManager.GetPlayerEvent)
|
||||
.Where(p => p != null && p.IsValid && p.Team != player.Team && !p.IsHLTV
|
||||
&& p.Team != CsTeam.Spectator && p.Team != CsTeam.None
|
||||
&& p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0
|
||||
&& PlayerManager.GetPlayerByIndex(p.Index)?.Skill != Skills.Chicken)
|
||||
.Cast<CCSPlayerController>()];
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8ad3ff", 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 minScale = 1.1f, float maxScale = 1.4f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public float MinScale { get; set; } = minScale;
|
||||
public float MaxScale { get; set; } = maxScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,8 @@ namespace src.player.skills
|
|||
|
||||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
||||
{
|
||||
if (smokes.IsEmpty || playersWithSkill.IsEmpty) return;
|
||||
|
||||
foreach (var (info, player) in infoList)
|
||||
{
|
||||
if (player == null || !player.IsValid) continue;
|
||||
|
|
|
|||
|
|
@ -51,10 +51,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using CounterStrikeSharp.API.Core;
|
|||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing;
|
||||
using static src.jRandomSkills;
|
||||
|
||||
namespace src.player.skills
|
||||
|
|
@ -53,12 +54,34 @@ namespace src.player.skills
|
|||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
SkillPlayerInfo.TryRemove(player.Index, out _);
|
||||
SkillPlayerInfo.TryRemove(player.Index, out var skillInfo);
|
||||
SkillUtils.ResetPrintHTML(player);
|
||||
|
||||
var playerPawn = player.PlayerPawn.Value;
|
||||
if (playerPawn != null && playerPawn.IsValid)
|
||||
{
|
||||
playerPawn.TakesDamage = true;
|
||||
RestoreRender(playerPawn, skillInfo?.OriginalRender);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetGodModeRender(CCSPlayerPawn pawn, PlayerSkillInfo skillInfo)
|
||||
{
|
||||
if (pawn == null || !pawn.IsValid) return;
|
||||
|
||||
var current = pawn.Render;
|
||||
skillInfo.OriginalRender ??= current;
|
||||
|
||||
pawn.Render = Color.FromArgb(current.A, 255, 255, 0);
|
||||
Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender");
|
||||
}
|
||||
|
||||
private static void RestoreRender(CCSPlayerPawn? pawn, Color? originalRender)
|
||||
{
|
||||
if (pawn == null || !pawn.IsValid || originalRender == null) return;
|
||||
|
||||
pawn.Render = originalRender.Value;
|
||||
Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender");
|
||||
}
|
||||
|
||||
private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo)
|
||||
|
|
@ -100,11 +123,17 @@ namespace src.player.skills
|
|||
|
||||
PlayerManager.GetPlayerFromEvent(player)?.PrintToChat($" {ChatColors.Green} {player.GetTranslation("godmode_on")}");
|
||||
player.PlayerPawn.Value.TakesDamage = false;
|
||||
SetGodModeRender(player.PlayerPawn.Value, skillInfo);
|
||||
|
||||
Instance.AddTimer(SkillsInfo.GetValue<float>(skillName, "duration"), () =>
|
||||
{
|
||||
Color? originalRender = null;
|
||||
if (SkillPlayerInfo.TryGetValue(playerIndex, out var skillInfo))
|
||||
{
|
||||
skillInfo.HaveGodMode = false;
|
||||
originalRender = skillInfo.OriginalRender;
|
||||
skillInfo.OriginalRender = null;
|
||||
}
|
||||
|
||||
var player = Utilities.GetPlayerFromIndex((int)playerIndex);
|
||||
if (player != null && player.IsValid)
|
||||
|
|
@ -112,6 +141,7 @@ namespace src.player.skills
|
|||
if (player.PlayerPawn == null || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return;
|
||||
|
||||
player.PlayerPawn.Value.TakesDamage = true;
|
||||
RestoreRender(player.PlayerPawn.Value, originalRender);
|
||||
PlayerManager.GetPlayerFromEvent(player)?.PrintToChat($" {ChatColors.Red} {player.GetTranslation("godmode_off")}");
|
||||
|
||||
if (player.PlayerPawn.Value.Health <= 0)
|
||||
|
|
@ -128,6 +158,7 @@ namespace src.player.skills
|
|||
public bool CanUse { get; set; }
|
||||
public bool HaveGodMode { get; set; }
|
||||
public DateTime Cooldown { get; set; }
|
||||
public Color? OriginalRender { get; set; }
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0d83a", 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 = 30f, float duration = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -50,11 +50,19 @@ namespace src.player.skills
|
|||
Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1");
|
||||
Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo");
|
||||
SkillUtils.AddHealth(pawn, SkillsInfo.GetValue<int>(skillName, "healthToAdd"));
|
||||
|
||||
float effectDuration = SkillsInfo.GetValue<float>(skillName, "effectDuration");
|
||||
if (effectDuration > 0)
|
||||
{
|
||||
pawn.HealthShotBoostExpirationTime = Server.CurrentTime + effectDuration;
|
||||
Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_flHealthShotBoostExpirationTime");
|
||||
}
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ded678", 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 healthToAdd = 100) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ded678", 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 healthToAdd = 100, float effectDuration = 1.0f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public int HealthToAdd { get; set; } = healthToAdd;
|
||||
public float EffectDuration { get; set; } = effectDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@ namespace src.player.skills
|
|||
if (players.IsEmpty || jRandomSkills.Instance.GameRules?.FreezePeriod == true) return;
|
||||
int damage = SkillsInfo.GetValue<int>(skillName, "damage");
|
||||
|
||||
var owner = GetSkillOwner();
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid && p.Team == CsTeam.Terrorist && p.PlayerPawn?.Value?.Health > 0))
|
||||
{
|
||||
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
|
||||
|
|
@ -37,11 +39,22 @@ namespace src.player.skills
|
|||
bool hasC4 = pawn.WeaponServices.MyWeapons.Any(w => w.Value?.DesignerName == "weapon_c4");
|
||||
if (!hasC4) continue;
|
||||
|
||||
SkillUtils.TakeHealth(pawn, damage);
|
||||
SkillUtils.TakeHealth(pawn, damage, owner, "planted_c4");
|
||||
playerEvent?.ExecuteClientCommand($"play player/player_damagebody_0{jRandomSkills.Instance.Random.Next(4, 8)}");
|
||||
}
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetSkillOwner()
|
||||
{
|
||||
foreach (var ownerIndex in players.Keys)
|
||||
{
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||
if (owner != null && owner.IsValid) return owner;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void WeaponPickup(EventItemPickup @event)
|
||||
{
|
||||
var weapon = @event.Item;
|
||||
|
|
@ -49,7 +62,7 @@ namespace src.player.skills
|
|||
|
||||
// Gate on the bomb being hot (red), not on `players`: the fresh round's C4 is
|
||||
// handed out before NewRound clears `players`, but a new C4 spawns white, so colour is race-free.
|
||||
var bomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||
var bomb = PlayerManager.GetTickBomb();
|
||||
if (bomb == null || !bomb.IsValid) return;
|
||||
if (bomb.Render.R != 255 || bomb.Render.G != 0 || bomb.Render.B != 0) return;
|
||||
|
||||
|
|
@ -63,11 +76,8 @@ namespace src.player.skills
|
|||
{
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
|
||||
if (bombEntities.Count == 0) return;
|
||||
|
||||
var bomb = bombEntities.FirstOrDefault();
|
||||
if (bomb == null) return;
|
||||
var bomb = PlayerManager.GetTickBomb();
|
||||
if (bomb == null || !bomb.IsValid) return;
|
||||
|
||||
bomb.Render = color;
|
||||
Utilities.SetStateChanged(bomb, "CBaseModelEntity", "m_clrRender");
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace src.player.skills
|
|||
{
|
||||
private const Skills skillName = Skills.Iana;
|
||||
private static readonly ConcurrentDictionary<uint, PlayerSkill> playersInfo = [];
|
||||
private static readonly ConcurrentDictionary<uint, byte> consumedClones = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -27,7 +28,10 @@ namespace src.player.skills
|
|||
KillClone(playerSkill);
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
playersInfo.Clear();
|
||||
consumedClones.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void WeaponPickup(EventItemPickup @event)
|
||||
|
|
@ -68,7 +72,7 @@ namespace src.player.skills
|
|||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.CameraServices == null) return;
|
||||
if (player == null || !player.IsValid) return;
|
||||
playersInfo.TryAdd(player.Index, new PlayerSkill
|
||||
{
|
||||
PlayerIndex = player.Index,
|
||||
|
|
@ -203,6 +207,8 @@ namespace src.player.skills
|
|||
playerSkill.NextUse = Server.TickCount + 64000;
|
||||
playerSkill.CloneProp = clone.Index;
|
||||
|
||||
consumedClones.TryRemove(clone.Index, out _);
|
||||
|
||||
jRandomSkills.Instance.AddTimer(SkillsInfo.GetValue<float>(skillName, "Duration"), () =>
|
||||
{
|
||||
if (playerSkill.CloneProp != null)
|
||||
|
|
@ -283,6 +289,19 @@ namespace src.player.skills
|
|||
return calculatedDamage;
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetShooter(CTakeDamageInfo info)
|
||||
{
|
||||
var attackerEnt = info.Attacker?.Value;
|
||||
if (attackerEnt == null || !attackerEnt.IsValid) return null;
|
||||
|
||||
var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle);
|
||||
if (!attackerPawn.IsValid || attackerPawn.DesignerName != "player") return null;
|
||||
if (attackerPawn.Controller?.Value == null) return null;
|
||||
|
||||
var shooter = PlayerManager.GetPlayerEvent(attackerPawn.Controller.Value.As<CCSPlayerController>());
|
||||
return shooter != null && shooter.IsValid ? shooter : null;
|
||||
}
|
||||
|
||||
public static void OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
||||
|
|
@ -327,11 +346,14 @@ namespace src.player.skills
|
|||
dealDamage = CalculateDamage(player, weapon, damage, isHead);
|
||||
}
|
||||
|
||||
if (playerSkill.CloneProp != null && !consumedClones.TryAdd(playerSkill.CloneProp.Value, 0))
|
||||
return;
|
||||
|
||||
KillClone(playerSkill);
|
||||
|
||||
var playerPawn = player.PlayerPawn.Value;
|
||||
if (playerPawn != null)
|
||||
SkillUtils.TakeHealth(playerPawn, (int)dealDamage);
|
||||
SkillUtils.TakeHealth(playerPawn, (int)dealDamage, GetShooter(param2), param2.Ability?.Value?.DesignerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -408,9 +430,8 @@ namespace src.player.skills
|
|||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo?.Skill != skillName) return false;
|
||||
|
||||
if (playersInfo.TryGetValue(player.Index, out var playerSkill))
|
||||
if (playerSkill.CloneProp == null || playerSkill.Weapons.Contains(econItem.ItemID))
|
||||
return false;
|
||||
if (!playersInfo.TryGetValue(player.Index, out var playerSkill)) return false;
|
||||
if (playerSkill.CloneProp == null || playerSkill.Weapons.Contains(econItem.ItemID)) return false;
|
||||
|
||||
hook.SetReturn(AcquireResult.InvalidItem);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ namespace src.player.skills
|
|||
private static int lastChange = 0;
|
||||
private static readonly object offsetLock = new();
|
||||
|
||||
private static int holdersTick = int.MinValue;
|
||||
private static readonly HashSet<uint> holders = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
|
|
@ -49,17 +52,28 @@ namespace src.player.skills
|
|||
if (!isActive || player == null || !player.IsValid) return false;
|
||||
if (player.Team == CsTeam.Spectator) return false;
|
||||
|
||||
var playersWithSkill = jRandomSkills.Instance.SkillPlayer.Where(p => p.Skill == skillName).Select(p => p.PlayerIndex).ToHashSet();
|
||||
if (playersWithSkill.Count == 0) return false;
|
||||
int tick = Server.TickCount;
|
||||
if (tick != holdersTick)
|
||||
{
|
||||
holdersTick = tick;
|
||||
holders.Clear();
|
||||
foreach (var p in jRandomSkills.Instance.SkillPlayer)
|
||||
if (p.Skill == skillName) holders.Add(p.PlayerIndex);
|
||||
}
|
||||
|
||||
return PlayerManager.GetTickPlayers().Any(
|
||||
p => p != null &&
|
||||
p.IsValid &&
|
||||
p.Pawn?.Value != null &&
|
||||
p.PlayerPawn?.Value != null &&
|
||||
p.PlayerPawn.Value.Health > 0 &&
|
||||
p.Team != player.Team &&
|
||||
playersWithSkill.Contains(p.Index));
|
||||
if (holders.Count == 0) return false;
|
||||
|
||||
foreach (var p in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (p == null || !p.IsValid) continue;
|
||||
if (p.Team == player.Team || !holders.Contains(p.Index)) continue;
|
||||
if (p.Pawn?.Value == null) continue;
|
||||
|
||||
var pawn = p.PlayerPawn?.Value;
|
||||
if (pawn != null && pawn.Health > 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string? GetRandomText(string? input)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace src.player.skills
|
|||
private const Skills skillName = Skills.Illusionist;
|
||||
private static readonly ConcurrentDictionary<uint, PlayerSkillInfo> SkillPlayerInfo = [];
|
||||
private static readonly ConcurrentDictionary<int, Timer> ActiveTimers = [];
|
||||
private static readonly ConcurrentDictionary<uint, byte> consumedReplicas = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -26,7 +27,10 @@ namespace src.player.skills
|
|||
{
|
||||
ClearAllReplicas();
|
||||
lock (setLock)
|
||||
{
|
||||
SkillPlayerInfo.Clear();
|
||||
consumedReplicas.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearAllReplicas()
|
||||
|
|
@ -113,6 +117,8 @@ namespace src.player.skills
|
|||
var replica = EntityManager.CreateTrackedPropOverride(player.Index);
|
||||
if (replica == null || !replica.IsValid) return;
|
||||
|
||||
consumedReplicas.TryRemove(replica.Index, out _);
|
||||
|
||||
replica.Collision.SolidType = SolidType_t.SOLID_VPHYSICS;
|
||||
replica.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(replica.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
|
||||
|
||||
|
|
@ -189,8 +195,12 @@ namespace src.player.skills
|
|||
var replica = param.As<CDynamicProp>();
|
||||
if (replica == null || !replica.IsValid) return;
|
||||
|
||||
if (!consumedReplicas.TryAdd(replica.Index, 0)) return;
|
||||
|
||||
replica.EmitSound("GlassBottle.BulletImpact", volume: 1f);
|
||||
if (ActiveTimers.TryRemove((int)replica.Index, out var timer)) timer?.Kill();
|
||||
|
||||
var owner = GetReplicaOwner(replica.Index);
|
||||
EntityManager.DestroyEntity(replica.Index);
|
||||
|
||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
||||
|
|
@ -198,7 +208,17 @@ namespace src.player.skills
|
|||
|
||||
var attackerTeam = attackerPawn.TeamNum;
|
||||
var replicaTeam = replica.Globalname.EndsWith("CT") ? 3 : 2;
|
||||
SkillUtils.TakeHealth(attackerPawn, attackerTeam != replicaTeam ? SkillsInfo.GetValue<int>(skillName, "EnemyTeamDamage") : SkillsInfo.GetValue<int>(skillName, "YourTeamDamage"));
|
||||
|
||||
SkillUtils.TakeHealth(attackerPawn, attackerTeam != replicaTeam ? SkillsInfo.GetValue<int>(skillName, "EnemyTeamDamage") : SkillsInfo.GetValue<int>(skillName, "YourTeamDamage"), owner, "planted_c4");
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetReplicaOwner(uint replicaIndex)
|
||||
{
|
||||
var ownerIndex = EntityManager.GetEntityOwner(replicaIndex);
|
||||
if (ownerIndex == null) return null;
|
||||
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex.Value);
|
||||
return owner != null && owner.IsValid ? owner : null;
|
||||
}
|
||||
|
||||
public class PlayerSkillInfo
|
||||
|
|
|
|||
|
|
@ -59,10 +59,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||
|
|
@ -111,7 +112,7 @@ namespace src.player.skills
|
|||
SetCrosshair(enemyEvent, false);
|
||||
playerInfo.SkillUsed = true;
|
||||
|
||||
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("jammer_player_info", player.PlayerName));
|
||||
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("jammer_player_info", enemy.PlayerName));
|
||||
enemyEvent.PrintToChat($" {ChatColors.Red}" + enemyEvent.GetTranslation("jammer_enemy_info"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,28 +120,19 @@ namespace src.player.skills
|
|||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (!Instance.IsPlayerValid(victim)) return false;
|
||||
|
||||
if (!IsActiveJester(victim!.Index))
|
||||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (!Instance.IsPlayerValid(attacker) || !IsActiveJester(attacker!.Index)) return false;
|
||||
}
|
||||
|
||||
if (!Instance.IsPlayerValid(victim)) return;
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker))
|
||||
{
|
||||
if (IsActiveJester(victim!.Index))
|
||||
{
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
RestoreArmor(victim, @event.DmgArmor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsActiveJester(victim!.Index) || IsActiveJester(attacker!.Index))
|
||||
{
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
RestoreArmor(victim, @event.DmgArmor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsActiveJester(uint playerIndex)
|
||||
|
|
@ -150,17 +141,6 @@ namespace src.player.skills
|
|||
return PlayerManager.GetPlayerByIndex(playerIndex)?.Skill == skillName;
|
||||
}
|
||||
|
||||
private static void RestoreArmor(CCSPlayerController? victim, int dmgArmor)
|
||||
{
|
||||
if (victim == null || !victim.IsValid || dmgArmor <= 0) return;
|
||||
|
||||
var pawn = victim.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid) return;
|
||||
|
||||
pawn.ArmorValue += dmgArmor;
|
||||
Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
var minTime = SkillsInfo.GetValue<float>(skillName, "minTime");
|
||||
|
|
|
|||
|
|
@ -64,10 +64,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => (e.PlayerName, e.Index.ToString()))];
|
||||
|
|
|
|||
186
jRandomSkills - SRC Files/src/player/skills/JumpCurse.cs
Normal file
186
jRandomSkills - SRC Files/src/player/skills/JumpCurse.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class JumpCurse : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.JumpCurse;
|
||||
private static readonly ConcurrentDictionary<uint, byte> cursedPlayers = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
cursedPlayers.TryRemove(playerIndex, out _);
|
||||
playersToTarget.TryRemove(playerIndex, out _);
|
||||
|
||||
foreach (var kvp in playersToTarget)
|
||||
if (kvp.Value == playerIndex)
|
||||
playersToTarget.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
cursedPlayers.Clear();
|
||||
playersToTarget.Clear();
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
public static void PlayerJump(EventPlayerJump @event)
|
||||
{
|
||||
if (cursedPlayers.IsEmpty) return;
|
||||
|
||||
var jumper = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (jumper == null || !jumper.IsValid) return;
|
||||
if (jumper.Team != CsTeam.Terrorist && jumper.Team != CsTeam.CounterTerrorist) return;
|
||||
|
||||
var jumperPawn = jumper.PlayerPawn?.Value;
|
||||
if (jumperPawn == null || !jumperPawn.IsValid) return;
|
||||
if (jumperPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || jumperPawn.Health <= 0) return;
|
||||
|
||||
float jumpVelocity = SkillsInfo.GetValue<float>(skillName, "jumpVelocity");
|
||||
|
||||
foreach (var cursedIndex in cursedPlayers.Keys)
|
||||
{
|
||||
if (cursedIndex == jumper.Index) continue;
|
||||
|
||||
var cursed = PlayerManager.GetPlayerEvent(Utilities.GetPlayerFromIndex((int)cursedIndex));
|
||||
if (cursed == null || !cursed.IsValid) continue;
|
||||
if (cursed.Index == jumper.Index || cursed.Team != jumper.Team) continue;
|
||||
|
||||
var pawn = cursed.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid) continue;
|
||||
if (pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || pawn.Health <= 0) continue;
|
||||
if ((pawn.Flags & (uint)PlayerFlags.FL_ONGROUND) == 0) continue;
|
||||
|
||||
pawn.AbsVelocity.Z = jumpVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
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 = GetSelectableEnemies(player);
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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;
|
||||
}
|
||||
|
||||
string enemyId = commands[0];
|
||||
|
||||
if (!uint.TryParse(enemyId, 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 || enemy.PlayerPawn.Value == null || !enemy.PlayerPawn.Value.IsValid)
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
|
||||
return;
|
||||
}
|
||||
|
||||
cursedPlayers.TryAdd(enemy.Index, 0);
|
||||
playersToTarget[player.Index] = enemy.Index;
|
||||
playerInfo.SkillUsed = true;
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
|
||||
if (enemyEvent == null || !enemyEvent.IsValid) return;
|
||||
|
||||
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("jumpcurse_player_info", enemy.PlayerName));
|
||||
enemyEvent.PrintToChat($" {ChatColors.Red}" + enemyEvent.GetTranslation("jumpcurse_enemy_info"));
|
||||
}
|
||||
|
||||
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 = GetSelectableEnemies(player);
|
||||
if (enemies.Length > 0)
|
||||
{
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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 (playersToTarget.TryRemove(player.Index, out uint targetIndex))
|
||||
{
|
||||
cursedPlayers.TryRemove(targetIndex, out _);
|
||||
|
||||
var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex));
|
||||
if (target != null && target.IsValid && target.PawnIsAlive && !SkillUtils.IsFreezeTime())
|
||||
target.PrintToChat($" {ChatColors.Green}" + target.GetTranslation("jumpcurse_disable_info"));
|
||||
}
|
||||
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
public static void PlayerDeath(EventPlayerDeath @event)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
cursedPlayers.TryRemove(player.Index, out _);
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
private static CCSPlayerController[] GetSelectableEnemies(CCSPlayerController player)
|
||||
{
|
||||
return [.. PlayerManager.GetTickPlayers()
|
||||
.Where(p => p != null && p.IsValid)
|
||||
.Select(PlayerManager.GetPlayerEvent)
|
||||
.Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None)
|
||||
.Cast<CCSPlayerController>()];
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7ad1c4", 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 = 301f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public float JumpVelocity { get; set; } = jumpVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ namespace src.player.skills
|
|||
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire") && player!.Team == attacker!.Team) return;
|
||||
|
||||
if (attackerInfo?.Skill == skillName && playerInfo?.Skill != Skills.AntyFlash && player!.PlayerPawn.Value!.FlashDuration >= SkillsInfo.GetValue<float>(skillName, "flashDuration"))
|
||||
SkillUtils.TakeHealth(player.PlayerPawn.Value, 9999);
|
||||
SkillUtils.TakeHealth(player.PlayerPawn.Value, 9999, attacker, "weapon_flashbang");
|
||||
}
|
||||
|
||||
public static void GrenadeThrown(EventGrenadeThrown @event)
|
||||
|
|
|
|||
55
jRandomSkills - SRC Files/src/player/skills/Knockback.cs
Normal file
55
jRandomSkills - SRC Files/src/player/skills/Knockback.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class Knockback : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Knockback;
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void WeaponFire(EventWeaponFire @event)
|
||||
{
|
||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
|
||||
var pawn = player.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid || pawn.Health <= 0) return;
|
||||
if ((pawn.Flags & (uint)PlayerFlags.FL_ONGROUND) != 0) return;
|
||||
|
||||
float force = SkillsInfo.GetValue<float>(skillName, "knockbackUnits");
|
||||
if (force <= 0) return;
|
||||
|
||||
Vector push = SkillUtils.GetForwardVector(pawn.EyeAngles) * -force;
|
||||
|
||||
pawn.AbsVelocity.X += push.X;
|
||||
pawn.AbsVelocity.Y += push.Y;
|
||||
pawn.AbsVelocity.Z += push.Z;
|
||||
|
||||
float maxSpeed = SkillsInfo.GetValue<float>(skillName, "maxSpeed");
|
||||
if (maxSpeed <= 0) return;
|
||||
|
||||
float speed = pawn.AbsVelocity.Length();
|
||||
if (speed <= maxSpeed) return;
|
||||
|
||||
float scale = maxSpeed / speed;
|
||||
pawn.AbsVelocity.X *= scale;
|
||||
pawn.AbsVelocity.Y *= scale;
|
||||
pawn.AbsVelocity.Z *= scale;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff8c42", 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 knockbackUnits = 120f, float maxSpeed = 1200f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public float KnockbackUnits { get; set; } = knockbackUnits;
|
||||
public float MaxSpeed { get; set; } = maxSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,10 +26,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"\u202A{e.PlayerName}\u202C : {e?.PlayerPawn?.Value?.Health ?? 0} HP", e.Index.ToString())));
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ namespace src.player.skills
|
|||
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire") && player.Team == target.Team) return;
|
||||
|
||||
target.PlayerPawn.Value.EmitSound("Player.DamageBody.Onlooker");
|
||||
SkillUtils.TakeHealth(target.PlayerPawn.Value, heavyHit ? Instance.Random.Next(45, 55) : Instance.Random.Next(21, 34));
|
||||
SkillUtils.TakeHealth(target.PlayerPawn.Value, heavyHit ? Instance.Random.Next(45, 55) : Instance.Random.Next(21, 34), player, "weapon_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)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace src.player.skills
|
|||
if (target.Handle == player.Handle) return;
|
||||
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire") && player.Team == target.Team) return;
|
||||
|
||||
SkillUtils.TakeHealth(target.PlayerPawn.Value, 9999);
|
||||
SkillUtils.TakeHealth(target.PlayerPawn.Value, 9999, player, "weapon_taser");
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
|
|
|
|||
|
|
@ -48,10 +48,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||
|
|
|
|||
|
|
@ -26,10 +26,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => ($"\u202A{e.PlayerName}\u202C : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account + e.InGameMoneyServices.CashSpentThisRound)}$", e.Index.ToString()))];
|
||||
|
|
|
|||
247
jRandomSkills - SRC Files/src/player/skills/Nightmare.cs
Normal file
247
jRandomSkills - SRC Files/src/player/skills/Nightmare.cs
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class Nightmare : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Nightmare;
|
||||
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> targetVolumes = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
jRandomSkills.Instance.AddToManifest(SkillsInfo.GetValue<string>(skillName, "postProcessing"));
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
RemoveVolume(playerIndex);
|
||||
playersToTarget.TryRemove(playerIndex, out _);
|
||||
|
||||
foreach (var kvp in playersToTarget)
|
||||
if (kvp.Value == playerIndex)
|
||||
playersToTarget.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
foreach (var targetIndex in targetVolumes.Keys.ToArray())
|
||||
RemoveVolume(targetIndex);
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
SkillUtils.CloseMenu(player);
|
||||
|
||||
targetVolumes.Clear();
|
||||
playersToTarget.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
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 = GetSelectableEnemies(player);
|
||||
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => (e.PlayerName, e.Index.ToString()))];
|
||||
SkillUtils.UpdateMenu(player, menuItems);
|
||||
}
|
||||
}
|
||||
|
||||
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 = GetSelectableEnemies(player);
|
||||
if (enemies.Length > 0)
|
||||
{
|
||||
ConcurrentBag<(string, string)> menuItems = [.. 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 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 (!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;
|
||||
}
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
if (!ApplyVolume(player.Index, enemy))
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("selectplayerskill_incorrect_enemy_index"));
|
||||
return;
|
||||
}
|
||||
|
||||
playersToTarget[player.Index] = enemy.Index;
|
||||
}
|
||||
|
||||
playerInfo.SkillUsed = true;
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
|
||||
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"));
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
if (playersToTarget.TryRemove(player.Index, out uint targetIndex))
|
||||
{
|
||||
RemoveVolume(targetIndex);
|
||||
|
||||
var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex));
|
||||
if (target != null && target.IsValid && target.PawnIsAlive && !SkillUtils.IsFreezeTime())
|
||||
target.PrintToChat($" {ChatColors.Green}" + target.GetTranslation("nightmare_disable_info"));
|
||||
}
|
||||
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PlayerDeath(EventPlayerDeath @event)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
lock (setLock)
|
||||
RemoveVolume(player.Index);
|
||||
}
|
||||
|
||||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
||||
{
|
||||
if (targetVolumes.IsEmpty) return;
|
||||
|
||||
foreach (var (info, viewer) in infoList)
|
||||
{
|
||||
if (viewer == null || !viewer.IsValid) continue;
|
||||
|
||||
foreach (var kvp in targetVolumes)
|
||||
{
|
||||
if (kvp.Key == viewer.Index) continue;
|
||||
if (info.TransmitEntities.Contains(kvp.Value))
|
||||
info.TransmitEntities.Remove(kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ApplyVolume(uint ownerIndex, CCSPlayerController target)
|
||||
{
|
||||
RemoveVolume(target.Index);
|
||||
|
||||
var targetPawn = target.PlayerPawn?.Value;
|
||||
if (targetPawn == null || !targetPawn.IsValid || targetPawn.AbsOrigin == null) return false;
|
||||
|
||||
if (EntityManager.OverBudget()) return false;
|
||||
|
||||
var volume = Utilities.CreateEntityByName<CPostProcessingVolume>("post_processing_volume");
|
||||
if (volume == null || !volume.IsValid || volume.Entity == null) return false;
|
||||
|
||||
var keys = new CEntityKeyValues();
|
||||
try
|
||||
{
|
||||
keys.SetString("targetname", $"Nightmare_{target.Index}");
|
||||
keys.SetString("postprocessing", SkillsInfo.GetValue<string>(skillName, "postProcessing"));
|
||||
keys.SetBool("master", true);
|
||||
keys.SetBool("enableexposure", true);
|
||||
keys.SetFloat("fadetime", SkillsInfo.GetValue<float>(skillName, "fadeTime"));
|
||||
keys.SetFloat("minexposure", SkillsInfo.GetValue<float>(skillName, "minExposure"));
|
||||
keys.SetFloat("maxexposure", SkillsInfo.GetValue<float>(skillName, "maxExposure"));
|
||||
keys.SetFloat("exposurespeedup", 1f);
|
||||
keys.SetFloat("exposurespeeddown", 1f);
|
||||
keys.SetBool("startdisabled", false);
|
||||
keys.SetInt("spawnflags", 4097);
|
||||
keys.SetVector("origin", targetPawn.AbsOrigin);
|
||||
|
||||
volume.DispatchSpawn(keys);
|
||||
}
|
||||
finally
|
||||
{
|
||||
keys.Dispose();
|
||||
}
|
||||
|
||||
if (!volume.IsValid) return false;
|
||||
|
||||
volume.AcceptInput("SetParent", targetPawn, null, "!activator");
|
||||
|
||||
EntityManager.RegisterExisting(volume, ownerIndex, "post_processing_volume");
|
||||
targetVolumes[target.Index] = volume.Index;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void RemoveVolume(uint targetIndex)
|
||||
{
|
||||
if (!targetVolumes.TryRemove(targetIndex, out uint volumeIndex)) return;
|
||||
EntityManager.DestroyEntity(volumeIndex);
|
||||
}
|
||||
|
||||
private static CCSPlayerController[] GetSelectableEnemies(CCSPlayerController player)
|
||||
{
|
||||
return [.. PlayerManager.GetTickPlayers()
|
||||
.Where(p => p != null && p.IsValid)
|
||||
.Select(PlayerManager.GetPlayerEvent)
|
||||
.Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null
|
||||
&& p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV
|
||||
&& p.Team != CsTeam.Spectator && p.Team != CsTeam.None)
|
||||
.Select(p => p!)];
|
||||
}
|
||||
|
||||
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 string PostProcessing { get; set; } = postProcessing;
|
||||
public float FadeTime { get; set; } = fadeTime;
|
||||
public float MinExposure { get; set; } = minExposure;
|
||||
public float MaxExposure { get; set; } = maxExposure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +91,8 @@ namespace src.player.skills
|
|||
if (info.TransmitEntities.Contains(entity.Index))
|
||||
info.TransmitEntities.Remove(entity.Index);
|
||||
|
||||
SkillUtils.HideCarriedEntities(info, playerPawn);
|
||||
|
||||
if (bomb == null || bombOwnerIndex != playerController.Index) continue;
|
||||
|
||||
if (info.TransmitEntities.Contains(bomb.Index))
|
||||
|
|
|
|||
|
|
@ -15,18 +15,19 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
private static readonly HashSet<string> blockedWeapons =
|
||||
["hegrenade", "inferno", "decoy", "flashbang", "smokegrenade", "molotov"];
|
||||
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var damage = @event.DmgHealth;
|
||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
var weapon = @event.Weapon;
|
||||
if (!Instance.IsPlayerValid(player)) return false;
|
||||
|
||||
if (!Instance.IsPlayerValid(player)) return;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
if (!blockedWeapons.Contains(@event.Weapon)) return false;
|
||||
if (PlayerManager.GetPlayerByIndex(player!.Index)?.Skill != skillName) return false;
|
||||
|
||||
if (weapon == "hegrenade" || weapon == "inferno" || weapon == "decoy" || weapon == "flashbang" || weapon == "smokegrenade" || weapon == "molotov")
|
||||
SkillUtils.RestoreHealth(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a38c1a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
|
@ -14,9 +15,12 @@ namespace src.player.skills
|
|||
private static readonly ConcurrentDictionary<uint, byte> holders = [];
|
||||
private static bool noSpreadActive;
|
||||
|
||||
private static bool defaultNoSpread;
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
defaultNoSpread = ConVar.Find("weapon_accuracy_nospread")?.GetPrimitiveValue<bool>() ?? false;
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
|
|
@ -47,7 +51,8 @@ namespace src.player.skills
|
|||
if (noSpreadActive == enabled) return;
|
||||
|
||||
noSpreadActive = enabled;
|
||||
Server.ExecuteCommand($"weapon_accuracy_nospread {(enabled ? 1 : 0)}");
|
||||
bool value = enabled || defaultNoSpread;
|
||||
Server.ExecuteCommand($"weapon_accuracy_nospread {(value ? 1 : 0)}");
|
||||
}
|
||||
|
||||
public static void OnTick()
|
||||
|
|
|
|||
|
|
@ -15,20 +15,17 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
int damage = @event.DmgHealth;
|
||||
var hitgroup = (HitGroup_t)@event.Hitgroup;
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return;
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim!.Index);
|
||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return false;
|
||||
if ((HitGroup_t)@event.Hitgroup == HitGroup_t.HITGROUP_HEAD) return false;
|
||||
if (PlayerManager.GetPlayerByIndex(victim!.Index)?.Skill != skillName) return false;
|
||||
|
||||
HitGroup_t[] disabledHitbox = [HitGroup_t.HITGROUP_LEFTARM, HitGroup_t.HITGROUP_RIGHTARM, HitGroup_t.HITGROUP_LEFTLEG, HitGroup_t.HITGROUP_RIGHTLEG];
|
||||
if (hitgroup != HitGroup_t.HITGROUP_HEAD)
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3c47de", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,22 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"), false);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
phoenixTicks.Clear();
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
phoenixTicks.TryRemove(player.Index, out _);
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
phoenixTicks.TryRemove(playerIndex, out _);
|
||||
}
|
||||
|
||||
public static void OnTakeDamage(DynamicHook h)
|
||||
{
|
||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace src.player.skills
|
|||
private const Skills skillName = Skills.Poison;
|
||||
private static readonly ConcurrentDictionary<uint, byte> poisonedPlayers = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
||||
private static readonly ConcurrentDictionary<uint, uint> targetToPlayer = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -23,7 +24,10 @@ namespace src.player.skills
|
|||
lock (setLock)
|
||||
{
|
||||
poisonedPlayers.TryRemove(playerIndex, out _);
|
||||
playersToTarget.TryRemove(playerIndex, out _);
|
||||
targetToPlayer.TryRemove(playerIndex, out _);
|
||||
|
||||
if (playersToTarget.TryRemove(playerIndex, out uint ownTarget))
|
||||
targetToPlayer.TryRemove(ownTarget, out _);
|
||||
|
||||
foreach (var kvp in playersToTarget)
|
||||
if (kvp.Value == playerIndex)
|
||||
|
|
@ -37,6 +41,7 @@ namespace src.player.skills
|
|||
{
|
||||
poisonedPlayers.Clear();
|
||||
playersToTarget.Clear();
|
||||
targetToPlayer.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +64,8 @@ namespace src.player.skills
|
|||
if (Jester.IsActiveJester(playerIndex)) continue;
|
||||
|
||||
if (pawn.Health <= SkillsInfo.GetValue<int>(skillName, "MinHealth")) continue;
|
||||
SkillUtils.TakeHealth(pawn, SkillsInfo.GetValue<int>(skillName, "Damage"));
|
||||
|
||||
SkillUtils.TakeHealth(pawn, SkillsInfo.GetValue<int>(skillName, "Damage"), GetSkillOwner(playerIndex), "planted_c4");
|
||||
|
||||
if (Server.TickCount % cooldown2 == 0)
|
||||
PlayerManager.GetPlayerFromEvent(player)?.ExecuteClientCommand($"play player/player_damagebody_0{jRandomSkills.Instance.Random.Next(4, 8)}");
|
||||
|
|
@ -69,10 +75,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||
|
|
@ -113,6 +120,7 @@ namespace src.player.skills
|
|||
|
||||
poisonedPlayers.TryAdd(enemy.Index, 0);
|
||||
playersToTarget[player.Index] = enemy.Index;
|
||||
targetToPlayer[enemy.Index] = player.Index;
|
||||
playerInfo.SkillUsed = true;
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerFromEvent(enemy);
|
||||
|
|
@ -141,11 +149,22 @@ namespace src.player.skills
|
|||
playerEvent.PrintToChat($" {ChatColors.Red}{player.GetTranslation("selectplayerskill_incorrect_enemy_index")}");
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetSkillOwner(uint targetIndex)
|
||||
{
|
||||
if (!targetToPlayer.TryGetValue(targetIndex, out uint ownerIndex)) return null;
|
||||
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||
return owner != null && owner.IsValid ? owner : null;
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
targetToPlayer.TryRemove(player.Index, out _);
|
||||
|
||||
if (playersToTarget.TryRemove(player.Index, out uint targetIndex))
|
||||
{
|
||||
poisonedPlayers.TryRemove(targetIndex, out _);
|
||||
targetToPlayer.TryRemove(targetIndex, out _);
|
||||
|
||||
var target = PlayerManager.GetPlayerFromEvent(Utilities.GetPlayerFromIndex((int)targetIndex));
|
||||
if (target != null && target.IsValid && target.PawnIsAlive && !SkillUtils.IsFreezeTime())
|
||||
|
|
@ -161,6 +180,8 @@ namespace src.player.skills
|
|||
if (player == null || !player.IsValid) return;
|
||||
|
||||
poisonedPlayers.TryRemove(player.Index, out _);
|
||||
targetToPlayer.TryRemove(player.Index, out _);
|
||||
|
||||
SkillUtils.CloseMenu(player);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,10 +70,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||
|
|
|
|||
|
|
@ -14,19 +14,20 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
private static readonly HitGroup_t[] disabledHitbox =
|
||||
[HitGroup_t.HITGROUP_LEFTARM, HitGroup_t.HITGROUP_RIGHTARM, HitGroup_t.HITGROUP_LEFTLEG, HitGroup_t.HITGROUP_RIGHTLEG];
|
||||
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
var hitgroup = (HitGroup_t)@event.Hitgroup;
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return;
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim!.Index);
|
||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return false;
|
||||
if (Array.IndexOf(disabledHitbox, (HitGroup_t)@event.Hitgroup) < 0) return false;
|
||||
if (PlayerManager.GetPlayerByIndex(victim!.Index)?.Skill != skillName) return false;
|
||||
|
||||
HitGroup_t[] disabledHitbox = [HitGroup_t.HITGROUP_LEFTARM, HitGroup_t.HITGROUP_RIGHTARM, HitGroup_t.HITGROUP_LEFTLEG, HitGroup_t.HITGROUP_RIGHTLEG];
|
||||
if (disabledHitbox.Contains(hitgroup))
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c9c9c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -120,10 +120,26 @@ namespace src.player.skills
|
|||
SetPlayerColor(victimPawn, false);
|
||||
SkillUtils.SetHealth(victimPawn, zombieHealth, zombieHealth);
|
||||
victim.ExecuteClientCommand("slot3");
|
||||
|
||||
ApplyTurnTint(victim);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyTurnTint(CCSPlayerController victim)
|
||||
{
|
||||
int alpha = SkillsInfo.GetValue<int>(skillName, "A");
|
||||
if (alpha <= 0) return;
|
||||
|
||||
SkillUtils.ApplyScreenColor(victim,
|
||||
r: SkillsInfo.GetValue<int>(skillName, "R"),
|
||||
g: SkillsInfo.GetValue<int>(skillName, "G"),
|
||||
b: SkillsInfo.GetValue<int>(skillName, "B"),
|
||||
a: alpha,
|
||||
duration: 200,
|
||||
holdTime: 600);
|
||||
}
|
||||
|
||||
private static float GetHeadshotMultiplier(CTakeDamageInfo info)
|
||||
{
|
||||
var ability = info.Ability?.Value;
|
||||
|
|
@ -188,9 +204,13 @@ namespace src.player.skills
|
|||
Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender");
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5C0A", 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 zombieHealth = 500) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5C0A", 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 zombieHealth = 500, int r = 255, int g = 0, int b = 0, int a = 60) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public int ZombieHealth { get; set; } = zombieHealth;
|
||||
public int R { get; set; } = r;
|
||||
public int G { get; set; } = g;
|
||||
public int B { get; set; } = b;
|
||||
public int A { get; set; } = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,26 +34,25 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
public static bool PlayerHurtPre(EventPlayerHurt @event)
|
||||
{
|
||||
var victim = @event.Userid;
|
||||
if (victim == null || !victim.IsValid) return;
|
||||
if (victim == null || !victim.IsValid || !victim.PawnIsAlive) return false;
|
||||
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
if (attacker == null || !attacker.IsValid) return false;
|
||||
|
||||
var victimEvent = PlayerManager.GetPlayerEvent(victim);
|
||||
if (victimEvent == null || !victimEvent.IsValid) return false;
|
||||
|
||||
if (attacker == null || !attacker.IsValid) return;
|
||||
if (victimEvent == null || !victimEvent.IsValid || !victim.PawnIsAlive) return;
|
||||
if (PlayerManager.GetPlayerByIndex(victimEvent.Index)?.Skill != skillName) return false;
|
||||
|
||||
if (PlayerManager.GetPlayerByIndex(victimEvent!.Index)?.Skill != skillName) return;
|
||||
if (!SkillPlayerInfo.TryGetValue(victimEvent.Index, out var skillInfo) || !skillInfo.CanUse) return false;
|
||||
|
||||
if (SkillPlayerInfo.TryGetValue(victimEvent!.Index, out var skillInfo))
|
||||
{
|
||||
if (!skillInfo.CanUse) return;
|
||||
skillInfo.CanUse = false;
|
||||
skillInfo.Cooldown = DateTime.Now;
|
||||
SkillUtils.RestoreHealth(victimEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace src.player.skills
|
|||
{
|
||||
private const Skills skillName = Skills.Replicator;
|
||||
private static readonly ConcurrentDictionary<uint, PlayerSkillInfo> SkillPlayerInfo = [];
|
||||
private static readonly ConcurrentDictionary<uint, byte> consumedReplicas = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -24,7 +25,10 @@ namespace src.player.skills
|
|||
{
|
||||
ClearAllReplicas();
|
||||
lock (setLock)
|
||||
{
|
||||
SkillPlayerInfo.Clear();
|
||||
consumedReplicas.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearAllReplicas()
|
||||
|
|
@ -112,6 +116,8 @@ namespace src.player.skills
|
|||
if (replica == null || !replica.IsValid)
|
||||
return;
|
||||
|
||||
consumedReplicas.TryRemove(replica.Index, out _);
|
||||
|
||||
replica.Flags = playerPawn.Flags;
|
||||
replica.Flags |= (uint)Flags_t.FL_DUCKING;
|
||||
replica.Collision.SolidType = SolidType_t.SOLID_VPHYSICS;
|
||||
|
|
@ -143,7 +149,12 @@ namespace src.player.skills
|
|||
|
||||
var replica = param.As<CPhysicsPropMultiplayer>();
|
||||
if (replica == null || !replica.IsValid) return;
|
||||
|
||||
if (!consumedReplicas.TryAdd(replica.Index, 0)) return;
|
||||
|
||||
replica.EmitSound("GlassBottle.BulletImpact", volume: 1f);
|
||||
|
||||
var owner = GetReplicaOwner(replica.Index);
|
||||
EntityManager.DestroyEntity(replica.Index);
|
||||
|
||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
||||
|
|
@ -152,7 +163,17 @@ namespace src.player.skills
|
|||
|
||||
var attackerTeam = attackerPawn.TeamNum;
|
||||
var replicaTeam = replica.Globalname.EndsWith("CT") ? 3 : 2;
|
||||
SkillUtils.TakeHealth(attackerPawn, attackerTeam != replicaTeam ? SkillsInfo.GetValue<int>(skillName, "EnemyTeamDamage") : SkillsInfo.GetValue<int>(skillName, "YourTeamDamage"));
|
||||
|
||||
SkillUtils.TakeHealth(attackerPawn, attackerTeam != replicaTeam ? SkillsInfo.GetValue<int>(skillName, "EnemyTeamDamage") : SkillsInfo.GetValue<int>(skillName, "YourTeamDamage"), owner, "planted_c4");
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetReplicaOwner(uint replicaIndex)
|
||||
{
|
||||
var ownerIndex = EntityManager.GetEntityOwner(replicaIndex);
|
||||
if (ownerIndex == null) return null;
|
||||
|
||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex.Value);
|
||||
return owner != null && owner.IsValid ? owner : null;
|
||||
}
|
||||
|
||||
public class PlayerSkillInfo
|
||||
|
|
|
|||
|
|
@ -42,18 +42,14 @@ namespace src.player.skills
|
|||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo?.Skill == skillName)
|
||||
{
|
||||
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
|
||||
if (bombEntities.Count != 0)
|
||||
{
|
||||
var bomb = bombEntities.FirstOrDefault();
|
||||
if (bomb != null)
|
||||
var bomb = PlayerManager.GetTickBomb();
|
||||
if (bomb != null && bomb.IsValid)
|
||||
{
|
||||
bomb.BombPlacedAnimation = false;
|
||||
bomb.ArmedTime = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8A2BE2", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ namespace src.player.skills
|
|||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return;
|
||||
|
||||
if (attacker!.Index == victim!.Index) return;
|
||||
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim!.Index);
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ namespace src.player.skills
|
|||
public class Spectator : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Spectator;
|
||||
private const string cameraViewModel = "models/sprays/spray_plane.vmdl";
|
||||
private static readonly ConcurrentDictionary<uint, (uint, uint, uint)> cameras = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
Instance.AddToManifest(cameraViewModel);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
|
|
@ -159,7 +161,7 @@ namespace src.player.skills
|
|||
if (camNode != null)
|
||||
camNode.Flags = (uint)(camNode.Flags & ~(1 << 2));
|
||||
|
||||
camera.SetModel("models/sprays/spray_plane.vmdl");
|
||||
camera.SetModel(cameraViewModel);
|
||||
camera.Render = Color.FromArgb(1, 255, 255, 255);
|
||||
camera.Teleport(pos, angle);
|
||||
camera.DispatchSpawn();
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ namespace src.player.skills
|
|||
public class ThirdEye : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.ThirdEye;
|
||||
private const string cameraViewModel = "models/sprays/spray_plane.vmdl";
|
||||
private static readonly ConcurrentDictionary<uint, (uint, uint)> cameras = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
jRandomSkills.Instance.AddToManifest(cameraViewModel);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
|
|
@ -114,7 +116,7 @@ namespace src.player.skills
|
|||
if (camNode != null)
|
||||
camNode.Flags = (uint)(camNode.Flags & ~(1 << 2));
|
||||
|
||||
camera.SetModel("models/sprays/spray_plane.vmdl");
|
||||
camera.SetModel(cameraViewModel);
|
||||
camera.Render = Color.FromArgb(0, 255, 255, 255);
|
||||
camera.Teleport(pawn.AbsOrigin, pawn.EyeAngles);
|
||||
camera.DispatchSpawn();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace src.player.skills
|
|||
int damage = (int)(@event.DmgHealth * SkillsInfo.GetValue<float>(skillName, "healthTakenScale"));
|
||||
damage = Math.Min(damage, SkillsInfo.GetValue<int>(skillName, "maxTakenDamagePerShot"));
|
||||
|
||||
SkillUtils.TakeHealth(attackerEvent.PlayerPawn.Value, damage);
|
||||
SkillUtils.TakeHealth(attackerEvent.PlayerPawn.Value, damage, victimEvent, "planted_c4");
|
||||
attackerEvent.EmitSound("Player.DamageBody.Onlooker");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,21 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
private static bool roundEnded;
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
roundEnded = false;
|
||||
KillAllKnives();
|
||||
}
|
||||
|
||||
public static void RoundEnd()
|
||||
{
|
||||
roundEnded = true;
|
||||
KillAllKnives();
|
||||
}
|
||||
|
||||
private static void KillAllKnives()
|
||||
{
|
||||
foreach (var knifeInfo in knivesInfo.Values.ToArray())
|
||||
DisableKnifeSkill(knifeInfo);
|
||||
|
|
@ -83,14 +97,16 @@ namespace src.player.skills
|
|||
if (knifeInfo.Timer != null)
|
||||
knifeInfo.Timer?.Kill();
|
||||
|
||||
uint knifeIndex = knife.Index;
|
||||
knifeInfo.Timer = Instance.AddTimer(3f, () =>
|
||||
{
|
||||
if (knife == null || !knife.IsValid) return;
|
||||
var droppedKnife = Utilities.GetEntityFromIndex<CBaseEntity>((int)knifeIndex);
|
||||
if (droppedKnife == null || !droppedKnife.IsValid) return;
|
||||
|
||||
var player = Utilities.GetPlayerFromIndex((int)knifeInfo.PlayerIndex);
|
||||
if (player != null && player.IsValid && CheckHasKnife(player)) return;
|
||||
|
||||
knife.AcceptInput("ClearParent");
|
||||
droppedKnife.AcceptInput("ClearParent");
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +125,10 @@ namespace src.player.skills
|
|||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo?.Skill != skillName) return false;
|
||||
|
||||
if (knivesInfo.TryGetValue(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null && knifeInfo.KnifeId == econItem.ItemID)
|
||||
if (!knivesInfo.TryGetValue(player.Index, out KnifeInfo? knifeInfo) || knifeInfo == null)
|
||||
return false;
|
||||
|
||||
if (knifeInfo.KnifeId == econItem.ItemID)
|
||||
{
|
||||
knifeInfo.IsDropped = false;
|
||||
if (knifeInfo.Timer != null)
|
||||
|
|
@ -121,6 +140,8 @@ namespace src.player.skills
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!knifeInfo.IsDropped) return false;
|
||||
|
||||
hook.SetReturn(AcquireResult.InvalidItem);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -162,12 +183,20 @@ namespace src.player.skills
|
|||
SkillUtils.SafeKillEntity<CTriggerMultiple>(trigger);
|
||||
SkillUtils.SafeKillEntity<CBaseEntity>(glow);
|
||||
SkillUtils.SafeKillEntity<CBaseEntity>(relay);
|
||||
|
||||
if (knifeInfo.IsDropped)
|
||||
{
|
||||
knifeInfo.IsDropped = false;
|
||||
EntityManager.DestroyEntity(knifeInfo.KnifeIndex, 0.25f);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UseSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
if (roundEnded) return;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
|
||||
|
|
@ -183,7 +212,7 @@ namespace src.player.skills
|
|||
|
||||
Instance.AddTickTimer(8, () =>
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (player == null || !player.IsValid || roundEnded) return;
|
||||
|
||||
var pawn = player.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid || pawn.WeaponServices == null) return;
|
||||
|
|
@ -194,7 +223,7 @@ namespace src.player.skills
|
|||
player.DropActiveWeapon();
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (player == null || !player.IsValid || roundEnded) return;
|
||||
if (weapon == null || !weapon.IsValid) return;
|
||||
ThrowKnife(player, weapon);
|
||||
});
|
||||
|
|
@ -331,7 +360,7 @@ namespace src.player.skills
|
|||
|
||||
if (CheckHasKnife(thrower!)) return;
|
||||
|
||||
SkillUtils.TakeHealth(victimPawn, SkillsInfo.GetValue<int>(skillName, "damage"));
|
||||
SkillUtils.TakeHealth(victimPawn, SkillsInfo.GetValue<int>(skillName, "damage"), thrower, "weapon_knife");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace src.player.skills
|
|||
public class ToxicSmoke : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.ToxicSmoke;
|
||||
private static readonly ConcurrentDictionary<Vector, byte> smokes = [];
|
||||
private static readonly ConcurrentDictionary<Vector, uint> smokes = [];
|
||||
private readonly static ConcurrentDictionary<uint, int> playersWithSkill = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -31,7 +31,7 @@ namespace src.player.skills
|
|||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
|
||||
smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0);
|
||||
smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), player.Index);
|
||||
}
|
||||
|
||||
public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event)
|
||||
|
|
@ -81,7 +81,11 @@ namespace src.player.skills
|
|||
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
|
||||
int smokeDamage = SkillsInfo.GetValue<int>(skillName, "smokeDamage");
|
||||
|
||||
foreach (Vector smokePos in smokes.Keys)
|
||||
foreach (var smoke in smokes)
|
||||
{
|
||||
var thrower = Utilities.GetPlayerFromIndex((int)smoke.Value);
|
||||
if (thrower != null && !thrower.IsValid) thrower = null;
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers().Where(p => p.IsValid))
|
||||
{
|
||||
var eventPlayer = PlayerManager.GetPlayerEvent(player);
|
||||
|
|
@ -90,11 +94,12 @@ namespace src.player.skills
|
|||
var pawn = eventPlayer.PlayerPawn.Value;
|
||||
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
|
||||
|
||||
if (SkillUtils.GetDistance(smokePos, pawn.AbsOrigin) <= smokeRadius)
|
||||
if (SkillUtils.TakeHealth(pawn, smokeDamage))
|
||||
if (SkillUtils.GetDistance(smoke.Key, pawn.AbsOrigin) <= smokeRadius)
|
||||
if (SkillUtils.TakeHealth(pawn, smokeDamage, thrower, "smokegrenade"))
|
||||
player.EmitSound("Player.DamageBody.Onlooker");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GrenadeThrown(EventGrenadeThrown @event)
|
||||
{
|
||||
|
|
|
|||
256
jRandomSkills - SRC Files/src/player/skills/Tripwire.cs
Normal file
256
jRandomSkills - SRC Files/src/player/skills/Tripwire.cs
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using RayTraceAPI;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace src.player.skills
|
||||
{
|
||||
public class Tripwire : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Tripwire;
|
||||
|
||||
private static readonly ConcurrentDictionary<uint, WireInfo> wires = [];
|
||||
private static readonly ConcurrentDictionary<uint, int> wireCount = [];
|
||||
private static readonly ConcurrentDictionary<(uint Owner, uint Target), (int Slot, int ExpiryTick)> revealed = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
foreach (var wire in wires.Values)
|
||||
DestroyWire(wire.BeamIndex);
|
||||
|
||||
wires.Clear();
|
||||
wireCount.Clear();
|
||||
revealed.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
RemovePlayerWires(playerIndex);
|
||||
|
||||
foreach (var key in revealed.Keys)
|
||||
if (key.Owner == playerIndex || key.Target == playerIndex)
|
||||
revealed.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null) return;
|
||||
RemovePlayerWires(player.Index);
|
||||
|
||||
foreach (var key in revealed.Keys)
|
||||
if (key.Owner == player.Index)
|
||||
revealed.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
private static void RemovePlayerWires(uint ownerIndex)
|
||||
{
|
||||
foreach (var kvp in wires)
|
||||
{
|
||||
if (kvp.Value.OwnerIndex != ownerIndex) continue;
|
||||
|
||||
DestroyWire(kvp.Key);
|
||||
wires.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
wireCount.TryRemove(ownerIndex, out _);
|
||||
}
|
||||
|
||||
private static void DestroyWire(uint beamIndex)
|
||||
{
|
||||
var beam = Utilities.GetEntityFromIndex<CBeam>((int)beamIndex);
|
||||
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");
|
||||
}
|
||||
|
||||
EntityManager.DestroyEntity(beamIndex);
|
||||
}
|
||||
|
||||
public static void UseSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
||||
|
||||
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
|
||||
if (playerEvent == null || !playerEvent.IsValid) return;
|
||||
|
||||
int maxWires = SkillsInfo.GetValue<int>(skillName, "maxWires");
|
||||
if (wireCount.TryGetValue(player.Index, out int placed) && placed >= maxWires)
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("tripwire_limit_info", maxWires));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryPlaceWire(player))
|
||||
{
|
||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("tripwire_no_wall_info"));
|
||||
return;
|
||||
}
|
||||
|
||||
wireCount.AddOrUpdate(player.Index, 1, (_, v) => v + 1);
|
||||
playerEvent.PrintToChat($" {ChatColors.Green}" + playerEvent.GetTranslation("tripwire_placed_info"));
|
||||
}
|
||||
|
||||
private static bool TryPlaceWire(CCSPlayerController player)
|
||||
{
|
||||
var pawn = player.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return false;
|
||||
if (!RayTrace.IsAvailable) return false;
|
||||
|
||||
float height = SkillsInfo.GetValue<float>(skillName, "wireHeight");
|
||||
float maxDistance = SkillsInfo.GetValue<float>(skillName, "maxWallDistance");
|
||||
|
||||
Vector origin = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + height);
|
||||
float yaw = pawn.EyeAngles.Y;
|
||||
|
||||
Vector rightDir = SkillUtils.GetForwardVector(new QAngle(0, yaw - 90, 0));
|
||||
Vector leftDir = SkillUtils.GetForwardVector(new QAngle(0, yaw + 90, 0));
|
||||
|
||||
ulong wallMask = (ulong)InteractionLayers.MASK_WORLD_ONLY;
|
||||
|
||||
var rightHit = RayTrace.TraceShape(player, origin, origin + rightDir * maxDistance, wallMask, 0);
|
||||
var leftHit = RayTrace.TraceShape(player, origin, origin + leftDir * maxDistance, wallMask, 0);
|
||||
|
||||
if (rightHit == null || leftHit == null) return false;
|
||||
if (!rightHit.Value.DidHit || !leftHit.Value.DidHit) return false;
|
||||
if (rightHit.Value.HitPlayer(out _) || leftHit.Value.HitPlayer(out _)) return false;
|
||||
|
||||
Vector start = new(rightHit.Value.EndPos.X, rightHit.Value.EndPos.Y, rightHit.Value.EndPos.Z);
|
||||
Vector end = new(leftHit.Value.EndPos.X, leftHit.Value.EndPos.Y, leftHit.Value.EndPos.Z);
|
||||
|
||||
if (EntityManager.OverBudget()) return false;
|
||||
|
||||
var beam = EntityManager.CreateTrackedBeam(player.Index, start, end, Color.FromArgb(255, 255, 40, 40));
|
||||
if (beam == null || !beam.IsValid) return false;
|
||||
|
||||
beam.Width = SkillsInfo.GetValue<float>(skillName, "wireWidth");
|
||||
|
||||
wires[beam.Index] = new WireInfo
|
||||
{
|
||||
OwnerIndex = player.Index,
|
||||
BeamIndex = beam.Index,
|
||||
Start = start,
|
||||
End = end,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void OnTick()
|
||||
{
|
||||
if (revealed.IsEmpty && wires.IsEmpty) return;
|
||||
|
||||
int tick = Server.TickCount;
|
||||
|
||||
foreach (var kvp in revealed)
|
||||
{
|
||||
if (kvp.Value.ExpiryTick <= tick)
|
||||
{
|
||||
revealed.TryRemove(kvp.Key, out _);
|
||||
continue;
|
||||
}
|
||||
|
||||
RevealOnRadar(kvp.Key.Target, kvp.Value.Slot);
|
||||
}
|
||||
|
||||
if (wires.IsEmpty || tick % 4 != 0) return;
|
||||
|
||||
float radius = SkillsInfo.GetValue<float>(skillName, "triggerRadius");
|
||||
int revealTicks = (int)(SkillsInfo.GetValue<float>(skillName, "radarDuration") * 64);
|
||||
float bodyHeight = SkillsInfo.GetValue<float>(skillName, "wireHeight");
|
||||
|
||||
foreach (var wire in wires.Values)
|
||||
{
|
||||
var owner = Utilities.GetPlayerFromIndex((int)wire.OwnerIndex);
|
||||
if (owner == null || !owner.IsValid) continue;
|
||||
|
||||
foreach (var enemy in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (enemy == null || !enemy.IsValid || enemy.Team == owner.Team) continue;
|
||||
|
||||
var enemyEvent = PlayerManager.GetPlayerEvent(enemy);
|
||||
if (enemyEvent == null || !enemyEvent.IsValid) continue;
|
||||
|
||||
var enemyPawn = enemyEvent.PlayerPawn?.Value;
|
||||
if (enemyPawn == null || !enemyPawn.IsValid || enemyPawn.Health <= 0 || enemyPawn.AbsOrigin == null) continue;
|
||||
|
||||
var key = (wire.OwnerIndex, enemyEvent.Index);
|
||||
if (revealed.ContainsKey(key)) continue;
|
||||
|
||||
Vector body = new(enemyPawn.AbsOrigin.X, enemyPawn.AbsOrigin.Y, enemyPawn.AbsOrigin.Z + bodyHeight);
|
||||
if (DistanceToSegment(body, wire.Start, wire.End) > radius) continue;
|
||||
|
||||
revealed[key] = (owner.Slot, tick + revealTicks);
|
||||
|
||||
var ownerEvent = PlayerManager.GetPlayerFromEvent(owner);
|
||||
if (ownerEvent != null && ownerEvent.IsValid)
|
||||
{
|
||||
ownerEvent.ExecuteClientCommand("play sounds/weapons/taser/taser_charge_ready");
|
||||
ownerEvent.PrintToChat($" {ChatColors.Red}" + ownerEvent.GetTranslation("tripwire_triggered_info", enemyEvent.PlayerName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RevealOnRadar(uint targetIndex, int ownerSlot)
|
||||
{
|
||||
var target = Utilities.GetPlayerFromIndex((int)targetIndex);
|
||||
if (target == null || !target.IsValid) return;
|
||||
|
||||
var targetPawn = target.PlayerPawn?.Value;
|
||||
if (targetPawn == null || !targetPawn.IsValid || targetPawn.Health <= 0) return;
|
||||
|
||||
targetPawn.EntitySpottedState.SpottedByMask[0] |= (1u << (ownerSlot % 32));
|
||||
}
|
||||
|
||||
private static float DistanceToSegment(Vector point, Vector start, Vector end)
|
||||
{
|
||||
Vector line = new(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
|
||||
float lengthSquared = line.X * line.X + line.Y * line.Y + line.Z * line.Z;
|
||||
if (lengthSquared <= .0001f) return (float)SkillUtils.GetDistance(point, start);
|
||||
|
||||
Vector toPoint = new(point.X - start.X, point.Y - start.Y, point.Z - start.Z);
|
||||
float t = (toPoint.X * line.X + toPoint.Y * line.Y + toPoint.Z * line.Z) / lengthSquared;
|
||||
t = Math.Clamp(t, 0f, 1f);
|
||||
|
||||
Vector closest = new(start.X + line.X * t, start.Y + line.Y * t, start.Z + line.Z * t);
|
||||
return (float)SkillUtils.GetDistance(point, closest);
|
||||
}
|
||||
|
||||
private class WireInfo
|
||||
{
|
||||
public required uint OwnerIndex { get; set; }
|
||||
public required uint BeamIndex { get; set; }
|
||||
public required Vector Start { get; set; }
|
||||
public required Vector End { 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 = 1.5f, float maxWallDistance = 400f, int maxWires = 2) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
{
|
||||
public float RadarDuration { get; set; } = radarDuration;
|
||||
public float TriggerRadius { get; set; } = triggerRadius;
|
||||
public float WireHeight { get; set; } = wireHeight;
|
||||
public float WireWidth { get; set; } = wireWidth;
|
||||
public float MaxWallDistance { get; set; } = maxWallDistance;
|
||||
public int MaxWires { get; set; } = maxWires;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,8 @@ namespace src.player.skills
|
|||
temporaryBlockList.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
if (glows.IsEmpty && temporaryBlockList.IsEmpty) return;
|
||||
|
||||
// One pawn->info map per frame instead of a GetPlayers scan per client.
|
||||
var infoByPawnHandle = new Dictionary<nint, jSkill_PlayerInfo?>();
|
||||
foreach (var p in PlayerManager.GetTickPlayers())
|
||||
|
|
@ -37,6 +39,27 @@ namespace src.player.skills
|
|||
infoByPawnHandle[h.Value] = PlayerManager.GetPlayerByIndex(p.Index);
|
||||
}
|
||||
|
||||
var glowStates = new List<(uint Relay, uint Glow, CsTeam Team, bool Showable, bool EntitiesValid)>(glows.Count);
|
||||
foreach (var kvp in glows)
|
||||
{
|
||||
var glowInfo = kvp.Value;
|
||||
var enemy = Utilities.GetPlayerFromIndex((int)kvp.Key);
|
||||
|
||||
bool showable = false;
|
||||
if (enemy != null && enemy.IsValid)
|
||||
{
|
||||
var enemyPawn = enemy.PlayerPawn?.Value;
|
||||
bool pawnValid = enemyPawn != null && enemyPawn.IsValid;
|
||||
showable = pawnValid && enemyPawn!.Health > 0 && enemyPawn!.Render.A != 102 && enemyPawn!.Render.A != 128;
|
||||
}
|
||||
|
||||
var relayEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)glowInfo.RelayIndex);
|
||||
var glowEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)glowInfo.GlowIndex);
|
||||
bool entitiesValid = relayEntity != null && relayEntity.IsValid && glowEntity != null && glowEntity.IsValid;
|
||||
|
||||
glowStates.Add((glowInfo.RelayIndex, glowInfo.GlowIndex, glowInfo.Team, showable, entitiesValid));
|
||||
}
|
||||
|
||||
foreach (var (info, player) in infoList)
|
||||
{
|
||||
if (player == null || !player.IsValid) continue;
|
||||
|
|
@ -47,40 +70,18 @@ namespace src.player.skills
|
|||
if (targetHandle != nint.Zero)
|
||||
infoByPawnHandle.TryGetValue(targetHandle, out observerInfo);
|
||||
|
||||
foreach (var kvp in glows)
|
||||
bool seesGlows = playerInfo?.Skill == skillName || (observerInfo != null && observerInfo?.Skill == skillName);
|
||||
|
||||
foreach (var glow in glowStates)
|
||||
{
|
||||
var enemyIndex = kvp.Key;
|
||||
var glowInfo = kvp.Value;
|
||||
var enemy = Utilities.GetPlayerFromIndex((int)enemyIndex);
|
||||
if (glow.Showable && seesGlows && glow.Team != player.Team) continue;
|
||||
if (!glow.EntitiesValid) continue;
|
||||
|
||||
bool hasSkill = playerInfo?.Skill == skillName;
|
||||
bool observerHasSkill = observerInfo != null && observerInfo?.Skill == skillName;
|
||||
bool differentTeam = glowInfo.Team != player.Team;
|
||||
if (info.TransmitEntities.Contains(glow.Relay))
|
||||
info.TransmitEntities.Remove(glow.Relay);
|
||||
|
||||
if (enemy != null && enemy.IsValid)
|
||||
{
|
||||
var enemyPawn = enemy.PlayerPawn?.Value;
|
||||
bool pawnValid = enemyPawn != null && enemyPawn.IsValid;
|
||||
bool alive = pawnValid && enemyPawn!.Health > 0;
|
||||
bool notInvisible = pawnValid && enemyPawn!.Render.A != 102 && enemyPawn!.Render.A != 128;
|
||||
|
||||
bool shouldShow = alive && notInvisible && differentTeam && (hasSkill || observerHasSkill);
|
||||
|
||||
if (shouldShow)
|
||||
continue;
|
||||
}
|
||||
|
||||
var glowEntity1 = Utilities.GetEntityFromIndex<CBaseEntity>((int)glowInfo.RelayIndex);
|
||||
if (glowEntity1 == null || !glowEntity1.IsValid) continue;
|
||||
|
||||
var glowEntity2 = Utilities.GetEntityFromIndex<CBaseEntity>((int)glowInfo.GlowIndex);
|
||||
if (glowEntity2 == null || !glowEntity2.IsValid) continue;
|
||||
|
||||
if (info.TransmitEntities.Contains(glowEntity1.Index))
|
||||
info.TransmitEntities.Remove(glowEntity1.Index);
|
||||
|
||||
if (info.TransmitEntities.Contains(glowEntity2.Index))
|
||||
info.TransmitEntities.Remove(glowEntity2.Index);
|
||||
if (info.TransmitEntities.Contains(glow.Glow))
|
||||
info.TransmitEntities.Remove(glow.Glow);
|
||||
}
|
||||
|
||||
foreach (var kvp in temporaryBlockList)
|
||||
|
|
@ -91,6 +92,8 @@ namespace src.player.skills
|
|||
|
||||
public static void NewRound()
|
||||
{
|
||||
buildInProgress = false;
|
||||
|
||||
foreach (var kvp in glows)
|
||||
{
|
||||
var relayIndex = kvp.Value.RelayIndex;
|
||||
|
|
@ -136,7 +139,7 @@ namespace src.player.skills
|
|||
Event.EnableTransmit();
|
||||
playersInAction.TryAdd(player.Index, 0);
|
||||
|
||||
if (glows.IsEmpty)
|
||||
if (glows.IsEmpty && !buildInProgress)
|
||||
SetGlowEffectForAll();
|
||||
|
||||
SkillUtils.ForceFullUpdateToAll();
|
||||
|
|
@ -153,6 +156,9 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
private const int GlowsPerFrame = 4;
|
||||
private static bool buildInProgress;
|
||||
|
||||
private static void SetGlowEffectForAll()
|
||||
{
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p =>
|
||||
|
|
@ -163,26 +169,50 @@ namespace src.player.skills
|
|||
p.PlayerPawn.Value.Health > 0 &&
|
||||
(p.Team == CsTeam.Terrorist || p.Team == CsTeam.CounterTerrorist)).ToList();
|
||||
|
||||
foreach (var enemy in enemies)
|
||||
buildInProgress = true;
|
||||
SpawnGlowChunk(enemies, 0);
|
||||
}
|
||||
|
||||
private static void SpawnGlowChunk(List<CCSPlayerController> enemies, int start)
|
||||
{
|
||||
if (!buildInProgress) return;
|
||||
|
||||
if (start >= enemies.Count)
|
||||
{
|
||||
buildInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int end = Math.Min(start + GlowsPerFrame, enemies.Count);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
var enemy = enemies[i];
|
||||
if (enemy == null || !enemy.IsValid) continue;
|
||||
SpawnGlowFor(enemy);
|
||||
}
|
||||
|
||||
Server.NextFrame(() => SpawnGlowChunk(enemies, end));
|
||||
}
|
||||
|
||||
private static void SpawnGlowFor(CCSPlayerController enemy)
|
||||
{
|
||||
var enemyInfo = PlayerManager.GetPlayerByIndex(enemy.Index);
|
||||
if (enemyInfo?.Skill == Skills.Ghost)
|
||||
continue;
|
||||
return;
|
||||
|
||||
var enemyPawn = enemy.PlayerPawn?.Value;
|
||||
if (enemyPawn == null || !enemyPawn.IsValid) continue;
|
||||
if (enemyPawn == null || !enemyPawn.IsValid) return;
|
||||
|
||||
var skeleton = enemyPawn.CBodyComponent?.SceneNode?.GetSkeletonInstance();
|
||||
var modelName = skeleton?.ModelState?.ModelName;
|
||||
if (string.IsNullOrEmpty(modelName)) continue;
|
||||
if (string.IsNullOrEmpty(modelName)) return;
|
||||
|
||||
var modelGlow = EntityManager.CreateTrackedDynamicProp(enemy.Index);
|
||||
var modelRelay = EntityManager.CreateTrackedDynamicProp(enemy.Index);
|
||||
|
||||
if (modelGlow == null || !modelGlow.IsValid || modelRelay == null || !modelRelay.IsValid)
|
||||
continue;
|
||||
return;
|
||||
|
||||
// Register before spawn so the glow is filtered from its first snapshot.
|
||||
glows.TryAdd(enemy.Index, (modelRelay.Index, modelGlow.Index, enemy.Team));
|
||||
|
||||
var relayEntity = modelRelay.CBodyComponent?.SceneNode?.Owner?.Entity;
|
||||
|
|
@ -217,7 +247,6 @@ namespace src.player.skills
|
|||
modelRelay.AcceptInput("FollowEntity", enemyPawn, modelRelay, "!activator");
|
||||
modelGlow.AcceptInput("FollowEntity", modelRelay, modelGlow, "!activator");
|
||||
}
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -137,10 +137,17 @@ namespace src.player.skills
|
|||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (enemy == null || !enemy.IsValid) return;
|
||||
|
||||
var playerGear = SnapshotGear(player);
|
||||
var enemyGear = SnapshotGear(enemy);
|
||||
|
||||
player.RemoveWeapons();
|
||||
enemy.RemoveWeapons();
|
||||
GiveWeapons(player, enemyWeapon, playerC4);
|
||||
GiveWeapons(enemy, playerWeapon, (enemyWeapon != null && enemyC4));
|
||||
|
||||
RestoreGear(player, playerGear);
|
||||
RestoreGear(enemy, enemyGear);
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
@ -148,6 +155,49 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
private static GearInfo? SnapshotGear(CCSPlayerController player)
|
||||
{
|
||||
var pawn = player.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid) return null;
|
||||
|
||||
var itemServices = pawn.ItemServices?.As<CCSPlayer_ItemServices>();
|
||||
|
||||
return new GearInfo
|
||||
{
|
||||
Armor = pawn.ArmorValue,
|
||||
Health = pawn.Health,
|
||||
HasHelmet = itemServices?.HasHelmet ?? false,
|
||||
HasDefuser = itemServices?.HasDefuser ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
private static void RestoreGear(CCSPlayerController player, GearInfo? gear)
|
||||
{
|
||||
if (gear == null) return;
|
||||
|
||||
var pawn = player.PlayerPawn?.Value;
|
||||
if (pawn == null || !pawn.IsValid) return;
|
||||
|
||||
if (pawn.ArmorValue != gear.Armor)
|
||||
{
|
||||
pawn.ArmorValue = gear.Armor;
|
||||
Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
}
|
||||
|
||||
if (pawn.Health != gear.Health)
|
||||
{
|
||||
pawn.Health = gear.Health;
|
||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
|
||||
}
|
||||
|
||||
var itemServices = pawn.ItemServices?.As<CCSPlayer_ItemServices>();
|
||||
if (itemServices == null) return;
|
||||
|
||||
itemServices.HasHelmet = gear.HasHelmet;
|
||||
itemServices.HasDefuser = gear.HasDefuser;
|
||||
Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_pItemServices");
|
||||
}
|
||||
|
||||
private static (WeaponInfo[]?, bool) GetWeapons(CCSPlayerController player)
|
||||
{
|
||||
bool haveC4 = false;
|
||||
|
|
@ -247,6 +297,14 @@ namespace src.player.skills
|
|||
public bool HaveWeapon { get; set; }
|
||||
}
|
||||
|
||||
public class GearInfo
|
||||
{
|
||||
public required int Armor { get; set; }
|
||||
public required int Health { get; set; }
|
||||
public required bool HasHelmet { get; set; }
|
||||
public required bool HasDefuser { get; set; }
|
||||
}
|
||||
|
||||
public class WeaponInfo
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
|
|
|||
|
|
@ -80,10 +80,11 @@ namespace src.player.skills
|
|||
if (Server.TickCount % 32 != 0) return;
|
||||
foreach (var player in PlayerManager.GetTickPlayers())
|
||||
{
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p =>
|
||||
p != null &&
|
||||
p.IsValid)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,12 @@ namespace src.utils
|
|||
|
||||
public static int GetTrackedCount(uint playerIndex) => GetPlayerEntities(playerIndex).Count;
|
||||
|
||||
public static uint? GetEntityOwner(uint entityIndex)
|
||||
{
|
||||
if (!trackedEntities.TryGetValue(entityIndex, out var data)) return null;
|
||||
return data.PlayerIndex == SystemOwnerIndex ? null : data.PlayerIndex;
|
||||
}
|
||||
|
||||
public static (int totalTracked, int ownerCount) GetStatistics()
|
||||
{
|
||||
return (trackedEntities.Count, trackedEntities.Values.Select(e => e.PlayerIndex).Distinct().Count());
|
||||
|
|
|
|||
|
|
@ -22,10 +22,33 @@ namespace src.utils
|
|||
|
||||
private static string defaultLangCode = "en";
|
||||
|
||||
private static readonly Dictionary<Skills, string> skillKeys = BuildSkillKeys("");
|
||||
private static readonly Dictionary<Skills, string> skillDescKeys = BuildSkillKeys("_desc");
|
||||
private static readonly Dictionary<Skills, string> skillDesc2Keys = BuildSkillKeys("_desc2");
|
||||
|
||||
private static Dictionary<Skills, string> BuildSkillKeys(string suffix)
|
||||
{
|
||||
var map = new Dictionary<Skills, string>();
|
||||
foreach (Skills skill in Enum.GetValues<Skills>())
|
||||
map[skill] = skill.ToString().ToLowerInvariant() + suffix;
|
||||
return map;
|
||||
}
|
||||
|
||||
private static string SkillKey(Skills skill) => skillKeys.TryGetValue(skill, out var k) ? k : skill.ToString().ToLowerInvariant();
|
||||
private static string SkillDescKey(Skills skill) => skillDescKeys.TryGetValue(skill, out var k) ? k : skill.ToString().ToLowerInvariant() + "_desc";
|
||||
private static string SkillDesc2Key(Skills skill) => skillDesc2Keys.TryGetValue(skill, out var k) ? k : skill.ToString().ToLowerInvariant() + "_desc2";
|
||||
|
||||
private static readonly ConcurrentDictionary<(Skills Skill, string Lang, double Chance), string> _skillNameCache = [];
|
||||
private static readonly ConcurrentDictionary<(Skills Skill, string Lang, double Chance), string> _skillDescCache = [];
|
||||
|
||||
private const double NoChance = double.NegativeInfinity;
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
_translations.Clear();
|
||||
_playersLanguage.Clear();
|
||||
_skillNameCache.Clear();
|
||||
_skillDescCache.Clear();
|
||||
SetLangCode();
|
||||
LoadAllLanguages();
|
||||
LoadPlayersLanguage();
|
||||
|
|
@ -77,9 +100,26 @@ namespace src.utils
|
|||
public static string GetSkillName(this CCSPlayerController player, Skills skill, float? chance = null)
|
||||
{
|
||||
string langCode = GetLangCode(player);
|
||||
|
||||
bool cacheable = !Illiterate.CheckIlliterateSkill(player);
|
||||
var cacheKey = (skill, langCode, chance == null ? NoChance : Math.Round((double)chance, 2));
|
||||
|
||||
if (cacheable && _skillNameCache.TryGetValue(cacheKey, out var cached))
|
||||
return cached;
|
||||
|
||||
string result = BuildSkillName(player, skill, chance, langCode);
|
||||
|
||||
if (cacheable)
|
||||
_skillNameCache[cacheKey] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string BuildSkillName(CCSPlayerController player, Skills skill, float? chance, string langCode)
|
||||
{
|
||||
if (chance == null)
|
||||
{
|
||||
var translation = GetTranslation(skill.ToString().ToLowerInvariant(), langCode);
|
||||
var translation = GetTranslation(SkillKey(skill), langCode);
|
||||
if (!translation.Contains("{0}"))
|
||||
return translation;
|
||||
|
||||
|
|
@ -92,7 +132,7 @@ namespace src.utils
|
|||
}
|
||||
|
||||
var value = Math.Round((double)(chance ?? 1), 2);
|
||||
var skillNameText = GetTranslation(skill.ToString().ToLowerInvariant(), langCode, player, value);
|
||||
var skillNameText = GetTranslation(SkillKey(skill), langCode, player, value);
|
||||
if (skillNameText.Contains('%')) skillNameText = skillNameText.Replace(value.ToString(), Math.Round(value * 100, 0).ToString());
|
||||
return skillNameText;
|
||||
}
|
||||
|
|
@ -100,9 +140,26 @@ namespace src.utils
|
|||
public static string GetSkillDescription(this CCSPlayerController player, Skills skill, float? chance = null)
|
||||
{
|
||||
string langCode = GetLangCode(player);
|
||||
|
||||
bool cacheable = !Illiterate.CheckIlliterateSkill(player);
|
||||
var cacheKey = (skill, langCode, chance == null ? NoChance : Math.Round((double)chance, 2));
|
||||
|
||||
if (cacheable && _skillDescCache.TryGetValue(cacheKey, out var cached))
|
||||
return cached;
|
||||
|
||||
string result = BuildSkillDescription(player, skill, chance, langCode);
|
||||
|
||||
if (cacheable)
|
||||
_skillDescCache[cacheKey] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string BuildSkillDescription(CCSPlayerController player, Skills skill, float? chance, string langCode)
|
||||
{
|
||||
if (chance == null)
|
||||
{
|
||||
var translation = GetTranslation($"{skill.ToString().ToLowerInvariant()}_desc", langCode);
|
||||
var translation = GetTranslation(SkillDescKey(skill), langCode);
|
||||
if (!translation.Contains("{0}"))
|
||||
return translation;
|
||||
|
||||
|
|
@ -114,12 +171,12 @@ namespace src.utils
|
|||
return string.Join(' ', filtered);
|
||||
}
|
||||
|
||||
var skillName = $"{skill.ToString().ToLowerInvariant()}_desc2";
|
||||
var skillName = SkillDesc2Key(skill);
|
||||
var value = Math.Round((double)(chance ?? 1), 2);
|
||||
var desc2 = GetTranslation(skillName, langCode, player, value);
|
||||
|
||||
var skilLDescription = desc2 == skillName
|
||||
? player.GetTranslation($"{skill.ToString().ToLowerInvariant()}_desc")
|
||||
? player.GetTranslation(SkillDescKey(skill))
|
||||
: desc2.Contains('%') ? desc2.Replace(value.ToString(), Math.Round(value * 100, 0).ToString()) : desc2;
|
||||
return skilLDescription;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ namespace src.utils
|
|||
|
||||
private static int cachedTick = int.MinValue;
|
||||
private static List<CCSPlayerController> cachedControllers = [];
|
||||
|
||||
private static int cachedBombTick = int.MinValue;
|
||||
private static CC4? cachedBomb;
|
||||
|
||||
private static void EnsureTickCache()
|
||||
|
|
@ -20,7 +22,6 @@ namespace src.utils
|
|||
if (tick == cachedTick) return;
|
||||
cachedTick = tick;
|
||||
cachedControllers = Utilities.GetPlayers();
|
||||
cachedBomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||
}
|
||||
|
||||
public static List<CCSPlayerController> GetTickPlayers()
|
||||
|
|
@ -31,7 +32,13 @@ namespace src.utils
|
|||
|
||||
public static CC4? GetTickBomb()
|
||||
{
|
||||
EnsureTickCache();
|
||||
int tick = Server.TickCount;
|
||||
if (tick != cachedBombTick)
|
||||
{
|
||||
cachedBombTick = tick;
|
||||
cachedBomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||
}
|
||||
|
||||
return cachedBomb != null && cachedBomb.IsValid ? cachedBomb : null;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +69,7 @@ namespace src.utils
|
|||
if (!player.ControllingBot)
|
||||
return player;
|
||||
|
||||
return Utilities.GetPlayers().FirstOrDefault(p =>
|
||||
return GetTickPlayers().FirstOrDefault(p =>
|
||||
p != null &&
|
||||
p.IsValid &&
|
||||
p.IsBot &&
|
||||
|
|
@ -78,7 +85,7 @@ namespace src.utils
|
|||
if (!player.IsBot)
|
||||
return player;
|
||||
|
||||
return Utilities.GetPlayers().FirstOrDefault(p =>
|
||||
return GetTickPlayers().FirstOrDefault(p =>
|
||||
p != null &&
|
||||
p.IsValid &&
|
||||
!p.IsBot &&
|
||||
|
|
@ -91,43 +98,6 @@ namespace src.utils
|
|||
return playersByIndex.Values;
|
||||
}
|
||||
|
||||
public static IEnumerable<jSkill_PlayerInfo> GetAlivePlayers()
|
||||
{
|
||||
return playersByIndex.Values.Where(p =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var controller = Utilities.GetPlayerFromIndex((int)p.PlayerIndex);
|
||||
return controller != null && controller.IsValid && controller.PawnIsAlive;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static IEnumerable<jSkill_PlayerInfo> GetPlayersByTeam(CsTeam team)
|
||||
{
|
||||
return playersByIndex.Values.Where(p =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var controller = Utilities.GetPlayerFromIndex((int)p.PlayerIndex);
|
||||
return controller != null && controller.IsValid && controller.Team == team;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static int GetTeamPlayerCount(CsTeam team)
|
||||
{
|
||||
return GetPlayersByTeam(team).Count();
|
||||
}
|
||||
|
||||
public static int GetPlayerCountBySkill(Skills skills)
|
||||
{
|
||||
return playersByIndex.Values.Count(p => p.Skill == skills);
|
||||
|
|
|
|||
|
|
@ -322,7 +322,31 @@ namespace src.utils
|
|||
return !ff && !tae; // same team + FF off -> engine will zero this damage
|
||||
}
|
||||
|
||||
public static bool TakeHealth(CCSPlayerPawn? pawn, int damage)
|
||||
private static readonly ConcurrentDictionary<uint, (uint AttackerIndex, string? Weapon, int ExpiryTick)> pendingKillCredits = [];
|
||||
|
||||
public static void RegisterKillCredit(uint victimIndex, uint attackerIndex, string? weapon = null)
|
||||
{
|
||||
pendingKillCredits[victimIndex] = (attackerIndex, weapon, Server.TickCount + 64);
|
||||
}
|
||||
|
||||
public static bool TryConsumeKillCredit(uint victimIndex, out uint attackerIndex, out string? weapon)
|
||||
{
|
||||
attackerIndex = 0;
|
||||
weapon = null;
|
||||
if (!pendingKillCredits.TryRemove(victimIndex, out var credit)) return false;
|
||||
if (credit.ExpiryTick < Server.TickCount) return false;
|
||||
|
||||
attackerIndex = credit.AttackerIndex;
|
||||
weapon = credit.Weapon;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void ClearKillCredits()
|
||||
{
|
||||
pendingKillCredits.Clear();
|
||||
}
|
||||
|
||||
public static bool TakeHealth(CCSPlayerPawn? pawn, int damage, CCSPlayerController? damageAttacker = null, string? damageWeapon = null)
|
||||
{
|
||||
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
|
||||
return false;
|
||||
|
|
@ -363,6 +387,10 @@ namespace src.utils
|
|||
|
||||
if (pawn.Health <= 0)
|
||||
{
|
||||
if (damageAttacker != null && damageAttacker.IsValid && victim != null && victim.IsValid
|
||||
&& damageAttacker.Index != victim.Index)
|
||||
RegisterKillCredit(victim.Index, damageAttacker.Index, damageWeapon);
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (pawn == null || !pawn.IsValid) return;
|
||||
|
|
@ -374,6 +402,29 @@ namespace src.utils
|
|||
return true;
|
||||
}
|
||||
|
||||
public static void HideCarriedEntities(CCheckTransmitInfo info, CCSPlayerPawn? pawn)
|
||||
{
|
||||
if (pawn == null || !pawn.IsValid) return;
|
||||
|
||||
var weaponServices = pawn.WeaponServices;
|
||||
if (weaponServices == null) return;
|
||||
|
||||
var activeWeapon = weaponServices.ActiveWeapon?.Value;
|
||||
if (activeWeapon != null && activeWeapon.IsValid && info.TransmitEntities.Contains(activeWeapon.Index))
|
||||
info.TransmitEntities.Remove(activeWeapon.Index);
|
||||
|
||||
if (weaponServices.MyWeapons == null) return;
|
||||
|
||||
foreach (var handle in weaponServices.MyWeapons)
|
||||
{
|
||||
var weapon = handle?.Value;
|
||||
if (weapon == null || !weapon.IsValid) continue;
|
||||
|
||||
if (info.TransmitEntities.Contains(weapon.Index))
|
||||
info.TransmitEntities.Remove(weapon.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResetPrintHTML(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -81,6 +81,25 @@
|
|||
"MaxPerServer": 1,
|
||||
"Rarity": "Epic"
|
||||
},
|
||||
{
|
||||
"RadarDuration": 5.0,
|
||||
"TriggerRadius": 24.0,
|
||||
"WireHeight": 30.0,
|
||||
"WireWidth": 1.5,
|
||||
"MaxWallDistance": 400.0,
|
||||
"MaxWires": 2,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#ff3b3b",
|
||||
"Active": true,
|
||||
"Name": "Tripwire",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": -1,
|
||||
"Rarity": "Rare"
|
||||
},
|
||||
{
|
||||
"SmokeDamage": 2,
|
||||
"SmokeRadius": 180.0,
|
||||
|
|
@ -371,6 +390,10 @@
|
|||
},
|
||||
{
|
||||
"ZombieHealth": 500,
|
||||
"R": 255,
|
||||
"G": 0,
|
||||
"B": 0,
|
||||
"A": 60,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
|
|
@ -756,6 +779,23 @@
|
|||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"PostProcessing": "lighting/postprocessing/effects/death_cam_phase1_low_violence.vpost",
|
||||
"FadeTime": 0.25,
|
||||
"MinExposure": 0.5,
|
||||
"MaxExposure": 2.0,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#5b2c6f",
|
||||
"Active": true,
|
||||
"Name": "Nightmare",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": -1,
|
||||
"Rarity": "Rare"
|
||||
},
|
||||
{
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
|
|
@ -887,6 +927,21 @@
|
|||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"KnockbackUnits": 120.0,
|
||||
"MaxSpeed": 1200.0,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#ff8c42",
|
||||
"Active": true,
|
||||
"Name": "Knockback",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"FlashDuration": 1.0,
|
||||
"FriendlyFire": true,
|
||||
|
|
@ -917,6 +972,20 @@
|
|||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"JumpVelocity": 301.0,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#7ad1c4",
|
||||
"Active": true,
|
||||
"Name": "JumpCurse",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
|
|
@ -1094,6 +1163,7 @@
|
|||
},
|
||||
{
|
||||
"HealthToAdd": 100,
|
||||
"EffectDuration": 1.0,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
|
|
@ -1191,6 +1261,21 @@
|
|||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"MinScale": 1.1,
|
||||
"MaxScale": 1.4,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#8ad3ff",
|
||||
"Active": true,
|
||||
"Name": "Giant",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
|
|
@ -1337,6 +1422,38 @@
|
|||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"Cooldown": 20.0,
|
||||
"ExplosionRadius": 210.0,
|
||||
"ExplosionDamage": 50,
|
||||
"PropModel": "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl",
|
||||
"DmgReductionForTeamates": 0.5,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": true,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#c0392b",
|
||||
"Active": true,
|
||||
"Name": "ExplodingBarrel",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": 2,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"MoneyPerShot": 50,
|
||||
"NeedsTeammates": false,
|
||||
"DisableOnFreezeTime": false,
|
||||
"OnlyTeam": 0,
|
||||
"Color": "#e0c341",
|
||||
"Active": true,
|
||||
"Name": "ExpensiveAmmo",
|
||||
"HudDuration": null,
|
||||
"DescriptionHudDuration": null,
|
||||
"RequiredPermission": "",
|
||||
"MaxPerServer": -1,
|
||||
"Rarity": "Common"
|
||||
},
|
||||
{
|
||||
"Cooldown": 15.0,
|
||||
"CooldownBeforeUse": 10.0,
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Feindes-Spawn",
|
||||
"enemyspawn_desc": "Klicke auf [css_useSkill], um zum Feindes-Spawn zu teleportieren",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Explosiver Schuss",
|
||||
"explosiveshot_desc": "Zufällige Chance, beim Schießen eine explosive Kugel abzufeuern",
|
||||
"explosiveshot_desc2": "Deine Chance, eine explosive Kugel abzufeuern: {0} %",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Geist",
|
||||
"ghost_desc": "Du bist vollständig unsichtbar",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Du kannst durch Rauchgranaten hindurchsehen",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "Der Spieler ‚{0}‘ kann nicht mehr springen.",
|
||||
"jumpban_select_info": "Spieler vom Springen ausschließen:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Jumping Jack",
|
||||
"jumpingjack_desc": "Durch Springen wird die Gesundheit wiederhergestellt",
|
||||
|
||||
"killerflash": "Killer Flash",
|
||||
"killerflash_desc": "Jeder, der durch deine Blendgranate vollständig geblendet wird, stirbt (einschließlich dir selbst)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
|
||||
"lifeswap": "Lebensaustausch",
|
||||
"lifeswap_desc": "Wähle einen Spieler aus, mit dem du deine Gesundheit austauschen möchtest",
|
||||
"lifeswap_enemy_info": "Jemand hat sich deine Gesundheit ausgeliehen",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Du explodierst beim Tod und tötest Spieler in deiner Nähe",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Wenn du stillstehst, erhöht sich deine Unsichtbarkeit um 33 %, wenn du dich duckst um 33 % und wenn du ein Messer hältst um 33 %",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Giftiger Rauch",
|
||||
"toxicsmoke_desc": "Deine Rauchgranaten verursachen Schaden",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Du kannst Feinde durch Wände hindurch sehen",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Enemy Spawn",
|
||||
"enemyspawn_desc": "Click [css_useSkill] to teleport to the enemy spawn",
|
||||
|
||||
"expensiveammo": "Expensive Ammo",
|
||||
"expensiveammo_desc": "A chosen enemy loses money with every shot",
|
||||
"expensiveammo_disable_info": "Your bullets are free again.",
|
||||
"expensiveammo_enemy_info": "Every shot you fire now costs you {0}$.",
|
||||
"expensiveammo_player_info": "Player '{0}' now pays for every bullet.",
|
||||
"expensiveammo_select_info": "Select player whose ammo becomes expensive:",
|
||||
|
||||
"explodingbarrel": "Exploding Barrel",
|
||||
"explodingbarrel_desc": "Click [css_useSkill] to place a barrel that explodes when shot",
|
||||
|
||||
"explosiveshot": "Explosive Shot",
|
||||
"explosiveshot_desc": "Random chance to fire an explosive bullet while shooting",
|
||||
"explosiveshot_desc2": "Your chance to fire an explosive bullet: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Ghost",
|
||||
"ghost_desc": "You are completely invisible",
|
||||
|
||||
"giant": "Giant",
|
||||
"giant_desc": "Enlarge an enemy of your choice",
|
||||
"giant_desc2": "Enlarge an enemy of your choice to {0}x",
|
||||
"giant_disable_info": "You are back to your normal size.",
|
||||
"giant_enemy_info": "You have been enlarged to {0}x.",
|
||||
"giant_player_info": "You enlarged player '{0}' to {1}x.",
|
||||
"giant_select_info": "Enlarge player:",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "You can see through smoke grenades",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "Player '{0}' can no longer jump.",
|
||||
"jumpban_select_info": "Ban player from jumping:",
|
||||
|
||||
"jumpcurse": "Jump Curse",
|
||||
"jumpcurse_desc": "A chosen enemy jumps whenever their teammates jump",
|
||||
"jumpcurse_disable_info": "Your legs are your own again.",
|
||||
"jumpcurse_enemy_info": "You now jump whenever your teammates jump.",
|
||||
"jumpcurse_player_info": "Player '{0}' now jumps with their team.",
|
||||
"jumpcurse_select_info": "Select player for the jump curse:",
|
||||
|
||||
"jumpingjack": "Jumping Jack",
|
||||
"jumpingjack_desc": "Jumping restores health",
|
||||
|
||||
"killerflash": "Killer Flash",
|
||||
"killerflash_desc": "Anyone fully blinded by your flashbang dies (including you)",
|
||||
|
||||
"knockback": "Knockback",
|
||||
"knockback_desc": "Firing while airborne pushes you backwards",
|
||||
|
||||
"lifeswap": "Life Swap",
|
||||
"lifeswap_desc": "Choose a player to swap health with",
|
||||
"lifeswap_enemy_info": "Someone borrowed your health.",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "You explode upon death, killing nearby players",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "Nightmare",
|
||||
"nightmare_desc": "Give a chosen enemy a nightmarish vision",
|
||||
"nightmare_disable_info": "The nightmare has faded.",
|
||||
"nightmare_enemy_info": "A nightmare clouds your vision.",
|
||||
"nightmare_player_info": "You cast a nightmare on player '{0}'.",
|
||||
"nightmare_select_info": "Select player for the nightmare:",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Standing still increases your invisibility by 33%, crouching by 33%, and holding a knife by 33%",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Toxic Smoke",
|
||||
"toxicsmoke_desc": "Your smoke grenades deal damage",
|
||||
|
||||
"tripwire": "Tripwire",
|
||||
"tripwire_desc": "Click [css_useSkill] to string a tripwire between two walls",
|
||||
"tripwire_limit_info": "You can only have {0} tripwires at a time.",
|
||||
"tripwire_no_wall_info": "No walls close enough on both sides.",
|
||||
"tripwire_placed_info": "Tripwire placed.",
|
||||
"tripwire_triggered_info": "'{0}' tripped your wire.",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "You can see enemies through walls",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Spawn Ennemi",
|
||||
"enemyspawn_desc": "Appuyez sur [css_useSkill] pour vous téléporter au spawn ennemi",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Tir Explosif",
|
||||
"explosiveshot_desc": "Chance aléatoire de tirer une balle explosive en tirant",
|
||||
"explosiveshot_desc2": "Votre chance de tirer une balle explosive est : {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Fantôme",
|
||||
"ghost_desc": "Vous êtes complètement invisible",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Vous pouvez voir à travers les fumigènes",
|
||||
|
||||
|
|
@ -241,12 +259,21 @@
|
|||
"jumpban_player_info": "Le joueur « {0} » ne peut plus sauter.",
|
||||
"jumpban_select_info": "Interdire de sauter au joueur :",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Sauteur",
|
||||
"jumpingjack_desc": "Sauter vous rend de la vie",
|
||||
|
||||
"killerflash": "Flash Mortel",
|
||||
"killerflash_desc": "Toute personne totalement aveuglée par votre flashbang meurt (y compris vous)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
"lifeswap": "Échange de Vie",
|
||||
"lifeswap_desc": "Choisissez un joueur avec qui échanger votre santé",
|
||||
"lifeswap_enemy_info": "Quelqu’un a emprunté votre santé.",
|
||||
|
|
@ -288,6 +315,13 @@
|
|||
"deathbomb_desc": "Vous explosez à votre mort, tuant les joueurs proches",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Rester immobile augmente votre invisibilité de 33%, s’accroupir de 33%, et tenir un couteau de 33%",
|
||||
|
||||
|
|
@ -450,6 +484,13 @@
|
|||
"toxicsmoke": "Fumée Toxique",
|
||||
"toxicsmoke_desc": "Vos grenades fumigènes infligent des dégâts",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Vous pouvez voir les ennemis à travers les murs",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Resp Wroga",
|
||||
"enemyspawn_desc": "Kliknij [css_useSkill], aby teleportować się na resp wroga",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Strzał Wybuchowy",
|
||||
"explosiveshot_desc": "Losowa szansa wystrzelenia pocisku wybuchowego podczas strzelania",
|
||||
"explosiveshot_desc2": "Twoja szansa na wystrzelenie pocisku wybuchowego: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Duszek",
|
||||
"ghost_desc": "Jesteś całkowicie niewidzialny",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Nie widzisz granatów dymnych",
|
||||
|
||||
|
|
@ -241,12 +259,21 @@
|
|||
"jumpban_player_info": "Gracz '{0}' nie może teraz skakać.",
|
||||
"jumpban_select_info": "Zablokuj skok dla gracza:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Pajacyk",
|
||||
"jumpingjack_desc": "Skakanie przywraca zdrowie",
|
||||
|
||||
"killerflash": "Zabójczy Flash",
|
||||
"killerflash_desc": "Każdy całkowicie oślepiony twoim granatem umiera (również ty)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
"lifeswap": "Zamiana Żyć",
|
||||
"lifeswap_desc": "Wybierasz gracza, z którym chcesz wymienić się zdrowiem",
|
||||
"lifeswap_enemy_info": "Ktoś pożyczył sobie twoje zdrowie.",
|
||||
|
|
@ -288,6 +315,13 @@
|
|||
"deathbomb_desc": "Po śmierci eksplodujesz i zabijasz graczy znajdujących się w zasięgu",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Stojąc nieruchomo zwiększasz swoją niewidzialność o 33%, kucając o 33%, a trzymając nóż o 33%",
|
||||
|
||||
|
|
@ -450,6 +484,13 @@
|
|||
"toxicsmoke": "Toksyczny Dym",
|
||||
"toxicsmoke_desc": "Twoje granaty dymny zadają obrażenia",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Widzisz wrogów przez ściany",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Spawn Inimigo",
|
||||
"enemyspawn_desc": "Clique em [css_useSkill] para se teletransportar para a base inimiga",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Tiro Explosivo",
|
||||
"explosiveshot_desc": "Chance aleatória de disparar uma bala explosiva enquanto atira",
|
||||
"explosiveshot_desc2": "Sua chance de disparar uma bala explosiva: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Fantasma",
|
||||
"ghost_desc": "Você é completamente invisível",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Smoke Transparente",
|
||||
"glaz_desc": "Smokes tornam-se transparentes para você",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "O jogador '{0}' não pode mais pular.",
|
||||
"jumpban_select_info": "Banir pulo do jogador:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Pula-Pula",
|
||||
"jumpingjack_desc": "Pular restaura vida",
|
||||
|
||||
"killerflash": "Flash Mortal",
|
||||
"killerflash_desc": "Qualquer pessoa cega pela sua flash morre (incluindo você)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
|
||||
"lifeswap": "Troca de Vida",
|
||||
"lifeswap_desc": "Escolha um jogador para trocar de vida",
|
||||
"lifeswap_enemy_info": "Alguém pegou sua vida emprestada.",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Você explode ao morrer, matando os jogadores próximos",
|
||||
"deathbomb_explosion": "Kaboom!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Ficar parado aumenta sua invisibilidade em 33%, agachar em 33%, e segurar uma faca em 33%",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Smoke Tóxica",
|
||||
"toxicsmoke_desc": "Suas smokes causam dano",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Você pode ver inimigos através das paredes",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Спавн врагов",
|
||||
"enemyspawn_desc": "Нажмите [css_useSkill], чтобы телепортироваться на спавн врагов",
|
||||
|
||||
"expensiveammo": "expensiveammo",
|
||||
"expensiveammo_desc": "expensiveammo_desc",
|
||||
"expensiveammo_disable_info": "expensiveammo_disable_info",
|
||||
"expensiveammo_enemy_info": "expensiveammo_enemy_info: {0}",
|
||||
"expensiveammo_player_info": "expensiveammo_player_info: {0}",
|
||||
"expensiveammo_select_info": "expensiveammo_select_info",
|
||||
|
||||
"explodingbarrel": "explodingbarrel",
|
||||
"explodingbarrel_desc": "explodingbarrel_desc",
|
||||
|
||||
"explosiveshot": "Взрывной выстрел",
|
||||
"explosiveshot_desc": "Случайный шанс выстрелить взрывной пулей при стрельбе",
|
||||
"explosiveshot_desc2": "Ваш шанс выстрелить взрывной пулей: {0}%",
|
||||
|
|
@ -165,6 +175,14 @@
|
|||
"ghost": "Призрак",
|
||||
"ghost_desc": "Вы полностью невидимы",
|
||||
|
||||
"giant": "giant",
|
||||
"giant_desc": "giant_desc",
|
||||
"giant_desc2": "giant_desc2: {0}x",
|
||||
"giant_disable_info": "giant_disable_info",
|
||||
"giant_enemy_info": "giant_enemy_info: {0}x",
|
||||
"giant_player_info": "giant_player_info: {0} {1}",
|
||||
"giant_select_info": "giant_select_info",
|
||||
|
||||
"glaz": "Глаз",
|
||||
"glaz_desc": "Вы видите сквозь дымовые гранаты",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "Игрок '{0}' больше не может прыгать.",
|
||||
"jumpban_select_info": "Запретить прыжки игроку:",
|
||||
|
||||
"jumpcurse": "jumpcurse",
|
||||
"jumpcurse_desc": "jumpcurse_desc",
|
||||
"jumpcurse_disable_info": "jumpcurse_disable_info",
|
||||
"jumpcurse_enemy_info": "jumpcurse_enemy_info",
|
||||
"jumpcurse_player_info": "jumpcurse_player_info: {0}",
|
||||
"jumpcurse_select_info": "jumpcurse_select_info",
|
||||
|
||||
"jumpingjack": "Прыгающий Джек",
|
||||
"jumpingjack_desc": "Прыжки восстанавливают здоровье",
|
||||
|
||||
"killerflash": "Смертельная флешка",
|
||||
"killerflash_desc": "Любой, полностью ослеплённый вашей флешкой, погибает (включая вас)",
|
||||
|
||||
"knockback": "knockback",
|
||||
"knockback_desc": "knockback_desc",
|
||||
|
||||
"lifeswap": "Обмен жизнями",
|
||||
"lifeswap_desc": "Выберите игрока для обмена здоровьем",
|
||||
"lifeswap_enemy_info": "Кто-то занял ваше здоровье.",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Вы взрываетесь при смерти, убивая ближайших игроков",
|
||||
"deathbomb_explosion": "АЛЛАХУ АКБАР!!!",
|
||||
|
||||
"nightmare": "nightmare",
|
||||
"nightmare_desc": "nightmare_desc",
|
||||
"nightmare_disable_info": "nightmare_disable_info",
|
||||
"nightmare_enemy_info": "nightmare_enemy_info",
|
||||
"nightmare_player_info": "nightmare_player_info: {0}",
|
||||
"nightmare_select_info": "nightmare_select_info",
|
||||
|
||||
"ninja": "Ниндзя",
|
||||
"ninja_desc": "Стоя на месте, ваша невидимость увеличивается на 33%, в приседе на 33%, с ножом на 33%",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Токсичный дым",
|
||||
"toxicsmoke_desc": "Ваши дымовые гранаты наносят урон",
|
||||
|
||||
"tripwire": "tripwire",
|
||||
"tripwire_desc": "tripwire_desc",
|
||||
"tripwire_limit_info": "tripwire_limit_info: {0}",
|
||||
"tripwire_no_wall_info": "tripwire_no_wall_info",
|
||||
"tripwire_placed_info": "tripwire_placed_info",
|
||||
"tripwire_triggered_info": "tripwire_triggered_info: {0}",
|
||||
|
||||
"wallhack": "Воллхак",
|
||||
"wallhack_desc": "Вы видите врагов сквозь стены",
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@
|
|||
"enemyspawn": "Base Baskını",
|
||||
"enemyspawn_desc": "[css_useSkill] tuşuna basarak düşman doğma noktasına ışınlan",
|
||||
|
||||
"expensiveammo": "Pahalı Mermi",
|
||||
"expensiveammo_desc": "Seçtiğin rakip her ateş ettiğinde para kaybeder",
|
||||
"expensiveammo_disable_info": "Mermilerin yeniden bedava.",
|
||||
"expensiveammo_enemy_info": "Artık her atışın sana {0}$ mal oluyor.",
|
||||
"expensiveammo_player_info": "'{0}' artık her mermisinin parasını ödüyor.",
|
||||
"expensiveammo_select_info": "Mermisi pahalanacak oyuncu:",
|
||||
|
||||
"explodingbarrel": "Patlayan Varil",
|
||||
"explodingbarrel_desc": "[css_useSkill] ile vurulunca patlayan bir varil koyarsın",
|
||||
|
||||
"explosiveshot": "Patlayıcı Mermi",
|
||||
"explosiveshot_desc": "Ateş ederken rastgele mermilerin patlayabilir",
|
||||
"explosiveshot_desc2": "Patlayan mermi şansı: %{0}",
|
||||
|
|
@ -159,12 +169,20 @@
|
|||
"gambler": "Kumarbaz",
|
||||
"gambler_desc": "Sunulan listeden bir yetenek seç.",
|
||||
"gambler_select_info": "Yeteneğini seç:",
|
||||
"gambler_more": "#00FF00|${0} karşılığında yeni yetenekler",
|
||||
"gambler_more": "#00FF00|${0} yenile",
|
||||
"gambler_no_money": "Yeterli para yok.",
|
||||
|
||||
"ghost": "Hayalet",
|
||||
"ghost_desc": "Tamamen görünmezsin",
|
||||
|
||||
"giant": "Dev",
|
||||
"giant_desc": "Seçtiğin bir rakibi büyütür",
|
||||
"giant_desc2": "Seçtiğin bir rakibi {0}x büyütür",
|
||||
"giant_disable_info": "Normal boyutuna geri döndün.",
|
||||
"giant_enemy_info": "{0}x boyutuna büyütüldün.",
|
||||
"giant_player_info": "'{0}' oyuncusunu {1}x büyüttün.",
|
||||
"giant_select_info": "Büyütülecek oyuncu:",
|
||||
|
||||
"glaz": "Glaz",
|
||||
"glaz_desc": "Sis bombalarının içini görebilirsin",
|
||||
|
||||
|
|
@ -241,12 +259,22 @@
|
|||
"jumpban_player_info": "'{0}' artık zıplayamaz.",
|
||||
"jumpban_select_info": "Zıplaması yasaklanacak oyuncu:",
|
||||
|
||||
"jumpcurse": "Zıplama Laneti",
|
||||
"jumpcurse_desc": "Seçtiğin rakip, takım arkadaşları her zıpladığında zıplar",
|
||||
"jumpcurse_disable_info": "Bacakların yeniden senin.",
|
||||
"jumpcurse_enemy_info": "Artık takım arkadaşların zıpladıkça sen de zıplıyorsun.",
|
||||
"jumpcurse_player_info": "'{0}' artık takımıyla birlikte zıplıyor.",
|
||||
"jumpcurse_select_info": "Zıplama laneti verilecek oyuncu:",
|
||||
|
||||
"jumpingjack": "Zıpzıp Jack",
|
||||
"jumpingjack_desc": "Zıplamak canını yeniler",
|
||||
|
||||
"killerflash": "Ölümcül Flaş",
|
||||
"killerflash_desc": "Flaşına tamamen kör olan herkes ölür (sen dahil)",
|
||||
|
||||
"knockback": "Savurma",
|
||||
"knockback_desc": "Havadayken ateş etmek seni geriye savurur",
|
||||
|
||||
"lifeswap": "Can Takası",
|
||||
"lifeswap_desc": "Can değerlerini takas etmek için bir oyuncu seç",
|
||||
"lifeswap_enemy_info": "Birisi canını çaldı",
|
||||
|
|
@ -288,6 +316,13 @@
|
|||
"deathbomb_desc": "Öldüğünde patlayarak etraftaki oyuncuları öldürürsün",
|
||||
"deathbomb_explosion": "BOOM!!!",
|
||||
|
||||
"nightmare": "Kabus",
|
||||
"nightmare_desc": "Seçtiğin bir rakibe kabus görüntüsü verir",
|
||||
"nightmare_disable_info": "Kabus sona erdi.",
|
||||
"nightmare_enemy_info": "Gözlerini bir kabus bürüdü.",
|
||||
"nightmare_player_info": "'{0}' oyuncusuna kabus gördürdün.",
|
||||
"nightmare_select_info": "Kabus görecek oyuncu:",
|
||||
|
||||
"ninja": "Ninja",
|
||||
"ninja_desc": "Görünmezliğini sabit durmak %33, eğilmek %33 ve bıçak tutmak %33 artırır",
|
||||
|
||||
|
|
@ -450,6 +485,13 @@
|
|||
"toxicsmoke": "Zehirli Sis",
|
||||
"toxicsmoke_desc": "Sis bombaların hasar verir",
|
||||
|
||||
"tripwire": "Tel Tuzağı",
|
||||
"tripwire_desc": "[css_useSkill] ile iki duvar arasına tel gerersin",
|
||||
"tripwire_limit_info": "Aynı anda en fazla {0} tel gerebilirsin.",
|
||||
"tripwire_no_wall_info": "İki yanında da yeterince yakın duvar yok.",
|
||||
"tripwire_placed_info": "Tel gerildi.",
|
||||
"tripwire_triggered_info": "'{0}' teline takıldı.",
|
||||
|
||||
"wallhack": "Wallhack",
|
||||
"wallhack_desc": "Düşmanları duvarların arkasından görebilirsin",
|
||||
|
||||
|
|
|
|||
Binary file not shown.
14
updater.json
14
updater.json
|
|
@ -1,7 +1,19 @@
|
|||
{
|
||||
"name": "jRandomSkills",
|
||||
"current_version": "v1.2.2.b8",
|
||||
"current_version": "v1.2.3.b1",
|
||||
"releases": [
|
||||
{
|
||||
"version": "v1.2.3.b1",
|
||||
"release_date": "2026-07-27",
|
||||
"download": "https://github.com/Juzlus/jRandomSkills/releases/download/v1.2.3.b1/jRandomSkills.v1.2.3.b1.zip",
|
||||
"output_folder": "addons/counterstrikesharp/"
|
||||
}
|
||||
{
|
||||
"version": "v1.2.2.b9",
|
||||
"release_date": "2026-07-23",
|
||||
"download": "https://github.com/Juzlus/jRandomSkills/releases/download/v1.2.2.b9/jRandomSkills.v1.2.2.b9.zip",
|
||||
"output_folder": "addons/counterstrikesharp/"
|
||||
}
|
||||
{
|
||||
"version": "v1.2.2.b8",
|
||||
"release_date": "2026-07-14",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue