@s2script/sdk 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/sdkhooks.d.ts ADDED
@@ -0,0 +1,543 @@
1
+ /**
2
+ * @s2script/sdkhooks — per-entity SDKHooks (SourceMod `SDKHook` / `SDKUnhook`).
3
+ * NO runtime code: the engine injects the implementation at load time.
4
+ */
5
+ import type { EntityRef } from "./entity";
6
+ import type { DamageInfo } from "./damage";
7
+ import type { HookResultValue } from "./events";
8
+ import type { Client } from "./clients";
9
+
10
+ type TouchCallback = (entity: EntityRef, other: EntityRef | null) => HookResultValue | void;
11
+ type TouchPostCallback = (entity: EntityRef, other: EntityRef | null) => void;
12
+ type WeaponCallback = (entity: EntityRef, weapon: EntityRef | null) => HookResultValue | void;
13
+ type WeaponPostCallback = (entity: EntityRef, weapon: EntityRef | null) => void;
14
+ type ThisCallback = (entity: EntityRef) => HookResultValue | void;
15
+ type ThisVoidCallback = (entity: EntityRef) => void;
16
+ type FireBulletsPostCallback = (entity: EntityRef, shots: number, weaponName: string) => void;
17
+ type ReloadPostCallback = (weapon: EntityRef, successful: boolean) => void;
18
+ type UseCallback = (
19
+ entity: EntityRef,
20
+ activator: EntityRef | null,
21
+ caller: EntityRef | null,
22
+ type: UseTypeValue,
23
+ value: number,
24
+ ) => HookResultValue | void;
25
+ type UsePostCallback = (
26
+ entity: EntityRef,
27
+ activator: EntityRef | null,
28
+ caller: EntityRef | null,
29
+ type: UseTypeValue,
30
+ value: number,
31
+ ) => void;
32
+
33
+ /**
34
+ * `CBaseEntity::Use` use-type (`USE_OFF` / `USE_ON` / `USE_SET` / `USE_TOGGLE`).
35
+ */
36
+ export type UseTypeValue = 0 | 1 | 2 | 3;
37
+
38
+ /**
39
+ * `CBaseEntity::Use` use-type constants (wiki `USE_*`).
40
+ */
41
+ export declare const UseType: {
42
+ /** `USE_OFF` — turn off. */
43
+ readonly Off: 0;
44
+ /** `USE_ON` — turn on. */
45
+ readonly On: 1;
46
+ /** `USE_SET` — set to `value`. */
47
+ readonly Set: 2;
48
+ /** `USE_TOGGLE` — toggle. */
49
+ readonly Toggle: 3;
50
+ };
51
+
52
+ /**
53
+ * Shipped SDKHook types. Wiki names without the `SDKHook_` prefix. A wiki name whose engine
54
+ * backing failed or is absent returns `false` from {@link SDKHook} (does not throw). A string
55
+ * that is not a member here throws.
56
+ */
57
+ export declare const SDKHookType: {
58
+ /** Pre-apply damage (`DispatchTraceAttack`). Mutate {@link DamageInfo.damage} in place. */
59
+ readonly OnTakeDamage: "OnTakeDamage";
60
+ /**
61
+ * CheckTransmit mux (wiki `SDKHook_SetTransmit`). `Handled` / `Stop` hide this entity from this
62
+ * viewer. AND-merge with `Transmit.setVisibleTo`: either API can hide; SetTransmit cannot
63
+ * un-hide a native mask clear. There is no `SetTransmitPost`.
64
+ */
65
+ readonly SetTransmit: "SetTransmit";
66
+ /** `CBaseEntity::StartTouch` pre. `Handled` / `Stop` skip the original virtual. */
67
+ readonly StartTouch: "StartTouch";
68
+ /** `CBaseEntity::Touch` pre. `Handled` / `Stop` skip the original virtual. */
69
+ readonly Touch: "Touch";
70
+ /** `CBaseEntity::EndTouch` pre. `Handled` / `Stop` skip the original virtual. */
71
+ readonly EndTouch: "EndTouch";
72
+ /** `CBaseEntity::Blocked` pre. `Handled` / `Stop` skip the original virtual. */
73
+ readonly Blocked: "Blocked";
74
+ /** `StartTouch` post. Return is ignored. */
75
+ readonly StartTouchPost: "StartTouchPost";
76
+ /** `Touch` post. Return is ignored. */
77
+ readonly TouchPost: "TouchPost";
78
+ /** `EndTouch` post. Return is ignored. */
79
+ readonly EndTouchPost: "EndTouchPost";
80
+ /** `Blocked` post. Return is ignored. */
81
+ readonly BlockedPost: "BlockedPost";
82
+ /** `CBaseEntity::Spawn` pre. `Handled` / `Stop` skip the original virtual. */
83
+ readonly Spawn: "Spawn";
84
+ /** `Spawn` post. Return is ignored. */
85
+ readonly SpawnPost: "SpawnPost";
86
+ /** `CBaseEntity::Think` pre. `Handled` / `Stop` skip the original virtual. */
87
+ readonly Think: "Think";
88
+ /** `Think` post. Return is ignored. */
89
+ readonly ThinkPost: "ThinkPost";
90
+ /** Player `PreThink`. Return is ignored. */
91
+ readonly PreThink: "PreThink";
92
+ /** `PreThink` post. Return is ignored. */
93
+ readonly PreThinkPost: "PreThinkPost";
94
+ /** Player `PostThink`. Return is ignored. */
95
+ readonly PostThink: "PostThink";
96
+ /** `PostThink` post. Return is ignored. */
97
+ readonly PostThinkPost: "PostThinkPost";
98
+ /** `CBaseEntity::Use` pre. `Handled` / `Stop` skip the original virtual. */
99
+ readonly Use: "Use";
100
+ /** `Use` post. Return is ignored. */
101
+ readonly UsePost: "UsePost";
102
+ /** `GetMaxHealth`. Mutate `info.maxHealth`. `Handled` / `Stop` SUPERCEDE the original. */
103
+ readonly GetMaxHealth: "GetMaxHealth";
104
+ /** `ShouldCollide`. Return a boolean (not {@link HookResultValue}); last defined wins. */
105
+ readonly ShouldCollide: "ShouldCollide";
106
+ /** `VPhysicsUpdate`. Return is ignored. */
107
+ readonly VPhysicsUpdate: "VPhysicsUpdate";
108
+ /** `VPhysicsUpdate` post. Return is ignored. */
109
+ readonly VPhysicsUpdatePost: "VPhysicsUpdatePost";
110
+ /** `GroundEntChanged` post. Return is ignored. */
111
+ readonly GroundEntChangedPost: "GroundEntChangedPost";
112
+ /** `CanBeAutobalanced`. Return a boolean (not {@link HookResultValue}); last defined wins. */
113
+ readonly CanBeAutobalanced: "CanBeAutobalanced";
114
+ /**
115
+ * Post-apply damage (`DispatchTraceAttack` after the original). {@link DamageInfo} is read-only
116
+ * for effect; mutations are not written back. Return is ignored.
117
+ */
118
+ readonly OnTakeDamagePost: "OnTakeDamagePost";
119
+ /**
120
+ * `OnTakeDamageAlive` pre. CS2 has no distinct function yet — {@link SDKHook} returns `false`.
121
+ */
122
+ readonly OnTakeDamageAlive: "OnTakeDamageAlive";
123
+ /**
124
+ * `OnTakeDamageAlive` post. CS2 has no distinct function yet — {@link SDKHook} returns `false`.
125
+ */
126
+ readonly OnTakeDamageAlivePost: "OnTakeDamageAlivePost";
127
+ /**
128
+ * `TraceAttack` pre ({@link TraceAttackInfo}). No distinct CS2 virtual yet — {@link SDKHook}
129
+ * returns `false` (OnTakeDamage remains the DTA mux).
130
+ */
131
+ readonly TraceAttack: "TraceAttack";
132
+ /**
133
+ * `TraceAttack` post. No distinct CS2 virtual yet — {@link SDKHook} returns `false`.
134
+ */
135
+ readonly TraceAttackPost: "TraceAttackPost";
136
+ /**
137
+ * `FireBulletsPost`. Wiki already documents this as often absent. {@link SDKHook} returns `false`
138
+ * until a live path is found.
139
+ */
140
+ readonly FireBulletsPost: "FireBulletsPost";
141
+ /** Weapon reload pre. `Handled` / `Stop` skip the original virtual. Missing VP → `false`. */
142
+ readonly Reload: "Reload";
143
+ /** Weapon reload post. `(weapon, successful)`. No bool-return thunk yet — {@link SDKHook} returns `false`. */
144
+ readonly ReloadPost: "ReloadPost";
145
+ /** `WeaponCanUse` pre. Callback is `(entity, weapon)`. Missing ItemServices VP → `false`. */
146
+ readonly WeaponCanUse: "WeaponCanUse";
147
+ /** `WeaponCanUse` post. Return is ignored. Missing VP → `false`. */
148
+ readonly WeaponCanUsePost: "WeaponCanUsePost";
149
+ /** `WeaponCanSwitchTo` pre. Callback is `(entity, weapon)`. Missing VP → `false`. */
150
+ readonly WeaponCanSwitchTo: "WeaponCanSwitchTo";
151
+ /** `WeaponCanSwitchTo` post. Return is ignored. Missing VP → `false`. */
152
+ readonly WeaponCanSwitchToPost: "WeaponCanSwitchToPost";
153
+ /** `WeaponDrop` pre. Callback is `(entity, weapon)`. Missing VP → `false`. */
154
+ readonly WeaponDrop: "WeaponDrop";
155
+ /** `WeaponDrop` post. Return is ignored. Missing VP → `false`. */
156
+ readonly WeaponDropPost: "WeaponDropPost";
157
+ /** `WeaponEquip` pre. Callback is `(entity, weapon)`. Missing VP → `false`. */
158
+ readonly WeaponEquip: "WeaponEquip";
159
+ /** `WeaponEquip` post. Return is ignored. Missing VP → `false`. */
160
+ readonly WeaponEquipPost: "WeaponEquipPost";
161
+ /** `WeaponSwitch` pre. Callback is `(entity, weapon)`. Missing VP → `false`. */
162
+ readonly WeaponSwitch: "WeaponSwitch";
163
+ /** `WeaponSwitch` post. Return is ignored. Missing VP → `false`. */
164
+ readonly WeaponSwitchPost: "WeaponSwitchPost";
165
+ };
166
+
167
+ /**
168
+ * Block-scoped view of a `TraceAttack` callback. Mutate {@link TraceAttackInfo.damage} on the
169
+ * pre-hook. Valid only during the handler.
170
+ */
171
+ export interface TraceAttackInfo {
172
+ /** Incoming damage. Mutate in place on the pre-hook. */
173
+ damage: number;
174
+ /** Damage type bits. */
175
+ readonly damageType: number;
176
+ /** Ammo type index. */
177
+ readonly ammoType: number;
178
+ /** Hitbox index. */
179
+ readonly hitbox: number;
180
+ /** Hitgroup index. */
181
+ readonly hitgroup: number;
182
+ /** Attacking entity, books-gated. */
183
+ readonly attacker: EntityRef | null;
184
+ /** Inflicting entity, books-gated. */
185
+ readonly inflictor: EntityRef | null;
186
+ /** Victim entity, books-gated. */
187
+ readonly victim: EntityRef | null;
188
+ }
189
+
190
+ /**
191
+ * Hook `entity` for `type`. Auto-unhooked on entity destroy and plugin unload.
192
+ *
193
+ * Not load-window-only — call from `OnEntityCreated`, `OnPluginStart` (ents already live), or any
194
+ * time you hold a live {@link EntityRef}.
195
+ *
196
+ * `OnTakeDamage`: mutate `info.damage` in place. No return needed. Return `HookResult.Handled` to
197
+ * zero the hit; `HookResult.Stop` to also skip later hooks.
198
+ *
199
+ * @returns `true` if the hook was recorded. `false` on `null`/stale `entity` (does not throw).
200
+ * @example
201
+ * import { SDKHook, SDKHookType, Entity } from "@s2script/sdk";
202
+ * import type { EntityRef, DamageInfo } from "@s2script/sdk";
203
+ *
204
+ * export function OnPluginStart(): void {
205
+ * for (const pawn of Entity.findByClass("player")) {
206
+ * SDKHook(pawn, SDKHookType.OnTakeDamage, onTakeDamage);
207
+ * }
208
+ * }
209
+ * export function OnEntityCreated(entity: EntityRef | null, className: string): void {
210
+ * if (!entity || className !== "player") return;
211
+ * SDKHook(entity, SDKHookType.OnTakeDamage, onTakeDamage);
212
+ * }
213
+ * function onTakeDamage(info: DamageInfo) {
214
+ * info.damage /= 2;
215
+ * }
216
+ */
217
+ export declare function SDKHook(
218
+ entity: EntityRef | null,
219
+ type: "OnTakeDamage",
220
+ callback: (info: DamageInfo) => HookResultValue | void,
221
+ ): boolean;
222
+ /**
223
+ * Post-apply damage. {@link DamageInfo} is a read of the live info after the original ran.
224
+ * Return is ignored; Handled does not zero the hit (that is the pre-hook).
225
+ */
226
+ export declare function SDKHook(
227
+ entity: EntityRef | null,
228
+ type: "OnTakeDamagePost",
229
+ callback: (info: DamageInfo) => void,
230
+ ): boolean;
231
+ /**
232
+ * `OnTakeDamageAlive` pre. No CS2 backing yet — always returns `false`.
233
+ */
234
+ export declare function SDKHook(
235
+ entity: EntityRef | null,
236
+ type: "OnTakeDamageAlive",
237
+ callback: (info: DamageInfo) => HookResultValue | void,
238
+ ): boolean;
239
+ /**
240
+ * `OnTakeDamageAlive` post. No CS2 backing yet — always returns `false`.
241
+ */
242
+ export declare function SDKHook(
243
+ entity: EntityRef | null,
244
+ type: "OnTakeDamageAlivePost",
245
+ callback: (info: DamageInfo) => void,
246
+ ): boolean;
247
+ /**
248
+ * `TraceAttack` pre. No distinct CS2 virtual yet — always returns `false`.
249
+ */
250
+ export declare function SDKHook(
251
+ entity: EntityRef | null,
252
+ type: "TraceAttack",
253
+ callback: (info: TraceAttackInfo) => HookResultValue | void,
254
+ ): boolean;
255
+ /**
256
+ * `TraceAttack` post. No distinct CS2 virtual yet — always returns `false`.
257
+ */
258
+ export declare function SDKHook(
259
+ entity: EntityRef | null,
260
+ type: "TraceAttackPost",
261
+ callback: (info: TraceAttackInfo) => void,
262
+ ): boolean;
263
+ /**
264
+ * `FireBulletsPost`. No CS2 backing yet — always returns `false`.
265
+ */
266
+ export declare function SDKHook(
267
+ entity: EntityRef | null,
268
+ type: "FireBulletsPost",
269
+ callback: FireBulletsPostCallback,
270
+ ): boolean;
271
+ /**
272
+ * Touch-family pre-hook. Callback is `(entity, other)`. Omit return = Continue.
273
+ * `Handled` / `Stop` SUPERCEDE the original virtual (`Stop` also skips later callbacks).
274
+ */
275
+ export declare function SDKHook(
276
+ entity: EntityRef | null,
277
+ type: "StartTouch" | "Touch" | "EndTouch" | "Blocked",
278
+ callback: TouchCallback,
279
+ ): boolean;
280
+ /**
281
+ * Touch-family post-hook. Return is ignored; the original virtual already ran.
282
+ */
283
+ export declare function SDKHook(
284
+ entity: EntityRef | null,
285
+ type: "StartTouchPost" | "TouchPost" | "EndTouchPost" | "BlockedPost",
286
+ callback: TouchPostCallback,
287
+ ): boolean;
288
+ /**
289
+ * Spawn / Think pre-hook. Callback is `(entity)`. Omit return = Continue.
290
+ * `Handled` / `Stop` SUPERCEDE the original virtual (`Stop` also skips later callbacks).
291
+ */
292
+ export declare function SDKHook(
293
+ entity: EntityRef | null,
294
+ type: "Spawn" | "Think" | "Reload",
295
+ callback: ThisCallback,
296
+ ): boolean;
297
+ /**
298
+ * Void this-only hooks (Posts, PreThink/PostThink, VPhysicsUpdate, GroundEntChangedPost).
299
+ * Return is ignored.
300
+ */
301
+ export declare function SDKHook(
302
+ entity: EntityRef | null,
303
+ type:
304
+ | "SpawnPost"
305
+ | "ThinkPost"
306
+ | "PreThink"
307
+ | "PreThinkPost"
308
+ | "PostThink"
309
+ | "PostThinkPost"
310
+ | "VPhysicsUpdate"
311
+ | "VPhysicsUpdatePost"
312
+ | "GroundEntChangedPost",
313
+ callback: ThisVoidCallback,
314
+ ): boolean;
315
+ /**
316
+ * `Use` pre-hook. `Handled` / `Stop` SUPERCEDE the original virtual.
317
+ */
318
+ export declare function SDKHook(
319
+ entity: EntityRef | null,
320
+ type: "Use",
321
+ callback: UseCallback,
322
+ ): boolean;
323
+ /**
324
+ * `Use` post-hook. Return is ignored.
325
+ */
326
+ export declare function SDKHook(
327
+ entity: EntityRef | null,
328
+ type: "UsePost",
329
+ callback: UsePostCallback,
330
+ ): boolean;
331
+ /**
332
+ * `GetMaxHealth`. Mutate `info.maxHealth` in place. `Handled` / `Stop` SUPERCEDE with the new value.
333
+ */
334
+ export declare function SDKHook(
335
+ entity: EntityRef | null,
336
+ type: "GetMaxHealth",
337
+ callback: (info: { maxHealth: number }) => HookResultValue | void,
338
+ ): boolean;
339
+ /**
340
+ * `ShouldCollide`. Return a boolean (not HookResult); last defined return wins, default original.
341
+ */
342
+ export declare function SDKHook(
343
+ entity: EntityRef | null,
344
+ type: "ShouldCollide",
345
+ callback: (
346
+ entity: EntityRef,
347
+ collisionGroup: number,
348
+ contentsMask: number,
349
+ originalResult: boolean,
350
+ ) => boolean,
351
+ ): boolean;
352
+ /**
353
+ * `CanBeAutobalanced`. Return a boolean (not HookResult); last defined return wins, default original.
354
+ * Callback is skipped when the hooked entity has no {@link Client} — never a raw slot `0`.
355
+ */
356
+ export declare function SDKHook(
357
+ entity: EntityRef | null,
358
+ type: "CanBeAutobalanced",
359
+ callback: (client: Client, origRet: boolean) => boolean,
360
+ ): boolean;
361
+ /**
362
+ * SetTransmit. Callback is `(entity, client)`. Omit return = Continue.
363
+ * `Handled` / `Stop` hide this entity from this viewer (AND-merge with `Transmit.setVisibleTo`;
364
+ * cannot un-hide a native mask clear). `Stop` also skips later SetTransmit callbacks on that pair.
365
+ */
366
+ export declare function SDKHook(
367
+ entity: EntityRef | null,
368
+ type: "SetTransmit",
369
+ callback: (entity: EntityRef, client: Client) => HookResultValue | void,
370
+ ): boolean;
371
+ /**
372
+ * Weapon-family pre-hook. Callback is `(entity, weapon)`. Omit return = Continue.
373
+ * `Handled` / `Stop` SUPERCEDE the original virtual. Missing ItemServices VP → `false`.
374
+ */
375
+ export declare function SDKHook(
376
+ entity: EntityRef | null,
377
+ type: "WeaponCanUse" | "WeaponCanSwitchTo" | "WeaponDrop" | "WeaponEquip" | "WeaponSwitch",
378
+ callback: WeaponCallback,
379
+ ): boolean;
380
+ /**
381
+ * Weapon-family post-hook. Return is ignored. Missing VP → `false`.
382
+ */
383
+ export declare function SDKHook(
384
+ entity: EntityRef | null,
385
+ type:
386
+ | "WeaponCanUsePost"
387
+ | "WeaponCanSwitchToPost"
388
+ | "WeaponDropPost"
389
+ | "WeaponEquipPost"
390
+ | "WeaponSwitchPost",
391
+ callback: WeaponPostCallback,
392
+ ): boolean;
393
+ /**
394
+ * `ReloadPost`. Callback is `(weapon, successful)`. No CS2 backing yet — always returns `false`.
395
+ */
396
+ export declare function SDKHook(
397
+ entity: EntityRef | null,
398
+ type: "ReloadPost",
399
+ callback: ReloadPostCallback,
400
+ ): boolean;
401
+
402
+ /**
403
+ * Remove one matching `(entity, type, callback)` hook. Callback identity is the function reference.
404
+ * @returns `true` if an entry was removed.
405
+ */
406
+ export declare function SDKUnhook(
407
+ entity: EntityRef | null,
408
+ type: "OnTakeDamage",
409
+ callback: (info: DamageInfo) => HookResultValue | void,
410
+ ): boolean;
411
+ /** Remove an `OnTakeDamagePost` hook. */
412
+ export declare function SDKUnhook(
413
+ entity: EntityRef | null,
414
+ type: "OnTakeDamagePost",
415
+ callback: (info: DamageInfo) => void,
416
+ ): boolean;
417
+ /** Remove an `OnTakeDamageAlive` hook. */
418
+ export declare function SDKUnhook(
419
+ entity: EntityRef | null,
420
+ type: "OnTakeDamageAlive",
421
+ callback: (info: DamageInfo) => HookResultValue | void,
422
+ ): boolean;
423
+ /** Remove an `OnTakeDamageAlivePost` hook. */
424
+ export declare function SDKUnhook(
425
+ entity: EntityRef | null,
426
+ type: "OnTakeDamageAlivePost",
427
+ callback: (info: DamageInfo) => void,
428
+ ): boolean;
429
+ /** Remove a `TraceAttack` hook. */
430
+ export declare function SDKUnhook(
431
+ entity: EntityRef | null,
432
+ type: "TraceAttack",
433
+ callback: (info: TraceAttackInfo) => HookResultValue | void,
434
+ ): boolean;
435
+ /** Remove a `TraceAttackPost` hook. */
436
+ export declare function SDKUnhook(
437
+ entity: EntityRef | null,
438
+ type: "TraceAttackPost",
439
+ callback: (info: TraceAttackInfo) => void,
440
+ ): boolean;
441
+ /** Remove a `FireBulletsPost` hook. */
442
+ export declare function SDKUnhook(
443
+ entity: EntityRef | null,
444
+ type: "FireBulletsPost",
445
+ callback: FireBulletsPostCallback,
446
+ ): boolean;
447
+ /** Remove a Touch-family pre-hook. */
448
+ export declare function SDKUnhook(
449
+ entity: EntityRef | null,
450
+ type: "StartTouch" | "Touch" | "EndTouch" | "Blocked",
451
+ callback: TouchCallback,
452
+ ): boolean;
453
+ /** Remove a Touch-family post-hook. */
454
+ export declare function SDKUnhook(
455
+ entity: EntityRef | null,
456
+ type: "StartTouchPost" | "TouchPost" | "EndTouchPost" | "BlockedPost",
457
+ callback: TouchPostCallback,
458
+ ): boolean;
459
+ /** Remove a Spawn / Think pre-hook. */
460
+ export declare function SDKUnhook(
461
+ entity: EntityRef | null,
462
+ type: "Spawn" | "Think" | "Reload",
463
+ callback: ThisCallback,
464
+ ): boolean;
465
+ /** Remove a void this-only hook. */
466
+ export declare function SDKUnhook(
467
+ entity: EntityRef | null,
468
+ type:
469
+ | "SpawnPost"
470
+ | "ThinkPost"
471
+ | "PreThink"
472
+ | "PreThinkPost"
473
+ | "PostThink"
474
+ | "PostThinkPost"
475
+ | "VPhysicsUpdate"
476
+ | "VPhysicsUpdatePost"
477
+ | "GroundEntChangedPost",
478
+ callback: ThisVoidCallback,
479
+ ): boolean;
480
+ /** Remove a `Use` pre-hook. */
481
+ export declare function SDKUnhook(
482
+ entity: EntityRef | null,
483
+ type: "Use",
484
+ callback: UseCallback,
485
+ ): boolean;
486
+ /** Remove a `Use` post-hook. */
487
+ export declare function SDKUnhook(
488
+ entity: EntityRef | null,
489
+ type: "UsePost",
490
+ callback: UsePostCallback,
491
+ ): boolean;
492
+ /** Remove a `GetMaxHealth` hook. */
493
+ export declare function SDKUnhook(
494
+ entity: EntityRef | null,
495
+ type: "GetMaxHealth",
496
+ callback: (info: { maxHealth: number }) => HookResultValue | void,
497
+ ): boolean;
498
+ /** Remove a `ShouldCollide` hook. */
499
+ export declare function SDKUnhook(
500
+ entity: EntityRef | null,
501
+ type: "ShouldCollide",
502
+ callback: (
503
+ entity: EntityRef,
504
+ collisionGroup: number,
505
+ contentsMask: number,
506
+ originalResult: boolean,
507
+ ) => boolean,
508
+ ): boolean;
509
+ /** Remove a `CanBeAutobalanced` hook. */
510
+ export declare function SDKUnhook(
511
+ entity: EntityRef | null,
512
+ type: "CanBeAutobalanced",
513
+ callback: (client: Client, origRet: boolean) => boolean,
514
+ ): boolean;
515
+ /** Remove a SetTransmit hook. */
516
+ export declare function SDKUnhook(
517
+ entity: EntityRef | null,
518
+ type: "SetTransmit",
519
+ callback: (entity: EntityRef, client: Client) => HookResultValue | void,
520
+ ): boolean;
521
+ /** Remove a Weapon-family pre-hook. */
522
+ export declare function SDKUnhook(
523
+ entity: EntityRef | null,
524
+ type: "WeaponCanUse" | "WeaponCanSwitchTo" | "WeaponDrop" | "WeaponEquip" | "WeaponSwitch",
525
+ callback: WeaponCallback,
526
+ ): boolean;
527
+ /** Remove a Weapon-family post-hook. */
528
+ export declare function SDKUnhook(
529
+ entity: EntityRef | null,
530
+ type:
531
+ | "WeaponCanUsePost"
532
+ | "WeaponCanSwitchToPost"
533
+ | "WeaponDropPost"
534
+ | "WeaponEquipPost"
535
+ | "WeaponSwitchPost",
536
+ callback: WeaponPostCallback,
537
+ ): boolean;
538
+ /** Remove a `ReloadPost` hook. */
539
+ export declare function SDKUnhook(
540
+ entity: EntityRef | null,
541
+ type: "ReloadPost",
542
+ callback: ReloadPostCallback,
543
+ ): boolean;
package/server.d.ts CHANGED
@@ -15,16 +15,16 @@ export declare const Server: {
15
15
  /** A cvar's current value as a string. `""` if the cvar doesn't exist (or an unsupported type → `"<type>"`). */
16
16
  getCvar(name: string): string;
17
17
  /**
18
- * Set a cvar via the server console (so any type is accepted). SECURITY: this builds and runs a console
19
- * command (`<name> <value>`), which the console splits on `;` — treat `value` like `command()` input and
20
- * sanitize/quote any untrusted value to avoid command injection. Queued: applies next frame.
18
+ * Set a cvar through `ICvar` now (SourceMod `SetConVarString`). Not a console command: `;` in
19
+ * `value` is data, not a second command. `getCvar` and `onCvarChange` see the new value before
20
+ * this returns. `false` if the cvar does not exist or the string cannot become that type.
21
21
  */
22
- setCvar(name: string, value: string): void;
22
+ setCvar(name: string, value: string): boolean;
23
23
  /**
24
24
  * Register a plugin-owned ConVar (CSSharp FakeConVar / SM CreateConVar parity). Idempotent —
25
25
  * re-registering an existing name is a no-op success, and the cvar + its value persist across
26
26
  * plugin reloads (SourceMod parity). The shim adds FCVAR_RELEASE (customer-visible); `flags`
27
- * are additive raw FCVAR bits. Read the value with `getCvar`; set it with `setCvar`/the console.
27
+ * are additive raw FCVAR bits. Read the value with `getCvar`; set it with `setCvar`.
28
28
  * `min`/`max` apply to numeric types only.
29
29
  */
30
30
  registerCvar(name: string, opts: {
package/translations.d.ts CHANGED
@@ -13,13 +13,15 @@ export type Phrases = Record<string, string>;
13
13
  * for it automatically, the same rule SourceMod's `LoadTranslations` enforces:
14
14
  *
15
15
  * @example
16
- * export default plugin((ctx) => {
17
- * ctx.translations.load("basecomm", "common");
18
- * ctx.commands.registerAdmin("sm_gag", ADMFLAG.CHAT, (cmd) => {
16
+ * import { command, translations } from "@s2script/sdk";
17
+ * import { ADMFLAG } from "@s2script/sdk";
18
+ * export function OnPluginStart(): void {
19
+ * translations.load("basecomm", "common");
20
+ * command.admin("sm_gag", ADMFLAG.CHAT, (cmd) => {
19
21
  * cmd.replyT("Usage Gag"); // key-checked
20
22
  * Chat.toSlot(p.slot, Translations.translate(p.slot, "Gagged Player", n));
21
23
  * });
22
- * });
24
+ * }
23
25
  *
24
26
  * Keys are checked against the files that plugin loads — see `@s2script/sdk/phrases`.
25
27
  */
package/unsafe.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  /**
2
- * @s2script/sdk/unsafe — plugin-declared engine calls. NO runtime code: the engine injects the
3
- * implementation at load (`__s2pkg_unsafe`).
2
+ * @s2script/sdk/unsafe — plugin-declared engine calls and inbound hooks. NO runtime code: the
3
+ * engine injects the implementation at load (`__s2pkg_unsafe`).
4
4
  *
5
- * UNSAFE by design. A declared call reaches a real engine function. The framework validates every
6
- * descriptor at load and degrades a failure to `null`, but it cannot prove your signature or vtable
7
- * index names the function you meant. Requires manifest `permissions: ["engine:calls"]` AND an
8
- * operator allow-list entry (see docs/superpowers/specs/2026-07-24-plugin-gamedata-design.md §6).
5
+ * UNSAFE by design. A declared call reaches a real engine function; a declared hook patches
6
+ * that function's prologue. The framework validates every descriptor at load and degrades a
7
+ * failure to `null`, but it cannot prove your signature or vtable index names the function you
8
+ * meant. Calls require manifest `permissions: ["engine:calls"]`; hooks require the separate
9
+ * `"engine:hooks"` permission. Both also need an operator allow-list entry
10
+ * (see docs/superpowers/specs/2026-07-24-plugin-gamedata-design.md §6 and ARCHITECTURE.md §2.0.7).
9
11
  */
10
12
 
11
13
  /**
@@ -15,13 +17,21 @@
15
17
  export interface EngineCalls {}
16
18
 
17
19
  /**
18
- * Plugin-declared engine calls, resolved from this plugin's own gamedata.
20
+ * The inbound hooks this plugin declares. The base interface is EMPTY — `s2s build` generates
21
+ * `.s2script/hooks.d.ts`, which augments this from your gamedata's `hooks` section.
22
+ */
23
+ export interface EngineHooks {}
24
+
25
+ /**
26
+ * Plugin-declared engine calls and inbound hooks, resolved from this plugin's own gamedata.
19
27
  *
20
28
  * @example
21
29
  * import { Engine } from "@s2script/sdk/unsafe";
22
30
  * // Resolve ONCE at load — null means the descriptor failed a load-time gate.
23
31
  * const ignite = Engine.call("ignite");
24
32
  * if (!ignite) console.log(`unavailable: ${Engine.status("ignite")}`);
33
+ * const onFoo = Engine.hook("onFoo");
34
+ * if (onFoo) onFoo((view) => { view.reason = 0; });
25
35
  */
26
36
  export declare const Engine: {
27
37
  /**
@@ -30,6 +40,16 @@ export declare const Engine: {
30
40
  * operator-allow-listed). Guard once at load; the returned function is a plain callable.
31
41
  */
32
42
  call<K extends keyof EngineCalls>(name: K): EngineCalls[K] | null;
33
- /** Why a descriptor is unavailable; `"available"` when it resolved. For diagnostics/operator reports. */
43
+ /** Why a call descriptor is unavailable; `"available"` when it resolved. For diagnostics/operator reports. */
34
44
  status(name: string): string;
45
+ /**
46
+ * The declared hook's subscribe function, or `null` when its descriptor failed a load-time
47
+ * gate (same class of reasons as {@link Engine.call}, plus a missing `validate` or an unknown
48
+ * thunk shape). Guard once at load; the returned function records the handler and lazily
49
+ * installs the detour. The owner is always the calling plugin — you cannot subscribe to
50
+ * another plugin's hook through this factory (game-package hooks hang off `ctx.*`).
51
+ */
52
+ hook<K extends keyof EngineHooks>(name: K): EngineHooks[K] | null;
53
+ /** Why a hook descriptor is unavailable; `"available"` when it resolved. */
54
+ hookStatus(name: string): string;
35
55
  };