Bump version, return SkillAction result & i18n

Bump module version to 1.2.1.b7 and change SkillAction to return the invoked method result (object?) or null when not found. Add and normalize many localization entries across multiple language files (aimlock, homingnades, hotbomb, illiterate, illusionist, magneticdecoy, magneto, miner, planter_planting, teleporter chance string, throwingknife, weightless, wildthrow, smoker, etc.) and clean up duplicate/obsolete entries. Update server plugin binaries and configs (WASDMenuAPI.dll, jRandomSkills.dll, config.json, skillsInfo.json) and remove playersLanguage.json.
This commit is contained in:
Juzlus 2026-04-07 20:57:34 +02:00
parent 3954fc3af4
commit abd3aed8fd
33 changed files with 1159 additions and 324 deletions

View file

@ -44,6 +44,7 @@ public interface ISkill
public static void OnTriggerEnter(CBaseTrigger _, CBaseEntity __) { }
public static void OnTriggerExit(CBaseTrigger _, CBaseEntity __) { }
public static bool OnWeaponCanAcquire(DynamicHook _, CCSPlayerController __, CEconItemView ___, CCSWeaponBaseVData ____) { return false; }
public class SkillConfig { }
}
@ -52,7 +53,7 @@ public enum Skills
{
None,
Aimbot,
AimLock, // <-
AimLock,
Anomaly,
AntyFlash,
AntyHead,
@ -81,7 +82,7 @@ public enum Skills
Dracula,
Duplicator,
Dwarf,
Earthquake, // <-
Earthquake,
EnemySpawn,
ExplosiveShot,
FalconEye,
@ -98,11 +99,11 @@ public enum Skills
Glue,
GodMode,
Grenadier,
HealingChicken, // <-
HealingChicken,
HealingSmoke,
Hermit,
HolyHandGrenade,
HomingNades, // <-
HomingNades,
HotBomb,
Iana,
Illiterate,
@ -134,7 +135,7 @@ public enum Skills
PawelJumper,
Phoenix,
PsychicDefusing,
Pilot, // <-
Pilot,
Planter,
Poison,
PrimaryBan,
@ -145,7 +146,7 @@ public enum Skills
RadarHack,
Rambo,
RandomWeapon,
RCMelon, // <-
RCMelon,
ReZombie,
ReactiveArmor,
Regeneration,
@ -158,10 +159,10 @@ public enum Skills
Saper,
SecondLife,
Shade,
ShortBomb, // <- short
ShortBomb,
Silent,
Smoker, // <- nie dziala
SniperElite, // <- nie dziala
Smoker,
SniperElite,
Soldier,
SoundMaker,
Spectator,

View file

@ -80,6 +80,7 @@ namespace src.player
VirtualFunctions.CBaseTrigger_StartTouchFunc.Hook(OnTriggerEnter, HookMode.Post);
VirtualFunctions.CBaseTrigger_EndTouchFunc.Hook(OnTriggerExit, HookMode.Pre);
VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Hook(OnWeaponCanAcquire, HookMode.Pre);
}
private static HookResult PlayerMakeSound(UserMessage um)
@ -388,6 +389,44 @@ namespace src.player
}
}
private static HookResult OnWeaponCanAcquire(DynamicHook hook)
{
lock (setLock)
{
CCSPlayer_ItemServices itemServices = hook.GetParam<CCSPlayer_ItemServices>(0);
if (itemServices == null || itemServices.Pawn.Value == null || !itemServices.Pawn.Value.IsValid) return HookResult.Continue;
CEconItemView econItem = hook.GetParam<CEconItemView>(1);
if (econItem == null) return HookResult.Continue;
CBasePlayerPawn pawn = itemServices.Pawn.Value;
if (pawn == null || !pawn.IsValid || pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return HookResult.Continue;
CCSPlayerController player = pawn.Controller.Value.As<CCSPlayerController>();
if (player == null || !player.IsValid) return HookResult.Continue;
CCSWeaponBaseVData vdata = VirtualFunctions.GetCSWeaponDataFromKeyFunc.Invoke(-1, econItem.ItemDefinitionIndex.ToString());
if (vdata == null) return HookResult.Continue;
var activeSkills = Instance.SkillPlayer
.Where(p => !p.IsDrawing)
.Select(p => p.Skill.ToString())
.Distinct();
bool block = false;
foreach (string skillName in activeSkills)
{
bool? result = (bool?)Instance.SkillAction(skillName, "OnWeaponCanAcquire", [hook, player, econItem, vdata]);
if (result == true)
{
block = true;
break;
}
}
return block ? HookResult.Stop : HookResult.Continue;
}
}
private static void OnTick()
{
lock (setLock)
@ -827,10 +866,6 @@ namespace src.player
}
}
if (randomSkill.Display)
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}",
border: !Utilities.GetPlayers().Any(p => p.Team == player.Team && !p.IsBot && p != player) ? "tb" : "t");
Instance?.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]);
skillPlayer.Skill = randomSkill.Skill;
skillPlayer.SpecialSkill = Skills.None;
@ -840,6 +875,10 @@ namespace src.player
Instance?.AddTimer(.2f, () =>
{
if (randomSkill.Display)
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}",
border: !Utilities.GetPlayers().Any(p => p.Team == player.Team && !p.IsBot && p != player) ? "tb" : "t");
if (SkillsInfo.GetValue<bool>(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () =>
{
@ -1016,10 +1055,10 @@ namespace src.player
}
var config = Config.LoadedConfig.HtmlHudCustomisation;
var emptySymbol = $"<font class='fontSize-{(string.IsNullOrEmpty(headerLine) ? "l" : "ml")}'> </font>";
var emptySymbol = $"<font class='fontSize-{(string.IsNullOrEmpty(headerLine) || string.IsNullOrEmpty(config.HeaderLineSize) ? "l" : "ml")}'> </font>";
var emptySymbol2 = $"<font class='fontSize-ml'> </font>";
string infoLine = string.IsNullOrEmpty(headerLine)
string infoLine = string.IsNullOrEmpty(headerLine) || string.IsNullOrEmpty(config.HeaderLineSize)
? ""
: $"<font class='fontWeight-Bold fontSize-{config.HeaderLineSize}' color='{config.HeaderLineColor}'>{headerLine}:</font><br>";
@ -1030,6 +1069,7 @@ namespace src.player
: $"<br>{emptySymbol}<font class='fontSize-{(isDescription ? config.SkillDescriptionLineSize : config.InfoLineSize)}' color='{(isDescription ? config.SkillDescriptionLineColor : config.InfoLineColor)}'>{extraLine}</font>{emptySymbol}";
var hudContent = infoLine + skillLine + remainingLine;
player.PrintToCenterHtml(hudContent);
}
}

