afnm-types 0.6.53 → 0.6.54-3
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/dist/avatarEffects.js +77 -77
- package/dist/buff.d.ts +6 -0
- package/dist/event.d.ts +5 -1
- package/dist/gameVersion.d.ts +1 -1
- package/dist/gameVersion.js +1 -1
- package/dist/item.d.ts +2 -9
- package/dist/itemAction.d.ts +33 -0
- package/dist/itemAction.js +1 -0
- package/dist/keybindings.d.ts +3 -3
- package/dist/keybindings.js +8 -0
- package/dist/manual.d.ts +8 -0
- package/dist/manual.js +1 -0
- package/dist/mod.d.ts +181 -14
- package/dist/reforge.d.ts +2 -0
- package/dist/soulShardDelve.d.ts +65 -157
- package/dist/soulShardDelve.js +7 -75
- package/dist/technique.d.ts +5 -0
- package/dist/typeTests.d.ts +9 -0
- package/package.json +1 -1
package/dist/avatarEffects.js
CHANGED
|
@@ -11,83 +11,83 @@
|
|
|
11
11
|
// Replicates the hand-authored glitch art style: strong persistent chromatic
|
|
12
12
|
// aberration (cyan/magenta fringing always visible), periodic brightness
|
|
13
13
|
// washout, and horizontal scanline tears during burst events.
|
|
14
|
-
const GLITCH_FRAG_SRC = `#version 300 es
|
|
15
|
-
precision mediump float;
|
|
16
|
-
in vec2 v_uv;
|
|
17
|
-
uniform sampler2D u_texA;
|
|
18
|
-
uniform sampler2D u_texB;
|
|
19
|
-
uniform float u_mix;
|
|
20
|
-
uniform float u_aspectA;
|
|
21
|
-
uniform float u_aspectB;
|
|
22
|
-
uniform float u_canvasAspect;
|
|
23
|
-
uniform float u_time;
|
|
24
|
-
out vec4 fragColor;
|
|
25
|
-
|
|
26
|
-
float hash(float n) {
|
|
27
|
-
return fract(sin(n) * 43758.5453);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
vec4 sampleContain(sampler2D tex, vec2 uv, float texAspect) {
|
|
31
|
-
float rel = texAspect / u_canvasAspect;
|
|
32
|
-
vec2 scale;
|
|
33
|
-
if (rel > 1.0) {
|
|
34
|
-
scale = vec2(1.0, 1.0 / rel);
|
|
35
|
-
} else {
|
|
36
|
-
scale = vec2(rel, 1.0);
|
|
37
|
-
}
|
|
38
|
-
vec2 offset = (1.0 - scale) * 0.5;
|
|
39
|
-
vec2 mapped = (uv - offset) / scale;
|
|
40
|
-
if (mapped.x < 0.0 || mapped.x > 1.0 || mapped.y < 0.0 || mapped.y > 1.0) {
|
|
41
|
-
return vec4(0.0);
|
|
42
|
-
}
|
|
43
|
-
return texture(tex, mapped);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
void main() {
|
|
47
|
-
vec2 uv = v_uv;
|
|
48
|
-
|
|
49
|
-
// Glitch burst timing — ~0.8 event slots per second, 35% chance each.
|
|
50
|
-
float eventT = floor(u_time * 0.8);
|
|
51
|
-
float isGlitching = step(0.65, hash(eventT * 1.618));
|
|
52
|
-
// Sharp onset, slow decay — the burst lingers before fading.
|
|
53
|
-
float slotFrac = fract(u_time * 0.8);
|
|
54
|
-
float decay = (1.0 - smoothstep(0.1, 0.9, slotFrac)) * isGlitching;
|
|
55
|
-
|
|
56
|
-
// Band-based horizontal displacement (scanline tearing) during bursts.
|
|
57
|
-
float band = floor(uv.y * 24.0);
|
|
58
|
-
float bandActive = step(0.55, hash(band + eventT * 23.7));
|
|
59
|
-
float bandShift = (hash(band * 5.2 + eventT * 7.1) * 2.0 - 1.0) * 0.025;
|
|
60
|
-
float shift = bandShift * bandActive * decay;
|
|
61
|
-
|
|
62
|
-
// Occasional large block tears.
|
|
63
|
-
float bigTear = step(0.92, hash(band * 3.7 + eventT * 11.3));
|
|
64
|
-
shift += (hash(band * 9.1 + eventT * 4.7) * 2.0 - 1.0) * 0.08 * bigTear * decay;
|
|
65
|
-
|
|
66
|
-
vec2 shiftedUv = vec2(uv.x + shift, uv.y);
|
|
67
|
-
|
|
68
|
-
// Chromatic aberration: strong baseline always present, amplified by bursts.
|
|
69
|
-
float aberr = 0.012 + 0.018 * decay;
|
|
70
|
-
|
|
71
|
-
float rA = sampleContain(u_texA, vec2(shiftedUv.x + aberr, shiftedUv.y), u_aspectA).r;
|
|
72
|
-
float gA = sampleContain(u_texA, shiftedUv, u_aspectA).g;
|
|
73
|
-
float bA = sampleContain(u_texA, vec2(shiftedUv.x - aberr, shiftedUv.y), u_aspectA).b;
|
|
74
|
-
float aA = sampleContain(u_texA, shiftedUv, u_aspectA).a;
|
|
75
|
-
vec4 colA = vec4(rA, gA, bA, aA);
|
|
76
|
-
|
|
77
|
-
float rB = sampleContain(u_texB, vec2(shiftedUv.x + aberr, shiftedUv.y), u_aspectB).r;
|
|
78
|
-
float gB = sampleContain(u_texB, shiftedUv, u_aspectB).g;
|
|
79
|
-
float bB = sampleContain(u_texB, vec2(shiftedUv.x - aberr, shiftedUv.y), u_aspectB).b;
|
|
80
|
-
float aB = sampleContain(u_texB, shiftedUv, u_aspectB).a;
|
|
81
|
-
vec4 colB = vec4(rB, gB, bB, aB);
|
|
82
|
-
|
|
83
|
-
vec4 col = mix(colA, colB, u_mix);
|
|
84
|
-
|
|
85
|
-
// Brightness washout during bursts — push toward overexposed white,
|
|
86
|
-
// scaled by alpha so transparent edges don't bloom.
|
|
87
|
-
float blowout = decay * 0.45;
|
|
88
|
-
col.rgb = mix(col.rgb, vec3(1.0), blowout * col.a);
|
|
89
|
-
|
|
90
|
-
fragColor = col;
|
|
14
|
+
const GLITCH_FRAG_SRC = `#version 300 es
|
|
15
|
+
precision mediump float;
|
|
16
|
+
in vec2 v_uv;
|
|
17
|
+
uniform sampler2D u_texA;
|
|
18
|
+
uniform sampler2D u_texB;
|
|
19
|
+
uniform float u_mix;
|
|
20
|
+
uniform float u_aspectA;
|
|
21
|
+
uniform float u_aspectB;
|
|
22
|
+
uniform float u_canvasAspect;
|
|
23
|
+
uniform float u_time;
|
|
24
|
+
out vec4 fragColor;
|
|
25
|
+
|
|
26
|
+
float hash(float n) {
|
|
27
|
+
return fract(sin(n) * 43758.5453);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
vec4 sampleContain(sampler2D tex, vec2 uv, float texAspect) {
|
|
31
|
+
float rel = texAspect / u_canvasAspect;
|
|
32
|
+
vec2 scale;
|
|
33
|
+
if (rel > 1.0) {
|
|
34
|
+
scale = vec2(1.0, 1.0 / rel);
|
|
35
|
+
} else {
|
|
36
|
+
scale = vec2(rel, 1.0);
|
|
37
|
+
}
|
|
38
|
+
vec2 offset = (1.0 - scale) * 0.5;
|
|
39
|
+
vec2 mapped = (uv - offset) / scale;
|
|
40
|
+
if (mapped.x < 0.0 || mapped.x > 1.0 || mapped.y < 0.0 || mapped.y > 1.0) {
|
|
41
|
+
return vec4(0.0);
|
|
42
|
+
}
|
|
43
|
+
return texture(tex, mapped);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void main() {
|
|
47
|
+
vec2 uv = v_uv;
|
|
48
|
+
|
|
49
|
+
// Glitch burst timing — ~0.8 event slots per second, 35% chance each.
|
|
50
|
+
float eventT = floor(u_time * 0.8);
|
|
51
|
+
float isGlitching = step(0.65, hash(eventT * 1.618));
|
|
52
|
+
// Sharp onset, slow decay — the burst lingers before fading.
|
|
53
|
+
float slotFrac = fract(u_time * 0.8);
|
|
54
|
+
float decay = (1.0 - smoothstep(0.1, 0.9, slotFrac)) * isGlitching;
|
|
55
|
+
|
|
56
|
+
// Band-based horizontal displacement (scanline tearing) during bursts.
|
|
57
|
+
float band = floor(uv.y * 24.0);
|
|
58
|
+
float bandActive = step(0.55, hash(band + eventT * 23.7));
|
|
59
|
+
float bandShift = (hash(band * 5.2 + eventT * 7.1) * 2.0 - 1.0) * 0.025;
|
|
60
|
+
float shift = bandShift * bandActive * decay;
|
|
61
|
+
|
|
62
|
+
// Occasional large block tears.
|
|
63
|
+
float bigTear = step(0.92, hash(band * 3.7 + eventT * 11.3));
|
|
64
|
+
shift += (hash(band * 9.1 + eventT * 4.7) * 2.0 - 1.0) * 0.08 * bigTear * decay;
|
|
65
|
+
|
|
66
|
+
vec2 shiftedUv = vec2(uv.x + shift, uv.y);
|
|
67
|
+
|
|
68
|
+
// Chromatic aberration: strong baseline always present, amplified by bursts.
|
|
69
|
+
float aberr = 0.012 + 0.018 * decay;
|
|
70
|
+
|
|
71
|
+
float rA = sampleContain(u_texA, vec2(shiftedUv.x + aberr, shiftedUv.y), u_aspectA).r;
|
|
72
|
+
float gA = sampleContain(u_texA, shiftedUv, u_aspectA).g;
|
|
73
|
+
float bA = sampleContain(u_texA, vec2(shiftedUv.x - aberr, shiftedUv.y), u_aspectA).b;
|
|
74
|
+
float aA = sampleContain(u_texA, shiftedUv, u_aspectA).a;
|
|
75
|
+
vec4 colA = vec4(rA, gA, bA, aA);
|
|
76
|
+
|
|
77
|
+
float rB = sampleContain(u_texB, vec2(shiftedUv.x + aberr, shiftedUv.y), u_aspectB).r;
|
|
78
|
+
float gB = sampleContain(u_texB, shiftedUv, u_aspectB).g;
|
|
79
|
+
float bB = sampleContain(u_texB, vec2(shiftedUv.x - aberr, shiftedUv.y), u_aspectB).b;
|
|
80
|
+
float aB = sampleContain(u_texB, shiftedUv, u_aspectB).a;
|
|
81
|
+
vec4 colB = vec4(rB, gB, bB, aB);
|
|
82
|
+
|
|
83
|
+
vec4 col = mix(colA, colB, u_mix);
|
|
84
|
+
|
|
85
|
+
// Brightness washout during bursts — push toward overexposed white,
|
|
86
|
+
// scaled by alpha so transparent edges don't bloom.
|
|
87
|
+
float blowout = decay * 0.45;
|
|
88
|
+
col.rgb = mix(col.rgb, vec3(1.0), blowout * col.a);
|
|
89
|
+
|
|
90
|
+
fragColor = col;
|
|
91
91
|
}`;
|
|
92
92
|
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
93
93
|
/**
|
package/dist/buff.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { CombatStatistic, Scaling } from './stat';
|
|
|
4
4
|
import type { DamageType } from './DamageType';
|
|
5
5
|
import type { CombatEntity } from './entity';
|
|
6
6
|
import { AvatarEffectId } from './avatarEffects';
|
|
7
|
+
import type { EffectTargeting } from './technique';
|
|
7
8
|
export type TechniqueCondition = ChanceTechniqueCondition | BuffTechniqueCondition | HpTechniqueCondition | ConditionTechniqueCondition | InventoryItemTechniqueCondition;
|
|
8
9
|
interface BaseTechniqueCondition {
|
|
9
10
|
/**
|
|
@@ -110,6 +111,7 @@ export interface Buff {
|
|
|
110
111
|
trigger?: TechniqueCondition;
|
|
111
112
|
damageModifier: DamageModifier;
|
|
112
113
|
effects?: BuffEffect[];
|
|
114
|
+
afterBarrier?: boolean;
|
|
113
115
|
}[];
|
|
114
116
|
/** Amplifies outgoing damage/barrier/heal effects. Runs before the effect is applied. */
|
|
115
117
|
techniqueAmplifierEffects?: {
|
|
@@ -339,11 +341,13 @@ interface HealEffect extends BaseBuff {
|
|
|
339
341
|
kind: 'heal';
|
|
340
342
|
amount: Scaling;
|
|
341
343
|
hits?: Scaling;
|
|
344
|
+
targeting?: EffectTargeting;
|
|
342
345
|
}
|
|
343
346
|
interface BarrierEffect extends BaseBuff {
|
|
344
347
|
kind: 'barrier';
|
|
345
348
|
amount: Scaling;
|
|
346
349
|
hits?: Scaling;
|
|
350
|
+
targeting?: EffectTargeting;
|
|
347
351
|
}
|
|
348
352
|
interface CreateBuffSelfEffect extends BaseBuff {
|
|
349
353
|
kind: 'buffSelf';
|
|
@@ -351,6 +355,7 @@ interface CreateBuffSelfEffect extends BaseBuff {
|
|
|
351
355
|
buff: Buff;
|
|
352
356
|
silent?: boolean;
|
|
353
357
|
hideBuff?: boolean;
|
|
358
|
+
targeting?: EffectTargeting;
|
|
354
359
|
}
|
|
355
360
|
interface ConsumeBuffSelfEffect extends BaseBuff {
|
|
356
361
|
kind: 'consumeSelf';
|
|
@@ -424,6 +429,7 @@ interface RepairEffect extends BaseBuff {
|
|
|
424
429
|
group: string;
|
|
425
430
|
/** Which matching buff(s) to repair: 'all', 'lowestHealth', or 'highestHealth' */
|
|
426
431
|
rule: RepairRule;
|
|
432
|
+
targeting?: EffectTargeting;
|
|
427
433
|
}
|
|
428
434
|
interface ConsumeInventoryItemEffect extends BaseBuff {
|
|
429
435
|
kind: 'consumeInventoryItem';
|
package/dist/event.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ export interface ChoiceStepChoice {
|
|
|
86
86
|
hideIfDisabled?: boolean;
|
|
87
87
|
children: EventStep[];
|
|
88
88
|
}
|
|
89
|
-
export type EventChoiceCondition = RealmCondition | PhysicalStatisticCondition | SocialStatisticCondition | ItemCondition | BuffCondition | MoneyCondition | FavourCondition | MultiCondition | AffinityCondition | QiCondition | ReputationCondition | HealthCondition | InjuredCondition;
|
|
89
|
+
export type EventChoiceCondition = RealmCondition | PhysicalStatisticCondition | SocialStatisticCondition | ItemCondition | BuffCondition | MoneyCondition | FavourCondition | MultiCondition | AffinityCondition | QiCondition | ReputationCondition | HealthCondition | InjuredCondition | SexualityCondition;
|
|
90
90
|
export interface RealmCondition {
|
|
91
91
|
kind: 'realm';
|
|
92
92
|
realm: Realm;
|
|
@@ -106,6 +106,10 @@ interface InjuredCondition {
|
|
|
106
106
|
kind: 'injured';
|
|
107
107
|
injured: boolean;
|
|
108
108
|
}
|
|
109
|
+
interface SexualityCondition {
|
|
110
|
+
kind: 'sexuality';
|
|
111
|
+
targetSex: 'male' | 'female';
|
|
112
|
+
}
|
|
109
113
|
interface SocialStatisticCondition {
|
|
110
114
|
kind: 'socialStatistic';
|
|
111
115
|
stat: SocialStatistic;
|
package/dist/gameVersion.d.ts
CHANGED
package/dist/gameVersion.js
CHANGED
package/dist/item.d.ts
CHANGED
|
@@ -7,12 +7,12 @@ import type { TechniqueElement } from './element';
|
|
|
7
7
|
import type { EnemyEntity, StoredRule } from './entity';
|
|
8
8
|
import type { Room } from './house';
|
|
9
9
|
import type { RegionContentType } from './mysticalRegion';
|
|
10
|
-
import type { AbsorberConfig } from './soulShardDelve';
|
|
11
10
|
import type { Rarity } from './rarity';
|
|
12
11
|
import type { Realm, RealmProgress } from './realm';
|
|
13
12
|
import type { RecipeDifficulty } from './RecipeDifficulty';
|
|
14
13
|
import type { CombatStatistic, CombatStatsMap, CraftingStatistic, CraftingStatsMap, PhysicalStatistic, Scaling, SocialStatistic } from './stat';
|
|
15
14
|
import type { Technique, TechniqueEffect } from './technique';
|
|
15
|
+
import { DelveRoom } from './soulShardDelve';
|
|
16
16
|
export declare const itemKinds: readonly ["clothing", "talisman", "artefact", "mount", "cauldron", "flame", "upgrade", "fruit", "elixir", "recipe", "technique", "action", "transport_seal", "enchantment", "pill", "reagent", "concoction", "consumable", "recuperation", "formation", "breakthrough", "pillar_shard", "material", "flare", "mystical_key", "condensation_art", "blueprint", "trophy", "treasure", "token", "life_essence", "device", "manual", "pillar_pattern", "local_map"];
|
|
17
17
|
export type ItemKind = (typeof itemKinds)[number];
|
|
18
18
|
export declare const itemKindToName: {
|
|
@@ -456,14 +456,7 @@ export interface PillarShardItem extends ItemBase {
|
|
|
456
456
|
kind: 'pillar_shard';
|
|
457
457
|
tooltip: Translatable;
|
|
458
458
|
maxInstances?: number;
|
|
459
|
-
|
|
460
|
-
* Shard subtype. 'soul' shards are used in soul shard delves — they have routing
|
|
461
|
-
* and a room type but no combat/crafting effects. Omit for normal pillar shards.
|
|
462
|
-
*/
|
|
463
|
-
shardSubtype?: 'soul';
|
|
464
|
-
/** Absorber config for soul shard delves. Hardcoded on soul shards; generated
|
|
465
|
-
* from the shard name hash for pillar shards when absent. */
|
|
466
|
-
absorberConfig?: AbsorberConfig;
|
|
459
|
+
delveRoomConfig?: DelveRoom;
|
|
467
460
|
variants?: PillarShardVariant[];
|
|
468
461
|
stability?: number;
|
|
469
462
|
portal?: {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { AppDispatch } from '../store';
|
|
3
|
+
import type { BreakthroughState } from './breakthrough';
|
|
4
|
+
import type { SoundEffectName } from './audio';
|
|
5
|
+
import type { Item } from './item';
|
|
6
|
+
import type { PlayerEntity } from './entity';
|
|
7
|
+
import type { InventoryState } from './reduxState';
|
|
8
|
+
interface OpenItem {
|
|
9
|
+
item: Item;
|
|
10
|
+
data: unknown;
|
|
11
|
+
onComplete: () => void;
|
|
12
|
+
}
|
|
13
|
+
export interface ItemActionContext {
|
|
14
|
+
item: Item;
|
|
15
|
+
player: PlayerEntity;
|
|
16
|
+
inventory: InventoryState;
|
|
17
|
+
breakthrough: BreakthroughState;
|
|
18
|
+
flags: Record<string, number | string | boolean>;
|
|
19
|
+
dispatch: AppDispatch;
|
|
20
|
+
setResult: (result: ReactNode) => void;
|
|
21
|
+
setClicked: (clicked: boolean) => void;
|
|
22
|
+
setOpenItem?: (openItem: OpenItem) => void;
|
|
23
|
+
setDoEnchant?: (doEnchant: boolean) => void;
|
|
24
|
+
setDoUpgrade?: (doUpgrade: boolean) => void;
|
|
25
|
+
setDoAppearanceChange?: (doAppearanceChange: boolean) => void;
|
|
26
|
+
playSfx: (sound: SoundEffectName) => void;
|
|
27
|
+
usageRestricted: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface ItemActionResult {
|
|
30
|
+
buttons: ReactNode[];
|
|
31
|
+
}
|
|
32
|
+
export type ItemActionHandler = (context: ItemActionContext) => ItemActionResult;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/keybindings.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export type KeybindingCategory = 'general' | 'navigation' | 'ui' | 'world' | 'combat' | 'crafting' | 'dialogs' | 'gamepad';
|
|
2
|
-
export type KeybindingAction = 'confirm' | 'cancel' | 'pause' | 'alternateConfirm' | 'moveUp' | 'moveDown' | 'moveLeft' | 'moveRight' | 'openInventory' | 'openQuests' | 'openCharacterStats' | 'openTechniques' | 'openCalendar' | 'openWorldMap' | 'combatSelectStance0' | 'combatSelectStance1' | 'combatSelectStance2' | 'combatSelectStance3' | 'combatSelectStance4' | 'combatSelectStance5' | 'combatSelectStance6' | 'combatSelectStance7' | 'combatSelectStance8' | 'combatSelectStance9' | 'combatToggleSpeed' | 'combatToggleLog' | 'combatShowStats' | 'combatAutoBattle' | 'combatUseItem' | 'craftingAction1' | 'craftingAction2' | 'craftingAction3' | 'craftingAction4' | 'craftingAction5' | 'craftingAction6' | 'craftingAction7' | 'craftingAction8' | 'craftingAction9' | 'craftingAction10' | 'craftingAction11' | 'craftingAction12' | 'craftingAction13' | 'craftingAction14' | 'craftingAction15' | 'craftingAction16' | 'craftingAction17' | 'craftingAction18' | 'craftingAction19' | 'craftingAction20' | 'craftingAction21' | 'craftingAction22' | 'craftingAction23' | 'craftingAction24' | 'craftingAction25' | 'craftingAction26' | 'craftingAction27' | 'craftingAction28' | 'craftingAction29' | 'craftingAction30' | 'craftingAction31' | 'craftingAction32' | 'craftingAction33' | 'craftingAction34' | 'craftingAction35' | 'craftingAction36' | 'craftingAction37' | 'craftingAction38' | 'craftingAction39' | 'craftingAction40' | 'craftingAction41' | 'craftingAction42' | 'craftingAction43' | 'craftingAction44' | 'craftingAction45' | 'craftingAction46' | 'craftingAction47' | 'craftingAction48' | 'craftingAction49' | 'craftingAction50' | 'dialogChoice1' | 'dialogChoice2' | 'dialogChoice3' | 'dialogChoice4' | 'dialogChoice5' | 'dialogChoice6' | 'dialogChoice7' | 'dialogChoice8' | 'dialogChoice9' | 'gamepadConfirm' | 'gamepadCancel' | 'gamepadUp' | 'gamepadDown' | 'gamepadLeft' | 'gamepadRight';
|
|
2
|
+
export type KeybindingAction = 'confirm' | 'cancel' | 'pause' | 'alternateConfirm' | 'moveUp' | 'moveDown' | 'moveLeft' | 'moveRight' | 'openInventory' | 'openQuests' | 'openCharacterStats' | 'openTechniques' | 'openCalendar' | 'openWorldMap' | 'combatSelectStance0' | 'combatSelectStance1' | 'combatSelectStance2' | 'combatSelectStance3' | 'combatSelectStance4' | 'combatSelectStance5' | 'combatSelectStance6' | 'combatSelectStance7' | 'combatSelectStance8' | 'combatSelectStance9' | 'combatToggleSpeed' | 'combatToggleLog' | 'combatShowStats' | 'combatAutoBattle' | 'combatUseItem' | 'combatCancel' | 'craftingAction1' | 'craftingAction2' | 'craftingAction3' | 'craftingAction4' | 'craftingAction5' | 'craftingAction6' | 'craftingAction7' | 'craftingAction8' | 'craftingAction9' | 'craftingAction10' | 'craftingAction11' | 'craftingAction12' | 'craftingAction13' | 'craftingAction14' | 'craftingAction15' | 'craftingAction16' | 'craftingAction17' | 'craftingAction18' | 'craftingAction19' | 'craftingAction20' | 'craftingAction21' | 'craftingAction22' | 'craftingAction23' | 'craftingAction24' | 'craftingAction25' | 'craftingAction26' | 'craftingAction27' | 'craftingAction28' | 'craftingAction29' | 'craftingAction30' | 'craftingAction31' | 'craftingAction32' | 'craftingAction33' | 'craftingAction34' | 'craftingAction35' | 'craftingAction36' | 'craftingAction37' | 'craftingAction38' | 'craftingAction39' | 'craftingAction40' | 'craftingAction41' | 'craftingAction42' | 'craftingAction43' | 'craftingAction44' | 'craftingAction45' | 'craftingAction46' | 'craftingAction47' | 'craftingAction48' | 'craftingAction49' | 'craftingAction50' | 'dialogChoice1' | 'dialogChoice2' | 'dialogChoice3' | 'dialogChoice4' | 'dialogChoice5' | 'dialogChoice6' | 'dialogChoice7' | 'dialogChoice8' | 'dialogChoice9' | 'gamepadConfirm' | 'gamepadCancel' | 'gamepadUp' | 'gamepadDown' | 'gamepadLeft' | 'gamepadRight';
|
|
3
3
|
export interface KeybindingDefinition {
|
|
4
|
-
action: KeybindingAction;
|
|
4
|
+
action: KeybindingAction | string;
|
|
5
5
|
category: KeybindingCategory;
|
|
6
6
|
displayName: string;
|
|
7
7
|
description: string;
|
|
8
8
|
defaultKey: string;
|
|
9
9
|
allowRebind: boolean;
|
|
10
10
|
}
|
|
11
|
-
export type KeybindingsMap = Record<KeybindingAction, string>;
|
|
11
|
+
export type KeybindingsMap = Record<KeybindingAction | string, string>;
|
|
12
12
|
export declare const keybindingDefinitions: KeybindingDefinition[];
|
|
13
13
|
export declare const keybindingCategoryInfo: Record<KeybindingCategory, {
|
|
14
14
|
name: string;
|
package/dist/keybindings.js
CHANGED
|
@@ -237,6 +237,14 @@ export const keybindingDefinitions = [
|
|
|
237
237
|
defaultKey: 's',
|
|
238
238
|
allowRebind: true,
|
|
239
239
|
},
|
|
240
|
+
{
|
|
241
|
+
action: 'combatCancel',
|
|
242
|
+
category: 'combat',
|
|
243
|
+
displayName: 'Cancel Auto Battle / Open Settings',
|
|
244
|
+
description: 'Cancel auto battle or open settings menu',
|
|
245
|
+
defaultKey: 'Escape',
|
|
246
|
+
allowRebind: true,
|
|
247
|
+
},
|
|
240
248
|
// Crafting Actions (Number keys 1-9, then 0)
|
|
241
249
|
{
|
|
242
250
|
action: 'craftingAction1',
|
package/dist/manual.d.ts
ADDED
package/dist/manual.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/mod.d.ts
CHANGED
|
@@ -49,6 +49,43 @@ import type { Translatable, TranslatableString } from './translatable';
|
|
|
49
49
|
import type { SaveFileInfo } from './electron';
|
|
50
50
|
import type { KeybindingDefinition } from './keybindings';
|
|
51
51
|
import { ScreenType } from './GameScreen';
|
|
52
|
+
export type SkipDialogueMode = 'flash' | 'silent';
|
|
53
|
+
export type Sexuality = 'straight' | 'gay' | 'bisexual';
|
|
54
|
+
export interface GameSettingsProps {
|
|
55
|
+
enableStancePreview: boolean;
|
|
56
|
+
setEnableStancePreview: (value: boolean) => void;
|
|
57
|
+
removeCombatNumbers: boolean;
|
|
58
|
+
setRemoveCombatNumbers: (value: boolean) => void;
|
|
59
|
+
fastRewardAnimations: boolean;
|
|
60
|
+
setFastRewardAnimations: (value: boolean) => void;
|
|
61
|
+
showCraftingRawNumbers: boolean;
|
|
62
|
+
setShowCraftingRawNumbers: (value: boolean) => void;
|
|
63
|
+
skipSeenDialogue: boolean;
|
|
64
|
+
setSkipSeenDialogue: (value: boolean) => void;
|
|
65
|
+
skipDialogueMode: SkipDialogueMode;
|
|
66
|
+
setSkipDialogueMode: (value: SkipDialogueMode) => void;
|
|
67
|
+
hideEmptySlotWarnings: boolean;
|
|
68
|
+
setHideEmptySlotWarnings: (value: boolean) => void;
|
|
69
|
+
pauseAutoBattleOnLowHealth: boolean;
|
|
70
|
+
setPauseAutoBattleOnLowHealth: (value: boolean) => void;
|
|
71
|
+
sexuality: Sexuality;
|
|
72
|
+
setSexuality: (value: Sexuality) => void;
|
|
73
|
+
eventHistoryLimit: number;
|
|
74
|
+
setEventHistoryLimit: (value: number) => void;
|
|
75
|
+
}
|
|
76
|
+
export type InjectPosition = 'inside' | 'before' | 'after';
|
|
77
|
+
export interface NewGameIntent {
|
|
78
|
+
items: ItemDesc[];
|
|
79
|
+
techniques: string[];
|
|
80
|
+
recipes: string[];
|
|
81
|
+
destinies: string[];
|
|
82
|
+
quests: string[];
|
|
83
|
+
money: number;
|
|
84
|
+
favour: number;
|
|
85
|
+
flags: Record<string, number>;
|
|
86
|
+
player: PlayerEntity;
|
|
87
|
+
craftingActions: string[];
|
|
88
|
+
}
|
|
52
89
|
/**
|
|
53
90
|
* Sprite images for a custom player character.
|
|
54
91
|
* All images should be strings (either mod:// URLs for mod assets, or data: URLs).
|
|
@@ -131,9 +168,10 @@ export interface ModReduxAPI {
|
|
|
131
168
|
usePlaySfx: () => (name: SoundEffectName) => void;
|
|
132
169
|
/**
|
|
133
170
|
* Hook to register keyboard shortcuts
|
|
134
|
-
* @param priority - Higher numbers
|
|
171
|
+
* @param priority - Higher numbers fully override lower numbers. Use 0 unless you have a specific reason to override other mods or base game keybindings.
|
|
135
172
|
* @param binding - Map of key names to callback functions
|
|
136
173
|
* @param disableSpaceOverride - If true, spacebar won't be overridden to map to Enter
|
|
174
|
+
* @param keyContext - Optional context string to group related keybindings (e.g. 'inventory', 'combat')
|
|
137
175
|
* @example
|
|
138
176
|
* useKeybinding(10, {
|
|
139
177
|
* 'Escape': () => closeDialog(),
|
|
@@ -141,7 +179,16 @@ export interface ModReduxAPI {
|
|
|
141
179
|
* 'i': () => openInventory()
|
|
142
180
|
* });
|
|
143
181
|
*/
|
|
144
|
-
useKeybinding: (priority: number, binding: Record<string, () => void> | undefined, disableSpaceOverride?: boolean) => void;
|
|
182
|
+
useKeybinding: (priority: number, binding: Record<string, () => void> | undefined, disableSpaceOverride?: boolean, keyContext?: string) => void;
|
|
183
|
+
/**
|
|
184
|
+
* Hook to access game settings/preferences.
|
|
185
|
+
* These are player-configured preferences like sexuality, combat display options, etc.
|
|
186
|
+
* @returns Object containing game settings and their setters
|
|
187
|
+
* @example
|
|
188
|
+
* const settings = useGameSettings();
|
|
189
|
+
* if (settings.sexuality === 'gay') { // Handle same-sex scenes }
|
|
190
|
+
*/
|
|
191
|
+
useGameSettings: () => GameSettingsProps;
|
|
145
192
|
actions: {
|
|
146
193
|
/**
|
|
147
194
|
* Navigate to a different game screen.
|
|
@@ -1061,6 +1108,21 @@ export interface ModAPI {
|
|
|
1061
1108
|
* });
|
|
1062
1109
|
*/
|
|
1063
1110
|
registerKeybinding: (definition: KeybindingDefinition) => void;
|
|
1111
|
+
/**
|
|
1112
|
+
* Trigger a UI refresh/reset across the entire game.
|
|
1113
|
+
* This causes all components wrapped in UIRefreshWrapper (including the combat screen,
|
|
1114
|
+
* dual cultivation screen, and other screen components) to unmount and remount,
|
|
1115
|
+
* forcing them to re-read their state and re-render fresh.
|
|
1116
|
+
*
|
|
1117
|
+
* Use this when your mod has modified game state that requires UI components
|
|
1118
|
+
* to fully re-render, such as after batch updates to Redux state or when
|
|
1119
|
+
* replacing content that mods might have extended.
|
|
1120
|
+
*
|
|
1121
|
+
* @example
|
|
1122
|
+
* // After making changes that require a full UI refresh
|
|
1123
|
+
* api.actions.triggerUIReset();
|
|
1124
|
+
*/
|
|
1125
|
+
triggerUIReset: () => void;
|
|
1064
1126
|
};
|
|
1065
1127
|
utils: {
|
|
1066
1128
|
/**
|
|
@@ -1995,6 +2057,24 @@ export interface ModAPI {
|
|
|
1995
2057
|
* const screen = determineCurrentScreen(store.getState());
|
|
1996
2058
|
*/
|
|
1997
2059
|
determineCurrentScreen: (rootState: RootState) => ScreenType;
|
|
2060
|
+
/**
|
|
2061
|
+
* Get the current keybind value for a registered keybinding action.
|
|
2062
|
+
* Use this to check what key is currently bound to an action, useful for
|
|
2063
|
+
* displaying key hints in custom UI or handling key events outside React components.
|
|
2064
|
+
* @param action - The action name (e.g., 'myMod.specialAction')
|
|
2065
|
+
* @returns The current bound key string (e.g., 'F12'), or undefined if not bound
|
|
2066
|
+
* @example
|
|
2067
|
+
* const key = modAPI.utils.getRegisteredKeybindValue('myMod.specialAction');
|
|
2068
|
+
* if (key) {
|
|
2069
|
+
* console.log(`Special action is bound to ${key}`);
|
|
2070
|
+
* }
|
|
2071
|
+
*/
|
|
2072
|
+
getRegisteredKeybindValue: (action: string) => string | undefined;
|
|
2073
|
+
/**
|
|
2074
|
+
*
|
|
2075
|
+
* @returns true if a save is currently loaded and game state is available, false otherwise.
|
|
2076
|
+
*/
|
|
2077
|
+
getHasSaveLoaded: () => boolean;
|
|
1998
2078
|
};
|
|
1999
2079
|
hooks: {
|
|
2000
2080
|
/**
|
|
@@ -2062,6 +2142,26 @@ export interface ModAPI {
|
|
|
2062
2142
|
* });
|
|
2063
2143
|
*/
|
|
2064
2144
|
onDeriveRecipeDifficulty: (interceptor: (recipe: RecipeItem, recipeStats: CraftingRecipeStats, gameFlags: Record<string, number>) => CraftingRecipeStats) => void;
|
|
2145
|
+
/**
|
|
2146
|
+
* Hook to modify recipe ingredients before crafting calculations.
|
|
2147
|
+
* This runs before onDeriveRecipeDifficulty and allows mods to change
|
|
2148
|
+
* the items or amounts used in a recipe.
|
|
2149
|
+
* @param interceptor - Function to modify the recipe
|
|
2150
|
+
* @example
|
|
2151
|
+
* onModifyRecipeIngredients((recipe, flags) => {
|
|
2152
|
+
* if (flags.efficient_crafting) {
|
|
2153
|
+
* // Reduce all ingredient counts by 50%
|
|
2154
|
+
* const modified = { ...recipe };
|
|
2155
|
+
* modified.ingredients = recipe.ingredients.map(ing => ({
|
|
2156
|
+
* ...ing,
|
|
2157
|
+
* count: Math.max(1, Math.floor(ing.count * 0.5))
|
|
2158
|
+
* }));
|
|
2159
|
+
* return modified;
|
|
2160
|
+
* }
|
|
2161
|
+
* return recipe;
|
|
2162
|
+
* });
|
|
2163
|
+
*/
|
|
2164
|
+
onModifyRecipeIngredients: (interceptor: (recipe: RecipeItem, gameFlags: Record<string, number>) => RecipeItem) => void;
|
|
2065
2165
|
/**
|
|
2066
2166
|
* Hook to add events after combat completion.
|
|
2067
2167
|
* @param interceptor - Function returning additional event steps
|
|
@@ -2267,34 +2367,98 @@ export interface ModAPI {
|
|
|
2267
2367
|
* return payload;
|
|
2268
2368
|
* });
|
|
2269
2369
|
*/
|
|
2270
|
-
onReduxActionPayload: (interceptor: (actionType: string, payload: unknown) => unknown | null) => void;
|
|
2370
|
+
onReduxActionPayload: (interceptor: (actionType: string, payload: unknown, stateBefore: RootState) => unknown | null) => void;
|
|
2371
|
+
/**
|
|
2372
|
+
* Called before the equipment upgrade dialog is shown. Allows mutating upgrade cost items and result (but not base item).
|
|
2373
|
+
*
|
|
2374
|
+
* @example
|
|
2375
|
+
* modAPI.hooks.onDeriveEquipmentUpgradeRequirement((baseItem, costItems, resultItem, flags) => {
|
|
2376
|
+
* // Modify the cost items (e.g., add additional requirements)
|
|
2377
|
+
* return { costItems: [...costItems, additionalItem], resultItem };
|
|
2378
|
+
* });
|
|
2379
|
+
*/
|
|
2380
|
+
onDeriveEquipmentUpgradeRequirement: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => {
|
|
2381
|
+
costItems?: Item[];
|
|
2382
|
+
resultItem?: Item;
|
|
2383
|
+
} | undefined) => void;
|
|
2384
|
+
/**
|
|
2385
|
+
* Called before the completion dialog showing the upgraded equipment. Allows mutating upgrade cost items and result (but not base item).
|
|
2386
|
+
*
|
|
2387
|
+
* @example
|
|
2388
|
+
* modAPI.hooks.onCompleteEquipmentUpgrade((baseItem, costItems, resultItem, flags) => {
|
|
2389
|
+
* // Boost the result item's stats
|
|
2390
|
+
* return { costItems, resultItem: { ...resultItem, damage: (resultItem.damage ?? 0) + 10 } };
|
|
2391
|
+
* });
|
|
2392
|
+
*/
|
|
2393
|
+
onCompleteEquipmentUpgrade: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => {
|
|
2394
|
+
costItems?: Item[];
|
|
2395
|
+
resultItem?: Item;
|
|
2396
|
+
} | undefined) => void;
|
|
2397
|
+
/**
|
|
2398
|
+
* Called before the equipment reforge dialog is shown. Allows mutating reforge cost items and result (but not base item).
|
|
2399
|
+
*
|
|
2400
|
+
* @example
|
|
2401
|
+
* modAPI.hooks.onDeriveEquipmentReforgeRequirement((baseItem, costItems, resultItem, flags) => {
|
|
2402
|
+
* // Modify the result item
|
|
2403
|
+
* return { costItems, resultItem: { ...resultItem, qualityTier: (resultItem.qualityTier ?? 0) + 1 } };
|
|
2404
|
+
* });
|
|
2405
|
+
*/
|
|
2406
|
+
onDeriveEquipmentReforgeRequirement: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => {
|
|
2407
|
+
costItems?: Item[];
|
|
2408
|
+
resultItem?: Item;
|
|
2409
|
+
} | undefined) => void;
|
|
2410
|
+
/**
|
|
2411
|
+
* Called before the completion dialog showing the reforged equipment. Allows mutating reforge cost items and result (but not base item).
|
|
2412
|
+
*
|
|
2413
|
+
* @example
|
|
2414
|
+
* modAPI.hooks.onCompleteEquipmentReforge((baseItem, costItems, resultItem, flags) => {
|
|
2415
|
+
* // Boost the result item's stats
|
|
2416
|
+
* return { costItems, resultItem: { ...resultItem, damage: (resultItem.damage ?? 0) + 5 } };
|
|
2417
|
+
* });
|
|
2418
|
+
*/
|
|
2419
|
+
onCompleteEquipmentReforge: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => {
|
|
2420
|
+
costItems?: Item[];
|
|
2421
|
+
resultItem?: Item;
|
|
2422
|
+
} | undefined) => void;
|
|
2423
|
+
/**
|
|
2424
|
+
* Intercept the full set of changes planned for a new game before they are applied.
|
|
2425
|
+
* Return a modified intent to change starting items, techniques, flags, etc.
|
|
2426
|
+
* @param interceptor - Receives the full intent and returns a modified copy
|
|
2427
|
+
* @example
|
|
2428
|
+
* modAPI.hooks.onNewGame((intent) => ({
|
|
2429
|
+
* ...intent,
|
|
2430
|
+
* flags: { ...intent.flags, my_mod_enabled: 1 },
|
|
2431
|
+
* }));
|
|
2432
|
+
*/
|
|
2433
|
+
onNewGame: (interceptor: (intent: NewGameIntent) => NewGameIntent) => void;
|
|
2271
2434
|
};
|
|
2272
2435
|
/**
|
|
2273
2436
|
* Inject UI into a named slot (dialog title or screen name).
|
|
2274
2437
|
*
|
|
2275
|
-
* The generator receives
|
|
2276
|
-
* -
|
|
2277
|
-
* -
|
|
2278
|
-
* -
|
|
2279
|
-
* matched by
|
|
2438
|
+
* The generator receives (api, element, inject):
|
|
2439
|
+
* - api: full ModReduxAPI with game state, actions, and components
|
|
2440
|
+
* - element: root DOM element of the slot; use querySelector to find children
|
|
2441
|
+
* - inject(selector, content, mode?, position?): helper to portal content into the element
|
|
2442
|
+
* matched by selector. mode is 'overlay' (default, floats over) or 'inline'
|
|
2280
2443
|
* (inserts as a sibling after the target, flowing with the layout).
|
|
2444
|
+
* position controls where content is placed (see InjectPosition enum).
|
|
2281
2445
|
*
|
|
2282
|
-
* Slot names for dialogs are the id (e.g.
|
|
2283
|
-
* Slot names for screens match the
|
|
2446
|
+
* Slot names for dialogs are the id (e.g. 'combat-victory'). You can find these by inspecting the DOM in dev mode.
|
|
2447
|
+
* Slot names for screens match the ScreenType value (e.g. 'combat').
|
|
2284
2448
|
*
|
|
2285
2449
|
* @example
|
|
2286
|
-
* // Add a
|
|
2287
|
-
* // The victory dialog's main content area has aria-live="assertive".
|
|
2450
|
+
* // Add a button to combat victory dialog
|
|
2288
2451
|
* modAPI.ui.injectUI('combat-victory', (api, element, inject) => {
|
|
2289
2452
|
* return inject('[aria-live="assertive"]',
|
|
2290
2453
|
* <api.components.GameButton onClick={() => api.actions.changeQi(100)}>
|
|
2291
2454
|
* Absorb Qi
|
|
2292
2455
|
* </api.components.GameButton>,
|
|
2293
|
-
* 'inline'
|
|
2456
|
+
* 'inline',
|
|
2457
|
+
* InjectPosition.After
|
|
2294
2458
|
* );
|
|
2295
2459
|
* });
|
|
2296
2460
|
*/
|
|
2297
|
-
injectUI: (slotName: string, generator: (api: ModReduxAPI, element: HTMLElement | null, inject: (selector: string, content: React.ReactNode, mode?: 'overlay' | 'inline') => React.ReactNode) => React.ReactNode) => void;
|
|
2461
|
+
injectUI: (slotName: string, generator: (api: ModReduxAPI, element: HTMLElement | null, inject: (selector: string, content: React.ReactNode, mode?: 'overlay' | 'inline', position?: InjectPosition) => React.ReactNode) => React.ReactNode) => void;
|
|
2298
2462
|
/**
|
|
2299
2463
|
* Subscribe to any Redux state changes. The callback receives the new state.
|
|
2300
2464
|
* Returns an unsubscribe function.
|
|
@@ -2319,3 +2483,6 @@ export interface ModAPI {
|
|
|
2319
2483
|
*/
|
|
2320
2484
|
getGameStateSnapshot: () => RootState | null;
|
|
2321
2485
|
}
|
|
2486
|
+
export type ModHooks = {
|
|
2487
|
+
[K in keyof ModAPI['hooks']]: Parameters<ModAPI['hooks'][K]>[0][];
|
|
2488
|
+
};
|
package/dist/reforge.d.ts
CHANGED
package/dist/soulShardDelve.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { ItemDesc, RegionMonsters } from './item';
|
|
|
3
3
|
import type { Rarity } from './rarity';
|
|
4
4
|
import type { Beam } from './pillarGrid';
|
|
5
5
|
import { Translatable } from './translatable';
|
|
6
|
+
import { Blessing, EnemyEntity } from '.';
|
|
7
|
+
import Prando from 'prando';
|
|
6
8
|
export interface PlacedShardEntry {
|
|
7
9
|
shardName: string;
|
|
8
10
|
x: number;
|
|
@@ -19,8 +21,40 @@ export interface GridSimulationResult {
|
|
|
19
21
|
power?: number;
|
|
20
22
|
}[];
|
|
21
23
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
export type DelveRoomStageKind = 'combat' | 'blessing' | 'curse' | 'reward' | 'event';
|
|
25
|
+
interface BaseDelveRoomStage {
|
|
26
|
+
kind: DelveRoomStageKind;
|
|
27
|
+
}
|
|
28
|
+
export interface CombatStage extends BaseDelveRoomStage {
|
|
29
|
+
kind: 'combat';
|
|
30
|
+
buildEnemies: (monsters: RegionMonsters, difficultyMult: number, rng: Prando) => EnemyEntity[];
|
|
31
|
+
}
|
|
32
|
+
export interface BlessingStage extends BaseDelveRoomStage {
|
|
33
|
+
kind: 'blessing';
|
|
34
|
+
blessingPool: Blessing[];
|
|
35
|
+
count: number;
|
|
36
|
+
}
|
|
37
|
+
export interface CurseStage extends BaseDelveRoomStage {
|
|
38
|
+
kind: 'curse';
|
|
39
|
+
cursePool: Blessing[];
|
|
40
|
+
count: number;
|
|
41
|
+
}
|
|
42
|
+
export interface RewardStage extends BaseDelveRoomStage {
|
|
43
|
+
kind: 'reward';
|
|
44
|
+
rewardPool: string;
|
|
45
|
+
increasedRarity: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface EventStage extends BaseDelveRoomStage {
|
|
48
|
+
kind: 'event';
|
|
49
|
+
event: GameEvent;
|
|
50
|
+
}
|
|
51
|
+
export type DelveRoomStage = CombatStage | BlessingStage | CurseStage | RewardStage | EventStage;
|
|
52
|
+
export interface DelveRoom {
|
|
53
|
+
name: string;
|
|
54
|
+
displayName?: Translatable;
|
|
55
|
+
description: string;
|
|
56
|
+
stages: DelveRoomStage[];
|
|
57
|
+
}
|
|
24
58
|
/** A pre-placed cell in the soul shard grid (source, fixed node, or fixed routing shard). */
|
|
25
59
|
export interface FixedGridCell {
|
|
26
60
|
inputs?: {
|
|
@@ -40,114 +74,17 @@ export interface FixedGridCell {
|
|
|
40
74
|
isNode?: boolean;
|
|
41
75
|
icon: string;
|
|
42
76
|
isSource: boolean;
|
|
43
|
-
/** Minimum
|
|
77
|
+
/** Minimum qither through this node to add a one-time reward to the run. */
|
|
44
78
|
nodeRoomThreshold?: number;
|
|
45
79
|
}
|
|
46
|
-
/** A reward slot earned from a room — the specific item is rolled when the remnant is opened. */
|
|
47
|
-
export interface VictoryReward {
|
|
48
|
-
rarity: Rarity;
|
|
49
|
-
}
|
|
50
|
-
/** Direct reward — no combat. Adds a deferred chest to end rewards. */
|
|
51
|
-
export interface DirectRewardContent {
|
|
52
|
-
kind: 'directReward';
|
|
53
|
-
reward: VictoryReward;
|
|
54
|
-
}
|
|
55
|
-
/** Cursed reward — gain reward immediately + a persistent curse for the rest of the delve. */
|
|
56
|
-
export interface CursedRewardContent {
|
|
57
|
-
kind: 'cursedReward';
|
|
58
|
-
reward: VictoryReward;
|
|
59
|
-
curse: {
|
|
60
|
-
buffName: string;
|
|
61
|
-
buffStacks: number;
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
/** Trade reward — sacrifice a spirit core for a powerful reward. */
|
|
65
|
-
export interface TradeRewardContent {
|
|
66
|
-
kind: 'tradeReward';
|
|
67
|
-
coreName: string;
|
|
68
|
-
reward: VictoryReward;
|
|
69
|
-
}
|
|
70
|
-
/** Figment — fight 3–4 normal enemies as a single party. Normal combat. */
|
|
71
|
-
export interface FigmentContent {
|
|
72
|
-
kind: 'figment';
|
|
73
|
-
enemyTier: number;
|
|
74
|
-
enemyCount: number;
|
|
75
|
-
victoryReward: VictoryReward;
|
|
76
|
-
}
|
|
77
|
-
/** Elite combat — fight 1 elite. Normal combat. */
|
|
78
|
-
export interface EliteCombatContent {
|
|
79
|
-
kind: 'eliteCombat';
|
|
80
|
-
enemyTier: number;
|
|
81
|
-
victoryReward: VictoryReward;
|
|
82
|
-
}
|
|
83
|
-
/** Augmented elite — fight 1 elite + 1 normal as party. Normal combat. */
|
|
84
|
-
export interface AugmentedEliteContent {
|
|
85
|
-
kind: 'augmentedElite';
|
|
86
|
-
enemyTier: number;
|
|
87
|
-
victoryReward: VictoryReward;
|
|
88
|
-
}
|
|
89
|
-
/** Horde combat — fight 2 normals + 1 elite as back-to-back sequential enemies. Normal combat. */
|
|
90
|
-
export interface HordeCombatContent {
|
|
91
|
-
kind: 'hordeCombat';
|
|
92
|
-
enemyTier: number;
|
|
93
|
-
victoryReward: VictoryReward;
|
|
94
|
-
}
|
|
95
|
-
/** Resurrect — fight 1 elite + 1 normal as party; either resurrects if not killed within 2 rounds. Hard combat. */
|
|
96
|
-
export interface ResurrectContent {
|
|
97
|
-
kind: 'resurrect';
|
|
98
|
-
enemyTier: number;
|
|
99
|
-
victoryReward: VictoryReward;
|
|
100
|
-
}
|
|
101
|
-
/** Reinforcement — fight 1 elite with 1 normal summoned every 6 rounds (up to 4 party members). Hard combat. */
|
|
102
|
-
export interface ReinforcementContent {
|
|
103
|
-
kind: 'reinforcement';
|
|
104
|
-
enemyTier: number;
|
|
105
|
-
victoryReward: VictoryReward;
|
|
106
|
-
}
|
|
107
|
-
/** Penalty — any normal combat, but also applies a dangerous curse for the fight. Hard combat. */
|
|
108
|
-
export interface PenaltyContent {
|
|
109
|
-
kind: 'penalty';
|
|
110
|
-
enemyTier: number;
|
|
111
|
-
playerCurse: {
|
|
112
|
-
buffName: string;
|
|
113
|
-
buffStacks: number;
|
|
114
|
-
};
|
|
115
|
-
victoryReward: VictoryReward;
|
|
116
|
-
}
|
|
117
|
-
/** Dual — fight 2 elites as a party. Hard combat. */
|
|
118
|
-
export interface DualContent {
|
|
119
|
-
kind: 'dual';
|
|
120
|
-
enemyTier: number;
|
|
121
|
-
victoryReward: VictoryReward;
|
|
122
|
-
}
|
|
123
|
-
/** Trio — fight 3 elites back to back (sequential). Hard combat. */
|
|
124
|
-
export interface TrioContent {
|
|
125
|
-
kind: 'trio';
|
|
126
|
-
enemyTier: number;
|
|
127
|
-
victoryReward: VictoryReward;
|
|
128
|
-
}
|
|
129
|
-
/** Blessing select — choose 1 of 3 blessings drawn from the shard's themed pool. */
|
|
130
|
-
export interface BlessingSelectContent {
|
|
131
|
-
kind: 'blessingSelect';
|
|
132
|
-
options: {
|
|
133
|
-
buffName: string;
|
|
134
|
-
buffStacks: number;
|
|
135
|
-
}[];
|
|
136
|
-
}
|
|
137
|
-
/** Core blessing — trade a spirit core for 1 of 3 powerful blessings. */
|
|
138
|
-
export interface CoreBlessingContent {
|
|
139
|
-
kind: 'coreBlessing';
|
|
140
|
-
coreName: string;
|
|
141
|
-
options: {
|
|
142
|
-
buffName: string;
|
|
143
|
-
buffStacks: number;
|
|
144
|
-
}[];
|
|
145
|
-
}
|
|
146
|
-
export type DelveRoomContent = DirectRewardContent | CursedRewardContent | TradeRewardContent | FigmentContent | EliteCombatContent | AugmentedEliteContent | HordeCombatContent | ResurrectContent | ReinforcementContent | PenaltyContent | DualContent | TrioContent | BlessingSelectContent | CoreBlessingContent;
|
|
147
80
|
export interface DelveDropPool {
|
|
148
81
|
id: string;
|
|
149
82
|
displayName: Translatable;
|
|
150
|
-
items:
|
|
83
|
+
items: {
|
|
84
|
+
name: string;
|
|
85
|
+
stacks: number;
|
|
86
|
+
rarity: Rarity;
|
|
87
|
+
}[];
|
|
151
88
|
}
|
|
152
89
|
/** A remnant accumulated during the delve. Item is rolled from pool when opened. */
|
|
153
90
|
export interface PendingReward {
|
|
@@ -155,50 +92,22 @@ export interface PendingReward {
|
|
|
155
92
|
/** Which loot pool to draw from when the remnant is opened. */
|
|
156
93
|
pool: string;
|
|
157
94
|
}
|
|
158
|
-
export type BlessingRoomType = 'blessingSelect' | 'coreBlessing';
|
|
159
|
-
export type RewardRoomType = Exclude<DelveRoomType, BlessingRoomType>;
|
|
160
|
-
/** Absorber config for reward/combat rooms — always has a reward pool. */
|
|
161
|
-
export interface RewardAbsorberConfig {
|
|
162
|
-
roomType: RewardRoomType;
|
|
163
|
-
rewardPoolId: string;
|
|
164
|
-
cursePool?: string[];
|
|
165
|
-
fightCursePool?: string[];
|
|
166
|
-
}
|
|
167
|
-
/** Absorber config for blessing rooms — has a blessing pool, no reward pool. */
|
|
168
|
-
export interface BlessingAbsorberConfig {
|
|
169
|
-
roomType: BlessingRoomType;
|
|
170
|
-
blessingPool?: string[];
|
|
171
|
-
}
|
|
172
|
-
export type AbsorberConfig = RewardAbsorberConfig | BlessingAbsorberConfig;
|
|
173
|
-
export interface DelveRoomDef {
|
|
174
|
-
label: string;
|
|
175
|
-
desc: string;
|
|
176
|
-
/** Rarity offset relative to the shard's base tier (-1, 0, +1). */
|
|
177
|
-
tierOffset: -1 | 0 | 1;
|
|
178
|
-
}
|
|
179
|
-
export declare const DELVE_ROOM_DEFS: Record<DelveRoomType, DelveRoomDef>;
|
|
180
95
|
/** Static config for a soul shard delve location. */
|
|
181
96
|
export interface SoulShardDelveConfig {
|
|
182
97
|
key: string;
|
|
183
|
-
|
|
184
|
-
description: string;
|
|
185
|
-
icon: string;
|
|
186
|
-
background: string;
|
|
187
|
-
/** The overall tier of this shard — determines base reward rarity. */
|
|
188
|
-
tier: Rarity;
|
|
98
|
+
location: string;
|
|
189
99
|
/** Events played when each threshold is crossed, in order. Length determines total threshold count. */
|
|
190
|
-
thresholdEvents:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
100
|
+
thresholdEvents: GameEvent[];
|
|
101
|
+
/** One-time item rewards granted when accumulatedQither reaches each intensity level. */
|
|
102
|
+
intensityRewards: {
|
|
103
|
+
intensity: number;
|
|
104
|
+
reward: ItemDesc;
|
|
105
|
+
}[];
|
|
194
106
|
/** Source power regeneration rate: 1 qither per this many months. Default 6. */
|
|
195
107
|
rechargeMonths: number;
|
|
196
|
-
music?: string;
|
|
197
|
-
/** Enemy pools — mob, elite, boss — same as mystical regions. */
|
|
198
108
|
monsters: RegionMonsters;
|
|
199
|
-
/**
|
|
200
|
-
|
|
201
|
-
/** Loot pools by category. */
|
|
109
|
+
/** The boss room fought after all vestige rooms are cleared. */
|
|
110
|
+
boss: DelveRoom;
|
|
202
111
|
customDropPool: DelveDropPool;
|
|
203
112
|
}
|
|
204
113
|
/** Persistent placed shard entry (survives between sessions). */
|
|
@@ -221,39 +130,38 @@ export interface SoulShardProgression {
|
|
|
221
130
|
sourcePower: number;
|
|
222
131
|
/** Months elapsed toward next recharge tick. */
|
|
223
132
|
rechargeProgress: number;
|
|
133
|
+
/** How many intensityRewards have already been granted. */
|
|
134
|
+
claimedIntensityRewards: number;
|
|
224
135
|
}
|
|
225
136
|
/** Current run state stored in Redux. */
|
|
226
137
|
export interface SoulShardDelveRunState {
|
|
227
138
|
shards: string[];
|
|
228
139
|
currentRoomIndex: number;
|
|
140
|
+
currentStageIndex: number;
|
|
229
141
|
failedRun: boolean;
|
|
230
142
|
progressQither: number;
|
|
231
143
|
difficultyMultiplier: number;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}[];
|
|
237
|
-
/** Persistent curses gained from cursedReward rooms (applied to player in every subsequent fight). */
|
|
238
|
-
delveCurses: {
|
|
239
|
-
buffName: string;
|
|
240
|
-
stacks: number;
|
|
241
|
-
}[];
|
|
242
|
-
/** Rewards accumulated during the run, revealed as chests at the end. */
|
|
243
|
-
pendingRewards: PendingReward[];
|
|
144
|
+
blessings: string[];
|
|
145
|
+
curses: string[];
|
|
146
|
+
rewards: PendingReward[];
|
|
147
|
+
openedRewards: boolean[];
|
|
244
148
|
}
|
|
245
149
|
export interface SoulShardDelveState {
|
|
246
150
|
/** Which soul shard config is currently selected (screen is open). */
|
|
247
151
|
delveKey?: string;
|
|
248
152
|
currentRun?: SoulShardDelveRunState;
|
|
249
|
-
/** delveKey -> calendar day the cooldown expires */
|
|
250
|
-
cooldowns: Record<string, number>;
|
|
251
153
|
/** delveKey -> persistent progression data */
|
|
252
154
|
progression: Record<string, SoulShardProgression>;
|
|
253
155
|
}
|
|
254
|
-
/** Cumulative qither required to reach threshold n (1-indexed).
|
|
156
|
+
/** Cumulative qither required to reach threshold n (1-indexed).
|
|
157
|
+
* Based on ~2 perfect runs per threshold, where a perfect run = 3 + gridSize.
|
|
158
|
+
* Grid starts at ~60 cells and grows by ~15 per threshold, so run(t) = 63 + t*15.
|
|
159
|
+
* Cumulative: sum of 2*(63 + t*15) for t=0..n-1 = n*(111 + 15n).
|
|
160
|
+
* Gives: 126, 282, 468, 684, 930, ...
|
|
161
|
+
*/
|
|
255
162
|
export declare function getThresholdQither(n: number): number;
|
|
256
163
|
/** Enemy difficulty multiplier from total qither used to activate all absorbers.
|
|
257
164
|
* +2% HP and power per 1 qither.
|
|
258
165
|
*/
|
|
259
166
|
export declare function getQitherDifficulty(totalQither: number): number;
|
|
167
|
+
export {};
|
package/dist/soulShardDelve.js
CHANGED
|
@@ -1,79 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
cursedReward: {
|
|
8
|
-
label: 'Cursed Offering',
|
|
9
|
-
desc: 'Gain a remnant immediately, but carry a curse for the rest of the delve',
|
|
10
|
-
tierOffset: 0,
|
|
11
|
-
},
|
|
12
|
-
tradeReward: {
|
|
13
|
-
label: 'Bargain',
|
|
14
|
-
desc: 'Trade a spirit core to gain a remnant',
|
|
15
|
-
tierOffset: 0,
|
|
16
|
-
},
|
|
17
|
-
figment: {
|
|
18
|
-
label: 'Figment',
|
|
19
|
-
desc: 'Fight three to four shades at once to gain a remnant',
|
|
20
|
-
tierOffset: 0,
|
|
21
|
-
},
|
|
22
|
-
eliteCombat: {
|
|
23
|
-
label: 'Keeper',
|
|
24
|
-
desc: 'Defeat an aspect to gain a remnant',
|
|
25
|
-
tierOffset: 0,
|
|
26
|
-
},
|
|
27
|
-
augmentedElite: {
|
|
28
|
-
label: "Keeper's Shadow",
|
|
29
|
-
desc: 'Defeat an aspect with a shade at its side to gain a remnant',
|
|
30
|
-
tierOffset: 0,
|
|
31
|
-
},
|
|
32
|
-
hordeCombat: {
|
|
33
|
-
label: 'Surge',
|
|
34
|
-
desc: 'Fight two shades then an aspect in sequence to gain a remnant',
|
|
35
|
-
tierOffset: 0,
|
|
36
|
-
},
|
|
37
|
-
resurrect: {
|
|
38
|
-
label: 'Tethered',
|
|
39
|
-
desc: 'Fight an aspect and a shade; either will revive the other if not killed together',
|
|
40
|
-
tierOffset: 1,
|
|
41
|
-
},
|
|
42
|
-
reinforcement: {
|
|
43
|
-
label: 'Summoner',
|
|
44
|
-
desc: 'Fight an aspect while it calls in shades every six rounds',
|
|
45
|
-
tierOffset: 1,
|
|
46
|
-
},
|
|
47
|
-
penalty: {
|
|
48
|
-
label: 'Marked',
|
|
49
|
-
desc: 'Fight while carrying a dangerous curse to gain a remnant',
|
|
50
|
-
tierOffset: 1,
|
|
51
|
-
},
|
|
52
|
-
dual: {
|
|
53
|
-
label: 'Twin Keepers',
|
|
54
|
-
desc: 'Fight two aspects at once to gain a remnant',
|
|
55
|
-
tierOffset: 1,
|
|
56
|
-
},
|
|
57
|
-
trio: {
|
|
58
|
-
label: 'Triad',
|
|
59
|
-
desc: 'Fight three aspects in sequence to gain a remnant',
|
|
60
|
-
tierOffset: 1,
|
|
61
|
-
},
|
|
62
|
-
blessingSelect: {
|
|
63
|
-
label: 'Attunement',
|
|
64
|
-
desc: "Choose one blessing from three drawn from the shard's pool",
|
|
65
|
-
tierOffset: 0,
|
|
66
|
-
},
|
|
67
|
-
coreBlessing: {
|
|
68
|
-
label: 'Deep Attunement',
|
|
69
|
-
desc: 'Trade a spirit core for one of three powerful blessings',
|
|
70
|
-
tierOffset: 0,
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
/** Cumulative qither required to reach threshold n (1-indexed). Scales quadratically. */
|
|
1
|
+
/** Cumulative qither required to reach threshold n (1-indexed).
|
|
2
|
+
* Based on ~2 perfect runs per threshold, where a perfect run = 3 + gridSize.
|
|
3
|
+
* Grid starts at ~60 cells and grows by ~15 per threshold, so run(t) = 63 + t*15.
|
|
4
|
+
* Cumulative: sum of 2*(63 + t*15) for t=0..n-1 = n*(111 + 15n).
|
|
5
|
+
* Gives: 126, 282, 468, 684, 930, ...
|
|
6
|
+
*/
|
|
74
7
|
export function getThresholdQither(n) {
|
|
75
|
-
|
|
76
|
-
return n * (n + 1) * 5;
|
|
8
|
+
return n * (111 + 15 * n);
|
|
77
9
|
}
|
|
78
10
|
/** Enemy difficulty multiplier from total qither used to activate all absorbers.
|
|
79
11
|
* +2% HP and power per 1 qither.
|
package/dist/technique.d.ts
CHANGED
|
@@ -71,12 +71,14 @@ interface BaseTechniqueEffect {
|
|
|
71
71
|
isAdditionalTooltip?: boolean;
|
|
72
72
|
cantUpgrade?: boolean;
|
|
73
73
|
}
|
|
74
|
+
export type EffectTargeting = 'self' | 'party' | 'all';
|
|
74
75
|
interface BuffSelfTechniqueEffect extends BaseTechniqueEffect {
|
|
75
76
|
kind: 'buffSelf';
|
|
76
77
|
buff: Buff;
|
|
77
78
|
amount: Scaling;
|
|
78
79
|
hits?: Scaling;
|
|
79
80
|
hideBuff?: boolean;
|
|
81
|
+
targeting?: EffectTargeting;
|
|
80
82
|
}
|
|
81
83
|
interface ConsumeSelfTechniqueEffect extends BaseTechniqueEffect {
|
|
82
84
|
kind: 'consumeSelf';
|
|
@@ -125,11 +127,13 @@ interface BarrierTechniqueEffect extends BaseTechniqueEffect {
|
|
|
125
127
|
kind: 'barrier';
|
|
126
128
|
amount: Scaling;
|
|
127
129
|
hits?: Scaling;
|
|
130
|
+
targeting?: EffectTargeting;
|
|
128
131
|
}
|
|
129
132
|
interface HealTechniqueEffect extends BaseTechniqueEffect {
|
|
130
133
|
kind: 'heal';
|
|
131
134
|
amount: Scaling;
|
|
132
135
|
hits?: Scaling;
|
|
136
|
+
targeting?: EffectTargeting;
|
|
133
137
|
}
|
|
134
138
|
interface CleanseToxicityTechniqueEffect extends BaseTechniqueEffect {
|
|
135
139
|
kind: 'cleanseToxicity';
|
|
@@ -154,6 +158,7 @@ interface RepairTechniqueEffect extends BaseTechniqueEffect {
|
|
|
154
158
|
group: string;
|
|
155
159
|
/** Which matching buff(s) to repair: 'all', 'lowestHealth', or 'highestHealth' */
|
|
156
160
|
rule: RepairRule;
|
|
161
|
+
targeting?: EffectTargeting;
|
|
157
162
|
}
|
|
158
163
|
export interface PermanentStatChangeTechniqueEffect extends BaseTechniqueEffect {
|
|
159
164
|
kind: 'permanentStatChange';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type Tests - These files ensure type compatibility across the codebase
|
|
3
|
+
* If any of these fail to compile, it means we have a type mismatch
|
|
4
|
+
*/
|
|
5
|
+
import type { RootState as StoreRootState } from '../store';
|
|
6
|
+
import type { RootState as TypesRootState } from './reduxState';
|
|
7
|
+
type TestExactMatch = StoreRootState extends TypesRootState ? TypesRootState extends StoreRootState ? true : false : false;
|
|
8
|
+
export type RootStateIsConsistent = TestExactMatch;
|
|
9
|
+
export {};
|