afnm-types 0.6.55-injecttest2 → 0.6.56

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/buff.d.ts CHANGED
@@ -18,14 +18,17 @@ export type DamageModifier = MultiplyDamageModifier | ReduceDamageModifier | Exp
18
18
  interface MultiplyDamageModifier {
19
19
  kind: 'multiply';
20
20
  value: number;
21
+ cantUpgrade?: boolean;
21
22
  }
22
23
  interface ReduceDamageModifier {
23
24
  kind: 'reduce';
24
25
  percent: number;
26
+ cantUpgrade?: boolean;
25
27
  }
26
28
  interface ExpressionDamageModifier {
27
29
  kind: 'expression';
28
30
  expression: string;
31
+ cantUpgrade?: boolean;
29
32
  }
30
33
  interface ChanceTechniqueCondition extends BaseTechniqueCondition {
31
34
  kind: 'chance';
@@ -121,7 +124,7 @@ export interface Buff {
121
124
  value: number;
122
125
  };
123
126
  effects?: BuffEffect[];
124
- appliesTo: ('damage' | 'barrier' | 'heal')[];
127
+ appliesTo: ('damage' | 'barrier' | 'heal' | 'tempHealth')[];
125
128
  }[];
126
129
  /** Amplifies buff creation. Modifies stack count when matching buffs are created on self. */