View file

@ -105,7 +105,7 @@ namespace src.player
var observeredPlayerSpecialSkillInfo = SkillData.Skills.FirstOrDefault(s => s.Skill == observeredPlayerSkill.SpecialSkill);
if (observeredPlayerSpecialSkillInfo == null) return;
string pName = observeredPlayerSkill.PlayerName;
string pName = System.Net.WebUtility.HtmlEncode(observeredPlayerSkill.PlayerName);
if (pName.Length > 18)
pName = $"{pName[..17]}...";
var observerSkill = player.GetTranslation("observer_skill");

View file

@ -50,7 +50,7 @@ namespace src.player.skills
}
string distanceColor = closetDistance > 1500 ? "#00FF00" : closetDistance > 600 ? "#FFFF00" : "#FF0000";
playerInfo.PrintHTML = $"{closetEnemy}: <font color='{distanceColor}'>{(closetDistance > 3000 ? "3000+" : closetDistance)}</font>";
playerInfo.PrintHTML = $"{System.Net.WebUtility.HtmlEncode(closetEnemy)}: <font color='{distanceColor}'>{(closetDistance > 3000 ? "3000+" : closetDistance)}</font>";
}
}

View file

@ -1,6 +1,5 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils;
@ -20,7 +19,7 @@ namespace src.player.skills
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
// SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
}
public static void NewRound()

