@s2script/sdk 0.22.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/README.md +12 -4
- package/commands.d.ts +52 -5
- package/config.d.ts +5 -0
- package/damage.d.ts +12 -7
- package/dist/cli.js +88 -47
- package/entity.d.ts +1 -1
- package/index.d.ts +47 -0
- package/package.json +8 -2
- package/phrases.d.ts +5 -4
- package/plugin.d.ts +103 -43
- package/sdkhooks.d.ts +543 -0
- package/translations.d.ts +6 -4
package/README.md
CHANGED
|
@@ -19,13 +19,21 @@ Copy the `.s2sp` into `addons/s2script/plugins/` on a running server and it load
|
|
|
19
19
|
re-drop to hot-reload, delete to unload. No restart.
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
-
import {
|
|
22
|
+
import { command, hook } from "@s2script/sdk";
|
|
23
|
+
import type { Client } from "@s2script/sdk";
|
|
23
24
|
|
|
24
|
-
export
|
|
25
|
-
|
|
25
|
+
export function OnPluginStart(): void {
|
|
26
|
+
command("hello", (cmd) => {
|
|
26
27
|
cmd.reply("hello from s2script");
|
|
27
28
|
});
|
|
28
|
-
|
|
29
|
+
hook.on("player_spawn", (ev) => {
|
|
30
|
+
console.log("spawned", ev.getPlayerSlot("userid"));
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function OnClientPostAdminCheck(client: Client): void {
|
|
35
|
+
client.chat("welcome");
|
|
36
|
+
}
|
|
29
37
|
```
|
|
30
38
|
|
|
31
39
|
This package is **types-only** — the engine injects the implementation at load time, and
|
package/commands.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export interface CommandInvocation {
|
|
|
57
57
|
*
|
|
58
58
|
* @example
|
|
59
59
|
* // `!help` answers in chat; `sm_help` typed at a console answers in that console.
|
|
60
|
-
*
|
|
60
|
+
* command("sm_help", (cmd) => { cmd.reply("[SM] Commands: …"); return HookResult.Handled; });
|
|
61
61
|
*/
|
|
62
62
|
reply(message: string): void;
|
|
63
63
|
/**
|
|
@@ -81,6 +81,53 @@ export interface CommandInvocation {
|
|
|
81
81
|
* never in the way. */
|
|
82
82
|
replyT(key: PhraseKey, ...args: (string | number)[]): void;
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/** Alias for {@link CommandInvocation} — the SourceMod-shaped name for a parsed invocation. */
|
|
86
|
+
export type Command = CommandInvocation;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A command callback: the parsed invocation, plus an optional {@link HookResultValue}.
|
|
90
|
+
* SourceMod `Plugin_Handled` is {@link HookResult}.Handled — return it from a command you own
|
|
91
|
+
* (usage errors included; the command was still consumed). Omit the return (`void`) to continue —
|
|
92
|
+
* the same as returning {@link HookResult}.Continue. Engine SUPERCEDE-on-Continue is not this API;
|
|
93
|
+
* chat `!` vs `/` suppress is unchanged.
|
|
94
|
+
*/
|
|
95
|
+
export type CommandHandler = (
|
|
96
|
+
cmd: CommandInvocation,
|
|
97
|
+
) => HookResultValue | void | Promise<HookResultValue | void>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Register a public command in the load window (SourceMod `RegConsoleCmd`).
|
|
101
|
+
* Callable only while `OnPluginStart` is running — throws after settle.
|
|
102
|
+
*
|
|
103
|
+
* `.admin` / `.server` are the `RegAdminCmd` / `RegServerCmd` shapes: name the handler anything.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* import { command, ADMFLAG, HookResult } from "@s2script/sdk";
|
|
107
|
+
* export function OnPluginStart(): void {
|
|
108
|
+
* command.admin("sm_kick", ADMFLAG.KICK, kick);
|
|
109
|
+
* }
|
|
110
|
+
* function kick(cmd: CommandInvocation): HookResultValue {
|
|
111
|
+
* cmd.reply("kicked");
|
|
112
|
+
* return HookResult.Handled;
|
|
113
|
+
* }
|
|
114
|
+
*/
|
|
115
|
+
export declare const command: {
|
|
116
|
+
(name: string, handler: CommandHandler): void;
|
|
117
|
+
/** Register an admin command gated by `flags` (an `ADMFLAG` bitmask). */
|
|
118
|
+
admin(name: string, flags: number, handler: CommandHandler): void;
|
|
119
|
+
/** Register a server-only command (console/rcon, not client-runnable). */
|
|
120
|
+
server(name: string, handler: CommandHandler): void;
|
|
121
|
+
/**
|
|
122
|
+
* Observe an existing CLIENT command by name — SourceMod's `AddCommandListener`.
|
|
123
|
+
* Load-window only. Same contract as {@link CtxCommands.onClientCommand} (on PluginContext).
|
|
124
|
+
*/
|
|
125
|
+
onClientCommand(
|
|
126
|
+
name: string,
|
|
127
|
+
handler: (slot: number, argString: string) => HookResultValue | void,
|
|
128
|
+
): void;
|
|
129
|
+
};
|
|
130
|
+
|
|
84
131
|
/** A parsed chat trigger: which command + args, and whether it was the silent (`/`) trigger. */
|
|
85
132
|
export interface ChatTrigger {
|
|
86
133
|
/** `true` = the silent trigger (`/`, hidden); `false` = the public trigger (`!`). */
|
|
@@ -93,9 +140,9 @@ export interface ChatTrigger {
|
|
|
93
140
|
|
|
94
141
|
/**
|
|
95
142
|
* Command-registry utilities: dispatch by name, parse/route chat triggers, and enumerate the global
|
|
96
|
-
* registry. Commands themselves are registered
|
|
143
|
+
* registry. Commands themselves are registered in the load window via {@link command}.
|
|
97
144
|
* @example
|
|
98
|
-
* import { Commands } from "@s2script/sdk
|
|
145
|
+
* import { Commands } from "@s2script/sdk";
|
|
99
146
|
* // sm_help backend: every registered command + its required admin flag mask.
|
|
100
147
|
* const cmds = Commands.list().slice().sort((a, b) => (a.name < b.name ? -1 : 1));
|
|
101
148
|
*/
|
|
@@ -122,7 +169,7 @@ export declare const Commands: {
|
|
|
122
169
|
/**
|
|
123
170
|
* Observe a CLIENT command by name — SourceMod's `AddCommandListener`.
|
|
124
171
|
*
|
|
125
|
-
* Unlike {@link
|
|
172
|
+
* Unlike {@link command}, which CREATES a command, this hooks one that already exists — including
|
|
126
173
|
* commands the engine itself owns (`player_ping`, `jointeam`, `drop`, `buy`). Registering a
|
|
127
174
|
* ConCommand of an engine-owned name fails outright ("unable to link multiple ConCommands named
|
|
128
175
|
* X"), so this is the only way to see those.
|
|
@@ -133,7 +180,7 @@ export declare const Commands: {
|
|
|
133
180
|
*
|
|
134
181
|
* @example
|
|
135
182
|
* // Middle-mouse ping opens the shop, and still places the ping.
|
|
136
|
-
*
|
|
183
|
+
* command.onClientCommand("player_ping", (slot) => { openShop(slot); });
|
|
137
184
|
*/
|
|
138
185
|
onClientCommand(name: string, handler: (slot: number, argString: string) => HookResultValue | void): void;
|
|
139
186
|
/** Every globally-registered command with its required admin `flags`: `0` = anyone, `-1` = console/server-only,
|
package/config.d.ts
CHANGED
|
@@ -23,4 +23,9 @@ export declare const config: {
|
|
|
23
23
|
readFile(name: string): string | null;
|
|
24
24
|
/** Write a raw file to the configs dir (creates/overwrites). */
|
|
25
25
|
writeFile(name: string, content: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Fires when this plugin's config file is re-materialized on disk. Ledgered per plugin.
|
|
28
|
+
* Not load-window-gated (safe from `OnPluginStart` or from `ctx.config.onChange`).
|
|
29
|
+
*/
|
|
30
|
+
onChange(handler: (cfg: Config) => void): void;
|
|
26
31
|
};
|
package/damage.d.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @s2script/damage —
|
|
3
|
-
*
|
|
2
|
+
* @s2script/damage — the block-scoped {@link DamageInfo} view of a live `CTakeDamageInfo`.
|
|
3
|
+
* NO runtime code (injected at load). Engine-generic: `CTakeDamageInfo` is a Source 2 type.
|
|
4
|
+
*
|
|
5
|
+
* Subscribe with `SDKHook` (`SDKHookType.OnTakeDamage` / `OnTakeDamagePost`), not a global mux.
|
|
4
6
|
*/
|
|
5
7
|
import type { EntityRef } from "./entity";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
|
-
* A block-scoped view of the current damage event (valid only inside
|
|
10
|
+
* A block-scoped view of the current damage event (valid only inside an `OnTakeDamage` or
|
|
11
|
+
* `OnTakeDamagePost` SDKHook).
|
|
9
12
|
* @example
|
|
10
|
-
* import type { DamageInfo } from "@s2script/sdk
|
|
11
|
-
*
|
|
12
|
-
* ctx.entities.onDamage((info: DamageInfo) => { info.damage = info.damage / 2; });
|
|
13
|
+
* import type { DamageInfo } from "@s2script/sdk";
|
|
14
|
+
* function onTakeDamage(info: DamageInfo) { info.damage = info.damage / 2; }
|
|
13
15
|
*/
|
|
14
16
|
export interface DamageInfo {
|
|
15
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* The damage amount (`m_flDamage`). On the pre-hook, assigning MODIFIES the live damage; set to
|
|
19
|
+
* 0 to block. On `OnTakeDamagePost`, assignment is ignored (the original already ran).
|
|
20
|
+
*/
|
|
16
21
|
damage: number;
|
|
17
22
|
/** The damage-type bit flags (m_bitsDamageType). */
|
|
18
23
|
readonly damageType: number;
|
package/dist/cli.js
CHANGED
|
@@ -3126,8 +3126,8 @@ var sharedCompilerOptionsJson = {
|
|
|
3126
3126
|
noEmit: true,
|
|
3127
3127
|
moduleResolution: "bundler",
|
|
3128
3128
|
module: "ESNext",
|
|
3129
|
-
target: "
|
|
3130
|
-
lib: ["
|
|
3129
|
+
target: "ES2024",
|
|
3130
|
+
lib: ["ES2024"],
|
|
3131
3131
|
types: [],
|
|
3132
3132
|
skipLibCheck: true,
|
|
3133
3133
|
allowImportingTsExtensions: true
|
|
@@ -3139,7 +3139,7 @@ function sharedProgramOptions(tsMod) {
|
|
|
3139
3139
|
allowImportingTsExtensions: sharedCompilerOptionsJson.allowImportingTsExtensions,
|
|
3140
3140
|
moduleResolution: tsMod.ModuleResolutionKind.Bundler,
|
|
3141
3141
|
module: tsMod.ModuleKind.ESNext,
|
|
3142
|
-
target: tsMod.ScriptTarget.
|
|
3142
|
+
target: tsMod.ScriptTarget.ES2024,
|
|
3143
3143
|
lib: sharedCompilerOptionsJson.lib.map((l2) => `lib.${l2.toLowerCase()}.d.ts`),
|
|
3144
3144
|
types: [...sharedCompilerOptionsJson.types],
|
|
3145
3145
|
skipLibCheck: sharedCompilerOptionsJson.skipLibCheck
|
|
@@ -3795,7 +3795,7 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3795
3795
|
const entryRel = s2.main ?? pkg.main;
|
|
3796
3796
|
if (!entryRel) throw new Error(`typecheckPlugin: no entry point in ${join7(absDir, "package.json")}`);
|
|
3797
3797
|
const entry = resolve6(absDir, entryRel);
|
|
3798
|
-
const isAlwaysResolved = (d) => d.startsWith("@s2script/sdk/") || d === "@s2script/cs2" || d.startsWith("@s2script/cs2/");
|
|
3798
|
+
const isAlwaysResolved = (d) => d === "@s2script/sdk" || d.startsWith("@s2script/sdk/") || d === "@s2script/cs2" || d.startsWith("@s2script/cs2/");
|
|
3799
3799
|
const localDts = localDeclarationFiles(absDir);
|
|
3800
3800
|
const locallyDeclared = declaredModules(localDts);
|
|
3801
3801
|
const allDeclaredDeps = [
|
|
@@ -3829,10 +3829,12 @@ function typecheckPlugin(pluginDir, opts) {
|
|
|
3829
3829
|
...sharedProgramOptions(ts),
|
|
3830
3830
|
baseUrl: packagesDir,
|
|
3831
3831
|
paths: {
|
|
3832
|
-
// Builtins
|
|
3833
|
-
//
|
|
3834
|
-
//
|
|
3835
|
-
// sdk
|
|
3832
|
+
// Builtins: the root barrel `@s2script/sdk` → packages/sdk/index.d.ts; subpaths
|
|
3833
|
+
// `@s2script/sdk/<cap>` → packages/sdk/<cap>.d.ts. The `@s2script/*` fallback serves
|
|
3834
|
+
// @s2script/cs2 → packages/cs2/index.d.ts (and would also hit sdk/index.d.ts; the exact
|
|
3835
|
+
// `@s2script/sdk` entry makes the barrel obvious). tsc picks the longest matching prefix,
|
|
3836
|
+
// so `@s2script/sdk/*` wins for capability imports.
|
|
3837
|
+
"@s2script/sdk": ["sdk/index.d.ts"],
|
|
3836
3838
|
"@s2script/sdk/*": ["sdk/*.d.ts"],
|
|
3837
3839
|
// Game-package SUBPATHS (e.g. @s2script/cs2/econ -> packages/cs2/econ.d.ts). Needed because
|
|
3838
3840
|
// the `@s2script/*` fallback below maps to `<pkg>/index.d.ts`, which cannot express a
|
|
@@ -4171,31 +4173,60 @@ function isPluginContext(type) {
|
|
|
4171
4173
|
const sym = type.getSymbol() ?? type.aliasSymbol;
|
|
4172
4174
|
return sym?.getName() === "PluginContext";
|
|
4173
4175
|
}
|
|
4176
|
+
function isFromPluginDts(checker, node) {
|
|
4177
|
+
let sym = checker.getSymbolAtLocation(node);
|
|
4178
|
+
if (sym === void 0) return false;
|
|
4179
|
+
if (sym.flags & ts2.SymbolFlags.Alias) {
|
|
4180
|
+
const aliased = checker.getAliasedSymbol(sym);
|
|
4181
|
+
if (aliased !== void 0) sym = aliased;
|
|
4182
|
+
}
|
|
4183
|
+
for (const d of sym.declarations ?? []) {
|
|
4184
|
+
const f = d.getSourceFile().fileName.replace(/\\/g, "/");
|
|
4185
|
+
if (f.endsWith("/plugin.d.ts")) return true;
|
|
4186
|
+
}
|
|
4187
|
+
return false;
|
|
4188
|
+
}
|
|
4189
|
+
function recordPublishOrUse(out, method, arg0, sf, node) {
|
|
4190
|
+
if (method === "publish") {
|
|
4191
|
+
if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
|
|
4192
|
+
out.publishNames.push(arg0.text);
|
|
4193
|
+
} else {
|
|
4194
|
+
const { line } = sf.getLineAndCharacterOfPosition(node.getStart());
|
|
4195
|
+
out.dynamicPublishSites.push(`${sf.fileName}:${line + 1}`);
|
|
4196
|
+
}
|
|
4197
|
+
} else if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
|
|
4198
|
+
out.useNames.push(arg0.text);
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
function recordImportSpecifier(out, spec) {
|
|
4202
|
+
if (spec.startsWith(".") || spec.startsWith("@s2script/")) return;
|
|
4203
|
+
out.importNames.push(spec);
|
|
4204
|
+
}
|
|
4174
4205
|
function scanPluginProgram(program, pluginDir) {
|
|
4175
4206
|
const checker = program.getTypeChecker();
|
|
4176
|
-
const out = { publishNames: [], dynamicPublishSites: [], useNames: [] };
|
|
4207
|
+
const out = { publishNames: [], dynamicPublishSites: [], useNames: [], importNames: [] };
|
|
4177
4208
|
const dirPrefix = pluginDir.replace(/\\/g, "/").replace(/\/+$/, "") + "/";
|
|
4178
4209
|
for (const sf of program.getSourceFiles()) {
|
|
4179
4210
|
if (sf.isDeclarationFile) continue;
|
|
4180
4211
|
if (!sf.fileName.replace(/\\/g, "/").startsWith(dirPrefix)) continue;
|
|
4181
4212
|
const visit = (node) => {
|
|
4182
|
-
if (ts2.
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
const { line } = sf.getLineAndCharacterOfPosition(node.getStart());
|
|
4193
|
-
out.dynamicPublishSites.push(`${sf.fileName}:${line + 1}`);
|
|
4194
|
-
}
|
|
4195
|
-
} else if (arg0 !== void 0 && ts2.isStringLiteralLike(arg0)) {
|
|
4196
|
-
out.useNames.push(arg0.text);
|
|
4213
|
+
if ((ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts2.isStringLiteralLike(node.moduleSpecifier)) {
|
|
4214
|
+
recordImportSpecifier(out, node.moduleSpecifier.text);
|
|
4215
|
+
}
|
|
4216
|
+
if (ts2.isCallExpression(node)) {
|
|
4217
|
+
if (ts2.isPropertyAccessExpression(node.expression)) {
|
|
4218
|
+
const method = node.expression.name.text;
|
|
4219
|
+
if (method === "publish" || method === "use" || method === "tryUse") {
|
|
4220
|
+
const recv = checker.getTypeAtLocation(node.expression.expression);
|
|
4221
|
+
if (isPluginContext(recv)) {
|
|
4222
|
+
recordPublishOrUse(out, method, node.arguments[0], sf, node);
|
|
4197
4223
|
}
|
|
4198
4224
|
}
|
|
4225
|
+
} else if (ts2.isIdentifier(node.expression)) {
|
|
4226
|
+
const name = node.expression.text;
|
|
4227
|
+
if ((name === "publish" || name === "use" || name === "tryUse") && isFromPluginDts(checker, node.expression)) {
|
|
4228
|
+
recordPublishOrUse(out, name, node.arguments[0], sf, node);
|
|
4229
|
+
}
|
|
4199
4230
|
}
|
|
4200
4231
|
}
|
|
4201
4232
|
ts2.forEachChild(node, visit);
|
|
@@ -4204,6 +4235,7 @@ function scanPluginProgram(program, pluginDir) {
|
|
|
4204
4235
|
}
|
|
4205
4236
|
out.publishNames = [...new Set(out.publishNames)];
|
|
4206
4237
|
out.useNames = [...new Set(out.useNames)];
|
|
4238
|
+
out.importNames = [...new Set(out.importNames)];
|
|
4207
4239
|
return out;
|
|
4208
4240
|
}
|
|
4209
4241
|
|
|
@@ -4569,12 +4601,14 @@ function emitHookDts(model) {
|
|
|
4569
4601
|
out.push(...emitViewIface(h2));
|
|
4570
4602
|
}
|
|
4571
4603
|
}
|
|
4572
|
-
for (const { ctxIface, hooks } of model) {
|
|
4604
|
+
for (const { ctxIface, hooks, ns } of model) {
|
|
4573
4605
|
out.push(`export interface ${ctxIface} {`);
|
|
4574
4606
|
for (const h2 of hooks) {
|
|
4575
4607
|
out.push(` ${h2.name}(handler: (view: ${h2.viewIface}) => HookResultValue | void): void;`);
|
|
4576
4608
|
}
|
|
4577
4609
|
out.push("}", "");
|
|
4610
|
+
out.push(`export declare const ${ns}: ${ctxIface};`);
|
|
4611
|
+
out.push("");
|
|
4578
4612
|
}
|
|
4579
4613
|
out.push('declare module "@s2script/sdk/plugin" {');
|
|
4580
4614
|
out.push(" interface PluginContext {");
|
|
@@ -4837,13 +4871,16 @@ ${lint.output}`);
|
|
|
4837
4871
|
for (const used of scan.useNames) {
|
|
4838
4872
|
if (!declaredDeps.has(used)) {
|
|
4839
4873
|
console.warn(
|
|
4840
|
-
`WARN:
|
|
4874
|
+
`WARN: use()/tryUse(${JSON.stringify(used)}) is not declared under s2script.pluginDependencies/optionalPluginDependencies \u2014 it will throw at runtime`
|
|
4841
4875
|
);
|
|
4842
4876
|
}
|
|
4843
4877
|
}
|
|
4878
|
+
const referenced = /* @__PURE__ */ new Set([...scan.useNames, ...scan.importNames]);
|
|
4844
4879
|
for (const dep of declaredDeps) {
|
|
4845
|
-
if (!
|
|
4846
|
-
console.warn(
|
|
4880
|
+
if (!referenced.has(dep)) {
|
|
4881
|
+
console.warn(
|
|
4882
|
+
`WARN: dependency ${JSON.stringify(dep)} is declared but never ctx.use()d or imported`
|
|
4883
|
+
);
|
|
4847
4884
|
}
|
|
4848
4885
|
}
|
|
4849
4886
|
const { name, version } = pkg;
|
|
@@ -4871,7 +4908,7 @@ ${lint.output}`);
|
|
|
4871
4908
|
platform: "neutral",
|
|
4872
4909
|
format: "cjs",
|
|
4873
4910
|
external,
|
|
4874
|
-
target: "
|
|
4911
|
+
target: "es2024",
|
|
4875
4912
|
// platform:"neutral" defaults mainFields to EMPTY, so a workspace-sibling package that
|
|
4876
4913
|
// declares `main` (rather than `exports`) fails to resolve with an undiagnosable error.
|
|
4877
4914
|
// Set it explicitly so a monorepo plugin bundles whichever field the author wrote.
|
|
@@ -8071,29 +8108,33 @@ function fileDevDeps(packagesDir, game) {
|
|
|
8071
8108
|
}
|
|
8072
8109
|
function pluginSource(game) {
|
|
8073
8110
|
if (game === "cs2") {
|
|
8074
|
-
return `import {
|
|
8075
|
-
import {
|
|
8111
|
+
return `import { command, Chat, HookResult } from "@s2script/sdk";
|
|
8112
|
+
import type { Command, HookResultValue } from "@s2script/sdk";
|
|
8076
8113
|
|
|
8077
|
-
export
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8114
|
+
export function OnPluginStart(): void {
|
|
8115
|
+
command("hello", hello);
|
|
8116
|
+
}
|
|
8117
|
+
|
|
8118
|
+
function hello(cmd: Command): HookResultValue {
|
|
8119
|
+
cmd.reply("hello from s2script");
|
|
8120
|
+
if (cmd.callerSlot >= 0) {
|
|
8121
|
+
Chat.toSlot(cmd.callerSlot, "hello from s2script");
|
|
8122
|
+
}
|
|
8123
|
+
return HookResult.Handled;
|
|
8124
|
+
}
|
|
8085
8125
|
`;
|
|
8086
8126
|
}
|
|
8087
|
-
return `import {
|
|
8088
|
-
import { delay } from "@s2script/sdk/timers";
|
|
8127
|
+
return `import { delay } from "@s2script/sdk";
|
|
8089
8128
|
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
n += 1;
|
|
8094
|
-
});
|
|
8129
|
+
let n = 0;
|
|
8130
|
+
|
|
8131
|
+
export function OnPluginStart(): void {
|
|
8095
8132
|
void delay(1000).then(() => console.log("s2script plugin alive; frames so far:", n));
|
|
8096
|
-
}
|
|
8133
|
+
}
|
|
8134
|
+
|
|
8135
|
+
export function OnGameFrame(): void {
|
|
8136
|
+
n += 1;
|
|
8137
|
+
}
|
|
8097
8138
|
`;
|
|
8098
8139
|
}
|
|
8099
8140
|
var LIBRARY_ENTRY = `/** Encode a string. Replace this with the library's real surface. */
|
package/entity.d.ts
CHANGED
|
@@ -220,7 +220,7 @@ export type EntityKeyValueMap = { [key: string]: string | number | boolean };
|
|
|
220
220
|
*/
|
|
221
221
|
export declare function createEntity(className: string, keyvalues?: EntityKeyValueMap): EntityRef | null;
|
|
222
222
|
|
|
223
|
-
/** The payload delivered to an `
|
|
223
|
+
/** The payload delivered to an `onOutput` handler. */
|
|
224
224
|
export interface OutputEvent {
|
|
225
225
|
/** The output's name (e.g. "OnTrigger", "OnPressed", "OnStartTouch"). */
|
|
226
226
|
output: string;
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@s2script/sdk` — the engine-generic authoring barrel.
|
|
3
|
+
*
|
|
4
|
+
* Subpath imports (`@s2script/sdk/commands`, `@s2script/sdk/plugin`, …) stay valid.
|
|
5
|
+
* This root re-exports the same names so a plugin can write:
|
|
6
|
+
*
|
|
7
|
+
* import { command, hook, HookResult, ADMFLAG } from "@s2script/sdk";
|
|
8
|
+
*
|
|
9
|
+
* `Player` / `Pawn` / CS2 schema types are NOT here — they live on `@s2script/cs2`.
|
|
10
|
+
* `@s2script/sdk/unsafe` stays a deliberate subpath (not re-exported).
|
|
11
|
+
*
|
|
12
|
+
* NO runtime code in this file: the engine injects `globalThis.__s2pkg_sdk` at load
|
|
13
|
+
* (`__s2require("@s2script/sdk")`).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export * from "./admin";
|
|
17
|
+
export * from "./bans";
|
|
18
|
+
export * from "./chat";
|
|
19
|
+
export * from "./clients";
|
|
20
|
+
export * from "./commands";
|
|
21
|
+
export * from "./config";
|
|
22
|
+
export * from "./cookies";
|
|
23
|
+
export * from "./damage";
|
|
24
|
+
export * from "./db";
|
|
25
|
+
export * from "./entity";
|
|
26
|
+
export * from "./events";
|
|
27
|
+
export * from "./http";
|
|
28
|
+
export * from "./interfaces";
|
|
29
|
+
export * from "./math";
|
|
30
|
+
export * from "./menu";
|
|
31
|
+
export * from "./net";
|
|
32
|
+
export * from "./phrases";
|
|
33
|
+
export * from "./plugin";
|
|
34
|
+
export * from "./plugins";
|
|
35
|
+
export * from "./sdkhooks";
|
|
36
|
+
export * from "./server";
|
|
37
|
+
export * from "./sound";
|
|
38
|
+
export * from "./timers";
|
|
39
|
+
export * from "./topmenu";
|
|
40
|
+
export * from "./trace";
|
|
41
|
+
export * from "./translations";
|
|
42
|
+
export * from "./transmit";
|
|
43
|
+
export * from "./usercmd";
|
|
44
|
+
export * from "./usermessages";
|
|
45
|
+
export * from "./voice";
|
|
46
|
+
export * from "./votes";
|
|
47
|
+
export * from "./ws";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@s2script/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"license": "MIT OR Apache-2.0",
|
|
5
5
|
"description": "s2script SDK — the builtin capability types (imported as @s2script/sdk/entity, …) plus the `s2s` CLI for building Source 2 / CS2 plugins.",
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"test": "node --experimental-strip-types --no-warnings --test test/*.test.mjs"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@s2script/eslint-plugin": "0.2.
|
|
16
|
+
"@s2script/eslint-plugin": "0.2.1",
|
|
17
17
|
"esbuild": "^0.25.0",
|
|
18
18
|
"eslint": "^10.7.0",
|
|
19
19
|
"typescript": "^5.6.0"
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
"contracts"
|
|
36
36
|
],
|
|
37
37
|
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./index.d.ts"
|
|
40
|
+
},
|
|
38
41
|
"./admin": {
|
|
39
42
|
"types": "./admin.d.ts"
|
|
40
43
|
},
|
|
@@ -92,6 +95,9 @@
|
|
|
92
95
|
"./plugins": {
|
|
93
96
|
"types": "./plugins.d.ts"
|
|
94
97
|
},
|
|
98
|
+
"./sdkhooks": {
|
|
99
|
+
"types": "./sdkhooks.d.ts"
|
|
100
|
+
},
|
|
95
101
|
"./server": {
|
|
96
102
|
"types": "./server.d.ts"
|
|
97
103
|
},
|
package/phrases.d.ts
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* from the phrase files you load.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* import { translations } from "@s2script/sdk";
|
|
10
|
+
* export function OnPluginStart(): void {
|
|
11
|
+
* translations.load("basecomm", "common");
|
|
11
12
|
*
|
|
12
13
|
* cmd.replyT("Usage Gag"); // ok — in translations/basecomm.phrases.json
|
|
13
14
|
* cmd.replyT("No matching players"); // ok — in translations/common.phrases.json
|
|
14
15
|
* cmd.replyT("Usage Gagg"); // compile error — no such key in either
|
|
15
|
-
* }
|
|
16
|
+
* }
|
|
16
17
|
*
|
|
17
18
|
* ## Writing phrases
|
|
18
19
|
*
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
*
|
|
30
31
|
* ## How the checking works
|
|
31
32
|
*
|
|
32
|
-
* The build reads your `
|
|
33
|
+
* The build reads your `translations.load(...)` call and writes `src/phrases.generated.d.ts`,
|
|
33
34
|
* filling in {@link PhraseKeys} with the keys of exactly those files. Load a file and its keys
|
|
34
35
|
* become valid; don't, and they don't — so a forgotten `load` is a compile error rather than raw key
|
|
35
36
|
* text printed to a player at runtime. SourceMod enforces the same rule, but only when it happens.
|
package/plugin.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @s2script/sdk/plugin —
|
|
3
|
-
* and
|
|
4
|
-
*
|
|
2
|
+
* @s2script/sdk/plugin — load-window authoring (`hook`, `publish`, `translations`, `Scope`)
|
|
3
|
+
* and the internal load-scoped {@link PluginContext} types. NO runtime code: the engine injects
|
|
4
|
+
* the implementation at load time (`__s2pkg_plugin`).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* The plugin artifact is `export function OnPluginStart` (plus optional named publics).
|
|
7
|
+
* See docs/superpowers/specs/2026-08-30-drop-plugin-factory-design.md.
|
|
7
8
|
*/
|
|
8
9
|
import type { GameEvent, HookResultValue } from "./events";
|
|
9
10
|
import type { Client } from "./clients";
|
|
10
11
|
import type { EntityRef, OutputEvent } from "./entity";
|
|
11
|
-
import type {
|
|
12
|
-
import type { CommandInvocation } from "./commands";
|
|
12
|
+
import type { CommandHandler } from "./commands";
|
|
13
13
|
import type { Config } from "./config";
|
|
14
14
|
import type { PublishHandle } from "./interfaces";
|
|
15
15
|
import type { TopMenuItem } from "./topmenu";
|
|
@@ -30,7 +30,7 @@ export interface CtxEvents {
|
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Handlers fire for clients that connect AFTER Active. To cover already-connected clients, seed
|
|
33
|
-
* explicitly in
|
|
33
|
+
* explicitly in OnPluginStart: `for (const c of Clients.all()) { … }` — there is no framework
|
|
34
34
|
* replay (replaying `onConnect` for pre-existing clients would fire auth/ban/reservation logic
|
|
35
35
|
* out of its real order).
|
|
36
36
|
*/
|
|
@@ -56,7 +56,7 @@ export interface CtxClients {
|
|
|
56
56
|
/** Per-tick usercmd hook (SM `OnPlayerRunCmd`): read/modify {@link UserCmdView}; return `Handled` to block the tick. */
|
|
57
57
|
onRunCmd(handler: (cmd: UserCmdView, info: { slot: number }) => HookResultValue | void): void;
|
|
58
58
|
}
|
|
59
|
-
/** Entity lifecycle
|
|
59
|
+
/** Entity lifecycle / I/O subscriptions on this plugin's load-scope ({@link PluginContext.entities}). */
|
|
60
60
|
export interface CtxEntities {
|
|
61
61
|
/** An entity of `className` was created (not yet spawned). @param className - match, or `"*"` for all. */
|
|
62
62
|
onCreate(className: string, handler: (entity: EntityRef | null, className: string) => void): void;
|
|
@@ -66,17 +66,9 @@ export interface CtxEntities {
|
|
|
66
66
|
onDelete(className: string, handler: (entity: EntityRef | null, className: string) => void): void;
|
|
67
67
|
/** Hook a named entity output (`FireOutputInternal`); return a {@link HookResultValue} to suppress it. */
|
|
68
68
|
onOutput(classname: string, output: string, handler: (ev: OutputEvent) => HookResultValue | void): void;
|
|
69
|
-
/**
|
|
70
|
-
* Damage pre-hook (SDKHooks-equivalent): read/modify {@link DamageInfo}; return `Handled` semantics via `info`.
|
|
71
|
-
* @example
|
|
72
|
-
* // plugins/basecommands/src/plugin.ts:81 — halve incoming damage
|
|
73
|
-
* ctx.entities.onDamage((info) => { info.damage = info.damage / 2; });
|
|
74
|
-
*/
|
|
75
|
-
onDamage(handler: (info: DamageInfo) => HookResultValue | void): void;
|
|
76
69
|
}
|
|
77
70
|
/** Per-frame + map/precache hooks on this plugin's load-scope ({@link PluginContext.server}). */
|
|
78
71
|
export interface CtxServer {
|
|
79
|
-
/** Run `fn` every game frame. @param opts - `priority` orders it within the frame (`monitor` runs last, read-only). */
|
|
80
72
|
/**
|
|
81
73
|
* Run `fn` every game frame.
|
|
82
74
|
*
|
|
@@ -97,11 +89,11 @@ export interface CtxServer {
|
|
|
97
89
|
/** Console/chat command registration on this plugin's load-scope ({@link PluginContext.commands}). */
|
|
98
90
|
export interface CtxCommands {
|
|
99
91
|
/** Register a public command (any client may run it). */
|
|
100
|
-
register(name: string, handler:
|
|
92
|
+
register(name: string, handler: CommandHandler): void;
|
|
101
93
|
/** Register a server-only command (console/rcon, not client-runnable). */
|
|
102
|
-
registerServer(name: string, handler:
|
|
94
|
+
registerServer(name: string, handler: CommandHandler): void;
|
|
103
95
|
/** Register an admin command gated by `flags` (an `ADMFLAG` bitmask; fail-safe default-deny). */
|
|
104
|
-
registerAdmin(name: string, flags: number, handler:
|
|
96
|
+
registerAdmin(name: string, flags: number, handler: CommandHandler): void;
|
|
105
97
|
/**
|
|
106
98
|
* Observe an existing CLIENT command by name — SourceMod's `AddCommandListener`.
|
|
107
99
|
*
|
|
@@ -162,7 +154,7 @@ export interface Scope {
|
|
|
162
154
|
readonly events: CtxEvents;
|
|
163
155
|
/** Client-lifecycle subscriptions bound to this scope. */
|
|
164
156
|
readonly clients: CtxClients;
|
|
165
|
-
/** Entity/
|
|
157
|
+
/** Entity lifecycle / I/O subscriptions bound to this scope. */
|
|
166
158
|
readonly entities: CtxEntities;
|
|
167
159
|
/** Per-frame/map subscriptions bound to this scope. */
|
|
168
160
|
readonly server: CtxServer;
|
|
@@ -175,8 +167,9 @@ export interface Scope {
|
|
|
175
167
|
}
|
|
176
168
|
|
|
177
169
|
/**
|
|
178
|
-
* The load-scoped context
|
|
179
|
-
*
|
|
170
|
+
* The load-scoped context the host builds for one plugin load. Public authoring uses
|
|
171
|
+
* {@link hook} / {@link command} / named publics instead of receiving this object.
|
|
172
|
+
* {@link Scope} reuses the same subscription namespaces.
|
|
180
173
|
*/
|
|
181
174
|
export interface PluginContext {
|
|
182
175
|
/** This plugin's id (manifest `id`). */
|
|
@@ -187,7 +180,7 @@ export interface PluginContext {
|
|
|
187
180
|
readonly events: CtxEvents;
|
|
188
181
|
/** Client-lifecycle subscriptions ({@link CtxClients}). */
|
|
189
182
|
readonly clients: CtxClients;
|
|
190
|
-
/** Entity/
|
|
183
|
+
/** Entity lifecycle / I/O subscriptions ({@link CtxEntities}). */
|
|
191
184
|
readonly entities: CtxEntities;
|
|
192
185
|
/** Per-frame/map/precache subscriptions ({@link CtxServer}). */
|
|
193
186
|
readonly server: CtxServer;
|
|
@@ -201,7 +194,7 @@ export interface PluginContext {
|
|
|
201
194
|
readonly topmenu: CtxTopMenu;
|
|
202
195
|
/** Publish this plugin's manifest-declared interface. Buffered; goes live at Active. */
|
|
203
196
|
publish<T extends object>(name: string, impl: T): PublishHandle;
|
|
204
|
-
/** Resolve a HARD dep (must be in `pluginDependencies`). Immediate — the proxy is callable
|
|
197
|
+
/** Resolve a HARD dep (must be in `pluginDependencies`). Immediate — the proxy is callable during OnPluginStart. */
|
|
205
198
|
use<T extends object>(name: string): InterfaceHandle<T>;
|
|
206
199
|
/** Resolve an OPTIONAL dep (must be in `optionalPluginDependencies`); null while unpublished. */
|
|
207
200
|
tryUse<T extends object>(name: string): InterfaceHandle<T> | null;
|
|
@@ -209,31 +202,98 @@ export interface PluginContext {
|
|
|
209
202
|
createScope(): Scope;
|
|
210
203
|
}
|
|
211
204
|
|
|
212
|
-
/** Optional lifecycle hooks a
|
|
205
|
+
/** Optional lifecycle hooks a plugin may return to participate in unload + hot-reload.
|
|
206
|
+
* Public authoring uses `export function OnPluginEnd` / `OnPluginState` instead. */
|
|
213
207
|
export interface PluginHooks {
|
|
214
208
|
/** Best-effort cleanup at unload (the ledger remains the teardown authority). */
|
|
215
209
|
onUnload?(): void;
|
|
216
|
-
/** Hot-reload handoff capture; JSON-serialized (EntityRef-aware) and revived as the next instance's
|
|
210
|
+
/** Hot-reload handoff capture; JSON-serialized (EntityRef-aware) and revived as the next instance's {@link previous}. */
|
|
217
211
|
state?(): unknown;
|
|
218
212
|
}
|
|
213
|
+
|
|
219
214
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
215
|
+
* Named publics the host finds on `module.exports` (missing export = not subscribed):
|
|
216
|
+
*
|
|
217
|
+
* Plugin: `OnPluginStart` / `OnPluginEnd` / `OnPluginState` / `OnAllPluginsLoaded` / `OnConfigsExecuted`
|
|
218
|
+
* Map/frame: `OnMapStart(map)` / `OnMapEnd()` / `OnGameFrame()` / `OnPrecache(pc)`
|
|
219
|
+
* Client: `OnClientConnected` / `PutInServer` / `Active` / `PostAdminCheck` / `Disconnect` /
|
|
220
|
+
* `SayCommand` / `SettingsChanged` / `Voice` / `CookiesCached` / `OnPlayerRunCmd`
|
|
221
|
+
* Entity: `OnEntityCreated` / `OnEntitySpawned` / `OnEntityDestroyed`
|
|
222
|
+
*
|
|
223
|
+
* `OnGameFrame` is SourceMod's public (before simulation). Post-simulation paint is
|
|
224
|
+
* {@link Scope}'s `server.onGameFrame(fn, { phase: "post" })` — Metamod's post hook, not a SM public.
|
|
225
|
+
* Filtered entity I/O is {@link onOutput}, not a public — SourceMod `HookEntityOutput`.
|
|
226
|
+
* Per-entity damage is `SDKHook` (`SDKHookType.OnTakeDamage`), not a named public.
|
|
222
227
|
*/
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Game-event catalog. Load-window only — throws after settle (same window as {@link command}).
|
|
231
|
+
*
|
|
232
|
+
* `hook.on` is post-fire; `hook.onPre` is pre (`Handled`/`Stop` suppress the client broadcast).
|
|
233
|
+
* The {@link GameEvent} is valid only synchronously.
|
|
234
|
+
*/
|
|
235
|
+
export declare const hook: {
|
|
236
|
+
/** Post-phase (`ctx.events.on`). */
|
|
237
|
+
on(name: string, handler: (ev: GameEvent) => void): void;
|
|
238
|
+
/**
|
|
239
|
+
* Pre-phase (`ctx.events.onPre`). Return {@link HookResultValue} `Handled`/`Stop` to suppress
|
|
240
|
+
* the client broadcast.
|
|
241
|
+
*/
|
|
242
|
+
onPre(name: string, handler: (ev: GameEvent) => HookResultValue | void): void;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Load-window entity I/O subscribe (SourceMod `HookEntityOutput`). Keyed by `(classname, output)`
|
|
247
|
+
* at the native mux — use `"*"` for either side. Throws after settle.
|
|
248
|
+
*/
|
|
249
|
+
export declare function onOutput(
|
|
250
|
+
classname: string,
|
|
251
|
+
output: string,
|
|
252
|
+
handler: (ev: OutputEvent) => HookResultValue | void,
|
|
253
|
+
): void;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The revived hot-reload handoff (the previous instance's `OnPluginState` return), or `undefined`.
|
|
257
|
+
* Load-window only.
|
|
258
|
+
*/
|
|
259
|
+
export declare function previous(): unknown;
|
|
260
|
+
/**
|
|
261
|
+
* This plugin's id (manifest `id`). Load-window only.
|
|
262
|
+
*/
|
|
263
|
+
export declare function pluginId(): string;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Allocate a disposable subscription scope. Load-window only — same contract as
|
|
267
|
+
* {@link PluginContext.createScope}.
|
|
268
|
+
*/
|
|
269
|
+
export declare function createScope(): Scope;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Publish this plugin's manifest-declared interface. Load-window only (buffered, armed at Active).
|
|
273
|
+
* Same contract as {@link PluginContext.publish}.
|
|
274
|
+
*/
|
|
275
|
+
export declare function publish<T extends object>(name: string, impl: T): PublishHandle;
|
|
276
|
+
/**
|
|
277
|
+
* Resolve a HARD dep (must be in `pluginDependencies`). Load-window only.
|
|
278
|
+
* Same contract as {@link PluginContext.use}. Prefer `import { greet } from "@demo/greeter"` for
|
|
279
|
+
* the producer-as-import form; `use()` remains the explicit load-window form and the optional-dep path.
|
|
280
|
+
*/
|
|
281
|
+
export declare function use<T extends object>(name: string): InterfaceHandle<T>;
|
|
282
|
+
/**
|
|
283
|
+
* Resolve an OPTIONAL dep (must be in `optionalPluginDependencies`); null while unpublished.
|
|
284
|
+
* Load-window only. Same contract as {@link PluginContext.tryUse}.
|
|
285
|
+
*/
|
|
286
|
+
export declare function tryUse<T extends object>(name: string): InterfaceHandle<T> | null;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Load-window TopMenu contribution. Same contract as {@link PluginContext.topmenu}.
|
|
290
|
+
* Throws after settle.
|
|
291
|
+
*/
|
|
292
|
+
export declare const topmenu: CtxTopMenu;
|
|
293
|
+
|
|
229
294
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* // examples/greeter-plugin/src/plugin.ts:8
|
|
234
|
-
* export default plugin((ctx) => {
|
|
235
|
-
* const handle = ctx.publish("@demo/greeter", impl);
|
|
236
|
-
* ctx.server.onGameFrame(() => handle.emit("greeted", { slot: 0 }));
|
|
237
|
-
* });
|
|
295
|
+
* Load-window phrase-file declaration. Same contract as {@link PluginContext.translations}.
|
|
296
|
+
* Throws after settle. `s2s build` / `sync-phrase-types.mjs` collect `translations.load(...)`
|
|
297
|
+
* the same way they collect `ctx.translations.load(...)`.
|
|
238
298
|
*/
|
|
239
|
-
export declare
|
|
299
|
+
export declare const translations: CtxTranslations;
|
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/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
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
*/
|