refactor: split bloated state types into State + Actions interfaces (issue #102)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
- CombatState: split into CombatState (data) + CombatActions + CombatStore - PrestigeState: split into PrestigeState (data) + PrestigeActions + PrestigeStore - ManaState: split into ManaState (data) + ManaActions + ManaStore - GameState: deprecated, removed from barrel exports - crafting-actions: updated to use CraftingState instead of GameState - combat-utils/mana-utils: replaced Pick<GameState,...> with focused interfaces - DisciplineCardProps: split into Definition + Runtime + Callbacks - stores/index.ts: now exports both State and Actions types
This commit is contained in:
@@ -1,13 +1,19 @@
|
||||
// ─── Enchantment Application Actions ────────────────────────────────────────
|
||||
|
||||
import type { GameState } from '../types';
|
||||
import type { CraftingState } from '../stores/craftingStore.types';
|
||||
import type { GameAction } from '../types';
|
||||
import * as CraftingApply from '../crafting-apply';
|
||||
|
||||
/**
|
||||
* Start applying an enchantment design to an equipment instance.
|
||||
* Note: currentAction must be passed from the combat store.
|
||||
*/
|
||||
export function startApplying(
|
||||
equipmentInstanceId: string,
|
||||
designId: string,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (partial: Partial<CraftingState>) => void,
|
||||
currentAction: GameAction,
|
||||
): boolean {
|
||||
const state = get();
|
||||
const instance = state.equipmentInstances[equipmentInstanceId];
|
||||
@@ -16,57 +22,53 @@ export function startApplying(
|
||||
const validation = CraftingApply.canApplyEnchantment(
|
||||
instance,
|
||||
design,
|
||||
state.currentAction
|
||||
currentAction
|
||||
);
|
||||
if (!validation.canApply) return false;
|
||||
|
||||
set(() => ({
|
||||
currentAction: 'enchant' as const,
|
||||
set({
|
||||
applicationProgress: CraftingApply.initializeApplicationProgress(
|
||||
equipmentInstanceId,
|
||||
designId,
|
||||
design!
|
||||
),
|
||||
}));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function pauseApplication(
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (partial: Partial<CraftingState>) => void
|
||||
) {
|
||||
set((state) => {
|
||||
if (!state.applicationProgress) return {};
|
||||
return {
|
||||
applicationProgress: {
|
||||
...state.applicationProgress,
|
||||
paused: true,
|
||||
},
|
||||
};
|
||||
const state = get();
|
||||
if (!state.applicationProgress) return;
|
||||
set({
|
||||
applicationProgress: {
|
||||
...state.applicationProgress,
|
||||
paused: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function resumeApplication(
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (partial: Partial<CraftingState>) => void
|
||||
) {
|
||||
set((state) => {
|
||||
if (!state.applicationProgress) return {};
|
||||
return {
|
||||
applicationProgress: {
|
||||
...state.applicationProgress,
|
||||
paused: false,
|
||||
},
|
||||
};
|
||||
const state = get();
|
||||
if (!state.applicationProgress) return;
|
||||
set({
|
||||
applicationProgress: {
|
||||
...state.applicationProgress,
|
||||
paused: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function cancelApplication(
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
set: (partial: Partial<CraftingState>) => void
|
||||
) {
|
||||
set(() => ({
|
||||
currentAction: 'meditate' as const,
|
||||
set({
|
||||
applicationProgress: null,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// ─── Computed Getters ──────────────────────────────────────────────────────
|
||||
|
||||
import type { GameState } from '../types';
|
||||
import type { CraftingState } from '../stores/craftingStore.types';
|
||||
import { ENCHANTMENT_EFFECTS } from '../data/enchantment-effects';
|
||||
|
||||
export function getEquipmentSpells(get: () => GameState): string[] {
|
||||
export function getEquipmentSpells(get: () => CraftingState): string[] {
|
||||
const state = get();
|
||||
const spells: string[] = [];
|
||||
|
||||
@@ -23,7 +23,7 @@ export function getEquipmentSpells(get: () => GameState): string[] {
|
||||
return [...new Set(spells)];
|
||||
}
|
||||
|
||||
export function getEquipmentEffects(get: () => GameState): Record<string, number> {
|
||||
export function getEquipmentEffects(get: () => CraftingState): Record<string, number> {
|
||||
const state = get();
|
||||
const effects: Record<string, number> = {};
|
||||
|
||||
@@ -47,7 +47,7 @@ export function getEquipmentEffects(get: () => GameState): Record<string, number
|
||||
|
||||
export function getAvailableCapacity(
|
||||
instanceId: string,
|
||||
get: () => GameState
|
||||
get: () => CraftingState
|
||||
): number {
|
||||
const state = get();
|
||||
const instance = state.equipmentInstances[instanceId];
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
// Note: The main implementation is now in craftingStore.ts
|
||||
// These wrappers are kept for backward compatibility but are no longer used directly
|
||||
|
||||
import type { GameState } from '../types';
|
||||
import type { CraftingState } from '../stores/craftingStore.types';
|
||||
import type { GameAction } from '../types';
|
||||
import * as CraftingEquipment from '../crafting-equipment';
|
||||
|
||||
// Wrapper functions kept for backward compatibility
|
||||
// The actual implementation is in craftingStore.ts using CraftingEquipment functions directly
|
||||
|
||||
export function startCraftingEquipment(
|
||||
blueprintId: string,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void,
|
||||
rawMana: number,
|
||||
currentAction: GameAction,
|
||||
): boolean {
|
||||
const state = get();
|
||||
|
||||
@@ -19,8 +19,8 @@ export function startCraftingEquipment(
|
||||
blueprintId,
|
||||
state.lootInventory.blueprints.includes(blueprintId),
|
||||
state.lootInventory.materials,
|
||||
state.rawMana,
|
||||
state.currentAction
|
||||
rawMana,
|
||||
currentAction
|
||||
);
|
||||
|
||||
if (!check.canCraft) return false;
|
||||
@@ -28,16 +28,14 @@ export function startCraftingEquipment(
|
||||
const result = CraftingEquipment.initializeEquipmentCrafting(
|
||||
blueprintId,
|
||||
state.lootInventory.materials,
|
||||
state.rawMana
|
||||
rawMana
|
||||
);
|
||||
|
||||
set((state) => ({
|
||||
set((s) => ({
|
||||
lootInventory: {
|
||||
...state.lootInventory,
|
||||
...s.lootInventory,
|
||||
materials: result.newMaterials,
|
||||
},
|
||||
rawMana: state.rawMana - result.manaCost,
|
||||
currentAction: 'craft' as const,
|
||||
equipmentCraftingProgress: result.progress,
|
||||
}));
|
||||
|
||||
@@ -45,12 +43,12 @@ export function startCraftingEquipment(
|
||||
}
|
||||
|
||||
export function cancelEquipmentCrafting(
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
set((state) => {
|
||||
const progress = state.equipmentCraftingProgress;
|
||||
if (!progress) return { currentAction: 'meditate' as const, equipmentCraftingProgress: null };
|
||||
if (!progress) return { equipmentCraftingProgress: null };
|
||||
|
||||
const cancelResult = CraftingEquipment.cancelEquipmentCrafting(
|
||||
progress.blueprintId,
|
||||
@@ -58,10 +56,8 @@ export function cancelEquipmentCrafting(
|
||||
);
|
||||
|
||||
return {
|
||||
currentAction: 'meditate' as const,
|
||||
equipmentCraftingProgress: null,
|
||||
rawMana: state.rawMana + cancelResult.manaRefund,
|
||||
log: [cancelResult.logMessage, ...state.log.slice(0, 49)],
|
||||
log: [cancelResult.logMessage],
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -69,8 +65,8 @@ export function cancelEquipmentCrafting(
|
||||
export function deleteMaterial(
|
||||
materialId: string,
|
||||
amount: number,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
set((state) => {
|
||||
const newMaterials = { ...state.lootInventory.materials };
|
||||
@@ -88,7 +84,7 @@ export function deleteMaterial(
|
||||
...state.lootInventory,
|
||||
materials: newMaterials,
|
||||
},
|
||||
log: [`🗑️ Deleted ${amount}x ${materialId}.`, ...state.log.slice(0, 49)],
|
||||
log: [`🗑️ Deleted ${amount}x ${materialId}.`],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
// ─── Enchantment Design Actions ────────────────────────────────────────────
|
||||
|
||||
import type { GameState, EnchantmentDesign, DesignEffect, DesignProgress } from '../types';
|
||||
import type { CraftingState } from '../stores/craftingStore.types';
|
||||
import type { EnchantmentDesign, DesignEffect } from '../types';
|
||||
import * as CraftingUtils from '../crafting-utils';
|
||||
import * as CraftingDesign from '../crafting-design';
|
||||
import { computeEffects } from '../effects/upgrade-effects';
|
||||
import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects';
|
||||
|
||||
export interface DesignActionsParams {
|
||||
skills: Record<string, number>;
|
||||
skillUpgrades: Record<string, string[]>;
|
||||
skillTiers: Record<string, number>;
|
||||
}
|
||||
|
||||
export function startDesigningEnchantment(
|
||||
name: string,
|
||||
equipmentTypeId: string,
|
||||
effects: DesignEffect[],
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
params: DesignActionsParams,
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
): boolean {
|
||||
const state = get();
|
||||
const enchantingLevel = state.skills.enchanting || 0;
|
||||
const enchantingLevel = params.skills.enchanting || 0;
|
||||
const validation = CraftingDesign.validateDesignEffects(
|
||||
effects,
|
||||
equipmentTypeId,
|
||||
@@ -25,21 +33,20 @@ export function startDesigningEnchantment(
|
||||
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
|
||||
if (!equipType) return false;
|
||||
|
||||
const efficiencyBonus = ((state.skillUpgrades || {})['efficientEnchant'] || [])?.length * 0.05 || 0;
|
||||
const efficiencyBonus = ((params.skillUpgrades || {})['efficientEnchant'] || [])?.length * 0.05 || 0;
|
||||
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
|
||||
|
||||
if (totalCapacityCost > equipType.baseCapacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const computedEffects = computeEffects(state.skillUpgrades || {}, state.skillTiers || {});
|
||||
const computedEffects = computeEffects(params.skillUpgrades || {}, params.skillTiers || {});
|
||||
const hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY);
|
||||
|
||||
let updates: Partial<GameState> = {};
|
||||
let updates: Partial<CraftingState> = {};
|
||||
|
||||
if (!state.designProgress) {
|
||||
updates = {
|
||||
currentAction: 'design' as const,
|
||||
designProgress: {
|
||||
designId: CraftingUtils.generateDesignId(),
|
||||
progress: 0,
|
||||
@@ -69,15 +76,14 @@ export function startDesigningEnchantment(
|
||||
}
|
||||
|
||||
export function cancelDesign(
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
const state = get();
|
||||
if (state.designProgress2 && !state.designProgress) {
|
||||
set(() => ({ designProgress2: null }));
|
||||
} else {
|
||||
set(() => ({
|
||||
currentAction: 'meditate' as const,
|
||||
designProgress: null,
|
||||
}));
|
||||
}
|
||||
@@ -85,27 +91,26 @@ export function cancelDesign(
|
||||
|
||||
export function saveDesign(
|
||||
design: EnchantmentDesign,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
const state = get();
|
||||
if (state.designProgress2 && state.designProgress2.designId === design.id) {
|
||||
set((state) => ({
|
||||
enchantmentDesigns: [...state.enchantmentDesigns, design],
|
||||
set((s) => ({
|
||||
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
||||
designProgress2: null,
|
||||
}));
|
||||
} else {
|
||||
set((state) => ({
|
||||
enchantmentDesigns: [...state.enchantmentDesigns, design],
|
||||
set((s) => ({
|
||||
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
||||
designProgress: null,
|
||||
currentAction: 'meditate' as const,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteDesign(
|
||||
designId: string,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
set((state) => ({
|
||||
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// ─── Disenchanting Actions ─────────────────────────────────────────────────
|
||||
|
||||
import type { GameState, EquipmentInstance } from '../types';
|
||||
import type { CraftingState } from '../stores/craftingStore.types';
|
||||
|
||||
export function disenchantEquipment(
|
||||
instanceId: string,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
const state = get();
|
||||
const instance = state.equipmentInstances[instanceId];
|
||||
@@ -19,16 +19,15 @@ export function disenchantEquipment(
|
||||
totalRecovered += Math.floor(ench.actualCost * recoveryRate);
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
rawMana: state.rawMana + totalRecovered,
|
||||
set((s) => ({
|
||||
equipmentInstances: {
|
||||
...state.equipmentInstances,
|
||||
...s.equipmentInstances,
|
||||
[instanceId]: {
|
||||
...instance,
|
||||
enchantments: [],
|
||||
usedCapacity: 0,
|
||||
},
|
||||
},
|
||||
log: [`✨ Disenchanted ${instance.name}, recovered ${totalRecovered} mana.`, ...state.log.slice(0, 49)],
|
||||
log: [`✨ Disenchanted ${instance.name}, recovered ${totalRecovered} mana.`],
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// ─── Equipment Management Actions ────────────────────────────────────────────
|
||||
|
||||
import type { GameState, EquipmentInstance, EquipmentSlot } from '../types';
|
||||
import type { CraftingState } from '../stores/craftingStore.types';
|
||||
import type { EquipmentInstance, EquipmentSlot } from '../types';
|
||||
import * as CraftingUtils from '../crafting-utils';
|
||||
|
||||
// Create equipment instance
|
||||
export function createEquipmentInstance(
|
||||
typeId: string,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
): string | null {
|
||||
const type = CraftingUtils.getEquipmentType(typeId);
|
||||
if (!type) return null;
|
||||
@@ -38,8 +39,8 @@ export function createEquipmentInstance(
|
||||
export function equipItem(
|
||||
instanceId: string,
|
||||
slot: EquipmentSlot,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
): boolean {
|
||||
const state = get();
|
||||
const instance = state.equipmentInstances[instanceId];
|
||||
@@ -69,7 +70,7 @@ export function equipItem(
|
||||
// Unequip item
|
||||
export function unequipItem(
|
||||
slot: EquipmentSlot,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
set((state) => ({
|
||||
equippedInstances: {
|
||||
@@ -82,8 +83,8 @@ export function unequipItem(
|
||||
// Delete equipment instance
|
||||
export function deleteEquipmentInstance(
|
||||
instanceId: string,
|
||||
get: () => GameState,
|
||||
set: (fn: (state: GameState) => Partial<GameState>) => void
|
||||
get: () => CraftingState,
|
||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||
) {
|
||||
const state = get();
|
||||
let newEquipped = { ...state.equippedInstances };
|
||||
|
||||
Reference in New Issue
Block a user