View file

@ -27,8 +27,10 @@ namespace src.player.skills
{
var player = @event.Userid;
if (player == null || !player.IsValid) return;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return;
smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0);
}
@ -36,8 +38,10 @@ namespace src.player.skills
{
var player = @event.Userid;
if (player == null || !player.IsValid) return;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return;
foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z))
smokes.TryRemove(smoke, out _);
}
@ -71,12 +75,20 @@ namespace src.player.skills
public static void OnTick()
{
int tick = SkillsInfo.GetValue<int>(skillName, "tickCooldown");
if (Server.TickCount % tick != 0) return;
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
int smokeHeal = SkillsInfo.GetValue<int>(skillName, "smokeHeal");
foreach (Vector smokePos in smokes.Keys)
foreach (var player in Utilities.GetPlayers())
if (Server.TickCount % tick == 0)
if (player.PlayerPawn.Value != null && player.PlayerPawn.Value.IsValid && player.PlayerPawn.Value.AbsOrigin != null)
if (SkillUtils.GetDistance(smokePos, player.PlayerPawn.Value.AbsOrigin) <= SkillsInfo.GetValue<float>(skillName, "smokeRadius"))
AddHealth(player.PlayerPawn.Value, SkillsInfo.GetValue<int>(skillName, "smokeHeal"));
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && p.PawnIsAlive))
{
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
if (SkillUtils.GetDistance(smokePos, pawn.AbsOrigin) <= smokeRadius)
AddHealth(pawn, smokeHeal);
}
}
public static void EnableSkill(CCSPlayerController player)

View file

@ -9,7 +9,7 @@ namespace src.player.skills
{
private const Skills skillName = Skills.Illiterate;
private static bool isActive = false;
private static int offset = jRandomSkills.Instance.Random.Next(0, 26);
private static int offset = jRandomSkills.Instance.Random.Next(2, 26);
public static void LoadSkill()
{
@ -55,7 +55,7 @@ namespace src.player.skills
{
if (string.IsNullOrEmpty(input)) return null;
if (Server.TickCount % 64 == 0)
offset = jRandomSkills.Instance.Random.Next(1, 26);
offset = jRandomSkills.Instance.Random.Next(2, 26);
return new string([.. input.Select(c =>
{

View file

@ -33,7 +33,7 @@ namespace src.player.skills
if (playerInfo == null || playerInfo.Skill != skillName) continue;
var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString())));
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account + e.InGameMoneyServices.CashSpentThisRound)}$", e.Index.ToString()))];
SkillUtils.UpdateMenu(player, menuItems);
}
}
@ -74,7 +74,7 @@ namespace src.player.skills
var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
if (enemies.Length > 0)
{
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString())));
ConcurrentBag<(string, string)> menuItems = [.. enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account + e.InGameMoneyServices.CashSpentThisRound)}$", e.Index.ToString()))];
SkillUtils.CreateMenu(player, menuItems);
}
else
@ -90,7 +90,7 @@ namespace src.player.skills
if (playerMoneyServices == null || enemyMoneyServices == null) return;
int playerMoney = playerMoneyServices.Account;
playerMoneyServices.Account = enemyMoneyServices.Account;
playerMoneyServices.Account = enemyMoneyServices.Account + enemyMoneyServices.CashSpentThisRound;
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices");
enemyMoneyServices.Account = playerMoney;

View file

