HudInputarm(slot: number, opts: { onActivate: () => void }): voidslot. Replaces any previous arm on that slot.disarm(slot: number): voidisArmed(slot: number): booleanisActive(slot: number): boolean@s2script/cs2
CS2 player/pawn model, schema accessors, typed events overlay, menus, CustomHudLayout / hudkit, CustomPlayerCamera, Fade/Shake/HintText, and pickup gates (items / ItemsApi).
import { HudInput } from "@s2script/cs2";Author-time types ship in the npm package; the engine injects the runtime at plugin load.
Add @s2script/sdk to your plugin's dependencies.
Generated from the shipped type definitions.
HudInputarm(slot: number, opts: { onActivate: () => void }): voidslot. Replaces any previous arm on that slot.disarm(slot: number): voidisArmed(slot: number): booleanisActive(slot: number): booleanPawnA CS2 player pawn (the in-world body): the generated CCSPlayerPawn schema fields + the liveness-gated ref.
controller is the typed reverse hop (shadows the raw generated m_hController handle).
Nav props (sceneNode, weaponServices, movementServices, aimPunchServices) are generated from nav-targets.json.
readonly ref: EntityRefreadonly isValid: booleanref.isValid() for any pawn WRITE — a staged-but-live
pawn passes ref.isValid() yet segfaults on SetModel/SetParent. Falls back to liveness if the
staging-flag read is unavailable.readonly controller: Player | nullreadonly origin: Vector | nullreadonly angles: QAngle | nulleyeAngles.readonly sceneNode: SceneNode | nullreadonly weaponServices: WeaponServices | nullreadonly movementServices: MovementServices | nullreadonly aimPunchServices: AimPunchServices | nullsetVelocity(x: number, y: number, z: number): booleangetCustomCamera(): CustomPlayerCamera | nullmoveType: number | nullslay(): voidreadonly maxSpeed: number | nullCCSPlayerPawn::GetPlayerMaxSpeed).
A getter rather than a field because the engine computes it — there is no m_flMaxSpeed on
CCSPlayerPawn in the schema, so there is nothing to read. null (never 0) when the descriptor
is unavailable or the ref is stale: 0 is a legitimate speed for a frozen player, so it must not
double as "unavailable".giveNamedItem(name: string): Weapon | nullreadonly activeWeapon: Weapon | nullreadonly weapons: Weapon[]removeWeapon(weapon: Weapon): booleanstripWeapons(): booleandisarm(): booleandropActiveWeapon(): booleanreadonly nextAttack: number | nullblockFiring(seconds?: number): booleanseconds (default effectively-indefinite) by writing m_flNextAttack. The
gate is server-authoritative and time-based: a durable block needs a large value or a per-frame refresh.
Returns false if unresolved/stale.allowFiring(): booleanaimTrace(opts?: { distance?: number; mask?: number; ignoreEntity?: EntityRef }): TraceHit | nulleyeAngles. Ignores
this pawn's own entity unless ignoreEntity is given. Returns null if the transform/angles are
unreadable (stale ref). distance defaults to 8192.emitSound(name: string, opts?: { recipients?: number[]; volume?: number }): numberstopSound(name: string): booleanrecipients/volume: the engine stops it for everyone hearing it. False if the ref is
stale or the op is unavailable.setGravityScale(scale: number): booleanm_flGravityScale, which the engine ignores — see EntityRef.setGravityScale. False if the
ref is stale, the op is unavailable, or scale is not finite.applyAbsVelocityImpulse(impulse: number[]): booleanteleport(null, null, velocity) which sets velocity absolutely. impulse is an [x,y,z] triple.
False if the ref is stale, the op is unavailable, or any component is not finite.setModelScale(scale: number): booleanPawnEntry point for the Pawn body object — resolve the in-world pawn for a player slot.
import { Pawn } from "@s2script/cs2";
const pawn = Pawn.forSlot(0);
if (pawn) console.log(pawn.activeWeapon?.clip1);forSlot(slot: number): Pawn | nullPlayerA CS2 player (the persistent controller entity): the generated CCSPlayerController schema fields
(team/score/ping/…) + the liveness-gated controller ref. pawn is the typed body (shadows the
raw generated m_hPawn handle). Bound to a host connection lifetime — a stored Player does not
target a replacement in the same slot.
readonly ref: EntityRefreadonly slot: numberCPlayerSlot) — not sufficient by itself to identify a connection.readonly pawn: Pawn | nullreadonly matchStats: MatchStats | nullm_pActionTrackingServices pointer hop plus the embedded m_matchStats block.
null when the services pointer is not resolvable (a controller with no tracking services yet).
Only the four counters a gamemode legitimately rewrites are writable; the rest of the block is
engine bookkeeping and stays read-only. See nav-targets.json.readonly userId: number-1 if unassigned/absent.readonly steamId: stringGetClientXUID). "0" for bots / unauthenticated.
This is the AUTHORITATIVE id for admin lookups (Admin.get/forSlot use it). Do NOT confuse it with
the schema-generated steamID (capital ID, from m_steamID) — that controller field is string | null
and can be "0"/null, so using it for authorization decisions is unreliable.kick(reason?: string): voidsetName(name: string): booleanchangeTeam(team: number): voidteam (Spectator=1, Terrorist=2, CounterTerrorist=3) via the
sig-resolved CCSPlayerController::ChangeTeam. Serial-gated; a no-op if the ref is stale or the
signature is unresolved. team is bounded to 0..3 engine-side.spectate(): voidchangeTeam(1)).switchTeam(team: number): voidchangeTeam,
which has jointeam semantics and usually kills). Works on DEAD controllers too (a pure
scoreboard/win-condition team move). CAVEAT: the engine MAY respawn the pawn during the call —
re-resolve player.pawn on the next frame before any pawn write. Game events the engine fires
inside the call run other plugins' handlers before this returns. For None (0) /
Spectator (1) this dispatches to changeTeam (CSSharp/SwiftlyS2 parity) — prefer spectate().
Serial-gated; a no-op if the ref is stale or the signature is unresolved. Bounded 0..3.respawn(): booleanEvents.on / onPre("player_spawn") run other plugins before this returns.
players.onRespawn does not fire (SourceMod blockhook / bypassWith).
Returns false when the player is already alive, the ref is stale, or Respawn/SetPawn failed
their boot gates.PlayerEntry point for the Player controller object — resolve players by slot or user-id, enumerate connected players, or resolve a SourceMod target string.
import { Player } from "@s2script/cs2";
for (const p of Player.all()) p.pawn?.slay();fromSlot(slot: number): Player | nullall(): Player[]fromUserId(userId: number): Player | nullnull if no such player. Pawnless-safe.allConnected(): Player[]all().target(pattern: string, callerSlot: number, filterImmunity?: boolean): Player[]#userid/name/@all/@me; empty on no match. callerSlot < 0 = server console (no @me).EventsGame-event subscription (typed overlay). Importing from @s2script/cs2 gives the typed overloads:
Events.on("player_death", ev => ev.getPlayerSlot("attacker")) typechecks via the GameEvents map.
Events.onPre runs before broadcast and may return a HookResult to block.
Events.fire fires an event with typed field constraints.
The off signature matches @s2script/events semantics: removes ALL of this plugin's handlers for name.
on<K extends keyof GameEvents>(name: K, handler: (ev: GameEvents[K]) => void): voidGameEvents[K] payload with typed field accessors.on(name: string, handler: (ev: GameEvent) => void): voidoff(name: string, handler: (ev: GameEvent) => void): voidname (the handler arg is accepted for API symmetry but not matched).onPre<K extends keyof GameEvents>(name: K, handler: (ev: GameEvents[K]) => HookResultValue | void): voidonPre(name: string, handler: (ev: GameEvent) => HookResultValue | void): voidfire<K extends keyof GameEvents>(name: K, fields?: Record<string, number | string | boolean | bigint>, dontBroadcast?: boolean): booleandontBroadcast keeps it server-side (not sent to clients). Returns false if the event is unknown.fire(name: string, fields?: Record<string, number | string | boolean | bigint>, dontBroadcast?: boolean): booleandontBroadcast keeps it server-side. Returns false if the event is unknown.ActivityShow-activity helper: SourceMod's FormatActivitySource per-recipient decision.
For each connected recipient, call formatSource(actorSlot, recipientSlot) to get
{ show, name } — whether to display the action to that recipient, and under what name
(real name for admins / self, generic label for non-admins, per the SHOW_ACTIVITY flags).
actorSlot < 0 = server console (always real "Console" label).
formatSource(actorSlot: number, recipientSlot: number): { show: boolean; name: string }ChatColorsCS2 chat color control bytes (values from CounterStrikeSharp's ChatColors enum). Prepend one to a chat
message to color it — CS2 requires a leading control byte for the message to render at all. The plugin
owns color (SourceMod-parity): e.g. Chat.toAll(ChatColors.Green + "[SM] hello").
readonly Default: stringreadonly White: stringreadonly DarkRed: stringreadonly LightPurple: stringreadonly Green: stringreadonly Olive: stringreadonly Lime: stringreadonly Red: stringreadonly Grey: stringreadonly Yellow: stringreadonly Silver: stringreadonly Blue: stringreadonly DarkBlue: stringreadonly BlueGrey: stringreadonly Purple: stringreadonly LightRed: stringreadonly Orange: stringpickPlayer(adminSlot: number, onPicked: (target: Player) => void): voidShow a target-picker Center menu of connected players to adminSlot (the adminmenu framework's
shared player picker). The picked player is re-resolved via Player.fromUserId at select time,
so onPicked only ever receives a live target — a player who left in the meantime is skipped
with a chat notice to adminSlot, and onPicked is not called.
BeamHandleA live CEnvBeam handle. update() moves both endpoints; remove() destroys it.
readonly ref: EntityRefupdate(start: Vector, end: Vector): voidstart to end.remove(): booleanBeamDraw a point-to-point beam (a CEnvBeam) from start to end. Returns a handle, or null if the entity couldn't be created. The beam is game-world-owned — call handle.remove() to clean up.
draw(start: Vector, end: Vector, opts?: { color?: [number, number, number, number]; width?: number }): BeamHandle | nullstart to end. opts.color is RGBA, opts.width in units. Returns a BeamHandle, or null if the entity couldn't be created.GameRulesViewA live read view over CCSGameRules (via the cs_gamerules proxy). Every field is liveness-gated at the proxy root and reads null if the proxy is gone (e.g. between maps).
readonly warmupPeriod: boolean | nullreadonly freezePeriod: boolean | nullreadonly roundTime: number | nullreadonly freezeTime: number | nullreadonly totalRoundsPlayed: number | nullreadonly gamePhase: number | nullreadonly bombPlanted: boolean | nullreadonly roundsPlayedThisPhase: number | nullreadonly gameRestart: boolean | nullreadonly gameStartTime: number | nullreadonly matchWaitingForResume: boolean | nullreadonly hasMatchStarted: boolean | nullreadonly roundStartTime: number | nullreadonly timeElapsed: number | nullreadonly timeRemaining: number | nullsetRoundTime(seconds: number): booleansetTimeRemaining(seconds: number): booleanaddTimeRemaining(seconds: number): booleanterminateRound(reason: number, delay?: number): booleanEvents.on / onPre("round_end") run other plugins
before this returns. gameRules.onTerminateRound does not fire for this call
(SourceMod CS_OnTerminateRound / blockhook). To cancel an *engine* round end, subscribe
to onTerminateRound. delay (default 5s) is the engine's pre-restart delay. Returns false
when degraded (unresolved signature, stale proxy, or reason outside 0..22).GameRulesRead + drive CCSGameRules state. get() re-finds the cs_gamerules proxy each call (liveness-gated cache); returns null when no proxy exists (e.g. pre-map-load).
get(): GameRulesView | nullterminateRound(reason: number, delay?: number): booleanTeamsTeam scoreboard scores (cs_team_manager entities, CTeam.m_iScore + notifyStateChanged). team is 0..3 (Unassigned/Spectator/T/CT), matched by m_iTeamNum; entities are re-found per call.
getScore(team: number): number | nullteam (0..3), or null if no matching cs_team_manager entity.setScore(team: number, score: number): booleanteam (writes m_iScore + notifyStateChanged). false if the entity isn't found.addScore(team: number, delta: number): booleandelta to team's scoreboard score. false if the entity isn't found.RoundEndReasonCS2 round-end reasons (CCSGameRules::TerminateRound / round_end.reason). Binary-validated against our build (reason bound = 22; #SFUI_Notice_* switch). Gaps 2/3/15 are removed legacy VIP reasons.
readonly Unknown: 0readonly TargetBombed: 1readonly TerroristsEscaped: 4readonly CTsPreventEscape: 5readonly EscapingTerroristsNeutralized: 6readonly BombDefused: 7readonly CTsWin: 8readonly TerroristsWin: 9readonly RoundDraw: 10readonly AllHostagesRescued: 11readonly TargetSaved: 12readonly HostagesNotRescued: 13readonly TerroristsNotEscaped: 14readonly GameCommencing: 16readonly TerroristsSurrender: 17readonly CTsSurrender: 18readonly TerroristsPlanted: 19readonly CTsReachedHostage: 20readonly SurvivalWin: 21readonly SurvivalDraw: 22WinPanelFinalEventcs_win_panel_round final_event values (validated at the live gate against a natural round end).
readonly CTsWin: 2readonly TerroristsWin: 3FadeScreen-fade user message (CUserMessageFade). duration/holdTime are engine fade units; color is a packed RGBA fixed32. Returns false if the message/fields don't resolve.
to(slot: number, opts: { duration?: number; holdTime?: number; color?: number; flags?: number }): booleanslot. duration/holdTime are engine fade units; color is packed RGBA fixed32. false if unresolved.blind(slot: number, duration?: number): booleanduration — the canonical flashbang-style effect.ShakeScreen-shake user message (CUserMessageShake). command 0 = start. Returns false if unresolved.
to(slot: number, opts: { command?: number; amplitude?: number; frequency?: number; duration?: number }): booleanslot. command 0 = start; amplitude/frequency/duration are engine shake units. false if unresolved.HintTextBest-effort hint text (TextMsg-family). Returns false if the message/fields don't resolve.
to(slot: number, text: string): booleantext as hint text to slot. false if the message/fields don't resolve.ZoneBoxA world-space coordinate triple (a corner or center of a TriggerZoneHandle box).
x: numbery: numberz: numberTriggerZoneHandleA live runtime trigger_multiple zone (from TriggerZone.create).
ref: EntityRefcenter: ZoneBoxremove(): booleanTriggerZoneCreate arbitrary-box touch zones at runtime (a runtime trigger_multiple).
create(min: ZoneBox, max: ZoneBox, opts?: { model?: string; spawnflags?: number }): TriggerZoneHandle | nullonOutput from @s2script/sdk). Non-solid (pass-through).SoundsCurated built-in CS2 soundevent names (see
readonly Ping: stringUI.PlayerPing).readonly PingUrgent: stringUI.PlayerPingUrgent).readonly Ak47Shot: stringWeapon_AK47.Single).readonly DeagleShot: stringWeapon_DEagle.Single).WeaponA CS2 weapon entity (CCSWeaponBase). All generated field accessors (clip1, clip2, fallbackPaintKit, ownerEntity, inherited health/teamNum/...) are read+write; a stale weapon reads null.
readonly ref: EntityRefisValid(): booleanpaintKit: number | nullfallbackPaintKit).readonly owner: Pawn | nullsetAmmo(clip: number, reserve?: number): booleanreserve is accepted but deferred (m_pReserveAmmo layout). false if stale.remove(): booleanWeaponEntry point for the Weapon entity object — wrap a raw EntityRef or enumerate weapons
by class name. Prefer Pawn's activeWeapon/weapons/giveNamedItem for owned weapons.
import { Weapon } from "@s2script/cs2";
// `ent` is an EntityRef, e.g. from Entity.onSpawn("weapon_*", …)
const weapon = Weapon.fromEntity(ent);
if (weapon) weapon.setAmmo(90);fromEntity(ref: EntityRef | null): Weapon | nullfindAll(className: string): Weapon[]className as a Weapon (e.g. "weapon_ak47").CustomCameraModeCS2's experimental camera modes. Requires the September 9, 2026 update.
readonly DISABLED: 0readonly CONTROLLED: 1readonly CONTROLLED_POSITION: 2readonly FOLLOW_POSITION: 3CustomCameraModeValueCameraFollowConfigreadonly followEntity: EntityRefreadonly followEyes?: booleanreadonly followOffset?: { x: number; y: number; z: number }readonly cameraOffset?: { x: number; y: number; z: number }readonly clipCameraOffset?: booleanreadonly cameraOffsetReturnStrength?: numberCustomPlayerCameraBorrowed, pawn-owned engine camera. Acquire with Pawn.getCustomCamera(). Shared by all scripts controlling that pawn; the latest mutation wins. Disable explicitly when finished; plugin unload does not undo engine state. Do not remove the entity: the engine owns its association with the pawn. Experimental, matching Valve's API. All accesses are entity-liveness gated.
readonly ref: EntityRefisValid(): booleangetPlayer(): Pawn | nullgetMode(): CustomCameraModeValue | nullsetMode(mode: CustomCameraModeValue): booleansetFollowConfig(config: CameraFollowConfig): boolean