v1.2.2.b2

## GENERAL
- Updated CSS to version 1.0.369.
- Upgraded the project from .NET 8.0 to .NET 10.0.
- Updated Turkish language translation by @ByDexterTR.
- Added Russian language translation by @213sdfsdgf.
- Fixed an issue with incorrect display of Arabic nicknames.
- Used ForceFullUpdate to resolve the "FATAL ERROR: CopyExistingEntity" crash.
- Disabled stale HandleCommandDrop hook to avoid crashes when a player joins a team (thanks to @vladimir214sd).
- Added new signatures to gamedata.
- Added the ability to configure the number of grenades for every skill that uses grenades as part of its ability (e.g., Miner, AntyFlash, MagneticDecoy, etc.).

## SKILL IMPROVEMENTS
- Re-Zombie:
  -- Fixed weapon switching issues when a player is holding a Zeus and a knife.
  -- Weapons are now removed for bots in zombie mode.
- Ghost:
  -- Blocked the ability to use the Zeus.
- Thorns:
  -- Added a configurable maximum health limit (37 HP) for the damage that can be reflected.
- Second Life / Phoenix / Re-Zombie:
  -- Fixed an issue where players would occasionally fail to respawn if they took too much damage too quickly.
- Ghost / Chicken:
  -- Blocked the ability to use the SG556 (`weapon_sg556`).
- Wallhack:
  -- Fixed a bug that caused temporary enemy highlighting right after death.
- Cypher:
  -- Added camera position persistence.
- God Mode:
  -- Fixed an issue with permanent immortality triggering after reaching exactly 0 HP.
- Weapons Swap:
  -- The ammo count is now also transferred when weapons are swapped.
- Muhammed:
  -- Renamed the skill to "DeathBomb".
- Spectator:
  -- Fixed a bug where a player with the Chicken skill would still display as a ragdoll after death when spectated.
- Friendly Fire:
  -- The skill now works even when the server's mp_friendlyfire setting is set to 0.
- Baseball:
  -- Disabled teammate killing.
This commit is contained in:
Juzlus 2026-06-10 13:47:34 +02:00
parent cba834e8f0
commit 91761f7376
84 changed files with 6894 additions and 1689 deletions

View file