@ -1,3 +1,4 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
@ -12,7 +13,19 @@ namespace src.player.skills
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"), false);
}
public static void EnableSkill(CCSPlayerController player)
{
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo == null) return;
float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue<float>(skillName, "ChanceTo") - SkillsInfo.GetValue<float>(skillName, "ChanceFrom")) + SkillsInfo.GetValue<float>(skillName, "ChanceFrom");
playerInfo.SkillChance = newChance;
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}",
border: !Utilities.GetPlayers().Any(p => p.Team == player.Team && !p.IsBot && p != player) ? "tb" : "t");
}
public static void PlayerHurt(EventPlayerHurt @event)
@ -24,7 +37,8 @@ namespace src.player.skills
var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker?.SteamID);
if (attackerInfo?.Skill == skillName)
TeleportPlayers(attacker!, victim!);
if (Instance.Random.NextDouble() <= attackerInfo.SkillChance)
TeleportPlayers(attacker!, victim!);
}
private static void TeleportPlayers(CCSPlayerController attacker, CCSPlayerController victim)
@ -46,8 +60,10 @@ namespace src.player.skills
attackerPawn.Teleport(victimPosition, victimAngles, victimVelocity);
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8A2BE2", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "") : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
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 chanceFrom = .5f, float chanceTo = .6f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
{
public float ChanceFrom { get; set; } = chanceFrom;
public float ChanceTo { get; set; } = chanceTo;
}
}
}

View file

