Updates & Fixes

# Updates & Fixes
- Vote system: Timer now resets after each vote
- Automatic copying of gamedata files from source to server (Thanks to vinicius-trev for the help)
- Improved power assignment at round start
- Observers can now see invisible players.
- Added geolocation support for Turkey and Czechia
- New power: Illiterate
- In development: Smoker skill
- Baseball: Decoy now have the same collision as players (PlayerClip)
- Noclip: Reworked
- Pilot: Currently broken
- Replicator: Added crouching animations for replicas
- SoundMaker: Reworked (automatically play a sound every 2s (only for you))
- Wallhack: Glow disabled for dead players

# TODO
- Add/update descriptions for new/reworked skills
- Fix Pilot, SniperElite
This commit is contained in:
Juzlus 2026-02-14 05:33:17 +01:00
parent f76093ef6e
commit 09803748bc
28 changed files with 691 additions and 295 deletions

View file

@ -1,9 +1,13 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
using static src.jRandomSkills;
using System.Collections.Concurrent;
using CS2TraceRay.Class;
using CS2TraceRay.Struct;
using src.utils;
using System.Collections.Concurrent;
using static src.jRandomSkills;
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
namespace src.player.skills
{
@ -87,52 +91,139 @@ namespace src.player.skills
public static void UseSkill(CCSPlayerController player)
{
var playerPawn = player.PlayerPawn.Value;
var duration = SkillsInfo.GetValue<float>(skillName, "duration");
if (playerPawn?.CBodyComponent == null) return;
if (SkillPlayerInfo.TryGetValue(player.SteamID, out var skillInfo))
{
if (!player.IsValid || !player.PawnIsAlive) return;
if (skillInfo.IsFlying)
{
StopFlying(player, skillInfo);
return;
}
if (skillInfo.CanUse)
{
var duration = SkillsInfo.GetValue<float>(skillName, "duration");
skillInfo.CanUse = false;
skillInfo.IsFlying = true;
skillInfo.Cooldown = DateTime.Now;
skillInfo.LastPosition = playerPawn.AbsOrigin == null ? null : new Vector(playerPawn.AbsOrigin.X, playerPawn.AbsOrigin.Y, playerPawn.AbsOrigin.Z);
SetNoclip(player, true);
skillInfo.Timer?.Kill();
playerPawn.ActualMoveType = MoveType_t.MOVETYPE_NOCLIP;
Instance.AddTimer(duration, () => {
if (playerPawn == null || !playerPawn.IsValid || !skillInfo.IsFlying) return;
skillInfo.IsFlying = false;
playerPawn.ActualMoveType = MoveType_t.MOVETYPE_WALK;
});
Instance.AddTimer(duration + 4, () => {
if (playerPawn == null || !playerPawn.IsValid || !player.PawnIsAlive || skillInfo.IsFlying) return;
if (skillInfo.LastPosition == null || playerPawn.AbsOrigin == null) return;
skillInfo.IsFlying = false;
var diff = Math.Abs(playerPawn.AbsOrigin.Z - skillInfo.LastPosition.Z);
if (diff > 3000 && playerPawn.AbsOrigin.Z < skillInfo.LastPosition.Z)
playerPawn.Teleport(skillInfo.LastPosition, null, new Vector(0,0,0));
});
}
else if (skillInfo.IsFlying)
{
skillInfo.IsFlying = false;
playerPawn.ActualMoveType = MoveType_t.MOVETYPE_WALK;
Instance.AddTimer(4, () => {
if (playerPawn == null || !playerPawn.IsValid || !player.PawnIsAlive || skillInfo.IsFlying) return;
if (skillInfo.LastPosition == null || playerPawn.AbsOrigin == null) return;
skillInfo.IsFlying = false;
var diff = Math.Abs(playerPawn.AbsOrigin.Z - skillInfo.LastPosition.Z);
if (diff > 3000 && playerPawn.AbsOrigin.Z < skillInfo.LastPosition.Z)
playerPawn.Teleport(skillInfo.LastPosition, null, new Vector(0, 0, 0));
skillInfo.Timer = Instance.AddTimer(duration, () =>
{
StopFlying(player, skillInfo);
});
}
}
}
private static void SetNoclip(CCSPlayerController player, bool noclip = true)
{
if (player == null || !player.IsValid) return;
var playerPawn = player.PlayerPawn.Value;
if (playerPawn == null || !playerPawn.IsValid || !player.PawnIsAlive) return;
playerPawn.MoveType = noclip ? MoveType_t.MOVETYPE_NOCLIP : MoveType_t.MOVETYPE_WALK;
Schema.SetSchemaValue(playerPawn.Handle, "CBaseEntity", "m_nActualMoveType", (int)playerPawn.MoveType);
Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_MoveType");
}
private static void StopFlying(CCSPlayerController player, PlayerSkillInfo skillInfo)
{
skillInfo.Timer?.Kill();
skillInfo.Timer = null;
if (!skillInfo.IsFlying) return;
skillInfo.IsFlying = false;
if (player == null || !player.IsValid || !player.PawnIsAlive) return;
SetNoclip(player, false);
var playerPawn = player.PlayerPawn.Value;
if (playerPawn == null || !playerPawn.IsValid || skillInfo.IsFlying) return;
Vector? safePoint = GetCorrectPosition(player, skillInfo);
playerPawn.Teleport(safePoint ?? skillInfo.LastPosition, null, new Vector(0, 0, 0));
}
private static Vector? GetCorrectPosition(CCSPlayerController player, PlayerSkillInfo skillInfo)
{
var playerPawn = player.PlayerPawn.Value;
if (playerPawn == null || !playerPawn.IsValid || playerPawn.AbsOrigin == null) return null;
Vector currentPos = playerPawn.AbsOrigin;
float offset = 50;
Vector[] checkOffsets =
{
currentPos,
currentPos + new Vector(offset, 0, 10),
currentPos + new Vector(-offset, 0, 10),
currentPos + new Vector(0, offset, 10),
currentPos + new Vector(0, -offset, 10),
currentPos + new Vector(offset, 0, 60),
currentPos + new Vector(-offset, 0, 60),
currentPos + new Vector(0, offset, 60),
currentPos + new Vector(0, -offset, 60),
};
ulong mask = playerPawn.Collision.CollisionAttribute.InteractsWith;
ulong contents = playerPawn.Collision.CollisionGroup;
bool hasGround = false;
foreach (Vector targetPos in checkOffsets)
{
Vector start = new(targetPos.X, targetPos.Y, targetPos.Z + 70);
Vector end = new(targetPos.X, targetPos.Y, targetPos.Z - 1000);
CGameTrace groundTrace = TraceRay.TraceShape(start, end, mask, contents, player);
if (!groundTrace.DidHit()) continue;
hasGround = true;
Vector newPos =
groundTrace.EndPos.Z > targetPos.Z
? new(groundTrace.EndPos.X, groundTrace.EndPos.Y, groundTrace.EndPos.Z)
: targetPos;
if (IsPositionSafe(newPos, player))
return newPos;
}
if (hasGround)
skillInfo.Cooldown = DateTime.Now.AddSeconds(-SkillsInfo.GetValue<float>(skillName, "cooldown") + SkillsInfo.GetValue<float>(skillName, "cooldownWhenStuck"));
return hasGround ? currentPos : null;
}
private static bool IsPositionSafe(Vector pos, CCSPlayerController player)
{
var playerPawn = player.PlayerPawn.Value;
if (playerPawn == null || !playerPawn.IsValid || playerPawn.AbsOrigin == null) return false;
float footHeight = 0;
float headHeight = 70;
float innerDist = 12;
ulong mask = playerPawn.Collision.CollisionAttribute.InteractsWith;
ulong contents = playerPawn.Collision.CollisionGroup;
Vector s1 = new(pos.X - innerDist, pos.Y - innerDist, pos.Z + footHeight);
Vector e1 = new(pos.X + innerDist, pos.Y + innerDist, pos.Z + headHeight);
CGameTrace t1 = TraceRay.TraceShape(s1, e1, mask, contents, player);
if (t1.DidHit() || t1.AllSolid) return false;
Vector s2 = new(pos.X + innerDist, pos.Y - innerDist, pos.Z + footHeight);
Vector e2 = new(pos.X - innerDist, pos.Y + innerDist, pos.Z + headHeight);
CGameTrace t2 = TraceRay.TraceShape(s2, e2, mask, contents, player);
if (t2.DidHit() || t2.AllSolid) return false;
return true;
}
public class PlayerSkillInfo
{
public ulong SteamID { get; set; }
@ -140,11 +231,13 @@ namespace src.player.skills
public bool IsFlying { get; set; }
public DateTime Cooldown { get; set; }
public Vector? LastPosition { get; set; }
public Timer? Timer { get; set; }
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#44ebd4", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float cooldown = 30f, float duration = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#44ebd4", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float cooldown = 30f, float duration = 2f, float cooldownWhenStuck = 5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission)
{
public float Cooldown { get; set; } = cooldown;
public float CooldownWhenStuck { get; set; } = cooldownWhenStuck;
public float Duration { get; set; } = duration;
}
}