fix: migrate equipment and enchantment state to modular stores, fix EnchantmentDesigner
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const MAX_LINES = 400;
|
const MAX_LINES = 400;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { execSync } = require('node:child_process');
|
const { execSync } = require('node:child_process');
|
||||||
|
|||||||
+4
-3
@@ -11,6 +11,7 @@ import {
|
|||||||
useSkillStore,
|
useSkillStore,
|
||||||
useCombatStore,
|
useCombatStore,
|
||||||
usePrestigeStore,
|
usePrestigeStore,
|
||||||
|
useCraftingStore,
|
||||||
fmt,
|
fmt,
|
||||||
computeMaxMana,
|
computeMaxMana,
|
||||||
computeRegen,
|
computeRegen,
|
||||||
@@ -147,9 +148,9 @@ export default function ManaLoopGame() {
|
|||||||
|
|
||||||
const gameOver = useUIStore((s) => s.gameOver);
|
const gameOver = useUIStore((s) => s.gameOver);
|
||||||
|
|
||||||
// Get equipment state from store
|
// Get equipment state from crafting store
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
|
|
||||||
// Derived state
|
// Derived state
|
||||||
const upgradeEffects = getUnifiedEffects({
|
const upgradeEffects = getUnifiedEffects({
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { ENCHANTMENT_EFFECTS } from '@/lib/game/data/enchantment-effects';
|
|||||||
import type { EquipmentInstance, EnchantmentDesign, AppliedEnchantment, LootInventory, EquipmentCraftingProgress, EquipmentSlot } from '@/lib/game/types';
|
import type { EquipmentInstance, EnchantmentDesign, AppliedEnchantment, LootInventory, EquipmentCraftingProgress, EquipmentSlot } from '@/lib/game/types';
|
||||||
import { fmt } from '@/lib/game/stores';
|
import { fmt } from '@/lib/game/stores';
|
||||||
import { CheckCircle, Sparkles } from 'lucide-react';
|
import { CheckCircle, Sparkles } from 'lucide-react';
|
||||||
import { useGameStore } from '@/lib/game/stores';
|
import { useGameStore, useCraftingStore, useManaStore } from '@/lib/game/stores';
|
||||||
|
|
||||||
export interface EnchantmentApplierProps {
|
export interface EnchantmentApplierProps {
|
||||||
selectedEquipmentInstance: string | null;
|
selectedEquipmentInstance: string | null;
|
||||||
@@ -31,15 +31,15 @@ export function EnchantmentApplier({
|
|||||||
onEnchantmentApplied,
|
onEnchantmentApplied,
|
||||||
onCapacityExceeded,
|
onCapacityExceeded,
|
||||||
}: EnchantmentApplierProps) {
|
}: EnchantmentApplierProps) {
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
const enchantmentDesigns = useGameStore((s) => s.enchantmentDesigns);
|
const enchantmentDesigns = useCraftingStore((s) => s.enchantmentDesigns);
|
||||||
const applicationProgress = useGameStore((s) => s.applicationProgress);
|
const applicationProgress = useCraftingStore((s) => s.applicationProgress);
|
||||||
const rawMana = useGameStore((s) => s.rawMana);
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
const startApplying = useGameStore((s) => s.startApplying);
|
const startApplying = useCraftingStore((s) => s.startApplying);
|
||||||
const pauseApplication = useGameStore((s) => s.pauseApplication);
|
const pauseApplication = useCraftingStore((s) => s.pauseApplication);
|
||||||
const resumeApplication = useGameStore((s) => s.resumeApplication);
|
const resumeApplication = useCraftingStore((s) => s.resumeApplication);
|
||||||
const cancelApplication = useGameStore((s) => s.cancelApplication);
|
const cancelApplication = useCraftingStore((s) => s.cancelApplication);
|
||||||
|
|
||||||
// Get equipped items as array - ONLY show items tagged 'Ready for Enchantment' (requirement cr5)
|
// Get equipped items as array - ONLY show items tagged 'Ready for Enchantment' (requirement cr5)
|
||||||
const equippedItems = Object.entries(equippedInstances)
|
const equippedItems = Object.entries(equippedInstances)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import {
|
|||||||
addEffectToDesign,
|
addEffectToDesign,
|
||||||
removeEffectFromDesign,
|
removeEffectFromDesign,
|
||||||
} from './EnchantmentDesigner/utils';
|
} from './EnchantmentDesigner/utils';
|
||||||
import { useGameStore } from '@/lib/game/stores';
|
import { useCraftingStore } from '@/lib/game/stores';
|
||||||
|
import { useSkillStore } from '@/lib/game/stores';
|
||||||
|
|
||||||
export function EnchantmentDesigner({
|
export function EnchantmentDesigner({
|
||||||
selectedEquipmentType,
|
selectedEquipmentType,
|
||||||
@@ -34,16 +35,21 @@ export function EnchantmentDesigner({
|
|||||||
selectedDesign,
|
selectedDesign,
|
||||||
setSelectedDesign,
|
setSelectedDesign,
|
||||||
}: EnchantmentDesignerProps) {
|
}: EnchantmentDesignerProps) {
|
||||||
const enchantmentDesigns = useGameStore((s) => s.enchantmentDesigns);
|
// Crafting store selectors
|
||||||
const designProgress = useGameStore((s) => s.designProgress);
|
const enchantmentDesigns = useCraftingStore((s) => s.enchantmentDesigns);
|
||||||
const startDesigningEnchantment = useGameStore((s) => s.startDesigningEnchantment);
|
const designProgress = useCraftingStore((s) => s.designProgress);
|
||||||
const cancelDesign = useGameStore((s) => s.cancelDesign);
|
const startDesigningEnchantment = useCraftingStore((s) => s.startDesigningEnchantment);
|
||||||
const deleteDesign = useGameStore((s) => s.deleteDesign);
|
const cancelDesign = useCraftingStore((s) => s.cancelDesign);
|
||||||
const unlockedEffects = useGameStore((s) => s.unlockedEffects);
|
const deleteDesign = useCraftingStore((s) => s.deleteDesign);
|
||||||
const skills = useGameStore((s) => s.skills);
|
const unlockedEffects = useCraftingStore((s) => s.unlockedEffects);
|
||||||
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
|
|
||||||
const enchantingLevel = skills.enchanting || 0;
|
// Skill store selectors
|
||||||
const efficiencyBonus = (skills.efficientEnchant || 0) * 0.05;
|
const skills = useSkillStore((s) => s.skills);
|
||||||
|
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||||
|
|
||||||
|
const enchantingLevel = skills?.enchanting || 0;
|
||||||
|
const efficiencyBonus = (skillUpgrades?.['efficientEnchant'] || []).length * 0.05 || 0;
|
||||||
|
|
||||||
// Calculate total capacity cost for current design
|
// Calculate total capacity cost for current design
|
||||||
const designCapacityCost = calculateDesignCapacityCost(selectedEffects, efficiencyBonus);
|
const designCapacityCost = calculateDesignCapacityCost(selectedEffects, efficiencyBonus);
|
||||||
@@ -84,7 +90,7 @@ export function EnchantmentDesigner({
|
|||||||
const incompatibleEffects = getIncompatibleEffects(selectedEquipmentType, unlockedEffects);
|
const incompatibleEffects = getIncompatibleEffects(selectedEquipmentType, unlockedEffects);
|
||||||
|
|
||||||
// Get equipment types that the player actually owns (has instances of)
|
// Get equipment types that the player actually owns (has instances of)
|
||||||
const ownedEquipmentTypes = getOwnedEquipmentTypes(useGameStore.getState());
|
const ownedEquipmentTypes = getOwnedEquipmentTypes(equipmentInstances);
|
||||||
|
|
||||||
// Get the reason why an effect is incompatible
|
// Get the reason why an effect is incompatible
|
||||||
const getIncompatibilityReasonWrapper = (effect: { id: string; name: string; description: string; allowedEquipmentCategories: any[] }) => {
|
const getIncompatibilityReasonWrapper = (effect: { id: string; name: string; description: string; allowedEquipmentCategories: any[] }) => {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import type { EquipmentInstance, EnchantmentDesign, DesignEffect, EquipmentCraftingProgress, EquipmentCategory } from '@/lib/game/types';
|
import type { EquipmentInstance, EnchantmentDesign, DesignEffect, EquipmentCraftingProgress, EquipmentCategory } from '@/lib/game/types';
|
||||||
import type { GameStore } from '@/lib/game/stores';
|
|
||||||
|
|
||||||
export interface EnchantmentDesignerProps {
|
export interface EnchantmentDesignerProps {
|
||||||
store: GameStore;
|
|
||||||
selectedEquipmentType: string | null;
|
selectedEquipmentType: string | null;
|
||||||
setSelectedEquipmentType: (type: string | null) => void;
|
setSelectedEquipmentType: (type: string | null) => void;
|
||||||
selectedEffects: DesignEffect[];
|
selectedEffects: DesignEffect[];
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment';
|
import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment';
|
||||||
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from '@/lib/game/data/enchantment-effects';
|
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from '@/lib/game/data/enchantment-effects';
|
||||||
import type { DesignEffect, EquipmentCategory } from '@/lib/game/types';
|
import type { DesignEffect, EquipmentInstance, EquipmentCategory } from '@/lib/game/types';
|
||||||
import type { GameStore } from '@/lib/game/stores';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get available effects for selected equipment type (only unlocked ones)
|
* Get available effects for selected equipment type (only unlocked ones)
|
||||||
@@ -44,12 +43,12 @@ export function getIncompatibleEffects(
|
|||||||
* Get equipment types that the player actually owns (has instances of)
|
* Get equipment types that the player actually owns (has instances of)
|
||||||
* This ensures enchantment compatibility is based on owned items, not just blueprints
|
* This ensures enchantment compatibility is based on owned items, not just blueprints
|
||||||
*/
|
*/
|
||||||
export function getOwnedEquipmentTypes(store: GameStore) {
|
export function getOwnedEquipmentTypes(equipmentInstances: Record<string, EquipmentInstance>) {
|
||||||
// Get all unique equipment type IDs from owned instances
|
// Get all unique equipment type IDs from owned instances
|
||||||
const ownedEquipmentTypeIds = new Set<string>();
|
const ownedEquipmentTypeIds = new Set<string>();
|
||||||
|
|
||||||
// Check all equipment instances the player owns
|
// Check all equipment instances the player owns
|
||||||
for (const instance of Object.values(store.equipmentInstances || {})) {
|
for (const instance of Object.values(equipmentInstances || {})) {
|
||||||
ownedEquipmentTypeIds.add(instance.typeId);
|
ownedEquipmentTypeIds.add(instance.typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Trash2, CheckCircle, AlertTriangle } from 'lucide-react';
|
|||||||
import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment';
|
import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment';
|
||||||
import type { EquipmentInstance, AppliedEnchantment, LootInventory, EquipmentCraftingProgress, EquipmentSlot } from '@/lib/game/types';
|
import type { EquipmentInstance, AppliedEnchantment, LootInventory, EquipmentCraftingProgress, EquipmentSlot } from '@/lib/game/types';
|
||||||
import { fmt } from '@/lib/game/stores';
|
import { fmt } from '@/lib/game/stores';
|
||||||
import { useGameStore } from '@/lib/game/stores';
|
import { useGameStore, useCraftingStore, useManaStore, useSkillStore } from '@/lib/game/stores';
|
||||||
import { useGameToast } from '@/components/game/GameToast';
|
import { useGameToast } from '@/components/game/GameToast';
|
||||||
|
|
||||||
export interface EnchantmentPreparerProps {
|
export interface EnchantmentPreparerProps {
|
||||||
@@ -26,13 +26,13 @@ export function EnchantmentPreparer({
|
|||||||
setSelectedEquipmentInstance,
|
setSelectedEquipmentInstance,
|
||||||
}: EnchantmentPreparerProps) {
|
}: EnchantmentPreparerProps) {
|
||||||
const showToast = useGameToast();
|
const showToast = useGameToast();
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
const preparationProgress = useGameStore((s) => s.preparationProgress);
|
const preparationProgress = useCraftingStore((s) => s.preparationProgress);
|
||||||
const rawMana = useGameStore((s) => s.rawMana);
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
const skills = useGameStore((s) => s.skills);
|
const skills = useSkillStore((s) => s.skills);
|
||||||
const startPreparing = useGameStore((s) => s.startPreparing);
|
const startPreparing = useCraftingStore((s) => s.startPreparing);
|
||||||
const cancelPreparation = useGameStore((s) => s.cancelPreparation);
|
const cancelPreparation = useCraftingStore((s) => s.cancelPreparation);
|
||||||
|
|
||||||
// Get equipped items as array
|
// Get equipped items as array
|
||||||
const equippedItems = Object.entries(equippedInstances)
|
const equippedItems = Object.entries(equippedInstances)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useGameStore } from '@/lib/game/stores';
|
import { useGameStore, useCraftingStore } from '@/lib/game/stores';
|
||||||
import { LootInventoryDisplay } from '@/components/game/LootInventory';
|
import { LootInventoryDisplay } from '@/components/game/LootInventory';
|
||||||
|
|
||||||
export function LootTab() {
|
export function LootTab() {
|
||||||
const lootInventory = useGameStore((s) => s.lootInventory);
|
const lootInventory = useGameStore((s) => s.lootInventory);
|
||||||
const elements = useGameStore((s) => s.elements);
|
const elements = useGameStore((s) => s.elements);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
const deleteMaterial = useGameStore((s) => s.deleteMaterial);
|
const deleteMaterial = useGameStore((s) => s.deleteMaterial);
|
||||||
const deleteEquipmentInstance = useGameStore((s) => s.deleteEquipmentInstance);
|
const deleteEquipmentInstance = useGameStore((s) => s.deleteEquipmentInstance);
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { getActiveEquipmentSpells, getTotalDPS } from '@/lib/game/computed-stats
|
|||||||
import { getUnifiedEffects } from '@/lib/game/effects';
|
import { getUnifiedEffects } from '@/lib/game/effects';
|
||||||
import { formatSpellCost, getSpellCostColor } from '@/lib/game/formatting';
|
import { formatSpellCost, getSpellCostColor } from '@/lib/game/formatting';
|
||||||
import { canAffordSpellCost, getFloorElement } from '@/lib/game/stores';
|
import { canAffordSpellCost, getFloorElement } from '@/lib/game/stores';
|
||||||
import { useGameStore, useManaStore, useSkillStore, useCombatStore, usePrestigeStore } from '@/lib/game/stores';
|
import { useGameStore, useManaStore, useSkillStore, useCombatStore, usePrestigeStore, useCraftingStore } from '@/lib/game/stores';
|
||||||
import { GOLEMS_DEF, getGolemDamage, getGolemAttackSpeed } from '@/lib/game/data/golems';
|
import { GOLEMS_DEF, getGolemDamage, getGolemAttackSpeed } from '@/lib/game/data/golems';
|
||||||
|
|
||||||
// Extracted components
|
// Extracted components
|
||||||
@@ -68,11 +68,11 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
|
|||||||
const skillTiers = useSkillStore((s) => s.skillTiers);
|
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||||
const rawMana = useManaStore((s) => s.rawMana);
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
const elements = useManaStore((s) => s.elements);
|
const elements = useManaStore((s) => s.elements);
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
const designProgress = useGameStore((s) => s.designProgress);
|
const designProgress = useCraftingStore((s) => s.designProgress);
|
||||||
const designProgress2 = useGameStore((s) => s.designProgress2);
|
const designProgress2 = useCraftingStore((s) => s.designProgress2);
|
||||||
const preparationProgress = useGameStore((s) => s.preparationProgress);
|
const preparationProgress = useCraftingStore((s) => s.preparationProgress);
|
||||||
const applicationProgress = useGameStore((s) => s.applicationProgress);
|
const applicationProgress = useGameStore((s) => s.applicationProgress);
|
||||||
const equipmentCraftingProgress = useGameStore((s) => s.equipmentCraftingProgress);
|
const equipmentCraftingProgress = useGameStore((s) => s.equipmentCraftingProgress);
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export function updateEnchanterAttunement(
|
|||||||
let newXP = enchanterState.experience + xpGained;
|
let newXP = enchanterState.experience + xpGained;
|
||||||
let newLevel = enchanterState.level;
|
let newLevel = enchanterState.level;
|
||||||
|
|
||||||
const { getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } = require('./data/attunements');
|
import { getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements.js';
|
||||||
|
|
||||||
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
|
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
|
||||||
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
|
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ describe('Combat Calculations', () => {
|
|||||||
describe('getFloorMaxHP', () => {
|
describe('getFloorMaxHP', () => {
|
||||||
it('should return guardian HP for guardian floors', () => {
|
it('should return guardian HP for guardian floors', () => {
|
||||||
// Import GUARDIANS from constants
|
// Import GUARDIANS from constants
|
||||||
const { GUARDIANS } = require('../../constants');
|
import { GUARDIANS } from '../../constants';
|
||||||
expect(getFloorMaxHP(10)).toBe(GUARDIANS[10].hp);
|
expect(getFloorMaxHP(10)).toBe(GUARDIANS[10].hp);
|
||||||
expect(getFloorMaxHP(100)).toBe(GUARDIANS[100].hp);
|
expect(getFloorMaxHP(100)).toBe(GUARDIANS[100].hp);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,22 @@
|
|||||||
|
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import type { DesignProgress, PreparationProgress, ApplicationProgress, EquipmentCraftingProgress } from '../types';
|
import type { DesignProgress, PreparationProgress, ApplicationProgress, EquipmentCraftingProgress, EnchantmentDesign, EquipmentInstance, DesignEffect } from '../types';
|
||||||
|
|
||||||
|
// Import crafting modules for action logic
|
||||||
|
import * as CraftingUtils from '../crafting-utils';
|
||||||
|
import * as CraftingDesign from '../crafting-design';
|
||||||
|
import { computeEffects } from '../upgrade-effects';
|
||||||
|
import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects';
|
||||||
|
|
||||||
|
// Import other stores to access required state
|
||||||
|
import { useSkillStore } from './skillStore';
|
||||||
|
import { useGameStore } from './gameStore';
|
||||||
|
|
||||||
|
// Import action modules
|
||||||
|
import * as ApplicationActions from '../crafting-actions/application-actions';
|
||||||
|
import * as CraftingApply from '../crafting-apply';
|
||||||
|
import * as PreparationActions from '../crafting-actions/preparation-actions';
|
||||||
|
|
||||||
export interface CraftingState {
|
export interface CraftingState {
|
||||||
// Crafting progress
|
// Crafting progress
|
||||||
@@ -13,6 +28,15 @@ export interface CraftingState {
|
|||||||
preparationProgress: PreparationProgress | null;
|
preparationProgress: PreparationProgress | null;
|
||||||
applicationProgress: ApplicationProgress | null;
|
applicationProgress: ApplicationProgress | null;
|
||||||
equipmentCraftingProgress: EquipmentCraftingProgress | null;
|
equipmentCraftingProgress: EquipmentCraftingProgress | null;
|
||||||
|
|
||||||
|
// Enchantment designs
|
||||||
|
enchantmentDesigns: EnchantmentDesign[];
|
||||||
|
// Unlocked enchantment effects
|
||||||
|
unlockedEffects: string[];
|
||||||
|
// Equipment instances (instanceId -> instance)
|
||||||
|
equipmentInstances: Record<string, EquipmentInstance>;
|
||||||
|
// Equipped instances (slot -> instanceId or null)
|
||||||
|
equippedInstances: Record<string, string | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CraftingActions {
|
export interface CraftingActions {
|
||||||
@@ -28,19 +52,39 @@ export interface CraftingActions {
|
|||||||
|
|
||||||
// Actions for equipment crafting progress
|
// Actions for equipment crafting progress
|
||||||
setEquipmentCraftingProgress: (progress: EquipmentCraftingProgress | null) => void;
|
setEquipmentCraftingProgress: (progress: EquipmentCraftingProgress | null) => void;
|
||||||
|
|
||||||
|
// Enchantment design actions
|
||||||
|
startDesigningEnchantment: (name: string, equipmentTypeId: string, effects: DesignEffect[]) => boolean;
|
||||||
|
cancelDesign: () => void;
|
||||||
|
saveDesign: (design: EnchantmentDesign) => void;
|
||||||
|
deleteDesign: (designId: string) => void;
|
||||||
|
|
||||||
|
// Enchantment application actions
|
||||||
|
startApplying: (equipmentInstanceId: string, designId: string) => boolean;
|
||||||
|
pauseApplication: () => void;
|
||||||
|
resumeApplication: () => void;
|
||||||
|
cancelApplication: () => void;
|
||||||
|
|
||||||
|
// Enchantment preparation actions
|
||||||
|
startPreparing: (equipmentInstanceId: string) => boolean;
|
||||||
|
cancelPreparation: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CraftingStore = CraftingState & CraftingActions;
|
export type CraftingStore = CraftingState & CraftingActions;
|
||||||
|
|
||||||
export const useCraftingStore = create<CraftingStore>()(
|
export const useCraftingStore = create<CraftingStore>()(
|
||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set, get) => ({
|
||||||
// Initial state
|
// Initial state
|
||||||
designProgress: null,
|
designProgress: null,
|
||||||
designProgress2: null,
|
designProgress2: null,
|
||||||
preparationProgress: null,
|
preparationProgress: null,
|
||||||
applicationProgress: null,
|
applicationProgress: null,
|
||||||
equipmentCraftingProgress: null,
|
equipmentCraftingProgress: null,
|
||||||
|
enchantmentDesigns: [],
|
||||||
|
unlockedEffects: [],
|
||||||
|
equipmentInstances: {},
|
||||||
|
equippedInstances: {},
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
setDesignProgress: (progress) => set({ designProgress: progress }),
|
setDesignProgress: (progress) => set({ designProgress: progress }),
|
||||||
@@ -48,6 +92,135 @@ export const useCraftingStore = create<CraftingStore>()(
|
|||||||
setPreparationProgress: (progress) => set({ preparationProgress: progress }),
|
setPreparationProgress: (progress) => set({ preparationProgress: progress }),
|
||||||
setApplicationProgress: (progress) => set({ applicationProgress: progress }),
|
setApplicationProgress: (progress) => set({ applicationProgress: progress }),
|
||||||
setEquipmentCraftingProgress: (progress) => set({ equipmentCraftingProgress: progress }),
|
setEquipmentCraftingProgress: (progress) => set({ equipmentCraftingProgress: progress }),
|
||||||
|
|
||||||
|
// Enchantment design actions
|
||||||
|
startDesigningEnchantment: (name, equipmentTypeId, effects) => {
|
||||||
|
// Get state from other stores
|
||||||
|
const skillState = useSkillStore.getState();
|
||||||
|
const state = get(); // crafting state
|
||||||
|
|
||||||
|
const enchantingLevel = skillState.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 = (skillState.skillUpgrades?.['efficientEnchant'] || []).length * 0.05 || 0;
|
||||||
|
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
|
||||||
|
|
||||||
|
if (totalCapacityCost > equipType.baseCapacity) return false;
|
||||||
|
|
||||||
|
const computedEffects = computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {});
|
||||||
|
const hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY);
|
||||||
|
|
||||||
|
let updates: Partial<CraftingState> = {};
|
||||||
|
|
||||||
|
if (!state.designProgress) {
|
||||||
|
updates = {
|
||||||
|
designProgress: {
|
||||||
|
designId: CraftingUtils.generateDesignId(),
|
||||||
|
progress: 0,
|
||||||
|
required: CraftingDesign.calculateDesignTime(effects),
|
||||||
|
name,
|
||||||
|
equipmentType: equipmentTypeId,
|
||||||
|
effects,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// Update currentAction in gameStore
|
||||||
|
useGameStore.setState({ currentAction: 'design' });
|
||||||
|
} 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;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelDesign: () => {
|
||||||
|
const state = get();
|
||||||
|
if (state.designProgress2 && !state.designProgress) {
|
||||||
|
set({ designProgress2: null });
|
||||||
|
} else {
|
||||||
|
set({ designProgress: null });
|
||||||
|
useGameStore.setState({ currentAction: 'meditate' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteDesign: (designId) => {
|
||||||
|
set((state) => ({
|
||||||
|
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enchantment design save
|
||||||
|
saveDesign: (design) => {
|
||||||
|
const state = get();
|
||||||
|
if (state.designProgress2 && state.designProgress2.designId === design.id) {
|
||||||
|
set((s) => ({
|
||||||
|
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
||||||
|
designProgress2: null,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
set((s) => ({
|
||||||
|
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
||||||
|
designProgress: null,
|
||||||
|
currentAction: 'meditate' as const,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enchantment application actions
|
||||||
|
startApplying: (equipmentInstanceId, designId) => {
|
||||||
|
return ApplicationActions.startApplying(
|
||||||
|
equipmentInstanceId,
|
||||||
|
designId,
|
||||||
|
get,
|
||||||
|
set
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
pauseApplication: () => {
|
||||||
|
ApplicationActions.pauseApplication(get, set);
|
||||||
|
},
|
||||||
|
|
||||||
|
resumeApplication: () => {
|
||||||
|
ApplicationActions.resumeApplication(get, set);
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelApplication: () => {
|
||||||
|
ApplicationActions.cancelApplication(set);
|
||||||
|
useGameStore.setState({ currentAction: 'meditate' });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Preparation actions
|
||||||
|
startPreparing: (equipmentInstanceId) => {
|
||||||
|
// Get rawMana from manaStore
|
||||||
|
const rawMana = useManaStore.getState().rawMana;
|
||||||
|
// Temporary state to pass to preparation action
|
||||||
|
const tempState = { ...get(), rawMana } as any;
|
||||||
|
return PreparationActions.startPreparing(
|
||||||
|
equipmentInstanceId,
|
||||||
|
() => tempState,
|
||||||
|
set
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelPreparation: () => {
|
||||||
|
PreparationActions.cancelPreparation(set);
|
||||||
|
useGameStore.setState({ currentAction: 'meditate' });
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'mana-loop-crafting',
|
name: 'mana-loop-crafting',
|
||||||
@@ -57,6 +230,10 @@ export const useCraftingStore = create<CraftingStore>()(
|
|||||||
preparationProgress: state.preparationProgress,
|
preparationProgress: state.preparationProgress,
|
||||||
applicationProgress: state.applicationProgress,
|
applicationProgress: state.applicationProgress,
|
||||||
equipmentCraftingProgress: state.equipmentCraftingProgress,
|
equipmentCraftingProgress: state.equipmentCraftingProgress,
|
||||||
|
enchantmentDesigns: state.enchantmentDesigns,
|
||||||
|
unlockedEffects: state.unlockedEffects,
|
||||||
|
equipmentInstances: state.equipmentInstances,
|
||||||
|
equippedInstances: state.equippedInstances,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useSkillStore } from './skillStore';
|
|||||||
import { usePrestigeStore } from './prestigeStore';
|
import { usePrestigeStore } from './prestigeStore';
|
||||||
import { useCombatStore } from './combatStore';
|
import { useCombatStore } from './combatStore';
|
||||||
import { useUIStore } from './uiStore';
|
import { useUIStore } from './uiStore';
|
||||||
|
import { useCraftingStore } from './craftingStore';
|
||||||
import { getUnifiedEffects } from '../effects';
|
import { getUnifiedEffects } from '../effects';
|
||||||
import {
|
import {
|
||||||
computeMaxMana,
|
computeMaxMana,
|
||||||
@@ -32,8 +33,8 @@ export function useGameLoop() {
|
|||||||
export function useUnifiedEffects() {
|
export function useUnifiedEffects() {
|
||||||
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||||
const skillTiers = useSkillStore((s) => s.skillTiers);
|
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
|
|
||||||
return getUnifiedEffects({
|
return getUnifiedEffects({
|
||||||
skillUpgrades,
|
skillUpgrades,
|
||||||
@@ -54,12 +55,14 @@ export function useManaStats() {
|
|||||||
const meditateTicks = useManaStore((s) => s.meditateTicks);
|
const meditateTicks = useManaStore((s) => s.meditateTicks);
|
||||||
const day = useGameStore((s) => s.day);
|
const day = useGameStore((s) => s.day);
|
||||||
const hour = useGameStore((s) => s.hour);
|
const hour = useGameStore((s) => s.hour);
|
||||||
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
|
|
||||||
const upgradeEffects = getUnifiedEffects({
|
const upgradeEffects = getUnifiedEffects({
|
||||||
skillUpgrades,
|
skillUpgrades,
|
||||||
skillTiers,
|
skillTiers,
|
||||||
equippedInstances: {},
|
equippedInstances,
|
||||||
equipmentInstances: {},
|
equipmentInstances,
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxMana = computeMaxMana(
|
const maxMana = computeMaxMana(
|
||||||
@@ -85,12 +88,12 @@ export function useManaStats() {
|
|||||||
|
|
||||||
// Mana Cascade bonus
|
// Mana Cascade bonus
|
||||||
const manaCascadeBonus = upgradeEffects.specials.has('mana_cascade')
|
const manaCascadeBonus = upgradeEffects.specials.has('mana_cascade')
|
||||||
? Math.floor(maxMana / 100) * 0.1
|
? Math.floor(maxMana /100) * 0.1
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
// Mana Waterfall bonus
|
// Mana Waterfall bonus
|
||||||
const manaWaterfallBonus = upgradeEffects.specials.has('mana_waterfall')
|
const manaWaterfallBonus = upgradeEffects.specials.has('mana_waterfall')
|
||||||
? Math.floor(maxMana / 100) * 0.25
|
? Math.floor(maxMana /100) * 0.25
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
|
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
|
||||||
@@ -114,8 +117,8 @@ export function useManaStats() {
|
|||||||
export function useCombatStats() {
|
export function useCombatStats() {
|
||||||
const skills = useSkillStore((s) => s.skills);
|
const skills = useSkillStore((s) => s.skills);
|
||||||
const signedPacts = usePrestigeStore((s) => s.signedPacts);
|
const signedPacts = usePrestigeStore((s) => s.signedPacts);
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||||
const skillTiers = useSkillStore((s) => s.skillTiers);
|
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||||
|
|
||||||
@@ -139,8 +142,8 @@ export function useCombatStats() {
|
|||||||
* Get equipment-related derived state
|
* Get equipment-related derived state
|
||||||
*/
|
*/
|
||||||
export function useEquipmentState() {
|
export function useEquipmentState() {
|
||||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
const elements = useManaStore((s) => s.elements);
|
const elements = useManaStore((s) => s.elements);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user