@ -1,19 +1,22 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
using System.Collections.Concurrent;
using System.Drawing;
using static src.jRandomSkills;
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
namespace src.player.skills
{
public class ThrowingKnife : ISkill
{
private const Skills skillName = Skills.ThrowingKnife;
private readonly static ConcurrentDictionary<uint, uint> triggers = [];
private readonly static ConcurrentDictionary<uint, uint> glows = [];
private readonly static ConcurrentDictionary<uint, KnifeInfo> knivesInfo = [];
public static void LoadSkill()
{
@ -22,40 +25,134 @@ namespace src.player.skills
public static void NewRound()
{
foreach (var player in Utilities.GetPlayers())
DisableSkill(player);
triggers.Clear();
glows.Clear();
foreach (var knifeInfo in knivesInfo.Values)
DisableKnifeSkill(knifeInfo);
knivesInfo.Clear();
}
public static void EnableSkill(CCSPlayerController player)
public static void PlayerMakeSound(UserMessage um)
{
var soundevent = um.ReadUInt("soundevent_hash");
var userIndex = um.ReadUInt("source_entity_index");
if (soundevent != 3208928088 || userIndex != 0) return;
byte[] packedParams = um.ReadBytes("packed_params");
Vector? soundPos = null;
if (packedParams != null && packedParams.Length >= 19)
{
try
{
float x = BitConverter.ToSingle(packedParams, 7);
float y = BitConverter.ToSingle(packedParams, 11);
float z = BitConverter.ToSingle(packedParams, 15);
soundPos = new Vector(x, y, z);
} catch { }
}
if (soundPos == null) return;
(var knife, var knifeInfo) = GetClosetKnife(soundPos);
if (knife == null || knife.AbsRotation == null || knifeInfo == null || knifeInfo.InitialLook == null) return;
knifeInfo.IsDropped = false;
QAngle rotation = new(knifeInfo.InitialLook?.X, knifeInfo.InitialLook?.Y, knife.AbsRotation.Z);
Vector offset = SkillUtils.GetForwardVector(new QAngle(rotation.X - 90, rotation.Y, rotation.Z)) * 2;
knife.Teleport(
new Vector(soundPos.X - offset.X, soundPos.Y - offset.Y, soundPos.Z - offset.Z),
rotation,
Vector.Zero
);
var world = Utilities.GetEntityFromIndex<CBaseEntity>(0);
knife.AcceptInput("SetParent", world, world, "!activator");
if (knifeInfo.Timer != null)
knifeInfo.Timer?.Kill();
knifeInfo.Timer =
Instance.AddTimer(3f, () =>
{
if (knife != null && knife.IsValid)
knife.AcceptInput("ClearParent");
}, TimerFlags.STOP_ON_MAPCHANGE);
}
public static bool OnWeaponCanAcquire(DynamicHook hook, CCSPlayerController player, CEconItemView econItem, CCSWeaponBaseVData vdata)
{
string weaponName = vdata.Name;
if (string.IsNullOrEmpty(weaponName)) return false;
if (!weaponName.Contains("knife") && !weaponName.Contains("bayonet"))
return false;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return false;
if (knivesInfo.TryGetValue(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null && knifeInfo.KnifeId == econItem.ItemID)
{
knifeInfo.IsDropped = false;
if (knifeInfo.Timer != null)
{
knifeInfo.Timer?.Kill();
knifeInfo.Timer = null;
}
return false;
}
hook.SetReturn(AcquireResult.InvalidItem);
return true;
}
public static void EnableSkill(CCSPlayerController _)
{
Event.EnableTransmit();
}
public static void DisableSkill(CCSPlayerController player)
{
var playersTriggers = triggers.Where(t => t.Value == player.Index).Select(t => t.Key).ToList();
foreach (var triggerIndex in playersTriggers)
if (triggers.TryRemove(triggerIndex, out _))
{
var entity = Utilities.GetEntityFromIndex<CTriggerMultiple>((int)triggerIndex);
if (entity != null && entity.IsValid)
Server.NextFrame(() =>
{
if (entity.IsValid)
entity.AcceptInput("Kill");
});
}
if (knivesInfo.TryRemove(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null)
DisableKnifeSkill(knifeInfo);
}
if (glows.TryRemove(player.Index, out uint glowIndex))
public static void DisableKnifeSkill(KnifeInfo knifeInfo)
{
if (knifeInfo.Timer != null)
knifeInfo.Timer?.Kill();
knifeInfo.Timer = null;
if (knifeInfo.TriggerIndex != null)
{
var entity = Utilities.GetEntityFromIndex<CTriggerMultiple>((int)glowIndex);
if (entity != null && entity.IsValid)
var trigger = Utilities.GetEntityFromIndex<CTriggerMultiple>((int)knifeInfo.TriggerIndex);
if (trigger != null && trigger.IsValid)
Server.NextFrame(() =>
{
if (entity.IsValid)
entity.AcceptInput("Kill");
if (trigger.IsValid)
trigger.AcceptInput("Kill");
});
}
if (knifeInfo.GlowIndex != null)
{
var glow = Utilities.GetEntityFromIndex<CTriggerMultiple>((int)knifeInfo.GlowIndex);
if (glow != null && glow.IsValid)
Server.NextFrame(() =>
{
if (glow.IsValid)
glow.AcceptInput("Kill");
});
}
if (knifeInfo.RelayIndex != null)
{
var relay = Utilities.GetEntityFromIndex<CTriggerMultiple>((int)knifeInfo.RelayIndex);
if (relay != null && relay.IsValid)
Server.NextFrame(() =>
{
if (relay.IsValid)
relay.AcceptInput("Kill");
});
}
}
@ -88,12 +185,12 @@ namespace src.player.skills
private static void ThrowKnife(CCSPlayerController player, CBasePlayerWeapon knife)
{
if (player == null || !player.IsValid) return;
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
if (knife == null || !knife.IsValid || knife.AbsOrigin == null) return;
float force = 2000;
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
float force = 2000f;
Vector pos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z);
QAngle angle = new(0, 0, 0);
@ -102,35 +199,61 @@ namespace src.player.skills
knife.Collision.CollisionAttribute.InteractsWith = pawn.Collision.CollisionAttribute.InteractsWith;
knife.Teleport(pos, angle, vel);
CreateTrigger(player, knife);
CreateGlow(player, knife);
if (!knivesInfo.ContainsKey(player.Index))
{
uint? triggerIndex = CreateTrigger(player, knife);
(uint? relayIndex, uint? glowIndex) = CreateGlow(player, knife);
knivesInfo.TryAdd(player.Index, new KnifeInfo
{
PlayerIndex = player.Index,
KnifeIndex = knife.Index,
KnifeId = knife.AttributeManager.Item.ItemID,
TriggerIndex = triggerIndex,
GlowIndex = glowIndex,
RelayIndex = relayIndex
});
}
if (knivesInfo.TryGetValue(player.Index, out KnifeInfo? knifeInfo))
{
knifeInfo.IsDropped = true;
knifeInfo.InitialLook = new QAngle(pawn.V_angle.X + 90, pawn.V_angle.Y, pawn.V_angle.Z);
}
}
private static void CreateTrigger(CCSPlayerController player, CBaseEntity knife)
private static uint? CreateTrigger(CCSPlayerController player, CBasePlayerWeapon knife)
{
if (player == null || !player.IsValid) return;
if (triggers.Any(t => t.Value == player.Index)) return;
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return;
if (knife == null || !knife.IsValid || knife.AbsOrigin == null) return;
if (player == null || !player.IsValid) return null;
if (knife == null || !knife.IsValid || knife.AbsOrigin == null) return null;
var trigger = SkillUtils.CreateTrigger($"trowingknife", 10, knife.AbsOrigin);
if (trigger == null || !trigger.IsValid) return;
if (trigger == null || !trigger.IsValid) return null;
trigger.AcceptInput("SetParent", knife, knife, "!activator");
triggers.TryAdd(trigger.Index, player.Index);
return trigger.Index;
}
private static void CreateGlow(CCSPlayerController player, CBaseEntity knife)
private static (uint?, uint?) CreateGlow(CCSPlayerController player, CBaseEntity knife)
{
if (player == null || !player.IsValid) return;
if (glows.ContainsKey(player.Index)) return;
if (knife == null || !knife.IsValid || knife.AbsOrigin == null) return;
if (player == null || !player.IsValid) return (null, null);
if (knife == null || !knife.IsValid || knife.AbsOrigin == null) return (null, null);
var modelRelay = Utilities.CreateEntityByName<CPhysicsPropMultiplayer>("prop_physics_multiplayer");
if (modelRelay == null) return (null, null);
modelRelay.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(modelRelay.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
modelRelay.SetModel(knife!.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.ModelName);
modelRelay.Spawnflags = 256u;
modelRelay.Render = Color.FromArgb(1, 255, 255, 255);
modelRelay.DispatchSpawn();
modelRelay.Teleport(knife.AbsOrigin, knife.AbsRotation, knife.AbsVelocity);
modelRelay.AcceptInput("SetParent", knife, modelRelay, "!activator");
var modelGlow = Utilities.CreateEntityByName<CPhysicsPropMultiplayer>("prop_physics_multiplayer");
if (modelGlow == null) return;
if (modelGlow == null) return (null, null);
modelGlow.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(modelGlow.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
@ -144,9 +267,9 @@ namespace src.player.skills
modelGlow.Glow.GlowRangeMin = 10;
modelGlow.Teleport(knife.AbsOrigin, knife.AbsRotation, knife.AbsVelocity);
modelGlow.AcceptInput("SetParent", knife, modelGlow, "!activator");
modelGlow.AcceptInput("SetParent", modelRelay, modelGlow, "!activator");
glows.TryAdd(player.Index, modelGlow.Index);
return (modelRelay.Index, modelGlow.Index);
}
public static void OnTriggerEnter(CBaseTrigger trigger, CBaseEntity entity)
@ -155,20 +278,25 @@ namespace src.player.skills
string triggerName = trigger.Globalname;
if (entity.DesignerName != "player" || string.IsNullOrEmpty(triggerName) || !triggerName.StartsWith("trowingknife")) return;
if (triggers.TryGetValue(trigger.Index, out var throwerIndex))
CCSPlayerPawn victimPawn = entity.As<CCSPlayerPawn>();
if (victimPawn == null || !victimPawn.IsValid || victimPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
KnifeInfo? knifeInfo = knivesInfo.Values.FirstOrDefault(ki => ki.TriggerIndex == trigger.Index);
if (knifeInfo != null)
{
var thrower = Utilities.GetPlayerFromIndex((int)throwerIndex);
var thrower = Utilities.GetPlayerFromIndex((int)knifeInfo.PlayerIndex);
if (thrower == null || !thrower.IsValid) return;
CCSPlayerPawn victimPawn = entity.As<CCSPlayerPawn>();
if (victimPawn == null || !victimPawn.IsValid || victimPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
if (thrower != null && thrower.Pawn.Value != null && victimPawn.Index == thrower.Pawn.Index) return;
bool friendlyFire = SkillsInfo.GetValue<bool>(skillName, "friendlyFire");
if (!friendlyFire && thrower?.TeamNum == victimPawn.TeamNum) return;
if (CheckHasKnife(thrower!)) return;
SkillUtils.TakeHealth(victimPawn, 99999);
Server.PrintToChatAll($"HIT");
SkillUtils.TakeHealth(victimPawn, SkillsInfo.GetValue<int>(skillName, "damage"));
}
}
@ -179,6 +307,31 @@ namespace src.player.skills
return pawn.WeaponServices.MyWeapons.Any(w => w.IsValid && w != null && w.Value != null && w.Value.IsValid && (w.Value.DesignerName.Contains("knife") || w.Value.DesignerName.Contains("bayonet")));
}
private static (CBaseEntity?, KnifeInfo?) GetClosetKnife(Vector pos)
{
double minDistance = double.MaxValue;
KnifeInfo? minKnifeInfo = null;
CBaseEntity? closet = null;
foreach (var knifeInfo in knivesInfo.Values)
{
if (!knifeInfo.IsDropped) continue;
var knifeEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)knifeInfo.KnifeIndex);
if (knifeEntity == null || !knifeEntity.IsValid || knifeEntity.AbsOrigin == null) continue;
double distance = SkillUtils.GetDistance(knifeEntity.AbsOrigin, pos);
if (distance < minDistance)
{
minDistance = distance;
closet = knifeEntity;
minKnifeInfo = knifeInfo;
}
}
return (closet, minKnifeInfo);
}
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
{
foreach (var (info, player) in infoList)
@ -186,19 +339,35 @@ namespace src.player.skills
if (player == null || !player.IsValid) continue;
var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle);
foreach (var glow in glows)
foreach ((uint playerIndex, KnifeInfo knifeInfo) in knivesInfo)
{
if (glow.Key == player.Index || (observedPlayer != null && observedPlayer.IsValid && glow.Key == observedPlayer.Index)) continue;
if (playerIndex == player.Index || (observedPlayer != null && observedPlayer.IsValid && playerIndex == observedPlayer.Index)) continue;
if (knifeInfo.GlowIndex == null) continue;
var glowEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)glow.Value);
var glowEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)knifeInfo.GlowIndex);
if (glowEntity == null || !glowEntity.IsValid) continue;
info.TransmitEntities.Remove(glowEntity.Index);
}
}
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8f108f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "") : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
public class KnifeInfo()
{
public required uint PlayerIndex { get; set; }
public required uint KnifeIndex { get; set; }
public required ulong KnifeId { get; set; }
public required uint? TriggerIndex { get; set; }
public required uint? GlowIndex { get; set; }
public required uint? RelayIndex { get; set; }
public bool IsDropped { get; set; } = false;
public QAngle? InitialLook { get; set; } = null;
public Timer? Timer { get; set; } = null;
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8f108f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", bool friendlyFire = false, int damage = 9999) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
{
public bool FriendlyFire { get; set; } = friendlyFire;
public int Damage { get; set; } = damage;
}
}
}

