Add floor navigation system with up/down direction and respawn mechanics

- Added climbDirection state to track player movement direction
- Added clearedFloors tracking for floor respawn system
- Players can now manually navigate between floors using ascend/descend buttons
- Floors respawn when player leaves and returns (for loot farming)
- Enhanced LootInventory component with:
  - Full inventory management (materials, essence, equipment)
  - Search and filter functionality
  - Sorting by name, rarity, or count
  - Delete functionality with confirmation dialog
- Added updateLootInventory function to store
- Blueprints are now shown as permanent unlocks in inventory
- Floor navigation UI shows direction toggle and respawn indicators
This commit is contained in:
2026-03-26 10:28:15 +00:00
parent ee0268d9f6
commit 4f4cbeb527
5 changed files with 679 additions and 94 deletions

View File

@@ -2,7 +2,7 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { GameState, GameAction, StudyTarget, SpellCost, SkillUpgradeChoice, EquipmentSlot, EquipmentInstance, EnchantmentDesign, DesignEffect } from './types';
import type { GameState, GameAction, StudyTarget, SpellCost, SkillUpgradeChoice, EquipmentSlot, EquipmentInstance, EnchantmentDesign, DesignEffect, LootInventory } from './types';
import {
ELEMENTS,
GUARDIANS,
@@ -490,6 +490,11 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
activeSpell: 'manaBolt',
currentAction: 'meditate',
castProgress: 0,
// Floor Navigation
climbDirection: 'up',
clearedFloors: {},
lastClearedFloor: null,
spells: startSpells,
skills: overrides.skills || {},
@@ -602,6 +607,13 @@ interface GameStore extends GameState, CraftingActions, FamiliarActions {
commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => void;
tierUpSkill: (skillId: string) => void;
// Floor Navigation
setClimbDirection: (direction: 'up' | 'down') => void;
changeFloor: (direction: 'up' | 'down') => void;
// Inventory Management
updateLootInventory: (inventory: LootInventory) => void;
// Computed getters
getMaxMana: () => number;
getRegen: () => number;
@@ -967,6 +979,12 @@ export const useGameStore = create<GameStore>()(
if (floorHP <= 0) {
// Floor cleared
const wasGuardian = GUARDIANS[currentFloor];
const clearedFloors = state.clearedFloors;
const climbDirection = state.climbDirection;
// Mark this floor as cleared (needs respawn if we leave and return)
clearedFloors[currentFloor] = true;
const lastClearedFloor = currentFloor;
// ─── Loot Drop System ───
const lootDrops = rollLootDrops(currentFloor, !!wasGuardian, 0);
@@ -1003,11 +1021,22 @@ export const useGameStore = create<GameStore>()(
}
}
currentFloor = currentFloor + 1;
if (currentFloor > 100) {
currentFloor = 100;
}
// Move to next floor based on direction
const nextFloor = climbDirection === 'up'
? Math.min(currentFloor + 1, 100)
: Math.max(currentFloor - 1, 1);
currentFloor = nextFloor;
floorMaxHP = getFloorMaxHP(currentFloor);
// Check if this floor was previously cleared (has enemies respawned?)
// Floors respawn when you leave them and come back
const floorWasCleared = clearedFloors[currentFloor];
if (floorWasCleared) {
// Floor has respawned - reset it but mark as uncleared
delete clearedFloors[currentFloor];
}
floorHP = floorMaxHP;
maxFloorReached = Math.max(maxFloorReached, currentFloor);
@@ -1019,6 +1048,10 @@ export const useGameStore = create<GameStore>()(
// Reset ALL spell progress on floor change
equipmentSpellStates = equipmentSpellStates.map(s => ({ ...s, castProgress: 0 }));
spellState = { ...spellState, castProgress: 0 };
// Update clearedFloors in the state
set((s) => ({ ...s, clearedFloors, lastClearedFloor }));
break; // Exit the while loop - new floor
}
}
@@ -1637,6 +1670,10 @@ export const useGameStore = create<GameStore>()(
});
},
updateLootInventory: (inventory: LootInventory) => {
set({ lootInventory: inventory });
},
startDesigningEnchantment: (name: string, equipmentTypeId: string, effects: DesignEffect[]) => {
const state = get();
@@ -1874,6 +1911,47 @@ export const useGameStore = create<GameStore>()(
if (!instance) return 0;
return instance.totalCapacity - instance.usedCapacity;
},
// ─── Floor Navigation ────────────────────────────────────────────────────────
setClimbDirection: (direction: 'up' | 'down') => {
set({ climbDirection: direction });
},
changeFloor: (direction: 'up' | 'down') => {
const state = get();
const currentFloor = state.currentFloor;
// Calculate next floor
const nextFloor = direction === 'up'
? Math.min(currentFloor + 1, 100)
: Math.max(currentFloor - 1, 1);
// Can't stay on same floor
if (nextFloor === currentFloor) return;
// Mark current floor as cleared (it will respawn when we come back)
const clearedFloors = { ...state.clearedFloors };
clearedFloors[currentFloor] = true;
// Check if next floor was cleared (needs respawn)
const nextFloorCleared = clearedFloors[nextFloor];
if (nextFloorCleared) {
// Respawn the floor
delete clearedFloors[nextFloor];
}
set({
currentFloor: nextFloor,
floorMaxHP: getFloorMaxHP(nextFloor),
floorHP: getFloorMaxHP(nextFloor),
maxFloorReached: Math.max(state.maxFloorReached, nextFloor),
clearedFloors,
climbDirection: direction,
equipmentSpellStates: state.equipmentSpellStates.map(s => ({ ...s, castProgress: 0 })),
log: [`🚶 Moved to floor ${nextFloor}${nextFloorCleared ? ' (respawned)' : ''}.`, ...state.log.slice(0, 49)],
});
},
}),
{
name: 'mana-loop-storage',
@@ -1908,6 +1986,9 @@ export const useGameStore = create<GameStore>()(
activeSpell: state.activeSpell,
currentAction: state.currentAction,
castProgress: state.castProgress,
climbDirection: state.climbDirection,
clearedFloors: state.clearedFloors,
lastClearedFloor: state.lastClearedFloor,
spells: state.spells,
skills: state.skills,
skillProgress: state.skillProgress,
@@ -1928,6 +2009,19 @@ export const useGameStore = create<GameStore>()(
designProgress: state.designProgress,
preparationProgress: state.preparationProgress,
applicationProgress: state.applicationProgress,
// Loot system
lootInventory: state.lootInventory,
lootDropsToday: state.lootDropsToday,
// Achievements
achievements: state.achievements,
totalDamageDealt: state.totalDamageDealt,
totalSpellsCast: state.totalSpellsCast,
totalCraftsCompleted: state.totalCraftsCompleted,
// Familiars
familiars: state.familiars,
activeFamiliarSlots: state.activeFamiliarSlots,
familiarSummonProgress: state.familiarSummonProgress,
totalFamiliarXpEarned: state.totalFamiliarXpEarned,
}),
}
)