Add Lightning mana type, AOE spells, room types, and monster armor
Features added: - Lightning mana type (Fire + Air composite) - AOE spells (Chain Lightning, Fireball AOE, Blizzard, etc.) - Magic sword enchantments (Fire Blade, Frost Blade, Lightning Blade) - Monster armor system (all guardians have armor, scales with floors) - Room types (puzzle, swarm, speed) with generation logic - FloorState and EnemyState types for room handling - Weapon mana storage fields for future magic sword system
This commit is contained in:
@@ -43,6 +43,7 @@ export const ELEMENTS: Record<string, ElementDef> = {
|
||||
metal: { name: "Metal", sym: "⚙️", color: "#BDC3C7", glow: "#BDC3C740", cat: "composite", recipe: ["fire", "earth"] },
|
||||
wood: { name: "Wood", sym: "🪵", color: "#8B5E3C", glow: "#8B5E3C40", cat: "composite", recipe: ["life", "earth"] },
|
||||
sand: { name: "Sand", sym: "⏳", color: "#D4AC0D", glow: "#D4AC0D40", cat: "composite", recipe: ["earth", "water"] },
|
||||
lightning: { name: "Lightning", sym: "⚡", color: "#FFEB3B", glow: "#FFEB3B40", cat: "composite", recipe: ["fire", "air"] },
|
||||
|
||||
// Exotic Elements
|
||||
crystal: { name: "Crystal", sym: "💎", color: "#85C1E9", glow: "#85C1E940", cat: "exotic", recipe: ["sand", "sand", "mental"] },
|
||||
@@ -52,10 +53,103 @@ export const ELEMENTS: Record<string, ElementDef> = {
|
||||
|
||||
export const FLOOR_ELEM_CYCLE = ["fire", "water", "air", "earth", "light", "dark", "life", "death"];
|
||||
|
||||
// ─── Room Types ────────────────────────────────────────────────────────────────
|
||||
// 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
|
||||
|
||||
// Puzzle room definitions - themed around attunements
|
||||
export const PUZZLE_ROOMS: Record<string, {
|
||||
name: string;
|
||||
attunements: string[]; // Which attunements speed up progress
|
||||
baseProgressPerTick: number; // Base progress rate
|
||||
attunementBonus: number; // Multiplier per relevant attunement level
|
||||
description: string;
|
||||
}> = {
|
||||
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_enchanter_fabricator: {
|
||||
name: "Fusion Workshop",
|
||||
attunements: ['enchanter', 'fabricator'],
|
||||
baseProgressPerTick: 0.015,
|
||||
attunementBonus: 0.025,
|
||||
description: "Enchant and construct in harmony."
|
||||
},
|
||||
hybrid_enchanter_invoker: {
|
||||
name: "Ritual Circle",
|
||||
attunements: ['enchanter', 'invoker'],
|
||||
baseProgressPerTick: 0.015,
|
||||
attunementBonus: 0.025,
|
||||
description: "Bind pact energies into enchantments."
|
||||
},
|
||||
hybrid_fabricator_invoker: {
|
||||
name: "Golem Forge",
|
||||
attunements: ['fabricator', 'invoker'],
|
||||
baseProgressPerTick: 0.015,
|
||||
attunementBonus: 0.025,
|
||||
description: "Channel guardian power into constructs."
|
||||
},
|
||||
};
|
||||
|
||||
// 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 (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
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// ─── Guardians ────────────────────────────────────────────────────────────────
|
||||
// All guardians have armor - damage reduction percentage
|
||||
export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
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' },
|
||||
@@ -66,6 +160,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
20: {
|
||||
name: "Aqua Regia", element: "water", hp: 15000, pact: 1.75, color: "#4ECDC4",
|
||||
armor: 0.15,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 5, desc: '+5% Water damage' },
|
||||
{ type: 'manaRegen', value: 0.5, desc: '+0.5 mana regen' },
|
||||
@@ -76,6 +171,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
30: {
|
||||
name: "Ventus Rex", element: "air", hp: 30000, pact: 2.0, color: "#00D4FF",
|
||||
armor: 0.18,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 5, desc: '+5% Air damage' },
|
||||
{ type: 'castingSpeed', value: 5, desc: '+5% cast speed' },
|
||||
@@ -86,6 +182,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
40: {
|
||||
name: "Terra Firma", element: "earth", hp: 50000, pact: 2.25, color: "#F4A261",
|
||||
armor: 0.25, // Earth guardian - highest armor
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 5, desc: '+5% Earth damage' },
|
||||
{ type: 'maxMana', value: 100, desc: '+100 max mana' },
|
||||
@@ -96,6 +193,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
50: {
|
||||
name: "Lux Aeterna", element: "light", hp: 80000, pact: 2.5, color: "#FFD700",
|
||||
armor: 0.20,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 10, desc: '+10% Light damage' },
|
||||
{ type: 'insightGain', value: 10, desc: '+10% insight gain' },
|
||||
@@ -106,6 +204,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
60: {
|
||||
name: "Umbra Mortis", element: "dark", hp: 120000, pact: 2.75, color: "#9B59B6",
|
||||
armor: 0.22,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 10, desc: '+10% Dark damage' },
|
||||
{ type: 'critDamage', value: 15, desc: '+15% crit damage' },
|
||||
@@ -116,6 +215,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
70: {
|
||||
name: "Vita Sempiterna", element: "life", hp: 180000, pact: 3.0, color: "#2ECC71",
|
||||
armor: 0.20,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 10, desc: '+10% Life damage' },
|
||||
{ type: 'manaRegen', value: 1, desc: '+1 mana regen' },
|
||||
@@ -126,6 +226,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
80: {
|
||||
name: "Mors Ultima", element: "death", hp: 250000, pact: 3.25, color: "#778CA3",
|
||||
armor: 0.25,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 10, desc: '+10% Death damage' },
|
||||
{ type: 'rawDamage', value: 10, desc: '+10% raw damage' },
|
||||
@@ -136,6 +237,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
90: {
|
||||
name: "Primordialis", element: "void", hp: 400000, pact: 4.0, color: "#4A235A",
|
||||
armor: 0.30,
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 15, desc: '+15% Void damage' },
|
||||
{ type: 'maxMana', value: 200, desc: '+200 max mana' },
|
||||
@@ -147,6 +249,7 @@ export const GUARDIANS: Record<number, GuardianDef> = {
|
||||
},
|
||||
100: {
|
||||
name: "The Awakened One", element: "stellar", hp: 1000000, pact: 5.0, color: "#F0E68C",
|
||||
armor: 0.35, // Final boss has highest armor
|
||||
boons: [
|
||||
{ type: 'elementalDamage', value: 20, desc: '+20% Stellar damage' },
|
||||
{ type: 'maxMana', value: 500, desc: '+500 max mana' },
|
||||
@@ -648,6 +751,228 @@ export const SPELLS_DEF: Record<string, SpellDef> = {
|
||||
studyTime: 36,
|
||||
desc: "Shatter crystalline energy."
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// LIGHTNING SPELLS - Fast, armor-piercing, harder to dodge
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Tier 1 - Basic Lightning
|
||||
spark: {
|
||||
name: "Spark",
|
||||
elem: "lightning",
|
||||
dmg: 8,
|
||||
cost: elemCost("lightning", 1),
|
||||
tier: 1,
|
||||
castSpeed: 4,
|
||||
unlock: 120,
|
||||
studyTime: 2,
|
||||
desc: "A quick spark of lightning. Very fast and hard to dodge.",
|
||||
effects: [{ type: 'armor_pierce', value: 0.2 }]
|
||||
},
|
||||
lightningBolt: {
|
||||
name: "Lightning Bolt",
|
||||
elem: "lightning",
|
||||
dmg: 14,
|
||||
cost: elemCost("lightning", 2),
|
||||
tier: 1,
|
||||
castSpeed: 3,
|
||||
unlock: 150,
|
||||
studyTime: 3,
|
||||
desc: "A bolt of lightning that pierces armor.",
|
||||
effects: [{ type: 'armor_pierce', value: 0.3 }]
|
||||
},
|
||||
|
||||
// Tier 2 - Advanced Lightning
|
||||
chainLightning: {
|
||||
name: "Chain Lightning",
|
||||
elem: "lightning",
|
||||
dmg: 25,
|
||||
cost: elemCost("lightning", 5),
|
||||
tier: 2,
|
||||
castSpeed: 2,
|
||||
unlock: 900,
|
||||
studyTime: 8,
|
||||
desc: "Lightning that arcs between enemies. Hits 3 targets.",
|
||||
isAoe: true,
|
||||
aoeTargets: 3,
|
||||
effects: [{ type: 'chain', value: 3 }]
|
||||
},
|
||||
stormCall: {
|
||||
name: "Storm Call",
|
||||
elem: "lightning",
|
||||
dmg: 40,
|
||||
cost: elemCost("lightning", 6),
|
||||
tier: 2,
|
||||
castSpeed: 1.5,
|
||||
unlock: 1100,
|
||||
studyTime: 10,
|
||||
desc: "Call down a storm. Hits 2 targets with armor pierce.",
|
||||
isAoe: true,
|
||||
aoeTargets: 2,
|
||||
effects: [{ type: 'armor_pierce', value: 0.4 }]
|
||||
},
|
||||
|
||||
// Tier 3 - Master Lightning
|
||||
thunderStrike: {
|
||||
name: "Thunder Strike",
|
||||
elem: "lightning",
|
||||
dmg: 150,
|
||||
cost: elemCost("lightning", 15),
|
||||
tier: 3,
|
||||
castSpeed: 0.8,
|
||||
unlock: 10000,
|
||||
studyTime: 24,
|
||||
desc: "Devastating lightning that ignores 50% armor.",
|
||||
effects: [{ type: 'armor_pierce', value: 0.5 }]
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// AOE SPELLS - Hit multiple enemies, less damage per target
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Tier 1 AOE
|
||||
fireballAoe: {
|
||||
name: "Fireball (AOE)",
|
||||
elem: "fire",
|
||||
dmg: 8,
|
||||
cost: elemCost("fire", 3),
|
||||
tier: 1,
|
||||
castSpeed: 2,
|
||||
unlock: 150,
|
||||
studyTime: 3,
|
||||
desc: "An explosive fireball that hits 3 enemies.",
|
||||
isAoe: true,
|
||||
aoeTargets: 3,
|
||||
effects: [{ type: 'aoe', value: 3 }]
|
||||
},
|
||||
frostNova: {
|
||||
name: "Frost Nova",
|
||||
elem: "water",
|
||||
dmg: 6,
|
||||
cost: elemCost("water", 3),
|
||||
tier: 1,
|
||||
castSpeed: 2,
|
||||
unlock: 140,
|
||||
studyTime: 3,
|
||||
desc: "A burst of frost hitting 4 enemies. May freeze.",
|
||||
isAoe: true,
|
||||
aoeTargets: 4,
|
||||
effects: [{ type: 'freeze', value: 0.15, chance: 0.2 }]
|
||||
},
|
||||
|
||||
// Tier 2 AOE
|
||||
meteorShower: {
|
||||
name: "Meteor Shower",
|
||||
elem: "fire",
|
||||
dmg: 20,
|
||||
cost: elemCost("fire", 8),
|
||||
tier: 2,
|
||||
castSpeed: 1,
|
||||
unlock: 1200,
|
||||
studyTime: 10,
|
||||
desc: "Rain meteors on 5 enemies.",
|
||||
isAoe: true,
|
||||
aoeTargets: 5
|
||||
},
|
||||
blizzard: {
|
||||
name: "Blizzard",
|
||||
elem: "water",
|
||||
dmg: 18,
|
||||
cost: elemCost("water", 7),
|
||||
tier: 2,
|
||||
castSpeed: 1.2,
|
||||
unlock: 1000,
|
||||
studyTime: 9,
|
||||
desc: "A freezing blizzard hitting 4 enemies.",
|
||||
isAoe: true,
|
||||
aoeTargets: 4,
|
||||
effects: [{ type: 'freeze', value: 0.1, chance: 0.15 }]
|
||||
},
|
||||
earthquakeAoe: {
|
||||
name: "Earth Tremor",
|
||||
elem: "earth",
|
||||
dmg: 25,
|
||||
cost: elemCost("earth", 8),
|
||||
tier: 2,
|
||||
castSpeed: 0.8,
|
||||
unlock: 1400,
|
||||
studyTime: 10,
|
||||
desc: "Shake the ground, hitting 3 enemies with high damage.",
|
||||
isAoe: true,
|
||||
aoeTargets: 3
|
||||
},
|
||||
|
||||
// Tier 3 AOE
|
||||
apocalypse: {
|
||||
name: "Apocalypse",
|
||||
elem: "fire",
|
||||
dmg: 80,
|
||||
cost: elemCost("fire", 20),
|
||||
tier: 3,
|
||||
castSpeed: 0.5,
|
||||
unlock: 15000,
|
||||
studyTime: 30,
|
||||
desc: "End times. Hits ALL enemies with devastating fire.",
|
||||
isAoe: true,
|
||||
aoeTargets: 10
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// MAGIC SWORD ENCHANTMENTS - For weapon enchanting system
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
fireBlade: {
|
||||
name: "Fire Blade",
|
||||
elem: "fire",
|
||||
dmg: 3,
|
||||
cost: rawCost(1),
|
||||
tier: 1,
|
||||
castSpeed: 4,
|
||||
unlock: 100,
|
||||
studyTime: 2,
|
||||
desc: "Enchant a blade with fire. Burns enemies over time.",
|
||||
isWeaponEnchant: true,
|
||||
effects: [{ type: 'burn', value: 2, duration: 3 }]
|
||||
},
|
||||
frostBlade: {
|
||||
name: "Frost Blade",
|
||||
elem: "water",
|
||||
dmg: 3,
|
||||
cost: rawCost(1),
|
||||
tier: 1,
|
||||
castSpeed: 4,
|
||||
unlock: 100,
|
||||
studyTime: 2,
|
||||
desc: "Enchant a blade with frost. Prevents enemy dodge.",
|
||||
isWeaponEnchant: true,
|
||||
effects: [{ type: 'freeze', value: 0, chance: 1 }] // 100% freeze = no dodge
|
||||
},
|
||||
lightningBlade: {
|
||||
name: "Lightning Blade",
|
||||
elem: "lightning",
|
||||
dmg: 4,
|
||||
cost: rawCost(1),
|
||||
tier: 1,
|
||||
castSpeed: 5,
|
||||
unlock: 150,
|
||||
studyTime: 3,
|
||||
desc: "Enchant a blade with lightning. Pierces 30% armor.",
|
||||
isWeaponEnchant: true,
|
||||
effects: [{ type: 'armor_pierce', value: 0.3 }]
|
||||
},
|
||||
voidBlade: {
|
||||
name: "Void Blade",
|
||||
elem: "dark",
|
||||
dmg: 5,
|
||||
cost: rawCost(2),
|
||||
tier: 2,
|
||||
castSpeed: 3,
|
||||
unlock: 800,
|
||||
studyTime: 8,
|
||||
desc: "Enchant a blade with void. 10% lifesteal.",
|
||||
isWeaponEnchant: true,
|
||||
effects: [{ type: 'lifesteal', value: 0.1 }]
|
||||
},
|
||||
};
|
||||
|
||||
// ─── Skills ───────────────────────────────────────────────────────────────────
|
||||
@@ -848,6 +1173,7 @@ export const ELEMENT_OPPOSITES: Record<string, string> = {
|
||||
air: 'earth', earth: 'air',
|
||||
light: 'dark', dark: 'light',
|
||||
life: 'death', death: 'life',
|
||||
lightning: 'earth', // Lightning is weak to earth (grounding)
|
||||
};
|
||||
|
||||
// ─── Element Icon Mapping (Lucide Icons) ──────────────────────────────────────
|
||||
@@ -869,6 +1195,7 @@ export const ELEMENT_ICON_NAMES: Record<string, string> = {
|
||||
metal: 'Target',
|
||||
wood: 'TreeDeciduous',
|
||||
sand: 'Hourglass',
|
||||
lightning: 'Zap',
|
||||
crystal: 'Gem',
|
||||
stellar: 'Star',
|
||||
void: 'CircleDot',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { GameState, GameAction, StudyTarget, SpellCost, SkillUpgradeChoice, EquipmentSlot, EquipmentInstance, EnchantmentDesign, DesignEffect, AttunementState } from './types';
|
||||
import type { GameState, GameAction, StudyTarget, SpellCost, SkillUpgradeChoice, EquipmentSlot, EquipmentInstance, EnchantmentDesign, DesignEffect, AttunementState, FloorState, EnemyState, RoomType } from './types';
|
||||
import {
|
||||
ELEMENTS,
|
||||
GUARDIANS,
|
||||
@@ -22,6 +22,14 @@ import {
|
||||
EFFECT_RESEARCH_MAPPING,
|
||||
BASE_UNLOCKED_EFFECTS,
|
||||
ENCHANTING_UNLOCK_EFFECTS,
|
||||
PUZZLE_ROOMS,
|
||||
PUZZLE_ROOM_INTERVAL,
|
||||
PUZZLE_ROOM_CHANCE,
|
||||
SWARM_ROOM_CHANCE,
|
||||
SPEED_ROOM_CHANCE,
|
||||
SWARM_CONFIG,
|
||||
SPEED_ROOM_CONFIG,
|
||||
FLOOR_ARMOR_CONFIG,
|
||||
} from './constants';
|
||||
import { computeEffects, hasSpecial, SPECIAL_EFFECTS, type ComputedEffects } from './upgrade-effects';
|
||||
import {
|
||||
@@ -99,6 +107,174 @@ export function getFloorElement(floor: number): string {
|
||||
return FLOOR_ELEM_CYCLE[(floor - 1) % 8];
|
||||
}
|
||||
|
||||
// ─── Room Generation Functions ────────────────────────────────────────────────
|
||||
|
||||
// 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++) {
|
||||
enemies.push({
|
||||
id: `enemy_${i}`,
|
||||
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',
|
||||
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':
|
||||
return {
|
||||
roomType: 'speed',
|
||||
enemies: [{
|
||||
id: 'speed_enemy',
|
||||
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
|
||||
return {
|
||||
roomType: 'combat',
|
||||
enemies: [{
|
||||
id: 'enemy',
|
||||
hp: baseHP,
|
||||
maxHP: baseHP,
|
||||
armor: getFloorArmor(floor),
|
||||
dodgeChance: 0,
|
||||
element,
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get puzzle progress speed based on attunements
|
||||
export function getPuzzleProgressSpeed(
|
||||
puzzleId: string,
|
||||
attunements: Record<string, AttunementState>
|
||||
): number {
|
||||
const puzzle = PUZZLE_ROOMS[puzzleId];
|
||||
if (!puzzle) return 0.02; // Default slow progress
|
||||
|
||||
let speed = puzzle.baseProgressPerTick;
|
||||
|
||||
// Add bonus for each relevant attunement level
|
||||
for (const attId of puzzle.attunements) {
|
||||
const attState = attunements[attId];
|
||||
if (attState?.active) {
|
||||
speed += puzzle.attunementBonus * (attState.level || 1);
|
||||
}
|
||||
}
|
||||
|
||||
return speed;
|
||||
}
|
||||
|
||||
// ─── Computed Stats Functions ─────────────────────────────────────────────────
|
||||
|
||||
// Helper to get effective skill level accounting for tiers
|
||||
@@ -467,6 +643,9 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
|
||||
currentAction: 'meditate',
|
||||
castProgress: 0,
|
||||
|
||||
// Initialize room state
|
||||
currentRoom: generateFloorState(startFloor),
|
||||
|
||||
spells: startSpells,
|
||||
skills: overrides.skills || {},
|
||||
skillProgress: {},
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface GuardianDef {
|
||||
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)
|
||||
}
|
||||
|
||||
// Spell cost can be raw mana or elemental mana
|
||||
@@ -84,12 +85,17 @@ export interface SpellDef {
|
||||
castSpeed?: number; // Casts per hour (default 1, higher = faster)
|
||||
desc?: string; // Optional spell description
|
||||
effects?: SpellEffect[]; // Optional special effects
|
||||
isAoe?: boolean; // AOE spell that hits multiple enemies
|
||||
aoeTargets?: number; // Number of enemies hit by AOE
|
||||
isWeaponEnchant?: boolean; // Can be used as weapon enchantment (magic swords)
|
||||
}
|
||||
|
||||
export interface SpellEffect {
|
||||
type: 'lifesteal' | 'burn' | 'freeze' | 'stun' | 'pierce' | 'multicast' | 'shield' | 'buff';
|
||||
type: 'lifesteal' | 'burn' | 'freeze' | 'stun' | 'pierce' | 'multicast' | 'shield' | 'buff' | 'chain' | 'aoe' | 'armor_pierce';
|
||||
value: number; // Effect potency
|
||||
duration?: number; // Duration in hours for timed effects
|
||||
targets?: number; // For AOE: number of targets
|
||||
chance?: number; // For chance-based effects (e.g., stun chance)
|
||||
}
|
||||
|
||||
export interface SpellState {
|
||||
@@ -98,6 +104,28 @@ export interface SpellState {
|
||||
studyProgress?: number; // Hours studied so far (for in-progress spells)
|
||||
}
|
||||
|
||||
// ─── Room and Enemy Types ─────────────────────────────────────────────────────
|
||||
|
||||
export type RoomType = 'combat' | 'puzzle' | 'swarm' | 'speed' | 'guardian';
|
||||
|
||||
export interface EnemyState {
|
||||
id: string;
|
||||
hp: number;
|
||||
maxHP: number;
|
||||
armor: number; // Damage reduction (0-1)
|
||||
dodgeChance: number; // For speed rooms (0-1)
|
||||
element: string;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
export interface SkillDef {
|
||||
name: string;
|
||||
desc: string;
|
||||
@@ -183,6 +211,11 @@ export interface EquipmentInstance {
|
||||
totalCapacity: number; // Base capacity + bonuses
|
||||
rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary' | 'mythic';
|
||||
quality: number; // 0-100, affects capacity efficiency
|
||||
weaponMana?: number; // Current mana stored in weapon (for weapon enchantments)
|
||||
weaponManaMax?: number; // Max mana the weapon can store
|
||||
weaponManaRegen?: number; // Mana regen per hour for weapon
|
||||
weaponManaType?: string; // Type of mana the weapon stores
|
||||
activeWeaponEnchant?: string; // Active weapon enchantment (for magic swords)
|
||||
}
|
||||
|
||||
export interface AppliedEnchantment {
|
||||
@@ -347,6 +380,9 @@ export interface GameState {
|
||||
currentAction: GameAction;
|
||||
castProgress: number; // Progress towards next spell cast (0-1)
|
||||
|
||||
// Room system for special floors
|
||||
currentRoom: FloorState; // Current room state (swarm, puzzle, speed, etc.)
|
||||
|
||||
// Spells
|
||||
spells: Record<string, SpellState>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user