@ -0,0 +1,101 @@
// https://discord.com/channels/1160907911501991946/1508172390863994910/1508180670659166348
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
namespace jRandomSkills.src.utils
{
#region Native Structs
[StructLayout(LayoutKind.Sequential)]
public unsafe struct CUtlMemory<T> where T : unmanaged
{
public T* m_pMemory;
public int m_nAllocationCount;
public int m_nGrowSize;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct CUtlVector<T> where T : unmanaged
{
public int m_Size;
public CUtlMemory<T> m_Memory;
public int Count => m_Size;
public ref T Element(int index)
{
if (index < 0 || index >= m_Size)
throw new IndexOutOfRangeException();
return ref m_Memory.m_pMemory[index];
}
}
#endregion
#region Network Services
public class INetworkServerService : NativeObject
{
private readonly VirtualFunctionWithReturn<nint, nint> GetIGameServerFunc;
public INetworkServerService() : base(NativeAPI.GetValveInterface(0, "NetworkServerService_001"))
{
GetIGameServerFunc = new VirtualFunctionWithReturn<nint, nint>(Handle, GameData.GetOffset("INetworkServerService_GetIGameServer"));
}
public INetworkGameServer GetIGameServer()
{
return new INetworkGameServer(GetIGameServerFunc.Invoke(Handle));
}
}
public unsafe class INetworkGameServer(nint ptr) : NativeObject(ptr)
{
private static readonly int SlotsOffset = GameData.GetOffset("INetworkGameServer_Slots");
private ref CUtlVector<nint> Slots => ref Unsafe.AsRef<CUtlVector<nint>>((void*)(Handle + SlotsOffset));
public CServerSideClient? GetClientBySlot(int slot)
{
if (slot < 0 || slot >= Slots.Count)
return null;
var ptr = Slots.Element(slot);
if (ptr == nint.Zero)
return null;
return new CServerSideClient(ptr);
}
}
#endregion
#region CServerSideClient
public unsafe class CServerSideClient(nint ptr) : NativeObject(ptr)
{
private static readonly int m_nDeltaTick = GameData.GetOffset("CServerSideClient_m_nDeltaTick");
private ref T Field<T>(int offset) where T : unmanaged
{
return ref Unsafe.AsRef<T>((void*)(Handle + offset));
}
public int DeltaTick
{
get => Field<int>(m_nDeltaTick);
set => Field<int>(m_nDeltaTick) = value;
}
public void ForceFullUpdate() => DeltaTick = -1;
}
#endregion
}

View file

@ -131,6 +131,7 @@ namespace src.utils
new LanguageInfo("PT-BR, PT, BR, AO, CV, GW, MZ, ST, TL", "pt-br"),
new LanguageInfo("FR, MC, HT", "fr"),
new LanguageInfo("DE, AT, CH, LI, LU, BE", "de"),
new LanguageInfo("RU, KZ, BY, KG, TJ, UZ, TM, AM, AZ, GE, MD", "ru"),
new LanguageInfo("TR", "tr"),
new LanguageInfo("CZ", "cs"),
new LanguageInfo("PL", "pl"),

View file

@ -4,8 +4,10 @@ using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Utils;
using jRandomSkills.src.utils;
using src.player;
using src.player.skills;
using System.Collections.Concurrent;
@ -56,7 +58,30 @@ namespace src.utils
SkillData.Skills.Add(new jSkill_SkillInfo(skill, color, display));
}
public static void TryGiveWeapon(CCSPlayerController player, CsItem item, int count = 1)
public static void UpdateGrenadeCount(CCSPlayerController player, CsItem item, int ammo)
{
string? itemString = EnumUtils.GetEnumMemberAttributeValue(item);
if (string.IsNullOrWhiteSpace(itemString)) return;
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return;
if (player.PlayerPawn.Value.WeaponServices == null) return;
var weapon = player.PlayerPawn.Value.WeaponServices.MyWeapons
.FirstOrDefault(w => w != null && w.IsValid && w.Value != null && w.Value.IsValid && !string.IsNullOrEmpty(w.Value.DesignerName) && w.Value.DesignerName == itemString);
if (weapon == null || !weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) return;
weapon.Value.Clip1 = ammo;
Utilities.SetStateChanged(weapon.Value, "CBasePlayerWeapon", "m_iClip1");
jRandomSkills.Instance.AddTimer(.1f, () =>
{
if (weapon == null || !weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) return;
weapon.Value.Clip1 = 1;
}, TimerFlags.STOP_ON_MAPCHANGE);
}
public static void TryGiveWeapon(CCSPlayerController player, CsItem item, int count = 1, bool existValidator = true)
{
string? itemString = EnumUtils.GetEnumMemberAttributeValue(item);
if (string.IsNullOrWhiteSpace(itemString)) return;
@ -66,7 +91,8 @@ namespace src.utils
var exists = player.PlayerPawn.Value.WeaponServices.MyWeapons
.FirstOrDefault(w => w != null && w.IsValid && w.Value != null && w.Value.IsValid && w.Value.DesignerName == itemString);
if (exists == null)
if (exists == null || !existValidator)
for (int i = 0; i < count; i++)
player.GiveNamedItem(item);
}
@ -259,6 +285,9 @@ namespace src.utils
var jester = Jester.GetJesterInfo(player.Index);
if (jester?.Active == true) return false;
if (playerInfo.Skill == Skills.GodMode && GodMode.HaveHodMode(player.Index))
return false;
}
int newHealth = (int)(pawn.Health - damage);
@ -291,9 +320,35 @@ namespace src.utils
return EntityManager.CreateTrackedTrigger(ownerPlayerIndex, name, radius, pos);
}
public static void ForceFullUpdate(CCSPlayerController player)
{
if (player == null || !player.IsValid)
return;
var pawn = player.PlayerPawn?.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
QAngle lastAngle = new(pawn.V_angle.X, pawn.V_angle.Y, pawn.V_angle.Z);
INetworkServerService networkServerService = new();
INetworkGameServer networkGameServer = networkServerService.GetIGameServer();
var client = networkGameServer.GetClientBySlot(player.Slot);
if (client == null) return;
client.ForceFullUpdate();
pawn.Look(lastAngle);
}
public static void ForceFullUpdateToAll()
{
foreach (var player in Utilities.GetPlayers())
ForceFullUpdate(player);
}
public static bool AddHealth(CCSPlayerPawn? pawn, int extraHealth, int? maxHealth = null)
{
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || pawn.Health <= 0)
return false;
maxHealth ??= pawn.MaxHealth;
@ -381,6 +436,18 @@ namespace src.utils
return manager.HasMenu(player);
}
private static string GetInvisibleSignature(string id)
{
if (string.IsNullOrEmpty(id)) return "";
var sb = new System.Text.StringBuilder();
foreach (char c in id)
for (int i = 0; i < 8; i++)
sb.Append(((c >> i) & 1) == 1 ? "\u200B" : "\u200C");
return sb.ToString();
}
public static void UpdateMenu(CCSPlayerController? player, ConcurrentBag<(string, string)> items)
{
if (player == null) return;
@ -395,13 +462,19 @@ namespace src.utils
Dictionary<string, Action<CCSPlayerController, IWasdMenuOption>> list = [];
foreach (var item in items)
list.TryAdd(isIlliterate
{
string encodedText = isIlliterate
? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(item.Item1)!)
: System.Net.WebUtility.HtmlEncode(item.Item1), (p, option) =>
: System.Net.WebUtility.HtmlEncode(item.Item1);
string uniqueKey = GetInvisibleSignature(item.Item2) + $"\u202A{encodedText}\u202C";
list.TryAdd(uniqueKey, (p, option) =>
{
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { item.Item2 }]);
manager.CloseMenu(p);
});
}
manager.UpdateActiveMenu(player, list);
}
@ -445,11 +518,11 @@ namespace src.utils
string infoLine = string.IsNullOrEmpty(your_skill)
? ""
: $"<font class='fontWeight-Bold fontSize-{config.HeaderLineSize}' color='{config.HeaderLineColor}'>{your_skill}:</font><br>";
: $"<font class='fontWeight-Bold fontSize-{config.HeaderLineSize}' color='{config.HeaderLineColor}'>\u202A{your_skill}:\u202C</font><br>";
string skillLine = Illiterate.CheckIlliterateSkill(player)
? $"<font class='fontWeight-Bold fontSize-{config.SkillLineSize}'>{Illiterate.GetRandomText(player.GetSkillName(skillData.Skill))}</font><br>"
: $"<font class='fontWeight-Bold fontSize-{config.SkillLineSize}' color='{skillData.Color}'>{player.GetSkillName(skillData.Skill)}</font><br>";
? $"<font class='fontWeight-Bold fontSize-{config.SkillLineSize}'>\u202A{Illiterate.GetRandomText(player.GetSkillName(skillData.Skill))}\u202C</font><br>"
: $"<font class='fontWeight-Bold fontSize-{config.SkillLineSize}' color='{skillData.Color}'>\u202A{player.GetSkillName(skillData.Skill)}\u202C</font><br>";
var skill_select_info = player.GetTranslation($"{playerInfo.Skill.ToString().ToLower()}_select_info");
string remainingLine = string.IsNullOrWhiteSpace(skill_select_info)
@ -470,22 +543,34 @@ namespace src.utils
IWasdMenu menu = manager.CreateMenu(hudContent, itemText, itemHoverText, controllsLine);
foreach (var enemy in enemies)
menu.Add(isIlliterate
{
string encodedEnemyName = isIlliterate
? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(enemy.Item1)!)
: System.Net.WebUtility.HtmlEncode(enemy.Item1), (p, option) =>
: System.Net.WebUtility.HtmlEncode(enemy.Item1);
string uniqueKey = GetInvisibleSignature(enemy.Item2) + $"\u202A{encodedEnemyName}\u202C";
menu.Add(uniqueKey, (p, option) =>
{
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { enemy.Item2 }]);
manager.CloseMenu(p);
});
}
if (lastElement != null)
menu.Add(isIlliterate
{
string encodedLastElement = isIlliterate
? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastElement.Value.Item1)!)
: System.Net.WebUtility.HtmlEncode(lastElement.Value.Item1), (p, option) =>
: System.Net.WebUtility.HtmlEncode(lastElement.Value.Item1);
menu.Add($"\u202A{encodedLastElement}\u202C", (p, option) =>
{
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]);
if (lastElement.Value.Item3)
manager.CloseMenu(p);
});
}
manager.OpenMainMenu(player, menu);
}