Refactor large files into modular components
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m9s

- Refactored page.tsx (613→252 lines) with GameOverScreen and LeftPanel extracted
- Refactored StatsTab.tsx (584→92 lines) with section components
- Refactored SkillsTab.tsx (434→54 lines) with sub-components
- Created modular structure for GameContext, LootInventory, and other components
- All extracted components organized into feature directories
This commit is contained in:
Refactoring Agent
2026-05-02 17:35:03 +02:00
parent c9ae2576f4
commit d2d28887b1
194 changed files with 16862 additions and 15729 deletions
@@ -0,0 +1,72 @@
// ─── Enchantment Application Actions ────────────────────────────────────────
import type { GameState } from '../types';
import * as CraftingApply from '../crafting-apply';
export function startApplying(
equipmentInstanceId: string,
designId: string,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
): boolean {
const state = get();
const instance = state.equipmentInstances[equipmentInstanceId];
const design = state.enchantmentDesigns.find(d => d.id === designId);
const validation = CraftingApply.canApplyEnchantment(
instance,
design,
state.currentAction
);
if (!validation.canApply) return false;
set(() => ({
currentAction: 'enchant' as const,
applicationProgress: CraftingApply.initializeApplicationProgress(
equipmentInstanceId,
designId,
design!
),
}));
return true;
}
export function pauseApplication(
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set((state) => {
if (!state.applicationProgress) return {};
return {
applicationProgress: {
...state.applicationProgress,
paused: true,
},
};
});
}
export function resumeApplication(
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set((state) => {
if (!state.applicationProgress) return {};
return {
applicationProgress: {
...state.applicationProgress,
paused: false,
},
};
});
}
export function cancelApplication(
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set(() => ({
currentAction: 'meditate' as const,
applicationProgress: null,
}));
}
@@ -0,0 +1,56 @@
// ─── Computed Getters ──────────────────────────────────────────────────────
import type { GameState } from '../types';
import { ENCHANTMENT_EFFECTS } from '../data/enchantment-effects';
export function getEquipmentSpells(get: () => GameState): string[] {
const state = get();
const spells: string[] = [];
for (const instanceId of Object.values(state.equippedInstances)) {
if (!instanceId) continue;
const instance = state.equipmentInstances[instanceId];
if (!instance) continue;
for (const ench of instance.enchantments) {
const effectDef = ENCHANTMENT_EFFECTS[ench.effectId];
if (effectDef?.effect.type === 'spell' && effectDef.effect.spellId) {
spells.push(effectDef.effect.spellId);
}
}
}
return [...new Set(spells)];
}
export function getEquipmentEffects(get: () => GameState): Record<string, number> {
const state = get();
const effects: Record<string, number> = {};
for (const instanceId of Object.values(state.equippedInstances)) {
if (!instanceId) continue;
const instance = state.equipmentInstances[instanceId];
if (!instance) continue;
for (const ench of instance.enchantments) {
const effectDef = ENCHANTMENT_EFFECTS[ench.effectId];
if (!effectDef) continue;
if (effectDef.effect.type === 'bonus' && effectDef.effect.stat && effectDef.effect.value) {
effects[effectDef.effect.stat] = (effects[effectDef.effect.stat] || 0) + effectDef.effect.value * ench.stacks;
}
}
}
return effects;
}
export function getAvailableCapacity(
instanceId: string,
get: () => GameState
): number {
const state = get();
const instance = state.equipmentInstances[instanceId];
if (!instance) return 0;
return instance.totalCapacity - instance.usedCapacity;
}
@@ -0,0 +1,89 @@
// ─── Equipment Crafting Actions ────────────────────────────────────────────
import type { GameState } from '../types';
import * as CraftingEquipment from '../crafting-equipment';
export function startCraftingEquipment(
blueprintId: string,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
): boolean {
const state = get();
const check = CraftingEquipment.canStartEquipmentCrafting(
blueprintId,
state.lootInventory.blueprints.includes(blueprintId),
state.lootInventory.materials,
state.rawMana,
state.currentAction
);
if (!check.canCraft) return false;
const result = CraftingEquipment.initializeEquipmentCrafting(
blueprintId,
state.lootInventory.materials,
state.rawMana
);
set((state) => ({
lootInventory: {
...state.lootInventory,
materials: result.newMaterials,
},
rawMana: state.rawMana - result.manaCost,
currentAction: 'craft' as const,
equipmentCraftingProgress: result.progress,
}));
return true;
}
export function cancelEquipmentCrafting(
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set((state) => {
const progress = state.equipmentCraftingProgress;
if (!progress) return { currentAction: 'meditate' as const, equipmentCraftingProgress: null };
const cancelResult = CraftingEquipment.cancelEquipmentCrafting(
progress.blueprintId,
progress.manaSpent
);
return {
currentAction: 'meditate' as const,
equipmentCraftingProgress: null,
rawMana: state.rawMana + cancelResult.manaRefund,
log: [cancelResult.logMessage, ...state.log.slice(0, 49)],
};
});
}
export function deleteMaterial(
materialId: string,
amount: number,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set((state) => {
const newMaterials = { ...state.lootInventory.materials };
const currentAmount = newMaterials[materialId] || 0;
const newAmount = Math.max(0, currentAmount - amount);
if (newAmount <= 0) {
delete newMaterials[materialId];
} else {
newMaterials[materialId] = newAmount;
}
return {
lootInventory: {
...state.lootInventory,
materials: newMaterials,
},
log: [`🗑️ Deleted ${amount}x ${materialId}.`, ...state.log.slice(0, 49)],
};
});
}
@@ -0,0 +1,113 @@
// ─── Enchantment Design Actions ────────────────────────────────────────────
import type { GameState, EnchantmentDesign, DesignEffect } from '../types';
import * as CraftingUtils from '../crafting-utils';
import * as CraftingDesign from '../crafting-design';
import { computeEffects } from '../upgrade-effects';
import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects';
export function startDesigningEnchantment(
name: string,
equipmentTypeId: string,
effects: DesignEffect[],
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
): boolean {
const state = get();
const enchantingLevel = state.skills.enchanting || 0;
const validation = CraftingDesign.validateDesignEffects(
effects,
equipmentTypeId,
enchantingLevel
);
if (!validation.valid) return false;
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
if (!equipType) return false;
const efficiencyBonus = ((state.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 hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY);
let updates: any = {};
if (!state.designProgress) {
updates = {
currentAction: 'design' as const,
designProgress: {
designId: CraftingUtils.generateDesignId(),
progress: 0,
required: CraftingDesign.calculateDesignTime(effects),
name,
equipmentType: equipmentTypeId,
effects,
},
};
} else if (hasEnchantMastery && !state.designProgress2) {
updates = {
designProgress2: {
designId: CraftingUtils.generateDesignId(),
progress: 0,
required: CraftingDesign.calculateDesignTime(effects),
name,
equipmentType: equipmentTypeId,
effects,
},
};
} else {
return false;
}
set(() => updates);
return true;
}
export function cancelDesign(
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
const state = get();
if (state.designProgress2 && !state.designProgress) {
set(() => ({ designProgress2: null }));
} else {
set(() => ({
currentAction: 'meditate' as const,
designProgress: null,
}));
}
}
export function saveDesign(
design: EnchantmentDesign,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
const state = get();
if (state.designProgress2 && state.designProgress2.designId === design.id) {
set((state) => ({
enchantmentDesigns: [...state.enchantmentDesigns, design],
designProgress2: null,
}));
} else {
set((state) => ({
enchantmentDesigns: [...state.enchantmentDesigns, design],
designProgress: null,
currentAction: 'meditate' as const,
}));
}
}
export function deleteDesign(
designId: string,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set((state) => ({
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
}));
}
@@ -0,0 +1,34 @@
// ─── Disenchanting Actions ─────────────────────────────────────────────────
import type { GameState, EquipmentInstance } from '../types';
export function disenchantEquipment(
instanceId: string,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
const state = get();
const instance = state.equipmentInstances[instanceId];
if (!instance || instance.enchantments.length === 0) return;
const disenchantLevel = 0;
const recoveryRate = 0.1 + disenchantLevel * 0.2;
let totalRecovered = 0;
for (const ench of instance.enchantments) {
totalRecovered += Math.floor(ench.actualCost * recoveryRate);
}
set((state) => ({
rawMana: state.rawMana + totalRecovered,
equipmentInstances: {
...state.equipmentInstances,
[instanceId]: {
...instance,
enchantments: [],
usedCapacity: 0,
},
},
log: [`✨ Disenchanted ${instance.name}, recovered ${totalRecovered} mana.`, ...state.log.slice(0, 49)],
}));
}
@@ -0,0 +1,103 @@
// ─── Equipment Management Actions ────────────────────────────────────────────
import type { GameState, 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
): string | null {
const type = CraftingUtils.getEquipmentType(typeId);
if (!type) return null;
const instanceId = CraftingUtils.generateInstanceId();
const instance: EquipmentInstance = {
instanceId,
typeId,
name: type.name,
enchantments: [],
usedCapacity: 0,
totalCapacity: type.baseCapacity,
rarity: 'common',
quality: 100,
tags: [],
};
set((state) => ({
equipmentInstances: {
...state.equipmentInstances,
[instanceId]: instance,
},
}));
return instanceId;
}
// Equip item
export function equipItem(
instanceId: string,
slot: EquipmentSlot,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
): boolean {
const state = get();
const instance = state.equipmentInstances[instanceId];
if (!instance) return false;
if (!CraftingUtils.canEquipInSlot(instance, slot, state.equippedInstances)) {
return false;
}
let newEquipped = { ...state.equippedInstances };
for (const [s, id] of Object.entries(newEquipped)) {
if (id === instanceId) {
newEquipped[s as EquipmentSlot] = null;
}
}
newEquipped[slot] = instanceId;
if (CraftingUtils.isTwoHanded(instance.typeId) && slot === 'mainHand') {
newEquipped.offHand = null;
}
set(() => ({ equippedInstances: newEquipped }));
return true;
}
// Unequip item
export function unequipItem(
slot: EquipmentSlot,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set((state) => ({
equippedInstances: {
...state.equippedInstances,
[slot]: null,
},
}));
}
// Delete equipment instance
export function deleteEquipmentInstance(
instanceId: string,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
const state = get();
let newEquipped = { ...state.equippedInstances };
for (const [slot, id] of Object.entries(newEquipped)) {
if (id === instanceId) {
newEquipped[slot as EquipmentSlot] = null;
}
}
const newInstances = { ...state.equipmentInstances };
delete newInstances[instanceId];
set(() => ({
equippedInstances: newEquipped,
equipmentInstances: newInstances,
}));
}
+11
View File
@@ -0,0 +1,11 @@
// ─── Crafting Action Implementations ──────────────────────────────────────────
// Modular structure for crafting actions
// Re-exports from the split modules
export { createEquipmentInstance, equipItem, unequipItem, deleteEquipmentInstance } from './equipment-actions';
export { startDesigningEnchantment, cancelDesign, saveDesign, deleteDesign } from './design-actions';
export { startPreparing, cancelPreparation } from './preparation-actions';
export { startApplying, pauseApplication, resumeApplication, cancelApplication } from './application-actions';
export { disenchantEquipment } from './disenchant-actions';
export { startCraftingEquipment, cancelEquipmentCrafting, deleteMaterial } from './crafting-equipment-actions';
export { getEquipmentSpells, getEquipmentEffects, getAvailableCapacity } from './computed-getters';
@@ -0,0 +1,44 @@
// ─── Enchantment Preparation Actions ────────────────────────────────────────
import type { GameState } from '../types';
import * as CraftingPrep from '../crafting-prep';
export function startPreparing(
equipmentInstanceId: string,
get: () => GameState,
set: (fn: (state: GameState) => Partial<GameState>) => void
): boolean {
const state = get();
const instance = state.equipmentInstances[equipmentInstanceId];
const validation = CraftingPrep.canPrepareEquipment(
instance,
instance?.tags || []
);
if (!validation.canPrepare) return false;
if (!instance) return false;
const costs = CraftingPrep.calculatePreparationCosts(instance.totalCapacity);
if (state.rawMana < costs.manaTotal) return false;
set(() => ({
currentAction: 'prepare' as const,
preparationProgress: CraftingPrep.initializePreparationProgress(
equipmentInstanceId,
instance.totalCapacity
),
}));
return true;
}
export function cancelPreparation(
set: (fn: (state: GameState) => Partial<GameState>) => void
) {
set(() => ({
currentAction: 'meditate' as const,
preparationProgress: null,
}));
}