View file

@ -4,7 +4,6 @@ using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Utils;
using src.utils;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using static src.jRandomSkills;
namespace src.player.skills
@ -12,51 +11,39 @@ namespace src.player.skills
public class ToxicSmoke : ISkill
{
private const Skills skillName = Skills.ToxicSmoke;
private static readonly ConcurrentDictionary<CCSPlayerController, byte> players = [];
private static readonly ConcurrentDictionary<CTriggerMultiple, byte> triggers = [];
private const string triggerName = "toxic_smoke";
private static readonly ConcurrentDictionary<Vector, byte> smokes = [];
public static void LoadSkill()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
}
public static void NewRound()
{
players.Clear();
triggers.Clear();
smokes.Clear();
}
public static void EnableSkill(CCSPlayerController player)
public static void SmokegrenadeDetonate(EventSmokegrenadeDetonate @event)
{
SkillUtils.TryGiveWeapon(player, CsItem.SmokeGrenade);
var player = @event.Userid;
if (player == null || !player.IsValid) return;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return;
smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0);
}
public static void OnTriggerEnter(CBaseTrigger trigger, CBaseEntity entity)
public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event)
{
if (!trigger.Globalname.StartsWith(triggerName) || entity.DesignerName != "player") return;
var player = @event.Userid;
if (player == null || !player.IsValid) return;
CCSPlayerPawn playerPawn = new(entity.Handle);
if (playerPawn == null || playerPawn.Controller?.Value == null)return;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return;
CCSPlayerController player = playerPawn.Controller.Value.As<CCSPlayerController>();
if (player == null) return;
players.TryAdd(player, 0);
}
public static void OnTriggerExit(CBaseTrigger trigger, CBaseEntity entity)
{
if (!trigger.Globalname.StartsWith(triggerName) || entity.DesignerName != "player") return;
CCSPlayerPawn playerPawn = new(entity.Handle);
if (playerPawn == null || playerPawn.Controller?.Value == null) return;
CCSPlayerController player = playerPawn.Controller.Value.As<CCSPlayerController>();
if (player == null) return;
players.TryRemove(player, out _);
foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z))
smokes.TryRemove(smoke, out _);
}
public static void OnEntitySpawned(CEntityInstance entity)
@ -85,30 +72,28 @@ namespace src.player.skills
});
}
public static void SmokegrenadeDetonate(EventSmokegrenadeDetonate @event)
public static void OnTick()
{
var player = @event.Userid;
if (player == null || !player.IsValid) return;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return;
int tick = SkillsInfo.GetValue<int>(skillName, "tickCooldown");
if (Server.TickCount % tick != 0) return;
var trigger = SkillUtils.CreateTrigger(triggerName, SkillsInfo.GetValue<float>(skillName, "smokeRadius"), new Vector(@event.X, @event.Y, @event.Z));
if (trigger == null) return;
triggers.TryAdd(trigger, 0);
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
int smokeDamage = SkillsInfo.GetValue<int>(skillName, "smokeDamage");
foreach (Vector smokePos in smokes.Keys)
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && p.PawnIsAlive))
{
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
if (SkillUtils.GetDistance(smokePos, pawn.AbsOrigin) <= smokeRadius)
AddHealth(pawn, -smokeDamage);
}
}
public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event)
public static void EnableSkill(CCSPlayerController player)
{
var player = @event.Userid;
if (player == null || !player.IsValid) return;
var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID);
if (playerInfo?.Skill != skillName) return;
foreach (var trigger in triggers.Keys.Where(t => t.AbsOrigin?.X == @event.X && t.AbsOrigin?.Y == @event.Y && t.AbsOrigin?.Z == @event.Z))
{
triggers.TryRemove(trigger, out _);
trigger.AcceptInput("Kill");
}
SkillUtils.TryGiveWeapon(player, CsItem.SmokeGrenade);
}
private static void AddHealth(CCSPlayerPawn player, int health)
@ -124,20 +109,11 @@ namespace src.player.skills
player.CommitSuicide(false, true);
}
public static void OnTick()
{
if (Server.TickCount % 17 != 0) return;
foreach (var player in players.Keys)
{
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid || !player.PawnIsAlive) return;
AddHealth(player.PlayerPawn.Value, -SkillsInfo.GetValue<int>(skillName, "smokeDamage"));
}
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", int smokeDamage = 2, float smokeRadius = 180) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
{
public int SmokeDamage { get; set; } = smokeDamage;
public float SmokeRadius { get; set; } = smokeRadius;
public int TickCooldown { get; set; } = tickCooldown;
}
}
}