@amxts/core 0.1.0 → 0.2.1

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 (77) hide show
  1. package/README.md +21 -25
  2. package/README.ru.md +22 -24
  3. package/as/amxts.d.ts +63 -3
  4. package/as/constants.ts +684 -116
  5. package/as/entities.ts +1905 -1364
  6. package/as/events.ts +1696 -907
  7. package/as/facade.ts +1240 -220
  8. package/as/fetch.ts +1519 -0
  9. package/as/flags.ts +159 -145
  10. package/as/hlds.ts +1071 -0
  11. package/as/hooks.ts +5226 -5379
  12. package/as/kit.ts +15 -5
  13. package/as/lib/check.ts +1 -1
  14. package/as/natives.ts +2658 -2499
  15. package/as/remote.ts +39 -15
  16. package/as/tsconfig.json +7 -2
  17. package/as/vector.ts +12 -9
  18. package/includes/order.txt +22 -29
  19. package/includes/sources.json +0 -8
  20. package/node_modules/assemblyscript/std/assembly/index.d.ts +20 -11
  21. package/package.json +29 -7
  22. package/runtime/deps/assemblyscript/dist/asc.js +86 -16
  23. package/runtime/deps/assemblyscript/dist/assemblyscript.js +29 -29
  24. package/runtime/licenses/MPL-2.0.txt +373 -0
  25. package/runtime/licenses/README.md +5 -1
  26. package/runtime/licenses/curl-COPYING +22 -0
  27. package/runtime/licenses/libssh2-COPYING +43 -0
  28. package/runtime/licenses/mbedTLS-LICENSE +553 -0
  29. package/runtime/natives.txt +108 -105
  30. package/runtime/patches/assemblyscript-0.28.20-amxts.patch +752 -296
  31. package/runtime/patches/wamr-2.4.5-amxts.patch +148 -0
  32. package/scripts/apply-docs.ts +2 -2
  33. package/scripts/auto-imports.ts +2 -2
  34. package/scripts/build-identity.ts +115 -0
  35. package/scripts/build-wasm.ts +101 -50
  36. package/scripts/check.ts +17 -3
  37. package/scripts/client-messages.ts +246 -0
  38. package/scripts/compile-cache.ts +4 -2
  39. package/scripts/compile-pool.ts +128 -0
  40. package/scripts/compile-worker.ts +23 -0
  41. package/scripts/compile.ts +156 -59
  42. package/scripts/docker-server.ts +13 -3
  43. package/scripts/docs/as/amxts.ts +86 -0
  44. package/scripts/docs/as/facade.ts +688 -188
  45. package/scripts/docs/as/fetch.ts +805 -0
  46. package/scripts/docs/entities.ts +43 -144
  47. package/scripts/docs/events.ts +17 -28
  48. package/scripts/docs/game.ts +68 -22
  49. package/scripts/docs/messages.ts +330 -16
  50. package/scripts/editor-api.ts +101 -0
  51. package/scripts/hlds-events.ts +370 -0
  52. package/scripts/includes.ts +12 -10
  53. package/scripts/player-fields.ts +16 -15
  54. package/scripts/prebuilt.ts +65 -23
  55. package/scripts/prepare.ts +42 -28
  56. package/scripts/project.ts +120 -20
  57. package/scripts/reapi-events.ts +98 -0
  58. package/scripts/shared-modules.ts +85 -23
  59. package/scripts/source-map.ts +242 -0
  60. package/scripts/system.ts +1 -1
  61. package/scripts/typed-commands.ts +263 -0
  62. package/scripts/typed-configs.ts +23 -12
  63. package/scripts/ui.ts +3 -1
  64. package/scripts/upgrade-menus.ts +274 -0
  65. package/scripts/upgrade-names.ts +384 -0
  66. package/scripts/upgrade.ts +857 -0
  67. package/src/cli-api.mjs +37 -4
  68. package/src/system.mjs +74 -3
  69. package/src/testing/compile.ts +8 -5
  70. package/src/testing/index.ts +2 -1
  71. package/src/testing/natives.ts +199 -5
  72. package/src/testing/network.ts +531 -0
  73. package/src/testing/server.ts +572 -48
  74. package/src/testing/setup.ts +1 -1
  75. package/src/testing/tables.ts +25 -0
  76. package/as/modules/http.ts +0 -184
  77. package/scripts/docs/as/modules/http.ts +0 -66
@@ -1,14 +1,31 @@
1
1
  // Our own words for the messages the server sends its clients, as
2
- // server.addEventListener("message:<Name>", ...) hears them - in both
3
- // languages. scripts/generate-host.ts writes them into as/events.ts as the
4
- // tooltips an editor shows on the name, the event and its fields. A message
5
- // without an entry has the words of every message: its arguments by number.
2
+ // server.addMessageListener(name, ...) hears them - in both languages.
3
+ // scripts/generate-host.ts writes them into as/events.ts as the tooltips an
4
+ // editor shows on the name, the event and its fields: the summary, then the
5
+ // game's name of the message, so a Pawn author who searches for `DeathMsg`
6
+ // finds `death`. A name that hears a few of the game's messages has its words
7
+ // under the first of them, its fields' words those of every one.
6
8
  import type { EventDoc, Text } from './events';
7
9
 
8
10
  const receiver: Text = { en: `The player the message goes to.`, ru: `Игрок, которому идёт сообщение.` };
9
11
  const seconds: Text = { en: `The seconds the bar or the clock shows. Assign to change them.`, ru: `Секунды, которые показывают полоса или часы. Присвойте, чтобы изменить.` };
