afnm-types 0.6.55 → 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;
@@ -28,6 +28,7 @@ export declare const MODIFIER_PREFIXES: {
28
28
  readonly alt: "Alt+";
29
29
  readonly shift: "Shift+";
30
30
  };
31
+ export declare const baseKeyToCode: Record<string, string>;
31
32
  export declare function getBaseKeyFromEvent(event: KeyboardEvent): string;
32
33
  export declare function buildKeybindingString(key: string, modifiers: {
33
34
  ctrl?: boolean;
@@ -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',
@@ -908,6 +916,8 @@ const codeToBaseKey = {
908
916
  F11: 'F11',
909
917
  F12: 'F12',
910
918
  };
919
+ // Build reverse lookup from base key to code for matching KeyboardEvent.code
920
+ export const baseKeyToCode = Object.fromEntries(Object.entries(codeToBaseKey).map(([code, baseKey]) => [baseKey, code]));
911
921
  // Get the base key from a keyboard event (uses code for consistent physical key)
912
922
  export function getBaseKeyFromEvent(event) {
913
923
  // Try to get base key from code first (handles Shift+2 -> '2' instead of '!')
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,8 +73,33 @@ 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
- export type InjectPosition = 'before' | 'after';
79
+ /**
80
+ * Where injected content is placed relative to the matched element.
81
+ *
82
+ * Sibling insertions:
83
+ * 'before' inserted as a sibling immediately before the target
84
+ * 'after' inserted as a sibling immediately after the target (default)
85
+ *
86
+ * Child insertions:
87
+ * 'prependChild' inserted as the first child inside the target
88
+ * 'appendChild' inserted as the last child inside the target
89
+ * { insertAt: N } inserted as the Nth child inside the target (0-based)
90
+ *
91
+ * Overlay:
92
+ * 'overlay' absolutely positioned layer covering the target (target gets position:relative if needed)
93
+ *
94
+ * Structural replacements (use with care on React-rendered elements):
95
+ * 'replaceContents' clears the target's children and renders content inside the target
96
+ * 'replaceElement' removes the target entirely and renders content in its place
97
+ * 'wrapElement' target becomes a DOM child of the content's outermost element (e.g. a border div wraps around target)
98
+ * 'wrapContents' target's existing children are moved inside the content's outermost element, which is then appended to target
99
+ */
100
+ export type InjectPlacement = 'before' | 'after' | 'prependChild' | 'appendChild' | 'overlay' | 'replaceContents' | 'replaceElement' | 'wrapElement' | 'wrapContents' | {
101
+ insertAt: number;
102
+ };
77
103
  export interface NewGameIntent {
78
104
  items: ItemDesc[];
79
105
  techniques: string[];
@@ -457,7 +483,6 @@ export interface ModAPI {
457
483
  deadlyFocus: Buff;
458
484
  rippleForce: Buff;
459
485
  transcendentFocus: Buff;
460
- weakness: Buff;
461
486
  goldenAura: Buff;
462
487
  };
463
488
  weapon: {
@@ -1398,7 +1423,7 @@ export interface ModAPI {
1398
1423
  */
1399
1424
  getClothingCharisma: (realm: Realm, mult: number) => number;
1400
1425
  /**
1401
- * Get max qi the player can hold in a realm.
1426
+ * Get qi amount to be used for qi rewards
1402
1427
  * @param realm - Target realm
1403
1428
  * @param realmProgress - Progress level
1404
1429
  * @returns Qi amount
@@ -1415,6 +1440,15 @@ export interface ModAPI {
1415
1440
  * const qiCost = getBreakthroughQi('coreFormation', 'Late');
1416
1441
  */
1417
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;
1418
1452
  /**
1419
1453
  * Scale a numeric reward amount to a value appropriate for the player's realm and progress.
1420
1454
  * @param base - Base reward amount
@@ -2091,7 +2125,7 @@ export interface ModAPI {
2091
2125
  * return combatEntity;
2092
2126
  * });
2093
2127
  */
2094
- 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;
2095
2129
  /**
2096
2130
  * Hook into player crafting entity creation to modify stats.
2097
2131
  * @param interceptor - Function to modify crafting entity
@@ -2103,7 +2137,7 @@ export interface ModAPI {
2103
2137
  * return craftingEntity;
2104
2138
  * });
2105
2139
  */
2106
- 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;
2107
2141
  /**
2108
2142
  * Hook called before crafting begins to modify recipe, recipe stats, or player.
2109
2143
  * @param interceptor - Function returning modified recipe/stats/player or undefined
@@ -2119,7 +2153,7 @@ export interface ModAPI {
2119
2153
  recipe?: RecipeItem;
2120
2154
  recipeStats?: CraftingRecipeStats;
2121
2155
  player?: CraftingEntity;
2122
- } | undefined) => void;
2156
+ } | undefined) => () => void;
2123
2157
  /**
2124
2158
  * Hook into enemy combat entity creation to modify stats.
2125
2159
  * @param interceptor - Function to modify combat entity
@@ -2132,7 +2166,7 @@ export interface ModAPI {
2132
2166
  * return combatEntity;
2133
2167
  * });
2134
2168
  */
2135
- 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;
2136
2170
  /**
2137
2171
  * Hook to modify recipe difficulty calculations.
2138
2172
  * @param interceptor - Function to adjust recipe stats
@@ -2144,7 +2178,7 @@ export interface ModAPI {
2144
2178
  * return stats;
2145
2179
  * });
2146
2180
  */
2147
- 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;
2148
2182
  /**
2149
2183
  * Hook to modify recipe ingredients before crafting calculations.
2150
2184
  * This runs before onDeriveRecipeDifficulty and allows mods to change
@@ -2164,7 +2198,7 @@ export interface ModAPI {
2164
2198
  * return recipe;
2165
2199
  * });
2166
2200
  */
2167
- onModifyRecipeIngredients: (interceptor: (recipe: RecipeItem, gameFlags: Record<string, number>) => RecipeItem) => void;
2201
+ onModifyRecipeIngredients: (interceptor: (recipe: RecipeItem, gameFlags: Record<string, number>) => RecipeItem) => () => void;
2168
2202
  /**
2169
2203
  * Hook to add events after combat completion.
2170
2204
  * @param interceptor - Function returning additional event steps
@@ -2176,7 +2210,7 @@ export interface ModAPI {
2176
2210
  * return [];
2177
2211
  * });
2178
2212
  */
2179
- 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;
2180
2214
  /**
2181
2215
  * Hook to add events after tournament completion.
2182
2216
  * @param interceptor - Function returning additional event steps
@@ -2188,7 +2222,7 @@ export interface ModAPI {
2188
2222
  * return [];
2189
2223
  * });
2190
2224
  */
2191
- 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;
2192
2226
  /**
2193
2227
  * Hook to add events after dual cultivation.
2194
2228
  * @param interceptor - Function returning additional event steps
@@ -2200,7 +2234,7 @@ export interface ModAPI {
2200
2234
  * return [];
2201
2235
  * });
2202
2236
  */
2203
- 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;
2204
2238
  /**
2205
2239
  * Hook to add events after crafting completion.
2206
2240
  * @param interceptor - Function returning additional event steps
@@ -2212,7 +2246,7 @@ export interface ModAPI {
2212
2246
  * return [];
2213
2247
  * });
2214
2248
  */
2215
- 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;
2216
2250
  /**
2217
2251
  * Hook to add events after auction completion.
2218
2252
  * @param interceptor - Function returning additional event steps
@@ -2224,12 +2258,12 @@ export interface ModAPI {
2224
2258
  * return [];
2225
2259
  * });
2226
2260
  */
2227
- 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;
2228
2262
  /**
2229
2263
  * Hook to add events after stone cutting.
2230
2264
  * @param interceptor - Function returning additional event steps
2231
2265
  */
2232
- onCompleteStoneCutting: (interceptor: (eventStep: StoneCuttingStep, gameFlags: Record<string, number>) => EventStep[]) => void;
2266
+ onCompleteStoneCutting: (interceptor: (eventStep: StoneCuttingStep, gameFlags: Record<string, number>) => EventStep[]) => () => void;
2233
2267
  /**
2234
2268
  * Hook to intercept and modify items granted by event steps (addItem, addMultipleItem, dropItem).
2235
2269
  * Return a modified ItemDesc to change the item name or stack count. Return stacks <= 0 to suppress the item entirely.
@@ -2242,7 +2276,7 @@ export interface ModAPI {
2242
2276
  * return item;
2243
2277
  * });
2244
2278
  */
2245
- 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;
2246
2280
  /**
2247
2281
  * Hook to modify the exploration event pool before one is selected.
2248
2282
  * Mods can add, remove, or reorder events. Fired after base-game eligibility
@@ -2256,7 +2290,7 @@ export interface ModAPI {
2256
2290
  * return events;
2257
2291
  * });
2258
2292
  */
2259
- 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;
2260
2294
  /**
2261
2295
  * Hook to intercept and modify combat damage after all base reductions.
2262
2296
  * Called for every damage application. Return the new damage value.
@@ -2269,7 +2303,7 @@ export interface ModAPI {
2269
2303
  * return damage;
2270
2304
  * });
2271
2305
  */
2272
- 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;
2273
2307
  /**
2274
2308
  * Hook fired when the player enters a new location.
2275
2309
  * @param interceptor - Receives the location id and current flags.
@@ -2278,7 +2312,7 @@ export interface ModAPI {
2278
2312
  * console.log('Player entered', locationId);
2279
2313
  * });
2280
2314
  */
2281
- onLocationEnter: (interceptor: (locationId: string, gameFlags: Record<string, number>) => void) => void;
2315
+ onLocationEnter: (interceptor: (locationId: string, gameFlags: Record<string, number>) => void) => () => void;
2282
2316
  /**
2283
2317
  * Hook fired when combat loot is distributed to the player after a fight.
2284
2318
  * @param interceptor - Receives the list of items dropped and current flags.
@@ -2287,7 +2321,7 @@ export interface ModAPI {
2287
2321
  * items.forEach(item => console.log('Got:', item.name));
2288
2322
  * });
2289
2323
  */
2290
- onLootDrop: (interceptor: (items: Item[], gameFlags: Record<string, number>) => void) => void;
2324
+ onLootDrop: (interceptor: (items: Item[], gameFlags: Record<string, number>) => void) => () => void;
2291
2325
  /**
2292
2326
  * Hook fired when the game advances time (player rests, travels, etc.).
2293
2327
  * @param interceptor - Receives the number of days advanced and current flags.
@@ -2296,7 +2330,7 @@ export interface ModAPI {
2296
2330
  * console.log('Time passed:', days, 'days');
2297
2331
  * });
2298
2332
  */
2299
- onAdvanceDay: (interceptor: (days: number, gameFlags: Record<string, number>) => void) => void;
2333
+ onAdvanceDay: (interceptor: (days: number, gameFlags: Record<string, number>) => void) => () => void;
2300
2334
  /**
2301
2335
  * Hook fired once for each month rollover that occurs during a day advance.
2302
2336
  * @param interceptor - Receives the new month, year, and current flags.
@@ -2305,7 +2339,7 @@ export interface ModAPI {
2305
2339
  * if (month === 3) modAPI.actions.startEvent(mySpringFestivalEvent);
2306
2340
  * });
2307
2341
  */
2308
- 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;
2309
2343
  /**
2310
2344
  * Hook fired before combat is initialized. Allows modifying enemies and the player combat entity.
2311
2345
  * Return modified copies of enemies and playerState. Mutations to the originals are not used.
@@ -2322,7 +2356,16 @@ export interface ModAPI {
2322
2356
  onBeforeCombat: (interceptor: (enemies: EnemyEntity[], playerState: CombatEntity, gameFlags: Record<string, number>) => {
2323
2357
  enemies: EnemyEntity[];
2324
2358
  playerState: CombatEntity;
2325
- }) => 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;
2326
2369
  /**
2327
2370
  * Hook fired after every Redux state update. Receives the action type, the
2328
2371
  * state before the action was applied, the state after, and a readonly copy of
@@ -2343,7 +2386,7 @@ export interface ModAPI {
2343
2386
  * return after;
2344
2387
  * });
2345
2388
  */
2346
- 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;
2347
2390
  /**
2348
2391
  * Hook fired before a Redux action is passed to the reducer. Allows
2349
2392
  * intercepting and modifying the action payload, or dropping the action
@@ -2370,22 +2413,31 @@ export interface ModAPI {
2370
2413
  * return payload;
2371
2414
  * });
2372
2415
  */
2373
- onReduxActionPayload: (interceptor: (actionType: string, payload: unknown, stateBefore: RootState) => unknown | null) => void;
2416
+ onReduxActionPayload: (interceptor: (actionType: string, payload: unknown, stateBefore: RootState) => unknown | null) => () => void;
2374
2417
  /**
2375
2418
  * Called before the equipment upgrade dialog is shown. Allows mutating upgrade cost items and
2376
- * result item quality tier (but not base item).
2419
+ * result item quality tier and hidden potential (but not base item).
2377
2420
  *
2378
2421
  * @example
2379
- * modAPI.hooks.onDeriveEquipmentUpgradeRequirement((baseItem, costItems, resultItemName, resultQualityTier, flags) => {
2380
- * // Override the result quality tier
2381
- * 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 } };
2382
2425
  * });
2383
2426
  */
2384
- 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>) => {
2385
2433
  costItems?: Item[];
2386
- resultItemName?: string;
2387
- resultQualityTier?: number;
2388
- } | undefined) => void;
2434
+ resultItem?: Partial<{
2435
+ resultItemName: string;
2436
+ resultQualityTier: number;
2437
+ resultHiddenPotential: number;
2438
+ resultEnchantment: EnchantmentDesc;
2439
+ }>;
2440
+ } | undefined) => () => void;
2389
2441
  /**
2390
2442
  * Called when an equipment upgrade completes. Read-only: cannot modify the result item.
2391
2443
  * Use onDeriveEquipmentUpgradeRequirement to change the result before the upgrade.
@@ -2395,22 +2447,31 @@ export interface ModAPI {
2395
2447
  * // React to the upgrade (e.g., trigger a side effect)
2396
2448
  * });
2397
2449
  */
2398
- 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;
2399
2451
  /**
2400
2452
  * Called before the equipment reforge dialog is shown. Allows mutating reforge cost items and
2401
- * result item quality tier (but not base item).
2453
+ * result item quality tier and hidden potential (but not base item).
2402
2454
  *
2403
2455
  * @example
2404
- * modAPI.hooks.onDeriveEquipmentReforgeRequirement((baseItem, costItems, resultItemName, resultQualityTier, flags) => {
2405
- * // Override the result quality tier (e.g., set to max)
2406
- * 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 } };
2407
2459
  * });
2408
2460
  */
2409
- 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>) => {
2410
2467
  costItems?: Item[];
2411
- resultItemName?: string;
2412
- resultQualityTier?: number;
2413
- } | undefined) => void;
2468
+ resultItem?: Partial<{
2469
+ resultItemName: string;
2470
+ resultQualityTier: number;
2471
+ resultHiddenPotential: number;
2472
+ resultEnchantment: EnchantmentDesc;
2473
+ }>;
2474
+ } | undefined) => () => void;
2414
2475
  /**
2415
2476
  * Called before the completion dialog showing the reforged equipment. Allows mutating reforge cost items and result (but not base item).
2416
2477
  *
@@ -2423,7 +2484,7 @@ export interface ModAPI {
2423
2484
  onCompleteEquipmentReforge: (interceptor: (baseItem: Item, costItems: Item[], resultItem: Item, gameFlags: Record<string, number>) => {
2424
2485
  costItems?: Item[];
2425
2486
  resultItem?: Item;
2426
- } | undefined) => void;
2487
+ } | undefined) => () => void;
2427
2488
  /**
2428
2489
  * Intercept the full set of changes planned for a new game before they are applied.
2429
2490
  * Return a modified intent to change starting items, techniques, flags, etc.
@@ -2434,35 +2495,71 @@ export interface ModAPI {
2434
2495
  * flags: { ...intent.flags, my_mod_enabled: 1 },
2435
2496
  * }));
2436
2497
  */
