@amxts/menu-core 0.2.1 → 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.
- package/PAWN.md +2 -2
- package/PAWN.ru.md +2 -2
- package/README.md +8 -5
- package/README.ru.md +8 -5
- package/package.json +3 -3
- package/prebuilt/linux/menu-core.aot +0 -0
- package/prebuilt/manifest.json +36 -35
- package/prebuilt/windows/menu-core.aot +0 -0
- package/scripts/docs/src/menus.ts +18 -2
- package/scripts/docs/src/types.ts +4 -4
- package/src/internal.ts +4 -0
- package/src/menu-file.ts +13 -5
- package/src/menus.ts +54 -15
- package/src/natives.ts +411 -411
- package/src/types.ts +4 -4
package/src/natives.ts
CHANGED
|
@@ -1,411 +1,411 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Menu Core for Pawn plugins: the 30 mc_* natives of the original
|
|
3
|
-
* menu_core.amxx, as include/menu_core.inc declares them.
|
|
4
|
-
*/
|
|
5
|
-
import { Player, plugin } from "@amxts/core";
|
|
6
|
-
import {
|
|
7
|
-
PawnFunction,
|
|
8
|
-
caller,
|
|
9
|
-
cellArrayRows,
|
|
10
|
-
cellsText,
|
|
11
|
-
colorTags,
|
|
12
|
-
createCellArray,
|
|
13
|
-
destroyCellArray,
|
|
14
|
-
menuColors,
|
|
15
|
-
pushCellArrayRow,
|
|
16
|
-
textCells,
|
|
17
|
-
} from "@amxts/core/kit";
|
|
18
|
-
import * as menus from "./index";
|
|
19
|
-
import { addNamedFilter, iniItem, insertItem, stateOf } from "./internal";
|
|
20
|
-
import { MenuShowOptions } from "./types";
|
|
21
|
-
|
|
22
|
-
plugin({ name: "Menu Core", version: "0.
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Menu properties: mc_set_menu_property takes MP_LOCKED .. MP_HIDE_EXIT,
|
|
26
|
-
* mc_set_menu_property_string MP_ON_TIMEOUT .. MP_FILTER, and
|
|
27
|
-
* mc_get_menu_property_string MP_SECTION.
|
|
28
|
-
*/
|
|
29
|
-
export enum MenuProperty {
|
|
30
|
-
MP_LOCKED = 0,
|
|
31
|
-
MP_GLOBAL_TIMER = 1,
|
|
32
|
-
MP_TIMER_DURATION = 2,
|
|
33
|
-
MP_HIDE_BACK = 3,
|
|
34
|
-
MP_HIDE_EXIT = 4,
|
|
35
|
-
// oxlint-disable-next-line typescript/no-duplicate-enum-values -- the include's own: 5 is read as the section and written as the timeout action (oxlint reports the first of the two)
|
|
36
|
-
MP_SECTION = 5,
|
|
37
|
-
MP_ON_TIMEOUT = 5,
|
|
38
|
-
MP_ACTIVE_ON = 6,
|
|
39
|
-
MP_FILTER = 7,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** A list data source's item: 289 cells, laid out as menu_core.inc says. */
|
|
43
|
-
const ROW_CELLS = 289;
|
|
44
|
-
const PLACEHOLDER_CELLS = 256;
|
|
45
|
-
|
|
46
|
-
// What each native has registered: the numbers the originals return.
|
|
47
|
-
let conditionCount = 0;
|
|
48
|
-
let actionCount = 0;
|
|
49
|
-
let restrictionCount = 0;
|
|
50
|
-
let filterCount = 0;
|
|
51
|
-
let openCount = 0;
|
|
52
|
-
let closeCount = 0;
|
|
53
|
-
let showCount = 0;
|
|
54
|
-
|
|
55
|
-
/** A public of the plugin calling the native that is running; null when it has none of that name. */
|
|
56
|
-
function publicOf(name: string) {
|
|
57
|
-
return PawnFunction.find(caller(), name);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** A data source fills an `Array:` of 289-cell items; one that does not return 1 is not used. */
|
|
61
|
-
function rowsFrom(fn: PawnFunction, viewer: Player) {
|
|
62
|
-
const items = createCellArray(ROW_CELLS);
|
|
63
|
-
const answered = fn.call().int(viewer.id).int(items).run();
|
|
64
|
-
const rows = answered == 1 ? cellArrayRows(items, ROW_CELLS).map(rowOf) : null;
|
|
65
|
-
destroyCellArray(items);
|
|
66
|
-
return rows;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** [0] target, or -2 for a line of text; [1..64] action; [65..128] text; [129..160] restriction; [161..288] message. */
|
|
70
|
-
function rowOf(cells: number[]) {
|
|
71
|
-
const text = colorTags(cellsText(cells, 65, 64));
|
|
72
|
-
if (cells[0] == -2) return menus.textRow(text);
|
|
73
|
-
return menus.listRow(cells[0], text, cellsText(cells, 1, 64), cellsText(cells, 129, 32), colorTags(cellsText(cells, 161, 128)));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** 1 or 0, as the original's natives answer. */
|
|
77
|
-
function answer(done: boolean) {
|
|
78
|
-
return done ? 1 : 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Registers a condition menus refer to - an item's condition, ACTIVE_ON, a
|
|
83
|
-
* FILTER. The first plugin to register a name answers for it (case-insensitive).
|
|
84
|
-
* Callback: public callback(id, viewerId, const name[]) - true when it holds.
|
|
85
|
-
* Returns the condition's id.
|
|
86
|
-
*/
|
|
87
|
-
export function mc_register_condition(name: string, callback: string) {
|
|
88
|
-
const fn = publicOf(callback);
|
|
89
|
-
if (fn != null) menus.addCondition(name, (player, viewer, used) => fn.call().int(player.id).int(viewer.id).text(used).run() != 0);
|
|
90
|
-
return conditionCount++;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Registers an action run when an item is chosen. Built in, not registered:
|
|
95
|
-
* SHOW_<SECTION> opens that menu, CLOSE_MENU closes it; an action line may
|
|
96
|
-
* list several, space-separated. Names are case-sensitive; the first wins.
|
|
97
|
-
* Callback: public callback(id, const action[]) in an items menu,
|
|
98
|
-
* public callback(id, targetId) in a LIST_ menu. isCritical is accepted and
|
|
99
|
-
* does nothing. Returns the action's id.
|
|
100
|
-
*/
|
|
101
|
-
export function mc_register_action(name: string, callback: string, _isCritical = false) {
|
|
102
|
-
const fn = publicOf(callback);
|
|
103
|
-
|
|
104
|
-
if (fn != null) {
|
|
105
|
-
menus.addAction(name, ({ player, row, menu }) => {
|
|
106
|
-
const call = fn.call().int(player.id);
|
|
107
|
-
// An items menu's action gets the action's name, a list menu's the row's target.
|
|
108
|
-
if (menu.kind == "items") call.text(name);
|
|
109
|
-
else call.int(row);
|
|
110
|
-
call.run();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return actionCount++;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Registers a placeholder, %name% in titles and items, without the %.
|
|
119
|
-
* Callback: public callback(id, targetId, value[], len) - fills value[].
|
|
120
|
-
* A name registered again keeps the first. Returns the placeholder's id.
|
|
121
|
-
*/
|
|
122
|
-
export function mc_register_placeholder(name: string, callback: string) {
|
|
123
|
-
const fn = publicOf(callback);
|
|
124
|
-
return menus.addPlaceholder(name, ({ player, row }) => {
|
|
125
|
-
if (fn == null) return "";
|
|
126
|
-
const call = fn.call().int(player.id).int(row).buffer(PLACEHOLDER_CELLS).int(PLACEHOLDER_CELLS - 1);
|
|
127
|
-
call.run();
|
|
128
|
-
return colorTags(call.bufferText);
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Registers the config file's [section] as a menu. Returns the menu's id, or
|
|
134
|
-
* -1 when the section is missing, has no TITLE or no items, or is MAIN.
|
|
135
|
-
*/
|
|
136
|
-
export function mc_register_menu(section: string) {
|
|
137
|
-
return menus.indexOf(menus.register(section));
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Shows a menu. time: countdown seconds, -1 for the menu's TIME (or the
|
|
142
|
-
* countdown running). targetId: who a LIST_ menu is about. resetHistory
|
|
143
|
-
* starts the way back anew; forceOpen opens over a menu that holds on (a
|
|
144
|
-
* countdown, a lock); ignoreHistory leaves this menu out of the way back.
|
|
145
|
-
* Returns 1 when it opened; 0 when the player is not in the game, there is no
|
|
146
|
-
* such menu, a show filter or ACTIVE_ON stopped it.
|
|
147
|
-
*/
|
|
148
|
-
export function mc_show_menu(player: Player, section: string, time = -1, targetId = 0, resetHistory = false, forceOpen = false, ignoreHistory = false) {
|
|
149
|
-
const options: MenuShowOptions = { resetHistory, force: forceOpen, skipHistory: ignoreHistory };
|
|
150
|
-
// -1, Pawn's "not given": the option left out.
|
|
151
|
-
if (time >= 0) options.time = time;
|
|
152
|
-
if (targetId > 0) options.target = new Player(targetId);
|
|
153
|
-
return answer(menus.show(player, section, options));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Sets the menu's shared countdown: starts one of `time` seconds when none
|
|
158
|
-
* runs, or changes the seconds left (0 stops it). Returns 1, or 0 when there
|
|
159
|
-
* is no such menu or nothing to change.
|
|
160
|
-
*/
|
|
161
|
-
export function mc_set_menu_timer(section: string, time: number) {
|
|
162
|
-
const menu = menus.find(section);
|
|
163
|
-
return answer(menu != null && menu.setTimer(time));
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Draws again every menu that uses the condition, for whoever looks at it. */
|
|
167
|
-
export function mc_notify_condition_changed(condition: string) {
|
|
168
|
-
menus.conditionChanged(condition);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/** Draws the menus again (space-separated names) for whoever looks at them. Returns how many were drawn. */
|
|
172
|
-
export function mc_refresh_menu(sections: string) {
|
|
173
|
-
return menus.refresh(sections);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Registers a restriction items name in their restriction column; "*"
|
|
178
|
-
* answers for every name no other restriction does. Callback:
|
|
179
|
-
* public callback(id, const restrictName[], targetId) - true when the player
|
|
180
|
-
* passes. message is said beside an item it greys out, when the item has no
|
|
181
|
-
* message of its own. Returns the restriction's id.
|
|
182
|
-
*/
|
|
183
|
-
export function mc_register_restriction(name: string, callback: string, message?: string) {
|
|
184
|
-
const fn = publicOf(callback);
|
|
185
|
-
if (fn != null) menus.addRestriction(name, ({ player, name: used, row }) => fn.call().int(player.id).text(used).int(row).run() != 0, colorTags(message ?? ""));
|
|
186
|
-
return restrictionCount++;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Greys out items whose action is actionName in menuSection while the
|
|
191
|
-
* callback says no; "" for either matches all. Callback:
|
|
192
|
-
* public callback(id, const menuSection[], const actionName[]). Returns 1.
|
|
193
|
-
*/
|
|
194
|
-
export function mc_register_action_condition(menuSection: string, actionName: string, callback: string) {
|
|
195
|
-
const fn = publicOf(callback);
|
|
196
|
-
if (fn != null) menus.addActionCheck(menuSection, actionName, ({ player, menu, name: action }) => fn.call().int(player.id).text(menu.name).text(action).run() != 0);
|
|
197
|
-
return 1;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Registers a filter over a condition someone else registered: it gets the
|
|
202
|
-
* condition's value and returns the one to use. Callback:
|
|
203
|
-
* public bool:callback(id, viewerId, const condition[], bool:currentVal).
|
|
204
|
-
* Returns the filter's id.
|
|
205
|
-
*/
|
|
206
|
-
export function mc_register_condition_filter(condition: string, callback: string) {
|
|
207
|
-
const fn = publicOf(callback);
|
|
208
|
-
if (fn == null) return filterCount - 1;
|
|
209
|
-
menus.addConditionFilter(condition, (player, viewer, name, value) => fn.call().int(player.id).int(viewer.id).text(name).bool(value).run() != 0);
|
|
210
|
-
return filterCount++;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* The rows of a LIST_ menu, instead of the players. Callback:
|
|
215
|
-
* public callback(id, Array:items) - pushes 289-cell items and returns 1:
|
|
216
|
-
* [0] the target the action gets (-2: a line of text), [1..64] an action of
|
|
217
|
-
* its own, [65..128] the text (%name%), [129..160] a restriction,
|
|
218
|
-
* [161..288] the message when it greys the row out. A second source for the
|
|
219
|
-
* menu replaces the first. Returns the source's id.
|
|
220
|
-
*/
|
|
221
|
-
export function mc_register_list_data_source(menuName: string, callback: string) {
|
|
222
|
-
const fn = publicOf(callback);
|
|
223
|
-
return menus.setListSource(menuName, ({ player }) => (fn != null ? rowsFrom(fn, player) : null));
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/** Called when a menu opens. Callback: public callback(id, const section[]). Returns its id. */
|
|
227
|
-
export function mc_register_menu_open_callback(callback: string) {
|
|
228
|
-
const fn = publicOf(callback);
|
|
229
|
-
if (fn == null) return openCount - 1;
|
|
230
|
-
menus.addEventListener("open", event => fn.call().int(event.player.id).text(event.menu.name).run());
|
|
231
|
-
return openCount++;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Called when a menu closes - by a key, a timeout, another menu opening over
|
|
236
|
-
* it, the player leaving. Callback: public callback(id, const section[], bool:isTimeout).
|
|
237
|
-
* Returns its id.
|
|
238
|
-
*/
|
|
239
|
-
export function mc_register_menu_close_callback(callback: string) {
|
|
240
|
-
const fn = publicOf(callback);
|
|
241
|
-
if (fn == null) return closeCount - 1;
|
|
242
|
-
menus.addEventListener("close", event => fn.call().int(event.player.id).text(event.menu.name).bool(event.timeout).run());
|
|
243
|
-
return closeCount++;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Asked before any menu opens; returning false stops it (mc_show_menu returns
|
|
248
|
-
* 0) - the place to tell the player why. Callback:
|
|
249
|
-
* public bool:callback(id, const section[]). Returns its id.
|
|
250
|
-
*/
|
|
251
|
-
export function mc_register_show_filter(callback: string) {
|
|
252
|
-
const fn = publicOf(callback);
|
|
253
|
-
if (fn == null) return showCount - 1;
|
|
254
|
-
// The first filter that says no stops the menu; the ones after it are not asked.
|
|
255
|
-
menus.addEventListener("show", (event) => {
|
|
256
|
-
if (!event.defaultPrevented && fn.call().int(event.player.id).text(event.menu.name).run() == 0) event.preventDefault();
|
|
257
|
-
});
|
|
258
|
-
return showCount++;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** Stops the menu's shared countdown and closes it for everyone. Returns 1, or 0 when none ran. */
|
|
262
|
-
export function mc_cancel_menu_timer(section: string) {
|
|
263
|
-
const menu = menus.find(section);
|
|
264
|
-
return answer(menu != null && menu.cancelTimer());
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** Locks (or unlocks) the player's menu: nothing can be chosen, and no other menu replaces it. */
|
|
268
|
-
export function mc_lock_menu(player: Player, lock = true) {
|
|
269
|
-
if (player.isConnected) menus.lock(player, lock);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/** Closes the player's menu. */
|
|
273
|
-
export function mc_hide_menu(player: Player) {
|
|
274
|
-
if (player.isConnected) menus.close(player);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/** Whether the player's menu is locked. */
|
|
278
|
-
export function mc_is_menu_locked(player: Player) {
|
|
279
|
-
return player.isConnected && menus.isLocked(player);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/** The id of the menu the player looks at, or -1. */
|
|
283
|
-
export function mc_get_active_menu(id: number) {
|
|
284
|
-
const player = new Player(id);
|
|
285
|
-
return player.isConnected ? menus.indexOf(menus.activeMenu(player)) : -1;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* A string property of a menu by its id (mc_get_active_menu): MP_SECTION, the
|
|
290
|
-
* section name, into out[]. Returns its length; 0 when the id or the property
|
|
291
|
-
* is not one.
|
|
292
|
-
*/
|
|
293
|
-
export function mc_get_menu_property_string(menuIdx: number, property: MenuProperty) {
|
|
294
|
-
const menu = menus.menuAt(menuIdx);
|
|
295
|
-
// 4 is what the original compared with; 5 is the include's MP_SECTION.
|
|
296
|
-
if (menu == null || (property != MenuProperty.MP_SECTION && property != 4)) return "";
|
|
297
|
-
return menu.name;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Sets MP_LOCKED, MP_GLOBAL_TIMER, MP_TIMER_DURATION, MP_HIDE_BACK or
|
|
302
|
-
* MP_HIDE_EXIT. Returns 1, or 0 when there is no such menu or property.
|
|
303
|
-
*/
|
|
304
|
-
export function mc_set_menu_property(section: string, property: MenuProperty, value: number) {
|
|
305
|
-
const menu = menus.find(section);
|
|
306
|
-
if (menu == null) return 0;
|
|
307
|
-
const on = value != 0;
|
|
308
|
-
if (property == MenuProperty.MP_LOCKED) menu.locked = on;
|
|
309
|
-
else if (property == MenuProperty.MP_GLOBAL_TIMER) menu.sharedTimer = on;
|
|
310
|
-
else if (property == MenuProperty.MP_TIMER_DURATION) menu.time = value;
|
|
311
|
-
else if (property == MenuProperty.MP_HIDE_BACK) menu.hideBack = on;
|
|
312
|
-
else if (property == MenuProperty.MP_HIDE_EXIT) menu.hideExit = on;
|
|
313
|
-
else return 0;
|
|
314
|
-
return 1;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Sets MP_ON_TIMEOUT (the action when the countdown ends), MP_ACTIVE_ON (the
|
|
319
|
-
* condition the menu opens under) or MP_FILTER ("CONDITION|MESSAGE", added to
|
|
320
|
-
* a LIST_ menu's filters). Returns 1, or 0 when there is no such menu or
|
|
321
|
-
* property.
|
|
322
|
-
*/
|
|
323
|
-
export function mc_set_menu_property_string(section: string, property: MenuProperty, value: string) {
|
|
324
|
-
const menu = menus.find(section);
|
|
325
|
-
if (menu == null) return 0;
|
|
326
|
-
if (property == MenuProperty.MP_ON_TIMEOUT) menu.onTimeout = value;
|
|
327
|
-
else if (property == MenuProperty.MP_ACTIVE_ON) menu.activeOn = value;
|
|
328
|
-
else if (property == MenuProperty.MP_FILTER) addFilter(menu, value);
|
|
329
|
-
else return 0;
|
|
330
|
-
return 1;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function addFilter(menu: menus.Menu, value: string) {
|
|
334
|
-
if (menu.kind != "list") return;
|
|
335
|
-
const pipe = value.indexOf("|");
|
|
336
|
-
if (pipe < 0) addNamedFilter(menu.name, value.trim(), "");
|
|
337
|
-
else addNamedFilter(menu.name, value.slice(0, pipe).trim(), colorTags(value.slice(pipe + 1).trim()));
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Adds an item to a menu. name, condition and action take "A|B" variants: the
|
|
342
|
-
* first whose condition holds is shown. iPosition: its place among the items,
|
|
343
|
-
* -1 the end; emptyBefore/emptyAfter: blank lines around it. Returns 1, or 0
|
|
344
|
-
* when there is no such menu or the name gives no item.
|
|
345
|
-
*/
|
|
346
|
-
export function mc_add_menu_item(section: string, name: string, placeholder?: string, condition?: string, action?: string, restriction?: string, restrictMsg?: string, iPosition = -1, emptyBefore = 0, emptyAfter = 0) {
|
|
347
|
-
const menu = menus.find(section);
|
|
348
|
-
if (menu == null) return 0;
|
|
349
|
-
if (!menus.hasText(name)) return 0;
|
|
350
|
-
const item = iniItem(colorTags(name), colorTags(placeholder ?? ""), condition ?? "", action ?? "", restriction ?? "", colorTags(restrictMsg ?? ""), -1);
|
|
351
|
-
item.spaceBefore = emptyBefore;
|
|
352
|
-
item.spaceAfter = emptyAfter;
|
|
353
|
-
insertItem(menu.name, item, iPosition);
|
|
354
|
-
return 1;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Adds an item that always takes slot 1-7 of every page. Returns 1, or 0 when
|
|
359
|
-
* there is no such menu or the name gives no item.
|
|
360
|
-
*/
|
|
361
|
-
export function mc_add_fixed_menu_item(section: string, slot: number, name: string, placeholder?: string, action?: string, condition?: string, restriction?: string, emptyBefore = 0, emptyAfter = 0) {
|
|
362
|
-
const menu = menus.find(section);
|
|
363
|
-
if (menu == null) return 0;
|
|
364
|
-
if (!menus.hasText(name)) return 0;
|
|
365
|
-
const item = iniItem(colorTags(name), colorTags(placeholder ?? ""), condition ?? "", action ?? "", restriction ?? "", "", slot - 1);
|
|
366
|
-
item.spaceBefore = emptyBefore;
|
|
367
|
-
item.spaceAfter = emptyAfter;
|
|
368
|
-
stateOf(menu.name).fixed.push(item);
|
|
369
|
-
return 1;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Adds a line of text - a header, a separator - to the items a data source
|
|
374
|
-
* fills; call it from the data source callback. Returns 1.
|
|
375
|
-
*/
|
|
376
|
-
export function mc_add_list_text(aItems: number, text: string, centered = false) {
|
|
377
|
-
const row = menus.textRow(text, centered);
|
|
378
|
-
const cells = [-2].concat(textCells("", 64)).concat(textCells(row.text, 64)).concat(textCells("", 32)).concat(textCells("", 128));
|
|
379
|
-
pushCellArrayRow(aItems, cells);
|
|
380
|
-
return 1;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/** Removes every item of a menu, fixed ones too. Returns 1, or 0 when there is no such menu. */
|
|
384
|
-
export function mc_clear_menu_items(section: string) {
|
|
385
|
-
const menu = menus.find(section);
|
|
386
|
-
if (menu != null) menu.clearItems();
|
|
387
|
-
return answer(menu != null);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Makes a menu in code, without a config section; a name starting with LIST_
|
|
392
|
-
* makes a list menu. Returns 1, or 0 when a menu of that name is there.
|
|
393
|
-
*/
|
|
394
|
-
export function mc_create_menu(section: string, title: string) {
|
|
395
|
-
if (menus.find(section) != null) return 0;
|
|
396
|
-
menus.create(section, { title: colorTags(title) });
|
|
397
|
-
return 1;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/** The page, from 0, the player's menu is drawn at next. */
|
|
401
|
-
export function mc_set_menu_page(player: Player, page: number) {
|
|
402
|
-
if (player.isConnected) menus.setPage(player, page);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* The text the player's menu shows at the moment, colour codes and all (""
|
|
407
|
-
* when none is open) - for a test or a log to see what the player sees.
|
|
408
|
-
*/
|
|
409
|
-
export function mc_get_menu_text(player: Player) {
|
|
410
|
-
return player.isConnected ? menuColors(menus.shownText(player)) : "";
|
|
411
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Menu Core for Pawn plugins: the 30 mc_* natives of the original
|
|
3
|
+
* menu_core.amxx, as include/menu_core.inc declares them.
|
|
4
|
+
*/
|
|
5
|
+
import { Player, plugin } from "@amxts/core";
|
|
6
|
+
import {
|
|
7
|
+
PawnFunction,
|
|
8
|
+
caller,
|
|
9
|
+
cellArrayRows,
|
|
10
|
+
cellsText,
|
|
11
|
+
colorTags,
|
|
12
|
+
createCellArray,
|
|
13
|
+
destroyCellArray,
|
|
14
|
+
menuColors,
|
|
15
|
+
pushCellArrayRow,
|
|
16
|
+
textCells,
|
|
17
|
+
} from "@amxts/core/kit";
|
|
18
|
+
import * as menus from "./index";
|
|
19
|
+
import { addNamedFilter, iniItem, insertItem, stateOf } from "./internal";
|
|
20
|
+
import { MenuShowOptions } from "./types";
|
|
21
|
+
|
|
22
|
+
plugin({ name: "Menu Core", version: "0.3.0", author: "kukson777", description: "Menus from ini files: the mc_* natives" });
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Menu properties: mc_set_menu_property takes MP_LOCKED .. MP_HIDE_EXIT,
|
|
26
|
+
* mc_set_menu_property_string MP_ON_TIMEOUT .. MP_FILTER, and
|
|
27
|
+
* mc_get_menu_property_string MP_SECTION.
|
|
28
|
+
*/
|
|
29
|
+
export enum MenuProperty {
|
|
30
|
+
MP_LOCKED = 0,
|
|
31
|
+
MP_GLOBAL_TIMER = 1,
|
|
32
|
+
MP_TIMER_DURATION = 2,
|
|
33
|
+
MP_HIDE_BACK = 3,
|
|
34
|
+
MP_HIDE_EXIT = 4,
|
|
35
|
+
// oxlint-disable-next-line typescript/no-duplicate-enum-values -- the include's own: 5 is read as the section and written as the timeout action (oxlint reports the first of the two)
|
|
36
|
+
MP_SECTION = 5,
|
|
37
|
+
MP_ON_TIMEOUT = 5,
|
|
38
|
+
MP_ACTIVE_ON = 6,
|
|
39
|
+
MP_FILTER = 7,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A list data source's item: 289 cells, laid out as menu_core.inc says. */
|
|
43
|
+
const ROW_CELLS = 289;
|
|
44
|
+
const PLACEHOLDER_CELLS = 256;
|
|
45
|
+
|
|
46
|
+
// What each native has registered: the numbers the originals return.
|
|
47
|
+
let conditionCount = 0;
|
|
48
|
+
let actionCount = 0;
|
|
49
|
+
let restrictionCount = 0;
|
|
50
|
+
let filterCount = 0;
|
|
51
|
+
let openCount = 0;
|
|
52
|
+
let closeCount = 0;
|
|
53
|
+
let showCount = 0;
|
|
54
|
+
|
|
55
|
+
/** A public of the plugin calling the native that is running; null when it has none of that name. */
|
|
56
|
+
function publicOf(name: string) {
|
|
57
|
+
return PawnFunction.find(caller(), name);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A data source fills an `Array:` of 289-cell items; one that does not return 1 is not used. */
|
|
61
|
+
function rowsFrom(fn: PawnFunction, viewer: Player) {
|
|
62
|
+
const items = createCellArray(ROW_CELLS);
|
|
63
|
+
const answered = fn.call().int(viewer.id).int(items).run();
|
|
64
|
+
const rows = answered == 1 ? cellArrayRows(items, ROW_CELLS).map(rowOf) : null;
|
|
65
|
+
destroyCellArray(items);
|
|
66
|
+
return rows;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** [0] target, or -2 for a line of text; [1..64] action; [65..128] text; [129..160] restriction; [161..288] message. */
|
|
70
|
+
function rowOf(cells: number[]) {
|
|
71
|
+
const text = colorTags(cellsText(cells, 65, 64));
|
|
72
|
+
if (cells[0] == -2) return menus.textRow(text);
|
|
73
|
+
return menus.listRow(cells[0], text, cellsText(cells, 1, 64), cellsText(cells, 129, 32), colorTags(cellsText(cells, 161, 128)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 1 or 0, as the original's natives answer. */
|
|
77
|
+
function answer(done: boolean) {
|
|
78
|
+
return done ? 1 : 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Registers a condition menus refer to - an item's condition, ACTIVE_ON, a
|
|
83
|
+
* FILTER. The first plugin to register a name answers for it (case-insensitive).
|
|
84
|
+
* Callback: public callback(id, viewerId, const name[]) - true when it holds.
|
|
85
|
+
* Returns the condition's id.
|
|
86
|
+
*/
|
|
87
|
+
export function mc_register_condition(name: string, callback: string) {
|
|
88
|
+
const fn = publicOf(callback);
|
|
89
|
+
if (fn != null) menus.addCondition(name, (player, viewer, used) => fn.call().int(player.id).int(viewer.id).text(used).run() != 0);
|
|
90
|
+
return conditionCount++;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Registers an action run when an item is chosen. Built in, not registered:
|
|
95
|
+
* SHOW_<SECTION> opens that menu, CLOSE_MENU closes it; an action line may
|
|
96
|
+
* list several, space-separated. Names are case-sensitive; the first wins.
|
|
97
|
+
* Callback: public callback(id, const action[]) in an items menu,
|
|
98
|
+
* public callback(id, targetId) in a LIST_ menu. isCritical is accepted and
|
|
99
|
+
* does nothing. Returns the action's id.
|
|
100
|
+
*/
|
|
101
|
+
export function mc_register_action(name: string, callback: string, _isCritical = false) {
|
|
102
|
+
const fn = publicOf(callback);
|
|
103
|
+
|
|
104
|
+
if (fn != null) {
|
|
105
|
+
menus.addAction(name, ({ player, row, menu }) => {
|
|
106
|
+
const call = fn.call().int(player.id);
|
|
107
|
+
// An items menu's action gets the action's name, a list menu's the row's target.
|
|
108
|
+
if (menu.kind == "items") call.text(name);
|
|
109
|
+
else call.int(row);
|
|
110
|
+
call.run();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return actionCount++;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Registers a placeholder, %name% in titles and items, without the %.
|
|
119
|
+
* Callback: public callback(id, targetId, value[], len) - fills value[].
|
|
120
|
+
* A name registered again keeps the first. Returns the placeholder's id.
|
|
121
|
+
*/
|
|
122
|
+
export function mc_register_placeholder(name: string, callback: string) {
|
|
123
|
+
const fn = publicOf(callback);
|
|
124
|
+
return menus.addPlaceholder(name, ({ player, row }) => {
|
|
125
|
+
if (fn == null) return "";
|
|
126
|
+
const call = fn.call().int(player.id).int(row).buffer(PLACEHOLDER_CELLS).int(PLACEHOLDER_CELLS - 1);
|
|
127
|
+
call.run();
|
|
128
|
+
return colorTags(call.bufferText);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Registers the config file's [section] as a menu. Returns the menu's id, or
|
|
134
|
+
* -1 when the section is missing, has no TITLE or no items, or is MAIN.
|
|
135
|
+
*/
|
|
136
|
+
export function mc_register_menu(section: string) {
|
|
137
|
+
return menus.indexOf(menus.register(section));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Shows a menu. time: countdown seconds, -1 for the menu's TIME (or the
|
|
142
|
+
* countdown running). targetId: who a LIST_ menu is about. resetHistory
|
|
143
|
+
* starts the way back anew; forceOpen opens over a menu that holds on (a
|
|
144
|
+
* countdown, a lock); ignoreHistory leaves this menu out of the way back.
|
|
145
|
+
* Returns 1 when it opened; 0 when the player is not in the game, there is no
|
|
146
|
+
* such menu, a show filter or ACTIVE_ON stopped it.
|
|
147
|
+
*/
|
|
148
|
+
export function mc_show_menu(player: Player, section: string, time = -1, targetId = 0, resetHistory = false, forceOpen = false, ignoreHistory = false) {
|
|
149
|
+
const options: MenuShowOptions = { resetHistory, force: forceOpen, skipHistory: ignoreHistory };
|
|
150
|
+
// -1, Pawn's "not given": the option left out.
|
|
151
|
+
if (time >= 0) options.time = time;
|
|
152
|
+
if (targetId > 0) options.target = new Player(targetId);
|
|
153
|
+
return answer(menus.show(player, section, options));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Sets the menu's shared countdown: starts one of `time` seconds when none
|
|
158
|
+
* runs, or changes the seconds left (0 stops it). Returns 1, or 0 when there
|
|
159
|
+
* is no such menu or nothing to change.
|
|
160
|
+
*/
|
|
161
|
+
export function mc_set_menu_timer(section: string, time: number) {
|
|
162
|
+
const menu = menus.find(section);
|
|
163
|
+
return answer(menu != null && menu.setTimer(time));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Draws again every menu that uses the condition, for whoever looks at it. */
|
|
167
|
+
export function mc_notify_condition_changed(condition: string) {
|
|
168
|
+
menus.conditionChanged(condition);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Draws the menus again (space-separated names) for whoever looks at them. Returns how many were drawn. */
|
|
172
|
+
export function mc_refresh_menu(sections: string) {
|
|
173
|
+
return menus.refresh(sections);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Registers a restriction items name in their restriction column; "*"
|
|
178
|
+
* answers for every name no other restriction does. Callback:
|
|
179
|
+
* public callback(id, const restrictName[], targetId) - true when the player
|
|
180
|
+
* passes. message is said beside an item it greys out, when the item has no
|
|
181
|
+
* message of its own. Returns the restriction's id.
|
|
182
|
+
*/
|
|
183
|
+
export function mc_register_restriction(name: string, callback: string, message?: string) {
|
|
184
|
+
const fn = publicOf(callback);
|
|
185
|
+
if (fn != null) menus.addRestriction(name, ({ player, name: used, row }) => fn.call().int(player.id).text(used).int(row).run() != 0, colorTags(message ?? ""));
|
|
186
|
+
return restrictionCount++;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Greys out items whose action is actionName in menuSection while the
|
|
191
|
+
* callback says no; "" for either matches all. Callback:
|
|
192
|
+
* public callback(id, const menuSection[], const actionName[]). Returns 1.
|
|
193
|
+
*/
|
|
194
|
+
export function mc_register_action_condition(menuSection: string, actionName: string, callback: string) {
|
|
195
|
+
const fn = publicOf(callback);
|
|
196
|
+
if (fn != null) menus.addActionCheck(menuSection, actionName, ({ player, menu, name: action }) => fn.call().int(player.id).text(menu.name).text(action).run() != 0);
|
|
197
|
+
return 1;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Registers a filter over a condition someone else registered: it gets the
|
|
202
|
+
* condition's value and returns the one to use. Callback:
|
|
203
|
+
* public bool:callback(id, viewerId, const condition[], bool:currentVal).
|
|
204
|
+
* Returns the filter's id.
|
|
205
|
+
*/
|
|
206
|
+
export function mc_register_condition_filter(condition: string, callback: string) {
|
|
207
|
+
const fn = publicOf(callback);
|
|
208
|
+
if (fn == null) return filterCount - 1;
|
|
209
|
+
menus.addConditionFilter(condition, (player, viewer, name, value) => fn.call().int(player.id).int(viewer.id).text(name).bool(value).run() != 0);
|
|
210
|
+
return filterCount++;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The rows of a LIST_ menu, instead of the players. Callback:
|
|
215
|
+
* public callback(id, Array:items) - pushes 289-cell items and returns 1:
|
|
216
|
+
* [0] the target the action gets (-2: a line of text), [1..64] an action of
|
|
217
|
+
* its own, [65..128] the text (%name%), [129..160] a restriction,
|
|
218
|
+
* [161..288] the message when it greys the row out. A second source for the
|
|
219
|
+
* menu replaces the first. Returns the source's id.
|
|
220
|
+
*/
|
|
221
|
+
export function mc_register_list_data_source(menuName: string, callback: string) {
|
|
222
|
+
const fn = publicOf(callback);
|
|
223
|
+
return menus.setListSource(menuName, ({ player }) => (fn != null ? rowsFrom(fn, player) : null));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Called when a menu opens. Callback: public callback(id, const section[]). Returns its id. */
|
|
227
|
+
export function mc_register_menu_open_callback(callback: string) {
|
|
228
|
+
const fn = publicOf(callback);
|
|
229
|
+
if (fn == null) return openCount - 1;
|
|
230
|
+
menus.addEventListener("open", event => fn.call().int(event.player.id).text(event.menu.name).run());
|
|
231
|
+
return openCount++;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Called when a menu closes - by a key, a timeout, another menu opening over
|
|
236
|
+
* it, the player leaving. Callback: public callback(id, const section[], bool:isTimeout).
|
|
237
|
+
* Returns its id.
|
|
238
|
+
*/
|
|
239
|
+
export function mc_register_menu_close_callback(callback: string) {
|
|
240
|
+
const fn = publicOf(callback);
|
|
241
|
+
if (fn == null) return closeCount - 1;
|
|
242
|
+
menus.addEventListener("close", event => fn.call().int(event.player.id).text(event.menu.name).bool(event.timeout).run());
|
|
243
|
+
return closeCount++;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Asked before any menu opens; returning false stops it (mc_show_menu returns
|
|
248
|
+
* 0) - the place to tell the player why. Callback:
|
|
249
|
+
* public bool:callback(id, const section[]). Returns its id.
|
|
250
|
+
*/
|
|
251
|
+
export function mc_register_show_filter(callback: string) {
|
|
252
|
+
const fn = publicOf(callback);
|
|
253
|
+
if (fn == null) return showCount - 1;
|
|
254
|
+
// The first filter that says no stops the menu; the ones after it are not asked.
|
|
255
|
+
menus.addEventListener("show", (event) => {
|
|
256
|
+
if (!event.defaultPrevented && fn.call().int(event.player.id).text(event.menu.name).run() == 0) event.preventDefault();
|
|
257
|
+
});
|
|
258
|
+
return showCount++;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Stops the menu's shared countdown and closes it for everyone. Returns 1, or 0 when none ran. */
|
|
262
|
+
export function mc_cancel_menu_timer(section: string) {
|
|
263
|
+
const menu = menus.find(section);
|
|
264
|
+
return answer(menu != null && menu.cancelTimer());
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Locks (or unlocks) the player's menu: nothing can be chosen, and no other menu replaces it. */
|
|
268
|
+
export function mc_lock_menu(player: Player, lock = true) {
|
|
269
|
+
if (player.isConnected) menus.lock(player, lock);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Closes the player's menu. */
|
|
273
|
+
export function mc_hide_menu(player: Player) {
|
|
274
|
+
if (player.isConnected) menus.close(player);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Whether the player's menu is locked. */
|
|
278
|
+
export function mc_is_menu_locked(player: Player) {
|
|
279
|
+
return player.isConnected && menus.isLocked(player);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** The id of the menu the player looks at, or -1. */
|
|
283
|
+
export function mc_get_active_menu(id: number) {
|
|
284
|
+
const player = new Player(id);
|
|
285
|
+
return player.isConnected ? menus.indexOf(menus.activeMenu(player)) : -1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* A string property of a menu by its id (mc_get_active_menu): MP_SECTION, the
|
|
290
|
+
* section name, into out[]. Returns its length; 0 when the id or the property
|
|
291
|
+
* is not one.
|
|
292
|
+
*/
|
|
293
|
+
export function mc_get_menu_property_string(menuIdx: number, property: MenuProperty) {
|
|
294
|
+
const menu = menus.menuAt(menuIdx);
|
|
295
|
+
// 4 is what the original compared with; 5 is the include's MP_SECTION.
|
|
296
|
+
if (menu == null || (property != MenuProperty.MP_SECTION && property != 4)) return "";
|
|
297
|
+
return menu.name;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Sets MP_LOCKED, MP_GLOBAL_TIMER, MP_TIMER_DURATION, MP_HIDE_BACK or
|
|
302
|
+
* MP_HIDE_EXIT. Returns 1, or 0 when there is no such menu or property.
|
|
303
|
+
*/
|
|
304
|
+
export function mc_set_menu_property(section: string, property: MenuProperty, value: number) {
|
|
305
|
+
const menu = menus.find(section);
|
|
306
|
+
if (menu == null) return 0;
|
|
307
|
+
const on = value != 0;
|
|
308
|
+
if (property == MenuProperty.MP_LOCKED) menu.locked = on;
|
|
309
|
+
else if (property == MenuProperty.MP_GLOBAL_TIMER) menu.sharedTimer = on;
|
|
310
|
+
else if (property == MenuProperty.MP_TIMER_DURATION) menu.time = value;
|
|
311
|
+
else if (property == MenuProperty.MP_HIDE_BACK) menu.hideBack = on;
|
|
312
|
+
else if (property == MenuProperty.MP_HIDE_EXIT) menu.hideExit = on;
|
|
313
|
+
else return 0;
|
|
314
|
+
return 1;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Sets MP_ON_TIMEOUT (the action when the countdown ends), MP_ACTIVE_ON (the
|
|
319
|
+
* condition the menu opens under) or MP_FILTER ("CONDITION|MESSAGE", added to
|
|
320
|
+
* a LIST_ menu's filters). Returns 1, or 0 when there is no such menu or
|
|
321
|
+
* property.
|
|
322
|
+
*/
|
|
323
|
+
export function mc_set_menu_property_string(section: string, property: MenuProperty, value: string) {
|
|
324
|
+
const menu = menus.find(section);
|
|
325
|
+
if (menu == null) return 0;
|
|
326
|
+
if (property == MenuProperty.MP_ON_TIMEOUT) menu.onTimeout = value;
|
|
327
|
+
else if (property == MenuProperty.MP_ACTIVE_ON) menu.activeOn = value;
|
|
328
|
+
else if (property == MenuProperty.MP_FILTER) addFilter(menu, value);
|
|
329
|
+
else return 0;
|
|
330
|
+
return 1;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function addFilter(menu: menus.Menu, value: string) {
|
|
334
|
+
if (menu.kind != "list") return;
|
|
335
|
+
const pipe = value.indexOf("|");
|
|
336
|
+
if (pipe < 0) addNamedFilter(menu.name, value.trim(), "");
|
|
337
|
+
else addNamedFilter(menu.name, value.slice(0, pipe).trim(), colorTags(value.slice(pipe + 1).trim()));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Adds an item to a menu. name, condition and action take "A|B" variants: the
|
|
342
|
+
* first whose condition holds is shown. iPosition: its place among the items,
|
|
343
|
+
* -1 the end; emptyBefore/emptyAfter: blank lines around it. Returns 1, or 0
|
|
344
|
+
* when there is no such menu or the name gives no item.
|
|
345
|
+
*/
|
|
346
|
+
export function mc_add_menu_item(section: string, name: string, placeholder?: string, condition?: string, action?: string, restriction?: string, restrictMsg?: string, iPosition = -1, emptyBefore = 0, emptyAfter = 0) {
|
|
347
|
+
const menu = menus.find(section);
|
|
348
|
+
if (menu == null) return 0;
|
|
349
|
+
if (!menus.hasText(name)) return 0;
|
|
350
|
+
const item = iniItem(colorTags(name), colorTags(placeholder ?? ""), condition ?? "", action ?? "", restriction ?? "", colorTags(restrictMsg ?? ""), -1);
|
|
351
|
+
item.spaceBefore = emptyBefore;
|
|
352
|
+
item.spaceAfter = emptyAfter;
|
|
353
|
+
insertItem(menu.name, item, iPosition);
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Adds an item that always takes slot 1-7 of every page. Returns 1, or 0 when
|
|
359
|
+
* there is no such menu or the name gives no item.
|
|
360
|
+
*/
|
|
361
|
+
export function mc_add_fixed_menu_item(section: string, slot: number, name: string, placeholder?: string, action?: string, condition?: string, restriction?: string, emptyBefore = 0, emptyAfter = 0) {
|
|
362
|
+
const menu = menus.find(section);
|
|
363
|
+
if (menu == null) return 0;
|
|
364
|
+
if (!menus.hasText(name)) return 0;
|
|
365
|
+
const item = iniItem(colorTags(name), colorTags(placeholder ?? ""), condition ?? "", action ?? "", restriction ?? "", "", slot - 1);
|
|
366
|
+
item.spaceBefore = emptyBefore;
|
|
367
|
+
item.spaceAfter = emptyAfter;
|
|
368
|
+
stateOf(menu.name).fixed.push(item);
|
|
369
|
+
return 1;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Adds a line of text - a header, a separator - to the items a data source
|
|
374
|
+
* fills; call it from the data source callback. Returns 1.
|
|
375
|
+
*/
|
|
376
|
+
export function mc_add_list_text(aItems: number, text: string, centered = false) {
|
|
377
|
+
const row = menus.textRow(text, centered);
|
|
378
|
+
const cells = [-2].concat(textCells("", 64)).concat(textCells(row.text, 64)).concat(textCells("", 32)).concat(textCells("", 128));
|
|
379
|
+
pushCellArrayRow(aItems, cells);
|
|
380
|
+
return 1;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Removes every item of a menu, fixed ones too. Returns 1, or 0 when there is no such menu. */
|
|
384
|
+
export function mc_clear_menu_items(section: string) {
|
|
385
|
+
const menu = menus.find(section);
|
|
386
|
+
if (menu != null) menu.clearItems();
|
|
387
|
+
return answer(menu != null);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Makes a menu in code, without a config section; a name starting with LIST_
|
|
392
|
+
* makes a list menu. Returns 1, or 0 when a menu of that name is there.
|
|
393
|
+
*/
|
|
394
|
+
export function mc_create_menu(section: string, title: string) {
|
|
395
|
+
if (menus.find(section) != null) return 0;
|
|
396
|
+
menus.create(section, { title: colorTags(title) });
|
|
397
|
+
return 1;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** The page, from 0, the player's menu is drawn at next. */
|
|
401
|
+
export function mc_set_menu_page(player: Player, page: number) {
|
|
402
|
+
if (player.isConnected) menus.setPage(player, page);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* The text the player's menu shows at the moment, colour codes and all (""
|
|
407
|
+
* when none is open) - for a test or a log to see what the player sees.
|
|
408
|
+
*/
|
|
409
|
+
export function mc_get_menu_text(player: Player) {
|
|
410
|
+
return player.isConnected ? menuColors(menus.shownText(player)) : "";
|
|
411
|
+
}
|