@amxts/menu-core 0.2.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.
- package/package.json +3 -3
- package/prebuilt/linux/menu-core.aot +0 -0
- package/prebuilt/manifest.json +30 -30
- package/prebuilt/windows/menu-core.aot +0 -0
- package/src/natives.ts +411 -411
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amxts/menu-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "Menus for amxts plugins, from ini files or code: conditions, placeholders, lists - and the mc_* natives for Pawn plugins.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "kukson777",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"lint:fix": "oxfmt && oxlint --fix --silent || oxlint --fix --silent || oxlint --fix"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@amxts/config-core": "^0.1.
|
|
53
|
-
"@amxts/core": "^0.2.
|
|
52
|
+
"@amxts/config-core": "^0.1.2",
|
|
53
|
+
"@amxts/core": "^0.2.2"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@e18e/eslint-plugin": "^0.8.1",
|
|
Binary file
|
package/prebuilt/manifest.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": 1,
|
|
3
3
|
"module": "@amxts/menu-core",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"from": {
|
|
6
6
|
"@amxts/core": {
|
|
7
|
-
"version": "0.2.
|
|
8
|
-
"hash": "
|
|
7
|
+
"version": "0.2.2",
|
|
8
|
+
"hash": "71afaf25b04163866b491556b2ae1740c8109576"
|
|
9
9
|
},
|
|
10
10
|
"@amxts/config-core": {
|
|
11
|
-
"version": "0.1.
|
|
12
|
-
"hash": "
|
|
11
|
+
"version": "0.1.2",
|
|
12
|
+
"hash": "d4f17332c3543c43e4c52535a89ca218a2af1742"
|
|
13
13
|
},
|
|
14
14
|
"@amxts/menu-core": {
|
|
15
|
-
"version": "0.2.
|
|
16
|
-
"hash": "
|
|
15
|
+
"version": "0.2.1",
|
|
16
|
+
"hash": "a754b5002dbcf723b5b5fe3727b14600469c325d"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"options": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
}
|
|
75
75
|
],
|
|
76
76
|
"result": "int",
|
|
77
|
-
"doc": "/**\n * Registers a condition menus refer to - an item's condition, ACTIVE_ON, a\n * FILTER. The first plugin to register a name answers for it (case-insensitive).\n * Callback: public callback(id, viewerId, const name[]) - true when it holds.\n * Returns the condition's id.\n */"
|
|
77
|
+
"doc": "/**\r\n * Registers a condition menus refer to - an item's condition, ACTIVE_ON, a\r\n * FILTER. The first plugin to register a name answers for it (case-insensitive).\r\n * Callback: public callback(id, viewerId, const name[]) - true when it holds.\r\n * Returns the condition's id.\r\n */"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"name": "mc_register_action",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
}
|
|
97
97
|
],
|
|
98
98
|
"result": "int",
|
|
99
|
-
"doc": "/**\n * Registers an action run when an item is chosen. Built in, not registered:\n * SHOW_<SECTION> opens that menu, CLOSE_MENU closes it; an action line may\n * list several, space-separated. Names are case-sensitive; the first wins.\n * Callback: public callback(id, const action[]) in an items menu,\n * public callback(id, targetId) in a LIST_ menu. isCritical is accepted and\n * does nothing. Returns the action's id.\n */"
|
|
99
|
+
"doc": "/**\r\n * Registers an action run when an item is chosen. Built in, not registered:\r\n * SHOW_<SECTION> opens that menu, CLOSE_MENU closes it; an action line may\r\n * list several, space-separated. Names are case-sensitive; the first wins.\r\n * Callback: public callback(id, const action[]) in an items menu,\r\n * public callback(id, targetId) in a LIST_ menu. isCritical is accepted and\r\n * does nothing. Returns the action's id.\r\n */"
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
"name": "mc_register_placeholder",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
}
|
|
114
114
|
],
|
|
115
115
|
"result": "int",
|
|
116
|
-
"doc": "/**\n * Registers a placeholder, %name% in titles and items, without the %.\n * Callback: public callback(id, targetId, value[], len) - fills value[].\n * A name registered again keeps the first. Returns the placeholder's id.\n */"
|
|
116
|
+
"doc": "/**\r\n * Registers a placeholder, %name% in titles and items, without the %.\r\n * Callback: public callback(id, targetId, value[], len) - fills value[].\r\n * A name registered again keeps the first. Returns the placeholder's id.\r\n */"
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"name": "mc_register_menu",
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
}
|
|
126
126
|
],
|
|
127
127
|
"result": "int",
|
|
128
|
-
"doc": "/**\n * Registers the config file's [section] as a menu. Returns the menu's id, or\n * -1 when the section is missing, has no TITLE or no items, or is MAIN.\n */"
|
|
128
|
+
"doc": "/**\r\n * Registers the config file's [section] as a menu. Returns the menu's id, or\r\n * -1 when the section is missing, has no TITLE or no items, or is MAIN.\r\n */"
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"name": "mc_show_menu",
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
}
|
|
168
168
|
],
|
|
169
169
|
"result": "int",
|
|
170
|
-
"doc": "/**\n * Shows a menu. time: countdown seconds, -1 for the menu's TIME (or the\n * countdown running). targetId: who a LIST_ menu is about. resetHistory\n * starts the way back anew; forceOpen opens over a menu that holds on (a\n * countdown, a lock); ignoreHistory leaves this menu out of the way back.\n * Returns 1 when it opened; 0 when the player is not in the game, there is no\n * such menu, a show filter or ACTIVE_ON stopped it.\n */"
|
|
170
|
+
"doc": "/**\r\n * Shows a menu. time: countdown seconds, -1 for the menu's TIME (or the\r\n * countdown running). targetId: who a LIST_ menu is about. resetHistory\r\n * starts the way back anew; forceOpen opens over a menu that holds on (a\r\n * countdown, a lock); ignoreHistory leaves this menu out of the way back.\r\n * Returns 1 when it opened; 0 when the player is not in the game, there is no\r\n * such menu, a show filter or ACTIVE_ON stopped it.\r\n */"
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
"name": "mc_set_menu_timer",
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
}
|
|
185
185
|
],
|
|
186
186
|
"result": "int",
|
|
187
|
-
"doc": "/**\n * Sets the menu's shared countdown: starts one of `time` seconds when none\n * runs, or changes the seconds left (0 stops it). Returns 1, or 0 when there\n * is no such menu or nothing to change.\n */"
|
|
187
|
+
"doc": "/**\r\n * Sets the menu's shared countdown: starts one of `time` seconds when none\r\n * runs, or changes the seconds left (0 stops it). Returns 1, or 0 when there\r\n * is no such menu or nothing to change.\r\n */"
|
|
188
188
|
},
|
|
189
189
|
{
|
|
190
190
|
"name": "mc_notify_condition_changed",
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
}
|
|
231
231
|
],
|
|
232
232
|
"result": "int",
|
|
233
|
-
"doc": "/**\n * Registers a restriction items name in their restriction column; \"*\"\n * answers for every name no other restriction does. Callback:\n * public callback(id, const restrictName[], targetId) - true when the player\n * passes. message is said beside an item it greys out, when the item has no\n * message of its own. Returns the restriction's id.\n */"
|
|
233
|
+
"doc": "/**\r\n * Registers a restriction items name in their restriction column; \"*\"\r\n * answers for every name no other restriction does. Callback:\r\n * public callback(id, const restrictName[], targetId) - true when the player\r\n * passes. message is said beside an item it greys out, when the item has no\r\n * message of its own. Returns the restriction's id.\r\n */"
|
|
234
234
|
},
|
|
235
235
|
{
|
|
236
236
|
"name": "mc_register_action_condition",
|
|
@@ -252,7 +252,7 @@
|
|
|
252
252
|
}
|
|
253
253
|
],
|
|
254
254
|
"result": "int",
|
|
255
|
-
"doc": "/**\n * Greys out items whose action is actionName in menuSection while the\n * callback says no; \"\" for either matches all. Callback:\n * public callback(id, const menuSection[], const actionName[]). Returns 1.\n */"
|
|
255
|
+
"doc": "/**\r\n * Greys out items whose action is actionName in menuSection while the\r\n * callback says no; \"\" for either matches all. Callback:\r\n * public callback(id, const menuSection[], const actionName[]). Returns 1.\r\n */"
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
258
|
"name": "mc_register_condition_filter",
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
}
|
|
270
270
|
],
|
|
271
271
|
"result": "int",
|
|
272
|
-
"doc": "/**\n * Registers a filter over a condition someone else registered: it gets the\n * condition's value and returns the one to use. Callback:\n * public bool:callback(id, viewerId, const condition[], bool:currentVal).\n * Returns the filter's id.\n */"
|
|
272
|
+
"doc": "/**\r\n * Registers a filter over a condition someone else registered: it gets the\r\n * condition's value and returns the one to use. Callback:\r\n * public bool:callback(id, viewerId, const condition[], bool:currentVal).\r\n * Returns the filter's id.\r\n */"
|
|
273
273
|
},
|
|
274
274
|
{
|
|
275
275
|
"name": "mc_register_list_data_source",
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
}
|
|
287
287
|
],
|
|
288
288
|
"result": "int",
|
|
289
|
-
"doc": "/**\n * The rows of a LIST_ menu, instead of the players. Callback:\n * public callback(id, Array:items) - pushes 289-cell items and returns 1:\n * [0] the target the action gets (-2: a line of text), [1..64] an action of\n * its own, [65..128] the text (%name%), [129..160] a restriction,\n * [161..288] the message when it greys the row out. A second source for the\n * menu replaces the first. Returns the source's id.\n */"
|
|
289
|
+
"doc": "/**\r\n * The rows of a LIST_ menu, instead of the players. Callback:\r\n * public callback(id, Array:items) - pushes 289-cell items and returns 1:\r\n * [0] the target the action gets (-2: a line of text), [1..64] an action of\r\n * its own, [65..128] the text (%name%), [129..160] a restriction,\r\n * [161..288] the message when it greys the row out. A second source for the\r\n * menu replaces the first. Returns the source's id.\r\n */"
|
|
290
290
|
},
|
|
291
291
|
{
|
|
292
292
|
"name": "mc_register_menu_open_callback",
|
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
}
|
|
311
311
|
],
|
|
312
312
|
"result": "int",
|
|
313
|
-
"doc": "/**\n * Called when a menu closes - by a key, a timeout, another menu opening over\n * it, the player leaving. Callback: public callback(id, const section[], bool:isTimeout).\n * Returns its id.\n */"
|
|
313
|
+
"doc": "/**\r\n * Called when a menu closes - by a key, a timeout, another menu opening over\r\n * it, the player leaving. Callback: public callback(id, const section[], bool:isTimeout).\r\n * Returns its id.\r\n */"
|
|
314
314
|
},
|
|
315
315
|
{
|
|
316
316
|
"name": "mc_register_show_filter",
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
}
|
|
323
323
|
],
|
|
324
324
|
"result": "int",
|
|
325
|
-
"doc": "/**\n * Asked before any menu opens; returning false stops it (mc_show_menu returns\n * 0) - the place to tell the player why. Callback:\n * public bool:callback(id, const section[]). Returns its id.\n */"
|
|
325
|
+
"doc": "/**\r\n * Asked before any menu opens; returning false stops it (mc_show_menu returns\r\n * 0) - the place to tell the player why. Callback:\r\n * public bool:callback(id, const section[]). Returns its id.\r\n */"
|
|
326
326
|
},
|
|
327
327
|
{
|
|
328
328
|
"name": "mc_cancel_menu_timer",
|
|
@@ -405,7 +405,7 @@
|
|
|
405
405
|
}
|
|
406
406
|
],
|
|
407
407
|
"result": "string",
|
|
408
|
-
"doc": "/**\n * A string property of a menu by its id (mc_get_active_menu): MP_SECTION, the\n * section name, into out[]. Returns its length; 0 when the id or the property\n * is not one.\n */"
|
|
408
|
+
"doc": "/**\r\n * A string property of a menu by its id (mc_get_active_menu): MP_SECTION, the\r\n * section name, into out[]. Returns its length; 0 when the id or the property\r\n * is not one.\r\n */"
|
|
409
409
|
},
|
|
410
410
|
{
|
|
411
411
|
"name": "mc_set_menu_property",
|
|
@@ -428,7 +428,7 @@
|
|
|
428
428
|
}
|
|
429
429
|
],
|
|
430
430
|
"result": "int",
|
|
431
|
-
"doc": "/**\n * Sets MP_LOCKED, MP_GLOBAL_TIMER, MP_TIMER_DURATION, MP_HIDE_BACK or\n * MP_HIDE_EXIT. Returns 1, or 0 when there is no such menu or property.\n */"
|
|
431
|
+
"doc": "/**\r\n * Sets MP_LOCKED, MP_GLOBAL_TIMER, MP_TIMER_DURATION, MP_HIDE_BACK or\r\n * MP_HIDE_EXIT. Returns 1, or 0 when there is no such menu or property.\r\n */"
|
|
432
432
|
},
|
|
433
433
|
{
|
|
434
434
|
"name": "mc_set_menu_property_string",
|
|
@@ -451,7 +451,7 @@
|
|
|
451
451
|
}
|
|
452
452
|
],
|
|
453
453
|
"result": "int",
|
|
454
|
-
"doc": "/**\n * Sets MP_ON_TIMEOUT (the action when the countdown ends), MP_ACTIVE_ON (the\n * condition the menu opens under) or MP_FILTER (\"CONDITION|MESSAGE\", added to\n * a LIST_ menu's filters). Returns 1, or 0 when there is no such menu or\n * property.\n */"
|
|
454
|
+
"doc": "/**\r\n * Sets MP_ON_TIMEOUT (the action when the countdown ends), MP_ACTIVE_ON (the\r\n * condition the menu opens under) or MP_FILTER (\"CONDITION|MESSAGE\", added to\r\n * a LIST_ menu's filters). Returns 1, or 0 when there is no such menu or\r\n * property.\r\n */"
|
|
455
455
|
},
|
|
456
456
|
{
|
|
457
457
|
"name": "mc_add_menu_item",
|
|
@@ -508,7 +508,7 @@
|
|
|
508
508
|
}
|
|
509
509
|
],
|
|
510
510
|
"result": "int",
|
|
511
|
-
"doc": "/**\n * Adds an item to a menu. name, condition and action take \"A|B\" variants: the\n * first whose condition holds is shown. iPosition: its place among the items,\n * -1 the end; emptyBefore/emptyAfter: blank lines around it. Returns 1, or 0\n * when there is no such menu or the name gives no item.\n */"
|
|
511
|
+
"doc": "/**\r\n * Adds an item to a menu. name, condition and action take \"A|B\" variants: the\r\n * first whose condition holds is shown. iPosition: its place among the items,\r\n * -1 the end; emptyBefore/emptyAfter: blank lines around it. Returns 1, or 0\r\n * when there is no such menu or the name gives no item.\r\n */"
|
|
512
512
|
},
|
|
513
513
|
{
|
|
514
514
|
"name": "mc_add_fixed_menu_item",
|
|
@@ -560,7 +560,7 @@
|
|
|
560
560
|
}
|
|
561
561
|
],
|
|
562
562
|
"result": "int",
|
|
563
|
-
"doc": "/**\n * Adds an item that always takes slot 1-7 of every page. Returns 1, or 0 when\n * there is no such menu or the name gives no item.\n */"
|
|
563
|
+
"doc": "/**\r\n * Adds an item that always takes slot 1-7 of every page. Returns 1, or 0 when\r\n * there is no such menu or the name gives no item.\r\n */"
|
|
564
564
|
},
|
|
565
565
|
{
|
|
566
566
|
"name": "mc_add_list_text",
|
|
@@ -582,7 +582,7 @@
|
|
|
582
582
|
}
|
|
583
583
|
],
|
|
584
584
|
"result": "int",
|
|
585
|
-
"doc": "/**\n * Adds a line of text - a header, a separator - to the items a data source\n * fills; call it from the data source callback. Returns 1.\n */"
|
|
585
|
+
"doc": "/**\r\n * Adds a line of text - a header, a separator - to the items a data source\r\n * fills; call it from the data source callback. Returns 1.\r\n */"
|
|
586
586
|
},
|
|
587
587
|
{
|
|
588
588
|
"name": "mc_clear_menu_items",
|
|
@@ -611,7 +611,7 @@
|
|
|
611
611
|
}
|
|
612
612
|
],
|
|
613
613
|
"result": "int",
|
|
614
|
-
"doc": "/**\n * Makes a menu in code, without a config section; a name starting with LIST_\n * makes a list menu. Returns 1, or 0 when a menu of that name is there.\n */"
|
|
614
|
+
"doc": "/**\r\n * Makes a menu in code, without a config section; a name starting with LIST_\r\n * makes a list menu. Returns 1, or 0 when a menu of that name is there.\r\n */"
|
|
615
615
|
},
|
|
616
616
|
{
|
|
617
617
|
"name": "mc_set_menu_page",
|
|
@@ -640,7 +640,7 @@
|
|
|
640
640
|
}
|
|
641
641
|
],
|
|
642
642
|
"result": "string",
|
|
643
|
-
"doc": "/**\n * The text the player's menu shows at the moment, colour codes and all (\"\"\n * when none is open) - for a test or a log to see what the player sees.\n */"
|
|
643
|
+
"doc": "/**\r\n * The text the player's menu shows at the moment, colour codes and all (\"\"\r\n * when none is open) - for a test or a log to see what the player sees.\r\n */"
|
|
644
644
|
}
|
|
645
645
|
],
|
|
646
646
|
"beside": {
|
|
@@ -685,7 +685,7 @@
|
|
|
685
685
|
"value": 7
|
|
686
686
|
}
|
|
687
687
|
],
|
|
688
|
-
"doc": "/**\n * Menu properties: mc_set_menu_property takes MP_LOCKED .. MP_HIDE_EXIT,\n * mc_set_menu_property_string MP_ON_TIMEOUT .. MP_FILTER, and\n * mc_get_menu_property_string MP_SECTION.\n */"
|
|
688
|
+
"doc": "/**\r\n * Menu properties: mc_set_menu_property takes MP_LOCKED .. MP_HIDE_EXIT,\r\n * mc_set_menu_property_string MP_ON_TIMEOUT .. MP_FILTER, and\r\n * mc_get_menu_property_string MP_SECTION.\r\n */"
|
|
689
689
|
}
|
|
690
690
|
],
|
|
691
691
|
"contract": null,
|
|
@@ -695,12 +695,12 @@
|
|
|
695
695
|
"windows": {
|
|
696
696
|
"file": "prebuilt/windows/menu-core.aot",
|
|
697
697
|
"size": 1548430,
|
|
698
|
-
"sha256": "
|
|
698
|
+
"sha256": "714b3746abb5da366478b330941d6454ff0080a20fcc31bf63361df58d662ef5"
|
|
699
699
|
},
|
|
700
700
|
"linux": {
|
|
701
701
|
"file": "prebuilt/linux/menu-core.aot",
|
|
702
702
|
"size": 1564122,
|
|
703
|
-
"sha256": "
|
|
703
|
+
"sha256": "f0cffdf136ea7e53fcf12b25ac4fe3894b1b830436a35fa34580f1db28729ffd"
|
|
704
704
|
}
|
|
705
705
|
},
|
|
706
706
|
"surface": {
|
|
Binary file
|
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.2.
|
|
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.2.1", 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
|
+
}
|