@amxts/core 0.1.0 → 0.2.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 +18 -23
- package/README.ru.md +19 -22
- package/as/amxts.d.ts +63 -3
- package/as/constants.ts +684 -116
- package/as/entities.ts +1905 -1364
- package/as/events.ts +1694 -905
- package/as/facade.ts +1240 -220
- package/as/fetch.ts +1519 -0
- package/as/flags.ts +159 -145
- package/as/hlds.ts +1071 -0
- package/as/hooks.ts +5226 -5379
- package/as/kit.ts +15 -5
- package/as/lib/check.ts +1 -1
- package/as/natives.ts +2658 -2499
- package/as/remote.ts +39 -15
- package/as/tsconfig.json +6 -2
- package/as/vector.ts +12 -9
- package/includes/order.txt +22 -29
- package/includes/sources.json +0 -8
- package/node_modules/assemblyscript/std/assembly/index.d.ts +20 -11
- package/package.json +29 -7
- package/runtime/deps/assemblyscript/dist/asc.js +86 -16
- package/runtime/deps/assemblyscript/dist/assemblyscript.js +29 -29
- package/runtime/licenses/MPL-2.0.txt +373 -0
- package/runtime/licenses/README.md +5 -1
- package/runtime/licenses/curl-COPYING +22 -0
- package/runtime/licenses/libssh2-COPYING +43 -0
- package/runtime/licenses/mbedTLS-LICENSE +553 -0
- package/runtime/natives.txt +108 -105
- package/runtime/patches/assemblyscript-0.28.20-amxts.patch +752 -296
- package/runtime/patches/wamr-2.4.5-amxts.patch +148 -0
- package/scripts/apply-docs.ts +2 -2
- package/scripts/auto-imports.ts +2 -2
- package/scripts/build-identity.ts +115 -0
- package/scripts/build-wasm.ts +101 -50
- package/scripts/check.ts +17 -3
- package/scripts/client-messages.ts +246 -0
- package/scripts/compile-pool.ts +128 -0
- package/scripts/compile-worker.ts +23 -0
- package/scripts/compile.ts +156 -59
- package/scripts/docker-server.ts +13 -3
- package/scripts/docs/as/amxts.ts +86 -0
- package/scripts/docs/as/facade.ts +688 -188
- package/scripts/docs/as/fetch.ts +805 -0
- package/scripts/docs/entities.ts +43 -144
- package/scripts/docs/events.ts +15 -26
- package/scripts/docs/game.ts +68 -22
- package/scripts/docs/messages.ts +330 -16
- package/scripts/editor-api.ts +101 -0
- package/scripts/hlds-events.ts +370 -0
- package/scripts/includes.ts +12 -10
- package/scripts/player-fields.ts +16 -15
- package/scripts/prebuilt.ts +65 -23
- package/scripts/prepare.ts +39 -28
- package/scripts/project.ts +120 -20
- package/scripts/reapi-events.ts +98 -0
- package/scripts/shared-modules.ts +85 -23
- package/scripts/source-map.ts +242 -0
- package/scripts/system.ts +1 -1
- package/scripts/typed-commands.ts +263 -0
- package/scripts/typed-configs.ts +23 -12
- package/scripts/ui.ts +3 -1
- package/scripts/upgrade-menus.ts +274 -0
- package/scripts/upgrade-names.ts +384 -0
- package/scripts/upgrade.ts +830 -0
- package/src/cli-api.mjs +37 -4
- package/src/system.mjs +74 -3
- package/src/testing/compile.ts +8 -5
- package/src/testing/natives.ts +199 -5
- package/src/testing/network.ts +531 -0
- package/src/testing/server.ts +565 -47
- package/src/testing/tables.ts +25 -0
- package/as/modules/http.ts +0 -184
- package/scripts/docs/as/modules/http.ts +0 -66
package/scripts/docs/messages.ts
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
// Our own words for the messages the server sends its clients, as
|
|
2
|
-
// server.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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: {
|
|
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. \`"
|
|
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
|
|
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. \`"
|
|
165
|
+
fields: { flags: { en: `The hidden parts, e.g. \`"money"\`, \`"timer"\`. Assign to change them.`, ru: `Скрытые части, например \`"money"\`, \`"timer"\`. Присвойте, чтобы изменить.` } },
|
|
66
166
|
},
|
|
67
|
-
|
|
68
|
-
summary: { en: `A
|
|
69
|
-
|
|
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
|
|
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
|
-
/**
|
|
145
|
-
|
|
146
|
-
|
|
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
|
+
}
|