@amxts/core 0.2.3 → 0.3.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.
Files changed (71) hide show
  1. package/README.md +5 -5
  2. package/README.ru.md +5 -5
  3. package/as/amxts.d.ts +13 -3
  4. package/as/entities.ts +571 -501
  5. package/as/events.ts +3323 -557
  6. package/as/facade.ts +3079 -718
  7. package/as/fetch.ts +2 -2
  8. package/as/fs.ts +352 -59
  9. package/as/hlds.ts +121 -120
  10. package/as/hooks.ts +10585 -5177
  11. package/as/lib/check.ts +3 -2
  12. package/as/natives.ts +856 -1272
  13. package/as/promise.ts +37 -27
  14. package/as/vector.ts +94 -14
  15. package/includes/order.txt +17 -19
  16. package/node_modules/assemblyscript/std/assembly/index.d.ts +1 -1
  17. package/package.json +9 -6
  18. package/runtime/deps/assemblyscript/dist/asc.js +136 -42
  19. package/runtime/deps/assemblyscript/dist/assemblyscript.js +141 -141
  20. package/runtime/licenses/README.md +9 -7
  21. package/runtime/licenses/ReGameDLL-LICENSE +21 -0
  22. package/runtime/licenses/ReHLDS-LICENSE +21 -0
  23. package/runtime/licenses/zlib-LICENSE +22 -0
  24. package/runtime/natives.txt +618 -523
  25. package/runtime/patches/README.md +87 -1
  26. package/runtime/patches/assemblyscript-0.28.20-amxts.patch +4838 -416
  27. package/runtime/patches/wamr-2.4.5-amxts.patch +417 -5
  28. package/scripts/auto-imports.ts +1 -1
  29. package/scripts/build-identity.ts +102 -65
  30. package/scripts/build-wasm.ts +17 -13
  31. package/scripts/check.ts +4 -1
  32. package/scripts/client-messages.ts +73 -1
  33. package/scripts/compile-cache.ts +11 -8
  34. package/scripts/compile-pool.ts +6 -3
  35. package/scripts/compile-worker.ts +23 -23
  36. package/scripts/compile.ts +142 -19
  37. package/scripts/docs/as/amxts.ts +10 -4
  38. package/scripts/docs/as/facade.ts +1779 -275
  39. package/scripts/docs/as/fetch.ts +2 -2
  40. package/scripts/docs/as/fs.ts +190 -6
  41. package/scripts/docs/as/vector.ts +81 -0
  42. package/scripts/docs/entities.ts +12 -0
  43. package/scripts/docs/events.ts +49 -13
  44. package/scripts/docs/game.ts +6 -0
  45. package/scripts/docs/messages.ts +10 -1
  46. package/scripts/hlds-events.ts +11 -11
  47. package/scripts/includes.ts +3 -4
  48. package/scripts/leaf-natives.ts +84 -0
  49. package/scripts/player-fields.ts +1 -1
  50. package/scripts/plugin-natives.ts +1 -1
  51. package/scripts/prebuilt.ts +42 -11
  52. package/scripts/project.ts +63 -4
  53. package/scripts/reapi-events.ts +9 -9
  54. package/scripts/server-env.ts +145 -0
  55. package/scripts/shared-modules.ts +23 -7
  56. package/scripts/system.ts +1 -1
  57. package/scripts/tracked-fs.ts +11 -2
  58. package/scripts/typed-commands.ts +153 -15
  59. package/scripts/upgrade-calls.ts +225 -0
  60. package/scripts/upgrade-names.ts +1 -4
  61. package/scripts/upgrade.ts +47 -34
  62. package/src/cli-api.mjs +3 -2
  63. package/src/system.mjs +21 -11
  64. package/src/testing/compile.ts +31 -8
  65. package/src/testing/coroutines.ts +0 -10
  66. package/src/testing/memory.ts +89 -2
  67. package/src/testing/natives.ts +67 -30
  68. package/src/testing/server.ts +1094 -155
  69. package/src/testing/tables.ts +23 -6
  70. package/src/typecheck.mjs +110 -0
  71. package/runtime/licenses/Bun-LICENSE.md +0 -84
package/as/hlds.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  // The game events and game rules fields of ReGameDLL's and ReHLDS's own,
2
- // which reapi alone delivers, on a server without it - plain HLDS - through
3
- // the stock modules: AMX Mod X's logevents and messages, Ham Sandwich,
4
- // fakemeta, cstrike, a client's commands. scripts/hlds-events.ts says, for
5
- // each, what plain HLDS gives and what it does not.
2
+ // which their hookchains alone deliver, on a server without them - plain
3
+ // HLDS - through the stock hooks, each the module's own: the game's log and
4
+ // messages, Ham Sandwich's functions (in a vtable), the engine's and the
5
+ // game's functions fakemeta hooks, cstrike's buying, a client's commands.
6
+ // scripts/hlds-events.ts says, for each, what plain HLDS gives and what it
7
+ // does not.
6
8
  //
7
9
  // `<event>Hlds(fire, hook)` is registered once, on the event's first listener,