2437
- 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;
2438
2534
  };
2439
2535
  /**
2440
2536
  * Inject UI into a named slot (dialog title or screen name).
2441
2537
  *
2442
- * The generator receives (api, element, inject):
2538
+ * The generator receives (api, inject):
2443
2539
  * - api: full ModReduxAPI with game state, actions, and components
2444
- * - element: root DOM element of the slot; use querySelector to find children
2445
- * - inject(selector, content, mode?, position?): helper to portal content into the element
2446
- * matched by selector. mode is 'overlay' (default, floats over) or 'inline'
2447
- * (inserts as a sibling after the target, flowing with the layout).
2448
- * position controls where content is placed (see InjectPosition enum).
2540
+ * - inject(selector, content, placement?): places content relative to the matched element.
2541
+ * selector can be:
2542
+ * '' the slot root itself
2543
+ * '#my-id' find by id
2544
+ * '.a .b > .c' find by path (searched within the slot root)
2545
+ * someElement a direct HTMLElement reference
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).
2449
2548
  *
2450
2549
  * Slot names for dialogs are the id (e.g. 'combat-victory'). You can find these by inspecting the DOM in dev mode.
2451
2550
  * Slot names for screens match the ScreenType value (e.g. 'combat').
2452
2551
  *
2453
2552
  * @example
2454
- * // Add a button to combat victory dialog
2455
- * modAPI.ui.injectUI('combat-victory', (api, element, inject) => {
2456
- * return inject('[aria-live="assertive"]',
2553
+ * modAPI.ui.injectUI('combat-victory', (api, inject) => {
2554
+ * inject('[aria-live="assertive"]',
2457
2555
  * <api.components.GameButton onClick={() => api.actions.changeQi(100)}>
2458
2556
  * Absorb Qi
2459
2557
  * </api.components.GameButton>,
2460
- * 'inline',
2461
- * InjectPosition.After
2558
+ * 'after'
2462
2559
  * );
2463
2560
  * });
2464
2561
  */
2465
- injectUI: (slotName: string, generator: (api: ModReduxAPI, element: HTMLElement | null, inject: (selector: string, content: React.ReactNode, mode?: 'overlay' | 'inline', position?: InjectPosition) => React.ReactNode) => React.ReactNode) => void;
2562
+ injectUI: (slotName: string, generator: (api: ModReduxAPI, inject: (selector: string | HTMLElement, content: React.ReactNode, placement?: InjectPlacement) => void) => void) => void;
2466
2563
  /**
2467
2564
  * Subscribe to any Redux state changes. The callback receives the new state.
2468
2565
  * Returns an unsubscribe function.
@@ -2486,6 +2583,27 @@ export interface ModAPI {
2486
2583
  * if (snap) console.log('Player realm:', snap.player.player.realm);
2487
2584
  */
2488
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
+ };
2489
2607
  }
2490
2608
  export type ModHooks = {
2491
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",
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",