afnm-types 0.6.58 → 0.6.59

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.
@@ -1,2 +1,2 @@
1
- export type GameScreen = 'location' | 'recipe' | 'mission' | 'craftingHall' | 'manual' | 'cultivation' | 'map' | 'healer' | 'market' | 'favour' | 'herbField' | 'mine' | 'recipeLibrary' | 'requestBoard' | 'compendium' | 'library' | 'altar' | 'research' | 'reforge' | 'expedition' | 'pillarGrid' | 'fallenStar' | 'trainingGround' | 'tenThousandFlames' | 'lifeScreen' | 'soulShardDelve' | 'enchantmentShop' | 'challengeBoard';
1
+ export type GameScreen = 'location' | 'recipe' | 'mission' | 'craftingHall' | 'manual' | 'cultivation' | 'map' | 'healer' | 'market' | 'favour' | 'herbField' | 'mine' | 'recipeLibrary' | 'requestBoard' | 'compendium' | 'library' | 'altar' | 'research' | 'reforge' | 'expedition' | 'pillarGrid' | 'fallenStar' | 'trainingGround' | 'tenThousandFlames' | 'lifeScreen' | 'soulShardDelve' | 'enchantmentShop' | 'challengeBoard' | 'formationPuzzle' | 'rumours';
2
2
  export type ScreenType = GameScreen | 'newgame' | 'combat' | 'crafting' | 'dualCultivation' | 'event' | 'auction' | 'mysticalRegion' | 'tournament' | 'house' | 'guild' | 'stoneCutting' | 'fallenStar' | 'soulShardDelve' | 'expedition';
package/dist/audio.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export declare const ambienceNames: readonly ["HerbField", "Market", "Forge", "SectHall", "Wind", "Crypt", "Forest", "Rain", "BoilingLake", "BoilingLakeSurface", "Field", "Space", "CultivationChamber"];
2
2
  export type AmbienceName = (typeof ambienceNames)[number];
3
- export declare const musicNames: readonly ["Ascending", "Crafting", "Crypt", "Forest", "Herb", "Lake", "Liangtiao", "Mine", "Observatory", "Reststop", "Sect", "Settingoff", "Shenhenda", "Tomb", "Valley", "Combat", "Xidian", "North", "Wastes", "DualCultivation", "ProgenitorRuGong", "ProgenitorDongTiang", "ProgenitorYingMeihua", "ProgenitorDiaoGian", "ProgenitorTeJinshu", "ProgenitorLuBuLin"];
3
+ export declare const musicNames: readonly ["Ascending", "Crafting", "Crypt", "Forest", "Herb", "Lake", "Liangtiao", "Mine", "Observatory", "Reststop", "Sect", "Settingoff", "Shenhenda", "Tomb", "Valley", "Combat", "Xidian", "North", "Wastes", "DualCultivation", "ProgenitorRuGong", "ProgenitorDongTiang", "ProgenitorYingMeihua", "ProgenitorDiaoGian", "ProgenitorTeJinshu", "ProgenitorLuBuLin", "YuMaiMine", "JadeForest", "Expedition", "SoulShard"];
4
4
  export type MusicName = (typeof musicNames)[number];
5
5
  export declare const soundEffectNames: readonly ["Hit", "Buff", "Debuff", "Heal", "Death", "Fuse", "Refine", "Stabilize", "Equip", "Drink", "Learn", "Crystal", "Eat", "BuySell", "Select", "Confirm", "Hover", "Cancel", "OpenEquip", "PlayerDialogOpen", "CrystalCrack", "CrystalCrackOpen", "CrystalCrackBg", "ChangeTab", "Failed", "MasteryUnveil", "BuildingEnter", "Talk", "Healer", "DialogClose", "BuildingLeave", "SelectItem", "RemoveItem", "BottleneckBreakthrough", "BreakthroughLightning", "Pool", "CraftingBuff", "CraftFail", "CraftSuccess", "PerfectCraft", "TakeItem", "Text", "Speech", "Destiny", "Money", "Favour", "Reputation", "Qi", "Location", "BreakthroughItemSet", "BreakthroughItemRemove", "Footstep", "Fly", "SpawnImpact", "SelectStance", "Plant", "Unplant", "Collect", "Dig", "GrowlBark", "GrowlChirp", "GrowlDeep", "GrowlDrone", "GrowlFrog", "GrowlGrind", "GrowlHigh", "GrowlLoud", "GrowlMinotaur", "GrowlRumble", "GrowlScreech", "GrowlSlow", "GrowlSnuffle", "Satisfaction", "CloudAttack", "BlossomAttack", "FistAttack", "WeaponAttack", "BloodAttack", "CelestialAttack"];
6
6
  export type SoundEffectName = (typeof soundEffectNames)[number];
package/dist/audio.js CHANGED
@@ -40,6 +40,10 @@ export const musicNames = [
40
40
  'ProgenitorDiaoGian',
41
41
  'ProgenitorTeJinshu',
42
42
  'ProgenitorLuBuLin',
43
+ 'YuMaiMine',
44
+ 'JadeForest',
45
+ 'Expedition',
46
+ 'SoulShard',
43
47
  ];
