feat: Recreate Spire Combat Page — full spire climbing experience
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s

- Add guardian-encounters.ts: Extended guardian definitions for all mana types (compound, exotic, combo) with dynamic name generation
- Add spire-utils.ts: Spire-specific utilities (room generation, enemy stat scaling, insight calculation)
- Add enemy-generator.ts: Enemy generation with combinable modifiers (mage, shield, armored, swarm, agile)
- Add SpireCombatPage/ directory with modular sub-components:
  - SpireHeader.tsx: Floor info, climb controls, exit button, HP/room progress bars
  - RoomDisplay.tsx: Current room info with enemies, barriers, armor, dodge stats
  - SpireCombatControls.tsx: Spell selection panel, golem status panel
  - SpireActivityLog.tsx: Combat activity log
  - SpireManaDisplay.tsx: Compact mana display with elemental pools
- Modify page.tsx: Conditionally render SpireCombatPage when spireMode is true
- Add comprehensive tests (49 tests) for spire utilities, guardian encounters, and enemy generation
This commit is contained in:
2026-05-20 09:28:05 +02:00
parent 1c7fc8c551
commit 7d56fc368f
17 changed files with 2004 additions and 5 deletions
@@ -0,0 +1,43 @@
'use client';
import type { ActivityLogEntry } from '@/lib/game/types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { ScrollArea } from '@/components/ui/scroll-area';
interface SpireActivityLogProps {
activityLog: ActivityLogEntry[];
maxEntries?: number;
}
export function SpireActivityLog({ activityLog, maxEntries = 30 }: SpireActivityLogProps) {
const entries = activityLog.slice(0, maxEntries);
return (
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-sm text-gray-400">📜 Activity Log</CardTitle>
</CardHeader>
<CardContent>
<ScrollArea className="h-48">
{entries.length === 0 ? (
<div className="text-xs text-gray-500 italic">No activity yet.</div>
) : (
<div className="space-y-1">
{entries.map((entry) => (
<div
key={entry.id}
className="text-xs text-gray-300 border-b border-gray-800 pb-1 last:border-0"
>
<span className="text-gray-600 mr-1">
[{entry.eventType}]
</span>
{entry.message}
</div>
))}
</div>
)}
</ScrollArea>
</CardContent>
</Card>
);
}