@rpgm-tools/neo-angband-core 1.2.1 → 1.8.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/mod/hooks.ts CHANGED
@@ -114,6 +114,24 @@ export interface HistoryDisplayEntry {
114
114
  readonly expandUserInput?: true;
115
115
  }
116
116
 
117
+ /** A newly available player ability, named without exposing a mutable game object. */
118
+ export type AbilityGained =
119
+ | {
120
+ readonly kind: "spell";
121
+ readonly spellIndex: number;
122
+ readonly name: string;
123
+ readonly realm: string;
124
+ /** The host command that opens this spell's casting picker. */
125
+ readonly command: "cast";
126
+ }
127
+ | {
128
+ readonly kind: "activation";
129
+ readonly kindIndex: number;
130
+ readonly name: string;
131
+ /** The host command that opens the activation picker. */
132
+ readonly command: "activate";
133
+ };
134
+
117
135
  /**
118
136
  * A mod's behaviour contributions. Every member is optional; an absent member
119
137
  * means "this mod does not touch that point" and core takes its faithful path.
@@ -387,6 +405,16 @@ export interface ModHooks {
387
405
  * choices past the character they were made on).
388
406
  */
389
407
  optionsChanged?: (options: OptionStateData) => void;
408
+
409
+ /**
410
+ * The player learned a spell or gained a known activatable item
411
+ * (spell-cmd.ts, pickup.ts, and obj-cmd.ts). This is a notification: core has already
412
+ * committed the knowledge and does not read a return value. `command` is the
413
+ * semantic command a host turns into its current keyset's activation input;
414
+ * a mod may use it when offering a keymap without guessing which command the
415
+ * player uses.
416
+ */
417
+ abilityGained?: (ability: AbilityGained) => void;
390
418
  }
391
419
 
392
420
  /**
@@ -415,7 +443,7 @@ export interface ModHooks {
415
443
  * - ANY hooks (saveNoiseScent, shapeLearnObviousFlagsDirectly) are disjunctive:
416
444
  * one mod asking for the data is enough, because the data is additive and a
417
445
  * second mod cannot object.
418
- * - NOTIFICATION hooks (optionsChanged, levelRevisited) call every contributor
446
+ * - NOTIFICATION hooks (optionsChanged, levelRevisited, abilityGained) call every contributor
419
447
  * in load order. There is no answer for a later mod to override.
420
448
  *
421
449
  * WHY THE LAST TWO ARE NOT EXCEPTIONS. "Later wins" answers the question "two
@@ -478,6 +506,7 @@ export const MOD_HOOK_FOLDS: Readonly<Record<keyof ModHooks, ModHookFold>> = {
478
506
  levelRevisited: "all-observe",
479
507
  messageText: "chained",
480
508
  optionsChanged: "all-observe",
509
+ abilityGained: "all-observe",
481
510
  };
482
511
 
483
512
  /**
@@ -658,6 +687,13 @@ export function guardModHooks(
658
687
  };
659
688
  }
660
689
 
690
+ const ability = hooks.abilityGained;
691
+ if (ability) {
692
+ out.abilityGained = (gained): void => {
693
+ guard("abilityGained", () => ability({ ...gained }), undefined);
694
+ };
695
+ }
696
+
661
697
  return out;
662
698
  }
663
699
 
@@ -814,6 +850,13 @@ export function composeModHooks(
814
850
  };
815
851
  }
816
852
 
853
+ const abilityGained = list.map((c) => c.abilityGained).filter(isFn);
854
+ if (abilityGained.length > 0) {
855
+ out.abilityGained = (gained): void => {
856
+ for (const fn of abilityGained) fn({ ...gained });
857
+ };
858
+ }
859
+
817
860
  return out;
818
861
  }
819
862
 
@@ -328,10 +328,15 @@ export type MenuTransformer = (
328
328
  rows: readonly MenuTransformRow[],
329
329
  ) => readonly MenuTransformRow[];
330
330
 
331
+ /** Code to run after the player selects a mod-owned menu action. */
332
+ export type MenuActionHandler = () => void | Promise<void>;
333
+
331
334
  /** Structural target implemented by the web front end, not by headless core. */
332
335
  export interface MenuRegistryTarget {
333
336
  register(id: string, transformer: MenuTransformer, owner?: string): void;
334
337
  handlerFor(id: string): MenuTransformer | null;
338
+ /** Add one owned, runnable row to an existing menu. */
339
+ addAction?(id: string, action: string, label: string, handler: MenuActionHandler, owner?: string): void;
335
340
  }
336
341
 
337
342
  /**
@@ -680,6 +685,16 @@ export interface MenuFacade {
680
685
  register(id: string, transformer: MenuTransformer): void;
681
686
  /** The currently installed transformer, for layering/wrapping an earlier mod. */
682
687
  handlerFor(id: string): MenuTransformer | null;
688
+ /**
689
+ * Add a row the host can actually run, rather than merely transforming the
690
+ * presentation of a row whose action core already knows. `action` is scoped
691
+ * to this mod, so two mods may both call theirs "backup" without colliding.
692
+ *
693
+ * The first supported location is `core:game-menu`, the Escape game menu.
694
+ * The callback starts directly from the selection gesture, so a browser-only
695
+ * action such as `ctx.backupFolder.choose()` may still open its folder picker.
696
+ */
697
+ addAction(id: "core:game-menu", action: string, label: string, handler: MenuActionHandler): void;
683
698
  }
684
699
 
685
700
  /**
@@ -1608,6 +1623,14 @@ export function createModRegistryHost(
1608
1623
  requireCap(capabilities, "menu");
1609
1624
  return requireTarget(targets.menus, "menu").handlerFor(id);
1610
1625
  },
1626
+ addAction(id, action, label, handler): void {
1627
+ requireCap(capabilities, "menu");
1628
+ const menus = requireTarget(targets.menus, "menu");
1629
+ if (!menus.addAction) {
1630
+ throw new Error("mod registry: menu actions are not available in this game (host did not wire them)");
1631
+ }
1632
+ menus.addAction(id, action, label, handler);
1633
+ },
1611
1634
  },
1612
1635
  tiles: {
1613
1636
  register(filler): void {
package/src/version.ts CHANGED
@@ -24,4 +24,4 @@ export const PARITY_BASELINE = "4.2.6";
24
24
  * warrants, because a published tag is pinned by digest in a catalogue and
25
25
  * must never be moved.
26
26
  */
27
- export const ENGINE_VERSION = "1.2.1";
27
+ export const ENGINE_VERSION = "1.8.0";