44
48
  export const soundEffectNames = [
45
49
  'Hit',
@@ -3,9 +3,17 @@ import type { ReactNode } from 'react';
3
3
  import type { CombatEntity, CraftingEntity, PlayerEntity, StoredStance, StoredStyle } from './entity';
4
4
  import type { ItemDesc } from './item';
5
5
  import type { Realm, RealmProgress } from './realm';
6
- import type { PhysicalStatistic, SocialStatistic, Scaling } from './stat';
6
+ import type { CombatStatistic, CraftingStatistic, PhysicalStatistic, Scaling, SocialStatistic } from './stat';
7
7
  import type { Buff } from './buff';
8
8
  import type { CraftingBuff } from './craftingBuff';
9
+ /**
10
+ * Discriminator for manifestation events queued and replayed during the
11
+ * lifeFlourishing breakthrough. Runtime constants matching each member live
12
+ * in src/store/slices/manifestation/manifestationFlags.ts and are typed
13
+ * against this union, so adding a new event there only compiles after the
14
+ * member is added here.
15
+ */
16
+ export type ManifestationEventId = 'manifestation_birth' | 'manifestation_aspect_filled_creativity' | 'manifestation_aspect_filled_empathy' | 'manifestation_aspect_filled_agency' | 'manifestation_focus_selector';
9
17
  export interface Position {
10
18
  x: number;
11
19
  y: number;
@@ -25,11 +33,26 @@ export interface StoredFigment {
25
33
  storedStyles?: StoredStyle[];
26
34
  assignedTiers?: Record<string, number>;
27
35
  }
36
+ /** Variant of an incursion fight. Drives the enemy roster and the reward
37
+ * amount the player gets when they win. */
38
+ export type IncursionKind = 'normal' | 'horde' | 'boss';
28
39
  /** An active firmament incursion targeting the player's inner world */
29
40
  export interface ActiveIncursion {
30
41
  id: string;
31
42
  spawnedYear: number;
32
43
  spawnedMonth: number;
44
+ /** Variant of fight when the player engages this incursion. Defaults to
45
+ * 'normal' for backward compatibility with saves spawned before kinds
46
+ * existed. */
47
+ kind?: IncursionKind;
48
+ /** Frozen difficulty proxy used to scale the enemies on engagement.
49
+ * Sampled at spawn time from the live firmament progress plus a small
50
+ * random jitter and a -5% offset per active incursion. Falling back to
51
+ * the live progress is fine for legacy incursions. */
52
+ effectiveProgress?: number;
53
+ /** Amount of +N applied per chosen figment-upgrade pick after a win.
54
+ * 1 for normal, larger for harder variants. */
55
+ upgradeAmount?: number;
33
56
  }
34
57
  /** Generated but uncollected essence accumulating in the inner world */
35
58
  export interface PendingEssence {
@@ -37,6 +60,70 @@ export interface PendingEssence {
37
60
  count: number;
38
61
  progress: number;
39
62
  }
63
+ /** Three life aspects that grow as the manifestation lives in the world. */
64
+ export type ManifestationAspect = 'creativity' | 'empathy' | 'agency';
65
+ /** A single entry in the manifestation's activity diary. */
66
+ export interface ManifestationLogEntry {
67
+ year: number;
68
+ month: number;
69
+ day: number;
70
+ /** Localisable log line built from real game-data nouns. */
71
+ text: Translatable;
72
+ /** Which aspect this entry advanced, if any. */
73
+ aspect?: ManifestationAspect;
74
+ /** Aspect delta from this entry (typically 0 on failure, +1 on success). */
75
+ delta?: number;
76
+ }
77
+ /** Tracks one recently-consumed item for the creativity gift bonus. */
78
+ export interface ManifestationConsumptionEntry {
79
+ item: string;
80
+ count: number;
81
+ day: number;
82
+ }
83
+ /** Capstone of the Life Flourishing realm: an autonomous lifeform walking the world. */
84
+ export interface ManifestationState {
85
+ /** Player-visible name. Generated at first manifest, editable. */
86
+ name: string;
87
+ /** Linked StoredFigment id that provides visuals, species, and stances. */
88
+ figmentId: string;
89
+ /** Calendar year of first manifestation. */
90
+ birthYear: number;
91
+ /** Calendar month of first manifestation. */
92
+ birthMonth: number;
93
+ /** Aspect values, 0..100 integer. */
94
+ creativity: number;
95
+ empathy: number;
96
+ agency: number;
97
+ /** Biases the monthly move decision and which aspect category actions prefer. */
98
+ focus: ManifestationAspect;
99
+ /** Current location (GameLocation.name) the manifestation is wandering at. */
100
+ locationName: string;
101
+ /** Months spent at the current location; forces a move if it stagnates. */
102
+ monthsAtLocation: number;
103
+ /**
104
+ * Destination the manifestation is travelling toward. Movement is
105
+ * resolved one node per month along the shortest path; once
106
+ * `locationName === destinationLocationName` the field is cleared.
107
+ * Undefined means "no current journey, idle at locationName."
108
+ */
109
+ destinationLocationName?: string;
110
+ /** Ring buffer of diary entries, newest last. Capped at MANIFESTATION_LOG_CAP. */
111
+ log: ManifestationLogEntry[];
112
+ /** Accumulator so day-batches still emit one entry per 3 days. */
113
+ daysSinceLastLog: number;
114
+ /** Manifestation events that have already played. */
115
+ seenEvents: ManifestationEventId[];
116
+ /** Manifestation event queued to play on next player co-located interaction. */
117
+ pendingEvent?: ManifestationEventId;
118
+ /** Months until a random-encounter milestone is eligible again. */
119
+ randomEventCooldown: number;
120
+ /** Creativity gift cooldown in months. <=0 means eligible to gift. */
121
+ creativityGiftCooldown: number;
122
+ /** Empathy companion cooldown in months. <=0 means eligible to join. */
123
+ empathyCompanionCooldown: number;
124
+ /** Items the player has recently consumed; bounded ring of size MANIFESTATION_CONSUMPTION_CAP. */
125
+ recentConsumption: ManifestationConsumptionEntry[];
126
+ }
40
127
  /** State for the Life Flourishing breakthrough system */
41
128
  export interface LifeFlourishingState {
42
129
  unlockedSpecies: string[];
@@ -47,6 +134,74 @@ export interface LifeFlourishingState {
47
134
  activeIncursions?: ActiveIncursion[];
48
135
  pendingEssences?: PendingEssence[];
49
136
  essenceTiersUnlocked?: Record<string, number>;
137
+ /** The single manifested lifeform, if any. */
138
+ manifestation?: ManifestationState;
139
+ /**
140
+ * Lifeform realm used for inner-world combat (incursions + expand) ONLY.
141
+ * Independent of the player's realm. Defaults to bodyForging / Early when
142
+ * unset. When the lifeform is manifested in the outer world, the
143
+ * manifestation companion stats still read player realm.
144
+ */
145
+ lifeformRealm?: Realm;
146
+ /** Step within the lifeform realm: 0 = Early, 1 = Middle, 2 = Late. */
147
+ lifeformRealmStep?: LifeformRealmStep;
148
+ /**
149
+ * Calendar stamp of when the player first created any figment. Drives the
150
+ * time component of the incursion spawn rate. Undefined => no incursions
151
+ * can spawn yet.
152
+ */
153
+ firstFigmentYear?: number;
154
+ firstFigmentMonth?: number;
155
+ /** Global figment upgrade pool earned from incursion victories. */
156
+ figmentUpgrades?: FigmentUpgrades;
157
+ /**
158
+ * Present while an unspent incursion-victory upgrade is owed to the player.
159
+ * The Life screen renders a blocking picker until the player picks one.
160
+ * `upgradeAmount` is the +N applied when an upgrade is chosen; carried over
161
+ * from the resolved incursion so harder variants pay out more.
162
+ * `baseExpansion` is the firmament-expansion gain already auto-applied on
163
+ * victory (1-3%), surfaced in the picker so the player can see it.
164
+ */
165
+ pendingFigmentUpgrade?: {
166
+ upgradeAmount: number;
167
+ baseExpansion: number;
168
+ };
169
+ /** Ids of minor world aspects the player has unlocked. */
170
+ unlockedMinorAspectIds?: string[];
171
+ /**
172
+ * Three slots arranged in an arc below the main aspect; each entry is the
173
+ * world aspect id placed in that slot (major or minor) or undefined.
174
+ * Length grows as the player unlocks slots (cap 3).
175
+ */
176
+ slottedAspectIds?: (string | undefined)[];
177
+ /**
178
+ * Months remaining of firmament-suppression from a consumed Calming item.
179
+ * While positive, the monthly incursion-chance accumulation is halved.
180
+ * Refreshing the buff (consuming another item) re-sets it to the full
181
+ * duration rather than stacking.
182
+ */
183
+ firmamentReductionMonths?: number;
184
+ /**
185
+ * Accumulated incursion-spawn chance, 0..100. Each month the spawn formula
186
+ * adds its computed value to this counter (instead of rolling against it).
187
+ * When it crosses 100 an incursion spawns and the counter resets to 0.
188
+ */
189
+ incursionChance?: number;
190
+ }
191
+ /** Step within a lifeform realm: 0 = Early, 1 = Middle, 2 = Late. */
192
+ export type LifeformRealmStep = 0 | 1 | 2;
193
+ /**
194
+ * Global figment combat upgrades. The five percent fields are capped at 30
195
+ * (each point = +1%). Enhancement is uncapped and applies as a flat +N
196
+ * enhancement to every figment technique used in inner-world combat.
197
+ */
198
+ export interface FigmentUpgrades {
199
+ hp: number;
200
+ power: number;
201
+ protection: number;
202
+ barrierMit: number;
203
+ critMult: number;
204
+ enhancement: number;
50
205
  }
51
206
  export interface BreakthroughState {
52
207
  mundane?: {
@@ -66,6 +221,10 @@ export interface BreakthroughState {
66
221
  };
67
222
  coreFormation?: {
68
223
  compressions?: number;
224
+ /** Location names of altars the player has successfully compressed at
225
+ * at least once. Used to gate past-9 over-compressions to new altars,
226
+ * and to grant per-altar stat bonuses on breakthrough. */
227
+ beatenAltars?: string[];
69
228
  };
70
229
  pillarCreation?: {
71
230
  grid: string[];
@@ -100,6 +259,16 @@ export interface BreakthroughBase {
100
259
  socialStats: Partial<{
101
260
  [key in SocialStatistic]: number;
102
261
  }>;
262
+ /** Permanent additive combat-stat bonuses granted by the breakthrough.
263
+ * Values are `Scaling` so the dialog can render multipliers as "+10%
264
+ * Power" instead of the evaluated flat amount. */
265
+ combatStats?: Partial<{
266
+ [key in CombatStatistic]: Scaling;
267
+ }>;
268
+ /** Permanent additive crafting-stat bonuses granted by the breakthrough. */
269
+ craftingStats?: Partial<{
270
+ [key in CraftingStatistic]: Scaling;
271
+ }>;
103
272
  combatBuffs?: {
104
273
  buff: Buff;
105
274
  buffStacks: Scaling;
@@ -159,6 +328,12 @@ export interface Breakthrough extends BreakthroughBase {
159
328
  socialStats: Partial<{
160
329
  [key in SocialStatistic]: number;
161
330
  }>;
331
+ combatStats?: Partial<{
332
+ [key in CombatStatistic]: Scaling;
333
+ }>;
334
+ craftingStats?: Partial<{
335
+ [key in CraftingStatistic]: Scaling;
336
+ }>;
162
337
  combatBuffs?: {
163
338
  buff: Buff;
164
339
  buffStacks: Scaling;
package/dist/buff.d.ts CHANGED
@@ -101,6 +101,14 @@ export interface Buff {
101
101
  type?: TechniqueElement;
102
102
  noneType?: string;
103
103
  secondaryType?: TechniqueElement | 'origin';
104
+ /** Marks this buff as droplet-flavoured. Its outgoing damage / heal / barrier
105
+ * / temp HP effects gain the entity's <n>dropletBoost</n>, exactly as if the
106
+ * effects came from a technique with a droplet cost. */
107
+ isDroplet?: boolean;
108
+ /** Marks this buff as an opener for boost purposes. Effects gain <n>openerBoost</n>. */
109
+ isOpener?: boolean;
110
+ /** Marks this buff as a finisher for boost purposes. Effects gain <n>finisherBoost</n>. */
111
+ isFinisher?: boolean;
104
112
  enhancement?: number;
105
113
  beforeTechniqueEffects?: BuffEffect[];
106
114
  afterTechniqueEffects?: BuffEffect[];
@@ -188,6 +196,11 @@ export interface Buff {
188
196
  guardianHp?: number;
189
197
  /** Runtime guardian max HP — set automatically from guardianIntercept.maxHp at buff creation */
190
198
  guardianMaxHp?: number;
199
+ /** Cached hash of the static portion of this buff (name, flag, buffType,
200
+ * stats keys and values, condition). Populated lazily by cloneCombatEntity
201
+ * and createBuff so subsequent clones carry it forward via object spread,
202
+ * saving the per-call string-hashing work in calculateCombatEntityHash. */
203
+ _staticHash?: number;
191
204
  }
192
205
  type BuffCombatImage = ScatterCombatImage | ArcCombatImage | FloatingCombatImage | OverlayCombatImage | CompanionCombatImage | GuardianCombatImage | GroundCombatImage | FormationCombatImage | ForegroundCombatImage | BackgroundCombatImage | TransformationCombatImage | AvatarEffectCombatImage;
193
206
  interface BaseCombatImage {
@@ -0,0 +1,21 @@
1
+ import type { Buff } from './buff';
2
+ /**
3
+ * Per-entity indexes built by walking entity.buffs once, then read repeatedly
4
+ * during combat to avoid filtering the full buff list per category lookup.
5
+ *
6
+ * Invariant: indexes hold direct references into entity.buffs. They are valid
7
+ * only while that array's contents are unchanged. cloneCombatEntity does not
8
+ * carry the cache forward; invalidateEntityHash clears it after any mutation
9
+ * routed through mutateCombatEntity.
10
+ */
11
+ export interface BuffIndexes {
12
+ beforeTechnique: Buff[];
13
+ afterTechnique: Buff[];
14
+ onRound: Buff[];
15
+ onRoundStart: Buff[];
16
+ damageIntercept: Buff[];
17
+ guardian: Buff[];
18
+ buffAmplifier: Buff[];
19
+ techniqueAmplifier: Buff[];
20
+ byStat: Record<string, Buff[]>;
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -109,5 +109,7 @@ export interface NormalizedBuildSubmission {
109
109
  name?: string;
110
110
  stances: StoredStance[];
111
111
  currentStyle?: StoredStyle;
112
+ /** Per-essence tier assignments for this figment. */
113
+ assignedTiers?: Record<string, number>;
112
114
  };
113
115
  }
@@ -111,7 +111,7 @@ export interface Character {
111
111
  condition: string;
112
112
  }[];
113
113
  }
114
- export declare const characterRelationships: readonly ["Hostile", "Neutral", "Friendly", "Intimate"];
114
+ export declare const characterRelationships: readonly ["Hostile", "Neutral", "Friendly", "Intimate", "Special"];
115
115
  export type CharacterRelationship = (typeof characterRelationships)[number];
116
116
  export declare const relationshipTextColour: Record<CharacterRelationship, string>;
117
117
  export declare const relationshipBorderColour: Record<CharacterRelationship, string>;
@@ -196,6 +196,7 @@ export interface ShopCharacterInteraction extends BaseCharacterInteraction {
196
196
  tokenStock: Array<{
197
197
  item: Item;
198
198
  tokenCost: number;
199
+ buyAsStack?: boolean;
199
200
  }>;
200
201
  };
201
202
  }
package/dist/character.js CHANGED
@@ -1,19 +1,28 @@
1
- export const characterRelationships = ['Hostile', 'Neutral', 'Friendly', 'Intimate'];
1
+ export const characterRelationships = [
2
+ 'Hostile',
3
+ 'Neutral',
4
+ 'Friendly',
5
+ 'Intimate',
6
+ 'Special',
7
+ ];
2
8
  export const relationshipTextColour = {
3
9
  Hostile: 'red',
4
10
  Neutral: 'white',
5
11
  Friendly: 'palegreen',
6
12
  Intimate: 'pink',
13
+ Special: 'gold',
7
14
  };
8
15
  export const relationshipBorderColour = {
9
16
  Hostile: '#780000',
10
17
  Neutral: 'rgb(75,75,75)',
11
18
  Friendly: '#19521e',
12
19
  Intimate: '#9e007c',
20
+ Special: '#7a5900',
13
21
  };
14
22
  export const relationshipTooltip = {
15
23
  Hostile: 'You can attack a hostile character, or threaten them to steal their possessions.',
16
24
  Neutral: 'Willing to talk and maybe trade with you, but little else.',
17
25
  Friendly: '',
18
26
  Intimate: '',
27
+ Special: '',
19
28
  };
@@ -46,6 +46,7 @@ exports.NON_TRANSLATABLE_PROPERTIES = [
46
46
  'shard',
47
47
  'ability',
48
48
  'guild',
49
+ 'relationship',
49
50
  'eventName',
50
51
  'achievementID',
51
52
  'apiSecret',
@@ -327,6 +328,10 @@ exports.EXCLUDE_PATTERNS = [
327
328
  /^[a-z]+-[a-z]+,\s*[a-z]+-[a-z]+$/,
328
329
  // shaders
329
330
  /.*vec2.*/,
331
+ // CSS anywhere in the string
332
+ /@keyframes/,
333
+ /box-shadow/,
334
+ /rgb/i,
330
335
  ];
331
336
  /** Patterns to exclude from code-changes report */
332
337
  exports.CODE_CHANGE_EXCLUDE_PATTERNS = [
package/dist/combat.d.ts CHANGED
@@ -97,7 +97,7 @@ export interface CurrentCombatState {
97
97
  enemyEffectTracking: Record<string, CombatEffectTracking>;
98
98
  stanceTracking: Record<string, StanceTracking>;
99
99
  partyStanceTracking: Record<number, Record<string, StanceTracking>>;
100
- pillTracking: Record<string, number>;
100
+ pillTracking: Record<string, InventoryItemState>;
101
101
  autoUseRowTracking: Record<number, number>;
102
102
  inventoryItems?: InventoryItemState[];
103
103
  loot: Item[];
package/dist/entity.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Translatable } from './translatable';
2
2
  import type { SoundEffectName } from './audio';
3
3
  import type { Buff } from './buff';
4
+ import type { BuffIndexes } from './buffIndexes';
4
5
  import type { CraftingBuff } from './craftingBuff';
5
6
  import type { CraftingTechnique, KnownCraftingTechnique } from './craftingTechnique';
6
7
  import type { TechniqueElement } from './element';
@@ -337,6 +338,12 @@ export interface CombatEntity {
337
338
  maxqiDroplets?: number;
338
339
  /** Cached hash for getVariablesFromEntity. Cleared whenever stats or buffs mutate. */
339
340
  _cachedHash?: string;
341
+ /** Cached buff indexes (by effect category and by stat). Cleared whenever buffs mutate.
342
+ * See src/util/buffIndexes.ts. */
343
+ _buffIndexes?: BuffIndexes;
344
+ /** Cached pre-trigger buff-count snapshot. Cleared whenever buffs mutate.
345
+ * See src/combat/effects/preTriggerSnapshot.ts. */
346
+ _preTriggerSnapshot?: Record<string, number>;
340
347
  }
341
348
  export type StanceRule = SingleStance | RandomStance;
342
349
  export interface SingleStance {
package/dist/event.d.ts CHANGED
@@ -7,6 +7,7 @@ import type { CraftingBuff } from './craftingBuff';
7
7
  import type { IntimateTrait } from './dualCultivation';
8
8
  import type { TechniqueElement } from './element';
9
9
  import type { EnemyEntity } from './entity';
10
+ import type { FormationGlyphId, FormationNodeId, FormationWinCondition } from './formation';
10
11
  import type { ItemDesc } from './item';
11
12
  import type { Realm, RealmProgress } from './realm';
12
13
  import type { PhysicalStatistic, ReputationTier, SocialStatistic } from './stat';
@@ -33,13 +34,13 @@ export interface GameEvent {
33
34
  value: number;
34
35
  }[];
35
36
  }
36
- export declare const EVENT_STEP_KINDS: readonly ["text", "speech", "combat", "crafting", "choice", "conditional", "flag", "exit", "createBuff", "consumeBuff", "addItem", "addMultipleItem", "dropItem", "removeItem", "replaceItem", "location", "quest", "money", "favour", "destiny", "reputation", "qi", "unlockLocation", "unlockAltar", "setCharacter", "clearCharacter", "label", "gotoLabel", "unlockCraftingTechnique", "unlockTechnique", "unlockAuctionTechnique", "addRecipe", "addManual", "learnNpcStances", "talkToCharacter", "tradeWithCharacter", "craftWithCharacter", "fightCharacter", "markBeatCharacter", "markDidEncounter", "changeHp", "passTime", "analytics", "approval", "progressRelationship", "selectRelationshipPath", "updateCharacterDefinition", "markGifted", "auction", "markCalendarEventComplete", "advanceMysticalRegion", "craftSkill", "tournament", "dualCultivation", "teamUp", "craftingTeamUp", "addFollower", "breakParty", "clearTeamUp", "setAltarCooldown", "setTradeCooldown", "compressCore", "changeScreen", "changeBGM", "clearChangeBGM", "addGuildApproval", "advanceGuildRank", "overridePlayerRealm", "setContentRealmOverride", "setAidBreakthroughCooldown", "stoneCutting", "giveItem", "changePhysicalStat", "changeSocialStat", "status", "clearStatus"];
37
+ export declare const EVENT_STEP_KINDS: readonly ["text", "speech", "combat", "crafting", "choice", "conditional", "flag", "exit", "createBuff", "consumeBuff", "addItem", "addMultipleItem", "dropItem", "removeItem", "replaceItem", "location", "quest", "money", "favour", "destiny", "reputation", "qi", "unlockLocation", "unlockAltar", "setCharacter", "clearCharacter", "label", "gotoLabel", "unlockCraftingTechnique", "unlockTechnique", "unlockAuctionTechnique", "addRecipe", "addManual", "learnNpcStances", "talkToCharacter", "tradeWithCharacter", "craftWithCharacter", "fightCharacter", "markBeatCharacter", "markDidEncounter", "changeHp", "passTime", "analytics", "approval", "progressRelationship", "selectRelationshipPath", "updateCharacterDefinition", "markGifted", "auction", "markCalendarEventComplete", "advanceMysticalRegion", "craftSkill", "tournament", "dualCultivation", "teamUp", "craftingTeamUp", "addFollower", "breakParty", "clearTeamUp", "setAltarCooldown", "setTradeCooldown", "compressCore", "changeScreen", "changeBGM", "clearChangeBGM", "addGuildApproval", "advanceGuildRank", "overridePlayerRealm", "setContentRealmOverride", "setAidBreakthroughCooldown", "stoneCutting", "formationPuzzle", "giveItem", "changePhysicalStat", "changeSocialStat", "status", "clearStatus"];
37
38
  export type EventStepKind = (typeof EVENT_STEP_KINDS)[number];
38
39
  export interface BaseEventStep {
39
40
  kind: EventStepKind;
40
41
  condition?: string;
41
42
  }
42
- export type EventStep = TextStep | SpeechStep | CombatStep | CraftingStep | ChoiceStep | ConditionalStep | SetFlagStep | ExitStep | CreateBuffStep | ConsumeBuffStep | ChangeLocationStep | AddItemStep | RemoveItemStep | AddQuestStep | ChangeMoneyStep | ChangeFavourStep | AddDestinyStep | ChangeReputationStep | QiStep | UnlockLocationStep | ClearCharacterStep | SetCharacterStep | LabelStep | GotoLabelStep | UnlockCraftingTechniqueStep | TalkToCharacterStep | TradeWithCharacterStep | CraftWithCharacterStep | FightCharacterStep | MarkBeatCharacterStep | MarkDidEncounterStep | ChangeHpStep | PassTimeStep | ReportAnalyticsStep | ApprovalStep | ProgressRelationshipStep | MarkGiftedStep | UpdateCharacterDefinitionStep | AuctionStep | MarkCalendarEventCompleteStep | AddMultipleItemStep | AdvanceMysticalRegionStep | CraftSkillStep | TournamentStep | TeamUpStep | CraftingTeamUpStep | AddFollowerStep | BreakPartyStep | ClearTeamUpStep | UnlockAltarStep | DropItemStep | SetAltarCooldownStep | CompressCoreStep | ChangeScreenStep | UnlockTechniqueStep | UnlockAuctionTechniqueStep | AddRecipeStep | AddManualStep | LearnNpcStancesStep | ReplaceItemStep | DualCultivationStep | ChangeBGMStep | ClearChangeBGMStep | AddGuildApprovalStep | AdvanceGuildRankStep | OverridePlayerRealmStep | SetContentRealmOverrideStep | SetAidBreakthroughCooldownStep | StoneCuttingStep | GiveItemStep | ChangePhysicalStatStep | ChangeSocialStatStep | SelectRelationshipPathStep | SetTradeCooldownStep | StatusStep | ClearStatusStep | IncreaseInstabilityStep | EndExpeditionStep | AddItemsToFoundStep | AddItemsToExtractedStep;
43
+ export type EventStep = TextStep | SpeechStep | CombatStep | CraftingStep | ChoiceStep | ConditionalStep | SetFlagStep | ExitStep | CreateBuffStep | ConsumeBuffStep | ChangeLocationStep | AddItemStep | RemoveItemStep | AddQuestStep | ChangeMoneyStep | ChangeFavourStep | AddDestinyStep | ChangeReputationStep | QiStep | UnlockLocationStep | ClearCharacterStep | SetCharacterStep | LabelStep | GotoLabelStep | UnlockCraftingTechniqueStep | TalkToCharacterStep | TradeWithCharacterStep | CraftWithCharacterStep | FightCharacterStep | MarkBeatCharacterStep | MarkDidEncounterStep | ChangeHpStep | PassTimeStep | ReportAnalyticsStep | ApprovalStep | ProgressRelationshipStep | MarkGiftedStep | UpdateCharacterDefinitionStep | AuctionStep | MarkCalendarEventCompleteStep | AddMultipleItemStep | AdvanceMysticalRegionStep | CraftSkillStep | TournamentStep | TeamUpStep | CraftingTeamUpStep | AddFollowerStep | BreakPartyStep | ClearTeamUpStep | UnlockAltarStep | DropItemStep | SetAltarCooldownStep | CompressCoreStep | ChangeScreenStep | UnlockTechniqueStep | UnlockAuctionTechniqueStep | AddRecipeStep | AddManualStep | LearnNpcStancesStep | ReplaceItemStep | DualCultivationStep | ChangeBGMStep | ClearChangeBGMStep | AddGuildApprovalStep | AdvanceGuildRankStep | OverridePlayerRealmStep | SetContentRealmOverrideStep | SetAidBreakthroughCooldownStep | StoneCuttingStep | FormationPuzzleStep | GiveItemStep | ChangePhysicalStatStep | ChangeSocialStatStep | SelectRelationshipPathStep | SetTradeCooldownStep | StatusStep | ClearStatusStep | IncreaseInstabilityStep | EndExpeditionStep | AddItemsToFoundStep | AddItemsToExtractedStep;
43
44
  export interface TextStep extends BaseEventStep {
44
45
  kind: 'text';
45
46
  text: Translatable;
@@ -125,6 +126,7 @@ interface ItemCondition {
125
126
  item: ItemDesc;
126
127
  alternates?: ItemDesc[];
127
128
  amount: number;
129
+ displayName?: Translatable;
128
130
  }
129
131
  interface MoneyCondition {
130
132
  kind: 'money';
@@ -470,6 +472,39 @@ export interface StoneCuttingStep extends BaseEventStep {
470
472
  /** Number of stones the player can cut for free, evaluated as expression */
471
473
  freeCount?: string;
472
474
  }
475
+ export interface FormationPuzzleStep extends BaseEventStep {
476
+ kind: 'formationPuzzle';
477
+ isTutorial?: boolean;
478
+ /** Shown in the activity history when the puzzle starts. */
479
+ displayName?: string;
480
+ /** Prefix for the per-glyph completion flags. For each glyph type with at
481
+ * least one activated placement, two flags are written, keyed by the
482
+ * glyph's single-character `gridSymbol` (the same symbol used in `layout`).
483
+ * `{flagPrefix}_{gridSymbol}` holds the total power absorbed across
484
+ * activated placements of that glyph type, and
485
+ * `{flagPrefix}_{gridSymbol}_count` holds the number of activated (fully
486
+ * powered) placements of that glyph type. */
487
+ flagPrefix: string;
488
+ /** ASCII grid layout. Each non-empty line is one row (top = y=0).
489
+ * Tokens: `.` empty cell, `_` off-grid, node gridSymbol for pre-placed
490
+ * (locked) nodes, `SYM:>` for a rotated pre-placed node. */
491
+ layout: string;
492
+ pool: {
493
+ nodes: {
494
+ nodeId: FormationNodeId;
495
+ count: number;
496
+ }[];
497
+ glyphs: {
498
+ nodeId: FormationGlyphId;
499
+ count: number;
500
+ }[];
501
+ };
502
+ winCondition: FormationWinCondition[];
503
+ /** Followed when the player presses Seal with the win condition met. */
504
+ success: EventStep[];
505
+ /** Followed when the player presses Give Up. */
506
+ failure: EventStep[];
507
+ }
473
508
  export interface GiveItemStep extends BaseEventStep {
474
509
  kind: 'giveItem';
475
510
  description: Translatable;
package/dist/event.js CHANGED
@@ -70,6 +70,7 @@ export const EVENT_STEP_KINDS = [
70
70
  'setContentRealmOverride',
71
71
  'setAidBreakthroughCooldown',
72
72
  'stoneCutting',
73
+ 'formationPuzzle',
73
74
  'giveItem',
74
75
  'changePhysicalStat',
75
76
  'changeSocialStat',
@@ -17,7 +17,7 @@ export interface ExpeditionPoint {
17
17
  x: number;
18
18
  y: number;
19
19
  }
20
- export type RoleRarity = Exclude<Rarity, 'transcendent'>;
20
+ export type RoleRarity = Rarity;
21
21
  export declare const RoleRarityWeight: Record<RoleRarity, number>;
22
22
  export type Roles = 'fighter' | 'healer' | 'surveyor' | 'archaeologist' | 'stabilizer' | 'qiflow';
23
23
  export declare const rolesToName: Record<Roles, string>;
@@ -28,7 +28,8 @@ export type MemberRole = {
28
28
  rarity: RoleRarity;
29
29
  };
30
30
  export type TeamMember = {
31
- name: Translatable;
31
+ name: string;
32
+ displayName: Translatable;
32
33
  buff?: Buff;
33
34
  role: MemberRole;
34
35
  };
@@ -125,4 +126,12 @@ export interface ItemWCount {
125
126
  item: ItemDesc;
126
127
  count: number;
127
128
  }
129
+ export interface NpcPoints {
130
+ name: Translatable;
131
+ points: number;
132
+ }
133
+ export interface TeamPoints {
134
+ playerPoints: number;
135
+ npcPoints: NpcPoints[];
136
+ }
128
137
  export {};
@@ -29,7 +29,8 @@ export const RoleRarityWeight = {
29
29
  qitouched: 30,
30
30
  empowered: 25,
31
31
  resplendent: 20,
32
- incandescent: 5,
32
+ incandescent: 10,
33
+ transcendent: 3,
33
34
  };
34
35
  export const rolesToName = {
35
36
  fighter: 'Fighter',
@@ -0,0 +1,74 @@
1
+ import type { ReactNode } from 'react';
2
+ export type FormationSourcePower = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
3
+ export type FormationSourceId = `source${FormationSourcePower}`;
4
+ export type FormationChannelingNodeId = 'multiplyForward' | 'turnRight' | 'turnLeft' | 'splitTwo' | 'splitThree' | 'mergeTo5' | 'mergeOpposite' | 'mergeThree' | 'pivotHalve' | 'crossHalve' | 'crossEnhance' | 'flatSplit' | 'forkRight' | 'forkLeft' | 'enhanceRight' | 'enhanceLeft' | 'enhanceExcessRight' | 'enhanceExcessLeft' | 'flatForward' | 'flatForwardHigh' | 'mergeCornerRight' | 'mergeCornerLeft' | 'splitTwoFull' | 'forkAsymmetricRight' | 'forkAsymmetricLeft' | 'addOne' | 'addTwo';
5
+ export type FormationBlockerId = 'blocker';
6
+ export interface FormationNode {
7
+ id: FormationNodeId;
8
+ displayName: string;
9
+ description: string;
10
+ /** Single character used to reference this node in ASCII puzzle layouts
11
+ * (see formationPuzzleFromAscii). Must be unique across the registry.
12
+ * Reserved: '.' (empty cell) and ' ' (off-grid). */
13
+ gridSymbol: string;
14
+ /** SVG inner body, rendered by FormationSymbol on a 0..100 viewBox.
15
+ * Authored canonically (top of symbol = up). Grid rotation handles the
16
+ * physical orientation. */
17
+ renderer: ReactNode;
18
+ inputs?: {
19
+ left?: number;
20
+ right?: number;
21
+ bottom?: number;
22
+ top?: number;
23
+ };
24
+ anyInputSuffices?: boolean;
25
+ output?: {
26
+ mode: 'flat' | 'multiply' | 'add';
27
+ left: number;
28
+ top: number;
29
+ right: number;
30
+ bottom: number;
31
+ };
32
+ portal?: {
33
+ type: 'entrance' | 'exit';
34
+ };
35
+ complexity: 'basicRouting' | 'basicEnhance' | 'advancedRouting' | 'advancedEnhance' | 'overpowered' | 'special';
36
+ }
37
+ export interface FormationPuzzleCell {
38
+ id: string;
39
+ x: number;
40
+ y: number;
41
+ }
42
+ export interface FormationPuzzlePlacement {
43
+ nodeId: FormationNodeId;
44
+ x: number;
45
+ y: number;
46
+ rotation: number;
47
+ locked?: boolean;
48
+ }
49
+ export type FormationAbsorberMode = 'All' | 'One';
50
+ export type FormationAbsorberThreshold = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
51
+ export type FormationGlyphId = `absorber${FormationAbsorberMode}${FormationAbsorberThreshold}`;
52
+ type AbsorberModeSymbol = 'a' | 'o';
53
+ export type FormationGlyphSymbol = `${AbsorberModeSymbol}${FormationAbsorberThreshold}`;
54
+ export type FormationNodeId = FormationSourceId | FormationChannelingNodeId | FormationBlockerId | FormationGlyphId;
55
+ export interface FormationWinCondition {
56
+ glyph: FormationGlyphSymbol;
57
+ minActivated: number;
58
+ }
59
+ export interface FormationPuzzleDefinition {
60
+ cells: FormationPuzzleCell[];
61
+ prePlaced: FormationPuzzlePlacement[];
62
+ pool: {
63
+ nodes: {
64
+ nodeId: FormationNodeId;
65
+ count: number;
66
+ }[];
67
+ glyphs: {
68
+ nodeId: FormationGlyphId;
69
+ count: number;
70
+ }[];
71
+ };
72
+ winCondition: FormationWinCondition[];
73
+ }
74
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -15,4 +15,4 @@
15
15
  * };
16
16
  * }
17
17
  */
18
- export declare const GAME_VERSION = "0.6.58";
18
+ export declare const GAME_VERSION = "0.6.59";
@@ -15,4 +15,4 @@
15
15
  * };
16
16
  * }
17
17
  */
18
- export const GAME_VERSION = "0.6.58";
18
+ export const GAME_VERSION = "0.6.59";
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export * from './DamageType';
17
17
  export * from './destiny';
18
18
  export * from './dualCultivation';
19
19
  export * from './element';
20
+ export * from './expedition';
20
21
  export * from './entity';
21
22
  export * from './event';
22
23
  export * from './fallenStar';
@@ -50,3 +51,6 @@ export * from './tournament';
50
51
  export * from './trainingGround';
51
52
  export * from './translatable';
52
53
  export * from './tutorial';
54
+ export * from './expedition';
55
+ export * from './worldAspect';
56
+ export * from './avatarEffects';
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ export * from './DamageType';
17
17
  export * from './destiny';
18
18
  export * from './dualCultivation';
19
19
  export * from './element';
20
+ export * from './expedition';
20
21
  export * from './entity';
21
22
  export * from './event';
22
23
  export * from './fallenStar';
@@ -50,3 +51,6 @@ export * from './tournament';
50
51
  export * from './trainingGround';
51
52
  export * from './translatable';
52
53
  export * from './tutorial';
54
+ export * from './expedition';
55
+ export * from './worldAspect';
56
+ export * from './avatarEffects';
package/dist/item.d.ts CHANGED
@@ -241,6 +241,18 @@ export interface ConsumablePillItem extends BasePillItem {
241
241
  physicalStats: Partial<Record<PhysicalStatistic, number>>;
242
242
  socialStats: Partial<Record<SocialStatistic, number>>;
243
243
  rawStats?: Partial<Record<CombatStatistic | CraftingStatistic, Scaling>>;
244
+ /**
245
+ * Optional flag effect: when consumed, iterates the flag value by `amount`.
246
+ * The flag is used to track cumulative effects across multiple pill consumptions.
247
+ */
248
+ flagEffect?: {
249
+ /** Key to search in the flags Record */
250
+ flag: string;
251
+ /** Value to iterate the flag by (can be negative for subtraction) */
252
+ amount: number;
253
+ };
254
+ /** Optional tooltip to explain the pill's effects in the consumption dialogue */
255
+ tooltip?: string;
244
256
  toxicity?: undefined;
245
257
  }
246
258
  export interface CraftingEquipmentItem extends ItemBase {
@@ -413,10 +425,6 @@ export interface CondensationArtItem extends ItemBase {
413
425
  artName: string;
414
426
  patternBg: string;
415
427
  patternOpacity: number;
416
- condenseCost: number;
417
- hpCost?: number;
418
- moneyCost?: number;
419
- producedDroplets: number;
420
428
  maxDroplets: number;
421
429
  restoredDroplets?: number;
422
430
  statChange: Partial<Record<PhysicalStatistic, number>>;
@@ -435,7 +443,6 @@ export interface CondensationArtItem extends ItemBase {
435
443
  export interface CondensationArtEnchantment extends Enchantment {
436
444
  itemKind: 'condensation_art';
437
445
  condenseEfficiency?: number;
438
- producedDroplets?: number;
439
446
  maxDroplets?: number;
440
447
  charisma?: number;
441
448
  qiAbsorption?: number;
package/dist/life.d.ts CHANGED
@@ -14,6 +14,7 @@ export interface LifeForm {
14
14
  role: string;
15
15
  species: string;
16
16
  image: string;
17
+ portrait?: string;
17
18
  unlockCost: {
18
19
  essence: string;
19
20
  amount: number;
@@ -3,7 +3,7 @@ import type { ScreenEffectType } from './ScreenEffectType';
3
3
  import type { EventStep } from './event';
4
4
  import type { Rarity } from './rarity';
5
5
  import type { Realm, RealmProgress } from './realm';
6
- import type { ReputationTier } from './stat';
6
+ import type { CombatStatistic, CraftingStatistic, ReputationTier, Scaling } from './stat';
7
7
  import type { EnemyEntity } from './entity';
8
8
  import type { Item, ItemDesc } from './item';
9
9
  import type { House, HouseData } from './house';
@@ -146,9 +146,34 @@ export interface CraftingMission {
146
146
  condition: string;
147
147
  }
148
148
  export declare const exploresPerUnlock = 3;
149
- export type BuildingType = 'cultivation' | 'manual' | 'crafting' | 'mission' | 'craftingHall' | 'healer' | 'market' | 'favourExchange' | 'vault' | 'custom' | 'herbField' | 'mine' | 'recipe' | 'requestBoard' | 'compendium' | 'mysticalRegion' | 'expedition' | 'trainingGround' | 'library' | 'house' | 'altar' | 'research' | 'reforge' | 'guild' | 'tenThousandFlames' | 'soulShardDelve' | 'enchantmentShop' | 'challengeBoard' | 'jadeCutter' | 'modBuilding';
150
- export type LocationBuilding = CultivationBuilding | ManualBuilding | CraftingBuilding | MissionBuilding | CraftingHallBuilding | HealerBuilding | MarketBuilding | VaultBuilding | FavourExchangeBuilding | CustomBuilding | HerbFieldBuilding | MineBuilding | RecipeLibraryBuilding | RequestBoardBuilding | CompendiumBuilding | MysticalRegionBuilding | TrainingGroundBuilding | LibraryBuilding | HouseBuilding | CompressionAltarBuilding | ResearchBuilding | ReforgeBuilding | ExpeditionBuilding | GuildBuilding | TenThousandFlamesBuilding | SoulShardDelveBuilding | EnchantmentShopBuilding | ChallengeBoardBuilding | JadeCutterBuilding | ModBuilding;
151
- export type LocationBuildingState = MissionBuildingState | CraftingHallBuildingState | ShopBuildingState | RequestBoardBuildingState | HouseBuildingState | CompressionAltarBuildingState;
149
+ export type BuildingType = 'cultivation' | 'manual' | 'crafting' | 'mission' | 'craftingHall' | 'healer' | 'market' | 'favourExchange' | 'vault' | 'custom' | 'herbField' | 'mine' | 'recipe' | 'requestBoard' | 'compendium' | 'mysticalRegion' | 'expedition' | 'trainingGround' | 'library' | 'house' | 'altar' | 'research' | 'reforge' | 'guild' | 'tenThousandFlames' | 'soulShardDelve' | 'enchantmentShop' | 'challengeBoard' | 'jadeCutter' | 'rumours' | 'modBuilding';
150
+ export type LocationBuilding = CultivationBuilding | ManualBuilding | CraftingBuilding | MissionBuilding | CraftingHallBuilding | HealerBuilding | MarketBuilding | VaultBuilding | FavourExchangeBuilding | CustomBuilding | HerbFieldBuilding | MineBuilding | RecipeLibraryBuilding | RequestBoardBuilding | CompendiumBuilding | MysticalRegionBuilding | TrainingGroundBuilding | LibraryBuilding | HouseBuilding | CompressionAltarBuilding | ResearchBuilding | ReforgeBuilding | ExpeditionBuilding | GuildBuilding | TenThousandFlamesBuilding | SoulShardDelveBuilding | EnchantmentShopBuilding | ChallengeBoardBuilding | JadeCutterBuilding | RumoursBuilding | ModBuilding;
151
+ export type LocationBuildingState = MissionBuildingState | CraftingHallBuildingState | ShopBuildingState | RequestBoardBuildingState | HouseBuildingState | CompressionAltarBuildingState | RumoursBuildingState;
152
+ export interface Rumour {
153
+ /** Stable identifier used to track purchase state. */
154
+ key: string;
155
+ /** Revealed title once purchased. */
156
+ title: Translatable;
157
+ /** Cost in spirit stones to buy (before realm scaling). */
158
+ cost: number;
159
+ /** Realm used to scale the cost via getNumericReward. */
160
+ costRealm: Realm;
161
+ /** Revealed body text, the actual hint. */
162
+ hint: Translatable;
163
+ /** Optional gate so a rumour only appears once relevant. */
164
+ condition?: string;
165
+ }
166
+ export interface RumoursBuilding extends BuildingBase {
167
+ kind: 'rumours';
168
+ /** Display name for the trader screen header. */
169
+ traderName: Translatable;
170
+ rumours: Rumour[];
171
+ }
172
+ export interface RumoursBuildingState {
173
+ kind: 'rumours';
174
+ /** Keys of rumours the player has purchased. */
175
+ purchased: string[];
176
+ }
152
177
  interface BuildingBase {
153
178
  kind: BuildingType;
154
179
  condition?: string;
@@ -164,6 +189,21 @@ export interface CultivationBuilding extends BuildingBase {
164
189
  export interface CompressionAltarBuilding extends BuildingBase {
165
190
  kind: 'altar';
166
191
  buff: Buff;
192
+ /** Permanent combat / crafting stat increments granted on the Core
193
+ * Formation breakthrough once the player has compressed at this altar.
194
+ * Granted once regardless of how many times the altar is used. Values use
195
+ * the same `Scaling` shape as background `rawStats`, so altars can mix flat
196
+ * bonuses (`{ value: 5, stat: undefined, scaling: 'stacks' }`) with
197
+ * realm-scaling multipliers (`{ value: 0.1, stat: 'power', scaling: 'stacks' }`
198
+ * for +10% of current power). */
199
+ breakthroughReward?: {
200
+ combatStats?: Partial<{
201
+ [key in CombatStatistic]: Scaling;
202
+ }>;
203
+ craftingStats?: Partial<{
204
+ [key in CraftingStatistic]: Scaling;
205
+ }>;
206
+ };
167
207
  }
168
208
  export interface CompressionAltarBuildingState {
169
209
  kind: 'altar';
@@ -229,12 +269,19 @@ export interface MarketTokenStock {
229
269
  item: ShopItem;
230
270
  /** Number of currency-item tokens consumed per stack purchased. */
231
271
  tokenCost: number;
272
+ /**
273
+ * If set, the listing is sold as a bundle: the player must buy the entire stack
274
+ * for `tokenCost` (no partial purchases). Defaults to per-unit pricing when omitted.
275
+ */
276
+ buyAsStack?: boolean;
232
277
  }
233
278
  export interface MarketTokenCurrency {
234
279
  /** The token item used as currency (e.g. Serpent Token). The market reads stack count from the player's inventory. */
235
280
  currencyItem: Item;
236
- /** Stock entries. Each refresh restores the listed `item.stacks` count for each entry. */
237
- tokenStock: MarketTokenStock[];
281
+ /** Stock entries, grouped by realm. Items from all realms up to the player's current realm are shown. */
282
+ tokenStock: {
283
+ [key in Realm]: MarketTokenStock[];
284
+ };
238
285
  }
239
286
  export interface MarketBuilding extends ShopBuilding {
240
287
  kind: 'market';
package/dist/location.js CHANGED
@@ -29,5 +29,6 @@ export const buildingTypeToName = {
29
29
  enchantmentShop: 'Enchantment Shop',
30
30
  challengeBoard: 'Challenge Board',
31
31
  jadeCutter: 'Jade Cutter',
32
+ rumours: 'Rumour Trader',
32
33
  modBuilding: '',
33
34
  };
@@ -1,3 +1,4 @@
1
+ import { AmbienceName, MusicName } from './audio';
1
2
  import type { Buff } from './buff';
2
3
  import type { EnemyEntity } from './entity';
3
4
  import type { FlareItem } from './item';
@@ -56,9 +57,9 @@ export interface MineConfig {
56
57
  /** Defeat flavor text after pathing combat. */
57
58
  combatDefeatText: string;
58
59
  /** Music cue name (resolved against musicMap in AppRouter). */
59
- music: string;
60
+ music: MusicName;
60
61
  /** Ambience cue name (resolved against ambienceMap in AppRouter). */
61
- ambience: string;
62
+ ambience: AmbienceName;
62
63
  }
63
64
  export declare const getMineStratumIndex: (y: number) => number;
64
65
  export declare const getMineStratumAt: (config: MineConfig, y: number) => MineStratum | undefined;
package/dist/mod.d.ts CHANGED
@@ -130,6 +130,14 @@ export interface PlayerSpriteImages {
130
130
  support: string;
131
131
  /** Image shown when using utility stance techniques */
132
132
  utility: string;
133
+ /** Image shown during crafting support technique */
134
+ craftingSupport?: string;
135
+ /** Image shown during crafting stabilize technique */
136
+ craftingStabilize?: string;
137
+ /** Image shown during crafting refine technique */
138
+ craftingRefine?: string;
139
+ /** Image shown during crafting fusion technique */
140
+ craftingFusion?: string;
133
141
  }
134
142
  /**
135
143
  * A custom player sprite that can be selected during character creation.
@@ -240,6 +248,14 @@ export interface ModReduxAPI {
240
248
  * setModData('myMod', 'customNPC_affinity', { npcId: 'elder_li', value: 75 });
241
249
  */
242
250
  setModData: (modName: string, key: string, data: unknown) => void;
251
+ /**
252
+ * Remove a key from mod-specific data stored in save file.
253
+ * @param modName - Name of your mod (for namespacing)
254
+ * @param key - Data key identifier to remove
255
+ * @example
256
+ * removeModData('myMod', 'customNPC_affinity');
257
+ */
258
+ removeModData: (modName: string, key: string) => void;
243
259
  /**
244
260
  * Move player to a different location on the world map.
245
261
  * @param location - Location key from the locations data
@@ -399,6 +415,26 @@ export interface ModReduxAPI {
399
415
  export type ModScreenFC = React.FC<{
400
416
  screenAPI: ModReduxAPI;
401
417
  }>;
418
+ /**
419
+ * Represents save file metadata shown on the Load page.
420
+ */
421
+ export interface Save {
422
+ dbName: string;
423
+ playerName: string;
424
+ forename?: string;
425
+ surname?: string;
426
+ icon: string;
427
+ realm: string;
428
+ realmProgress: string;
429
+ age: number;
430
+ createdAt: number;
431
+ lastPlayed?: number;
432
+ playtime: number;
433
+ version: string;
434
+ mods: string[];
435
+ location?: string;
436
+ screen?: string;
437
+ }
402
438
  /**
403
439
  * A React component that renders a mod's settings/options UI.
404
440
  * Receives a Redux API for accessing game state and dispatching actions.
@@ -2294,13 +2330,6 @@ export interface ModAPI {
2294
2330
  * @returns Total exploration amount per action
2295
2331
  */
2296
2332
  getExplorationAmount: (player: PlayerEntity) => number;
2297
- /**
2298
- * Get the number of qi droplets produced per condensation action.
2299
- * @param player - The player entity
2300
- * @param breakthrough - The current breakthrough state
2301
- * @returns Number of droplets produced (0 if no vessel equipped)
2302
- */
2303
- getDropletProduction: (player: PlayerEntity, breakthrough: BreakthroughState) => number;
2304
2333
  /**
2305
2334
  * Calculate the effective toxicity of a pill before rounding.
2306
2335
  * Applies toxicity resistance and realm-based scaling penalty.
@@ -2481,8 +2510,6 @@ export interface ModAPI {
2481
2510
  */
2482
2511
  getCondensationCost: (player: PlayerEntity, breakthrough: BreakthroughState) => {
2483
2512
  qi: number;
2484
- money: number;
2485
- hp: number;
2486
2513
  };
2487
2514
  /**
2488
2515
  * Check whether the player can currently perform a condensation action.
@@ -2916,6 +2943,16 @@ export interface ModAPI {
2916
2943
  * }));
2917
2944
  */
2918
2945
  onGameLoad: (interceptor: (state: RootState) => RootState) => () => void;
2946
+ /**
2947
+ * Called when a character save is deleted from the Load page.
2948
+ * Use this to clean up per-character mod configuration.
2949
+ * @param interceptor - Receives the save info being deleted
2950
+ * @example
2951
+ * modAPI.hooks.onDeleteCharacter((saveInfo) => {
2952
+ * myModConfig.deleteCharacterConfig(saveInfo.dbName);
2953
+ * });
2954
+ */
2955
+ onDeleteCharacter: (interceptor: (saveInfo: Save) => void) => () => void;
2919
2956
  /**
2920
2957
  * Hook fired before combat advances a step
2921
2958
  * Return a modified context to change combat state, a cancel object (with log message) to skip the step, or `null` to continue as normal.
package/dist/quest.d.ts CHANGED
@@ -45,7 +45,7 @@ interface GuildApprovalQuestReward {
45
45
  amount: number;
46
46
  guild: string;
47
47
  }
48
- export type QuestStep = EventQuestStep | ConditionQuestStep | CollectQuestStep | MissionHallQuestStep | FlagValueQuestStep | SpeakToCharacterQuestStep | KillQuestStep | WaitQuestStep | RaidQuestStep;
48
+ export type QuestStep = EventQuestStep | ExplorationEventQuestStep | ConditionQuestStep | CollectQuestStep | MissionHallQuestStep | FlagValueQuestStep | SpeakToCharacterQuestStep | KillQuestStep | WaitQuestStep | RaidQuestStep;
49
49
  interface QuestStepBase {
50
50
  hint: Translatable;
51
51
  }
@@ -54,6 +54,13 @@ interface EventQuestStep extends QuestStepBase {
54
54
  event: GameEvent;
55
55
  completionCondition?: string;
56
56
  }
57
+ export interface ExplorationEventQuestStep extends QuestStepBase {
58
+ kind: 'explorationEvent';
59
+ event: GameEvent;
60
+ locations: string[];
61
+ completionCondition?: string;
62
+ triggerChance?: number;
63
+ }
57
64
  interface ConditionQuestStep extends QuestStepBase {
58
65
  kind: 'condition';
59
66
  completionCondition: string;
@@ -21,7 +21,8 @@ import type { EventStep, GameEvent, ReportAnalyticsStep, StatusStep, TournamentS
21
21
  import type { Background } from './background';
22
22
  import type { Realm } from './realm';
23
23
  import type { CraftingBuff } from './craftingBuff';
24
- import { ExpeditionTeam } from './expedition';
24
+ import { ExpeditionTeam, TeamPoints } from './expedition';
25
+ import { FormationPuzzleCell, FormationWinCondition } from './formation';
25
26
  export interface Viewport {
26
27
  x: number;
27
28
  y: number;
@@ -34,6 +35,8 @@ export interface PlayerState {
34
35
  }[];
35
36
  numStancesSeen: number;
36
37
  learnedNpcManuals?: ManualItem[];
38
+ /** Formation node ids the player has learned and can use in crafting mode. */
39
+ learnedFormationGlyphs?: string[];
37
40
  }
38
41
  export interface InventoryItemState {
39
42
  name: string;
@@ -91,10 +94,13 @@ export interface CombatState {
91
94
  buildId: string | null;
92
95
  prevWins: number;
93
96
  };
94
- /** Marker for an inner-world fight (Life Flourishing). VictoryDialog reads it to dispatch the right follow-up. */
97
+ /**
98
+ * Marker for an inner-world fight (Life Flourishing). VictoryDialog reads
99
+ * it to dispatch the post-incursion reward picker on victory.
100
+ */
95
101
  lifeFlourishingCombat?: {
96
- kind: 'expand' | 'incursion';
97
- incursionId?: string;
102
+ kind: 'incursion';
103
+ incursionId: string;
98
104
  };
99
105
  }
100
106
  export type CraftingSliceState = ExistingCraftingState;
@@ -297,6 +303,8 @@ export interface ExpeditionState {
297
303
  };
298
304
  explored: string[];
299
305
  instability: number;
306
+ pointPool: number;
307
+ teamPoints?: TeamPoints;
300
308
  }
301
309
  export interface TournamentSliceState {
302
310
  sourceStep?: TournamentStep;
@@ -458,6 +466,12 @@ export interface GameEventState {
458
466
  useLocationbg?: boolean;
459
467
  teamUpOverride?: Buff;
460
468
  craftingTeamUpOverride?: CraftingBuff;
469
+ /**
470
+ * Empathy companion crafting buff generated by handleCrafting when the
471
+ * manifestation rolls in, mirrored after craftingTeamUpOverride. Picked up
472
+ * by the crafting confirmation UI at session start.
473
+ */
474
+ empathyCraftingOverride?: CraftingBuff;
461
475
  musicOverride?: MusicName;
462
476
  flags: Record<string, number>;
463
477
  sourceQuest?: {
@@ -539,5 +553,40 @@ export interface RootState {
539
553
  fallenStar: FallenStarState;
540
554
  characterUiPreferences: CharacterUiPreferencesState;
541
555
  soulShardDelve: SoulShardDelveState;
556
+ formationPuzzle: FormationPuzzleState;
557
+ }
558
+ export interface FormationPuzzlePlacementState {
559
+ nodeId: string;
560
+ x: number;
561
+ y: number;
562
+ rotation: number;
563
+ locked?: boolean;
564
+ /** Pool entry index this placement was drawn from (undefined for prePlaced). */
565
+ poolIndex?: number;
566
+ }
567
+ export interface FormationPuzzleActiveState {
568
+ flagPrefix: string;
569
+ displayName?: string;
570
+ cells: FormationPuzzleCell[];
571
+ pool: {
572
+ nodes: {
573
+ nodeId: string;
574
+ count: number;
575
+ }[];
576
+ glyphs: {
577
+ nodeId: string;
578
+ count: number;
579
+ }[];
580
+ };
581
+ winCondition: FormationWinCondition[];
582
+ }
583
+ export interface FormationPuzzleState {
584
+ /** Active inline puzzle definition (from the FormationPuzzleStep), or
585
+ * undefined when no puzzle is in progress. */
586
+ active?: FormationPuzzleActiveState;
587
+ /** All node placements on the grid (locked + player). */
588
+ placements: FormationPuzzlePlacementState[];
589
+ /** Snapshot of event state when the puzzle started; restored on completion. */
590
+ eventStateSnapshot?: GameEventState;
542
591
  }
543
592
  export {};
@@ -131,11 +131,14 @@ export interface SoulShardDelveConfig {
131
131
  location: string;
132
132
  /** Events played when each threshold is crossed, in order. Length determines total threshold count. */
133
133
  thresholdEvents: GameEvent[];
134
- /** One-time item rewards granted when accumulatedQither reaches each intensity level. */
135
- intensityRewards: {
134
+ /** One-time rewards granted when accumulatedQither reaches each intensity level. */
135
+ intensityRewards: ({
136
136
  intensity: number;
137
137
  reward: ItemDesc;
138
- }[];
138
+ } | {
139
+ intensity: number;
140
+ minorAspectId: string;
141
+ })[];
139
142
  /** Source power regeneration rate: 1 qither per this many months. Default 6. */
140
143
  rechargeMonths: number;
141
144
  monsters: RegionMonsters;
package/dist/stat.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { TechniqueElement } from './element';
3
3
  export declare const physicalStatistics: readonly ["eyes", "meridians", "dantian", "muscles", "digestion", "flesh"];
4
4
  export declare const socialStatistics: readonly ["age", "lifespan", "charisma", "battlesense", "craftskill", "artefactslots", "talismanslots", "condenseEfficiency", "pillsPerRound"];
5
5
  export declare const craftingStatistics: readonly ["maxpool", "pool", "maxtoxicity", "toxicity", "resistance", "itemEffectiveness", "control", "intensity", "critchance", "critmultiplier", "pillsPerRound", "poolCostPercentage", "stabilityCostPercentage", "successChanceBonus", "poolCostFlat"];
6
- export declare const combatStatistics: readonly ["maxhp", "hp", "maxbarrier", "barrier", "maxtoxicity", "toxicity", "resistance", "pillsPerRound", "itemEffectiveness", "power", "artefactpower", "critchance", "defense", "protection", "dr", "barrierMitigation", "lifesteal", "critmultiplier", "vulnerability", "weakness", "fistBoost", "blossomBoost", "weaponBoost", "cloudBoost", "bloodBoost", "celestialBoost", "fistAffinity", "blossomAffinity", "weaponAffinity", "cloudAffinity", "bloodAffinity", "celestialAffinity", "noneAffinity", "qiDroplets", "fistDisabled", "bloodDisabled", "blossomDisabled", "cloudDisabled", "celestialDisabled", "weaponDisabled", "noneDisabled", "pillsDisabled", "dropletsDisabled", "fistResistance", "blossomResistance", "weaponResistance", "cloudResistance", "bloodResistance", "celestialResistance", "damageBoost", "healingBoost", "barrierBoost", "temphpBoost", "overheal", "barrierBleed", "formationPartRecovery", "overcrit", "cultivatorResistance", "temphp"];
6
+ export declare const combatStatistics: readonly ["maxhp", "hp", "maxbarrier", "barrier", "maxtoxicity", "toxicity", "resistance", "pillsPerRound", "itemEffectiveness", "power", "artefactpower", "critchance", "defense", "protection", "dr", "barrierMitigation", "lifesteal", "critmultiplier", "vulnerability", "weakness", "fistBoost", "blossomBoost", "weaponBoost", "cloudBoost", "bloodBoost", "celestialBoost", "fistAffinity", "blossomAffinity", "weaponAffinity", "cloudAffinity", "bloodAffinity", "celestialAffinity", "noneAffinity", "qiDroplets", "excessDropletsCost", "dropletBoost", "openerBoost", "finisherBoost", "fistDisabled", "bloodDisabled", "blossomDisabled", "cloudDisabled", "celestialDisabled", "weaponDisabled", "noneDisabled", "pillsDisabled", "dropletsDisabled", "fistResistance", "blossomResistance", "weaponResistance", "cloudResistance", "bloodResistance", "celestialResistance", "damageBoost", "healingBoost", "barrierBoost", "temphpBoost", "overheal", "barrierBleed", "formationPartRecovery", "overcrit", "cultivatorResistance", "temphp"];
7
7
  export type PhysicalStatistic = (typeof physicalStatistics)[number];
8
8
  export type SocialStatistic = (typeof socialStatistics)[number];
9
9
  export type CraftingStatistic = (typeof craftingStatistics)[number];
@@ -32,8 +32,11 @@ export declare const combatStatToName: {
32
32
  export declare const combatStatToDescription: {
33
33
  [key in CombatStatistic]: string;
34
34
  };
35
- export declare const percentageCombatStats: Set<"pillsPerRound" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "critchance" | "critmultiplier" | "maxhp" | "hp" | "maxbarrier" | "barrier" | "power" | "artefactpower" | "defense" | "protection" | "dr" | "barrierMitigation" | "lifesteal" | "vulnerability" | "weakness" | "fistBoost" | "blossomBoost" | "weaponBoost" | "cloudBoost" | "bloodBoost" | "celestialBoost" | "fistAffinity" | "blossomAffinity" | "weaponAffinity" | "cloudAffinity" | "bloodAffinity" | "celestialAffinity" | "noneAffinity" | "qiDroplets" | "fistDisabled" | "bloodDisabled" | "blossomDisabled" | "cloudDisabled" | "celestialDisabled" | "weaponDisabled" | "noneDisabled" | "pillsDisabled" | "dropletsDisabled" | "fistResistance" | "blossomResistance" | "weaponResistance" | "cloudResistance" | "bloodResistance" | "celestialResistance" | "damageBoost" | "healingBoost" | "barrierBoost" | "temphpBoost" | "overheal" | "barrierBleed" | "formationPartRecovery" | "overcrit" | "cultivatorResistance" | "temphp">;
36
- export declare const multiplicativePercentStats: Set<"pillsPerRound" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "critchance" | "critmultiplier" | "maxhp" | "hp" | "maxbarrier" | "barrier" | "power" | "artefactpower" | "defense" | "protection" | "dr" | "barrierMitigation" | "lifesteal" | "vulnerability" | "weakness" | "fistBoost" | "blossomBoost" | "weaponBoost" | "cloudBoost" | "bloodBoost" | "celestialBoost" | "fistAffinity" | "blossomAffinity" | "weaponAffinity" | "cloudAffinity" | "bloodAffinity" | "celestialAffinity" | "noneAffinity" | "qiDroplets" | "fistDisabled" | "bloodDisabled" | "blossomDisabled" | "cloudDisabled" | "celestialDisabled" | "weaponDisabled" | "noneDisabled" | "pillsDisabled" | "dropletsDisabled" | "fistResistance" | "blossomResistance" | "weaponResistance" | "cloudResistance" | "bloodResistance" | "celestialResistance" | "damageBoost" | "healingBoost" | "barrierBoost" | "temphpBoost" | "overheal" | "barrierBleed" | "formationPartRecovery" | "overcrit" | "cultivatorResistance" | "temphp">;
35
+ export declare const percentageCombatStats: Set<"pillsPerRound" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "critchance" | "critmultiplier" | "maxhp" | "hp" | "maxbarrier" | "barrier" | "power" | "artefactpower" | "defense" | "protection" | "dr" | "barrierMitigation" | "lifesteal" | "vulnerability" | "weakness" | "fistBoost" | "blossomBoost" | "weaponBoost" | "cloudBoost" | "bloodBoost" | "celestialBoost" | "fistAffinity" | "blossomAffinity" | "weaponAffinity" | "cloudAffinity" | "bloodAffinity" | "celestialAffinity" | "noneAffinity" | "qiDroplets" | "excessDropletsCost" | "dropletBoost" | "openerBoost" | "finisherBoost" | "fistDisabled" | "bloodDisabled" | "blossomDisabled" | "cloudDisabled" | "celestialDisabled" | "weaponDisabled" | "noneDisabled" | "pillsDisabled" | "dropletsDisabled" | "fistResistance" | "blossomResistance" | "weaponResistance" | "cloudResistance" | "bloodResistance" | "celestialResistance" | "damageBoost" | "healingBoost" | "barrierBoost" | "temphpBoost" | "overheal" | "barrierBleed" | "formationPartRecovery" | "overcrit" | "cultivatorResistance" | "temphp">;
36
+ export declare const multiplicativePercentStats: Set<"pillsPerRound" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "critchance" | "critmultiplier" | "maxhp" | "hp" | "maxbarrier" | "barrier" | "power" | "artefactpower" | "defense" | "protection" | "dr" | "barrierMitigation" | "lifesteal" | "vulnerability" | "weakness" | "fistBoost" | "blossomBoost" | "weaponBoost" | "cloudBoost" | "bloodBoost" | "celestialBoost" | "fistAffinity" | "blossomAffinity" | "weaponAffinity" | "cloudAffinity" | "bloodAffinity" | "celestialAffinity" | "noneAffinity" | "qiDroplets" | "excessDropletsCost" | "dropletBoost" | "openerBoost" | "finisherBoost" | "fistDisabled" | "bloodDisabled" | "blossomDisabled" | "cloudDisabled" | "celestialDisabled" | "weaponDisabled" | "noneDisabled" | "pillsDisabled" | "dropletsDisabled" | "fistResistance" | "blossomResistance" | "weaponResistance" | "cloudResistance" | "bloodResistance" | "celestialResistance" | "damageBoost" | "healingBoost" | "barrierBoost" | "temphpBoost" | "overheal" | "barrierBleed" | "formationPartRecovery" | "overcrit" | "cultivatorResistance" | "temphp">;
37
+ export declare const percentageCraftingStats: Set<"pillsPerRound" | "maxpool" | "pool" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "control" | "intensity" | "critchance" | "critmultiplier" | "poolCostPercentage" | "stabilityCostPercentage" | "successChanceBonus" | "poolCostFlat">;
38
+ export declare const unsignedCraftingStats: Set<"pillsPerRound" | "maxpool" | "pool" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "control" | "intensity" | "critchance" | "critmultiplier" | "poolCostPercentage" | "stabilityCostPercentage" | "successChanceBonus" | "poolCostFlat">;
39
+ export declare const fractionalCraftingStats: Set<"pillsPerRound" | "maxpool" | "pool" | "maxtoxicity" | "toxicity" | "resistance" | "itemEffectiveness" | "control" | "intensity" | "critchance" | "critmultiplier" | "poolCostPercentage" | "stabilityCostPercentage" | "successChanceBonus" | "poolCostFlat">;
37
40
  export declare const uncommonStatTooltips: Partial<{
38
41
  [key in CombatStatistic]: string;
39
42
  }>;
package/dist/stat.js CHANGED
@@ -69,6 +69,10 @@ export const combatStatistics = [
69
69
  'celestialAffinity',
70
70
  'noneAffinity',
71
71
  'qiDroplets',
72
+ 'excessDropletsCost',
73
+ 'dropletBoost',
74
+ 'openerBoost',
75
+ 'finisherBoost',
72
76
  'fistDisabled',
73
77
  'bloodDisabled',
74
78
  'blossomDisabled',
@@ -133,6 +137,10 @@ export const combatStatToName = {
133
137
  bloodBoost: 'Blood Boost',
134
138
  celestialBoost: 'Celestial Boost',
135
139
  qiDroplets: 'Qi Droplets',
140
+ excessDropletsCost: 'Excess Droplet Health Cost',
141
+ dropletBoost: 'Droplet Boost',
142
+ openerBoost: 'Opener Boost',
143
+ finisherBoost: 'Finisher Boost',
136
144
  fistAffinity: 'Fist Root',
137
145
  blossomAffinity: 'Blossom Root',
138
146
  weaponAffinity: 'Weapon Root',
@@ -194,6 +202,10 @@ export const combatStatToDescription = {
194
202
  celestialBoost: '',
195
203
  barrierMitigation: 'Controls how efficiently your barrier absorbs damage, with diminishing returns. Barrier always blocks damage first before health is lost.',
196
204
  qiDroplets: '',
205
+ excessDropletsCost: '',
206
+ dropletBoost: '',
207
+ openerBoost: '',
208
+ finisherBoost: '',
197
209
  fistAffinity: '',
198
210
  blossomAffinity: '',
199
211
  weaponAffinity: '',
@@ -261,11 +273,34 @@ export const percentageCombatStats = new Set([
261
273
  'bloodResistance',
262
274
  'celestialResistance',
263
275
  'cultivatorResistance',
276
+ 'excessDropletsCost',
277
+ 'dropletBoost',
278
+ 'openerBoost',
279
+ 'finisherBoost',
264
280
  ]);
265
281
  // Stats whose sources multiply together rather than add.
266
282
  // Each source applies as (1 - value/100)^scaleFactor to a running multiplier.
267
283
  // The final value is converted back to a percentage: (1 - multiplier) * 100.
268
284
  export const multiplicativePercentStats = new Set(['weakness', 'dr']);
285
+ // Crafting stats that are displayed as percentages in tooltips and UI.
286
+ export const percentageCraftingStats = new Set([
287
+ 'critchance',
288
+ 'critmultiplier',
289
+ 'resistance',
290
+ 'itemEffectiveness',
291
+ 'successChanceBonus',
292
+ 'poolCostPercentage',
293
+ 'stabilityCostPercentage',
294
+ ]);
295
+ // Crafting percentage stats that are multipliers (100 = baseline) rather than
296
+ // signed bonuses, so they render without a +/- prefix.
297
+ export const unsignedCraftingStats = new Set([
298
+ 'poolCostPercentage',
299
+ 'stabilityCostPercentage',
300
+ ]);
301
+ // successChanceBonus is stored as a fraction (0.05 = 5%); other percentage
302
+ // crafting stats store the integer percent directly.
303
+ export const fractionalCraftingStats = new Set(['successChanceBonus']);
269
304
  // Uncommon stats that need auxiliary tooltips when they appear on buffs
270
305
  // These descriptions only show as aux tooltips in buff tooltips, not in the stats dialog
271
306
  export const uncommonStatTooltips = {
@@ -281,6 +316,10 @@ export const uncommonStatTooltips = {
281
316
  formationPartRecovery: 'Each point of <n>Formation Part Recovery</n> returns a <n>Formation Part</n> used in combat back to your Spatial Ring after battle.',
282
317
  overcrit: '<n>Overcrit</n> allows critical hits to chain up to <n>9</n> additional times. Your crit chance is multiplied by your overcrit percentage after each chain. Overcrit has a maximum of <n>90%</n>.',
283
318
  dr: '<n>Damage Resistance</n> reduces damage taken to your health. Multiple sources multiply together. Does not affect damage taken to your barrier.',
319
+ excessDropletsCost: '<n>Excess Droplet Health Cost</n> lets droplet techniques be used even when you lack enough <n>Qi Droplets</n>. Each missing droplet instead costs this percentage of your <n>Max Health</n>, dealt as <name>True Damage</name>.',
320
+ dropletBoost: '<n>Droplet Boost</n> increases the power of techniques that cost <n>Qi Droplets</n>.',
321
+ openerBoost: '<n>Opener Boost</n> increases the power of <n>Opener</n> techniques.',
322
+ finisherBoost: '<n>Finisher Boost</n> increases the power of <n>Finisher</n> techniques.',
284
323
  };
285
324
  export const craftingStatToName = {
286
325
  maxpool: 'Max Qi Pool',
@@ -20,4 +20,11 @@ export interface WorldAspect {
20
20
  /** GameLocation name. The location's `image` is the World tab background and its `icon` is the aspect picker icon. */
21
21
  location: string;
22
22
  essenceGeneration: WorldAspectEssence[];
23
+ /**
24
+ * Marks this aspect as a minor aspect. Minor aspects are only available
25
+ * through the manual expansion unlock; they cannot be chosen as the main
26
+ * world aspect. They can be placed into minor aspect slots alongside major
27
+ * aspects to generate their essences at full per-month rate.
28
+ */
29
+ minor?: boolean;
23
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "afnm-types",
3
- "version": "0.6.58",
3
+ "version": "0.6.59",
4
4
  "description": "Type definitions for Ascend From Nine Mountains",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",