Task 2: SpireTab Overhaul - add Climb the Spire button, implement Spire Mode with exit condition
This commit is contained in:
+15
-10
@@ -1,6 +1,6 @@
|
|||||||
# Task 2 Progress Tracking
|
# Task 2 Progress Tracking
|
||||||
|
|
||||||
**Last Updated**: 2026-04-26 10:35:00
|
**Last Updated**: 2026-04-26 12:57:00
|
||||||
**Current Status**: In Progress
|
**Current Status**: In Progress
|
||||||
|
|
||||||
## Completed Tasks
|
## Completed Tasks
|
||||||
@@ -9,21 +9,22 @@
|
|||||||
- [2026-04-25] Task 4: Equipment System - 2-Handed Weapons ✓
|
- [2026-04-25] Task 4: Equipment System - 2-Handed Weapons ✓
|
||||||
- [2026-04-25] Task 12: StatsTab - Lock Fire/Water/Air/Earth at start ✓
|
- [2026-04-25] Task 12: StatsTab - Lock Fire/Water/Air/Earth at start ✓
|
||||||
- [2026-04-26] Task 7: CRITICAL BUG FIX - Mana Well 'Deep Basin' upgrade ✓
|
- [2026-04-26] Task 7: CRITICAL BUG FIX - Mana Well 'Deep Basin' upgrade ✓
|
||||||
|
- [2026-04-26] Task 2: Research Locking - Prevent switching topics while study in progress ✓
|
||||||
|
- [2026-04-26] Task 5: DebugTab Update - Invoker Debugging Buttons ✓
|
||||||
|
|
||||||
## In Progress Tasks
|
## In Progress Tasks
|
||||||
None currently
|
None currently
|
||||||
|
|
||||||
## Pending Tasks
|
## Pending Tasks
|
||||||
1. Task 1: ActionButtons Rework [FAILED - sub-agent context too long]
|
1. Task 1: ActionButtons Rework [Try context file approach]
|
||||||
2. Task 2: Research Locking - SkillsTab [Pending]
|
2. Task 3: SpireTab Overhaul [Try context file approach]
|
||||||
3. Task 3: SpireTab Overhaul [FAILED - sub-agent context too long]
|
3. Task 6: System Integrity - Fix 'Show Component Names' [In Progress - investigating]
|
||||||
4. Task 5: DebugTab Update - Invoker Debugging Buttons [Pending]
|
4. Task 8: Bug Fix: Combat UI - Casting Bar animation [Try context file approach]
|
||||||
5. Task 6: System Integrity - Fix 'Show Component Names' [In Progress - investigating]
|
5. Task 10: Bug Fix: Crafting - Prepare/Design limits [Try again]
|
||||||
6. Task 8: Bug Fix: Combat UI - Casting Bar animation [FAILED - sub-agent context too long]
|
|
||||||
7. Task 10: Bug Fix: Crafting - Prepare/Design limits [Pending]
|
|
||||||
|
|
||||||
## Blocked Tasks
|
## Failed Tasks (Context Length)
|
||||||
- Task 1, 3, 8: Sub-agent attempts failed due to context length limits (603k+ tokens)
|
- Task 1, 3, 8, 10: Sub-agent attempts failed due to context length limits (423k-603k tokens > 262k limit)
|
||||||
|
- BUT Task 2 and Task 5 SUCCEEDED with context file approach!
|
||||||
|
|
||||||
## Commit History
|
## Commit History
|
||||||
- 65b0f96: Remove Transference from LootTab, delete Ascension skills
|
- 65b0f96: Remove Transference from LootTab, delete Ascension skills
|
||||||
@@ -32,3 +33,7 @@ None currently
|
|||||||
- 2355be6: StatsTab - Lock Fire/Water/Air/Earth at start
|
- 2355be6: StatsTab - Lock Fire/Water/Air/Earth at start
|
||||||
- f61ed00: FIX: Skill perks multiplier values (Deep Basin + others)
|
- f61ed00: FIX: Skill perks multiplier values (Deep Basin + others)
|
||||||
- a6ce36b: WIP: Task 2 progress - investigating Show Component Names debug option
|
- a6ce36b: WIP: Task 2 progress - investigating Show Component Names debug option
|
||||||
|
- 4193718: WIP: Task 2 - completed 5/12 tasks, investigating remaining
|
||||||
|
- fc9e4c8: Add context files for Task 2 sub-agents
|
||||||
|
- 229cb16: Task 2: Research Locking - prevent switching topics while study in progress
|
||||||
|
- 9f029d9: Task 2: DebugTab Update - add Invoker Debugging Buttons for Pacts
|
||||||
|
|||||||
+177
-133
@@ -12,7 +12,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { RotateCcw } from 'lucide-react';
|
import { RotateCcw, Mountain, ChevronDown } from 'lucide-react';
|
||||||
import { TooltipProvider } from '@/components/ui/tooltip';
|
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||||
import { DebugName } from '@/lib/game/debug-context';
|
import { DebugName } from '@/lib/game/debug-context';
|
||||||
// Non-tab component imports
|
// Non-tab component imports
|
||||||
@@ -181,7 +181,7 @@ export default function ManaLoopGame() {
|
|||||||
<TimeDisplay
|
<TimeDisplay
|
||||||
day={store.day}
|
day={store.day}
|
||||||
hour={store.hour}
|
hour={store.hour}
|
||||||
isPaused={store.isPaused}
|
isPaused={store.paused}
|
||||||
togglePause={store.togglePause}
|
togglePause={store.togglePause}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,16 +207,34 @@ export default function ManaLoopGame() {
|
|||||||
/>
|
/>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Climb the Spire Button - only show when not in Spire Mode */}
|
||||||
<DebugName name="ActionButtons">
|
{!store.spireMode && (
|
||||||
<ActionButtons
|
<DebugName name="ClimbSpireButton">
|
||||||
currentAction={store.currentAction}
|
<Button
|
||||||
designProgress={store.designProgress}
|
className="w-full bg-gradient-to-r from-amber-600 to-orange-600 hover:from-amber-700 hover:to-orange-700"
|
||||||
preparationProgress={store.preparationProgress}
|
size="lg"
|
||||||
applicationProgress={store.applicationProgress}
|
onClick={() => store.enterSpireMode()}
|
||||||
setAction={store.setAction}
|
>
|
||||||
/>
|
<Mountain className="w-5 h-5 mr-2" />
|
||||||
</DebugName>
|
Climb the Spire
|
||||||
|
</Button>
|
||||||
|
</DebugName>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Action Buttons - only show when not in Spire Mode */}
|
||||||
|
{!store.spireMode && (
|
||||||
|
<DebugName name="ActionButtons">
|
||||||
|
<ActionButtons
|
||||||
|
currentAction={store.currentAction}
|
||||||
|
currentStudyTarget={store.currentStudyTarget}
|
||||||
|
designProgress={store.designProgress}
|
||||||
|
designProgress2={store.designProgress2}
|
||||||
|
preparationProgress={store.preparationProgress}
|
||||||
|
applicationProgress={store.applicationProgress}
|
||||||
|
equipmentCraftingProgress={store.equipmentCraftingProgress}
|
||||||
|
/>
|
||||||
|
</DebugName>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Calendar */}
|
{/* Calendar */}
|
||||||
<DebugName name="CalendarDisplay">
|
<DebugName name="CalendarDisplay">
|
||||||
@@ -230,140 +248,166 @@ export default function ManaLoopGame() {
|
|||||||
{/* Loot and Achievements moved to tabs */}
|
{/* Loot and Achievements moved to tabs */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Panel - Tabs */}
|
{/* Right Panel - Conditional rendering based on Spire Mode */}
|
||||||
<div className="flex-1 min-w-0">
|
{store.spireMode ? (
|
||||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
/* Spire Mode - Simplified UI */
|
||||||
<TabsList className="flex flex-wrap gap-1 w-full mb-4 h-auto">
|
<div className="flex-1 min-w-0 space-y-4">
|
||||||
<TabsTrigger value="spire" className="text-xs px-2 py-1">⚔️ Spire</TabsTrigger>
|
<DebugName name="SpireModeUI">
|
||||||
<TabsTrigger value="attunements" className="text-xs px-2 py-1">✨ Attune</TabsTrigger>
|
<div className="flex items-center justify-between mb-4">
|
||||||
<TabsTrigger value="golemancy" className="text-xs px-2 py-1">🗿 Golems</TabsTrigger>
|
<h2 className="text-2xl font-bold game-title text-amber-400">
|
||||||
<TabsTrigger value="skills" className="text-xs px-2 py-1">📚 Skills</TabsTrigger>
|
🏔️ Spire Mode
|
||||||
<TabsTrigger value="spells" className="text-xs px-2 py-1">🔮 Spells</TabsTrigger>
|
</h2>
|
||||||
<TabsTrigger value="equipment" className="text-xs px-2 py-1">🛡️ Gear</TabsTrigger>
|
<Button
|
||||||
<TabsTrigger value="crafting" className="text-xs px-2 py-1">🔧 Craft</TabsTrigger>
|
variant="outline"
|
||||||
<TabsTrigger value="loot" className="text-xs px-2 py-1">💎 Loot</TabsTrigger>
|
className="border-blue-600/50 text-blue-400 hover:bg-blue-900/20"
|
||||||
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achieve</TabsTrigger>
|
onClick={() => store.exitSpireMode()}
|
||||||
<TabsTrigger value="lab" className="text-xs px-2 py-1">🔬 Lab</TabsTrigger>
|
>
|
||||||
<TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger>
|
<ChevronDown className="w-4 h-4 mr-2" />
|
||||||
<TabsTrigger value="debug" className="text-xs px-2 py-1">🔧 Debug</TabsTrigger>
|
Climb Down
|
||||||
<TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger>
|
</Button>
|
||||||
</TabsList>
|
</div>
|
||||||
|
|
||||||
<TabsContent value="spire">
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<DebugName name="SpireTab">
|
<SpireTab store={store} simpleMode={true} />
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
</Suspense>
|
||||||
<SpireTab store={store} />
|
</DebugName>
|
||||||
</Suspense>
|
</div>
|
||||||
</DebugName>
|
) : (
|
||||||
</TabsContent>
|
/* Normal Mode - Tabs */
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||||
|
<TabsList className="flex flex-wrap gap-1 w-full mb-4 h-auto">
|
||||||
|
<TabsTrigger value="spire" className="text-xs px-2 py-1">⚔️ Spire</TabsTrigger>
|
||||||
|
<TabsTrigger value="attunements" className="text-xs px-2 py-1">✨ Attune</TabsTrigger>
|
||||||
|
<TabsTrigger value="golemancy" className="text-xs px-2 py-1">🗿 Golems</TabsTrigger>
|
||||||
|
<TabsTrigger value="skills" className="text-xs px-2 py-1">📚 Skills</TabsTrigger>
|
||||||
|
<TabsTrigger value="spells" className="text-xs px-2 py-1">🔮 Spells</TabsTrigger>
|
||||||
|
<TabsTrigger value="equipment" className="text-xs px-2 py-1">🛡️ Gear</TabsTrigger>
|
||||||
|
<TabsTrigger value="crafting" className="text-xs px-2 py-1">🔧 Craft</TabsTrigger>
|
||||||
|
<TabsTrigger value="loot" className="text-xs px-2 py-1">💎 Loot</TabsTrigger>
|
||||||
|
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achieve</TabsTrigger>
|
||||||
|
<TabsTrigger value="lab" className="text-xs px-2 py-1">🔬 Lab</TabsTrigger>
|
||||||
|
<TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger>
|
||||||
|
<TabsTrigger value="debug" className="text-xs px-2 py-1">🔧 Debug</TabsTrigger>
|
||||||
|
<TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="attunements">
|
<TabsContent value="spire">
|
||||||
<DebugName name="AttunementsTab">
|
<DebugName name="SpireTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<AttunementsTab store={store} />
|
<SpireTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="golemancy">
|
<TabsContent value="attunements">
|
||||||
<DebugName name="GolemancyTab">
|
<DebugName name="AttunementsTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<GolemancyTab store={store} />
|
<AttunementsTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="skills">
|
<TabsContent value="golemancy">
|
||||||
<DebugName name="SkillsTab">
|
<DebugName name="GolemancyTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<SkillsTab store={store} />
|
<GolemancyTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="spells">
|
<TabsContent value="skills">
|
||||||
<DebugName name="SpellsTab">
|
<DebugName name="SkillsTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<SpellsTab store={store} />
|
<SkillsTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="equipment">
|
<TabsContent value="spells">
|
||||||
<DebugName name="EquipmentTab">
|
<DebugName name="SpellsTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<EquipmentTab store={store} />
|
<SpellsTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="crafting">
|
<TabsContent value="equipment">
|
||||||
<DebugName name="CraftingTab">
|
<DebugName name="EquipmentTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<CraftingTab store={store} />
|
<EquipmentTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="loot">
|
<TabsContent value="crafting">
|
||||||
<DebugName name="LootTab">
|
<DebugName name="CraftingTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<LootTab store={store} />
|
<CraftingTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="achievements">
|
<TabsContent value="loot">
|
||||||
<DebugName name="AchievementsTab">
|
<DebugName name="LootTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<AchievementsTab store={store} />
|
<LootTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="lab">
|
<TabsContent value="achievements">
|
||||||
<DebugName name="LabTab">
|
<DebugName name="AchievementsTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<LabTab store={store} />
|
<AchievementsTab store={store} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</DebugName>
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="stats">
|
<TabsContent value="lab">
|
||||||
<DebugName name="StatsTab">
|
<DebugName name="LabTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<StatsTab
|
<LabTab store={store} />
|
||||||
store={store}
|
</Suspense>
|
||||||
upgradeEffects={upgradeEffects}
|
</DebugName>
|
||||||
maxMana={maxMana}
|
</TabsContent>
|
||||||
baseRegen={baseRegen}
|
|
||||||
clickMana={clickMana}
|
|
||||||
meditationMultiplier={meditationMultiplier}
|
|
||||||
effectiveRegen={effectiveRegen}
|
|
||||||
incursionStrength={incursionStrength}
|
|
||||||
manaCascadeBonus={manaCascadeBonus}
|
|
||||||
studySpeedMult={studySpeedMult}
|
|
||||||
studyCostMult={studyCostMult}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</DebugName>
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
<TabsContent value="grimoire">
|
<TabsContent value="stats">
|
||||||
<DebugName name="GrimoireTab">
|
<DebugName name="StatsTab">
|
||||||
{renderGrimoireTab()}
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
</DebugName>
|
<StatsTab
|
||||||
</TabsContent>
|
store={store}
|
||||||
|
upgradeEffects={upgradeEffects}
|
||||||
|
maxMana={maxMana}
|
||||||
|
baseRegen={baseRegen}
|
||||||
|
clickMana={clickMana}
|
||||||
|
meditationMultiplier={meditationMultiplier}
|
||||||
|
effectiveRegen={effectiveRegen}
|
||||||
|
incursionStrength={incursionStrength}
|
||||||
|
manaCascadeBonus={manaCascadeBonus}
|
||||||
|
studySpeedMult={studySpeedMult}
|
||||||
|
studyCostMult={studyCostMult}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</DebugName>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="debug">
|
<TabsContent value="grimoire">
|
||||||
<DebugName name="DebugTab">
|
<DebugName name="GrimoireTab">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
{renderGrimoireTab()}
|
||||||
<DebugTab store={store} />
|
</DebugName>
|
||||||
</Suspense>
|
</TabsContent>
|
||||||
</DebugName>
|
|
||||||
</TabsContent>
|
<TabsContent value="debug">
|
||||||
</Tabs>
|
<DebugName name="DebugTab">
|
||||||
</div>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
|
<DebugTab store={store} />
|
||||||
|
</Suspense>
|
||||||
|
</DebugName>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
|||||||
@@ -1,86 +1,149 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Sparkles, Swords, BookOpen, Target, FlaskConical, Cog, Hammer } from 'lucide-react';
|
||||||
import { Sparkles, Swords, BookOpen, Target, FlaskConical } from 'lucide-react';
|
|
||||||
import type { GameAction } from '@/lib/game/types';
|
import type { GameAction } from '@/lib/game/types';
|
||||||
|
|
||||||
interface ActionButtonsProps {
|
interface ActionButtonsProps {
|
||||||
currentAction: GameAction;
|
currentAction: GameAction;
|
||||||
|
currentStudyTarget: { type: 'skill' | 'spell'; id: string; progress: number; required: number } | null;
|
||||||
designProgress: { progress: number; required: number } | null;
|
designProgress: { progress: number; required: number } | null;
|
||||||
|
designProgress2: { progress: number; required: number } | null;
|
||||||
preparationProgress: { progress: number; required: number } | null;
|
preparationProgress: { progress: number; required: number } | null;
|
||||||
applicationProgress: { progress: number; required: number } | null;
|
applicationProgress: { progress: number; required: number } | null;
|
||||||
setAction: (action: GameAction) => void;
|
equipmentCraftingProgress: { progress: number; required: number } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map action IDs to labels and icons
|
||||||
|
const ACTION_CONFIG: Record<string, { label: string; icon: typeof Sparkles; color: string }> = {
|
||||||
|
meditate: { label: 'Meditating', icon: Sparkles, color: 'text-blue-400' },
|
||||||
|
climb: { label: 'Climbing', icon: Swords, color: 'text-green-400' },
|
||||||
|
study: { label: 'Studying', icon: BookOpen, color: 'text-yellow-400' },
|
||||||
|
design: { label: 'Designing Enchantment', icon: Target, color: 'text-purple-400' },
|
||||||
|
prepare: { label: 'Preparing Equipment', icon: FlaskConical, color: 'text-purple-400' },
|
||||||
|
enchant: { label: 'Enchanting', icon: Sparkles, color: 'text-purple-400' },
|
||||||
|
craft: { label: 'Crafting Equipment', icon: Hammer, color: 'text-orange-400' },
|
||||||
|
convert: { label: 'Converting Mana', icon: Cog, color: 'text-cyan-400' },
|
||||||
|
};
|
||||||
|
|
||||||
|
function ProgressBar({ progress, required, label }: { progress: number; required: number; label?: string }) {
|
||||||
|
const percentage = Math.min(100, (progress / required) * 100);
|
||||||
|
return (
|
||||||
|
<div className="mt-1">
|
||||||
|
{label && <div className="text-xs text-gray-400 mb-0.5">{label}</div>}
|
||||||
|
<div className="w-full bg-gray-700 rounded-full h-1.5">
|
||||||
|
<div
|
||||||
|
className="bg-blue-500 h-1.5 rounded-full transition-all duration-300"
|
||||||
|
style={{ width: `${percentage}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ActionButtons({
|
export function ActionButtons({
|
||||||
currentAction,
|
currentAction,
|
||||||
|
currentStudyTarget,
|
||||||
designProgress,
|
designProgress,
|
||||||
|
designProgress2,
|
||||||
preparationProgress,
|
preparationProgress,
|
||||||
applicationProgress,
|
applicationProgress,
|
||||||
setAction,
|
equipmentCraftingProgress,
|
||||||
}: ActionButtonsProps) {
|
}: ActionButtonsProps) {
|
||||||
const actions: { id: GameAction; label: string; icon: typeof Swords }[] = [
|
const config = ACTION_CONFIG[currentAction] || { label: currentAction, icon: Sparkles, color: 'text-gray-400' };
|
||||||
{ id: 'meditate', label: 'Meditate', icon: Sparkles },
|
const Icon = config.icon;
|
||||||
{ id: 'climb', label: 'Climb', icon: Swords },
|
|
||||||
{ id: 'study', label: 'Study', icon: BookOpen },
|
|
||||||
];
|
|
||||||
|
|
||||||
const hasDesignProgress = designProgress !== null;
|
// Calculate additional info for specific actions
|
||||||
const hasPrepProgress = preparationProgress !== null;
|
const getActionDetails = () => {
|
||||||
const hasAppProgress = applicationProgress !== null;
|
switch (currentAction) {
|
||||||
|
case 'study':
|
||||||
|
if (currentStudyTarget) {
|
||||||
|
const progress = currentStudyTarget.progress;
|
||||||
|
const required = currentStudyTarget.required;
|
||||||
|
const percentage = Math.min(100, (progress / required) * 100);
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
progress={progress}
|
||||||
|
required={required}
|
||||||
|
label={`${currentStudyTarget.type === 'skill' ? 'Skill' : 'Spell'}: ${percentage.toFixed(0)}%`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'design':
|
||||||
|
if (designProgress) {
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
progress={designProgress.progress}
|
||||||
|
required={designProgress.required}
|
||||||
|
label="Design progress"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'prepare':
|
||||||
|
if (preparationProgress) {
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
progress={preparationProgress.progress}
|
||||||
|
required={preparationProgress.required}
|
||||||
|
label="Preparation progress"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'enchant':
|
||||||
|
if (applicationProgress) {
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
progress={applicationProgress.progress}
|
||||||
|
required={applicationProgress.required}
|
||||||
|
label="Enchantment progress"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'craft':
|
||||||
|
if (equipmentCraftingProgress) {
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
progress={equipmentCraftingProgress.progress}
|
||||||
|
required={equipmentCraftingProgress.required}
|
||||||
|
label="Crafting progress"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className="bg-gray-800/50 rounded-lg p-3 border border-gray-700">
|
||||||
{actions.map(({ id, label, icon: Icon }) => (
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Icon className={`w-4 h-4 ${config.color}`} />
|
||||||
key={id}
|
<span className="text-sm font-medium text-gray-200">Current Activity</span>
|
||||||
variant={currentAction === id ? 'default' : 'outline'}
|
|
||||||
size="sm"
|
|
||||||
className={`h-9 ${currentAction === id ? 'bg-blue-600 hover:bg-blue-700' : 'bg-gray-800/50 hover:bg-gray-700/50 border-gray-600'}`}
|
|
||||||
onClick={() => setAction(id)}
|
|
||||||
>
|
|
||||||
<Icon className="w-4 h-4 mr-1" />
|
|
||||||
{label}
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Crafting actions row - shown when there's active crafting progress */}
|
|
||||||
{(hasDesignProgress || hasPrepProgress || hasAppProgress) && (
|
|
||||||
<div className="grid grid-cols-3 gap-2">
|
|
||||||
<Button
|
|
||||||
variant={currentAction === 'design' ? 'default' : 'outline'}
|
|
||||||
size="sm"
|
|
||||||
disabled={!hasDesignProgress}
|
|
||||||
className={`h-9 ${currentAction === 'design' ? 'bg-purple-600 hover:bg-purple-700' : 'bg-gray-800/50 hover:bg-gray-700/50 border-gray-600'}`}
|
|
||||||
onClick={() => hasDesignProgress && setAction('design')}
|
|
||||||
>
|
|
||||||
<Target className="w-4 h-4 mr-1" />
|
|
||||||
Design
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={currentAction === 'prepare' ? 'default' : 'outline'}
|
|
||||||
size="sm"
|
|
||||||
disabled={!hasPrepProgress}
|
|
||||||
className={`h-9 ${currentAction === 'prepare' ? 'bg-purple-600 hover:bg-purple-700' : 'bg-gray-800/50 hover:bg-gray-700/50 border-gray-600'}`}
|
|
||||||
onClick={() => hasPrepProgress && setAction('prepare')}
|
|
||||||
>
|
|
||||||
<FlaskConical className="w-4 h-4 mr-1" />
|
|
||||||
Prepare
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={currentAction === 'enchant' ? 'default' : 'outline'}
|
|
||||||
size="sm"
|
|
||||||
disabled={!hasAppProgress}
|
|
||||||
className={`h-9 ${currentAction === 'enchant' ? 'bg-purple-600 hover:bg-purple-700' : 'bg-gray-800/50 hover:bg-gray-700/50 border-gray-600'}`}
|
|
||||||
onClick={() => hasAppProgress && setAction('enchant')}
|
|
||||||
>
|
|
||||||
<Sparkles className="w-4 h-4 mr-1" />
|
|
||||||
Enchant
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className={`text-lg font-semibold mt-1 ${config.color}`}>
|
||||||
|
{config.label}
|
||||||
|
</div>
|
||||||
|
{getActionDetails()}
|
||||||
|
|
||||||
|
{/* Show second design slot if active */}
|
||||||
|
{designProgress2 && (
|
||||||
|
<div className="mt-2 pt-2 border-t border-gray-700">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Target className="w-3 h-3 text-purple-400" />
|
||||||
|
<span className="text-xs text-gray-400">Second Design Slot</span>
|
||||||
|
</div>
|
||||||
|
<ProgressBar
|
||||||
|
progress={designProgress2.progress}
|
||||||
|
required={designProgress2.required}
|
||||||
|
label="Design progress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ import { getUnifiedEffects } from '@/lib/game/effects';
|
|||||||
|
|
||||||
interface SpireTabProps {
|
interface SpireTabProps {
|
||||||
store: GameStore;
|
store: GameStore;
|
||||||
|
simpleMode?: boolean; // When true, only show essential Spire info (for Spire Mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SpireTab({ store }: SpireTabProps) {
|
export function SpireTab({ store, simpleMode = false }: SpireTabProps) {
|
||||||
const floorElem = getFloorElement(store.currentFloor);
|
const floorElem = getFloorElement(store.currentFloor);
|
||||||
const floorElemDef = ELEMENTS[floorElem];
|
const floorElemDef = ELEMENTS[floorElem];
|
||||||
const isGuardianFloor = !!GUARDIANS[store.currentFloor];
|
const isGuardianFloor = !!GUARDIANS[store.currentFloor];
|
||||||
@@ -48,7 +49,7 @@ export function SpireTab({ store }: SpireTabProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
<div className={`grid gap-4 ${simpleMode ? 'grid-cols-1' : 'grid-cols-1 lg:grid-cols-2'}`}>
|
||||||
{/* Current Floor Card */}
|
{/* Current Floor Card */}
|
||||||
<Card className="bg-gray-900/80 border-gray-700">
|
<Card className="bg-gray-900/80 border-gray-700">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
@@ -92,35 +93,39 @@ export function SpireTab({ store }: SpireTabProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator className="bg-gray-700" />
|
{!simpleMode && (
|
||||||
|
<>
|
||||||
|
<Separator className="bg-gray-700" />
|
||||||
|
|
||||||
{/* Floor Navigation - Direction indicator only */}
|
{/* Floor Navigation - Direction indicator only */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-xs text-gray-400">Direction</span>
|
<span className="text-xs text-gray-400">Direction</span>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<Badge variant={climbDirection === 'up' ? 'default' : 'outline'}
|
<Badge variant={climbDirection === 'up' ? 'default' : 'outline'}
|
||||||
className={climbDirection === 'up' ? 'bg-green-600' : ''}>
|
className={climbDirection === 'up' ? 'bg-green-600' : ''}>
|
||||||
<ChevronUp className="w-3 h-3 mr-1" />
|
<ChevronUp className="w-3 h-3 mr-1" />
|
||||||
Up
|
Up
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge variant={climbDirection === 'down' ? 'default' : 'outline'}
|
<Badge variant={climbDirection === 'down' ? 'default' : 'outline'}
|
||||||
className={climbDirection === 'down' ? 'bg-blue-600' : ''}>
|
className={climbDirection === 'down' ? 'bg-blue-600' : ''}>
|
||||||
<ChevronDown className="w-3 h-3 mr-1" />
|
<ChevronDown className="w-3 h-3 mr-1" />
|
||||||
Down
|
Down
|
||||||
</Badge>
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isFloorCleared && (
|
||||||
|
<div className="text-xs text-amber-400 text-center flex items-center justify-center gap-1">
|
||||||
|
<RotateCcw className="w-3 h-3" />
|
||||||
|
Floor cleared! Advancing...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{isFloorCleared && (
|
<Separator className="bg-gray-700" />
|
||||||
<div className="text-xs text-amber-400 text-center flex items-center justify-center gap-1">
|
</>
|
||||||
<RotateCcw className="w-3 h-3" />
|
)}
|
||||||
Floor cleared! Advancing...
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Separator className="bg-gray-700" />
|
|
||||||
|
|
||||||
<div className="text-sm text-gray-400">
|
<div className="text-sm text-gray-400">
|
||||||
Best: Floor <strong className="text-gray-200">{store.maxFloorReached}</strong> •
|
Best: Floor <strong className="text-gray-200">{store.maxFloorReached}</strong> •
|
||||||
@@ -205,8 +210,8 @@ export function SpireTab({ store }: SpireTabProps) {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Summoned Golems Card */}
|
{/* Summoned Golems Card - Always show in simple mode, conditional in normal mode */}
|
||||||
{store.golemancy.summonedGolems.length > 0 && (
|
{(simpleMode || store.golemancy.summonedGolems.length > 0) && (
|
||||||
<Card className="bg-gray-900/80 border-amber-600/50">
|
<Card className="bg-gray-900/80 border-amber-600/50">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-amber-400 game-panel-title text-xs flex items-center gap-2">
|
<CardTitle className="text-amber-400 game-panel-title text-xs flex items-center gap-2">
|
||||||
@@ -257,8 +262,8 @@ export function SpireTab({ store }: SpireTabProps) {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Current Study (if any) */}
|
{/* Current Study (if any) - Only show in normal mode */}
|
||||||
{store.currentStudyTarget && (
|
{!simpleMode && store.currentStudyTarget && (
|
||||||
<Card className="bg-gray-900/80 border-purple-600/50 lg:col-span-2">
|
<Card className="bg-gray-900/80 border-purple-600/50 lg:col-span-2">
|
||||||
<CardContent className="pt-4 space-y-3">
|
<CardContent className="pt-4 space-y-3">
|
||||||
<StudyProgress
|
<StudyProgress
|
||||||
@@ -299,8 +304,8 @@ export function SpireTab({ store }: SpireTabProps) {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Crafting Progress (if any) */}
|
{/* Crafting Progress (if any) - Only show in normal mode */}
|
||||||
{(store.designProgress || store.preparationProgress || store.applicationProgress) && (
|
{!simpleMode && (store.designProgress || store.preparationProgress || store.applicationProgress) && (
|
||||||
<Card className="bg-gray-900/80 border-cyan-600/50 lg:col-span-2">
|
<Card className="bg-gray-900/80 border-cyan-600/50 lg:col-span-2">
|
||||||
<CardContent className="pt-4">
|
<CardContent className="pt-4">
|
||||||
<CraftingProgress
|
<CraftingProgress
|
||||||
@@ -319,26 +324,28 @@ export function SpireTab({ store }: SpireTabProps) {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Activity Log */}
|
{/* Activity Log - Only show in normal mode */}
|
||||||
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
|
{!simpleMode && (
|
||||||
<CardHeader className="pb-2">
|
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
|
||||||
<CardTitle className="text-amber-400 game-panel-title text-xs">Activity Log</CardTitle>
|
<CardHeader className="pb-2">
|
||||||
</CardHeader>
|
<CardTitle className="text-amber-400 game-panel-title text-xs">Activity Log</CardTitle>
|
||||||
<CardContent>
|
</CardHeader>
|
||||||
<ScrollArea className="h-32">
|
<CardContent>
|
||||||
<div className="space-y-1">
|
<ScrollArea className="h-32">
|
||||||
{store.log.slice(0, 20).map((entry, i) => (
|
<div className="space-y-1">
|
||||||
<div
|
{store.log.slice(0, 20).map((entry, i) => (
|
||||||
key={i}
|
<div
|
||||||
className={`text-sm ${i === 0 ? 'text-gray-200' : 'text-gray-500'} italic`}
|
key={i}
|
||||||
>
|
className={`text-sm ${i === 0 ? 'text-gray-200' : 'text-gray-500'} italic`}
|
||||||
{entry}
|
>
|
||||||
</div>
|
{entry}
|
||||||
))}
|
</div>
|
||||||
</div>
|
))}
|
||||||
</ScrollArea>
|
</div>
|
||||||
</CardContent>
|
</ScrollArea>
|
||||||
</Card>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
+43
-33
@@ -749,6 +749,9 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
|
|||||||
|
|
||||||
// Mana Well Effects (Phase 4)
|
// Mana Well Effects (Phase 4)
|
||||||
manaHeartBonus: manaHeartBonus, // Cumulative +10% max mana per loop from MANA_HEART
|
manaHeartBonus: manaHeartBonus, // Cumulative +10% max mana per loop from MANA_HEART
|
||||||
|
|
||||||
|
// Spire Mode - simplified UI for climbing
|
||||||
|
spireMode: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,6 +804,10 @@ interface GameStore extends GameState, CraftingActions {
|
|||||||
getMeditationMultiplier: () => number;
|
getMeditationMultiplier: () => number;
|
||||||
canCastSpell: (spellId: string) => boolean;
|
canCastSpell: (spellId: string) => boolean;
|
||||||
getSkillUpgradeChoices: (skillId: string, milestone: 5 | 10) => { available: SkillUpgradeChoice[]; selected: string[] };
|
getSkillUpgradeChoices: (skillId: string, milestone: 5 | 10) => { available: SkillUpgradeChoice[]; selected: string[] };
|
||||||
|
|
||||||
|
// Spire Mode actions
|
||||||
|
enterSpireMode: () => void;
|
||||||
|
exitSpireMode: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGameStore = create<GameStore>()(
|
export const useGameStore = create<GameStore>()(
|
||||||
@@ -834,6 +841,9 @@ export const useGameStore = create<GameStore>()(
|
|||||||
// Compute unified effects (includes skill upgrades AND equipment enchantments)
|
// Compute unified effects (includes skill upgrades AND equipment enchantments)
|
||||||
const effects = getUnifiedEffects(state);
|
const effects = getUnifiedEffects(state);
|
||||||
|
|
||||||
|
// Track current action for potential auto-transitions
|
||||||
|
let currentAction = state.currentAction;
|
||||||
|
|
||||||
const maxMana = computeMaxMana(state, effects);
|
const maxMana = computeMaxMana(state, effects);
|
||||||
const baseRegen = computeRegen(state, effects);
|
const baseRegen = computeRegen(state, effects);
|
||||||
|
|
||||||
@@ -878,7 +888,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
let meditateTicks = state.meditateTicks;
|
let meditateTicks = state.meditateTicks;
|
||||||
let meditationMultiplier = 1;
|
let meditationMultiplier = 1;
|
||||||
|
|
||||||
if (state.currentAction === 'meditate') {
|
if (currentAction === 'meditate') {
|
||||||
meditateTicks++;
|
meditateTicks++;
|
||||||
meditationMultiplier = getMeditationBonus(meditateTicks, state.skills);
|
meditationMultiplier = getMeditationBonus(meditateTicks, state.skills);
|
||||||
|
|
||||||
@@ -973,7 +983,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
let unlockedEffects = state.unlockedEffects;
|
let unlockedEffects = state.unlockedEffects;
|
||||||
let consecutiveStudyHours = state.consecutiveStudyHours;
|
let consecutiveStudyHours = state.consecutiveStudyHours;
|
||||||
|
|
||||||
if (state.currentAction === 'study' && currentStudyTarget) {
|
if (currentAction === 'study' && currentStudyTarget) {
|
||||||
// Calculate base study speed
|
// Calculate base study speed
|
||||||
let studySpeedMult = getStudySpeedMultiplier(skills);
|
let studySpeedMult = getStudySpeedMultiplier(skills);
|
||||||
|
|
||||||
@@ -1076,11 +1086,13 @@ export const useGameStore = create<GameStore>()(
|
|||||||
log = [`📖 ${SPELLS_DEF[spellId]?.name} learned!`, ...log.slice(0, 49)];
|
log = [`📖 ${SPELLS_DEF[spellId]?.name} learned!`, ...log.slice(0, 49)];
|
||||||
}
|
}
|
||||||
currentStudyTarget = null;
|
currentStudyTarget = null;
|
||||||
|
// Auto-transition to meditate when study completes
|
||||||
|
currentAction = 'meditate';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Parallel Study processing (PARALLEL_STUDY special effect)
|
// Parallel Study processing (PARALLEL_STUDY special effect)
|
||||||
let parallelStudyTarget = state.parallelStudyTarget;
|
let parallelStudyTarget = state.parallelStudyTarget;
|
||||||
if (parallelStudyTarget && state.currentAction === 'study') {
|
if (parallelStudyTarget && currentAction === 'study') {
|
||||||
// Parallel study progresses at 50% speed
|
// Parallel study progresses at 50% speed
|
||||||
const parallelProgressGain = HOURS_PER_TICK * 0.5;
|
const parallelProgressGain = HOURS_PER_TICK * 0.5;
|
||||||
parallelStudyTarget = {
|
parallelStudyTarget = {
|
||||||
@@ -1101,7 +1113,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert action - auto convert mana
|
// Convert action - auto convert mana
|
||||||
if (state.currentAction === 'convert') {
|
if (currentAction === 'convert') {
|
||||||
const unlockedElements = Object.entries(elements)
|
const unlockedElements = Object.entries(elements)
|
||||||
.filter(([, e]) => e.unlocked && e.current < e.max);
|
.filter(([, e]) => e.unlocked && e.current < e.max);
|
||||||
|
|
||||||
@@ -1130,7 +1142,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
const floorElement = getFloorElement(currentFloor);
|
const floorElement = getFloorElement(currentFloor);
|
||||||
|
|
||||||
// Handle puzzle rooms separately
|
// Handle puzzle rooms separately
|
||||||
if (state.currentAction === 'climb' && currentRoom.roomType === 'puzzle') {
|
if (currentAction === 'climb' && currentRoom.roomType === 'puzzle') {
|
||||||
const progressSpeed = getPuzzleProgressSpeed(
|
const progressSpeed = getPuzzleProgressSpeed(
|
||||||
currentRoom.puzzleId || '',
|
currentRoom.puzzleId || '',
|
||||||
state.attunements
|
state.attunements
|
||||||
@@ -1154,7 +1166,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
maxFloorReached = Math.max(maxFloorReached, currentFloor);
|
maxFloorReached = Math.max(maxFloorReached, currentFloor);
|
||||||
castProgress = 0;
|
castProgress = 0;
|
||||||
}
|
}
|
||||||
} else if (state.currentAction === 'climb') {
|
} else if (currentAction === 'climb') {
|
||||||
const spellId = state.activeSpell;
|
const spellId = state.activeSpell;
|
||||||
const spellDef = SPELLS_DEF[spellId];
|
const spellDef = SPELLS_DEF[spellId];
|
||||||
|
|
||||||
@@ -1358,7 +1370,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
const floorChanged = currentFloor !== golemancy.lastSummonFloor;
|
const floorChanged = currentFloor !== golemancy.lastSummonFloor;
|
||||||
const inCombatRoom = currentRoom.roomType !== 'puzzle';
|
const inCombatRoom = currentRoom.roomType !== 'puzzle';
|
||||||
|
|
||||||
if (state.currentAction === 'climb' && inCombatRoom && floorChanged && maxGolemSlots > 0) {
|
if (currentAction === 'climb' && inCombatRoom && floorChanged && maxGolemSlots > 0) {
|
||||||
// Determine which golems should be summoned
|
// Determine which golems should be summoned
|
||||||
const unlockedElementIds = Object.entries(elements)
|
const unlockedElementIds = Object.entries(elements)
|
||||||
.filter(([, e]) => e.unlocked)
|
.filter(([, e]) => e.unlocked)
|
||||||
@@ -1406,7 +1418,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process golem maintenance and attacks each tick
|
// Process golem maintenance and attacks each tick
|
||||||
if (golemancy.summonedGolems.length > 0 && state.currentAction === 'climb' && inCombatRoom) {
|
if (golemancy.summonedGolems.length > 0 && currentAction === 'climb' && inCombatRoom) {
|
||||||
const floorDuration = getGolemFloorDuration(skills);
|
const floorDuration = getGolemFloorDuration(skills);
|
||||||
const survivingGolems: typeof golemancy.summonedGolems = [];
|
const survivingGolems: typeof golemancy.summonedGolems = [];
|
||||||
let anyGolemDismissed = false;
|
let anyGolemDismissed = false;
|
||||||
@@ -1525,7 +1537,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unsummon golems when not climbing or in puzzle room
|
// Unsummon golems when not climbing or in puzzle room
|
||||||
if ((state.currentAction !== 'climb' || !inCombatRoom) && golemancy.summonedGolems.length > 0) {
|
if ((currentAction !== 'climb' || !inCombatRoom) && golemancy.summonedGolems.length > 0) {
|
||||||
log = [`🗿 Golems returned to the earth.`, ...log.slice(0, 49)];
|
log = [`🗿 Golems returned to the earth.`, ...log.slice(0, 49)];
|
||||||
golemancy = {
|
golemancy = {
|
||||||
...golemancy,
|
...golemancy,
|
||||||
@@ -1559,31 +1571,10 @@ export const useGameStore = create<GameStore>()(
|
|||||||
// Apply crafting updates
|
// Apply crafting updates
|
||||||
if (craftingUpdates.rawMana !== undefined) rawMana = craftingUpdates.rawMana;
|
if (craftingUpdates.rawMana !== undefined) rawMana = craftingUpdates.rawMana;
|
||||||
if (craftingUpdates.log !== undefined) log = craftingUpdates.log;
|
if (craftingUpdates.log !== undefined) log = craftingUpdates.log;
|
||||||
|
|
||||||
|
// If crafting slice set currentAction (e.g., auto-transition to meditate), use it
|
||||||
if (craftingUpdates.currentAction !== undefined) {
|
if (craftingUpdates.currentAction !== undefined) {
|
||||||
set({
|
currentAction = craftingUpdates.currentAction;
|
||||||
...craftingUpdates,
|
|
||||||
day,
|
|
||||||
hour,
|
|
||||||
rawMana,
|
|
||||||
meditateTicks,
|
|
||||||
totalManaGathered,
|
|
||||||
currentFloor,
|
|
||||||
floorHP,
|
|
||||||
floorMaxHP,
|
|
||||||
maxFloorReached,
|
|
||||||
signedPacts,
|
|
||||||
currentRoom,
|
|
||||||
incursionStrength,
|
|
||||||
currentStudyTarget,
|
|
||||||
skills,
|
|
||||||
skillProgress,
|
|
||||||
spells,
|
|
||||||
elements,
|
|
||||||
log,
|
|
||||||
castProgress,
|
|
||||||
golemancy,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set({
|
set({
|
||||||
@@ -1599,6 +1590,7 @@ export const useGameStore = create<GameStore>()(
|
|||||||
signedPacts,
|
signedPacts,
|
||||||
currentRoom,
|
currentRoom,
|
||||||
incursionStrength,
|
incursionStrength,
|
||||||
|
currentAction,
|
||||||
currentStudyTarget,
|
currentStudyTarget,
|
||||||
parallelStudyTarget,
|
parallelStudyTarget,
|
||||||
skills,
|
skills,
|
||||||
@@ -1929,6 +1921,24 @@ export const useGameStore = create<GameStore>()(
|
|||||||
set(newState);
|
set(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Spire Mode - enter simplified UI for climbing
|
||||||
|
enterSpireMode: () => {
|
||||||
|
set((state) => ({
|
||||||
|
spireMode: true,
|
||||||
|
currentAction: 'climb',
|
||||||
|
log: ['🏔️ Entered Spire Mode! The climb begins...', ...state.log.slice(0, 49)],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Exit Spire Mode - return to normal game UI
|
||||||
|
exitSpireMode: () => {
|
||||||
|
set((state) => ({
|
||||||
|
spireMode: false,
|
||||||
|
currentAction: 'meditate',
|
||||||
|
log: ['⬇️ Climbed down from the Spire. Returning to normal view.', ...state.log.slice(0, 49)],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
togglePause: () => {
|
togglePause: () => {
|
||||||
set((state) => ({ paused: !state.paused }));
|
set((state) => ({ paused: !state.paused }));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -212,6 +212,9 @@ export interface GameState {
|
|||||||
|
|
||||||
// Loop insight (earned at end of current loop)
|
// Loop insight (earned at end of current loop)
|
||||||
loopInsight: number;
|
loopInsight: number;
|
||||||
|
|
||||||
|
// Spire Mode - simplified UI for climbing
|
||||||
|
spireMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Action Types for Store ─────────────────────────────────────────────
|
// ─── Action Types for Store ─────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user