Compare commits
No commits in common. "main" and "v1.2.4.b2-panorama1" have entirely different histories.
main
...
v1.2.4.b2-
24 changed files with 147 additions and 714 deletions
21
.github/en.md
vendored
21
.github/en.md
vendored
|
|
@ -424,27 +424,6 @@ This plugin uses content from the following projects:
|
||||||
|
|
||||||
## 📋 Changelog
|
## 📋 Changelog
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>v1.2.4.b3</b></summary>
|
|
||||||
|
|
||||||
- #### General
|
|
||||||
- Updated compatibility with **CounterStrikeSharp v1.0.375**.
|
|
||||||
- **Removed RayTrace-CSS-API / RayTrace-MM** (ray tracing is now built into CounterStrikeSharp).
|
|
||||||
- **Updated gamedata** signatures and offsets for Windows/Linux.
|
|
||||||
- Skill distribution failures are now logged and retried.
|
|
||||||
- Spectators without a pawn are excluded from connected-player checks.
|
|
||||||
|
|
||||||
- #### Skill Fixes
|
|
||||||
- **No Recoil** - Spread and inaccuracy are now disabled server-side for accurate shots.
|
|
||||||
- **Long Zeus** - Kills now correctly count as Zeus kills, including killfeed, sound and ragdoll effects.
|
|
||||||
- **Long Knife** - Long-range hits now show a tracer.
|
|
||||||
- **Fragile Bomb** - Planted bombs are detected reliably and bullet paths are checked correctly.
|
|
||||||
- **Tripwire** - Beams are now removed correctly when the skill changes.
|
|
||||||
|
|
||||||
**Full update contributed by [@ByDexterTR](https://github.com/ByDexterTR) in pull request [#61](https://github.com/Juzlus/jRandomSkills/pull/61). Thanks to ByDexterTR!**
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>v1.2.4.b2</b></summary>
|
<summary><b>v1.2.4.b2</b></summary>
|
||||||
|
|
||||||
|
|
|
||||||
21
.github/pl.md
vendored
21
.github/pl.md
vendored
|
|
@ -424,27 +424,6 @@ Plugin korzysta z zawartości następujących projektów:
|
||||||
|
|
||||||
## 📋 Lista Zmian
|
## 📋 Lista Zmian
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>v1.2.4.b3</b></summary>
|
|
||||||
|
|
||||||
- #### Ogólne
|
|
||||||
- Zaktualizowano kompatybilność z **CounterStrikeSharp v1.0.375**.
|
|
||||||
- **Usunięto RayTrace-CSS-API / RayTrace-MM** (ray tracing jest teraz wbudowany w CounterStrikeSharp).
|
|
||||||
- **Zaktualizowano gamedata** - sygnatury i offsety dla Windows/Linux.
|
|
||||||
- Obserwatorzy bez pawna nie są już uwzględniani podczas sprawdzania liczby połączonych graczy.
|
|
||||||
- Błędy podczas przydzielania umiejętności są teraz obserwowane i ponawiane.
|
|
||||||
|
|
||||||
- #### Poprawki mocy
|
|
||||||
- **Skupienie** - Rozrzut i niecelność są teraz wyłączane również po stronie serwera, zapewniając celniejsze strzały.
|
|
||||||
- **Długi Zeus** - Zabójstwa są teraz poprawnie zaliczane jako zabójstwa z Zeusa, wraz z killfeedem, dźwiękiem i efektami ragdolla.
|
|
||||||
- **Długi Nóż** - Trafienia z dużego dystansu pokazują teraz ślad.
|
|
||||||
- **Krucha Bomba** - Podłożone bomby są teraz wykrywane poprawnie, a trajektoria pocisku jest prawidłowo sprawdzana.
|
|
||||||
- **"Linka z Drutu** - Wiązki są teraz poprawnie usuwane po zmianie umiejętności.
|
|
||||||
|
|
||||||
**Pełna aktualizacja została przygotowana przez [@ByDexterTR](https://github.com/ByDexterTR) w ramach pull requesta [#61](https://github.com/Juzlus/jRandomSkills/pull/61). Dziękujemy ByDexterTR!**
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>v1.2.4.b2</b></summary>
|
<summary><b>v1.2.4.b2</b></summary>
|
||||||
|
|
||||||
|
|
|
||||||
21
README-PL.md
21
README-PL.md
|
|
@ -441,27 +441,6 @@ Plugin korzysta z zawartości następujących projektów:
|
||||||
|
|
||||||
## 📋 Lista Zmian
|
## 📋 Lista Zmian
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>v1.2.4.b3</b></summary>
|
|
||||||
|
|
||||||
- #### Ogólne
|
|
||||||
- Zaktualizowano kompatybilność z **CounterStrikeSharp v1.0.375**.
|
|
||||||
- **Usunięto RayTrace-CSS-API / RayTrace-MM** (ray tracing jest teraz wbudowany w CounterStrikeSharp).
|
|
||||||
- **Zaktualizowano gamedata** - sygnatury i offsety dla Windows/Linux.
|
|
||||||
- Obserwatorzy bez pawna nie są już uwzględniani podczas sprawdzania liczby połączonych graczy.
|
|
||||||
- Błędy podczas przydzielania umiejętności są teraz obserwowane i ponawiane.
|
|
||||||
|
|
||||||
- #### Poprawki mocy
|
|
||||||
- **Skupienie** - Rozrzut i niecelność są teraz wyłączane również po stronie serwera, zapewniając celniejsze strzały.
|
|
||||||
- **Długi Zeus** - Zabójstwa są teraz poprawnie zaliczane jako zabójstwa z Zeusa, wraz z killfeedem, dźwiękiem i efektami ragdolla.
|
|
||||||
- **Długi Nóż** - Trafienia z dużego dystansu pokazują teraz ślad.
|
|
||||||
- **Krucha Bomba** - Podłożone bomby są teraz wykrywane poprawnie, a trajektoria pocisku jest prawidłowo sprawdzana.
|
|
||||||
- **"Linka z Drutu** - Wiązki są teraz poprawnie usuwane po zmianie umiejętności.
|
|
||||||
|
|
||||||
**Pełna aktualizacja została przygotowana przez [@ByDexterTR](https://github.com/ByDexterTR) w ramach pull requesta [#58](https://github.com/Juzlus/jRandomSkills/pull/58). Dziękujemy ByDexterTR!**
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>v1.2.4.b2</b></summary>
|
<summary><b>v1.2.4.b2</b></summary>
|
||||||
|
|
||||||
|
|
|
||||||
21
README.md
21
README.md
|
|
@ -441,27 +441,6 @@ This plugin uses content from the following projects:
|
||||||
|
|
||||||
## 📋 Changelog
|
## 📋 Changelog
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>v1.2.4.b3</b></summary>
|
|
||||||
|
|
||||||
- #### General
|
|
||||||
- Updated compatibility with **CounterStrikeSharp v1.0.375**.
|
|
||||||
- **Removed RayTrace-CSS-API / RayTrace-MM** (ray tracing is now built into CounterStrikeSharp).
|
|
||||||
- **Updated gamedata** signatures and offsets for Windows/Linux.
|
|
||||||
- Skill distribution failures are now logged and retried.
|
|
||||||
- Spectators without a pawn are excluded from connected-player checks.
|
|
||||||
|
|
||||||
- #### Skill Fixes
|
|
||||||
- **No Recoil** - Spread and inaccuracy are now disabled server-side for accurate shots.
|
|
||||||
- **Long Zeus** - Kills now correctly count as Zeus kills, including killfeed, sound and ragdoll effects.
|
|
||||||
- **Long Knife** - Long-range hits now show a tracer.
|
|
||||||
- **Fragile Bomb** - Planted bombs are detected reliably and bullet paths are checked correctly.
|
|
||||||
- **Tripwire** - Beams are now removed correctly when the skill changes.
|
|
||||||
|
|
||||||
**Full update contributed by [@ByDexterTR](https://github.com/ByDexterTR) in pull request [#61](https://github.com/Juzlus/jRandomSkills/pull/61). Thanks to ByDexterTR!**
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>v1.2.4.b2</b></summary>
|
<summary><b>v1.2.4.b2</b></summary>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Matches the server pin (pre.sh CSS_VERSION). PanoramaManager below needs 1.0.374+,
|
<!-- Matches the server pin (pre.sh CSS_VERSION). PanoramaManager below needs 1.0.374+,
|
||||||
the release that added the CCSCustomHudLayout API it drives. -->
|
the release that added the CCSCustomHudLayout API it drives. -->
|
||||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.376" />
|
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.375" />
|
||||||
<PackageReference Include="MaxMind.Db" Version="5.1.0">
|
<PackageReference Include="MaxMind.Db" Version="5.1.0">
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
|
@ -30,6 +30,12 @@
|
||||||
<PackageReference Include="PanoramaManager" Version="0.4.1" />
|
<PackageReference Include="PanoramaManager" Version="0.4.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RayTraceApi">
|
||||||
|
<HintPath>libs/RayTraceApi.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Properties\Resources.Designer.cs">
|
<Compile Update="Properties\Resources.Designer.cs">
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
|
|
|
||||||
BIN
jRandomSkills - SRC Files/libs/RayTraceApi.dll
Normal file
BIN
jRandomSkills - SRC Files/libs/RayTraceApi.dll
Normal file
Binary file not shown.
|
|
@ -3,20 +3,20 @@
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"library": "server",
|
"library": "server",
|
||||||
"windows": "48 8B C4 48 89 58 ? 48 89 68 ? 48 89 70 ? 57 41 56 41 57 48 81 EC ? ? ? ? 48 8B B4 24 ? ? ? ? 4D 8B F8",
|
"windows": "48 8B C4 48 89 58 ? 48 89 68 ? 48 89 70 ? 57 41 56 41 57 48 81 EC ? ? ? ? 48 8B B4 24 ? ? ? ? 4D 8B F8",
|
||||||
"linux": "55 4C 89 C1 48 89 E5 41 57 49 89 FF 41 56 45 89 CE 41 55 4D 89 C5 41 54 53 48 81 EC ? ? ? ? 48 89 95 ? ? ? ? 48 89 F2 48 89 FE 48 8D 3D"
|
"linux": "55 4C 89 C1 48 89 E5 41 57 49 89 FF 41 56 45 89 CE 41 55 4D 89 C5 41 54 53 48 83 EC ? 48 89 55 ? 48 89 F2 48 89 FE"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"HEGrenadeProjectile_CreateFunc": {
|
"HEGrenadeProjectile_CreateFunc": {
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"library": "server",
|
"library": "server",
|
||||||
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 48 8B AC 24 ? ? ? ? 49 8B F8 4C 8B C2 0F 29 74 24 ? 48 8B D1 0F 29 7C 24 ? 48 8B D9 4C 8B CD 48 8D 0D ? ? ? ? E8",
|
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 48 8B 6C 24 ? 49 8B F8 4C 8B C2 0F 29 74 24 ? 48 8B D1 48 8B D9 48 8D 0D ? ? ? ? 4C 8B CD E8 ? ? ? ? F3 0F 10 0D ? ? ? ? 48 8B C8 48 8B F0 E8 ? ? ? ? 48 8B D7 48 8B CE",
|
||||||
"linux": "55 4C 89 C1 48 89 E5 41 57 49 89 FF 41 56 49 89 D6 48 89 F2 48 89 FE 41 55 48 8D 3D ? ? ? ? 4D 89 C5 41 54 45 89 CC 53"
|
"linux": "55 4C 89 C1 48 89 E5 41 57 49 89 FF 41 56 49 89 D6 48 89 F2 48 89 FE 41 55 48 8D 3D ? ? ? ? 4D 89 C5 41 54 45 89 CC 53"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CMolotovProjectile_CreateFunc": {
|
"CMolotovProjectile_CreateFunc": {
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"library": "server",
|
"library": "server",
|
||||||
"windows": "48 8B C4 48 89 58 08 48 89 70 10 48 89 78 20 4C 89 40 18 55 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 45 33 FF",
|
"windows": "48 8B C4 48 89 58 10 4C 89 40 18 48 89 48 08",
|
||||||
"linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 41 55 41 54 49 89 FC 53 48 81 EC ? ? ? ? 4C 8D 35"
|
"linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 41 55 41 54 49 89 FC 53 48 81 EC ? ? ? ? 4C 8D 35"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
"SnapViewAngles": {
|
"SnapViewAngles": {
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"library": "server",
|
"library": "server",
|
||||||
"windows": "48 89 7C 24 ? 55 48 8B EC 48 81 EC ? ? ? ? 48 8B F9 48 8D 4D ? E8 ? ? ? ? F2 0F 10 87 ? ? ? ? 48 8B CF",
|
"windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B DA 48 8B F1 48 8D 55",
|
||||||
"linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC ? ? ? ? E8"
|
"linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC ? ? ? ? E8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -42,8 +42,8 @@
|
||||||
},
|
},
|
||||||
"INetworkGameServer_Slots": {
|
"INetworkGameServer_Slots": {
|
||||||
"offsets": {
|
"offsets": {
|
||||||
"windows": 616,
|
"windows": 584,
|
||||||
"linux": 616
|
"linux": 584
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"INetworkServerService_GetIGameServer": {
|
"INetworkServerService_GetIGameServer": {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ namespace src
|
||||||
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
||||||
public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)";
|
public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)";
|
||||||
public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus.";
|
public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus.";
|
||||||
public override string ModuleVersion => "1.2.4.b3-panorama1";
|
public override string ModuleVersion => "1.2.4.b2-panorama1";
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
|
|
@ -303,6 +303,9 @@ namespace src
|
||||||
{ "Newtonsoft Json", "./Newtonsoft.Json.dll" },
|
{ "Newtonsoft Json", "./Newtonsoft.Json.dll" },
|
||||||
{ "MaxMind", "./MaxMind.Db.dll" },
|
{ "MaxMind", "./MaxMind.Db.dll" },
|
||||||
{ "GeoLite2", "./packages/GeoLite2-Country.mmdb" },
|
{ "GeoLite2", "./packages/GeoLite2-Country.mmdb" },
|
||||||
|
{ "RayTraceApi", "./../../shared/RayTraceApi/RayTraceApi.dll" },
|
||||||
|
{ "RayTraceImpl", "./../../plugins/RayTraceImpl/RayTraceImpl.dll" },
|
||||||
|
{ "RayTrace MetaMod", "./../../../metamod/RayTrace.vdf" },
|
||||||
{ "jRandomSkills gamedata", "./../../gamedata/jRandomSkills.gamedata.json" }
|
{ "jRandomSkills gamedata", "./../../gamedata/jRandomSkills.gamedata.json" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,6 @@ namespace src.player
|
||||||
!Instance.SkillPlayer.Any(p => !p.IsDrawing && p.Skill == Skills.Fortnite))
|
!Instance.SkillPlayer.Any(p => !p.IsDrawing && p.Skill == Skills.Fortnite))
|
||||||
Instance.SkillAction("Fortnite", "OnTakeDamage", args);
|
Instance.SkillAction("Fortnite", "OnTakeDamage", args);
|
||||||
|
|
||||||
SkillUtils.ApplyNativeKill(entity, info);
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using CounterStrikeSharp.API.Modules.Memory;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
using RayTraceAPI;
|
||||||
using src.player.skills;
|
using src.player.skills;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
@ -107,7 +108,6 @@ namespace src.player
|
||||||
TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre));
|
TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre));
|
||||||
TryUnhook(() => Instance.UnhookUserMessage(208, PlayerMakeSound));
|
TryUnhook(() => Instance.UnhookUserMessage(208, PlayerMakeSound));
|
||||||
TryUnhook(() => Instance.RemoveListener<CheckTransmit>(CheckTransmit));
|
TryUnhook(() => Instance.RemoveListener<CheckTransmit>(CheckTransmit));
|
||||||
TryUnhook(NoRecoil.RestoreSpread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TryUnhook(Action unhook)
|
private static void TryUnhook(Action unhook)
|
||||||
|
|
@ -507,7 +507,6 @@ namespace src.player
|
||||||
private static void OnTick()
|
private static void OnTick()
|
||||||
{
|
{
|
||||||
ReportStall();
|
ReportStall();
|
||||||
NoRecoil.RestoreSpread();
|
|
||||||
|
|
||||||
long perfStart = PerfLog.Start();
|
long perfStart = PerfLog.Start();
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
@ -795,7 +794,7 @@ namespace src.player
|
||||||
Vector eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
|
Vector eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
|
||||||
Vector endPos = eyePos + SkillUtils.GetForwardVector(pawn.EyeAngles) * 80;
|
Vector endPos = eyePos + SkillUtils.GetForwardVector(pawn.EyeAngles) * 80;
|
||||||
|
|
||||||
ulong mask = (ulong)Masks.SolidBrushOnly | (ulong)(Contents.Player | Contents.Npc);
|
ulong mask = (ulong)(InteractionLayers.MASK_WORLD_ONLY | InteractionLayers.Player | InteractionLayers.NPC);
|
||||||
ulong contents = 0;
|
ulong contents = 0;
|
||||||
var result = RayTrace.TraceShape(player, eyePos, endPos, mask, contents);
|
var result = RayTrace.TraceShape(player, eyePos, endPos, mask, contents);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ namespace src.player
|
||||||
|
|
||||||
private static void OnMapStart(string mapName)
|
private static void OnMapStart(string mapName)
|
||||||
{
|
{
|
||||||
skills.NoRecoil.ForgetSpread();
|
|
||||||
PlayerManager.SetServerActive(true);
|
PlayerManager.SetServerActive(true);
|
||||||
Instance.GameRules = null;
|
Instance.GameRules = null;
|
||||||
Event.OnMapChange();
|
Event.OnMapChange();
|
||||||
|
|
@ -54,8 +53,6 @@ namespace src.player
|
||||||
|
|
||||||
private static void OnMapEnd()
|
private static void OnMapEnd()
|
||||||
{
|
{
|
||||||
skills.NoRecoil.RestoreSpread();
|
|
||||||
skills.NoRecoil.ForgetSpread();
|
|
||||||
PerfLog.Info("===== MAP END (clean map change) =====");
|
PerfLog.Info("===== MAP END (clean map change) =====");
|
||||||
Debug.WriteToDebug("===== MAP END (clean map change) =====");
|
Debug.WriteToDebug("===== MAP END (clean map change) =====");
|
||||||
BotManager.Stop();
|
BotManager.Stop();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using CounterStrikeSharp.API.Modules.Memory;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
using RayTraceAPI;
|
||||||
using src.player.skills;
|
using src.player.skills;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
@ -97,14 +98,14 @@ namespace src.player
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
bool isWarmup = IsWarmupPeriod();
|
bool isWarmup = Instance.GameRules == null || Instance.GameRules.WarmupPeriod == true;
|
||||||
isTransmitRegistered = false;
|
isTransmitRegistered = false;
|
||||||
setSkillRetries = 0;
|
setSkillRetries = 0;
|
||||||
SkillUtils.ClearKillCredits();
|
SkillUtils.ClearKillCredits();
|
||||||
SkillUtils.ClearCurses();
|
SkillUtils.ClearCurses();
|
||||||
Instance.AddTimer(.1f, () => DisableAll(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
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 && InPlayingTeam(p)))
|
foreach (var player in Utilities.GetPlayers().Where(p => p != null && p.IsValid && !p.IsHLTV && p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist))
|
||||||
{
|
{
|
||||||
var skillPlayer = PlayerManager.GetPlayerByIndex(player!.Index);
|
var skillPlayer = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (skillPlayer == null) continue;
|
if (skillPlayer == null) continue;
|
||||||
|
|
@ -114,21 +115,18 @@ namespace src.player
|
||||||
|
|
||||||
Instance.RemoveListener<CheckTransmit>(CheckTransmit);
|
Instance.RemoveListener<CheckTransmit>(CheckTransmit);
|
||||||
int freezetime = SkillUtils.CvarValue("mp_freezetime", 0);
|
int freezetime = SkillUtils.CvarValue("mp_freezetime", 0);
|
||||||
int introDelay = IsTeamIntroPeriod() ? 7 : 0;
|
freezeTimeEnd = DateTime.Now.AddSeconds(freezetime + (Instance?.GameRules?.TeamIntroPeriod == true ? 7 : 0));
|
||||||
freezeTimeEnd = DateTime.Now.AddSeconds(freezetime + introDelay);
|
|
||||||
|
|
||||||
setSkillTimer?.Kill();
|
setSkillTimer?.Kill();
|
||||||
|
|
||||||
if (isWarmup)
|
if (isWarmup)
|
||||||
{
|
{
|
||||||
setSkillTimer = Instance?.AddTimer(1f, SetSkill, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
setSkillTimer = Instance?.AddTimer(1f, SetSkill, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
Debug.WriteToDebug("RoundStart: warmup, SetSkill scheduled in 1.00s.", DebugCategory.Round);
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float timeToDraw = introDelay + Math.Max(freezetime - Config.LoadedConfig.SkillTimeBeforeStart, 0) + .3f;
|
float timeToDraw = (Instance?.GameRules?.TeamIntroPeriod == true ? 7 : 0) + Math.Max(freezetime - Config.LoadedConfig.SkillTimeBeforeStart, 0) + .3f;
|
||||||
setSkillTimer = Instance?.AddTimer(timeToDraw, SetSkill, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
setSkillTimer = Instance?.AddTimer(timeToDraw, SetSkill, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
Debug.WriteToDebug($"RoundStart: SetSkill scheduled in {timeToDraw:F2}s (freezetime={freezetime}, intro={introDelay}, timer={(setSkillTimer == null ? "null" : "ok")}).", DebugCategory.Round);
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -295,22 +293,7 @@ namespace src.player
|
||||||
private static void SetSkill()
|
private static void SetSkill()
|
||||||
{
|
{
|
||||||
long perfStart = PerfLog.Start();
|
long perfStart = PerfLog.Start();
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SetSkillCore();
|
SetSkillCore();
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteToDebug($"SetSkill threw: {ex}", DebugCategory.Skill);
|
|
||||||
|
|
||||||
if (setSkillRetries < MaxSetSkillRetries)
|
|
||||||
{
|
|
||||||
setSkillRetries++;
|
|
||||||
setSkillTimer = Instance?.AddTimer(.5f, SetSkill, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PerfLog.End("SetSkill total", perfStart, 2.0);
|
PerfLog.End("SetSkill total", perfStart, 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,46 +472,10 @@ namespace src.player
|
||||||
|
|
||||||
private static int CountConnectedPlayers()
|
private static int CountConnectedPlayers()
|
||||||
{
|
{
|
||||||
try
|
try { return Utilities.GetPlayers().Count(p => p != null && p.IsValid && !p.IsHLTV); }
|
||||||
{
|
|
||||||
return Utilities.GetPlayers().Count(p =>
|
|
||||||
{
|
|
||||||
if (p == null || !p.IsValid || p.IsHLTV) return false;
|
|
||||||
var pawn = p.PlayerPawn?.Value;
|
|
||||||
return pawn != null && pawn.IsValid;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch { return 0; }
|
catch { return 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool InPlayingTeam(CCSPlayerController player)
|
|
||||||
{
|
|
||||||
try { return player.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist; }
|
|
||||||
catch { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsWarmupPeriod()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var gameRules = Instance?.GameRules;
|
|
||||||
if (gameRules == null || gameRules.Handle == IntPtr.Zero) return true;
|
|
||||||
return gameRules.WarmupPeriod;
|
|
||||||
}
|
|
||||||
catch { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTeamIntroPeriod()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var gameRules = Instance?.GameRules;
|
|
||||||
if (gameRules == null || gameRules.Handle == IntPtr.Zero) return false;
|
|
||||||
return gameRules.TeamIntroPeriod;
|
|
||||||
}
|
|
||||||
catch { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetSkillCore()
|
private static void SetSkillCore()
|
||||||
{
|
{
|
||||||
setSkillTimer = null;
|
setSkillTimer = null;
|
||||||
|
|
@ -537,7 +484,7 @@ namespace src.player
|
||||||
if (Instance == null) return;
|
if (Instance == null) return;
|
||||||
|
|
||||||
// GameRules null = not ready; keep polling so skills land right after warmup ends.
|
// GameRules null = not ready; keep polling so skills land right after warmup ends.
|
||||||
if (IsWarmupPeriod())
|
if (Instance.GameRules == null || Instance.GameRules.WarmupPeriod == true)
|
||||||
{
|
{
|
||||||
if (++gameRulesPolls % 10 == 0)
|
if (++gameRulesPolls % 10 == 0)
|
||||||
Debug.WriteToDebug($"SetSkill waiting: gameRules={(Instance.GameRules == null ? "null" : "warmup")}, poll {gameRulesPolls}.", DebugCategory.Skill);
|
Debug.WriteToDebug($"SetSkill waiting: gameRules={(Instance.GameRules == null ? "null" : "warmup")}, poll {gameRulesPolls}.", DebugCategory.Skill);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Timers;
|
using CounterStrikeSharp.API.Modules.Timers;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
using RayTraceAPI;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
@ -340,7 +341,7 @@ namespace src.player.skills
|
||||||
Vector eyePos = new(playerPawn.AbsOrigin.X, playerPawn.AbsOrigin.Y, playerPawn.AbsOrigin.Z + playerPawn.ViewOffset.Z);
|
Vector eyePos = new(playerPawn.AbsOrigin.X, playerPawn.AbsOrigin.Y, playerPawn.AbsOrigin.Z + playerPawn.ViewOffset.Z);
|
||||||
Vector endPos = eyePos + SkillUtils.GetForwardVector(playerPawn.V_angle) * 4096f;
|
Vector endPos = eyePos + SkillUtils.GetForwardVector(playerPawn.V_angle) * 4096f;
|
||||||
|
|
||||||
ulong mask = (ulong)(Contents.Solid | Contents.Window | Contents.PassBullets);
|
ulong mask = (ulong)(InteractionLayers.Solid | InteractionLayers.Window | InteractionLayers.PassBullets);
|
||||||
var result = RayTrace.TraceShape(player, eyePos, endPos, mask);
|
var result = RayTrace.TraceShape(player, eyePos, endPos, mask);
|
||||||
|
|
||||||
if (result.HasValue && result.Value.HitWorld(out _))
|
if (result.HasValue && result.Value.HitWorld(out _))
|
||||||
|
|
@ -369,7 +370,7 @@ namespace src.player.skills
|
||||||
Vector endPos = cameraVector + SkillUtils.GetForwardVector(new QAngle(0, playerPawn.V_angle.Y + 180, 0)) * -5;
|
Vector endPos = cameraVector + SkillUtils.GetForwardVector(new QAngle(0, playerPawn.V_angle.Y + 180, 0)) * -5;
|
||||||
cameraVector += SkillUtils.GetForwardVector(new QAngle(0, playerPawn.V_angle.Y + 180, 0)) * 5;
|
cameraVector += SkillUtils.GetForwardVector(new QAngle(0, playerPawn.V_angle.Y + 180, 0)) * 5;
|
||||||
|
|
||||||
ulong mask = (ulong)(Contents.Solid | Contents.Window | Contents.PassBullets);
|
ulong mask = (ulong)(InteractionLayers.Solid | InteractionLayers.Window | InteractionLayers.PassBullets);
|
||||||
var result = RayTrace.TraceShape(player, cameraVector, endPos, mask);
|
var result = RayTrace.TraceShape(player, cameraVector, endPos, mask);
|
||||||
|
|
||||||
if (result.HasValue && result.Value.HitWorld(out _))
|
if (result.HasValue && result.Value.HitWorld(out _))
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,10 @@ namespace src.player.skills
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BombPlanted(EventBombPlanted _)
|
public static void BombPlanted(EventBombPlanted _)
|
||||||
{
|
|
||||||
plantedC4 = FindPlantedC4();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector? FindPlantedC4()
|
|
||||||
{
|
{
|
||||||
var plantedBomb = Utilities.FindAllEntitiesByDesignerName<CPlantedC4>("planted_c4").FirstOrDefault();
|
var plantedBomb = Utilities.FindAllEntitiesByDesignerName<CPlantedC4>("planted_c4").FirstOrDefault();
|
||||||
if (plantedBomb == null || !plantedBomb.IsValid || plantedBomb.AbsOrigin == null) return null;
|
if (plantedBomb == null || !plantedBomb.IsValid || plantedBomb.AbsOrigin == null) return;
|
||||||
if (!plantedBomb.BombTicking || plantedBomb.BombDefused) return null;
|
plantedC4 = new(plantedBomb.AbsOrigin.X, plantedBomb.AbsOrigin.Y, plantedBomb.AbsOrigin.Z);
|
||||||
return new(plantedBomb.AbsOrigin.X, plantedBomb.AbsOrigin.Y, plantedBomb.AbsOrigin.Z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RemoveBomb()
|
private static void RemoveBomb()
|
||||||
|
|
@ -55,21 +49,14 @@ namespace src.player.skills
|
||||||
if (lastTick == Server.TickCount) return;
|
if (lastTick == Server.TickCount) return;
|
||||||
|
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid || plantedC4 == null) return;
|
||||||
|
|
||||||
|
var pos = new Vector(@event.X, @event.Y, @event.Z);
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (playerInfo == null || playerInfo.Skill != skillName) return;
|
if (playerInfo == null || playerInfo.Skill != skillName) return;
|
||||||
|
|
||||||
plantedC4 ??= FindPlantedC4();
|
if (SkillUtils.Distance(plantedC4, pos) >= 8)
|
||||||
if (plantedC4 == null) return;
|
|
||||||
|
|
||||||
var pawn = player.PlayerPawn.Value;
|
|
||||||
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
|
|
||||||
|
|
||||||
var eyePos = new Vector(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
|
|
||||||
var impactPos = new Vector(@event.X, @event.Y, @event.Z);
|
|
||||||
|
|
||||||
if (DistanceToSegment(plantedC4, eyePos, impactPos) >= 8)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lastTick = Server.TickCount;
|
lastTick = Server.TickCount;
|
||||||
|
|
@ -84,16 +71,6 @@ namespace src.player.skills
|
||||||
Localization.PrintTranslationToChatAll($" {ChatColors.Gold}{{0}}: {ChatColors.Red}{bombHealth}{ChatColors.Gold}/{ChatColors.Green}{maxBombHealth}", ["fragilebomb_bomb_health"]);
|
Localization.PrintTranslationToChatAll($" {ChatColors.Gold}{{0}}: {ChatColors.Red}{bombHealth}{ChatColors.Gold}/{ChatColors.Green}{maxBombHealth}", ["fragilebomb_bomb_health"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float DistanceToSegment(Vector point, Vector start, Vector end)
|
|
||||||
{
|
|
||||||
float lineX = end.X - start.X, lineY = end.Y - start.Y, lineZ = end.Z - start.Z;
|
|
||||||
float lengthSquared = lineX * lineX + lineY * lineY + lineZ * lineZ;
|
|
||||||
float t = lengthSquared <= .0001f ? 0f : Math.Clamp(((point.X - start.X) * lineX + (point.Y - start.Y) * lineY + (point.Z - start.Z) * lineZ) / lengthSquared, 0f, 1f);
|
|
||||||
|
|
||||||
float dx = start.X + lineX * t - point.X, dy = start.Y + lineY * t - point.Y, dz = start.Z + lineZ * t - point.Z;
|
|
||||||
return MathF.Sqrt(dx * dx + dy * dy + dz * dz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Common, int maxBombHealth = 1000) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 1, Rarity rarity = Rarity.Common, int maxBombHealth = 1000) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public int MaxBombHealth { get; set; } = maxBombHealth;
|
public int MaxBombHealth { get; set; } = maxBombHealth;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ namespace src.player.skills
|
||||||
private const string victimSound = "Player.DamageBody.Victim";
|
private const string victimSound = "Player.DamageBody.Victim";
|
||||||
private const string heavyHitSound = "Weapon_Knife.Hit.Heavy.Flesh";
|
private const string heavyHitSound = "Weapon_Knife.Hit.Heavy.Flesh";
|
||||||
private const string lightHitSound = "Weapon_Knife.Hit.Light.Flesh";
|
private const string lightHitSound = "Weapon_Knife.Hit.Light.Flesh";
|
||||||
private const string tracerParticle = "particles/weapons/cs_weapon_fx/weapon_tracers_rifle_wisp.vpcf";
|
|
||||||
|
|
||||||
private static bool hooked = false;
|
private static bool hooked = false;
|
||||||
private const int actionCode = 503;
|
private const int actionCode = 503;
|
||||||
|
|
@ -31,7 +30,6 @@ namespace src.player.skills
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
Instance.AddToManifest(tracerParticle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
|
|
@ -115,9 +113,6 @@ namespace src.player.skills
|
||||||
if (result == null || !result.HasValue)
|
if (result == null || !result.HasValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (result.Value.Distance() > 70)
|
|
||||||
SkillUtils.CreateTracer(player, tracerParticle, result.Value);
|
|
||||||
|
|
||||||
if (!result.Value.HitPlayer(out CCSPlayerController? target) || target == null)
|
if (!result.Value.HitPlayer(out CCSPlayerController? target) || target == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,10 @@ namespace src.player.skills
|
||||||
public class LongZeus : ISkill
|
public class LongZeus : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.LongZeus;
|
private const Skills skillName = Skills.LongZeus;
|
||||||
private const string shockParticle = "particles/blood_impact/impact_taser_bodyfx.vpcf";
|
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
Instance.AddToManifest(shockParticle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static void WeaponFire(EventWeaponFire @event)
|
public unsafe static void WeaponFire(EventWeaponFire @event)
|
||||||
|
|
@ -42,12 +40,7 @@ namespace src.player.skills
|
||||||
if (target.Handle == player.Handle) return;
|
if (target.Handle == player.Handle) return;
|
||||||
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire") && player.Team == target.Team) return;
|
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire") && player.Team == target.Team) return;
|
||||||
|
|
||||||
var targetPawn = target.PlayerPawn.Value;
|
SkillUtils.TakeHealth(target.PlayerPawn.Value, 9999, player, KillfeedIcons.Taser);
|
||||||
if (targetPawn == null || !targetPawn.IsValid) return;
|
|
||||||
|
|
||||||
EntityManager.CreateEntityParticle(player.Index, shockParticle, targetPawn);
|
|
||||||
SkillUtils.RegisterNativeKill(targetPawn, pawn, activeWeapon, DamageTypes_t.DMG_SHOCK);
|
|
||||||
SkillUtils.TakeHealth(targetPawn, 9999);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnableSkill(CCSPlayerController player)
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
|
|
|
||||||
|
|
@ -17,27 +17,6 @@ namespace src.player.skills
|
||||||
private static readonly ConcurrentDictionary<uint, byte> holders = [];
|
private static readonly ConcurrentDictionary<uint, byte> holders = [];
|
||||||
private static readonly ConcurrentDictionary<uint, string> originalNoSpreadValues = [];
|
private static readonly ConcurrentDictionary<uint, string> originalNoSpreadValues = [];
|
||||||
|
|
||||||
private sealed class SpreadPatch(CCSWeaponBaseVData vdata)
|
|
||||||
{
|
|
||||||
public readonly CCSWeaponBaseVData VData = vdata;
|
|
||||||
public readonly CFiringModeFloat[] Fields =
|
|
||||||
[
|
|
||||||
vdata.Spread, vdata.InaccuracyCrouch, vdata.InaccuracyStand, vdata.InaccuracyJump,
|
|
||||||
vdata.InaccuracyLand, vdata.InaccuracyLadder, vdata.InaccuracyFire, vdata.InaccuracyMove
|
|
||||||
];
|
|
||||||
public readonly float[] Values = new float[16];
|
|
||||||
public int Count;
|
|
||||||
public float JumpInitial;
|
|
||||||
public float JumpApex;
|
|
||||||
public float Reload;
|
|
||||||
public bool Active;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Dictionary<nint, SpreadPatch> spreadPatches = [];
|
|
||||||
private static readonly List<SpreadPatch> activePatches = [];
|
|
||||||
private static readonly Dictionary<nint, CCSWeaponBaseVData> wantedVData = [];
|
|
||||||
private static readonly HashSet<nint> blockedVData = [];
|
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
|
|
@ -104,106 +83,6 @@ namespace src.player.skills
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTick()
|
|
||||||
{
|
|
||||||
if (holders.IsEmpty) return;
|
|
||||||
|
|
||||||
wantedVData.Clear();
|
|
||||||
blockedVData.Clear();
|
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
|
||||||
{
|
|
||||||
if (player == null || !player.IsValid || !player.PawnIsAlive) continue;
|
|
||||||
|
|
||||||
var vdata = GetActiveWeaponVData(player);
|
|
||||||
if (vdata == null) continue;
|
|
||||||
|
|
||||||
bool isHolder = holders.ContainsKey(player.Index) && PlayerManager.GetPlayerByIndex(player.Index)?.Skill == skillName;
|
|
||||||
if (isHolder)
|
|
||||||
wantedVData[vdata.Handle] = vdata;
|
|
||||||
else
|
|
||||||
blockedVData.Add(vdata.Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (handle, vdata) in wantedVData)
|
|
||||||
{
|
|
||||||
if (!blockedVData.Contains(handle))
|
|
||||||
PatchSpread(vdata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CCSWeaponBaseVData? GetActiveWeaponVData(CCSPlayerController player)
|
|
||||||
{
|
|
||||||
var weapon = player.PlayerPawn.Value?.WeaponServices?.ActiveWeapon.Value;
|
|
||||||
if (weapon == null || !weapon.IsValid) return null;
|
|
||||||
|
|
||||||
var vdata = weapon.As<CCSWeaponBase>().VData;
|
|
||||||
return vdata == null || vdata.Handle == IntPtr.Zero ? null : vdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PatchSpread(CCSWeaponBaseVData vdata)
|
|
||||||
{
|
|
||||||
if (!spreadPatches.TryGetValue(vdata.Handle, out var patch))
|
|
||||||
{
|
|
||||||
patch = new SpreadPatch(vdata);
|
|
||||||
spreadPatches[vdata.Handle] = patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (patch.Active) return;
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
foreach (var field in patch.Fields)
|
|
||||||
{
|
|
||||||
var values = field.Values;
|
|
||||||
for (int i = 0; i < values.Length && index < patch.Values.Length; i++)
|
|
||||||
{
|
|
||||||
patch.Values[index++] = values[i];
|
|
||||||
values[i] = 0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patch.Count = index;
|
|
||||||
patch.JumpInitial = vdata.InaccuracyJumpInitial;
|
|
||||||
patch.JumpApex = vdata.InaccuracyJumpApex;
|
|
||||||
patch.Reload = vdata.InaccuracyReload;
|
|
||||||
|
|
||||||
vdata.InaccuracyJumpInitial = 0f;
|
|
||||||
vdata.InaccuracyJumpApex = 0f;
|
|
||||||
vdata.InaccuracyReload = 0f;
|
|
||||||
|
|
||||||
patch.Active = true;
|
|
||||||
activePatches.Add(patch);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RestoreSpread()
|
|
||||||
{
|
|
||||||
if (activePatches.Count == 0) return;
|
|
||||||
|
|
||||||
foreach (var patch in activePatches)
|
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
foreach (var field in patch.Fields)
|
|
||||||
{
|
|
||||||
var values = field.Values;
|
|
||||||
for (int i = 0; i < values.Length && index < patch.Count; i++)
|
|
||||||
values[i] = patch.Values[index++];
|
|
||||||
}
|
|
||||||
|
|
||||||
patch.VData.InaccuracyJumpInitial = patch.JumpInitial;
|
|
||||||
patch.VData.InaccuracyJumpApex = patch.JumpApex;
|
|
||||||
patch.VData.InaccuracyReload = patch.Reload;
|
|
||||||
patch.Active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
activePatches.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ForgetSpread()
|
|
||||||
{
|
|
||||||
activePatches.Clear();
|
|
||||||
spreadPatches.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WeaponFire(EventWeaponFire @event)
|
public static void WeaponFire(EventWeaponFire @event)
|
||||||
{
|
{
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using jRandomSkills.src.utils;
|
using jRandomSkills.src.utils;
|
||||||
|
using RayTraceAPI;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
@ -21,7 +22,7 @@ namespace src.player.skills
|
||||||
private static readonly List<Flight> flights = [];
|
private static readonly List<Flight> flights = [];
|
||||||
private static readonly object flightLock = new();
|
private static readonly object flightLock = new();
|
||||||
private static readonly Color tracerColor = Color.FromArgb(170, 255, 226, 140);
|
private static readonly Color tracerColor = Color.FromArgb(170, 255, 226, 140);
|
||||||
private static readonly ulong worldOnlyMask = (ulong)Masks.SolidBrushOnly;
|
private static readonly ulong worldOnlyMask = (ulong)InteractionLayers.MASK_WORLD_ONLY;
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
using RayTraceAPI;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
@ -103,7 +104,19 @@ namespace src.player.skills
|
||||||
|
|
||||||
private static void DestroyWire(uint beamIndex)
|
private static void DestroyWire(uint beamIndex)
|
||||||
{
|
{
|
||||||
EntityManager.DestroyBeam(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)
|
public static void UseSkill(CCSPlayerController player)
|
||||||
|
|
@ -162,7 +175,7 @@ namespace src.player.skills
|
||||||
Vector rightDir = SkillUtils.GetForwardVector(new QAngle(0, yaw - 90, 0));
|
Vector rightDir = SkillUtils.GetForwardVector(new QAngle(0, yaw - 90, 0));
|
||||||
Vector leftDir = SkillUtils.GetForwardVector(new QAngle(0, yaw + 90, 0));
|
Vector leftDir = SkillUtils.GetForwardVector(new QAngle(0, yaw + 90, 0));
|
||||||
|
|
||||||
ulong wallMask = (ulong)Masks.SolidBrushOnly;
|
ulong wallMask = (ulong)InteractionLayers.MASK_WORLD_ONLY;
|
||||||
|
|
||||||
var rightHit = RayTrace.TraceShape(player, origin, origin + rightDir * maxDistance, wallMask, 0);
|
var rightHit = RayTrace.TraceShape(player, origin, origin + rightDir * maxDistance, wallMask, 0);
|
||||||
var leftHit = RayTrace.TraceShape(player, origin, origin + leftDir * maxDistance, wallMask, 0);
|
var leftHit = RayTrace.TraceShape(player, origin, origin + leftDir * maxDistance, wallMask, 0);
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,30 @@
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using RayTraceAPI;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||||
|
|
||||||
namespace jRandomSkills.src.utils
|
namespace jRandomSkills.src.utils
|
||||||
{
|
{
|
||||||
public struct CustomTraceResult
|
public struct CustomTraceResult(TraceResult result, Vector startPos, ulong mask, ulong contents, bool drawBeam)
|
||||||
{
|
{
|
||||||
public float StartPosX;
|
public float StartPosX = startPos.X;
|
||||||
public float StartPosY;
|
public float StartPosY = startPos.Y;
|
||||||
public float StartPosZ;
|
public float StartPosZ = startPos.Z;
|
||||||
|
|
||||||
public float EndPosX;
|
public float EndPosX = result.EndPosX;
|
||||||
public float EndPosY;
|
public float EndPosY = result.EndPosY;
|
||||||
public float EndPosZ;
|
public float EndPosZ = result.EndPosZ;
|
||||||
|
|
||||||
public nint HitEntity;
|
public nint HitEntity = result.HitEntity;
|
||||||
public float Fraction;
|
public float Fraction = result.Fraction;
|
||||||
public int AllSolid;
|
public int AllSolid = result.AllSolid;
|
||||||
|
|
||||||
public float NormalX;
|
public float NormalX = result.NormalX;
|
||||||
public float NormalY;
|
public float NormalY = result.NormalY;
|
||||||
public float NormalZ;
|
public float NormalZ = result.NormalZ;
|
||||||
|
|
||||||
public ulong InteractsWith;
|
public ulong InteractsWith = mask;
|
||||||
public ulong InteractsExclude;
|
public ulong InteractsExclude = contents;
|
||||||
public bool DrawBeam;
|
public bool DrawBeam = drawBeam;
|
||||||
|
|
||||||
public CustomTraceResult(TraceResult result, Vector startPos, ulong mask, ulong contents, bool drawBeam)
|
|
||||||
{
|
|
||||||
StartPosX = startPos.X;
|
|
||||||
StartPosY = startPos.Y;
|
|
||||||
StartPosZ = startPos.Z;
|
|
||||||
|
|
||||||
var end = result.EndPos;
|
|
||||||
EndPosX = end.X;
|
|
||||||
EndPosY = end.Y;
|
|
||||||
EndPosZ = end.Z;
|
|
||||||
|
|
||||||
var normal = result.Normal;
|
|
||||||
NormalX = normal.X;
|
|
||||||
NormalY = normal.Y;
|
|
||||||
NormalZ = normal.Z;
|
|
||||||
|
|
||||||
HitEntity = result.HitEntity().Handle;
|
|
||||||
Fraction = result.Fraction;
|
|
||||||
AllSolid = result.IsAllSolid ? 1 : 0;
|
|
||||||
|
|
||||||
InteractsWith = mask;
|
|
||||||
InteractsExclude = contents;
|
|
||||||
DrawBeam = drawBeam;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly Vector3 StartPos => new(StartPosX, StartPosY, StartPosZ);
|
public readonly Vector3 StartPos => new(StartPosX, StartPosY, StartPosZ);
|
||||||
public readonly Vector3 EndPos => new(EndPosX, EndPosY, EndPosZ);
|
public readonly Vector3 EndPos => new(EndPosX, EndPosY, EndPosZ);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||||
using CounterStrikeSharp.API.Modules.Memory;
|
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using src.player;
|
using src.player;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using static src.jRandomSkills;
|
using static src.jRandomSkills;
|
||||||
|
|
||||||
namespace src.utils
|
namespace src.utils
|
||||||
|
|
@ -138,67 +136,6 @@ namespace src.utils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CParticleSystem? CreateTracer(uint playerIndex, string particleName, Vector start, Vector end, float lifetime = 1f)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (OverBudget()) return null;
|
|
||||||
var particle = Utilities.CreateEntityByName<CParticleSystem>("info_particle_system");
|
|
||||||
if (particle == null || !particle.IsValid) return null;
|
|
||||||
|
|
||||||
particle.EffectName = particleName;
|
|
||||||
particle.StartActive = true;
|
|
||||||
particle.Teleport(start);
|
|
||||||
|
|
||||||
var controlPoint = new Vector(particle.Handle + Schema.GetSchemaOffset("CParticleSystem", "m_vServerControlPoints"));
|
|
||||||
controlPoint.X = end.X;
|
|
||||||
controlPoint.Y = end.Y;
|
|
||||||
controlPoint.Z = end.Z;
|
|
||||||
particle.ServerControlPointAssignments[0] = 1;
|
|
||||||
|
|
||||||
particle.DispatchSpawn();
|
|
||||||
|
|
||||||
RegisterEntity(particle.Index, playerIndex, "tracer");
|
|
||||||
ScheduleAutoDestroy(particle.Index, lifetime);
|
|
||||||
return particle;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogEntityError($"CreateTracer: {ex.Message}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CParticleSystem? CreateEntityParticle(uint playerIndex, string particleName, CBaseEntity target, float lifetime = 5f)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (OverBudget() || target.AbsOrigin == null) return null;
|
|
||||||
var particle = Utilities.CreateEntityByName<CParticleSystem>("info_particle_system");
|
|
||||||
if (particle == null || !particle.IsValid) return null;
|
|
||||||
|
|
||||||
particle.EffectName = particleName;
|
|
||||||
particle.StartActive = true;
|
|
||||||
particle.Teleport(target.AbsOrigin);
|
|
||||||
|
|
||||||
nint controlPointEnts = particle.Handle + Schema.GetSchemaOffset("CParticleSystem", "m_hControlPointEnts");
|
|
||||||
Marshal.WriteInt32(controlPointEnts, (int)target.EntityHandle.Raw);
|
|
||||||
Marshal.WriteInt32(controlPointEnts + 4, (int)target.EntityHandle.Raw);
|
|
||||||
|
|
||||||
particle.DispatchSpawn();
|
|
||||||
particle.AcceptInput("SetParent", target, particle, "!activator");
|
|
||||||
|
|
||||||
RegisterEntity(particle.Index, playerIndex, "particle_system");
|
|
||||||
ScheduleAutoDestroy(particle.Index, lifetime);
|
|
||||||
return particle;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogEntityError($"CreateEntityParticle: {ex.Message}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CDynamicProp? CreateTrackedDynamicProp(uint playerIndex, string designerName = "prop_dynamic", string? trackAs = null)
|
public static CDynamicProp? CreateTrackedDynamicProp(uint playerIndex, string designerName = "prop_dynamic", string? trackAs = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -358,7 +295,7 @@ namespace src.utils
|
||||||
|
|
||||||
public static bool SuppressKills = false;
|
public static bool SuppressKills = false;
|
||||||
|
|
||||||
public static bool DestroyEntity(uint entityIndex, float delay = 0.1f, bool hideFromTransmit = true)
|
public static bool DestroyEntity(uint entityIndex, float delay = 0.1f)
|
||||||
{
|
{
|
||||||
bool wasTracked = trackedEntities.TryRemove(entityIndex, out var removed);
|
bool wasTracked = trackedEntities.TryRemove(entityIndex, out var removed);
|
||||||
|
|
||||||
|
|
@ -373,7 +310,6 @@ namespace src.utils
|
||||||
var entity = Utilities.GetEntityFromIndex<CBaseEntity>((int)entityIndex);
|
var entity = Utilities.GetEntityFromIndex<CBaseEntity>((int)entityIndex);
|
||||||
if (entity != null && entity.IsValid)
|
if (entity != null && entity.IsValid)
|
||||||
{
|
{
|
||||||
if (hideFromTransmit)
|
|
||||||
recentlyDestroyed[entityIndex] = new DyingEntity(DateTime.UtcNow.AddSeconds(delay + 0.5), entity.EntityHandle.Raw);
|
recentlyDestroyed[entityIndex] = new DyingEntity(DateTime.UtcNow.AddSeconds(delay + 0.5), entity.EntityHandle.Raw);
|
||||||
// Detach first so no follower is left on a freed parent.
|
// Detach first so no follower is left on a freed parent.
|
||||||
entity.AcceptInput("ClearParent");
|
entity.AcceptInput("ClearParent");
|
||||||
|
|
@ -410,7 +346,7 @@ namespace src.utils
|
||||||
LogEntityError($"DestroyBeam {entityIndex}: {ex.Message}");
|
LogEntityError($"DestroyBeam {entityIndex}: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool killed = DestroyEntity(entityIndex, hideFromTransmit: false);
|
bool killed = DestroyEntity(entityIndex);
|
||||||
|
|
||||||
if (!SuppressKills)
|
if (!SuppressKills)
|
||||||
Server.NextFrame(() =>
|
Server.NextFrame(() =>
|
||||||
|
|
@ -467,7 +403,7 @@ namespace src.utils
|
||||||
DestroyAllTracked();
|
DestroyAllTracked();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ScheduleAutoDestroy(uint entityIndex, float? seconds)
|
private static void ScheduleAutoDestroy(uint entityIndex, float? seconds)
|
||||||
{
|
{
|
||||||
if (seconds is not > 0) return;
|
if (seconds is not > 0) return;
|
||||||
Instance?.AddTimer(seconds.Value, () => DestroyEntity(entityIndex), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
Instance?.AddTimer(seconds.Value, () => DestroyEntity(entityIndex), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,45 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using CounterStrikeSharp.API.Core.Capabilities;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using jRandomSkills.src.utils;
|
using jRandomSkills.src.utils;
|
||||||
|
using RayTraceAPI;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using TraceOptions = RayTraceAPI.TraceOptions;
|
||||||
|
using TraceResult = RayTraceAPI.TraceResult;
|
||||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||||
|
|
||||||
namespace src.utils
|
namespace src.utils
|
||||||
{
|
{
|
||||||
public static class RayTrace
|
public static class RayTrace
|
||||||
{
|
{
|
||||||
|
private static PluginCapability<CRayTraceInterface> RayTraceInterface { get; } = new("raytrace:craytraceinterface");
|
||||||
|
|
||||||
private static bool traceFailureLogged;
|
private static bool missingModuleLogged;
|
||||||
|
|
||||||
public static bool IsAvailable => true;
|
private static CRayTraceInterface? GetInterface()
|
||||||
|
|
||||||
private static bool TryTraceHull(Vector startPos, Vector endPos, Vector mins, Vector maxs, CBaseEntity? ignore, ulong mask, ulong contents, out TraceResult result)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = Trace.TraceHullShape(startPos, endPos, mins, maxs, ignore, new TraceOptions
|
var rayTrace = RayTraceInterface.Get();
|
||||||
|
if (rayTrace != null) return rayTrace;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (!missingModuleLogged)
|
||||||
{
|
{
|
||||||
InteractsWith = (Contents)mask,
|
missingModuleLogged = true;
|
||||||
InteractsExclude = (Contents)contents,
|
Server.PrintToConsole("[jRandomSkills] RayTrace module not found - skills that need it do nothing: " +
|
||||||
});
|
"LongZeus, LongKnife, Iana, Cypher, Noclip, Shade (and the skill-use button's aim check). " +
|
||||||
return true;
|
"Install RayTrace-CSS-API and RayTrace-MM: https://github.com/FUNPLAY-pro-CS2/Ray-Trace/releases");
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
result = default;
|
|
||||||
if (!traceFailureLogged)
|
|
||||||
{
|
|
||||||
traceFailureLogged = true;
|
|
||||||
Server.PrintToConsole($"[jRandomSkills] Native trace failed: {ex.Message}");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsAvailable => GetInterface() != null;
|
||||||
|
|
||||||
public static CustomTraceResult? TraceShape(CCSPlayerController player, Vector startPos, Vector endPos, ulong? mask = null, ulong? contents = null)
|
public static CustomTraceResult? TraceShape(CCSPlayerController player, Vector startPos, Vector endPos, ulong? mask = null, ulong? contents = null)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return null;
|
if (player == null || !player.IsValid) return null;
|
||||||
|
|
@ -51,39 +53,54 @@ namespace src.utils
|
||||||
playerPawn.CBodyComponent?.SceneNode == null)
|
playerPawn.CBodyComponent?.SceneNode == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
var rayTrace = GetInterface();
|
||||||
|
if (rayTrace == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
if (mask == null)
|
if (mask == null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (playerPawn.Collision?.CollisionAttribute != null)
|
if (playerPawn.Collision?.CollisionAttribute != null)
|
||||||
{
|
{
|
||||||
mask = playerPawn.Collision.CollisionAttribute.InteractsWith | (ulong)Contents.Hitbox;
|
mask = playerPawn.Collision.CollisionAttribute.InteractsWith | (ulong)InteractionLayers.Hitboxes;
|
||||||
mask &= ~(ulong)Contents.PlayerClip;
|
mask &= ~(ulong)InteractionLayers.PlayerClip;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mask = (ulong)(Contents.Solid | Contents.Hitbox);
|
mask = (ulong)(InteractionLayers.Solid | InteractionLayers.Hitboxes);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
mask = (ulong)(Contents.Solid | Contents.Hitbox);
|
mask = (ulong)(InteractionLayers.Solid | InteractionLayers.Hitboxes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contents ??= 0;
|
contents ??= 0;
|
||||||
|
|
||||||
bool drawBeam = Config.LoadedConfig.TraceRayBeam;
|
bool drawBeam = Config.LoadedConfig.TraceRayBeam;
|
||||||
|
|
||||||
|
TraceOptions options = new()
|
||||||
|
{
|
||||||
|
InteractsWith = (ulong)mask,
|
||||||
|
InteractsExclude = (ulong)contents,
|
||||||
|
DrawBeam = drawBeam == true ? 1 : 0,
|
||||||
|
};
|
||||||
|
|
||||||
Vector mins = new(-0.5f, -0.5f, -0.5f);
|
Vector mins = new(-0.5f, -0.5f, -0.5f);
|
||||||
Vector maxs = new(0.5f, 0.5f, 0.5f);
|
Vector maxs = new(0.5f, 0.5f, 0.5f);
|
||||||
|
|
||||||
if (!TryTraceHull(startPos, endPos, mins, maxs, playerPawn, (ulong)mask, (ulong)contents, out var result))
|
TraceResult result = default;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rayTrace.TraceHullShape(startPos, endPos, mins, maxs, playerPawn, options, out result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[jRandomSkills] A memory error was caught during RayTrace: {ex.Message}");
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var traced = new CustomTraceResult(result, startPos, (ulong)mask, (ulong)contents, drawBeam);
|
return new CustomTraceResult(result, startPos, (ulong)mask, (ulong)contents, drawBeam);
|
||||||
|
|
||||||
if (drawBeam)
|
|
||||||
CreateBeamLine(startPos, new Vector(traced.EndPosX, traced.EndPosY, traced.EndPosZ), traced.DidHit ? Color.Red : Color.Green);
|
|
||||||
|
|
||||||
return traced;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CustomTraceResult? EyeTrace(CCSPlayerController player)
|
public static CustomTraceResult? EyeTrace(CCSPlayerController player)
|
||||||
|
|
@ -119,6 +136,10 @@ namespace src.utils
|
||||||
playerPawn.CBodyComponent?.SceneNode == null)
|
playerPawn.CBodyComponent?.SceneNode == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
var rayTrace = GetInterface();
|
||||||
|
if (rayTrace == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
Vector safeMins = mins ?? playerPawn.Collision.Mins;
|
Vector safeMins = mins ?? playerPawn.Collision.Mins;
|
||||||
Vector safeMaxs = maxs ?? playerPawn.Collision.Maxs;
|
Vector safeMaxs = maxs ?? playerPawn.Collision.Maxs;
|
||||||
|
|
||||||
|
|
@ -127,8 +148,26 @@ namespace src.utils
|
||||||
|
|
||||||
bool drawBeam = Config.LoadedConfig.TraceRayBeam;
|
bool drawBeam = Config.LoadedConfig.TraceRayBeam;
|
||||||
|
|
||||||
if (!TryTraceHull(startPos, endPos, safeMins, safeMaxs, playerPawn, safeMask, safeContents, out var result))
|
TraceResult result = default;
|
||||||
|
TraceOptions options = new()
|
||||||
|
{
|
||||||
|
InteractsWith = safeMask,
|
||||||
|
InteractsExclude = safeContents,
|
||||||
|
DrawBeam = drawBeam == true ? 1 : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
CEntityInstance? entityToIgnore = (playerPawn.LifeState == (byte)LifeState_t.LIFE_ALIVE)
|
||||||
|
? playerPawn
|
||||||
|
: null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rayTrace.TraceHullShape(startPos, endPos, safeMins, safeMaxs, entityToIgnore, options, out result);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (drawBeam)
|
if (drawBeam)
|
||||||
{
|
{
|
||||||
|
|
@ -207,9 +246,6 @@ namespace src.utils
|
||||||
beam.EndPos.Z = end.Z;
|
beam.EndPos.Z = end.Z;
|
||||||
|
|
||||||
beam.DispatchSpawn();
|
beam.DispatchSpawn();
|
||||||
|
|
||||||
EntityManager.RegisterEntity(beam.Index, int.MaxValue, "beam");
|
|
||||||
EntityManager.ScheduleAutoDestroy(beam.Index, 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float Distance(this CustomTraceResult result)
|
public static float Distance(this CustomTraceResult result)
|
||||||
|
|
@ -224,12 +260,6 @@ namespace src.utils
|
||||||
|
|
||||||
public static bool HitEntityByDesignerName<T>(this CustomTraceResult result, out T? entity, string designerName, DesignerNameMatchType matchType = DesignerNameMatchType.Equals) where T : CEntityInstance
|
public static bool HitEntityByDesignerName<T>(this CustomTraceResult result, out T? entity, string designerName, DesignerNameMatchType matchType = DesignerNameMatchType.Equals) where T : CEntityInstance
|
||||||
{
|
{
|
||||||
if (result.HitEntity == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
entity = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
T? val = (T?)Activator.CreateInstance(typeof(T), result.HitEntity);
|
T? val = (T?)Activator.CreateInstance(typeof(T), result.HitEntity);
|
||||||
if ((object?)val != null && matchType switch
|
if ((object?)val != null && matchType switch
|
||||||
{
|
{
|
||||||
|
|
@ -249,12 +279,6 @@ namespace src.utils
|
||||||
|
|
||||||
public static bool HitEntity(this CustomTraceResult result, out CBaseEntity? entity)
|
public static bool HitEntity(this CustomTraceResult result, out CBaseEntity? entity)
|
||||||
{
|
{
|
||||||
if (result.HitEntity == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
entity = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CEntityInstance entityInstance = new(result.HitEntity);
|
CEntityInstance entityInstance = new(result.HitEntity);
|
||||||
if (string.IsNullOrEmpty(entityInstance.DesignerName))
|
if (string.IsNullOrEmpty(entityInstance.DesignerName))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -480,48 +480,9 @@ namespace src.utils
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CreateTracer(CCSPlayerController player, string particleName, CustomTraceResult result)
|
|
||||||
{
|
|
||||||
var pawn = player.PlayerPawn.Value;
|
|
||||||
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
|
|
||||||
|
|
||||||
Vector forward = GetForwardVector(pawn.EyeAngles);
|
|
||||||
Vector start = new(pawn.AbsOrigin.X + forward.X * 16, pawn.AbsOrigin.Y + forward.Y * 16, pawn.AbsOrigin.Z + pawn.ViewOffset.Z - 6);
|
|
||||||
Vector end = new(result.EndPos.X, result.EndPos.Y, result.EndPos.Z);
|
|
||||||
|
|
||||||
EntityManager.CreateTracer(player.Index, particleName, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ClearKillCredits()
|
public static void ClearKillCredits()
|
||||||
{
|
{
|
||||||
pendingKillCredits.Clear();
|
pendingKillCredits.Clear();
|
||||||
pendingNativeKills.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<uint, (uint AttackerHandle, uint InflictorHandle, DamageTypes_t DamageType, int ExpiryTick)> pendingNativeKills = [];
|
|
||||||
|
|
||||||
public static void RegisterNativeKill(CCSPlayerPawn victimPawn, CBaseEntity attacker, CBaseEntity inflictor, DamageTypes_t damageType)
|
|
||||||
{
|
|
||||||
pendingNativeKills[victimPawn.Index] = (attacker.EntityHandle.Raw, inflictor.EntityHandle.Raw, damageType, Server.TickCount + 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ApplyNativeKill(CEntityInstance victim, CTakeDamageInfo info)
|
|
||||||
{
|
|
||||||
if (pendingNativeKills.IsEmpty) return;
|
|
||||||
if (!pendingNativeKills.TryGetValue(victim.Index, out var kill)) return;
|
|
||||||
|
|
||||||
if (kill.ExpiryTick < Server.TickCount)
|
|
||||||
{
|
|
||||||
pendingNativeKills.TryRemove(victim.Index, out _);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.Attacker.Value?.Handle != victim.Handle) return;
|
|
||||||
|
|
||||||
pendingNativeKills.TryRemove(victim.Index, out _);
|
|
||||||
info.Attacker.Raw = kill.AttackerHandle;
|
|
||||||
info.Inflictor.Raw = kill.InflictorHandle;
|
|
||||||
info.BitsDamageType = kill.DamageType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly HashSet<string> bulletWeapons = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> bulletWeapons = new(StringComparer.Ordinal)
|
||||||
|
|
|
||||||
189
release.sh
189
release.sh
|
|
@ -1,189 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Build jRandomSkills and its Panorama layouts, package them as one tarball and publish it as a
|
|
||||||
# Forgejo release.
|
|
||||||
#
|
|
||||||
# ./release.sh <tag> e.g. ./release.sh v1.2.4.b2-panorama1
|
|
||||||
#
|
|
||||||
# Commit first: the plugin is rebuilt here from the committed source, so the release always matches
|
|
||||||
# the tag. The tag is created on HEAD and pushed to origin if it doesn't exist yet. Re-running with
|
|
||||||
# the same tag replaces that release's attachment.
|
|
||||||
#
|
|
||||||
# Asset: jRandomSkills-<tag>.tar.gz, laid out like the server's game/csgo/ - extract it there:
|
|
||||||
# tar -xzf jRandomSkills-<tag>.tar.gz -C /srv/cs2/game/csgo
|
|
||||||
#
|
|
||||||
# addons/counterstrikesharp/plugins/jRandomSkills/ plugin, deps, languages/, packages/
|
|
||||||
# addons/counterstrikesharp/gamedata/ jRandomSkills.gamedata.json
|
|
||||||
# panorama/{layout,styles}/custom_game/ compiled skill_hud / jrs_menu (.vxml_c/.vcss_c)
|
|
||||||
#
|
|
||||||
# No configs/: the plugin writes config.json and skillsInfo.json itself when they are missing, so an
|
|
||||||
# upgrade never touches the server's config. The Panorama files are compiled from
|
|
||||||
# "jRandomSkills - SRC Files/panorama" with the CS2 client's resourcecompiler.exe (WSL interop).
|
|
||||||
#
|
|
||||||
# Players get the layouts from the Workshop, not from this tarball: the sources are compiled into
|
|
||||||
# the client's jrandomskills_hud Workshop Tools addon (Workshop item 3807837213, mounted for every
|
|
||||||
# player by the server's MultiAddonManager, mm_extra_addons). The panorama/ files in the tarball
|
|
||||||
# only land on the server. When the compiled layouts changed, the script says so at the end:
|
|
||||||
# re-upload the addon from Workshop Tools. That step stays manual.
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
FORGEJO_URL="https://git.zio.sh"
|
|
||||||
OWNER="cs2"
|
|
||||||
REPO="jRandomSkills"
|
|
||||||
# Placeholder - replace, or set FORGEJO_TOKEN in the environment instead. Needs write:repository.
|
|
||||||
# Only uploading needs it; downloading from the public repo doesn't. Don't commit a real token.
|
|
||||||
FORGEJO_TOKEN="${FORGEJO_TOKEN:-CHANGE_ME_FORGEJO_TOKEN}"
|
|
||||||
|
|
||||||
# CS2 client used to compile the Panorama layouts, and the Workshop Tools addon compiled into.
|
|
||||||
CS2_CLIENT_DIR="${CS2_CLIENT_DIR:-/mnt/e/SteamLibrary/steamapps/common/Counter-Strike Global Offensive}"
|
|
||||||
CS2_ADDON="${CS2_ADDON:-jrandomskills_hud}"
|
|
||||||
WORKSHOP_ID="3807837213"
|
|
||||||
PANORAMA_FILES=(
|
|
||||||
layout/custom_game/skill_hud.xml
|
|
||||||
layout/custom_game/jrs_menu.xml
|
|
||||||
styles/custom_game/skill_hud.css
|
|
||||||
styles/custom_game/jrs_menu.css
|
|
||||||
)
|
|
||||||
|
|
||||||
# Only these ship from the build output. bin/ also holds CounterStrikeSharp.API.dll, RayTraceApi.dll
|
|
||||||
# and framework DLLs, which the server already provides and which must not be duplicated per plugin.
|
|
||||||
PLUGIN_BINARIES=(
|
|
||||||
jRandomSkills.dll
|
|
||||||
jRandomSkills.pdb
|
|
||||||
MaxMind.Db.dll
|
|
||||||
Newtonsoft.Json.dll
|
|
||||||
PanoramaManager.dll
|
|
||||||
)
|
|
||||||
|
|
||||||
TAG="${1:-}"
|
|
||||||
if [[ -z "$TAG" ]]; then
|
|
||||||
echo "usage: $0 <tag> (e.g. v1.2.4.b2-panorama1)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ "$FORGEJO_TOKEN" == "CHANGE_ME_FORGEJO_TOKEN" ]]; then
|
|
||||||
echo "Set FORGEJO_TOKEN (edit release.sh or export it) first." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
|
|
||||||
|
|
||||||
SRC="jRandomSkills - SRC Files"
|
|
||||||
|
|
||||||
if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then
|
|
||||||
echo "Tracked files have uncommitted changes; commit them so the tag matches the release." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Build -----------------------------------------------------------------------
|
|
||||||
BIN="$SRC/bin/Release/net10.0"
|
|
||||||
rm -rf "$SRC/bin" "$SRC/obj"
|
|
||||||
podman run --rm -v "$(pwd)":/src:Z -w "/src/$SRC" \
|
|
||||||
mcr.microsoft.com/dotnet/sdk:10.0 dotnet build -c Release
|
|
||||||
|
|
||||||
# --- Stage (layout of game/csgo/) --------------------------------------------------
|
|
||||||
OUT="release-out"
|
|
||||||
STAGE="$OUT/stage"
|
|
||||||
rm -rf "$OUT"
|
|
||||||
PLUGIN_DIR="$STAGE/addons/counterstrikesharp/plugins/jRandomSkills"
|
|
||||||
mkdir -p "$PLUGIN_DIR/languages" "$PLUGIN_DIR/packages" "$STAGE/addons/counterstrikesharp/gamedata"
|
|
||||||
|
|
||||||
for file in "${PLUGIN_BINARIES[@]}"; do
|
|
||||||
cp "$BIN/$file" "$PLUGIN_DIR/"
|
|
||||||
done
|
|
||||||
cp "$SRC"/src/lang/*.json "$PLUGIN_DIR/languages/"
|
|
||||||
cp -r "$SRC"/packages/. "$PLUGIN_DIR/packages/"
|
|
||||||
cp "$SRC"/src/gamedata/*.json "$STAGE/addons/counterstrikesharp/gamedata/"
|
|
||||||
|
|
||||||
# --- Panorama: compile sources with the CS2 client ---------------------------------
|
|
||||||
RC_DIR="$CS2_CLIENT_DIR/game/bin/win64"
|
|
||||||
[[ -x "$RC_DIR/resourcecompiler.exe" ]] || { echo "resourcecompiler.exe not found in $RC_DIR (set CS2_CLIENT_DIR)." >&2; exit 1; }
|
|
||||||
CONTENT_ADDON="$CS2_CLIENT_DIR/content/csgo_addons/$CS2_ADDON/panorama"
|
|
||||||
GAME_ADDON="$CS2_CLIENT_DIR/game/csgo_addons/$CS2_ADDON/panorama"
|
|
||||||
WIN_CLIENT_DIR="$(wslpath -w "$CS2_CLIENT_DIR")"
|
|
||||||
|
|
||||||
# Checksums of the addon's compiled layouts, to tell whether the Workshop item needs a re-upload.
|
|
||||||
compiled_sums() {
|
|
||||||
local file compiled
|
|
||||||
for file in "${PANORAMA_FILES[@]}"; do
|
|
||||||
case "$file" in
|
|
||||||
*.xml) compiled="${file%.xml}.vxml_c" ;;
|
|
||||||
*.css) compiled="${file%.css}.vcss_c" ;;
|
|
||||||
esac
|
|
||||||
if [[ -f "$GAME_ADDON/$compiled" ]]; then
|
|
||||||
sha256sum "$GAME_ADDON/$compiled" | cut -d' ' -f1
|
|
||||||
else
|
|
||||||
echo "missing $compiled"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
sums_before="$(compiled_sums)"
|
|
||||||
|
|
||||||
for file in "${PANORAMA_FILES[@]}"; do
|
|
||||||
mkdir -p "$CONTENT_ADDON/$(dirname "$file")" "$STAGE/panorama/$(dirname "$file")"
|
|
||||||
cp -f "$SRC/panorama/$file" "$CONTENT_ADDON/$file"
|
|
||||||
|
|
||||||
log="$(cd "$RC_DIR" && ./resourcecompiler.exe -game "$WIN_CLIENT_DIR\\game\\csgo" -f \
|
|
||||||
-i "$WIN_CLIENT_DIR\\content\\csgo_addons\\$CS2_ADDON\\panorama\\${file//\//\\}" 2>&1 | tr -d '\r')"
|
|
||||||
if ! grep -q 'OK: 1 compiled, 0 failed' <<<"$log"; then
|
|
||||||
echo "$log" >&2
|
|
||||||
echo "Failed to compile $file" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$file" in
|
|
||||||
*.xml) compiled="${file%.xml}.vxml_c" ;;
|
|
||||||
*.css) compiled="${file%.css}.vcss_c" ;;
|
|
||||||
esac
|
|
||||||
cp -f "$GAME_ADDON/$compiled" "$STAGE/panorama/$compiled"
|
|
||||||
echo "Compiled $compiled"
|
|
||||||
done
|
|
||||||
|
|
||||||
# --- Package -------------------------------------------------------------------------
|
|
||||||
TARBALL="jRandomSkills-${TAG}.tar.gz"
|
|
||||||
tar -czf "$OUT/$TARBALL" -C "$STAGE" addons panorama
|
|
||||||
rm -rf "$STAGE"
|
|
||||||
|
|
||||||
echo "Packaged $OUT/$TARBALL:"
|
|
||||||
tar -tzf "$OUT/$TARBALL" | grep -v '/$'
|
|
||||||
|
|
||||||
# --- Tag -------------------------------------------------------------------------
|
|
||||||
if ! git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
|
|
||||||
git tag -a "$TAG" -m "$TAG"
|
|
||||||
fi
|
|
||||||
git push origin "refs/tags/$TAG"
|
|
||||||
|
|
||||||
# --- Release ---------------------------------------------------------------------
|
|
||||||
API="$FORGEJO_URL/api/v1/repos/$OWNER/$REPO"
|
|
||||||
AUTH=(-H "Authorization: token $FORGEJO_TOKEN")
|
|
||||||
|
|
||||||
release_json="$(curl -fsS "${AUTH[@]}" "$API/releases/tags/$TAG" 2>/dev/null || true)"
|
|
||||||
if [[ -z "$release_json" ]]; then
|
|
||||||
body="$(git log -1 --format=%B "$TAG")"
|
|
||||||
release_json="$(jq -n --arg tag "$TAG" --arg body "$body" \
|
|
||||||
'{tag_name: $tag, name: $tag, body: $body, draft: false, prerelease: false}' |
|
|
||||||
curl -fsS "${AUTH[@]}" -H "Content-Type: application/json" -X POST --data @- "$API/releases")"
|
|
||||||
echo "Created release $TAG"
|
|
||||||
else
|
|
||||||
echo "Release $TAG already exists, replacing its attachment"
|
|
||||||
fi
|
|
||||||
release_id="$(jq -r '.id' <<<"$release_json")"
|
|
||||||
|
|
||||||
existing_id="$(jq -r --arg n "$TARBALL" '.assets[]? | select(.name == $n) | .id' <<<"$release_json")"
|
|
||||||
if [[ -n "$existing_id" ]]; then
|
|
||||||
curl -fsS "${AUTH[@]}" -X DELETE "$API/releases/$release_id/assets/$existing_id" >/dev/null
|
|
||||||
fi
|
|
||||||
curl -fsS "${AUTH[@]}" -X POST -F "attachment=@$OUT/$TARBALL" \
|
|
||||||
"$API/releases/$release_id/assets?name=$TARBALL" >/dev/null
|
|
||||||
echo "Uploaded $TARBALL"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Download: $FORGEJO_URL/$OWNER/$REPO/releases/download/$TAG/$TARBALL"
|
|
||||||
echo "Install: tar -xzf $TARBALL -C /srv/cs2/game/csgo"
|
|
||||||
|
|
||||||
echo
|
|
||||||
if [[ "$(compiled_sums)" != "$sums_before" ]]; then
|
|
||||||
echo "Workshop: the compiled Panorama layouts CHANGED. Players won't see the change until the"
|
|
||||||
echo " '$CS2_ADDON' addon is re-uploaded from Workshop Tools to item $WORKSHOP_ID:"
|
|
||||||
echo " https://steamcommunity.com/sharedfiles/filedetails/?id=$WORKSHOP_ID"
|
|
||||||
else
|
|
||||||
echo "Workshop: compiled Panorama layouts unchanged; no Workshop update needed."
|
|
||||||
fi
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue