# Context: Task5 (2a Floor Rendering & Identity) ## Floor Type Definitions ### Room Types (from `src/lib/game/constants/rooms.ts` and `src/lib/game/types/game.ts`) ```typescript // Room types for spire floors export type RoomType = 'combat' | 'puzzle' | 'swarm' | 'speed' | 'guardian'; // Room generation rules: // - Guardian floors (10, 20, 30, etc.) are ALWAYS guardian type // - Every 5th floor (5, 15, 25, etc.) has a chance for special rooms // - Other floors are combat with chance for swarm/speed export const PUZZLE_ROOM_INTERVAL = 7; // Every 7 floors, chance for puzzle export const SWARM_ROOM_CHANCE = 0.15; // 15% chance for swarm room export const SPEED_ROOM_CHANCE = 0.10; // 10% chance for speed room export const PUZZLE_ROOM_CHANCE = 0.20; // 20% chance for puzzle room on puzzle floors ``` ### Swarm Room Configuration ```typescript // Swarm room configuration export const SWARM_CONFIG = { minEnemies: 3, maxEnemies: 6, hpMultiplier: 0.4, // Each enemy has 40% of normal floor HP armorBase: 0, // Swarm enemies start with no armor armorPerFloor: 0.01, // Gain 1% armor per 10 floors }; ``` ### Speed Room Configuration ```typescript // Speed room configuration (dodging enemies) export const SPEED_ROOM_CONFIG = { baseDodgeChance: 0.25, // 25% base dodge chance dodgePerFloor: 0.005, // +0.5% dodge per floor maxDodge: 0.50, // Max 50% dodge speedBonus: 0.5, // 50% less time to complete if dodged }; ``` ### Floor Armor Configuration ```typescript // Armor scaling for normal floors export const FLOOR_ARMOR_CONFIG = { baseChance: 0, // No armor on floor 1-9 chancePerFloor: 0.01, // +1% chance per floor after 10 maxArmorChance: 0.5, // Max 50% of floors have armor minArmor: 0.05, // Min 5% armor maxArmor: 0.25, // Max 25% armor on non-guardians }; ``` ### Puzzle Room Definitions ```typescript // Puzzle room definitions - themed around attunements export const PUZZLE_ROOMS: Record = { enchanter_trial: { name: "Enchanter's Trial", attunements: ['enchanter'], baseProgressPerTick: 0.02, attunementBonus: 0.03, description: "Decipher ancient enchantment runes." }, fabricator_trial: { name: "Fabricator's Trial", attunements: ['fabricator'], baseProgressPerTick: 0.02, attunementBonus: 0.03, description: "Construct a mana-powered mechanism." }, invoker_trial: { name: "Invoker's Trial", attunements: ['invoker'], baseProgressPerTick: 0.02, attunementBonus: 0.03, description: "Commune with guardian spirits." }, // ... hybrid rooms also defined }; ``` ### Guardian Definitions (from `src/lib/game/constants/guardians.ts`) ```typescript // All guardians have armor - damage reduction percentage export const GUARDIANS: Record = { 10: { name: "Ignis Prime", element: "fire", hp: 5000, pact: 1.5, color: "#FF6B35", armor: 0.10, // 10% damage reduction boons: [ { type: 'elementalDamage', value: 5, desc: '+5% Fire damage' }, { type: 'maxMana', value: 50, desc: '+50 max mana' }, ], pactCost: 500, pactTime: 2, uniquePerk: "Fire spells cast 10% faster" }, 20: { name: "Aqua Regia", element: "water", hp: 15000, pact: 1.75, color: "#4ECDC4", armor: 0.15, ... }, 30: { name: "Ventus Rex", element: "air", hp: 30000, pact: 2.0, color: "#00D4FF", armor: 0.18, ... }, 40: { name: "Terra Firma", element: "earth", hp: 50000, pact: 2.25, color: "#F4A261", armor: 0.25, ... }, 50: { name: "Lux Aeterna", element: "light", hp: 80000, pact: 2.5, color: "#FFD700", armor: 0.20, ... }, 60: { name: "Umbra Mortis", element: "dark", hp: 120000, pact: 2.75, color: "#9B59B6", armor: 0.22, ... }, 80: { name: "Mors Ultima", element: "death", hp: 250000, pact: 3.25, color: "#778CA3", armor: 0.25, ... }, 90: { name: "Primordialis", element: "void", hp: 400000, pact: 4.0, color: "#4A235A", armor: 0.30, ... }, 100: { name: "The Awakened One", element: "stellar", hp: 1000000, pact: 5.0, color: "#F0E68C", armor: 0.35, ... }, }; ``` ### GuardianDef Type (from `src/lib/game/types/attunements.ts`) ```typescript export interface GuardianDef { name: string; element: string; hp: number; pact: number; // Pact multiplier when signed color: string; boons: GuardianBoon[]; // Bonuses granted when pact is signed pactCost: number; // Mana cost to perform pact ritual pactTime: number; // Hours required for pact ritual uniquePerk: string; // Description of unique perk armor?: number; // Damage reduction (0-1, e.g., 0.2 = 20% reduction) } export interface GuardianBoon { type: 'maxMana' | 'manaRegen' | 'castingSpeed' | 'elementalDamage' | 'rawDamage' | 'critChance' | 'critDamage' | 'spellEfficiency' | 'manaGain' | 'insightGain' | 'studySpeed' | 'prestigeInsight'; value: number; desc: string; } ``` ### Element Definitions (from `src/lib/game/constants/elements.ts`) ```typescript export const ELEMENTS: Record = { // Base Elements fire: { name: "Fire", sym: "🔥", color: "#FF6B35", glow: "#FF6B3540", cat: "base" }, water: { name: "Water", sym: "💧", color: "#4ECDC4", glow: "#4ECDC440", cat: "base" }, air: { name: "Air", sym: "🌬️", color: "#00D4FF", glow: "#00D4FF40", cat: "base" }, earth: { name: "Earth", sym: "⛰️", color: "#F4A261", glow: "#F4A26140", cat: "base" }, light: { name: "Light", sym: "☀️", color: "#FFD700", glow: "#FFD70040", cat: "base" }, dark: { name: "Dark", sym: "🌑", color: "#9B59B6", glow: "#9B59B640", cat: "base" }, death: { name: "Death", sym: "💀", color: "#778CA3", glow: "#778CA340", cat: "base" }, // ... other elements }; export const FLOOR_ELEM_CYCLE = ["fire", "water", "air", "earth", "light", "dark", "death"]; ``` ### Room Type Labels (from `src/lib/game/constants/index.ts`) ```typescript export const ROOM_TYPE_LABELS: Record = { combat: { label: 'Combat', icon: '⚔️', color: '#EF4444' }, swarm: { label: 'Swarm', icon: '🐝', color: '#F59E0B' }, speed: { label: 'Speed', icon: '💨', color: '#3B82F6' }, guardian: { label: 'Guardian', icon: '🛡️', color: '#EF4444' }, puzzle: { label: 'Puzzle', icon: '🧩', color: '#8B5CF6' }, }; ``` --- ## Current Floor Rendering Code ### SpireTab.tsx (from `src/components/game/tabs/SpireTab.tsx`) #### Room Type Display Configuration ```typescript // Room type configurations for display const ROOM_TYPE_CONFIG: Record = { combat: { label: 'Combat', icon: '⚔️', color: '#EF4444' }, swarm: { label: 'Swarm', icon: '🐝', color: '#F59E0B' }, speed: { label: 'Speed', icon: '💨', color: '#3B82F6' }, guardian: { label: 'Guardian', icon: '🛡️', color: '#EF4444' }, puzzle: { label: 'Puzzle', icon: '🧩', color: '#8B5CF6' }, }; ``` #### Floor Type Badge Rendering ```tsx {roomConfig.icon} {roomConfig.label} ``` #### Guardian Name Display ```tsx {isGuardianFloor && currentGuardian && (
⚔️ {currentGuardian.name}
)} ``` #### Single Enemy Display (Combat/Speed/Guardian) ```tsx {!isGuardianFloor && primaryEnemy && roomType !== 'swarm' && (
{primaryEnemy.name || 'Unknown Enemy'}
{ELEMENTS[primaryEnemy.element]?.sym} {ELEMENTS[primaryEnemy.element]?.name}
{/* Enemy HP Bar */}
{fmt(primaryEnemy.hp)} / {fmt(primaryEnemy.maxHP)} HP
{/* Enemy Properties */}
{primaryEnemy.armor > 0 && ( {(primaryEnemy.armor * 100).toFixed(0)}% Armor

Reduces incoming damage by {(primaryEnemy.armor * 100).toFixed(0)}%

)} {primaryEnemy.dodgeChance > 0 && ( {(primaryEnemy.dodgeChance * 100).toFixed(0)}% Dodge

Chance to dodge attacks and reduce progress

)}
)} ``` #### Swarm Enemies Display ```tsx {roomType === 'swarm' && swarmEnemies.length > 0 && (
Swarm Enemies ({swarmEnemies.length})
{swarmEnemies.map((enemy, index) => (
{enemy.name || `Enemy ${index + 1}`}
{ELEMENTS[enemy.element]?.sym} {fmt(enemy.hp)}/{fmt(enemy.maxHP)} HP
))}
)} ``` #### Puzzle Room Display ```tsx {roomType === 'puzzle' && (
🧩 {currentRoom.puzzleId ? currentRoom.puzzleId.replace(/_/g, ' ').toUpperCase() : 'Puzzle Room'}
Progress {((currentRoom.puzzleProgress || 0) * 100).toFixed(0)}%
)} ``` --- ## Enemy Naming Logic ### Enemy Name Generation (from `src/lib/game/store.ts`) ```typescript // Generate enemy names based on element and floor tier const ENEMY_NAMES_BY_ELEMENT: Record = { fire: ['Fire Imp', 'Flame Sprite', 'Emberling', 'Scorchling', 'Inferno Whelp'], water: ['Water Elemental', 'Tidal Wraith', 'Aqua Sprite', 'Drowned One', 'Tsunami Spawn'], air: ['Wind Sylph', 'Gale Rider', 'Storm Spirit', 'Zephyr Darter', 'Cyclone Wisp'], earth: ['Stone Golem', 'Earth Elemental', 'Graveling', 'Mountain Giant', 'Terra Brute'], light: ['Light Saint', 'Radiant Angel', 'Luminous Spirit', 'Divine Warden', 'Holy Sentinel'], dark: ['Shadow Assassin', 'Dark Cultist', 'Umbral Fiend', 'Void Walker', 'Night Stalker'], death: ['Skeleton Warrior', 'Zombie Lord', 'Lichling', 'Bone Reaper', 'Necrotic Wraith'], // Special element names lightning: ['Storm Elemental', 'Thunder Hawk', 'Lightning Eel', 'Shock Sprite', 'Voltaic Wisp'], metal: ['Iron Golem', 'Steel Guardian', 'Rust Monster', 'Chrome Beetle', 'Mercury Spirit'], sand: ['Sand Wraith', 'Dune Stalker', 'Desert Spirit', 'Cactus Thrasher', 'Mirage Runner'], crystal: ['Crystal Guardian', 'Prism Sprite', 'Gem Hound', 'Diamond Golem', 'Shardling'], stellar: ['Star Spawn', 'Cosmic Entity', 'Nova Spirit', 'Astral Watcher', 'Supernova Seed'], void: ['Void Lord', 'Abyssal Horror', 'Entropy Spawn', 'Chaos Elemental', 'Nether Beast'], }; // Get enemy name based on element and floor tier (1-100) export function getEnemyName(element: string, floor: number): string { const names = ENEMY_NAMES_BY_ELEMENT[element] || ['Unknown Entity']; // Higher floors get "stronger" sounding names (pick from later in the list) const tierIndex = Math.min(names.length - 1, Math.floor(floor / 20)); const randomIndex = (tierIndex + Math.floor(Math.random() * (names.length - tierIndex))) % names.length; return names[randomIndex!]; } ``` ### Enemy State Type (from `src/lib/game/types/game.ts`) ```typescript export interface EnemyState { id: string; name: string; // Display name for the enemy hp: number; maxHP: number; armor: number; // Damage reduction (0-1) dodgeChance: number; // For speed rooms (0-1) element: string; } ``` ### Floor State Type ```typescript export interface FloorState { roomType: RoomType; enemies: EnemyState[]; // For swarm rooms, multiple enemies puzzleProgress?: number; // For puzzle rooms (0-1) puzzleRequired?: number; // Total progress needed puzzleId?: string; // Which puzzle type puzzleAttunements?: string[]; // Which attunements speed up this puzzle } ``` ### Floor Generation Functions (from `src/lib/game/store.ts`) ```typescript // Generate room type for a floor export function generateRoomType(floor: number): RoomType { // Guardian floors are always guardian type if (GUARDIANS[floor]) { return 'guardian'; } // Check for puzzle room (every PUZZLE_ROOM_INTERVAL floors) if (floor % PUZZLE_ROOM_INTERVAL === 0 && Math.random() < PUZZLE_ROOM_CHANCE) { return 'puzzle'; } // Check for swarm room if (Math.random() < SWARM_ROOM_CHANCE) { return 'swarm'; } // Check for speed room if (Math.random() < SPEED_ROOM_CHANCE) { return 'speed'; } // Default to combat return 'combat'; } // Get armor for a non-guardian floor export function getFloorArmor(floor: number): number { if (GUARDIANS[floor]) { return GUARDIANS[floor].armor || 0; } // Armor becomes more common on higher floors if (floor < 10) return 0; const armorChance = Math.min(FLOOR_ARMOR_CONFIG.maxArmorChance, FLOOR_ARMOR_CONFIG.baseChance + (floor - 10) * FLOOR_ARMOR_CONFIG.chancePerFloor); if (Math.random() > armorChance) return 0; // Scale armor with floor const armorRange = FLOOR_ARMOR_CONFIG.maxArmor - FLOOR_ARMOR_CONFIG.minArmor; const floorProgress = Math.min(1, (floor - 10) / 90); return FLOOR_ARMOR_CONFIG.minArmor + armorRange * floorProgress * Math.random(); } // Get dodge chance for a speed room export function getDodgeChance(floor: number): number { return Math.min( SPEED_ROOM_CONFIG.maxDodge, SPEED_ROOM_CONFIG.baseDodgeChance + floor * SPEED_ROOM_CONFIG.dodgePerFloor ); } // Generate enemies for a swarm room export function generateSwarmEnemies(floor: number): EnemyState[] { const baseHP = getFloorMaxHP(floor); const element = getFloorElement(floor); const numEnemies = SWARM_CONFIG.minEnemies + Math.floor(Math.random() * (SWARM_CONFIG.maxEnemies - SWARM_CONFIG.minEnemies + 1)); const enemies: EnemyState[] = []; for (let i = 0; i < numEnemies; i++) { const enemyName = getEnemyName(element, floor); enemies.push({ id: `enemy_${i}`, name: enemyName, hp: Math.floor(baseHP * SWARM_CONFIG.hpMultiplier), maxHP: Math.floor(baseHP * SWARM_CONFIG.hpMultiplier), armor: SWARM_CONFIG.armorBase + Math.floor(floor / 10) * SWARM_CONFIG.armorPerFloor, dodgeChance: 0, element, }); } return enemies; } // Generate initial floor state export function generateFloorState(floor: number): FloorState { const roomType = generateRoomType(floor); const element = getFloorElement(floor); const baseHP = getFloorMaxHP(floor); const guardian = GUARDIANS[floor]; switch (roomType) { case 'guardian': return { roomType: 'guardian', enemies: [{ id: 'guardian', name: guardian.name, hp: guardian.hp, maxHP: guardian.hp, armor: guardian.armor || 0, dodgeChance: 0, element: guardian.element, }], }; case 'swarm': return { roomType: 'swarm', enemies: generateSwarmEnemies(floor), }; case 'speed': { const speedEnemyName = getEnemyName(element, floor); return { roomType: 'speed', enemies: [{ id: 'speed_enemy', name: speedEnemyName, hp: baseHP, maxHP: baseHP, armor: getFloorArmor(floor), dodgeChance: getDodgeChance(floor), element, }], }; } case 'puzzle': { // Select a puzzle type based on player's attunements const puzzleKeys = Object.keys(PUZZLE_ROOMS); const selectedPuzzle = puzzleKeys[Math.floor(Math.random() * puzzleKeys.length)]; const puzzle = PUZZLE_ROOMS[selectedPuzzle]; return { roomType: 'puzzle', enemies: [], puzzleProgress: 0, puzzleRequired: 1, puzzleId: selectedPuzzle, puzzleAttunements: puzzle.attunements, }; } default: // combat const combatEnemyName = getEnemyName(element, floor); return { roomType: 'combat', enemies: [{ id: 'enemy', name: combatEnemyName, hp: baseHP, maxHP: baseHP, armor: getFloorArmor(floor), dodgeChance: 0, element, }], }; } } ``` --- ## Special Floor Properties ### Currently Implemented Properties #### Armor (Damage Reduction) - **Guardian floors**: Defined in `GUARDIANS[floor].armor` (0.10 to 0.35) - **Non-guardian floors**: Randomly generated via `getFloorArmor(floor)` using `FLOOR_ARMOR_CONFIG` - **Swarm enemies**: `SWARM_CONFIG.armorBase + Math.floor(floor / 10) * SWARM_CONFIG.armorPerFloor` - Displayed in UI with shield icon and percentage #### Dodge Chance - **Speed rooms only**: Generated via `getDodgeChance(floor)` using `SPEED_ROOM_CONFIG` - Base: 25%, scales +0.5% per floor, max 50% - Displayed in UI with wind icon and percentage #### Health/HP - **Guardian floors**: `GUARDIANS[floor].hp` (5000 to 1000000) - **Normal floors**: `getFloorMaxHP(floor)` - scales with floor number - **Swarm enemies**: `baseHP * SWARM_CONFIG.hpMultiplier` (40% of normal) ### Properties Mentioned in Task But Not Currently in Floor Config - **healthRegen**: Not currently implemented as a floor/enemy property (only exists in guardian boons as `manaRegen` for player) - **barrier**: Not currently implemented as a floor/enemy property (only exists as attunement mana type) Note: The task mentions displaying "Special floor properties (armor%, health regen, barrier, dodge)" but `healthRegen` and `barrier` are not currently implemented in the floor config. These may need to be added as part of this task. --- ## File Paths ### Key Files for Task 5 (2a Floor Rendering & Identity) 1. **Floor/Room Type Definitions**: - `/home/user/repos/Mana-Loop/src/lib/game/constants/rooms.ts` - Room types, swarm/speed config, armor config - `/home/user/repos/Mana-Loop/src/lib/game/constants/guardians.ts` - Guardian definitions with names, HP, armor - `/home/user/repos/Mana-Loop/src/lib/game/constants/elements.ts` - Element definitions with symbols and colors - `/home/user/repos/Mana-Loop/src/lib/game/constants/index.ts` - ROOM_TYPE_LABELS export 2. **Type Definitions**: - `/home/user/repos/Mana-Loop/src/lib/game/types/game.ts` - RoomType, EnemyState, FloorState interfaces - `/home/user/repos/Mana-Loop/src/lib/game/types/attunements.ts` - GuardianDef, GuardianBoon interfaces 3. **Floor Rendering UI**: - `/home/user/repos/Mana-Loop/src/components/game/tabs/SpireTab.tsx` - Main floor rendering component with enemy display, room type badges, armor/dodge tooltips 4. **Floor Generation Logic**: - `/home/user/repos/Mana-Loop/src/lib/game/store.ts` - `getEnemyName()`, `generateRoomType()`, `generateFloorState()`, `getFloorArmor()`, `getDodgeChance()`, `generateSwarmEnemies()` 5. **Element Cycle for Floors**: - `/home/user/repos/Mana-Loop/src/lib/game/store.ts` - `getFloorElement()`, `getFloorMaxHP()` - `/home/user/repos/Mana-Loop/src/lib/game/constants/elements.ts` - `FLOOR_ELEM_CYCLE`