8
- // when the server has no reapi (as/hooks.ts), and everything it registers is
10
+ // when the server has not the chain's API (as/hooks.ts), and everything it registers is
9
11
  // switched by `hook`: off while the event has no listener, so that what it
10
12
  // hears does not reach the plugin for nothing. It makes the event with the fields
11
13
  // the stock hook knows - by the argument's place in reapi's hookchain, as the
@@ -15,8 +17,9 @@
15
17
  // answer, a field written - reaches it where the stock hook can take it, and
16
18
  // is one line in the console where it cannot (`settle`).
17
19
  import {
18
- Call, CellBuffer, Player, TouchEvent, game, server, handled, arg, argText, cellFloat, floatCell,
19
- __Switch, __ham, __off, __onCell, __outcome, __sayOnce, __switchedPublic, __whenPrecache, __whenUp
20
+ Call, CellBuffer, MessageArgs, Player, TouchEvent, game, server, handled, arg, argText, cellFloat, floatCell,
21
+ __Listeners, __Switch, __chainSet, __chainSetText, __ham, __logHook, __messageHook, __playerOf, __off, __onCell, __outcome,
22
+ __sayOnce, __stockHook, __whenUp
20
23
  } from "./facade";
21
24
  import { Entity } from "./entities";
22
25
  import {
@@ -31,18 +34,15 @@ import {
31
34
  ThrowFlashbangEvent, ThrowGrenadeEvent, ThrowHeGrenadeEvent, ThrowSmokeGrenadeEvent
32
35
  } from "./hooks";
33
36
  import {
34
- GetHamReturnInteger, NATIVE_dllfunc, NATIVE_engfunc, NATIVE_forward_return, NATIVE_register_event,
35
- NATIVE_register_logevent, cs_get_user_money, find_ent_by_model, get_cvar_float, get_ent_data,
36
- get_gametime, get_maxplayers, get_orig_retval, get_timeleft, get_user_info, get_user_name,
37
- get_user_userid, is_user_alive, is_user_connected, pev_valid, read_argv, read_logargv, read_logdata, register_clcmd, register_forward,
37
+ NATIVE_dllfunc, NATIVE_engfunc, cs_get_user_money, find_ent_by_model, get_cvar_float, get_ent_data,
38
+ get_gametime, get_maxplayers, get_timeleft, get_user_info, get_user_msgid, get_user_name,
39
+ get_user_userid, is_user_alive, is_user_connected, pev_valid, read_argv, read_logargv,
38
40
  set_cvar_float,
39
41
  } from "./natives";
40
42
  import {
41
43
  CSI_DEFUSER, CSI_FLASHBANG, CSI_HEGRENADE, CSI_LAST_WEAPON, CSI_NVGS, CSI_PRIAMMO, CSI_SECAMMO, CSI_SHIELD,
42
44
  CSI_SHIELDGUN, CSI_SMOKEGRENADE, CSI_VEST, CSI_VESTHELM, DLLFunc_GetGameDescription, EngFunc_PrecacheEvent,
43
- EngFunc_SetClientListening, FMRES_IGNORED, FMRES_SUPERCEDE, FMV_CELL, FMV_STRING, FM_EmitSound,
44
- FM_GetGameDescription, FM_PrecacheGeneric, FM_PrecacheModel, FM_PrecacheSound, FM_SetModel,
45
- FM_Voice_SetClientListening, Ham_AddPlayerItem, Ham_CS_Player_Blind, Ham_Killed, Ham_Spawn,
45
+ Ham_AddPlayerItem, Ham_CS_Player_Blind, Ham_Killed, Ham_Spawn, MSG_ONE, MSG_ONE_UNRELIABLE, SVC_INTERMISSION,
46
46
  ITEM_ASSAULT, ITEM_DEFUSEKIT, ITEM_KEVLAR, ITEM_NVG, ITEM_SHIELDGUN, ITEM_TYPE_BUYING,
47
47
  ROUND_ALL_HOSTAGES_RESCUED, ROUND_BOMB_DEFUSED, ROUND_CTS_PREVENT_ESCAPE, ROUND_CTS_WIN, ROUND_END_DRAW,
48
48
  ROUND_ESCAPING_TERRORISTS_NEUTRALIZED, ROUND_GAME_COMMENCE, ROUND_HOSTAGE_NOT_RESCUED, ROUND_NONE,
@@ -52,7 +52,7 @@ import {
52
52
  } from "./constants";
53
53
  import {
54
54
  TextMsgMessage, SendAudioMessage, MoneyMessage, DeathMsgMessage, VGUIMenuMessage, CS_OnBuyEvent, CS_OnBuyAttemptEvent,
55
- ClientConnectEvent, ClientDisconnectedEvent, ClientInfochangedEvent, ClientPutinserverEvent, ServerChangelevelEvent,
55
+ ClientCommandEvent, ClientConnectEvent, ClientDisconnectedEvent, ClientInfochangedEvent, ClientPutinserverEvent, ServerChangelevelEvent,
56
56
  ServerFrameEvent, ServerMessageMap, addServerListener, removeServerListener
57
57
  } from "./events";
58
58
 
@@ -109,7 +109,7 @@ function onMessage<K extends keyof ServerMessageMap>(name: K, listener: (event:
109
109
  * for, and the hook is switched off while none is.
110
110
  */
111
111
  class Hearers<H> {
112
- list: H[] = [];
112
+ list: __Listeners<H> = new __Listeners<H>();
113
113
  private hook: __Switch = new __Switch();
114
114
  private hooked: bool = false;
115
115
 
@@ -122,8 +122,8 @@ class Hearers<H> {
122
122
  this.list.push(hearer);
123
123
  listened.add((on: bool): void => {
124
124
  if (on) this.list.push(hearer);
125
- else this.list.splice(this.list.indexOf(hearer), 1);
126
- this.hook.set(this.list.length > 0);
125
+ else this.list.remove(hearer);
126
+ this.hook.set(this.list.count > 0);
127
127
  });
128
128
  }
129
129
  }
@@ -134,27 +134,8 @@ class Hearers<H> {
134
134
  * Calls `handler` on a line of the game's log that AMX Mod X's logevent
135
135
  * filter takes: `argc` arguments, `filter` such as "1=Round_End".
136
136
  */
137
- function onLog(key: string, argc: i32, filter: string, handler: () => void, hook: __Switch): void {
138
- __whenUp((): void => {
139
- const pub = __switchedPublic((a: number, b: number, c: number, d: number): void => {
140
- if (heardTwice(key)) return;
141
- handler();
142
- }, `hlds:log:${key}`, hook);
143
- if (pub.length > 0) new Call(NATIVE_register_logevent).str(pub).num(argc).str(filter).run();
144
- });
145
- }
146
-
147
- // AMX Mod X walks its logevents with the line it parsed last. A line logged
148
- // while a logevent runs - the "Round_End" of the kills a listener of
149
- // "Round_Start" makes - is heard, and then again by every logevent after
150
- // the outer one as its walk goes on. Each logevent hears a line once a frame.
151
- const lastLines = new Map<string, string>();
152
-
153
- function heardTwice(key: string): bool {
154
- const line = `${get_gametime()} ${read_logdata()}`;
155
- if (lastLines.has(key) && lastLines.get(key) == line) return true;
156
- lastLines.set(key, line);
157
- return false;
137
+ function onLog(argc: i32, filter: string, handler: () => void, hook: __Switch): void {
138
+ __logHook(argc, filter, handler, hook);
158
139
  }
159
140
 
160
141
  /** The player a log line names first - `"Name<userid><authid><team>"` - or 0. */
@@ -174,8 +155,8 @@ function loggedPlayer(): i32 {
174
155
  }
175
156
 
176
157
  /** A player's line of the log - `"..." triggered "<what>"`: the player, 0 when he is gone. */
177
- function onPlayerLog(event: string, what: string, handler: (id: i32) => void, hook: __Switch): void {
178
- onLog(event, 3, `2=${what}`, (): void => handler(loggedPlayer()), hook);
158
+ function onPlayerLog(what: string, handler: (id: i32) => void, hook: __Switch): void {
159
+ onLog(3, `2=${what}`, (): void => handler(loggedPlayer()), hook);
179
160
  }
180
161
 
181
162
  /** The planted bomb: the grenade with the C4's model, or 0. */
@@ -183,16 +164,20 @@ function plantedBomb(): i32 {
183
164
  return max(<i32>find_ent_by_model(-1, "grenade", "models/w_c4.mdl"), 0);
184
165
  }
185
166
 
186
- // ---------------------------------------------------------------- fakemeta
167
+ // ---------------------------------------------------------------- the functions fakemeta hooks
187
168
 
188
- /** Hooks a fakemeta function: `handler` reads its arguments with arg() and argText(). */
189
- function onFakemeta(fn: i32, post: bool, key: string, handler: () => void, hook: __Switch | null, early: bool = false): void {
190
- const register = (): void => {
191
- const pub = __switchedPublic((a: number, b: number, c: number, d: number): void => handler(), `hlds:fm:${key}`, hook, FMRES_IGNORED);
192
- if (pub.length > 0) register_forward(fn, pub, post ? 1 : 0);
193
- };
194
- if (early) __whenPrecache(register);
195
- else __whenUp(register);
169
+ // The functions by stock_hook's number: must match StockFunction in runtime/src/enginehooks.h.
170
+ const STOCK_SET_MODEL: i32 = 0;
171
+ const STOCK_EMIT_SOUND: i32 = 1;
172
+ const STOCK_CLIENT_LISTENING: i32 = 2;
173
+ const STOCK_PRECACHE_MODEL: i32 = 3;
174
+ const STOCK_PRECACHE_SOUND: i32 = 4;
175
+ const STOCK_PRECACHE_GENERIC: i32 = 5;
176
+ const STOCK_GAME_DESCRIPTION: i32 = 6;
177
+
178
+ /** Hooks one of the engine's or the game's functions (STOCK_*): `handler` reads its arguments with arg() and argText(). */
179
+ function onStock(fn: i32, post: bool, handler: () => void, hook: __Switch | null): void {
180
+ __stockHook(fn, (a: number, b: number, c: number, d: number): void => handler(), post, hook);
196
181
  }
197
182
 
198
183
  // One SetModel hook for every event that hears an entity get its model: each
@@ -200,48 +185,64 @@ function onFakemeta(fn: i32, post: bool, key: string, handler: () => void, hook:
200
185
  const modelHearers = new Hearers<(entity: i32, model: string) => void>();
201
186
 
202
187
  function onSetModel(hearer: (entity: i32, model: string) => void, hook: __Switch): void {
203
- modelHearers.add(hearer, hook, (shared: __Switch): void => onFakemeta(FM_SetModel, false, "setmodel", heardModel, shared));
188
+ modelHearers.add(hearer, hook, (shared: __Switch): void => onStock(STOCK_SET_MODEL, false, heardModel, shared));
204
189
  }
205
190
 
206
191
  function heardModel(): void {
207
192
  const entity = <i32>arg(0);
208
193
  const model = argText(1);
209
- const hearers = modelHearers.list.slice(0);
210
- for (let i = 0; i < hearers.length; i++) hearers[i](entity, model);
194
+ const hearers = modelHearers.list;
195
+ const n = hearers.begin();
196
+ for (let i = 0; i < n; i++) {
197
+ const hearer = hearers.at(i);
198
+ if (hearer) hearer(entity, model);
199
+ }
200
+ hearers.end();
211
201
  }
212
202
 
213
203
  // One EmitSound hook for every event that hears a sound.
214
204
  const soundHearers = new Hearers<(entity: i32, sample: string) => void>();
215
205
 
216
206
  function onSound(hearer: (entity: i32, sample: string) => void, hook: __Switch): void {
217
- soundHearers.add(hearer, hook, (shared: __Switch): void => onFakemeta(FM_EmitSound, false, "emitsound", heardSound, shared));
207
+ soundHearers.add(hearer, hook, (shared: __Switch): void => onStock(STOCK_EMIT_SOUND, false, heardSound, shared));
218
208
  }
219
209
 
220
210
  function heardSound(): void {
221
211
  const entity = <i32>arg(0);
222
212
  const sample = argText(2);
223
- const hearers = soundHearers.list.slice(0);
224
- for (let i = 0; i < hearers.length; i++) hearers[i](entity, sample);
213
+ const hearers = soundHearers.list;
214
+ const n = hearers.begin();
215
+ for (let i = 0; i < n; i++) {
216
+ const hearer = hearers.at(i);
217
+ if (hearer) hearer(entity, sample);
218
+ }
219
+ hearers.end();
225
220
  }
226
221
 
227
222
  // ---------------------------------------------------------------- client commands
228
223
 
229
224
  /** Hears a client command before the game: `handler` may block it with handled(). */
230
- function onCommand(event: string, command: string, handler: (id: i32) => void, hook: __Switch): void {
231
- __whenUp((): void => {
232
- const pub = __switchedPublic((a: number, b: number, c: number, d: number): void => handler(<i32>a), `hlds:${event}:${command}`, hook);
233
- if (pub.length > 0) register_clcmd(command, pub);
234
- });
225
+ function onCommand(command: string, handler: (id: i32) => void, hook: __Switch): void {
226
+ onServer<ClientCommandEvent>((heard: ClientCommandEvent): void => {
227
+ if (read_argv(0) == command) handler(<i32>heard.player.id);
228
+ }, hook);
235
229
  }
236
230
 
237
- /** Hears one of the game's messages as AMX Mod X's events do: `flags` and `conditions` as register_event takes them. */
238
- function onEvent(message: string, handler: () => void, hook: __Switch, flags: string, conditions: string[] = []): void {
231
+ const heardArgs = new MessageArgs();
232
+
233
+ /**
234
+ * Hears one of the game's messages to everyone - not to one player - as
235
+ * AMX Mod X's register_event with "a" does: by its name, or an engine
236
+ * message by its number (`svc`); `wanted` picks those of the arguments it is
237
+ * after. The game numbers its messages as the map loads.
238
+ */
239
+ function onGlobalMessage(name: string, svc: i32, handler: () => void, hook: __Switch, wanted: ((args: MessageArgs) => bool) | null = null): void {
239
240
  __whenUp((): void => {
240
- const pub = __switchedPublic((a: number, b: number, c: number, d: number): void => handler(), `hlds:event:${message}`, hook);
241
- if (pub.length == 0) return;
242
- const call = new Call(NATIVE_register_event).str(message).str(pub).str(flags);
243
- for (let i = 0; i < conditions.length; i++) call.str(conditions[i]);
244
- call.run();
241
+ const id = svc > 0 ? svc : <i32>get_user_msgid(name);
242
+ __messageHook(id, (receiver: number, message: number, dest: number, d: number): void => {
243
+ if (dest == MSG_ONE || dest == MSG_ONE_UNRELIABLE) return;
244
+ if (wanted == null || wanted(heardArgs)) handler();
245
+ }, hook);
245
246
  });
246
247
  }
247
248
 
@@ -263,7 +264,7 @@ let newRoundAt: f64 = -1;
263
264
  * game commences once both sides have players.
264
265
  */
265
266
  export function newRoundHlds(fire: Fire<NewRoundEvent>, hook: __Switch): void {
266
- onEvent("HLTV", (): void => announceRound(fire), hook, "a", ["1=0", "2=0"]);
267
+ onGlobalMessage("HLTV", 0, (): void => announceRound(fire), hook, (args: MessageArgs): bool => args.number(0) == 0 && args.number(1) == 0);
267
268
  }
268
269
 
269
270
  function announceRound(fire: Fire<NewRoundEvent>): void {
@@ -293,8 +294,13 @@ function onDecalsReset(hearer: () => void, hook: __Switch): void {
293
294
  }
294
295
 
295
296
  function decalsReset(a: i32): void {
296
- const hearers = decalHearers.list.slice(0);
297
- for (let i = 0; i < hearers.length; i++) hearers[i]();
297
+ const hearers = decalHearers.list;
298
+ const n = hearers.begin();
299
+ for (let i = 0; i < n; i++) {
300
+ const hearer = hearers.at(i);
301
+ if (hearer) hearer();
302
+ }
303
+ hearers.end();
298
304
  }
299
305
 
300
306
  /** The round once its players have respawned: the decals' reset at its HLTV message's time. */
@@ -314,7 +320,7 @@ export function mapResetHlds(fire: Fire<MapResetEvent>, hook: __Switch): void {
314
320
 
315
321
  /** The freeze time is over: the game logs "Round_Start". */
316
322
  export function roundStartHlds(fire: Fire<RoundStartEvent>, hook: __Switch): void {
317
- onLog("roundStart", 2, "1=Round_Start", (): void => hear(fire, new RoundStartEvent(), "roundStart"), hook);
323
+ onLog(2, "1=Round_Start", (): void => hear(fire, new RoundStartEvent(), "roundStart"), hook);
318
324
  }
319
325
 
320
326
  // The round's end, told before its log line, by the time each came: the
@@ -377,15 +383,15 @@ export function roundEndHlds(fire: Fire<RoundEndEvent>, hook: __Switch): void {
377
383
  endSound = message.sound;
378
384
  endSoundAt = get_gametime();
379
385
  }, hook);
380
- onLog("roundEnd:team", 6, "0=Team", (): void => {
386
+ onLog(6, "0=Team", (): void => {
381
387
  endTrigger = `#${read_logargv(3)}`;
382
388
  endTriggerAt = get_gametime();
383
389
  }, hook);
384
- onLog("roundEnd:world", 4, "0=World triggered", (): void => {
390
+ onLog(4, "0=World triggered", (): void => {
385
391
  endTrigger = `#${read_logargv(1)}`;
386
392
  endTriggerAt = get_gametime();
387
393
  }, hook);
388
- onLog("roundEnd", 2, "1=Round_End", (): void => {
394
+ onLog(2, "1=Round_End", (): void => {
389
395
  const now = get_gametime();
390
396
  let ending = endTextAt == now ? endingOf(endText) : null;
391
397
  if (ending == null && endTriggerAt == now) ending = endingOf(endTrigger);
@@ -416,7 +422,7 @@ export function gameThinkHlds(fire: Fire<GameThinkEvent>, hook: __Switch): void
416
422
 
417
423
  /** The map is over: the game sends SVC_INTERMISSION, which AMX Mod X's events hear as "30". */
418
424
  export function intermissionHlds(fire: Fire<IntermissionEvent>, hook: __Switch): void {
419
- onEvent("30", (): void => hear(fire, new IntermissionEvent(), "intermission"), hook, "a");
425
+ onGlobalMessage("", SVC_INTERMISSION, (): void => hear(fire, new IntermissionEvent(), "intermission"), hook);
420
426
  }
421
427
 
422
428
  /** The game changes the map: AMX Mod X's server_changelevel. */
@@ -494,7 +500,7 @@ export function painHlds(fire: Fire<PainEvent>, hook: __Switch): void {
494
500
  event.__give(0, entity);
495
501
  event.__give(1, hitGroup);
496
502
  event.__give(2, armour ? 1 : 0);
497
- hear(fire, event, "pain", FMRES_SUPERCEDE);
503
+ hear(fire, event, "pain", PLUGIN_HANDLED);
498
504
  }, hook);
499
505
  }
500
506
 
@@ -504,7 +510,7 @@ export function deathSoundHlds(fire: Fire<DeathSoundEvent>, hook: __Switch): voi
504
510
  if (playerOr0(entity) == 0 || !DEATH_SOUNDS.includes(sample)) return;
505
511
  const event = new DeathSoundEvent();
506
512
  event.__give(0, entity);
507
- hear(fire, event, "deathSound", FMRES_SUPERCEDE);
513
+ hear(fire, event, "deathSound", PLUGIN_HANDLED);
508
514
  }, hook);
509
515
  }
510
516
 
@@ -519,7 +525,7 @@ export function startSoundHlds(fire: Fire<StartSoundEvent>, hook: __Switch): voi
519
525
  event.__give(5, <i32>arg(4));
520
526
  event.__give(6, <i32>arg(5));
521
527
  event.__give(7, <i32>arg(6));
522
- hear(fire, event, "startSound", FMRES_SUPERCEDE);
528
+ hear(fire, event, "startSound", PLUGIN_HANDLED);
523
529
  }, hook);
524
530
  }
525
531
 
@@ -527,7 +533,7 @@ export function startSoundHlds(fire: Fire<StartSoundEvent>, hook: __Switch): voi
527
533
 
528
534
  /** The terrorist who gets the bomb at the round's start: the game logs "Spawned_With_The_Bomb". */
529
535
  export function giveBombHlds(fire: Fire<GiveBombEvent>, hook: __Switch): void {
530
- onPlayerLog("giveBomb", "Spawned_With_The_Bomb", (id: i32): void => {
536
+ onPlayerLog("Spawned_With_The_Bomb", (id: i32): void => {
531
537
  const event = new GiveBombEvent();
532
538
  event.__give(-1, id);
533
539
  hear(fire, event, "giveBomb");
@@ -535,7 +541,7 @@ export function giveBombHlds(fire: Fire<GiveBombEvent>, hook: __Switch): void {
535
541
  }
536
542
 
537
543
  export function becomeBomberHlds(fire: Fire<BecomeBomberEvent>, hook: __Switch): void {
538
- onPlayerLog("becomeBomber", "Spawned_With_The_Bomb", (id: i32): void => {
544
+ onPlayerLog("Spawned_With_The_Bomb", (id: i32): void => {
539
545
  const event = new BecomeBomberEvent();
540
546
  event.__give(0, id);
541
547
  event.__give(-1, 1);
@@ -544,7 +550,7 @@ export function becomeBomberHlds(fire: Fire<BecomeBomberEvent>, hook: __Switch):
544
550
  }
545
551
 
546
552
  export function becomeVipHlds(fire: Fire<BecomeVipEvent>, hook: __Switch): void {
547
- onPlayerLog("becomeVip", "Became_VIP", (id: i32): void => {
553
+ onPlayerLog("Became_VIP", (id: i32): void => {
548
554
  const event = new BecomeVipEvent();
549
555
  event.__give(0, id);
550
556
  hear(fire, event, "becomeVip");
@@ -568,7 +574,7 @@ export function plantBombHlds(fire: Fire<PlantBombEvent>, hook: __Switch): void
568
574
 
569
575
  /** A defuse begins: the game logs "Begin_Bomb_Defuse_With_Kit", or "_Without_Kit". */
570
576
  export function defuseBombStartHlds(fire: Fire<DefuseBombStartEvent>, hook: __Switch): void {
571
- onLog("defuseBombStart", 3, "2&Begin_Bomb_Defuse_", (): void => {
577
+ onLog(3, "2&Begin_Bomb_Defuse_", (): void => {
572
578
  const event = new DefuseBombStartEvent();
573
579
  event.__give(0, plantedBomb());
574
580
  event.__give(1, loggedPlayer());
@@ -578,7 +584,7 @@ export function defuseBombStartHlds(fire: Fire<DefuseBombStartEvent>, hook: __Sw
578
584
 
579
585
  /** The bomb defused: the game logs "Defused_The_Bomb". */
580
586
  export function defuseBombEndHlds(fire: Fire<DefuseBombEndEvent>, hook: __Switch): void {
581
- onPlayerLog("defuseBombEnd", "Defused_The_Bomb", (id: i32): void => {
587
+ onPlayerLog("Defused_The_Bomb", (id: i32): void => {
582
588
  const event = new DefuseBombEndEvent();
583
589
  event.__give(0, plantedBomb());
584
590
  event.__give(1, id);
@@ -589,7 +595,7 @@ export function defuseBombEndHlds(fire: Fire<DefuseBombEndEvent>, hook: __Switch
589
595
 
590
596
  /** The bomb blew up: the terrorists trigger "Target_Bombed". */
591
597
  export function explodeBombHlds(fire: Fire<ExplodeBombEvent>, hook: __Switch): void {
592
- onLog("explodeBomb", 6, "3=Target_Bombed", (): void => {
598
+ onLog(6, "3=Target_Bombed", (): void => {
593
599
  const event = new ExplodeBombEvent();
594
600
  event.__give(0, plantedBomb());
595
601
  hear(fire, event, "explodeBomb");
@@ -718,9 +724,9 @@ export function chooseTeamHlds(fire: Fire<ChooseTeamEvent>, hook: __Switch): voi
718
724
  event.__give(1, commandNumber());
719
725
  hear(fire, event, "chooseTeam", PLUGIN_HANDLED);
720
726
  };
721
- onCommand("chooseTeam", "jointeam", chosen, hook);
722
- onCommand("chooseTeam", "menuselect", (id: i32): void => {
723
- const menu = new Player(id).openMenu;
727
+ onCommand("jointeam", chosen, hook);
728
+ onCommand("menuselect", (id: i32): void => {
729
+ const menu = __playerOf(id).openMenu;
724
730
  if (menu == "team" || menu == "teamInGame") chosen(id);
725
731
  }, hook);
726
732
  }
@@ -733,15 +739,15 @@ export function chooseAppearanceHlds(fire: Fire<ChooseAppearanceEvent>, hook: __
733
739
  event.__give(1, commandNumber());
734
740
  hear(fire, event, "chooseAppearance", PLUGIN_HANDLED);
735
741
  };
736
- onCommand("chooseAppearance", "joinclass", chosen, hook);
737
- onCommand("chooseAppearance", "menuselect", (id: i32): void => {
738
- if (new Player(id).openMenu == "appearance") chosen(id);
742
+ onCommand("joinclass", chosen, hook);
743
+ onCommand("menuselect", (id: i32): void => {
744
+ if (__playerOf(id).openMenu == "appearance") chosen(id);
739
745
  }, hook);
740
746
  }
741
747
 
742
748
  /** A weapon dropped: the player's `drop` command, with the weapon he names. */
743
749
  export function dropPlayerItemHlds(fire: Fire<DropPlayerItemEvent>, hook: __Switch): void {
744
- onCommand("dropPlayerItem", "drop", (id: i32): void => {
750
+ onCommand("drop", (id: i32): void => {
745
751
  const event = new DropPlayerItemEvent();
746
752
  event.__give(0, id);
747
753
  event.__giveText(1, read_argv(1));
@@ -821,14 +827,10 @@ export function playerBlindHlds(fire: Fire<PlayerBlindEvent>, hook: __Switch): v
821
827
  }, true, hook);
822
828
  }
823
829
 
824
- // Ham Sandwich's by-address answer.
825
- const hamAnswer = new StaticArray<i32>(1);
826
-
827
- /** A player got a weapon: Ham Sandwich's AddPlayerItem after the game, when it took the item. */
830
+ /** A player got a weapon: Ham Sandwich's AddPlayerItem after the game, when it took the item - its answer, arg(-1). */
828
831
  export function playerGotWeaponHlds(fire: Fire<PlayerGotWeaponEvent>, hook: __Switch): void {
829
832
  __ham(Ham_AddPlayerItem, "player", (id: number, item: number, c: number, d: number): void => {
830
- GetHamReturnInteger(changetype<i32>(hamAnswer));
831
- if (hamAnswer[0] == 0) return;
833
+ if (arg(-1) == 0) return;
832
834
  const event = new PlayerGotWeaponEvent();
833
835
  event.__give(0, <i32>id);
834
836
  event.__give(1, <i32>item);
@@ -841,7 +843,7 @@ export function playerGotWeaponHlds(fire: Fire<PlayerGotWeaponEvent>, hook: __Sw
841
843
  * answer is told instead - preventDefault() answers `false`.
842
844
  */
843
845
  export function canPlayerHearPlayerHlds(fire: Fire<CanPlayerHearPlayerEvent>, hook: __Switch): void {
844
- onFakemeta(FM_Voice_SetClientListening, false, "voice", (): void => {
846
+ onStock(STOCK_CLIENT_LISTENING, false, (): void => {
845
847
  const listener = playerOr0(<i32>arg(0));
846
848
  const sender = playerOr0(<i32>arg(1));
847
849
  if (listener == 0 || sender == 0) return;
@@ -853,9 +855,7 @@ export function canPlayerHearPlayerHlds(fire: Fire<CanPlayerHearPlayerEvent>, ho
853
855
  fire(event, true);
854
856
  if (event.__answered || event.__prevented) {
855
857
  const hears = event.__prevented ? 0 : event.__answerCell;
856
- new Call(NATIVE_engfunc).num(EngFunc_SetClientListening).ref(listener).ref(sender).ref(hears).run();
857
- new Call(NATIVE_forward_return).num(FMV_CELL).ref(hears).run();
858
- __outcome(FMRES_SUPERCEDE);
858
+ __chainSet(2, hears);
859
859
  }
860
860
  event.__prevented = false;
861
861
  settle(event, "canPlayerHearPlayer", -1, true);
@@ -919,7 +919,7 @@ const THROWN: string[] = ["models/w_hegrenade.mdl", "models/w_flashbang.mdl", "m
919
919
  export function throwGrenadeHlds(fire: Fire<ThrowGrenadeEvent>, hook: __Switch): void {
920
920
  for (let i = 0; i < THROWN.length; i++) {
921
921
  onThrown(THROWN[i], (grenade: Entity, thrower: i32, fuse: f64): void => {
922
- const weapon = thrower > 0 ? new Player(thrower).activeItem : null;
922
+ const weapon = thrower > 0 ? __playerOf(thrower).activeItem : null;
923
923
  const event = new ThrowGrenadeEvent();
924
924
  event.__give(0, thrower);
925
925
  event.__give(1, weapon != null ? <i32>weapon.id : 0);
@@ -939,7 +939,7 @@ export function setModelHlds(fire: Fire<SetModelEvent>, hook: __Switch): void {
939
939
  const event = new SetModelEvent();
940
940
  event.__give(0, entity);
941
941
  event.__giveText(1, model);
942
- hear(fire, event, "setModel", FMRES_SUPERCEDE);
942
+ hear(fire, event, "setModel", PLUGIN_HANDLED);
943
943
  }, hook);
944
944
  }
945
945
 
@@ -970,38 +970,39 @@ export function bounceGibTouchHlds(fire: Fire<BounceGibTouchEvent>, hook: __Swit
970
970
  }
971
971
 
972
972
  /**
973
- * The game precaches a file: fakemeta's forward, added at plugin_precache,
974
- * before the game's own. The listeners before the game hear it as it is
975
- * asked, preventDefault() skips it; the ones after hear the engine's answer.
973
+ * The game precaches a file: the engine's function, hooked as the plugin
974
+ * loads, before the game's own precaches. The listeners before the game hear
975
+ * it as it is asked, preventDefault() skips it; the ones after hear the
976
+ * engine's answer.
976
977
  */
977
- function onPrecache<E>(fn: i32, key: string, make: () => E, fire: Fire<E>, name: string, hook: __Switch): void {
978
- onFakemeta(fn, false, `${key}:pre`, (): void => {
978
+ function onPrecache<E>(fn: i32, make: () => E, fire: Fire<E>, name: string, hook: __Switch): void {
979
+ onStock(fn, false, (): void => {
979
980
  const event = make();
980
981
  const fields = changetype<HookEvent>(event);
981
982
  fields.__giveText(0, argText(0));
982
983
  fire(event, false);
983
- settle(changetype<HookEvent>(event), name, FMRES_SUPERCEDE);
984
- }, hook, true);
985
- onFakemeta(fn, true, `${key}:post`, (): void => {
984
+ settle(changetype<HookEvent>(event), name, PLUGIN_HANDLED);
985
+ }, hook);
986
+ onStock(fn, true, (): void => {
986
987
  const event = make();
987
988
  const fields = changetype<HookEvent>(event);
988
989
  fields.__giveText(0, argText(0));
989
- fields.__give(-1, <i32>get_orig_retval());
990
+ fields.__give(-1, <i32>arg(-1));
990
991
  fire(event, true);
991
992
  settle(changetype<HookEvent>(event), name);
992
- }, hook, true);
993
+ }, hook);
993
994
  }
994
995
 
995
996
  export function precacheModelHlds(fire: Fire<PrecacheModelEvent>, hook: __Switch): void {
996
- onPrecache<PrecacheModelEvent>(FM_PrecacheModel, "model", (): PrecacheModelEvent => new PrecacheModelEvent(), fire, "precacheModel", hook);
997
+ onPrecache<PrecacheModelEvent>(STOCK_PRECACHE_MODEL, (): PrecacheModelEvent => new PrecacheModelEvent(), fire, "precacheModel", hook);
997
998
  }
998
999
 
999
1000
  export function precacheSoundHlds(fire: Fire<PrecacheSoundEvent>, hook: __Switch): void {
1000
- onPrecache<PrecacheSoundEvent>(FM_PrecacheSound, "sound", (): PrecacheSoundEvent => new PrecacheSoundEvent(), fire, "precacheSound", hook);
1001
+ onPrecache<PrecacheSoundEvent>(STOCK_PRECACHE_SOUND, (): PrecacheSoundEvent => new PrecacheSoundEvent(), fire, "precacheSound", hook);
1001
1002
  }
1002
1003
 
1003
1004
  export function precacheFileHlds(fire: Fire<PrecacheFileEvent>, hook: __Switch): void {
1004
- onPrecache<PrecacheFileEvent>(FM_PrecacheGeneric, "generic", (): PrecacheFileEvent => new PrecacheFileEvent(), fire, "precacheFile", hook);
1005
+ onPrecache<PrecacheFileEvent>(STOCK_PRECACHE_GENERIC, (): PrecacheFileEvent => new PrecacheFileEvent(), fire, "precacheFile", hook);
1005
1006
  }
1006
1007
 
1007
1008
  // ---------------------------------------------------------------- the game rules' fields
@@ -1020,9 +1021,9 @@ export function gameNameHlds(): string {
1020
1021
  /** Writes the game's name: the game's GetGameDescription is answered with it from now on. */
1021
1022
  export function setGameNameHlds(value: string): void {
1022
1023
  if (gameNameWritten.length == 0) {
1023
- onFakemeta(FM_GetGameDescription, false, "gamedesc", (): void => {
1024
- new Call(NATIVE_forward_return).num(FMV_STRING).str(gameNameWritten).run();
1025
- __outcome(FMRES_SUPERCEDE);
1024
+ onStock(STOCK_GAME_DESCRIPTION, false, (): void => {
1025
+ __chainSetText(-1, gameNameWritten);
1026
+ handled();
1026
1027
  }, null);
1027
1028
  }
1028
1029
  gameNameWritten = value;