12
+ const params: Text = {
13
+ en: `The texts put into the game's text in place of \`%s1\`, \`%s2\`, ... - e.g. a player's name. Assign to change them; their number stays.`,
14
+ ru: `Тексты, которые подставляются в текст игры вместо \`%s1\`, \`%s2\`, ... — например, имя игрока. Присвойте, чтобы изменить; их число остаётся.`,
15
+ };
16
+ const battery: Text = { en: `The flashlight's battery, in percent.`, ru: `Заряд фонарика, в процентах.` };
17
+ const health: Text = { en: `The health shown.`, ru: `Показанное здоровье.` };
18
+ const hostage: Text = { en: `The hostage's number on the map, from \`1\`.`, ru: `Номер заложника на карте, с \`1\`.` };
19
+ const text: Text = { en: `The text, or the game's own for it, e.g. \`"#Hint_press_buy_to_purchase"\`.`, ru: `Текст или собственный текст игры, например \`"#Hint_press_buy_to_purchase"\`.` };
10
20
 
11
21
  export const MESSAGES: Record<string, EventDoc> = {
22
+ ADStop: {
23
+ summary: { en: `A player's HUD stops showing the round's advertisement.`, ru: `HUD игрока перестаёт показывать рекламу раунда.` },
24
+ },
25
+ AllowSpec: {
26
+ summary: { en: `Whether a player may pick Spectate in the team menu.`, ru: `Можно ли игроку выбрать «Наблюдать» в меню команды.` },
27
+ fields: { allowed: { en: `\`true\` when the menu offers it.`, ru: `\`true\`, когда меню это предлагает.` } },
28
+ },
12
29
  AmmoPickup: {
13
30
  summary: { en: `A player picks up ammo: the notice at the side of his screen.`, ru: `Игрок подбирает патроны: уведомление сбоку экрана.` },
14
31
  fields: {
@@ -16,21 +33,70 @@ export const MESSAGES: Record<string, EventDoc> = {
16
33
  amount: { en: `The amount picked up.`, ru: `Подобранное количество.` },
17
34
  },
18
35
  },
36
+ AmmoX: {
37
+ summary: { en: `A player's reserve ammo of one kind on his HUD changes.`, ru: `Меняется запас патронов одного вида на HUD игрока.` },
38
+ fields: {
39
+ ammo: { en: `The ammo's index in the game's list of kinds.`, ru: `Индекс патронов в списке видов у игры.` },
40
+ amount: { en: `The reserve shown.`, ru: `Показанный запас.` },
41
+ },
42
+ },
43
+ ArmorType: {
44
+ summary: { en: `The armour icon on a player's HUD: a vest, or a vest and a helmet.`, ru: `Значок брони на HUD игрока: жилет или жилет со шлемом.` },
45
+ fields: { helmet: { en: `\`true\` for a vest and a helmet.`, ru: `\`true\` — жилет со шлемом.` } },
46
+ },
19
47
  BarTime: {
20
- summary: { en: `The progress bar in the middle of a player's screen is shown or hidden.`, ru: `Полосу прогресса посреди экрана игрока показывают или прячут.` },
21
- fields: { seconds },
48
+ summary: { en: `The progress bar in the middle of a player's screen is shown or hidden, from empty or part of the way full.`, ru: `Полосу прогресса посреди экрана игрока показывают или прячут — пустой или заполненной не с нуля.` },
49
+ fields: {
50
+ seconds,
51
+ startPercent: { en: `The bar's fill at the start, in percent.`, ru: `Заполненность полосы в начале, в процентах.` },
52
+ },
22
53
  },
23
54
  Battery: {
24
55
  summary: { en: `A player's armour on his HUD changes.`, ru: `Меняется броня игрока на его HUD.` },
25
56
  fields: { armor: { en: `The armour shown.`, ru: `Показанная броня.` } },
26
57
  },
58
+ BlinkAcct: {
59
+ summary: { en: `A player's money on his HUD blinks: he cannot afford what he tried to buy.`, ru: `Деньги на HUD игрока мигают: ему не хватает на покупку.` },
60
+ fields: { blinks: { en: `The number of blinks.`, ru: `Число миганий.` } },
61
+ },
62
+ BombDrop: {
63
+ summary: { en: `The bomb on the terrorists' radar: dropped or planted.`, ru: `Бомба на радаре террористов: брошена или заложена.` },
64
+ fields: {
65
+ origin: { en: `The point where the bomb lies.`, ru: `Точка, где лежит бомба.` },
66
+ planted: { en: `\`true\` for a bomb planted, \`false\` for one dropped.`, ru: `\`true\` — бомба заложена, \`false\` — брошена.` },
67
+ },
68
+ },
69
+ BombPickup: {
70
+ summary: { en: `The bomb is picked up: it leaves the terrorists' radar.`, ru: `Бомбу подобрали: она пропадает с радара террористов.` },
71
+ },
72
+ BotProgress: {
73
+ summary: { en: `The progress bar a player's client shows while the bots learn a new map.`, ru: `Полоса прогресса, которую клиент игрока показывает, пока боты изучают новую карту.` },
74
+ },
75
+ BotVoice: {
76
+ summary: { en: `The voice icon over a bot who talks on the radio.`, ru: `Значок голоса над ботом, который говорит по рации.` },
77
+ fields: {
78
+ talking: { en: `\`true\` while the bot talks.`, ru: `\`true\`, пока бот говорит.` },
79
+ target: { en: `The bot who talks.`, ru: `Бот, который говорит.` },
80
+ },
81
+ },
82
+ Brass: {
83
+ summary: { en: `A spent shell a weapon throws out, for the clients to draw.`, ru: `Гильза, которую выбрасывает оружие, — клиенты её рисуют.` },
84
+ },
85
+ BuyClose: {
86
+ summary: { en: `A player's buy menu is closed.`, ru: `Меню закупки игрока закрывается.` },
87
+ },
27
88
  ClCorpse: {
28
89
  summary: { en: `A dead player's body is left on the ground for the clients to draw.`, ru: `Тело погибшего игрока остаётся на земле, чтобы клиенты его нарисовали.` },
29
90
  fields: {
30
91
  model: { en: `The body's model, e.g. \`"sas"\`.`, ru: `Модель тела, например \`"sas"\`.` },
92
+ team: { en: `The team of the player whose body it is.`, ru: `Команда игрока, чьё это тело.` },
31
93
  target: { en: `The player whose body it is.`, ru: `Игрок, чьё это тело.` },
32
94
  },
33
95
  },
96
+ Crosshair: {
97
+ summary: { en: `Counter-Strike's own crosshair on a player's screen is shown or hidden.`, ru: `Собственный прицел Counter-Strike на экране игрока показывают или прячут.` },
98
+ fields: { shown: { en: `\`true\` to show it.`, ru: `\`true\` — показать.` } },
99
+ },
34
100
  CurWeapon: {
35
101
  summary: { en: `The weapon in a player's hands and its clip on his HUD.`, ru: `Оружие в руках игрока и его обойма на HUD.` },
36
102
  fields: {
@@ -39,12 +105,19 @@ export const MESSAGES: Record<string, EventDoc> = {
39
105
  clip: { en: `The rounds in the clip.`, ru: `Патроны в обойме.` },
40
106
  },
41
107
  },
108
+ CZCareer: {
109
+ summary: { en: `A step of Condition Zero's career, its single-player campaign.`, ru: `Шаг карьеры Condition Zero, её одиночной кампании.` },
110
+ },
111
+ CZCareerHUD: {
112
+ summary: { en: `Condition Zero's career on a player's HUD.`, ru: `Карьера Condition Zero на HUD игрока.` },
113
+ },
42
114
  Damage: {
43
115
  summary: { en: `A player is shown the damage he took: the red marks at the side of his screen.`, ru: `Игроку показывают полученный урон: красные метки сбоку экрана.` },
44
116
  fields: {
45
117
  armor: { en: `The armour he lost.`, ru: `Потерянная броня.` },
46
118
  damage: { en: `The health he lost.`, ru: `Потерянное здоровье.` },
47
- damageType: { en: `The kinds of damage, e.g. \`"Fall"\`, \`"Bullet"\`.`, ru: `Виды урона, например \`"Fall"\`, \`"Bullet"\`.` },
119
+ damageType: { en: `The kinds of damage, e.g. \`"fall"\`, \`"bullet"\`.`, ru: `Виды урона, например \`"fall"\`, \`"bullet"\`.` },
120
+ origin: { en: `The point the damage came from: the marks point to it.`, ru: `Точка, откуда пришёл урон: метки указывают на неё.` },
48
121
  },
49
122
  },
50
123
  DeathMsg: {
@@ -56,22 +129,80 @@ export const MESSAGES: Record<string, EventDoc> = {
56
129
  weapon: { en: `The weapon's name as the icon shows it, e.g. \`"ak47"\`, \`"grenade"\`.`, ru: `Имя оружия, как его показывает значок, например \`"ak47"\`, \`"grenade"\`.` },
57
130
  },
58
131
  },
132
+ Flashlight: {
133
+ summary: { en: `The flashlight icon on a player's HUD: on or off, and its battery.`, ru: `Значок фонарика на HUD игрока: включён ли он, и его заряд.` },
134
+ fields: {
135
+ on: { en: `\`true\` while the flashlight is on.`, ru: `\`true\`, пока фонарик включён.` },
136
+ battery,
137
+ },
138
+ },
139
+ FlashBat: {
140
+ summary: { en: `The flashlight's battery on a player's HUD changes.`, ru: `Меняется заряд фонарика на HUD игрока.` },
141
+ fields: { battery },
142
+ },
143
+ Fog: {
144
+ summary: { en: `The fog on a player's screen: its colour and density.`, ru: `Туман на экране игрока: его цвет и плотность.` },
145
+ },
146
+ ForceCam: {
147
+ summary: { en: `The views a dead player may watch the game in, as \`mp_forcecamera\` and \`mp_forcechasecam\` allow.`, ru: `Виды, в которых мёртвый игрок может смотреть игру, как разрешают \`mp_forcecamera\` и \`mp_forcechasecam\`.` },
148
+ },
149
+ GameMode: {
150
+ summary: { en: `Whether the game is played in teams, for a player's client.`, ru: `Играют ли командами — для клиента игрока.` },
151
+ },
152
+ GameTitle: {
153
+ summary: { en: `The game's title on a player's screen when he enters the game.`, ru: `Заголовок игры на экране игрока, когда он входит в игру.` },
154
+ },
155
+ Geiger: {
156
+ summary: { en: `The Geiger counter's clicks a player hears near radiation.`, ru: `Щелчки счётчика Гейгера, которые игрок слышит рядом с радиацией.` },
157
+ fields: { range: { en: `The distance to the radiation: the less, the faster the clicks.`, ru: `Расстояние до радиации: чем меньше, тем чаще щелчки.` } },
158
+ },
59
159
  Health: {
60
160
  summary: { en: `A player's health on his HUD changes.`, ru: `Меняется здоровье игрока на его HUD.` },
61
- fields: { health: { en: `The health shown.`, ru: `Показанное здоровье.` } },
161
+ fields: { health },
62
162
  },
63
163
  HideWeapon: {
64
164
  summary: { en: `The parts of a player's HUD that are hidden change.`, ru: `Меняются скрытые части HUD игрока.` },
65
- fields: { flags: { en: `The hidden parts, e.g. \`"Money"\`, \`"Timer"\`. Assign to change them.`, ru: `Скрытые части, например \`"Money"\`, \`"Timer"\`. Присвойте, чтобы изменить.` } },
165
+ fields: { flags: { en: `The hidden parts, e.g. \`"money"\`, \`"timer"\`. Assign to change them.`, ru: `Скрытые части, например \`"money"\`, \`"timer"\`. Присвойте, чтобы изменить.` } },
66
166
  },
67
- HudTextArgs: {
68
- summary: { en: `A hint in the middle of a player's screen, from the game's own texts.`, ru: `Подсказка посреди экрана игрока из собственных текстов игры.` },
69
- fields: { text: { en: `The game's text, e.g. \`"#Hint_press_buy_to_purchase"\`.`, ru: `Текст игры, например \`"#Hint_press_buy_to_purchase"\`.` } },
167
+ HLTV: {
168
+ summary: { en: `A note to the HLTV proxies, such as a new round's start.`, ru: `Заметка для прокси HLTV, например о начале нового раунда.` },
169
+ },
170
+ HostageK: {
171
+ summary: { en: `A hostage is killed: it leaves the counter-terrorists' radar.`, ru: `Заложник убит: он пропадает с радара спецназа.` },
172
+ fields: { hostage },
173
+ },
174
+ HostagePos: {
175
+ summary: { en: `A hostage on the counter-terrorists' radar.`, ru: `Заложник на радаре спецназа.` },
176
+ fields: {
177
+ hostage,
178
+ origin: { en: `The point where the hostage is.`, ru: `Точка, где находится заложник.` },
179
+ },
180
+ },
181
+ HudText: {
182
+ summary: { en: `A hint in the middle of a player's screen.`, ru: `Подсказка посреди экрана игрока.` },
183
+ fields: { text, params },
184
+ },
185
+ InitHUD: {
186
+ summary: { en: `A player's HUD is set up, when he enters the game.`, ru: `HUD игрока готовится, когда он входит в игру.` },
70
187
  },
71
188
  ItemPickup: {
72
189
  summary: { en: `A player picks up an item: the notice at the side of his screen.`, ru: `Игрок подбирает предмет: уведомление сбоку экрана.` },
73
190
  fields: { item: { en: `The item's class name, e.g. \`"item_kevlar"\`.`, ru: `Имя класса предмета, например \`"item_kevlar"\`.` } },
74
191
  },
192
+ ItemStatus: {
193
+ summary: { en: `The night vision and the defuse kit a player has, for his HUD.`, ru: `Прибор ночного видения и набор сапёра, которые есть у игрока, для его HUD.` },
194
+ fields: {
195
+ nightVision: { en: `\`true\` when the player has night vision.`, ru: `\`true\`, когда у игрока есть ночное видение.` },
196
+ defuseKit: { en: `\`true\` when the player has a defuse kit.`, ru: `\`true\`, когда у игрока есть набор сапёра.` },
197
+ },
198
+ },
199
+ Location: {
200
+ summary: { en: `The place on the map a player is in, as the radio names it.`, ru: `Место на карте, где находится игрок, как его называет рация.` },
201
+ fields: {
202
+ target: { en: `The player whose place it is.`, ru: `Игрок, чьё это место.` },
203
+ place: { en: `The place's name, e.g. \`"BombsiteA"\`.`, ru: `Имя места, например \`"BombsiteA"\`.` },
204
+ },
205
+ },
75
206
  Money: {
76
207
  summary: { en: `A player's money on his HUD changes.`, ru: `Меняются деньги игрока на его HUD.` },
77
208
  fields: {
@@ -79,6 +210,33 @@ export const MESSAGES: Record<string, EventDoc> = {
79
210
  flash: { en: `\`true\` to flash the change.`, ru: `\`true\` — мигнуть изменением.` },
80
211
  },
81
212
  },
213
+ MOTD: {
214
+ summary: { en: `A part of the message of the day, the window a joining player sees.`, ru: `Часть сообщения дня — окна, которое видит вошедший игрок.` },
215
+ fields: {
216
+ last: { en: `\`true\` for the last part: the window opens.`, ru: `\`true\` — последняя часть: окно открывается.` },
217
+ text: { en: `The part's text.`, ru: `Текст части.` },
218
+ },
219
+ },
220
+ NVGToggle: {
221
+ summary: { en: `A player's night vision is turned on or off.`, ru: `Ночное видение игрока включают или выключают.` },
222
+ fields: { on: { en: `\`true\` while night vision is on.`, ru: `\`true\`, пока ночное видение включено.` } },
223
+ },
224
+ Radar: {
225
+ summary: { en: `A teammate on a player's radar.`, ru: `Союзник на радаре игрока.` },
226
+ fields: {
227
+ target: { en: `The teammate shown.`, ru: `Показанный союзник.` },
228
+ origin: { en: `The point where the teammate is.`, ru: `Точка, где находится союзник.` },
229
+ },
230
+ },
231
+ ReceiveW: {
232
+ summary: { en: `The weather on the map, rain or snow, for a player's client.`, ru: `Погода на карте, дождь или снег, — для клиента игрока.` },
233
+ },
234
+ ReloadSound: {
235
+ summary: { en: `The sound of a weapon reloaded nearby, for a player's client.`, ru: `Звук перезарядки оружия поблизости — для клиента игрока.` },
236
+ },
237
+ ReqState: {
238
+ summary: { en: `The game asks a player's client for its state, for the voice.`, ru: `Игра запрашивает у клиента игрока его состояние — для голоса.` },
239
+ },
82
240
  ResetHUD: {
83
241
  summary: { en: `A player's HUD is reset, at his spawn.`, ru: `HUD игрока сбрасывается при его появлении.` },
84
242
  },
@@ -91,6 +249,22 @@ export const MESSAGES: Record<string, EventDoc> = {
91
249
  fields: {
92
250
  sender: { en: `The player who wrote it; \`null\` for the server.`, ru: `Игрок, который её написал; \`null\` — сервер.` },
93
251
  text: { en: `The line, or the game's format for it, e.g. \`"#Cstrike_Chat_All"\`.`, ru: `Строка или формат игры для неё, например \`"#Cstrike_Chat_All"\`.` },
252
+ params,
253
+ },
254
+ },
255
+ Scenario: {
256
+ summary: { en: `The scenario icon on a player's HUD, such as the bomb's or a hostage's.`, ru: `Значок сценария на HUD игрока, например бомбы или заложника.` },
257
+ fields: {
258
+ active: { en: `\`true\` while the icon is shown.`, ru: `\`true\`, пока значок показан.` },
259
+ sprite: { en: `The icon's sprite name, e.g. \`"hostage1"\`.`, ru: `Имя спрайта значка, например \`"hostage1"\`.` },
260
+ alpha: { en: `The icon's opacity, \`0\` to \`255\`.`, ru: `Непрозрачность значка, от \`0\` до \`255\`.` },
261
+ },
262
+ },
263
+ ScoreAttrib: {
264
+ summary: { en: `The marks the scoreboard shows beside a player: dead, the bomb, the VIP.`, ru: `Отметки, которые таблица счёта показывает рядом с игроком: погиб, бомба, VIP.` },
265
+ fields: {
266
+ target: { en: `The player whose row it is.`, ru: `Игрок, чья это строка.` },
267
+ flags: { en: `The marks on the row, e.g. \`"dead"\`, \`"bomb"\`, \`"vip"\`.`, ru: `Отметки в строке, например \`"dead"\`, \`"bomb"\`, \`"vip"\`.` },
94
268
  },
95
269
  },
96
270
  ScoreInfo: {
@@ -102,6 +276,25 @@ export const MESSAGES: Record<string, EventDoc> = {
102
276
  team: { en: `The team the row is under.`, ru: `Команда, под которой строка.` },
103
277
  },
104
278
  },
279
+ ScreenFade: {
280
+ summary: { en: `A player's screen is coloured, fading in or out - a flashbang, a fade to black.`, ru: `Экран игрока окрашивается с затуханием — флешка, затемнение.` },
281
+ fields: {
282
+ duration: { en: `The fade's duration, in seconds.`, ru: `Длительность затухания, в секундах.` },
283
+ hold: { en: `The time the full colour holds, in seconds.`, ru: `Время, которое держится полный цвет, в секундах.` },
284
+ direction: { en: `One of \`"in"\`, from the colour to a clear view, or \`"out"\`, from a clear view to the colour.`, ru: `Одно из \`"in"\` — от цвета к чистому экрану, или \`"out"\` — от чистого экрана к цвету.` },
285
+ modulate: { en: `\`true\` when the colour tints the screen rather than painting over it.`, ru: `\`true\`, когда цвет тонирует экран, а не закрашивает его.` },
286
+ stay: { en: `\`true\` when the colour stays until the next fade.`, ru: `\`true\`, когда цвет остаётся до следующего затухания.` },
287
+ color: { en: `The colour: red, green, blue and alpha, \`0\` to \`255\` each.`, ru: `Цвет: красный, зелёный, синий и альфа, от \`0\` до \`255\` каждый.` },
288
+ },
289
+ },
290
+ ScreenShake: {
291
+ summary: { en: `A player's view shakes - an explosion nearby.`, ru: `Вид игрока трясётся — рядом взрыв.` },
292
+ fields: {
293
+ amplitude: { en: `The shake's strength: how far the view moves, up to 16 units.`, ru: `Сила тряски: насколько сдвигается вид, до 16 единиц.` },
294
+ duration: { en: `The shake's duration, in seconds.`, ru: `Длительность тряски, в секундах.` },
295
+ frequency: { en: `The shake's frequency, in jolts a second.`, ru: `Частота тряски, в толчках в секунду.` },
296
+ },
297
+ },
105
298
  SendAudio: {
106
299
  summary: { en: `A sound played to a player, such as a radio line.`, ru: `Звук, который играет игроку, например фраза рации.` },
107
300
  fields: {
@@ -110,22 +303,83 @@ export const MESSAGES: Record<string, EventDoc> = {
110
303
  pitch: { en: `The pitch in percent, \`100\` as recorded.`, ru: `Высота в процентах, \`100\` — как записан.` },
111
304
  },
112
305
  },
306
+ ServerName: {
307
+ summary: { en: `The server's name a player's client shows.`, ru: `Имя сервера, которое показывает клиент игрока.` },
308
+ fields: { serverName: { en: `The server's name, as \`hostname\` sets it.`, ru: `Имя сервера, как его задаёт \`hostname\`.` } },
309
+ },
113
310
  SetFOV: {
114
311
  summary: { en: `A player's field of view is set.`, ru: `Задаётся поле зрения игрока.` },
115
312
  fields: { fov: { en: `The field of view, in degrees.`, ru: `Поле зрения, в градусах.` } },
116
313
  },
314
+ ShadowIdx: {
315
+ summary: { en: `The sprite a player's client draws the players' shadows with.`, ru: `Спрайт, которым клиент игрока рисует тени игроков.` },
316
+ },
317
+ ShowMenu: {
318
+ summary: { en: `A text menu on a player's screen - the team menu without VGUI, the radio, a plugin's menu.`, ru: `Текстовое меню на экране игрока — меню команды без VGUI, рация, меню плагина.` },
319
+ fields: {
320
+ more: { en: `\`true\` when more of the menu's text follows in the next message.`, ru: `\`true\`, когда продолжение текста меню идёт следующим сообщением.` },
321
+ text: { en: `The menu's text, or the game's own for it, e.g. \`"#Team_Select"\`.`, ru: `Текст меню или собственный текст игры, например \`"#Team_Select"\`.` },
322
+ },
323
+ },
324
+ ShowTimer: {
325
+ summary: { en: `The round clock appears on a player's HUD.`, ru: `На HUD игрока появляются часы раунда.` },
326
+ },
327
+ SpecHealth: {
328
+ summary: { en: `The health of the player a spectator watches.`, ru: `Здоровье игрока, за которым следит наблюдатель.` },
329
+ fields: {
330
+ health,
331
+ target: { en: `The player watched.`, ru: `Игрок, за которым следят.` },
332
+ },
333
+ },
334
+ Spectator: {
335
+ summary: { en: `A player becomes a spectator, or stops being one, on the scoreboard.`, ru: `Игрок становится наблюдателем или перестаёт им быть — в таблице счёта.` },
336
+ fields: {
337
+ target: { en: `The player.`, ru: `Игрок.` },
338
+ spectator: { en: `\`true\` while the player spectates.`, ru: `\`true\`, пока игрок наблюдает.` },
339
+ },
340
+ },
117
341
  StatusIcon: {
118
342
  summary: { en: `A status icon on a player's HUD - the buy zone, the bomb - is shown, flashed or hidden.`, ru: `Значок состояния на HUD игрока — зона закупки, бомба — показывается, мигает или прячется.` },
119
343
  fields: {
120
344
  state: { en: `One of \`"hide"\`, \`"show"\` or \`"flash"\`.`, ru: `Одно из \`"hide"\`, \`"show"\` или \`"flash"\`.` },
121
345
  sprite: { en: `The icon's sprite name, e.g. \`"buyzone"\`.`, ru: `Имя спрайта значка, например \`"buyzone"\`.` },
346
+ color: { en: `The icon's colour: red, green and blue, \`0\` to \`255\` each; empty when it is hidden.`, ru: `Цвет значка: красный, зелёный и синий, от \`0\` до \`255\` каждый; пусто, когда он скрыт.` },
347
+ },
348
+ },
349
+ StatusText: {
350
+ summary: { en: `The status line at the bottom of a player's screen, such as the name of the player he aims at.`, ru: `Строка состояния внизу экрана игрока, например имя игрока, в которого он целится.` },
351
+ fields: {
352
+ line: { en: `The status line's number, from \`0\`.`, ru: `Номер строки состояния, с \`0\`.` },
353
+ text: { en: `The line's text, or its format, e.g. \`"1 %c1: %p2"\`.`, ru: `Текст строки или её формат, например \`"1 %c1: %p2"\`.` },
354
+ },
355
+ },
356
+ StatusValue: {
357
+ summary: { en: `A value the status line shows, such as the player a player aims at.`, ru: `Значение, которое показывает строка состояния, например игрок, в которого целится игрок.` },
358
+ fields: {
359
+ slot: { en: `The value's number in the line's format: \`1\` a team, \`2\` a player, \`3\` health.`, ru: `Номер значения в формате строки: \`1\` — команда, \`2\` — игрок, \`3\` — здоровье.` },
360
+ value: { en: `The value.`, ru: `Значение.` },
361
+ },
362
+ },
363
+ TaskTime: {
364
+ summary: { en: `The countdown of a task on a player's HUD, such as rescuing the hostages in a career.`, ru: `Отсчёт задания на HUD игрока, например спасения заложников в карьере.` },
365
+ fields: {
366
+ seconds: { en: `The seconds left.`, ru: `Оставшиеся секунды.` },
367
+ active: { en: `\`true\` while the clock runs.`, ru: `\`true\`, пока часы идут.` },
368
+ fade: { en: `The seconds the clock takes to fade out.`, ru: `Секунды, за которые часы гаснут.` },
122
369
  },
123
370
  },
124
371
  TeamInfo: {
125
372
  summary: { en: `A player's team on the scoreboard.`, ru: `Команда игрока в таблице счёта.` },
126
373
  fields: {
127
374
  target: { en: `The player whose team it is.`, ru: `Игрок, чья это команда.` },
128
- team: { en: `The team's name, e.g. \`"TERRORIST"\`, \`"CT"\`.`, ru: `Имя команды, например \`"TERRORIST"\`, \`"CT"\`.` },
375
+ team: { en: `The team, e.g. \`"TERRORIST"\`, \`"CT"\`.`, ru: `Команда, например \`"TERRORIST"\`, \`"CT"\`.` },
376
+ },
377
+ },
378
+ TeamScore: {
379
+ summary: { en: `A team's score on the scoreboard.`, ru: `Счёт команды в таблице счёта.` },
380
+ fields: {
381
+ team: { en: `The team, \`"TERRORIST"\` or \`"CT"\`.`, ru: `Команда, \`"TERRORIST"\` или \`"CT"\`.` },
382
+ score: { en: `The rounds the team has won.`, ru: `Раунды, которые выиграла команда.` },
129
383
  },
130
384
  },
131
385
  TextMsg: {
@@ -133,6 +387,46 @@ export const MESSAGES: Record<string, EventDoc> = {
133
387
  fields: {
134
388
  destination: { en: `The place it shows, one of \`"chat"\`, \`"center"\`, \`"console"\` or \`"notify"\`.`, ru: `Место, где он виден, одно из \`"chat"\`, \`"center"\`, \`"console"\` или \`"notify"\`.` },
135
389
  text: { en: `The text, or the game's own for it, e.g. \`"#Round_Draw"\`.`, ru: `Текст или собственный текст игры, например \`"#Round_Draw"\`.` },
390
+ params,
391
+ },
392
+ },
393
+ Train: {
394
+ summary: { en: `The speed of the train a player drives, on his HUD.`, ru: `Скорость поезда, которым управляет игрок, на его HUD.` },
395
+ fields: { speed: { en: `The speed step, \`0\` for none.`, ru: `Ступень скорости, \`0\` — никакой.` } },
396
+ },
397
+ TutorClose: {
398
+ summary: { en: `A tutor's message on a player's screen closes.`, ru: `Сообщение подсказчика на экране игрока закрывается.` },
399
+ },
400
+ TutorLine: {
401
+ summary: { en: `A tutor's pointer on a player's screen, at a thing in the world.`, ru: `Указатель подсказчика на экране игрока — на предмет в мире.` },
402
+ },
403
+ TutorState: {
404
+ summary: { en: `The tutor's state on a player's client.`, ru: `Состояние подсказчика на клиенте игрока.` },
405
+ },
406
+ TutorText: {
407
+ summary: { en: `A tutor's message on a player's screen.`, ru: `Сообщение подсказчика на экране игрока.` },
408
+ },
409
+ VGUIMenu: {
410
+ summary: { en: `A VGUI menu of the game opens on a player's screen: the team menu, the class menu, the buy menu.`, ru: `На экране игрока открывается VGUI-меню игры: меню команды, класса, закупки.` },
411
+ fields: { menu: { en: `The menu, e.g. \`"team"\`, \`"classT"\`, \`"buy"\`.`, ru: `Меню, например \`"team"\`, \`"classT"\`, \`"buy"\`.` } },
412
+ },
413
+ ViewMode: {
414
+ summary: { en: `A player's view goes back to first person.`, ru: `Вид игрока возвращается к виду от первого лица.` },
415
+ },
416
+ VoiceMask: {
417
+ summary: { en: `The players a player hears on the voice chat, and the ones he has muted.`, ru: `Игроки, которых игрок слышит в голосовом чате, и те, кого он заглушил.` },
418
+ },
419
+ WeaponList: {
420
+ summary: { en: `A weapon's description for a player's client: its ammo, its slot, its place in the slot.`, ru: `Описание оружия для клиента игрока: его патроны, слот, место в слоте.` },
421
+ fields: {
422
+ classname: { en: `The weapon's class name, e.g. \`"weapon_ak47"\`.`, ru: `Имя класса оружия, например \`"weapon_ak47"\`.` },
423
+ ammo: { en: `The index of the weapon's ammo in the game's list of kinds.`, ru: `Индекс патронов оружия в списке видов у игры.` },
424
+ maxAmmo: { en: `The most of that ammo a player carries.`, ru: `Наибольший запас этих патронов у игрока.` },
425
+ ammo2: { en: `The index of the weapon's second ammo, \`-1\` for none.`, ru: `Индекс вторых патронов оружия, \`-1\` — нет.` },
426
+ maxAmmo2: { en: `The most of the second ammo a player carries.`, ru: `Наибольший запас вторых патронов у игрока.` },
427
+ slot: { en: `The slot the weapon is in, from \`0\`.`, ru: `Слот оружия, с \`0\`.` },
428
+ position: { en: `The weapon's place in the slot, from \`0\`.`, ru: `Место оружия в слоте, с \`0\`.` },
429
+ weapon: { en: `The weapon, by its kind, e.g. \`"ak47"\`.`, ru: `Оружие по его виду, например \`"ak47"\`.` },
136
430
  },
137
431
  },
138
432
  WeapPickup: {
@@ -141,8 +435,28 @@ export const MESSAGES: Record<string, EventDoc> = {
141
435
  },
142
436
  };
143
437
 
144
- /** The words of a message that has no entry, and of the receiver every message has. */
145
- export const ANY_MESSAGE: { summary: Text; player: Text } = {
146
- summary: { en: `A message the server sends its clients; its arguments are \`event.args\`.`, ru: `Сообщение, которое сервер шлёт клиентам; его аргументы — \`event.args\`.` },
438
+ /** Names in code, joined as a sentence lists them: `A`, `B` and `C`. */
439
+ function listed(names: string[], and: string) {
440
+ const quoted = names.map(name => `\`${name}\``);
441
+ return quoted.length > 1 ? `${quoted.slice(0, -1).join(`, `)} ${and} ${quoted.at(-1)}` : quoted[0];
442
+ }
443
+
444
+ /** The line every message's words end with: the game's names of the messages it hears. */
445
+ export function gameName(names: string[]): Text {
446
+ if (names.length === 1) return { en: `The game's \`${names[0]}\` message.`, ru: `Сообщение игры \`${names[0]}\`.` };
447
+ return { en: `The game's ${listed(names, `and`)} messages.`, ru: `Сообщения игры ${listed(names, `и`)}.` };
448
+ }
449
+
450
+ /** The words of a field only some of a name's messages carry: what it reads as on the others. */
451
+ export function missingField(names: string[], value: string): Text {
452
+ return {
453
+ en: `${listed(names, `or`)} does not carry it: there it reads as \`${value}\`, and writing it does nothing.`,
454
+ ru: `В ${listed(names, `и`)} его нет: там он читается как \`${value}\`, а запись ничего не делает.`,
455
+ };
456
+ }
457
+
458
+ /** The words of a message without fields, and of the receiver every message has. */
459
+ export const ANY_MESSAGE: { args: Text; player: Text } = {
460
+ args: { en: `Its arguments are read by place, through \`event.args\`.`, ru: `Его аргументы читаются по месту, через \`event.args\`.` },
147
461
  player: receiver,
148
462
  };
@@ -0,0 +1,101 @@
1
+ import type { Lang } from './apply-docs';
2
+ import type { Project } from './project';
3
+ // What an editor reads of the core and the project's modules, with the
4
+ // tooltips in the language AMXTS_DOCS_LANG picks: .amxts/api/ in the project,
5
+ // written by `amxts prepare`.
6
+ //
7
+ // The installed packages are never written to. A package manager may link
8
+ // them from a store every project on the machine shares (pnpm, bun), a
9
+ // reinstall puts them back, and the build compiles them as they are. They
10
+ // carry English, so English needs no copy: the editor reads the packages.
11
+ // Another language gets a copy of what the editor reads - the core's as/ and
12
+ // the files each module's module file reaches - with the words written in
13
+ // (scripts/apply-docs.ts), and .amxts/tsconfig.json points `@amxts/core`, its
14
+ // entries and the modules' names at it. The core is copied whole, not only
15
+ // the files with words: the facade and what it imports are one program, and
16
+ // a copy that imported an original would give the editor two Players.
17
+ //
18
+ // .amxts/api/core/facade.ts the core's as/, the words in the language
19
+ // .amxts/api/menu-core/src/... a module's files
20
+ // .amxts/api/stamp.json what they were made from: the copy is
21
+ // written again only when that changes
22
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
23
+ import { dirname, join, relative, resolve } from 'node:path';
24
+ import { applyTable, docsFileOf, loadTable } from './apply-docs';
25
+ import { CORE_DIR, CORE_ENTRIES, CORE_PLUGINS } from './project';
26
+
27
+ /** What .amxts/tsconfig.json takes from the copy. */
28
+ export interface EditorApi {
29
+ /** Module names and the copied files they are, absolute. */
30
+ paths: Record<string, string>;
31
+ /** The copied core's folder, whose `.d.ts` files declare the globals. */
32
+ core: string;
33
+ /** Whether the copy was written now rather than found up to date. */
34
+ written: boolean;
35
+ }
36
+
37
+ /** A tree of files to copy: from its folder on disk to its folder in the copy, with its package's folder for the words. */
38
+ interface Tree {
39
+ root: string;
40
+ from: string;
41
+ to: string;
42
+ files: string[];
43
+ }
44
+
45
+ function filesUnder(dir: string): string[] {
46
+ return readdirSync(dir, { recursive: true, withFileTypes: true })
47
+ .filter(entry => entry.isFile() && entry.name.endsWith('.ts'))
48
+ .map(entry => join(entry.parentPath, entry.name));
49
+ }
50
+
51
+ /** The files `file` reaches by relative imports inside `dir`, itself first. */
52
+ function reached(file: string, dir: string, seen: string[] = []): string[] {
53
+ if (seen.includes(file) || !existsSync(file) || relative(dir, file).startsWith('..')) return seen;
54
+ seen.push(file);
55
+ for (const [, spec] of readFileSync(file, 'utf8').matchAll(/(?:from|import)\s*(?:\(\s*)?["'](\.{1,2}\/[^"']+)["']/g)) {
56
+ const target = resolve(dirname(file), spec);
57
+ const found = [target, `${target}.ts`, join(target, 'index.ts')].find(each => existsSync(each) && statSync(each).isFile());
58
+ if (found) reached(found, dir, seen);
59
+ }
60
+ return seen;
61
+ }
62
+
63
+ /**
64
+ * Writes the editor's copy of the API for `lang` under `dir`, or removes it
65
+ * for English; what the tsconfig maps to it, null for none.
66
+ */
67
+ export async function editorApi(project: Project, lang: Lang, dir: string): Promise<EditorApi | null> {
68
+ if (lang === 'en') {
69
+ rmSync(dir, { recursive: true, force: true });
70
+ return null;
71
+ }
72
+
73
+ const core = join(dir, 'core');
74
+ const trees: Tree[] = [
75
+ { root: CORE_DIR, from: CORE_PLUGINS, to: core, files: filesUnder(CORE_PLUGINS) },
76
+ ...project.modules.map(pkg => ({ root: pkg.dir, from: pkg.dir, to: join(dir, pkg.short), files: reached(pkg.module, pkg.dir) })),
77
+ ];
78
+ const paths: Record<string, string> = Object.fromEntries(Object.entries(CORE_ENTRIES).map(([name, file]) => [name, join(core, file)]));
79
+ for (const pkg of project.modules) paths[pkg.name] = join(dir, pkg.short, relative(pkg.dir, pkg.module));
80
+
81
+ // What the copy is made of, by size and time: a new version, a module
82
+ // added or a local file saved writes it again.
83
+ const inputs = trees.flatMap(tree => tree.files.flatMap(file => [file, docsFileOf(tree.root, file)]).filter(existsSync));
84
+ const stamp = JSON.stringify({ lang, inputs: inputs.map((file) => {
85
+ const stat = statSync(file);
86
+ return [file, stat.size, stat.mtimeMs];
87
+ }) });
88
+ const stampFile = join(dir, 'stamp.json');
89
+ if (existsSync(stampFile) && readFileSync(stampFile, 'utf8') === stamp) return { paths, core, written: false };
90
+
91
+ rmSync(dir, { recursive: true, force: true });
92
+ const copies = trees.flatMap(tree => tree.files.map(file => ({ file, docs: docsFileOf(tree.root, file), to: join(tree.to, relative(tree.from, file)) })));
93
+ const tables = await Promise.all(copies.map(copy => (existsSync(copy.docs) ? loadTable(copy.docs) : null)));
94
+ for (const [i, copy] of copies.entries()) {
95
+ const text = readFileSync(copy.file, 'utf8');
96
+ mkdirSync(dirname(copy.to), { recursive: true });
97
+ writeFileSync(copy.to, tables[i] ? applyTable(copy.file, text, tables[i], lang) : text);
98
+ }
99
+ writeFileSync(stampFile, stamp);
100
+ return { paths, core, written: true };
101
+ }