@rpgm-tools/neo-angband-core 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/game/effect-general.d.ts.map +1 -1
- package/dist/game/effect-general.js +4 -2
- package/dist/game/effect-general.js.map +1 -1
- package/dist/mod/hooks.d.ts +52 -5
- package/dist/mod/hooks.d.ts.map +1 -1
- package/dist/mod/hooks.js +31 -0
- package/dist/mod/hooks.js.map +1 -1
- package/dist/mod/save-blocks.d.ts +1 -1
- package/dist/obj/knowledge.d.ts +16 -2
- package/dist/obj/knowledge.d.ts.map +1 -1
- package/dist/obj/knowledge.js +17 -1
- package/dist/obj/knowledge.js.map +1 -1
- package/dist/session/game.d.ts.map +1 -1
- package/dist/session/game.js +10 -0
- package/dist/session/game.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/game/effect-general.ts +9 -2
- package/src/mod/hooks.ts +89 -5
- package/src/obj/knowledge.ts +16 -0
- package/src/session/game.ts +11 -0
- package/src/version.ts +1 -1
package/src/mod/hooks.ts
CHANGED
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
|
|
73
73
|
import type { GameState } from "../game/context.js";
|
|
74
74
|
import type { OptionStateData } from "../player/options.js";
|
|
75
|
+
import type { Chunk } from "../world/chunk.js";
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
78
|
* A history entry offered to the write seam.
|
|
@@ -251,6 +252,52 @@ export interface ModHooks {
|
|
|
251
252
|
*/
|
|
252
253
|
saveNoiseScent?: () => boolean;
|
|
253
254
|
|
|
255
|
+
/**
|
|
256
|
+
* A previously frozen level has been restored as the live level
|
|
257
|
+
* (session/game.ts' persistent-level and single-combat return paths).
|
|
258
|
+
*
|
|
259
|
+
* `frozenAt` is the game turn when the level was put aside and `now` is the
|
|
260
|
+
* game turn at which it came back. Both are supplied rather than a rounded
|
|
261
|
+
* elapsed count: callers that model once-per-world-tick work need the same
|
|
262
|
+
* boundary calculation as the engine's `turn % 10 === 0` scheduler.
|
|
263
|
+
*
|
|
264
|
+
* This is a notification. Core changes none of the restored level's state
|
|
265
|
+
* when it is absent, preserving 4.2.6 exactly; a mod may adjust per-level
|
|
266
|
+
* transient state such as tracking heatmaps.
|
|
267
|
+
*
|
|
268
|
+
* Serves: the upstream-catchup mod's post-4.2.6 noise clear and scent aging
|
|
269
|
+
* (upstream 5c45eb9588b8227d4f1b1998e0a627ad7ee11a75, #4605).
|
|
270
|
+
*/
|
|
271
|
+
levelRevisited?: (chunk: Chunk, frozenAt: number, now: number) => void;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Whether a shapechange's obvious flags should be learned directly as
|
|
275
|
+
* runes (obj/knowledge.ts, shapeLearnOnAssume's per-flag loop, reached from
|
|
276
|
+
* the EF_SHAPECHANGE handler).
|
|
277
|
+
*
|
|
278
|
+
* Return true to learn them directly, ON TOP OF the existing equipment-based
|
|
279
|
+
* `equipLearnFlag` call rather than instead of it - a flag both the shape and
|
|
280
|
+
* a worn item carry still fires the worn-item message unchanged. Faithful
|
|
281
|
+
* core never does this: `shapeLearnOnAssume` only ever learns a shape's
|
|
282
|
+
* obvious flag through `equipLearnFlag`, which requires a WORN item to carry
|
|
283
|
+
* the flag too, so a flag the shape alone grants is never learned. That is
|
|
284
|
+
* the 4.2.6-era gap this mod exists to catalogue, not a bug this port
|
|
285
|
+
* introduced.
|
|
286
|
+
*
|
|
287
|
+
* Core still computes and restricts to the exact SAME obvious-flag set it
|
|
288
|
+
* always has - `shape.flags` intersected with the OFID_WIELD mask,
|
|
289
|
+
* `shapeLearnOnAssume`'s existing first two lines. This hook decides only
|
|
290
|
+
* WHETHER that already-computed set is learned directly, never WHICH flags
|
|
291
|
+
* are in it, so a mod cannot widen this seam into "learn everything the
|
|
292
|
+
* shape has" - the same shortcut this project already reverted once for
|
|
293
|
+
* runes broadly (commit 7970af462, docs/modding/BUG_FIXES.md's "port's own
|
|
294
|
+
* code" section).
|
|
295
|
+
*
|
|
296
|
+
* Serves: the upstream-catchup mod's shape-obvious-flag learning (upstream
|
|
297
|
+
* c8036c51537942a560e3d7f81749c431bbb4701f).
|
|
298
|
+
*/
|
|
299
|
+
shapeLearnObviousFlagsDirectly?: () => boolean;
|
|
300
|
+
|
|
254
301
|
/**
|
|
255
302
|
* Player-visible message text, on its way to the message line (the host's
|
|
256
303
|
* message sink).
|
|
@@ -267,9 +314,8 @@ export interface ModHooks {
|
|
|
267
314
|
/**
|
|
268
315
|
* The player finished changing their options (the '=' menu closed).
|
|
269
316
|
*
|
|
270
|
-
* A NOTIFICATION
|
|
271
|
-
* and
|
|
272
|
-
* thing that already happened and core does not read the return value. The
|
|
317
|
+
* A NOTIFICATION: like levelRevisited, it is told a thing that already
|
|
318
|
+
* happened and core does not read a return value. The
|
|
273
319
|
* fold is "all-observe" for that reason - there is no answer to pick between,
|
|
274
320
|
* so every contributor simply runs.
|
|
275
321
|
*
|
|
@@ -313,8 +359,11 @@ export interface ModHooks {
|
|
|
313
359
|
* or over the radius the blast is built from.
|
|
314
360
|
* - VETO hooks (levelGenerated, artifactCommit, historyAdd) are conjunctive:
|
|
315
361
|
* every contributor runs and any refusal decides.
|
|
316
|
-
* - ANY hooks (saveNoiseScent) are disjunctive:
|
|
317
|
-
* enough, because the data is additive and a
|
|
362
|
+
* - ANY hooks (saveNoiseScent, shapeLearnObviousFlagsDirectly) are disjunctive:
|
|
363
|
+
* one mod asking for the data is enough, because the data is additive and a
|
|
364
|
+
* second mod cannot object.
|
|
365
|
+
* - NOTIFICATION hooks (optionsChanged, levelRevisited) call every contributor
|
|
366
|
+
* in load order. There is no answer for a later mod to override.
|
|
318
367
|
*
|
|
319
368
|
* WHY THE LAST TWO ARE NOT EXCEPTIONS. "Later wins" answers the question "two
|
|
320
369
|
* mods disagree about one thing - whose answer is used?", and a veto hook is not
|
|
@@ -370,6 +419,8 @@ export const MOD_HOOK_FOLDS: Readonly<Record<keyof ModHooks, ModHookFold>> = {
|
|
|
370
419
|
historyAdd: "all-must-agree",
|
|
371
420
|
historyDisplay: "chained",
|
|
372
421
|
saveNoiseScent: "any-yes",
|
|
422
|
+
shapeLearnObviousFlagsDirectly: "any-yes",
|
|
423
|
+
levelRevisited: "all-observe",
|
|
373
424
|
messageText: "chained",
|
|
374
425
|
optionsChanged: "all-observe",
|
|
375
426
|
};
|
|
@@ -500,6 +551,22 @@ export function guardModHooks(
|
|
|
500
551
|
out.saveNoiseScent = (): boolean => guard("saveNoiseScent", () => noise(), false);
|
|
501
552
|
}
|
|
502
553
|
|
|
554
|
+
const shapeFlags = hooks.shapeLearnObviousFlagsDirectly;
|
|
555
|
+
if (shapeFlags) {
|
|
556
|
+
/* DECLINE, the upstream 4.2.6-era gap this seam exists to let a mod close. */
|
|
557
|
+
out.shapeLearnObviousFlagsDirectly = (): boolean =>
|
|
558
|
+
guard("shapeLearnObviousFlagsDirectly", () => shapeFlags(), false);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const revisited = hooks.levelRevisited;
|
|
562
|
+
if (revisited) {
|
|
563
|
+
/* A throwing observer changes nothing: faithful core resumes the frozen
|
|
564
|
+
* level as-is, and the host receives the fault. */
|
|
565
|
+
out.levelRevisited = (chunk, frozenAt, now): void => {
|
|
566
|
+
guard("levelRevisited", () => revisited(chunk, frozenAt, now), undefined);
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
503
570
|
const text = hooks.messageText;
|
|
504
571
|
if (text) {
|
|
505
572
|
/* The raw message, unrestated - never an empty string, which would silently
|
|
@@ -611,6 +678,23 @@ export function composeModHooks(
|
|
|
611
678
|
out.saveNoiseScent = (): boolean => noise.some((fn) => fn());
|
|
612
679
|
}
|
|
613
680
|
|
|
681
|
+
const shapeFlags = list.map((c) => c.shapeLearnObviousFlagsDirectly).filter(isFn);
|
|
682
|
+
if (shapeFlags.length > 0) {
|
|
683
|
+
/* One mod asking for direct learning is enough - two mods both wanting the
|
|
684
|
+
* 4.2.6-era gap closed are not in conflict, they are two mods agreeing. */
|
|
685
|
+
out.shapeLearnObviousFlagsDirectly = (): boolean => shapeFlags.some((fn) => fn());
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const revisited = list.map((c) => c.levelRevisited).filter(isFn);
|
|
689
|
+
if (revisited.length > 0) {
|
|
690
|
+
/* Like optionsChanged, every contributor observes the same resume event.
|
|
691
|
+
* The chunk is intentionally live: this seam exists for a mod to update
|
|
692
|
+
* transient, level-owned state before monsters receive another turn. */
|
|
693
|
+
out.levelRevisited = (chunk, frozenAt, now): void => {
|
|
694
|
+
for (const fn of revisited) fn(chunk, frozenAt, now);
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
614
698
|
const text = list.map((c) => c.messageText).filter(isFn);
|
|
615
699
|
if (text.length > 0) {
|
|
616
700
|
out.messageText = (raw): string => text.reduce((s, fn) => fn(s), raw);
|
package/src/obj/knowledge.ts
CHANGED
|
@@ -798,11 +798,26 @@ export function equipLearnElement(p: Player, env: RuneEnv, element: number): voi
|
|
|
798
798
|
* shape_learn_on_assume (L1892): on taking a shape, learn its obvious
|
|
799
799
|
* (OFID_WIELD) flags and its resists, through the same equipment learn
|
|
800
800
|
* calls upstream uses.
|
|
801
|
+
*
|
|
802
|
+
* `learnObviousFlagsDirectly` is the ModHooks.shapeLearnObviousFlagsDirectly
|
|
803
|
+
* seam's resolved answer, threaded in by the caller (effect-general.ts'
|
|
804
|
+
* EF_SHAPECHANGE handler, the only one with a GameState to read a mod hook
|
|
805
|
+
* from - this module stays state-free, like the rest of obj/knowledge.ts).
|
|
806
|
+
* Faithful core passes `false`: a shape's obvious flag is then learned only
|
|
807
|
+
* through `equipLearnFlag`, which requires a WORN item to also carry the
|
|
808
|
+
* flag, so a flag the shape alone grants is never learned - the 4.2.6-era
|
|
809
|
+
* gap upstream's own `c8036c51537942a560e3d7f81749c431bbb4701f` fixed by
|
|
810
|
+
* learning the same already-computed set directly as runes instead. When
|
|
811
|
+
* true, this does exactly that IN ADDITION to the existing equipment call
|
|
812
|
+
* per flag: `playerLearnFlagRune` is a no-op for a flag already learned by
|
|
813
|
+
* the equipment path, so a flag the shape and a worn item both carry still
|
|
814
|
+
* fires the worn-item's own message and nothing here duplicates it.
|
|
801
815
|
*/
|
|
802
816
|
export function shapeLearnOnAssume(
|
|
803
817
|
p: Player,
|
|
804
818
|
env: RuneEnv,
|
|
805
819
|
shape: import("../player/types.js").Shape,
|
|
820
|
+
learnObviousFlagsDirectly = false,
|
|
806
821
|
): void {
|
|
807
822
|
/* Get the shape's obvious flags. */
|
|
808
823
|
const f = shape.flags.clone();
|
|
@@ -811,6 +826,7 @@ export function shapeLearnOnAssume(
|
|
|
811
826
|
/* Learn flags. */
|
|
812
827
|
for (let flag = f.next(FLAG_START); flag !== NO_FLAG; flag = f.next(flag + 1)) {
|
|
813
828
|
equipLearnFlag(p, env, flag);
|
|
829
|
+
if (learnObviousFlagsDirectly) playerLearnFlagRune(p, env, flag);
|
|
814
830
|
}
|
|
815
831
|
|
|
816
832
|
/* Learn elements. */
|
package/src/session/game.ts
CHANGED
|
@@ -2659,6 +2659,11 @@ function makeChangeLevel(
|
|
|
2659
2659
|
state.traps = stash.traps;
|
|
2660
2660
|
state.known = stash.known;
|
|
2661
2661
|
if (stash.decoy) state.decoy = stash.decoy;
|
|
2662
|
+
/* A level restored after single combat is the same kind of revisit as
|
|
2663
|
+
* one recovered from birth_levels_persist. Faithful core leaves every
|
|
2664
|
+
* field untouched; an opt-in mod can update transient per-level state
|
|
2665
|
+
* before the player and its monsters resume. */
|
|
2666
|
+
state.modHooks?.levelRevisited?.(state.chunk, stash.turn, state.turn);
|
|
2662
2667
|
const back = state.oldGrid ?? state.actor.grid;
|
|
2663
2668
|
state.actor.grid = back;
|
|
2664
2669
|
state.chunk.setMon(back, -1);
|
|
@@ -2788,6 +2793,12 @@ function makeChangeLevel(
|
|
|
2788
2793
|
* the turns the level was frozen (turn - chunk->freeze-turn). */
|
|
2789
2794
|
restoreMonsters(state, state.turn - stored.turn);
|
|
2790
2795
|
|
|
2796
|
+
/* A restored persistent level is live again. Core deliberately does
|
|
2797
|
+
* nothing here, matching 4.2.6; a mod may observe the exact freeze and
|
|
2798
|
+
* resume turns to update level-owned transient state before any monster
|
|
2799
|
+
* can receive a turn. */
|
|
2800
|
+
state.modHooks?.levelRevisited?.(state.chunk, stored.turn, state.turn);
|
|
2801
|
+
|
|
2791
2802
|
/* Place the player (prepare_next_level non-arena, L1497-1501): sanitize
|
|
2792
2803
|
* the retained grid into a legal arrival square, then player_place.
|
|
2793
2804
|
* DEFERRAL: exact stair-connector matching (dun->persist / one_off_*
|
package/src/version.ts
CHANGED
|
@@ -23,4 +23,4 @@ export const PARITY_BASELINE = "4.2.6";
|
|
|
23
23
|
* released tag is iterated takes a MINOR bump, because a published tag is pinned
|
|
24
24
|
* by digest in a catalogue and must never be moved.
|
|
25
25
|
*/
|
|
26
|
-
export const ENGINE_VERSION = "0.
|
|
26
|
+
export const ENGINE_VERSION = "0.34.0";
|