@amxts/menu-core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/natives.ts CHANGED
@@ -19,7 +19,7 @@ import * as menus from "./index";
19
19
  import { addNamedFilter, iniItem, insertItem, stateOf } from "./internal";
20
20
  import { MenuShowOptions } from "./types";
21
21
 
22
- plugin({ name: "Menu Core", version: "0.1.0", author: "kukson777", description: "Menus from ini files: the mc_* natives" });
22
+ plugin({ name: "Menu Core", version: "0.2.0", author: "kukson777", description: "Menus from ini files: the mc_* natives" });
23
23
 
24
24
  /**
25
25
  * Menu properties: mc_set_menu_property takes MP_LOCKED .. MP_HIDE_EXIT,
@@ -102,12 +102,11 @@ export function mc_register_action(name: string, callback: string, _isCritical =
102
102
  const fn = publicOf(callback);
103
103
 
104
104
  if (fn != null) {
105
- menus.addAction(name, (player, target) => {
106
- const menu = menus.activeMenu(player);
105
+ menus.addAction(name, ({ player, row, menu }) => {
107
106
  const call = fn.call().int(player.id);
108
107
  // An items menu's action gets the action's name, a list menu's the row's target.
109
- if (menu != null && menu.kind == "items") call.text(name);
110
- else call.int(target);
108
+ if (menu.kind == "items") call.text(name);
109
+ else call.int(row);
111
110
  call.run();
112
111
  });
113
112
  }
@@ -122,9 +121,9 @@ export function mc_register_action(name: string, callback: string, _isCritical =
122
121
  */