127
130
  buffAmplifierEffects?: {
@@ -317,7 +320,7 @@ export interface TransformationCombatImage {
317
320
  animateOnEntity?: boolean;
318
321
  }
319
322
  export type RepairRule = 'all' | 'lowestHealth' | 'highestHealth';
320
- export type BuffEffect = DamageEffect | DamageSelfEffect | HealEffect | BarrierEffect | CreateBuffSelfEffect | ConsumeBuffSelfEffect | CreateBuffTargetEffect | ConsumeBuffTargetEffect | NegateEffect | AddEffect | MultiplyEffect | MergeEffect | TriggerEffect | ModifyBuffGroupEffect | CleanseToxicityEffect | SetStateEffect | ConvertSelfEffect | RepairEffect | ConsumeInventoryItemEffect;
323
+ export type BuffEffect = DamageEffect | DamageSelfEffect | HealEffect | BarrierEffect | GiveTemporaryHealthEffect | CreateBuffSelfEffect | ConsumeBuffSelfEffect | CreateBuffTargetEffect | ConsumeBuffTargetEffect | NegateEffect | AddEffect | MultiplyEffect | MergeEffect | TriggerEffect | ModifyBuffGroupEffect | CleanseToxicityEffect | SetStateEffect | ConvertSelfEffect | RepairEffect | ConsumeInventoryItemEffect;
321
324
  interface BaseBuff {
322
325
  condition?: TechniqueCondition;
323
326
  triggerKey?: string;
@@ -349,6 +352,12 @@ interface BarrierEffect extends BaseBuff {
349
352
  hits?: Scaling;
350
353
  targeting?: EffectTargeting;
351
354
  }
355
+ interface GiveTemporaryHealthEffect extends BaseBuff {
356
+ kind: 'temporaryHealth';
357
+ amount: Scaling;
358
+ hits?: Scaling;
359
+ targeting?: EffectTargeting;
360
+ }
352
361
  interface CreateBuffSelfEffect extends BaseBuff {
353
362
  kind: 'buffSelf';
354
363
  amount: Scaling;
@@ -331,7 +331,7 @@ export interface CharacterRelationshipDefinition {
331
331
  tooltip: Translatable;
332
332
  followCharacter?: FollowCharacterDefinition;
333
333
  dualCultivation?: DualCultivationDefinition;
334
- progressionEvent: {
334
+ progressionEvent?: {
335
335
  name: string;
336
336
  tooltip: Translatable;
337
337
  event: EventStep[];
@@ -3,7 +3,7 @@
3
3
  * Configuration constants for the translation extraction script
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.REQUIREMENT_STRINGS = exports.CONDITION_TEMPLATES = exports.CONDITION_WORDS = exports.HARDCODED_TEMPLATE_PATTERNS = exports.TIER_ONLY_MAPS = exports.STATIC_MAPS = exports.STATIC_ARRAY_NAMES = exports.TEXT_SHADOW = exports.STYLE_COLORS = exports.ELEMENTS = exports.ELEMENT_TO_NAME = exports.RARITIES = exports.RARITY_TO_TIER = exports.REALMS = exports.REALM_TO_NAME = exports.REALM_TO_TIER = exports.CODE_CHANGE_EXCLUDE_FILES = exports.CODE_CHANGE_EXCLUDE_PATTERNS = exports.EXCLUDE_PATTERNS = exports.NON_TRANSLATABLE_PROPERTIES = exports.MIN_STRING_LENGTH = void 0;
6
+ exports.REQUIREMENT_STRINGS = exports.CONDITION_TEMPLATES = exports.CONDITION_WORDS = exports.HARDCODED_TEMPLATE_PATTERNS = exports.TIER_ONLY_MAPS = exports.STATIC_MAPS = exports.STATIC_ARRAY_NAMES = exports.ENEMY_STANCE_NAMES = exports.TEXT_SHADOW = exports.STYLE_COLORS = exports.ELEMENTS = exports.ELEMENT_TO_NAME = exports.RARITIES = exports.RARITY_TO_TIER = exports.REALMS = exports.REALM_TO_NAME = exports.REALM_TO_TIER = exports.CODE_CHANGE_EXCLUDE_FILES = exports.CODE_CHANGE_EXCLUDE_PATTERNS = exports.EXCLUDE_PATTERNS = exports.NON_TRANSLATABLE_PROPERTIES = exports.MIN_STRING_LENGTH = void 0;
7
7
  exports.isTranslatableProperty = isTranslatableProperty;
8
8
  exports.isCamelCase = isCamelCase;
9
9
  exports.shouldExclude = shouldExclude;
@@ -512,6 +512,33 @@ exports.STYLE_COLORS = {
512
512
  num: '#ff877d',
513
513
  };
514
514
  exports.TEXT_SHADOW = 'textShadow: -1px 1px 0 #000, 1px 1px 0 #000, 1px -1px 0 #000, -1px -1px 0 #000;';
515
+ /** Internal stance name identifiers used by EnemyEntity definitions. Never player-facing. */
516
+ exports.ENEMY_STANCE_NAMES = new Set([
517
+ 'absorb', 'accelerate', 'aggressive', 'agitation', 'amplify', 'animate', 'ascension',
518
+ 'assault', 'assess', 'attack', 'attack1', 'attack2', 'attack3', 'attack4', 'attackBig',
519
+ 'barrier', 'barrierHater', 'barrierMit', 'bigBloat', 'blast', 'block', 'blood', 'blossom',
520
+ 'boil', 'bolt', 'buff', 'buffSelf', 'buffStance', 'buffTarget', 'build', 'burn',
521
+ 'burrowStrike', 'burst', 'cacophonyPhase', 'celestial', 'celestialradiance', 'chaosForge',
522
+ 'charge', 'cleave', 'cloud', 'collapse', 'compliance', 'constrict', 'consume', 'consuming',
523
+ 'control', 'corrupt', 'corrupted_swarm', 'counterattack', 'crownedascension',
524
+ 'crownedgathering', 'crownedradiance', 'crushing', 'damage', 'damageHeavy', 'debuff',
525
+ 'debuffHeal', 'debuffStance', 'defend', 'defending', 'defensive', 'defensiveHeavy',
526
+ 'defensiveStance', 'desertGuardian', 'desperate', 'destabilize', 'dissonancePhase', 'drain',
527
+ 'eclipse', 'emerald', 'endure', 'enhanced_attack', 'enhanced_attack1', 'enhanced_attack2',
528
+ 'enhanced_attack3', 'enhanced_block', 'enhanced_rend', 'escalation1', 'escalation2',
529
+ 'fieldSet', 'finale', 'first', 'firstRound', 'fist', 'float', 'focus', 'forging', 'fortify',
530
+ 'frenzy', 'gathering', 'gore', 'gossamerLight', 'gossamerShadow', 'gossamerVault', 'guard',
531
+ 'harm', 'harmonyPhase', 'heal', 'healDebuff', 'initializing', 'kick', 'lash', 'lethargy',
532
+ 'mend', 'meteor', 'mindlessAttack', 'moon', 'multihits', 'mystic', 'needle', 'offensiveStance',
533
+ 'opener', 'origin', 'overcharged', 'pause', 'peck', 'pierce', 'plumagegathering', 'power',
534
+ 'powerBuildUp', 'preparation', 'prepare', 'pressureAssault', 'pulse', 'punch', 'purge',
535
+ 'recklessness', 'rend', 'ruby', 'sandstorm', 'sap', 'sapphire', 'seal', 'setup', 'setup1',
536
+ 'setup2', 'setup3', 'shatter', 'shroudLight', 'shroudShadow', 'shroudVault', 'siphon', 'soar',
537
+ 'spin', 'stack', 'stacks', 'steal', 'sun', 'surge', 'sustain', 'swarm', 'swarmling',
538
+ 'swarmlord', 'touch', 'turtle', 'twisted_rampage', 'underground', 'unleash', 'unravel',
539
+ 'unstableFury', 'vaultBreak', 'venom', 'venomMaster', 'wail', 'weapon', 'weave',
540
+ 'weaveAttacker', 'weaveHealer', 'weaveLight', 'weaveProtector', 'weaveShadow', 'windDancer',
541
+ ]);
515
542
  /** Arrays that indicate static map iteration */
516
543
  exports.STATIC_ARRAY_NAMES = ['realms', 'techniqueElements'];
517
544
  /** Static maps that can be expanded */
@@ -653,9 +653,10 @@ function extractFromFile(filePath) {
653
653
  // not player-facing text (e.g., "attack1", "enhanced_attack2", "stack", "power")
654
654
  // Exception: manual items have style/stances with player-facing names like "Defensive Focus"
655
655
  if (propName === 'name' &&
656
- nestedPath &&
657
- ((/\bstances\b/.test(nestedPath) && !/\bstyle\b/.test(nestedPath)) ||
658
- /\bmastery\b/.test(nestedPath))) {
656
+ ((nestedPath &&
657
+ ((/\bstances\b/.test(nestedPath) && !/\bstyle\b/.test(nestedPath)) ||
658
+ /\bmastery\b/.test(nestedPath))) ||
659
+ (typescript_1.default.isStringLiteral(node.initializer) && config_js_1.ENEMY_STANCE_NAMES.has(node.initializer.text)))) {
659
660
  typescript_1.default.forEachChild(node, visit);
660
661
  return;
661
662
  }
@@ -923,7 +924,12 @@ function extractFromFile(filePath) {
923
924
  }
924
925
  }
925
926
  }
926
- if (!isDirectExportedArray) {
927
+ // Skip arrays that are values of non-translatable properties (e.g., stances: ['attack1', ...])
928
+ const isInsideNonTranslatableProperty = parent &&
929
+ typescript_1.default.isPropertyAssignment(parent) &&
930
+ typescript_1.default.isIdentifier(parent.name) &&
931
+ !(0, config_js_1.isTranslatableProperty)(parent.name.text);
932
+ if (!isDirectExportedArray && !isInsideNonTranslatableProperty) {
927
933
  const stringElements = node.elements.filter((el) => typescript_1.default.isStringLiteral(el) || typescript_1.default.isNoSubstitutionTemplateLiteral(el));
928
934
  if (stringElements.length > 0) {
929
935
  const translatableStrings = stringElements
@@ -0,0 +1,186 @@
1
+ import { Buff, Translatable } from '.';
2
+ import { BreakthroughState } from './breakthrough';
3
+ import { PlayerEntity, CombatEntity, EnemyEntity, CombatEffectTracking, EntityType } from './entity';
4
+ import { ArtefactTechnique, Item } from './item';
5
+ import { InventoryItemState } from './reduxState';
6
+ import { Technique } from './technique';
7
+ import { PhysicalStatistic, SocialStatistic } from './stat';
8
+ export interface CombatLogEffects {
9
+ source: string;
10
+ damage?: number;
11
+ barrier?: number;
12
+ healing?: number;
13
+ temporaryHealth?: number;
14
+ /** Delta toxicity: positive = gained, negative = cleansed */
15
+ toxicity?: number;
16
+ buffsCreated?: Record<string, number>;
17
+ buffsInflicted?: Record<string, number>;
18
+ buffsConsumed?: Record<string, number>;
19
+ }
20
+ /**
21
+ * Optional semantic metadata for structured analysis of log entries.
22
+ * Used by defeatAnalysis and other systems to avoid regex-based string parsing.
23
+ */
24
+ export interface CombatLogMeta {
25
+ /** Name of the technique or buff that was executed */
26
+ techniqueName?: string;
27
+ /** True when the technique failed to execute */
28
+ failed?: boolean;
29
+ /** The resource that was insufficient/excessive when a technique failed */
30
+ failedResource?: string;
31
+ /** True when this entry records a self-damage event */
32
+ selfDamage?: boolean;
33
+ /** Remaining HP of the target after this damage event */
34
+ remainingHp?: number;
35
+ /** True when this entry marks the end of a round */
36
+ roundEnd?: boolean;
37
+ /** True when this entry is a turn section header (Player's Turn, Enemy's Turn, etc.) */
38
+ turnHeader?: boolean;
39
+ }
40
+ export interface CombatEntitySnapshot {
41
+ hp: number;
42
+ maxHp: number;
43
+ barrier: number;
44
+ }
45
+ export interface CombatPartyMemberSnapshot extends CombatEntitySnapshot {
46
+ partyId?: string;
47
+ }
48
+ export interface CombatStateSnapshot {
49
+ player?: CombatEntitySnapshot;
50
+ enemy?: CombatEntitySnapshot;
51
+ playerParty?: CombatPartyMemberSnapshot[];
52
+ enemyParty?: CombatPartyMemberSnapshot[];
53
+ }
54
+ export interface CombatLogEntry {
55
+ path: string;
56
+ /** Which entity this log entry is about */
57
+ entity: EntityType;
58
+ /** UUID of the specific party member — only set when entity is PlayerParty or EnemyParty */
59
+ partyId?: string;
60
+ message: string;
61
+ effects?: CombatLogEffects;
62
+ meta?: CombatLogMeta;
63
+ /** Snapshot of HP/barrier for all combat entities at the moment this entry was created */
64
+ state?: CombatStateSnapshot;
65
+ }
66
+ export interface StanceTracking {
67
+ techniques: string[];
68
+ name: string;
69
+ count: number;
70
+ }
71
+ export interface PlayerStanceData {
72
+ playerStanceIndex: number;
73
+ usedPlayerStanceOpeners: boolean[];
74
+ lastPlayerStance: string;
75
+ lastCycleKey?: string;
76
+ }
77
+ export interface CurrentCombatState {
78
+ player: PlayerEntity | undefined;
79
+ playerState: CombatEntity | undefined;
80
+ breakthrough: BreakthroughState | undefined;
81
+ playerStanceData: PlayerStanceData | undefined;
82
+ enemies: EnemyEntity[];
83
+ foughtEnemies: EnemyEntity[];
84
+ enemyState: CombatEntity | undefined;
85
+ lastEnemyStance: string | undefined;
86
+ currentPhase: number;
87
+ allPhases: EnemyEntity[];
88
+ phaseTransitioning: boolean;
89
+ roundNum: number;
90
+ roundState: RoundState | undefined;
91
+ consumedPills: number;
92
+ noEnhancement?: boolean;
93
+ noCrit?: boolean;
94
+ trainingMode?: boolean;
95
+ isSpar?: boolean;
96
+ playerEffectTracking: Record<string, CombatEffectTracking>;
97
+ enemyEffectTracking: Record<string, CombatEffectTracking>;
98
+ stanceTracking: Record<string, StanceTracking>;
99
+ partyStanceTracking: Record<number, Record<string, StanceTracking>>;
100
+ pillTracking: Record<string, number>;
101
+ autoUseRowTracking: Record<number, number>;
102
+ inventoryItems?: InventoryItemState[];
103
+ loot: Item[];
104
+ kills: {
105
+ name: string;
106
+ displayName?: Translatable;
107
+ }[];
108
+ qi: number;
109
+ combatState: 'victory' | 'defeat' | undefined;
110
+ combatLog: CombatLogEntry[];
111
+ gameFlags: Record<string, number>;
112
+ precalculatedLoot: {
113
+ items: Item[];
114
+ qi: number;
115
+ }[];
116
+ lootBurstTriggered: boolean;
117
+ lootBurstQueue: {
118
+ id: string;
119
+ loot: Item[];
120
+ qi: number;
121
+ enemyPosition: {
122
+ x: number;
123
+ y: number;
124
+ };
125
+ playerPosition: {
126
+ x: number;
127
+ y: number;
128
+ };
129
+ }[];
130
+ previewCallbacks?: {
131
+ onBuffFailed: (buff: Buff) => void;
132
+ };
133
+ /** Permanent stat changes accumulated during combat to be applied to the player on exit. */
134
+ permanentStatChanges?: Partial<Record<PhysicalStatistic | SocialStatistic, number>>;
135
+ }
136
+ export interface RoundState {
137
+ player: EntityRoundState;
138
+ enemy: EntityRoundState;
139
+ playerArtefacts: ArtefactRoundState[];
140
+ enemyArtefacts: ArtefactRoundState[];
141
+ playerParty: EntityRoundState[];
142
+ enemyParty: EntityRoundState[];
143
+ roundQueue: RoundStep[];
144
+ delay: number;
145
+ }
146
+ export type RoundStep = PlayerRoundStep | PlayerArtefactRoundStep | PlayerPartyRoundStep | EnemyRoundStep | EnemyArtefactRoundStep | EnemyPartyRoundStep | BuffsRoundStep | BuffsRoundStartStep | EndRoundStep;
147
+ export interface PlayerRoundStep {
148
+ kind: 'player';
149
+ }
150
+ export interface PlayerArtefactRoundStep {
151
+ kind: 'playerArtefact';
152
+ index: number;
153
+ }
154
+ export interface PlayerPartyRoundStep {
155
+ kind: 'playerParty';
156
+ index: number;
157
+ }
158
+ export interface EnemyArtefactRoundStep {
159
+ kind: 'enemyArtefact';
160
+ index: number;
161
+ }
162
+ export interface EnemyPartyRoundStep {
163
+ kind: 'enemyParty';
164
+ index: number;
165
+ }
166
+ export interface EnemyRoundStep {
167
+ kind: 'enemy';
168
+ }
169
+ export interface BuffsRoundStep {
170
+ kind: 'buffs';
171
+ }
172
+ export interface BuffsRoundStartStep {
173
+ kind: 'buffsStart';
174
+ }
175
+ export interface EndRoundStep {
176
+ kind: 'end';
177
+ }
178
+ export interface EntityRoundState {
179
+ techniques: Technique[];
180
+ used: Technique[];
181
+ doneUltimate: boolean;
182
+ }
183
+ export interface ArtefactRoundState {
184
+ techniques: ArtefactTechnique[];
185
+ used: ArtefactTechnique[];
186
+ }
package/dist/combat.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/entity.d.ts CHANGED
@@ -14,6 +14,7 @@ export interface CombatEffectTracking {
14
14
  damage: number;
15
15
  healing: number;
16
16
  barrier: number;
17
+ temphp: number;
17
18
  damageTaken: number;
18
19
  }
19
20
  export declare const entityTypes: readonly ["Player", "Lifeform", "Enemy", "PlayerParty", "EnemyParty", "System"];
@@ -15,4 +15,4 @@
15
15
  * };
16
16
  * }
17
17
  */
18
- export declare const GAME_VERSION = "0.6.55";
18
+ export declare const GAME_VERSION = "0.6.56";
@@ -15,4 +15,4 @@
15
15
  * };
16
16
  * }
17
17
  */
18
- export const GAME_VERSION = "0.6.55";
18
+ export const GAME_VERSION = "0.6.56";
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from './calendar';
8
8
  export * from './character';
9
9
  export * from './CharacterRequestEncounter';
10
10
  export * from './components';
11
+ export * from './combat';
11
12
  export * from './crafting';
12
13
  export * from './craftingBuff';
13
14
  export * from './craftingState';
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './calendar';
8
8
  export * from './character';
9
9
  export * from './CharacterRequestEncounter';
10
10
  export * from './components';
11
+ export * from './combat';
11
12
  export * from './crafting';
12
13
  export * from './craftingBuff';
13
14
  export * from './craftingState';
@@ -1,5 +1,5 @@
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' | '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';
2
+ export type KeybindingAction = 'confirm' | 'cancel' | 'pause' | 'alternateConfirm' | 'moveUp' | 'moveDown' | 'moveLeft' | 'moveRight' | 'openInventory' | 'openQuests' | 'openCharacterStats' | 'openTechniques' | 'openCalendar' | 'lockTooltip' | '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
4
  action: KeybindingAction | string;
5
5
  category: KeybindingCategory;
@@ -107,6 +107,14 @@ export const keybindingDefinitions = [
107
107
  defaultKey: 'm',
108
108
  allowRebind: true,
109
109
  },
110
+ {
111
+ action: 'lockTooltip',
112
+ category: 'ui',
113
+ displayName: 'Lock Tooltip',
114
+ description: 'Instantly lock the currently shown tooltip when it is overflowing',
115
+ defaultKey: 'l',
116
+ allowRebind: true,
117
+ },
110
118
  // World
111
119
  {
112
120
  action: 'openWorldMap',
package/dist/mod.d.ts CHANGED
@@ -49,6 +49,7 @@ import type { Translatable, TranslatableString } from './translatable';
49
49
  import type { SaveFileInfo } from './electron';
50
50
  import type { KeybindingDefinition, RegisteredKeybind } from './keybindings';
51
51
  import { ScreenType } from './GameScreen';
52
+ import { CurrentCombatState, CombatLogEntry, RoundStep } from './combat';
52
53
  export type SkipDialogueMode = 'flash' | 'silent';
53
54
  export type Sexuality = 'straight' | 'gay' | 'bisexual';
54
55
  export interface GameSettingsProps {
@@ -72,6 +73,8 @@ export interface GameSettingsProps {
72
73
  setSexuality: (value: Sexuality) => void;
73
74
  eventHistoryLimit: number;
74
75
  setEventHistoryLimit: (value: number) => void;
76
+ tooltipLockTimeMs: number;
77
+ setTooltipLockTimeMs: (value: number) => void;
75
78
  }
76
79
  /**
77
80
  * Where injected content is placed relative to the matched element.
@@ -480,7 +483,6 @@ export interface ModAPI {
480
483
  deadlyFocus: Buff;
481
484
  rippleForce: Buff;
482
485
  transcendentFocus: Buff;
483
- weakness: Buff;
484
486
  goldenAura: Buff;
485
487
  };
486
488
  weapon: {
@@ -1421,7 +1423,7 @@ export interface ModAPI {
1421
1423
  */
1422
1424
  getClothingCharisma: (realm: Realm, mult: number) => number;
1423
1425
  /**
1424
- * Get max qi the player can hold in a realm.
1426
+ * Get qi amount to be used for qi rewards
1425
1427
  * @param realm - Target realm
1426
1428
  * @param realmProgress - Progress level
1427
1429
  * @returns Qi amount
@@ -1438,6 +1440,15 @@ export interface ModAPI {
1438
1440
  * const qiCost = getBreakthroughQi('coreFormation', 'Late');
1439
1441
  */
1440
1442
  getBreakthroughQi: (realm: Realm, realmProgress: RealmProgress) => number;
1443
+ /**
1444
+ * Get maximum qi the player can hold
1445
+ * @param realm - Player realm
1446
+ * @param realmProgress - Progress level
1447
+ * @returns Max qi pool
1448
+ * @example
1449
+ * const maxQi = getMaxQi('coreFormation', 'Late');
1450
+ */
1451
+ getMaxQi: (realm: Realm, realmProgress: RealmProgress) => number;
1441
1452
  /**
1442
1453
  * Scale a numeric reward amount to a value appropriate for the player's realm and progress.
1443
1454
  * @param base - Base reward amount
@@ -2114,7 +2125,7 @@ export interface ModAPI {
2114
2125
  * return combatEntity;
2115
2126
  * });
2116
2127
  */
2117
- onCreatePlayerCombatEntity: (interceptor: (player: PlayerEntity, combatEntity: CombatEntity, breakthrough: BreakthroughState, gameFlags: Record<string, number>) => CombatEntity) => void;
2128
+ onCreatePlayerCombatEntity: (interceptor: (player: PlayerEntity, combatEntity: CombatEntity, breakthrough: BreakthroughState, gameFlags: Record<string, number>) => CombatEntity) => () => void;
2118
2129
  /**
2119
2130
  * Hook into player crafting entity creation to modify stats.
2120
2131
  * @param interceptor - Function to modify crafting entity
@@ -2126,7 +2137,7 @@ export interface ModAPI {
2126
2137
  * return craftingEntity;
2127
2138
  * });
2128
2139
  */
2129
- onCreatePlayerCraftingEntity: (interceptor: (player: PlayerEntity, craftingEntity: CraftingEntity, breakthrough: BreakthroughState, characters: CharactersState | undefined, gameFlags: Record<string, number>) => CraftingEntity) => void;
2140
+ onCreatePlayerCraftingEntity: (interceptor: (player: PlayerEntity, craftingEntity: CraftingEntity, breakthrough: BreakthroughState, characters: CharactersState | undefined, gameFlags: Record<string, number>) => CraftingEntity) => () => void;
2130
2141
  /**
2131
2142
  * Hook called before crafting begins to modify recipe, recipe stats, or player.
2132
2143
  * @param interceptor - Function returning modified recipe/stats/player or undefined
@@ -2142,7 +2153,7 @@ export interface ModAPI {
2142
2153
  recipe?: RecipeItem;
2143
2154
  recipeStats?: CraftingRecipeStats;
2144
2155
  player?: CraftingEntity;
2145
- } | undefined) => void;
2156
+ } | undefined) => () => void;
2146
2157
  /**
2147
2158
  * Hook into enemy combat entity creation to modify stats.
2148
2159
  * @param interceptor - Function to modify combat entity
@@ -2155,7 +2166,7 @@ export interface ModAPI {
2155
2166
  * return combatEntity;
2156
2167
  * });
2157
2168
  */
2158
- onCreateEnemyCombatEntity: (interceptor: (enemy: EnemyEntity, combatEntity: CombatEntity, gameFlags: Record<string, number>) => CombatEntity) => void;
2169
+ onCreateEnemyCombatEntity: (interceptor: (enemy: EnemyEntity, combatEntity: CombatEntity, gameFlags: Record<string, number>) => CombatEntity) => () => void;
2159
2170
  /**
2160
2171
  * Hook to modify recipe difficulty calculations.
2161
2172
  * @param interceptor - Function to adjust recipe stats
@@ -2167,7 +2178,7 @@ export interface ModAPI {
2167
2178
  * return stats;
2168
2179
  * });
2169
2180
  */
2170
- onDeriveRecipeDifficulty: (interceptor: (recipe: RecipeItem, recipeStats: CraftingRecipeStats, gameFlags: Record<string, number>) => CraftingRecipeStats) => void;
2181
+ onDeriveRecipeDifficulty: (interceptor: (recipe: RecipeItem, recipeStats: CraftingRecipeStats, gameFlags: Record<string, number>) => CraftingRecipeStats) => () => void;
2171
2182
  /**
2172
2183
  * Hook to modify recipe ingredients before crafting calculations.
2173
2184
  * This runs before onDeriveRecipeDifficulty and allows mods to change
@@ -2187,7 +2198,7 @@ export interface ModAPI {
2187
2198
  * return recipe;
2188
2199
  * });
2189
2200
  */
2190
- onModifyRecipeIngredients: (interceptor: (recipe: RecipeItem, gameFlags: Record<string, number>) => RecipeItem) => void;
2201
+ onModifyRecipeIngredients: (interceptor: (recipe: RecipeItem, gameFlags: Record<string, number>) => RecipeItem) => () => void;
2191
2202
  /**
2192
2203
  * Hook to add events after combat completion.
2193
2204
  * @param interceptor - Function returning additional event steps
@@ -2199,7 +2210,7 @@ export interface ModAPI {
2199
2210
  * return [];
2200
2211
  * });
2201
2212
  */
2202
- onCompleteCombat: (interceptor: (eventStep: CombatStep | FightCharacterStep, victory: boolean, playerCombatState: CombatEntity, foughtEnemies: EnemyEntity[], droppedItems: Item[], gameFlags: Record<string, number>) => EventStep[]) => void;
2213
+ onCompleteCombat: (interceptor: (eventStep: CombatStep | FightCharacterStep, victory: boolean, playerCombatState: CombatEntity, foughtEnemies: EnemyEntity[], droppedItems: Item[], gameFlags: Record<string, number>) => EventStep[]) => () => void;
2203
2214
  /**
2204
2215
  * Hook to add events after tournament completion.
2205
2216
  * @param interceptor - Function returning additional event steps
@@ -2211,7 +2222,7 @@ export interface ModAPI {
2211
2222
  * return [];
2212
2223
  * });
2213
2224
  */
2214
- onCompleteTournament: (interceptor: (eventStep: TournamentStep, tournamentState: 'victory' | 'second' | 'defeat', gameFlags: Record<string, number>) => EventStep[]) => void;
2225
+ onCompleteTournament: (interceptor: (eventStep: TournamentStep, tournamentState: 'victory' | 'second' | 'defeat', gameFlags: Record<string, number>) => EventStep[]) => () => void;
2215
2226
  /**
2216
2227
  * Hook to add events after dual cultivation.
2217
2228
  * @param interceptor - Function returning additional event steps
@@ -2223,7 +2234,7 @@ export interface ModAPI {
2223
2234
  * return [];
2224
2235
  * });
2225
2236
  */
2226
- onCompleteDualCultivation: (interceptor: (eventStep: DualCultivationStep, success: boolean, gameFlags: Record<string, number>) => EventStep[]) => void;
2237
+ onCompleteDualCultivation: (interceptor: (eventStep: DualCultivationStep, success: boolean, gameFlags: Record<string, number>) => EventStep[]) => () => void;
2227
2238
  /**
2228
2239
  * Hook to add events after crafting completion.
2229
2240
  * @param interceptor - Function returning additional event steps
@@ -2235,7 +2246,7 @@ export interface ModAPI {
2235
2246
  * return [];
2236
2247
  * });
2237
2248
  */
2238
- onCompleteCrafting: (interceptor: (eventStep: CraftingStep, item: CraftingResult | undefined, gameFlags: Record<string, number>) => EventStep[]) => void;
2249
+ onCompleteCrafting: (interceptor: (eventStep: CraftingStep, item: CraftingResult | undefined, gameFlags: Record<string, number>) => EventStep[]) => () => void;
2239
2250
  /**
2240
2251
  * Hook to add events after auction completion.
2241
2252
  * @param interceptor - Function returning additional event steps
@@ -2247,12 +2258,12 @@ export interface ModAPI {
2247
2258
  * return [];
2248
2259
  * });
2249
2260
  */
2250
- onCompleteAuction: (interceptor: (eventStep: AuctionStep, itemsBought: AuctionItem[], gameFlags: Record<string, number>) => EventStep[]) => void;
2261
+ onCompleteAuction: (interceptor: (eventStep: AuctionStep, itemsBought: AuctionItem[], gameFlags: Record<string, number>) => EventStep[]) => () => void;
2251
2262
  /**
2252
2263
  * Hook to add events after stone cutting.
2253
2264
  * @param interceptor - Function returning additional event steps
2254
2265
  */
2255
- onCompleteStoneCutting: (interceptor: (eventStep: StoneCuttingStep, gameFlags: Record<string, number>) => EventStep[]) => void;
2266
+ onCompleteStoneCutting: (interceptor: (eventStep: StoneCuttingStep, gameFlags: Record<string, number>) => EventStep[]) => () => void;
2256
2267
  /**
2257
2268
  * Hook to intercept and modify items granted by event steps (addItem, addMultipleItem, dropItem).
2258
2269
  * Return a modified ItemDesc to change the item name or stack count. Return stacks <= 0 to suppress the item entirely.
@@ -2265,7 +2276,7 @@ export interface ModAPI {
2265
2276
  * return item;
2266
2277
  * });
2267
2278
  */
2268
- onEventDropItem: (interceptor: (item: ItemDesc, step: AddItemStep | AddMultipleItemStep | DropItemStep, gameFlags: Record<string, number>) => ItemDesc) => void;
2279
+ onEventDropItem: (interceptor: (item: ItemDesc, step: AddItemStep | AddMultipleItemStep | DropItemStep, gameFlags: Record<string, number>) => ItemDesc) => () => void;
2269
2280
  /**
2270
2281
  * Hook to modify the exploration event pool before one is selected.
2271
2282
  * Mods can add, remove, or reorder events. Fired after base-game eligibility
@@ -2279,7 +2290,7 @@ export interface ModAPI {
2279
2290
  * return events;
2280
2291
  * });
2281
2292
  */
2282
- onGenerateExploreEvents: (interceptor: (locationId: string, events: LocationEvent[], gameFlags: Record<string, number>) => LocationEvent[]) => void;
2293
+ onGenerateExploreEvents: (interceptor: (locationId: string, events: LocationEvent[], gameFlags: Record<string, number>) => LocationEvent[]) => () => void;
2283
2294
  /**
2284
2295
  * Hook to intercept and modify combat damage after all base reductions.
2285
2296
  * Called for every damage application. Return the new damage value.
@@ -2292,7 +2303,7 @@ export interface ModAPI {
2292
2303
  * return damage;
2293
2304
  * });
2294
2305
  */
2295
- onCalculateDamage: (interceptor: (attacker: CombatEntity, defender: CombatEntity, damage: number, damageType: DamageType | undefined, gameFlags: Record<string, number>) => number) => void;
2306
+ onCalculateDamage: (interceptor: (attacker: CombatEntity, defender: CombatEntity, damage: number, damageType: DamageType | undefined, gameFlags: Record<string, number>) => number) => () => void;
2296
2307
  /**
2297
2308
  * Hook fired when the player enters a new location.
2298
2309
  * @param interceptor - Receives the location id and current flags.
@@ -2301,7 +2312,7 @@ export interface ModAPI {
2301
2312
  * console.log('Player entered', locationId);
2302
2313
  * });
2303
2314
  */
2304
- onLocationEnter: (interceptor: (locationId: string, gameFlags: Record<string, number>) => void) => void;
2315
+ onLocationEnter: (interceptor: (locationId: string, gameFlags: Record<string, number>) => void) => () => void;
2305
2316
  /**
2306
2317
  * Hook fired when combat loot is distributed to the player after a fight.
2307
2318
  * @param interceptor - Receives the list of items dropped and current flags.
@@ -2310,7 +2321,7 @@ export interface ModAPI {
2310
2321
  * items.forEach(item => console.log('Got:', item.name));
2311
2322
  * });
2312
2323
  */
2313
- onLootDrop: (interceptor: (items: Item[], gameFlags: Record<string, number>) => void) => void;
2324
+ onLootDrop: (interceptor: (items: Item[], gameFlags: Record<string, number>) => void) => () => void;
2314
2325
  /**
2315
2326
  * Hook fired when the game advances time (player rests, travels, etc.).
2316
2327
  * @param interceptor - Receives the number of days advanced and current flags.
@@ -2319,7 +2330,7 @@ export interface ModAPI {
2319
2330
  * console.log('Time passed:', days, 'days');
2320
2331
  * });
2321
2332
  */
2322
- onAdvanceDay: (interceptor: (days: number, gameFlags: Record<string, number>) => void) => void;
2333
+ onAdvanceDay: (interceptor: (days: number, gameFlags: Record<string, number>) => void) => () => void;
2323
2334
  /**
2324
2335
  * Hook fired once for each month rollover that occurs during a day advance.
2325
2336
  * @param interceptor - Receives the new month, year, and current flags.
@@ -2328,7 +2339,7 @@ export interface ModAPI {
2328
2339
  * if (month === 3) modAPI.actions.startEvent(mySpringFestivalEvent);
2329
2340
  * });
2330
2341
  */
2331
- onAdvanceMonth: (interceptor: (month: number, year: number, gameFlags: Record<string, number>) => void) => void;
2342
+ onAdvanceMonth: (interceptor: (month: number, year: number, gameFlags: Record<string, number>) => void) => () => void;
2332
2343
  /**
2333
2344
  * Hook fired before combat is initialized. Allows modifying enemies and the player combat entity.
2334
2345
  * Return modified copies of enemies and playerState. Mutations to the originals are not used.
@@ -2345,7 +2356,16 @@ export interface ModAPI {
2345
2356
  onBeforeCombat: (interceptor: (enemies: EnemyEntity[], playerState: CombatEntity, gameFlags: Record<string, number>) => {
2346
2357
  enemies: EnemyEntity[];
2347
2358
  playerState: CombatEntity;
2348
- }) => void;
2359
+ }) => () => void;
2360
+ /**
2361
+ * Hook fired when the player consumes a pill, concoction, or combat consumable during combat.
2362
+ * @param interceptor - Receives the item consumed, the target entity, and current flags.
2363
+ * @example
2364
+ * onConsumeItem((item, target, flags) => {
2365
+ * console.log('Player consumed', item.name, 'targeting', target.name);
2366
+ * });
2367
+ */
2368
+ onConsumeItem: (interceptor: (item: Item, target: CombatEntity, gameFlags: Record<string, number>) => void) => () => void;
2349
2369
  /**
2350
2370
  * Hook fired after every Redux state update. Receives the action type, the
2351
2371
  * state before the action was applied, the state after, and a readonly copy of
@@ -2366,7 +2386,7 @@ export interface ModAPI {
2366
2386
  * return after;
2367
2387
  * });
2368
2388
  */
2369
- onReduxAction: (interceptor: (actionType: string, stateBefore: RootState, stateAfter: RootState, payload: Readonly<unknown>) => RootState) => void;
2389
+ onReduxAction: (interceptor: (actionType: string, stateBefore: RootState, stateAfter: RootState, payload: Readonly<unknown>) => RootState) => () => void;
2370
2390
  /**
2371
2391
  * Hook fired before a Redux action is passed to the reducer. Allows
2372
2392
  * intercepting and modifying the action payload, or dropping the action
@@ -2393,22 +2413,31 @@ export interface ModAPI {
2393
2413
  * return payload;
2394
2414
  * });
2395
2415
  */
2396
- onReduxActionPayload: (interceptor: (actionType: string, payload: unknown, stateBefore: RootState) => unknown | null) => void;
2416
+ onReduxActionPayload: (interceptor: (actionType: string, payload: unknown, stateBefore: RootState) => unknown | null) => () => void;
2397
2417
  /**
2398
2418
  * Called before the equipment upgrade dialog is shown. Allows mutating upgrade cost items and
2399
- * result item quality tier (but not base item).
2419
+ * result item quality tier and hidden potential (but not base item).
2400
2420
  *
2401
2421
  * @example
2402
- * modAPI.hooks.onDeriveEquipmentUpgradeRequirement((baseItem, costItems, resultItemName, resultQualityTier, flags) => {
2403
- * // Override the result quality tier
2404
- * return { costItems, resultItemName, resultQualityTier: resultQualityTier + 2 };
2422
+ * modAPI.hooks.onDeriveEquipmentUpgradeRequirement((baseItem, costItems, resultItem, flags) => {
2423
+ * // Override the result quality tier and preserve hidden potential
2424
+ * return { costItems, resultItem: { ...resultItem, resultQualityTier: resultItem.resultQualityTier + 2 } };
2405
2425
  * });
2406
2426
  */
2407
- onDeriveEquipmentUpgradeRequirement: (interceptor: (baseItem: Item, costItems: Item[], resultItemName: string, resultQualityTier: number, gameFlags: Record<string, number>) => {
2427
+ onDeriveEquipmentUpgradeRequirement: (interceptor: (baseItem: Item, costItems: Item[], resultItem: {
2428
+ resultItemName: string;
2429
+ resultQualityTier: number;
2430
+ resultHiddenPotential?: number;
2431
+ resultEnchantment?: EnchantmentDesc;
2432
+ }, gameFlags: Record<string, number>) => {
2408
2433
  costItems?: Item[];
2409
- resultItemName?: string;
2410
- resultQualityTier?: number;
2411
- } | undefined) => void;
2434
+ resultItem?: Partial<{
2435
+ resultItemName: string;
2436
+ resultQualityTier: number;
2437
+ resultHiddenPotential: number;
2438
+ resultEnchantment: EnchantmentDesc;
2439
+ }>;
2440
+ } | undefined) => () => void;
2412
2441
  /**
2413
2442
  * Called when an equipment upgrade completes. Read-only: cannot modify the result item.
2414
2443
  * Use onDeriveEquipmentUpgradeRequirement to change the result before the upgrade.
@@ -2418,22 +2447,31 @@ export interface ModAPI {
2418
2447
  * // React to the upgrade (e.g., trigger a side effect)
2419
2448
  * });
2420
2449
  */
2421
- onCompleteEquipmentUpgrade: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => void) => void;
2450
+ onCompleteEquipmentUpgrade: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => void) => () => void;
2422
2451
  /**
2423
2452
  * Called before the equipment reforge dialog is shown. Allows mutating reforge cost items and
2424
- * result item quality tier (but not base item).
2453
+ * result item quality tier and hidden potential (but not base item).
2425
2454
  *
2426
2455
  * @example
2427
- * modAPI.hooks.onDeriveEquipmentReforgeRequirement((baseItem, costItems, resultItemName, resultQualityTier, flags) => {
2428
- * // Override the result quality tier (e.g., set to max)
2429
- * return { costItems, resultItemName, resultQualityTier: 10 };
2456
+ * modAPI.hooks.onDeriveEquipmentReforgeRequirement((baseItem, costItems, resultItem, flags) => {
2457
+ * // Override the result quality tier (e.g., set to max) and preserve hidden potential
2458
+ * return { costItems, resultItem: { ...resultItem, resultQualityTier: 10 } };
2430
2459
  * });
2431
2460
  */
2432
- onDeriveEquipmentReforgeRequirement: (interceptor: (baseItem: Item, costItems: Item[], resultItemName: string, resultQualityTier: number, gameFlags: Record<string, number>) => {
2461
+ onDeriveEquipmentReforgeRequirement: (interceptor: (baseItem: Item, costItems: Item[], resultItem: {
2462
+ resultItemName: string;
2463
+ resultQualityTier: number;
2464
+ resultHiddenPotential?: number;
2465
+ resultEnchantment?: EnchantmentDesc;
2466
+ }, gameFlags: Record<string, number>) => {
2433
2467
  costItems?: Item[];
2434
- resultItemName?: string;
2435
- resultQualityTier?: number;
2436
- } | undefined) => void;
2468
+ resultItem?: Partial<{
2469
+ resultItemName: string;
2470
+ resultQualityTier: number;
2471
+ resultHiddenPotential: number;
2472
+ resultEnchantment: EnchantmentDesc;
2473
+ }>;
2474
+ } | undefined) => () => void;
2437
2475
  /**
2438
2476
  * Called before the completion dialog showing the reforged equipment. Allows mutating reforge cost items and result (but not base item).
2439
2477
  *
@@ -2446,7 +2484,7 @@ export interface ModAPI {
2446
2484
  onCompleteEquipmentReforge: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => {
2447
2485
  costItems?: Item[];
2448
2486
  resultItem?: Item;
2449
- } | undefined) => void;
2487
+ } | undefined) => () => void;
2450
2488
  /**
2451
2489
  * Intercept the full set of changes planned for a new game before they are applied.
2452
2490
  * Return a modified intent to change starting items, techniques, flags, etc.
@@ -2457,7 +2495,42 @@ export interface ModAPI {
2457
2495
  * flags: { ...intent.flags, my_mod_enabled: 1 },
2458
2496
  * }));
2459
2497
  */
2460
- onNewGame: (interceptor: (intent: NewGameIntent) => NewGameIntent) => void;
2498
+ onNewGame: (interceptor: (intent: NewGameIntent) => NewGameIntent) => () => void;
2499
+ /**
2500
+ * Called when a saved game is loaded, allowing mods to mutate the initial state.
2501
+ * The interceptor receives the loaded RootState and returns a modified state.
2502
+ * Use this to adjust game state based on loaded save data.
2503
+ * @param interceptor - Receives the loaded state and returns a modified copy
2504
+ * @example
2505
+ * modAPI.hooks.onGameLoad((state) => ({
2506
+ * ...state,
2507
+ * player: { ...state.player, flags: { ...state.player.flags, my_mod_flag: 1 } }
2508
+ * }));
2509
+ */
2510
+ onGameLoad: (interceptor: (state: RootState) => RootState) => () => void;
2511
+ /**
2512
+ * Hook fired before combat advances a step
2513
+ * Return a modified context to change combat state, a cancel object (with log message) to skip the step, or `null` to continue as normal.
2514
+ */
2515
+ onCombatBeforeStep: (interceptor: (step: RoundStep, ctx: CurrentCombatState) => CurrentCombatState | {
2516
+ cancel: true;
2517
+ logMessage: string;
2518
+ } | null) => () => void;
2519
+ /**
2520
+ * Hook fired after combat advances a step
2521
+ * Return a modified context to change combat state, or `null` to continue as normal.
2522
+ */
2523
+ onCombatAfterStep: (interceptor: (step: RoundStep, ctx: CurrentCombatState) => CurrentCombatState | null) => () => void;
2524
+ /**
2525
+ * Hook fired before combat starts a new round
2526
+ * Return a modified context to change combat state, or `null` to continue as normal.
2527
+ */
2528
+ onCombatRoundStart: (interceptor: (ctx: CurrentCombatState) => CurrentCombatState | null) => () => void;
2529
+ /**
2530
+ * Hook fired after combat ends a round
2531
+ * Return a modified context to change combat state, or `null` to continue as normal.
2532
+ */
2533
+ onCombatRoundEnd: (interceptor: (ctx: CurrentCombatState) => CurrentCombatState | null) => () => void;
2461
2534
  };
2462
2535
  /**
2463
2536
  * Inject UI into a named slot (dialog title or screen name).
@@ -2470,7 +2543,8 @@ export interface ModAPI {
2470
2543
  * '#my-id' find by id
2471
2544
  * '.a .b > .c' find by path (searched within the slot root)
2472
2545
  * someElement a direct HTMLElement reference
2473
- * placement controls where content appears (see InjectPlacement). Defaults to 'after'.
2546
+ * placement controls where content appears (see InjectPlacement).
2547
+ * Defaults to 'appendChild' when selector is '' (add inside the slot), 'after' otherwise (sibling after the target).
2474
2548
  *
2475
2549
  * Slot names for dialogs are the id (e.g. 'combat-victory'). You can find these by inspecting the DOM in dev mode.
2476
2550
  * Slot names for screens match the ScreenType value (e.g. 'combat').
@@ -2509,6 +2583,27 @@ export interface ModAPI {
2509
2583
  * if (snap) console.log('Player realm:', snap.player.player.realm);
2510
2584
  */
2511
2585
  getGameStateSnapshot: () => RootState | null;
2586
+ /**
2587
+ * Optional combat sub-object that is only populated during active combat.
2588
+ * Provides direct access to the current combat state for reading and modification.
2589
+ *
2590
+ * @example
2591
+ * // Check if combat is active
2592
+ * if (modAPI.combat) {
2593
+ * const playerHp = modAPI.combat.getPlayerState()?.hp;
2594
+ * modAPI.combat.setPlayerHp(500);
2595
+ * }
2596
+ */
2597
+ combat?: {
2598
+ /** Returns the current combat state, or undefined if not in combat */
2599
+ getCombatState: () => CurrentCombatState | undefined;
2600
+ /** Sets the current combat state */
2601
+ setCombatState: (state: CurrentCombatState) => void;
2602
+ /** Returns the combat log entries */
2603
+ getCombatLog: () => CombatLogEntry[];
2604
+ /** Returns true if combat is in progress */
2605
+ isInCombat: () => boolean;
2606
+ };
2512
2607
  }
2513
2608
  export type ModHooks = {
2514
2609
  [K in keyof ModAPI['hooks']]: Parameters<ModAPI['hooks'][K]>[0][];
@@ -64,11 +64,6 @@ export interface InventoryState {
64
64
  favour: number;
65
65
  favoritedItems?: string[];
66
66
  }
67
- export interface StanceTracking {
68
- techniques: string[];
69
- name: string;
70
- count: number;
71
- }
72
67
  export interface CombatState {
73
68
  player: PlayerEntity | undefined;
74
69
  playerState: CombatEntity | undefined;
@@ -108,7 +108,6 @@ export interface SoulShardDelveConfig {
108
108
  monsters: RegionMonsters;
109
109
  /** The boss room fought after all vestige rooms are cleared. */
110
110
  boss: DelveRoom;
111
- customDropPool: DelveDropPool;
112
111
  }
113
112
  /** Persistent placed shard entry (survives between sessions). */
114
113
  export interface PersistedPlacedShard {
@@ -132,6 +131,8 @@ export interface SoulShardProgression {
132
131
  rechargeProgress: number;
133
132
  /** How many intensityRewards have already been granted. */
134
133
  claimedIntensityRewards: number;
134
+ /** True once the player has started at least one run on this shard. */
135
+ hasEnteredRun?: boolean;
135
136
  }
136
137
  /** Current run state stored in Redux. */
137
138
  export interface SoulShardDelveRunState {
@@ -145,6 +146,10 @@ export interface SoulShardDelveRunState {
145
146
  curses: string[];
146
147
  rewards: PendingReward[];
147
148
  openedRewards: boolean[];
149
+ /** Node rewards held back until the boss is beaten. */
150
+ nodeRewards: PendingReward[];
151
+ /** True once the threshold progression event has been played this run. */
152
+ thresholdEventPlayed?: boolean;
148
153
  }
149
154
  export interface SoulShardDelveState {
150
155
  /** Which soul shard config is currently selected (screen is open). */
@@ -11,5 +11,5 @@ export function getThresholdQither(n) {
11
11
  * +2% HP and power per 1 qither.
12
12
  */
13
13
  export function getQitherDifficulty(totalQither) {
14
- return 1 + totalQither * 0.02;
14
+ return 1 + totalQither * 0.01;
15
15
  }
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", "overheal", "barrierBleed", "formationPartRecovery", "overcrit", "cultivatorResistance"];
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"];
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,6 +32,8 @@ 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
37
  export declare const uncommonStatTooltips: Partial<{
36
38
  [key in CombatStatistic]: string;
37
39
  }>;
@@ -74,6 +76,7 @@ export interface Scaling {
74
76
  };
75
77
  max?: Scaling;
76
78
  divideByStanceLength?: boolean;
79
+ multiplyByStanceLength?: boolean;
77
80
  upgradeKey?: string;
78
81
  buff?: Buff;
79
82
  increment?: number;
package/dist/stat.js CHANGED
@@ -87,11 +87,13 @@ export const combatStatistics = [
87
87
  'damageBoost',
88
88
  'healingBoost',
89
89
  'barrierBoost',
90
+ 'temphpBoost',
90
91
  'overheal',
91
92
  'barrierBleed',
92
93
  'formationPartRecovery',
93
94
  'overcrit',
94
95
  'cultivatorResistance',
96
+ 'temphp',
95
97
  ];
96
98
  export const baseStatNumber = 10;
97
99
  export const expectedHpPerFlesh = 1000;
@@ -158,11 +160,13 @@ export const combatStatToName = {
158
160
  damageBoost: 'Damage Boost',
159
161
  healingBoost: 'Healing Boost',
160
162
  barrierBoost: 'Barrier Boost',
163
+ temphpBoost: 'Temporary Health Boost',
161
164
  overheal: 'Overheal',
162
165
  barrierBleed: 'Barrier Bleed',
163
166
  formationPartRecovery: 'Formation Part Recovery',
164
167
  overcrit: 'Overcrit',
165
168
  cultivatorResistance: 'Cultivator Resistance',
169
+ temphp: 'Temporary Health',
166
170
  };
167
171
  export const combatStatToDescription = {
168
172
  maxhp: 'The amount of damage you can take before you are unable to continue.',
@@ -177,7 +181,7 @@ export const combatStatToDescription = {
177
181
  protection: '',
178
182
  dr: '',
179
183
  lifesteal: '',
180
- critchance: 'Your chance to get a critical effect on damage, healing, and barrier. Crit chance over 100% converts to bonus crit multiplier at a 1:3 ratio.',
184
+ critchance: 'Your chance to get a critical effect on damage, healing, and barrier. Crit Chance above 100% converts to bonus crit multiplier at a 1:3 ratio.',
181
185
  critmultiplier: '',
182
186
  vulnerability: '',
183
187
  weakness: '',
@@ -217,26 +221,66 @@ export const combatStatToDescription = {
217
221
  damageBoost: '',
218
222
  healingBoost: '',
219
223
  barrierBoost: '',
224
+ temphpBoost: '',
220
225
  overheal: '',
221
226
  barrierBleed: '',
222
227
  formationPartRecovery: '',
223
228
  overcrit: '',
224
229
  cultivatorResistance: '',
230
+ temphp: '',
225
231
  };
232
+ // Combat stats that are displayed as percentages in tooltips and UI.
233
+ export const percentageCombatStats = new Set([
234
+ 'critchance',
235
+ 'critmultiplier',
236
+ 'dr',
237
+ 'lifesteal',
238
+ 'barrierMitigation',
239
+ 'resistance',
240
+ 'itemEffectiveness',
241
+ 'vulnerability',
242
+ 'weakness',
243
+ 'protection',
244
+ 'overheal',
245
+ 'barrierBleed',
246
+ 'overcrit',
247
+ 'fistBoost',
248
+ 'blossomBoost',
249
+ 'weaponBoost',
250
+ 'cloudBoost',
251
+ 'bloodBoost',
252
+ 'celestialBoost',
253
+ 'damageBoost',
254
+ 'healingBoost',
255
+ 'barrierBoost',
256
+ 'temphpBoost',
257
+ 'fistResistance',
258
+ 'blossomResistance',
259
+ 'weaponResistance',
260
+ 'cloudResistance',
261
+ 'bloodResistance',
262
+ 'celestialResistance',
263
+ 'cultivatorResistance',
264
+ ]);
265
+ // Stats whose sources multiply together rather than add.
266
+ // Each source applies as (1 - value/100)^scaleFactor to a running multiplier.
267
+ // The final value is converted back to a percentage: (1 - multiplier) * 100.
268
+ export const multiplicativePercentStats = new Set(['weakness', 'dr']);
226
269
  // Uncommon stats that need auxiliary tooltips when they appear on buffs
227
270
  // These descriptions only show as aux tooltips in buff tooltips, not in the stats dialog
228
271
  export const uncommonStatTooltips = {
229
- damageBoost: '<n>Damage Boost</n> increases all non-true damage dealt by a percentage.',
230
- healingBoost: '<n>Healing Boost</n> increases all healing received by a percentage.',
231
- barrierBoost: '<n>Barrier Boost</n> increases all barrier gained by a percentage.',
272
+ damageBoost: '<n>Damage Boost</n> modifies all non-true damage dealt by a percentage.',
273
+ healingBoost: '<n>Healing Boost</n> modifies all healing received by a percentage.',
274
+ barrierBoost: '<n>Barrier Boost</n> modifies all barrier gained by a percentage.',
275
+ temphpBoost: '<n>Temporary Health Boost</n> modifies all temporary health gained by a percentage.',
232
276
  overheal: '<n>Overheal</n> converts a percentage of healing beyond your maximum health into barrier.',
233
277
  protection: '<n>Protection</n> reduces damage taken to your health with diminishing returns. Does not affect damage taken to your barrier.',
234
- weakness: '<n>Weakness</n> reduces your power.',
235
- vulnerability: '<n>Vulnerability</n> increases damage taken to your <n>health</n>. Does not affect damage taken to your barrier.',
278
+ weakness: '<n>Weakness</n> reduces your power. Multiple sources multiply together.',
279
+ vulnerability: '<n>Vulnerability</n> increases damage taken to your <n>health</n>. Does not affect damage taken to your barrier. Multiple sources add together.',
236
280
  barrierBleed: '<n>Barrier Bleed</n> causes a percentage of any damage to bypass your barrier and strike your health directly.',
237
281
  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.',
238
- overcrit: 'Each point of <n>Overcrit</n> allows your excess <n>Crit Chance</n> (above 100%) to roll an additional critical hit, instead of converting to bonus crit damage.',
239
- dr: '<n>Damage Resistance</n> reduces damage taken to your health after defense is applied. Each point equals <num>1%</num> reduction. Does not affect damage taken to your barrier.',
282
+ 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
+ dr: '<n>Damage Resistance</n> reduces damage taken to your health. Multiple sources multiply together. Does not affect damage taken to your barrier.',
240
284
  };
241
285
  export const craftingStatToName = {
242
286
  maxpool: 'Max Qi Pool',
@@ -4,7 +4,7 @@ import type { DamageType } from './DamageType';
4
4
  import type { TechniqueElement } from './element';
5
5
  import type { Rarity } from './rarity';
6
6
  import type { Realm } from './realm';
7
- import type { PhysicalStatistic, SocialStatistic, Scaling } from './stat';
7
+ import type { PhysicalStatistic, SocialStatistic, CombatStatistic, Scaling } from './stat';
8
8
  export declare const techniquePriorities: readonly ["", "Support", "Defensive", "Utility", "Aggressive", "Offensive"];
9
9
  export type TechniquePriority = (typeof techniquePriorities)[number];
10
10
  export interface KnownTechniqueMastery {
@@ -62,14 +62,17 @@ export interface Technique {
62
62
  enhancement?: number;
63
63
  i?: number;
64
64
  }
65
- export type TechniqueEffectKind = 'buffSelf' | 'consumeSelf' | 'buffTarget' | 'consumeTarget' | 'damage' | 'damageSelf' | 'barrier' | 'heal' | 'convertSelf' | 'mergeSelf' | 'cleanseToxicity' | 'modifyBuffGroup' | 'trigger' | 'repair' | 'permanentStatChange';
66
- export type TechniqueEffect = BuffSelfTechniqueEffect | ConsumeSelfTechniqueEffect | BuffTargetTechniqueEffect | ConsumeTargetTechniqueEffect | DamageTechniqueEffect | BarrierTechniqueEffect | HealTechniqueEffect | DamageSelfTechniqueEffect | ConvertSelfTechniqueEffect | MergeSelfTechniqueEffect | CleanseToxicityTechniqueEffect | ModifyBuffGroupEffect | TriggerEffect | RepairTechniqueEffect | PermanentStatChangeTechniqueEffect;
65
+ export type TechniqueEffectKind = 'buffSelf' | 'consumeSelf' | 'buffTarget' | 'consumeTarget' | 'damage' | 'damageSelf' | 'barrier' | 'heal' | 'temporaryHealth' | 'convertSelf' | 'mergeSelf' | 'cleanseToxicity' | 'modifyBuffGroup' | 'trigger' | 'repair' | 'permanentStatChange';
66
+ export type TechniqueEffect = BuffSelfTechniqueEffect | ConsumeSelfTechniqueEffect | BuffTargetTechniqueEffect | ConsumeTargetTechniqueEffect | DamageTechniqueEffect | BarrierTechniqueEffect | HealTechniqueEffect | GiveTemporaryHealthTechniqueEffect | DamageSelfTechniqueEffect | ConvertSelfTechniqueEffect | MergeSelfTechniqueEffect | CleanseToxicityTechniqueEffect | ModifyBuffGroupEffect | TriggerEffect | RepairTechniqueEffect | PermanentStatChangeTechniqueEffect;
67
67
  interface BaseTechniqueEffect {
68
68
  kind: TechniqueEffectKind;
69
69
  condition?: TechniqueCondition;
70
70
  triggerKey?: string;
71
71
  isAdditionalTooltip?: boolean;
72
72
  cantUpgrade?: boolean;
73
+ statChanges?: Partial<{
74
+ [key in CombatStatistic]: Scaling;
75
+ }>;
73
76
  }
74
77
  export type EffectTargeting = 'self' | 'party' | 'all';
75
78
  interface BuffSelfTechniqueEffect extends BaseTechniqueEffect {
@@ -135,6 +138,12 @@ interface HealTechniqueEffect extends BaseTechniqueEffect {
135
138
  hits?: Scaling;
136
139
  targeting?: EffectTargeting;
137
140
  }
141
+ interface GiveTemporaryHealthTechniqueEffect extends BaseTechniqueEffect {
142
+ kind: 'temporaryHealth';
143
+ amount: Scaling;
144
+ hits?: Scaling;
145
+ targeting?: EffectTargeting;
146
+ }
138
147
  interface CleanseToxicityTechniqueEffect extends BaseTechniqueEffect {
139
148
  kind: 'cleanseToxicity';
140
149
  amount: Scaling;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "afnm-types",
3
- "version": "0.6.55-injecttest2",
3
+ "version": "0.6.56",
4
4
  "description": "Type definitions for Ascend From Nine Mountains",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",