123
122
  export function mc_register_placeholder(name: string, callback: string) {
124
123
  const fn = publicOf(callback);
125
- return menus.addPlaceholder(name, (player, target) => {
124
+ return menus.addPlaceholder(name, ({ player, row }) => {
126
125
  if (fn == null) return "";
127
- const call = fn.call().int(player.id).int(target).buffer(PLACEHOLDER_CELLS).int(PLACEHOLDER_CELLS - 1);
126
+ const call = fn.call().int(player.id).int(row).buffer(PLACEHOLDER_CELLS).int(PLACEHOLDER_CELLS - 1);
128
127
  call.run();
129
128
  return colorTags(call.bufferText);
130
129
  });
@@ -147,9 +146,10 @@ export function mc_register_menu(section: string) {
147
146
  * such menu, a show filter or ACTIVE_ON stopped it.
148
147
  */
149
148
  export function mc_show_menu(player: Player, section: string, time = -1, targetId = 0, resetHistory = false, forceOpen = false, ignoreHistory = false) {
150
- const options: MenuShowOptions = { target: targetId, resetHistory, force: forceOpen, skipHistory: ignoreHistory };
149
+ const options: MenuShowOptions = { resetHistory, force: forceOpen, skipHistory: ignoreHistory };
151
150
  // -1, Pawn's "not given": the option left out.
152
151
  if (time >= 0) options.time = time;
152
+ if (targetId > 0) options.target = new Player(targetId);
153
153
  return answer(menus.show(player, section, options));
154
154
  }
155
155
 
@@ -182,7 +182,7 @@ export function mc_refresh_menu(sections: string) {
182
182
  */
183
183
  export function mc_register_restriction(name: string, callback: string, message?: string) {
184
184
  const fn = publicOf(callback);
185
- if (fn != null) menus.addRestriction(name, (player, used, target) => fn.call().int(player.id).text(used).int(target).run() != 0, colorTags(message ?? ""));
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
186
  return restrictionCount++;
187
187
  }
188
188
 
@@ -193,7 +193,7 @@ export function mc_register_restriction(name: string, callback: string, message?
193
193
  */
194
194
  export function mc_register_action_condition(menuSection: string, actionName: string, callback: string) {
195
195
  const fn = publicOf(callback);
196
- if (fn != null) menus.addActionCheck(menuSection, actionName, (player, menu, action) => fn.call().int(player.id).text(menu).text(action).run() != 0);
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
197
  return 1;
198
198
  }
199
199
 
@@ -220,7 +220,7 @@ export function mc_register_condition_filter(condition: string, callback: string
220
220
  */
221
221
  export function mc_register_list_data_source(menuName: string, callback: string) {
222
222
  const fn = publicOf(callback);
223
- return menus.setListSource(menuName, viewer => (fn != null ? rowsFrom(fn, viewer) : null));
223
+ return menus.setListSource(menuName, ({ player }) => (fn != null ? rowsFrom(fn, player) : null));
224
224
  }
225
225
 
226
226
  /** Called when a menu opens. Callback: public callback(id, const section[]). Returns its id. */
package/src/types.ts CHANGED
@@ -3,26 +3,50 @@
3
3
  * register.
4
4
  */
5
5
  import { Player } from "@amxts/core";
6
+ import { Menu } from "./menus";
6
7
 
7
8
  /** A menu's kind, one of `"items"` (a list of items) or `"list"` (a row per player, or per row of a list source). */
8
9
  export type MenuKind = "items" | "list";
9
10
 
11
+ /**
12
+ * A menu's context, which its functions get: the player who looks, who or
13
+ * what the menu is about, and the menu.
14
+ *
15
+ * shop.addItem({ title: ({ player }) => `Heal (${player.health} HP)`, onSelect: ({ player }) => heal(player) });
16
+ */
17
+ export interface MenuContext {
18
+ /** The player the menu is shown to: who looks at it, and who chooses. */
19
+ player: Player;
20
+ /** The player the menu is about: the row's in a list menu, the one `show()` was given as `target` in an items menu; `player` himself when there is none. */
21
+ target: Player;
22
+ /** The row's number in a list menu, as `listRow()` gave it - e.g. an entity or an index of the source's own; a player's `id` in a list of players. In an items menu, the `id` of the menu's target, `0` without one. */
23
+ row: number;
24
+ /** The menu the function is asked for. */
25
+ menu: Menu;
26
+ }
27
+
28
+ /** The context of a function registered by name - an action, a placeholder, a restriction, an action check: the menu's context, and the name it is asked by. */
29
+ export interface NamedContext extends MenuContext {
30
+ /** The name it is asked by: an action's or a placeholder's, a restriction's whole token - `"NAME:param"` included; for an action check, the item's action. */
31
+ name: string;
32
+ }
33
+
10
34
  /**
11
35
  * Text of a menu - a title, an item, a message: the text itself, or a
12
- * function that gives it for the player who looks; `target` is the row's
13
- * in a list menu, else the menu's. A lang key is translated either way.
14
- * Colour tags are chat's letters: `!y` yellow, `!r` red, `!d` grey, `!w`
15
- * white, `!R` to the right edge; chat's own `!g`, `!b` and `!t` are dropped.
36
+ * function that gives it for the menu's context. A lang key is translated
37
+ * either way. Colour tags are chat's letters: `!y` yellow, `!r` red, `!d`
38
+ * grey, `!w` white, `!R` to the right edge; chat's own `!g`, `!b` and `!t`
39
+ * are dropped.
16
40
  *
17
- * menus.create("SHOP", { title: (player) => `Shop for ${player.name}` });
41
+ * menus.create("SHOP", { title: ({ player }) => `Shop for ${player.name}` });
18
42
  */
19
- export type MenuText = string | ((player: Player, target: number) => string);
43
+ export type MenuText = string | ((context: MenuContext) => string);
20
44
 
21
45
  /** A row of a list menu, as a list source gives it - made with `listRow()` or `textRow()`. */
22
46
  export interface ListRow {
23
47
  /** The row's kind, one of `"item"` (a row to choose) or `"text"` (a line of text, not a choice). */
24
48
  kind: "item" | "text";
25
- /** The row's target, handed to the action: e.g. a player's `id`, an entity or an index of the source's own; `0` for a line of text. */
49
+ /** The row's number, `row` of the context its item's functions get: e.g. a player's `id`, an entity or an index of the source's own; `0` for a line of text. */
26
50
  target: number;
27
51
  /** The row's text, put for `%name%` in the menu's row template. */
28
52
  text: string;
@@ -37,10 +61,10 @@ export interface ListRow {
37
61
  /**
38
62
  * The options of a menu made with `create()`. Every field may be left out:
39
63
  *
40
- * menus.create("SHOP", { title: "Shop", time: 30, activeWhen: player => player.isAlive });
64
+ * menus.create("SHOP", { title: "Shop", time: 30, activeWhen: ({ player }) => player.isAlive });
41
65
  */
42
66
  export interface MenuOptions {
43
- /** The menu's title: the text - a lang key too - or a function that gives it for the player; left out, the menu's name. */
67
+ /** The menu's title: the text - a lang key too - or a function that gives it for the menu's context; left out, the menu's name. */
44
68
  title?: MenuText;
45
69
  /** Seconds on the countdown when the menu opens, e.g. `10`; left out, none. */
46
70
  time?: number;
@@ -50,8 +74,8 @@ export interface MenuOptions {
50
74
  hideExit?: boolean;
51
75
  /** A lock on the menu: while `true`, items cannot be chosen and no other menu replaces this one. */
52
76
  locked?: boolean;
53
- /** A test the menu opens under: while it says no to the player, the menu does not open for him. */
54
- activeWhen?: (player: Player) => boolean;
77
+ /** A test the menu opens under: while it says no, the menu does not open for the player. */
78
+ activeWhen?: (context: MenuContext) => boolean;
55
79
  }
56
80
 
57
81
  /**
@@ -62,8 +86,8 @@ export interface MenuOptions {
62
86
  export interface MenuShowOptions {
63
87
  /** Seconds on the countdown; left out, the countdown running goes on, or the menu's own starts. */
64
88
  time?: number;
65
- /** The player the menu is about, by `id`: `%target%`, and the target an action gets; `0` when left out. */
66
- target?: number;
89
+ /** The player the menu is about: `target` of the context its functions get, and `%target%`. */
90
+ target?: Player;
67
91
  /** A new way back: `true` forgets the menus this one was opened from. */
68
92
  resetHistory?: boolean;
69
93
  /** Opening over a menu that holds on - a countdown, a lock: `true` opens anyway. */
@@ -73,32 +97,38 @@ export interface MenuShowOptions {
73
97
  }
74
98
 
75
99
  /**
76
- * The options of an item, besides its text. Every field may be left out:
100
+ * An item: its title, when it is shown and can be chosen, and what choosing
101
+ * it does. Every field but `title` may be left out:
77
102
  *
78
- * shop.addItem("Heal", {
79
- * visible: player => player.health < 100,
80
- * onSelect: (player) => { player.health = 100; },
103
+ * shop.addItem({
104
+ * title: ({ player }) => `Heal (${player.health} HP)`,
105
+ * visible: ({ player }) => player.health < 100,
106
+ * onSelect: ({ player }) => {
107
+ * player.health = 100;
108
+ * },
81
109
  * });
82
110
  */
83
111
  export interface MenuItemOptions {
84
- /** The function run when the item is chosen: the player who chose it, and the target - the row's in a list menu, else the menu's. */
85
- onSelect?: (player: Player, target: number) => void;
112
+ /** The item's text - a lang key too - or a function that gives it for the menu's context. */
113
+ title: MenuText;
114
+ /** The function run when the item is chosen; the menu is drawn again after it, while it stays open. */
115
+ onSelect?: (context: MenuContext) => void;
86
116
  /** A test the item is shown under: while it says no, the item is left out and takes no slot. */
87
- visible?: (player: Player, target: number) => boolean;
117
+ visible?: (context: MenuContext) => boolean;
88
118
  /**
89
119
  * A test the item can be chosen under - while it says no, the item is
90
120
  * greyed out with `message` beside it; or a list of requirements, each with
91
121
  * a message of its own - the first that fails gives its message.
92
122
  *
93
123
  * enabled: [
94
- * { when: (player) => player.frags >= 5, message: "5 frags needed" },
95
- * { when: (player) => player.armor < 100, message: (player) => `(${player.armor} already)` },
124
+ * { when: ({ player }) => player.frags >= 5, message: "5 frags needed" },
125
+ * { when: ({ player }) => player.armor < 100, message: ({ player }) => `(${player.armor} already)` },
96
126
  * ],
97
127
  */
98
- enabled?: ((player: Player, target: number) => boolean) | Requirement[];
99
- /** The text beside the item while `enabled` greys it out, for a requirement without a message of its own: the text, e.g. `"(full)"`, or a function that gives it for the player. */
128
+ enabled?: ((context: MenuContext) => boolean) | Requirement[];
129
+ /** The text beside the item while `enabled` greys it out, for a requirement without a message of its own: the text, e.g. `"(full)"`, or a function that gives it for the menu's context. */
100
130
  message?: MenuText;
101
- /** The text after the item's name, for items of menu files and Pawn plugins, placeholders and all, e.g. `"%hp%"`; in code the item's text is a function instead. */
131
+ /** The text after the item's title, for items of menu files and Pawn plugins, placeholders and all, e.g. `"%hp%"`; in code the title is a function instead. */
102
132
  placeholder?: string;
103
133
  /** Action names from `addAction()` run when the item is chosen, or a built-in one: `"SHOW_<MENU>"`, `"CLOSE_MENU"`. */
104
134
  action?: string;
@@ -114,31 +144,31 @@ export interface MenuItemOptions {
114
144
  * A requirement of an item, in the list `enabled` takes: while `when` says
115
145
  * no, the item is greyed out with `message` beside it.
116
146
  *
117
- * { when: (player) => player.frags >= 5, message: "5 frags needed" }
147
+ * { when: ({ player }) => player.frags >= 5, message: "5 frags needed" }
118
148
  */
119
149
  export interface Requirement {
120
- /** A test the requirement holds under: the player who looks, and the target - the row's in a list menu, else the menu's. */
121
- when: (player: Player, target: number) => boolean;
122
- /** The text beside the item while `when` says no: the text, a lang key, or a function that gives it for the player; left out, the item's `message`. */
150
+ /** A test the requirement holds under, given the menu's context. */
151
+ when: (context: MenuContext) => boolean;
152
+ /** The text beside the item while `when` says no: the text, a lang key, or a function that gives it for the menu's context; left out, the item's `message`. */
123
153
  message?: MenuText;
124
154
  }
125
155
 
126
156
  /** A condition's test, as `addCondition()` registers it. In a list menu `player` is the row's player and `viewer` whoever looks. */
127
157
  export type ConditionTest = (player: Player, viewer: Player, name: string) => boolean;
128
- /** An action, as `addAction()` registers it: `target` is the row's in a list menu, else the menu's. */
129
- export type ActionHandler = (player: Player, target: number, name: string) => void;
130
- /** A placeholder's value: the text `%name%` stands for. */
131
- export type PlaceholderValue = (player: Player, target: number, name: string) => string;
158
+ /** An action, as `addAction()` registers it: run with the context of the item chosen, and the action's name. */
159
+ export type ActionHandler = (context: NamedContext) => void;
160
+ /** A placeholder's value: the text `%name%` stands for, given the context of the text it is in. */
161
+ export type PlaceholderValue = (context: NamedContext) => string;
132
162
  /** A restriction's test, as `addRestriction()` registers it; `name` is the whole token, `"NAME:param"` included. */
133
- export type RestrictionTest = (player: Player, name: string, target: number) => boolean;
134
- /** A test of an item's action, as `addActionCheck()` registers it: `false` greys the item out. */
135
- export type ActionTest = (player: Player, menu: string, action: string) => boolean;
163
+ export type RestrictionTest = (context: NamedContext) => boolean;
164
+ /** A test of an item's action, as `addActionCheck()` registers it: `name` is the action, and `false` greys the item out. */
165
+ export type ActionTest = (context: NamedContext) => boolean;
136
166
  /** A filter over a condition someone else registered: gets its value and returns the one to use. */
137
167
  export type ConditionFilter = (player: Player, viewer: Player, name: string, value: boolean) => boolean;
138
- /** A test of a row of a list menu, as `addFilter()` takes it: `player` is the row's player, `viewer` whoever looks. */
139
- export type RowTest = (player: Player, viewer: Player) => boolean;
168
+ /** A test of a row of a list menu, as `addFilter()` takes it: `target` is the row's player, `player` whoever looks. */
169
+ export type RowTest = (context: MenuContext) => boolean;
140
170
  /** A list source: the rows of a list menu for the player who looks; `null` lists the players instead. */
141
- export type ListSource = (viewer: Player, menu: string) => ListRow[] | null;
171
+ export type ListSource = (context: MenuContext) => ListRow[] | null;
142
172
 
143
173
  /** A menu event's type, one of `"open"` and `"close"` as they happen, or `"show"` before a menu opens, to stop it. */
144
174
  export type MenuEventType = "open" | "close" | "show";