Initial commit

This commit is contained in:
Z User
2026-04-03 17:23:15 +00:00
commit 4f474dbcf3
192 changed files with 47527 additions and 0 deletions
+246
View File
@@ -0,0 +1,246 @@
// ─── Achievement Definitions ───────────────────────────────────────────────────
import type { AchievementDef } from '../types';
export const ACHIEVEMENTS: Record<string, AchievementDef> = {
// ─── Combat Achievements ───
firstBlood: {
id: 'firstBlood',
name: 'First Blood',
desc: 'Clear your first floor',
category: 'combat',
requirement: { type: 'floor', value: 2 },
reward: { insight: 10 },
},
floorClimber: {
id: 'floorClimber',
name: 'Floor Climber',
desc: 'Reach floor 10',
category: 'combat',
requirement: { type: 'floor', value: 10 },
reward: { insight: 25, manaBonus: 10 },
},
spireAssault: {
id: 'spireAssault',
name: 'Spire Assault',
desc: 'Reach floor 25',
category: 'combat',
requirement: { type: 'floor', value: 25 },
reward: { insight: 50, damageBonus: 0.05 },
},
towerConqueror: {
id: 'towerConqueror',
name: 'Tower Conqueror',
desc: 'Reach floor 50',
category: 'combat',
requirement: { type: 'floor', value: 50 },
reward: { insight: 100, manaBonus: 50, damageBonus: 0.1 },
},
spireMaster: {
id: 'spireMaster',
name: 'Spire Master',
desc: 'Reach floor 75',
category: 'combat',
requirement: { type: 'floor', value: 75 },
reward: { insight: 200, damageBonus: 0.15, title: 'Spire Master' },
},
apexReached: {
id: 'apexReached',
name: 'Apex Reached',
desc: 'Reach floor 100',
category: 'combat',
requirement: { type: 'floor', value: 100 },
reward: { insight: 500, manaBonus: 200, damageBonus: 0.25, title: 'Apex Climber' },
},
// ─── Damage Achievements ───
hundredDamage: {
id: 'hundredDamage',
name: 'Heavy Hitter',
desc: 'Deal 100 damage in a single hit',
category: 'combat',
requirement: { type: 'damage', value: 100 },
reward: { insight: 20 },
},
thousandDamage: {
id: 'thousandDamage',
name: 'Devastating Blow',
desc: 'Deal 1,000 damage in a single hit',
category: 'combat',
requirement: { type: 'damage', value: 1000 },
reward: { insight: 75, damageBonus: 0.03 },
},
tenThousandDamage: {
id: 'tenThousandDamage',
name: 'Apocalypse Now',
desc: 'Deal 10,000 damage in a single hit',
category: 'combat',
requirement: { type: 'damage', value: 10000 },
reward: { insight: 200, damageBonus: 0.05, title: 'Apocalypse Bringer' },
},
// ─── Pact Achievements ───
pactSeeker: {
id: 'pactSeeker',
name: 'Pact Seeker',
desc: 'Sign your first guardian pact',
category: 'progression',
requirement: { type: 'pact', value: 1 },
reward: { insight: 30 },
},
pactCollector: {
id: 'pactCollector',
name: 'Pact Collector',
desc: 'Sign 5 guardian pacts',
category: 'progression',
requirement: { type: 'pact', value: 5 },
reward: { insight: 100, manaBonus: 25 },
},
pactMaster: {
id: 'pactMaster',
name: 'Pact Master',
desc: 'Sign all guardian pacts',
category: 'progression',
requirement: { type: 'pact', value: 12 },
reward: { insight: 500, damageBonus: 0.2, title: 'Pact Master' },
},
// ─── Magic Achievements ───
spellCaster: {
id: 'spellCaster',
name: 'Spell Caster',
desc: 'Cast 100 spells',
category: 'magic',
requirement: { type: 'spells', value: 100 },
reward: { insight: 25 },
},
spellWeaver: {
id: 'spellWeaver',
name: 'Spell Weaver',
desc: 'Cast 1,000 spells',
category: 'magic',
requirement: { type: 'spells', value: 1000 },
reward: { insight: 75, regenBonus: 0.5 },
},
spellStorm: {
id: 'spellStorm',
name: 'Spell Storm',
desc: 'Cast 10,000 spells',
category: 'magic',
requirement: { type: 'spells', value: 10000 },
reward: { insight: 200, regenBonus: 1, title: 'Storm Caller' },
},
// ─── Mana Achievements ───
manaPool: {
id: 'manaPool',
name: 'Mana Pool',
desc: 'Accumulate 1,000 total mana',
category: 'magic',
requirement: { type: 'mana', value: 1000 },
reward: { insight: 20 },
},
manaLake: {
id: 'manaLake',
name: 'Mana Lake',
desc: 'Accumulate 100,000 total mana',
category: 'magic',
requirement: { type: 'mana', value: 100000 },
reward: { insight: 100, manaBonus: 50 },
},
manaOcean: {
id: 'manaOcean',
name: 'Mana Ocean',
desc: 'Accumulate 10,000,000 total mana',
category: 'magic',
requirement: { type: 'mana', value: 10000000 },
reward: { insight: 500, manaBonus: 200, title: 'Mana Ocean' },
},
// ─── Crafting Achievements ───
enchanter: {
id: 'enchanter',
name: 'Enchanter',
desc: 'Complete your first enchantment',
category: 'crafting',
requirement: { type: 'craft', value: 1 },
reward: { insight: 30 },
},
masterEnchanter: {
id: 'masterEnchanter',
name: 'Master Enchanter',
desc: 'Complete 10 enchantments',
category: 'crafting',
requirement: { type: 'craft', value: 10 },
reward: { insight: 100, unlockEffect: 'efficiencyBoost' },
},
legendaryEnchanter: {
id: 'legendaryEnchanter',
name: 'Legendary Enchanter',
desc: 'Complete 50 enchantments',
category: 'crafting',
requirement: { type: 'craft', value: 50 },
reward: { insight: 300, title: 'Legendary Enchanter' },
},
// ─── Special Achievements ───
speedRunner: {
id: 'speedRunner',
name: 'Speed Runner',
desc: 'Reach floor 50 in under 5 days',
category: 'special',
requirement: { type: 'time', value: 5, subType: 'floor50' },
reward: { insight: 200, title: 'Speed Demon' },
hidden: true,
},
perfectionist: {
id: 'perfectionist',
name: 'Perfectionist',
desc: 'Reach floor 100 without any guardian pacts',
category: 'special',
requirement: { type: 'floor', value: 100, subType: 'noPacts' },
reward: { insight: 1000, title: 'Perfect Climber' },
hidden: true,
},
survivor: {
id: 'survivor',
name: 'Survivor',
desc: 'Complete a loop during full incursion (day 30+)',
category: 'special',
requirement: { type: 'time', value: 30 },
reward: { insight: 300, manaBonus: 100, title: 'Survivor' },
},
};
// Category colors for UI
export const ACHIEVEMENT_CATEGORY_COLORS: Record<string, string> = {
combat: '#EF4444', // Red
progression: '#F59E0B', // Amber
crafting: '#8B5CF6', // Purple
magic: '#3B82F6', // Blue
special: '#EC4899', // Pink
};
// Get achievements by category
export function getAchievementsByCategory(): Record<string, AchievementDef[]> {
const result: Record<string, AchievementDef[]> = {};
for (const achievement of Object.values(ACHIEVEMENTS)) {
if (!result[achievement.category]) {
result[achievement.category] = [];
}
result[achievement.category].push(achievement);
}
return result;
}
// Check if an achievement should be revealed
export function isAchievementRevealed(
achievement: AchievementDef,
progress: number
): boolean {
if (!achievement.hidden) return true;
// Reveal hidden achievements when at 50% progress
return progress >= achievement.requirement.value * 0.5;
}
+185
View File
@@ -0,0 +1,185 @@
// ─── Attunement Definitions ─────────────────────────────────────────────────────
// Attunements are class-like abilities tied to body locations
// Each provides unique capabilities, primary mana types, and skill access
import type { AttunementDef, AttunementSlot } from '../types';
// Attunement slot display names
export const ATTUNEMENT_SLOT_NAMES: Record<AttunementSlot, string> = {
rightHand: 'Right Hand',
leftHand: 'Left Hand',
head: 'Head',
back: 'Back',
chest: 'Chest',
leftLeg: 'Left Leg',
rightLeg: 'Right Leg',
};
// All attunement definitions
export const ATTUNEMENTS_DEF: Record<string, AttunementDef> = {
// ─── Enchanter (Right Hand) ─────────────────────────────────────────────────
// Unlocks the enchanting system - applying magical effects to equipment
// Primary mana: Transference (used to move/apply enchantments)
enchanter: {
id: 'enchanter',
name: 'Enchanter',
desc: 'Channel transference mana through your right hand to apply magical enchantments to equipment. The art of enchanting allows you to imbue items with spell effects, stat bonuses, and special properties.',
slot: 'rightHand',
icon: '✨',
color: '#1ABC9C', // Teal (transference color)
primaryManaType: 'transference',
rawManaRegen: 0.5,
conversionRate: 0.2, // Converts 0.2 raw mana to transference per hour
unlocked: true, // Starting attunement
capabilities: ['enchanting', 'disenchanting'],
skillCategories: ['enchant', 'effectResearch'],
},
// ─── Invoker (Chest/Heart) ───────────────────────────────────────────────────
// Enables forming pacts with spire guardians
// No primary mana - instead gains mana types from each pact signed
invoker: {
id: 'invoker',
name: 'Invoker',
desc: 'Open your heart to the guardians of the spire. Form pacts with defeated guardians to gain their elemental affinity and access to their unique powers. Each pact grants access to a new mana type.',
slot: 'chest',
icon: '💜',
color: '#9B59B6', // Purple
primaryManaType: undefined, // Invoker has no primary - gains from pacts
rawManaRegen: 0.3,
conversionRate: 0, // No automatic conversion - mana comes from pacts
unlocked: false, // Unlocked through gameplay
unlockCondition: 'Defeat your first guardian and choose the path of the Invoker',
capabilities: ['pacts', 'guardianPowers', 'elementalMastery'],
skillCategories: ['invocation', 'pact'],
},
// ─── Fabricator (Left Hand) ──────────────────────────────────────────────────
// Crafts earth golems and earthen gear
// Primary mana: Earth
// Later with fire mana -> metal mana, can craft metallic gear and golems
fabricator: {
id: 'fabricator',
name: 'Fabricator',
desc: 'Shape earth and metal through your left hand to craft golems and equipment. Start with earthen constructs, and unlock metalworking when you gain fire mana to create metal mana.',
slot: 'leftHand',
icon: '⚒️',
color: '#F4A261', // Earth color
primaryManaType: 'earth',
rawManaRegen: 0.4,
conversionRate: 0.25, // Converts 0.25 raw mana to earth per hour
unlocked: false, // Unlocked through gameplay
unlockCondition: 'Prove your worth as a crafter',
capabilities: ['golemCrafting', 'gearCrafting', 'earthShaping'],
skillCategories: ['fabrication', 'golemancy'],
},
};
// Helper function to get attunement by slot
export function getAttunementBySlot(slot: AttunementSlot): AttunementDef | undefined {
return Object.values(ATTUNEMENTS_DEF).find(a => a.slot === slot);
}
// Helper function to get all unlocked attunements for a player
export function getUnlockedAttunements(attunements: Record<string, { active: boolean; level: number; experience: number }>): AttunementDef[] {
return Object.entries(attunements)
.filter(([id, state]) => state.active || ATTUNEMENTS_DEF[id]?.unlocked)
.map(([id]) => ATTUNEMENTS_DEF[id])
.filter(Boolean);
}
// Helper function to calculate total raw mana regen from attunements (with level scaling)
export function getTotalAttunementRegen(attunements: Record<string, { active: boolean; level: number; experience: number }>): number {
return Object.entries(attunements)
.filter(([, state]) => state.active)
.reduce((total, [id, state]) => {
const def = ATTUNEMENTS_DEF[id];
if (!def) return total;
// Exponential scaling: base * (1.5 ^ (level - 1))
const levelMult = Math.pow(1.5, (state.level || 1) - 1);
return total + def.rawManaRegen * levelMult;
}, 0);
}
// Get conversion rate with level scaling
export function getAttunementConversionRate(attunementId: string, level: number): number {
const def = ATTUNEMENTS_DEF[attunementId];
if (!def || def.conversionRate <= 0) return 0;
// Exponential scaling: base * (1.5 ^ (level - 1))
return def.conversionRate * Math.pow(1.5, (level || 1) - 1);
}
// XP required for attunement level
export function getAttunementXPForLevel(level: number): number {
// New scaling:
// Level 2: 1000 XP
// Level 3: 2500 XP
// Level 4: 5000 XP
// Level 5: 10000 XP
// etc. (each level requires 2x the previous, starting from 1000)
if (level <= 1) return 0;
if (level === 2) return 1000;
// For level 3+: 1000 * 2.5^(level-2), but rounded nicely
return Math.floor(1000 * Math.pow(2, level - 2) * (level >= 3 ? 1.25 : 1));
}
// Calculate XP gained from enchanting based on capacity used
export function calculateEnchantingXP(capacityUsed: number): number {
// 1 XP per 10 capacity used, floored, minimum 1
return Math.max(1, Math.floor(capacityUsed / 10));
}
// Max attunement level
export const MAX_ATTUNEMENT_LEVEL = 10;
// Helper function to get mana types from active attunements and pacts
export function getAttunementManaTypes(
attunements: Record<string, { active: boolean; level: number; experience: number }>,
signedPacts: number[]
): string[] {
const manaTypes: string[] = [];
// Add primary mana types from active attunements
Object.entries(attunements)
.filter(([, state]) => state.active)
.forEach(([id]) => {
const def = ATTUNEMENTS_DEF[id];
if (def?.primaryManaType) {
manaTypes.push(def.primaryManaType);
}
});
// Invoker gains mana types from signed pacts
if (attunements.invoker?.active && signedPacts.length > 0) {
// Import GUARDIANS would be circular, so this is handled in the store
// For now, just mark that invoker provides pact-based mana
manaTypes.push('pactElements');
}
return [...new Set(manaTypes)]; // Remove duplicates
}
// Get skill categories available to player based on active attunements
export function getAvailableSkillCategories(
attunements: Record<string, { active: boolean; level: number; experience: number }>
): string[] {
const categories = new Set<string>();
// Always available categories
categories.add('mana');
categories.add('study');
categories.add('research');
categories.add('ascension');
// Add categories from active attunements
Object.entries(attunements)
.filter(([, state]) => state.active)
.forEach(([id]) => {
const def = ATTUNEMENTS_DEF[id];
if (def?.skillCategories) {
def.skillCategories.forEach(cat => categories.add(cat));
}
});
return Array.from(categories);
}
+257
View File
@@ -0,0 +1,257 @@
// ─── Crafting Recipes ─────────────────────────────────────────────────────────
// Defines what materials are needed to craft equipment from blueprints
import type { EquipmentSlot } from '../types';
export interface CraftingRecipe {
id: string; // Blueprint ID (matches loot drop)
equipmentTypeId: string; // Resulting equipment type ID
name: string; // Display name
description: string;
rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
materials: Record<string, number>; // materialId -> count required
manaCost: number; // Raw mana cost to craft
craftTime: number; // Hours to craft
minFloor: number; // Minimum floor where blueprint drops
unlocked: boolean; // Whether the player has discovered this
}
export const CRAFTING_RECIPES: Record<string, CraftingRecipe> = {
// ─── Staff Blueprints ───
staffBlueprint: {
id: 'staffBlueprint',
equipmentTypeId: 'oakStaff',
name: 'Oak Staff',
description: 'A sturdy oak staff with decent mana capacity.',
rarity: 'uncommon',
materials: {
manaCrystalDust: 5,
arcaneShard: 2,
},
manaCost: 200,
craftTime: 4,
minFloor: 10,
unlocked: false,
},
wandBlueprint: {
id: 'wandBlueprint',
equipmentTypeId: 'crystalWand',
name: 'Crystal Wand',
description: 'A wand tipped with a small crystal. Excellent for elemental enchantments.',
rarity: 'rare',
materials: {
manaCrystalDust: 8,
arcaneShard: 4,
elementalCore: 1,
},
manaCost: 500,
craftTime: 6,
minFloor: 20,
unlocked: false,
},
robeBlueprint: {
id: 'robeBlueprint',
equipmentTypeId: 'scholarRobe',
name: 'Scholar Robe',
description: 'A robe worn by scholars and researchers.',
rarity: 'rare',
materials: {
manaCrystalDust: 6,
arcaneShard: 3,
elementalCore: 1,
},
manaCost: 400,
craftTime: 5,
minFloor: 25,
unlocked: false,
},
artifactBlueprint: {
id: 'artifactBlueprint',
equipmentTypeId: 'arcanistStaff',
name: 'Arcanist Staff',
description: 'A staff designed for advanced spellcasters. High capacity for complex enchantments.',
rarity: 'legendary',
materials: {
manaCrystalDust: 20,
arcaneShard: 10,
elementalCore: 5,
voidEssence: 2,
celestialFragment: 1,
},
manaCost: 2000,
craftTime: 12,
minFloor: 60,
unlocked: false,
},
// ─── Additional Blueprints ───
battlestaffBlueprint: {
id: 'battlestaffBlueprint',
equipmentTypeId: 'battlestaff',
name: 'Battlestaff',
description: 'A reinforced staff suitable for both casting and combat.',
rarity: 'rare',
materials: {
manaCrystalDust: 10,
arcaneShard: 5,
elementalCore: 2,
},
manaCost: 600,
craftTime: 6,
minFloor: 30,
unlocked: false,
},
catalystBlueprint: {
id: 'catalystBlueprint',
equipmentTypeId: 'fireCatalyst',
name: 'Fire Catalyst',
description: 'A catalyst attuned to fire magic. Enhances fire enchantments.',
rarity: 'rare',
materials: {
manaCrystalDust: 8,
arcaneShard: 4,
elementalCore: 3,
},
manaCost: 500,
craftTime: 5,
minFloor: 25,
unlocked: false,
},
shieldBlueprint: {
id: 'shieldBlueprint',
equipmentTypeId: 'runicShield',
name: 'Runic Shield',
description: 'A shield engraved with protective runes.',
rarity: 'rare',
materials: {
manaCrystalDust: 10,
arcaneShard: 6,
elementalCore: 2,
},
manaCost: 450,
craftTime: 5,
minFloor: 28,
unlocked: false,
},
hatBlueprint: {
id: 'hatBlueprint',
equipmentTypeId: 'wizardHat',
name: 'Wizard Hat',
description: 'A classic pointed wizard hat. Decent capacity for headwear.',
rarity: 'uncommon',
materials: {
manaCrystalDust: 4,
arcaneShard: 2,
},
manaCost: 150,
craftTime: 3,
minFloor: 12,
unlocked: false,
},
glovesBlueprint: {
id: 'glovesBlueprint',
equipmentTypeId: 'spellweaveGloves',
name: 'Spellweave Gloves',
description: 'Gloves woven with mana-conductive threads.',
rarity: 'uncommon',
materials: {
manaCrystalDust: 3,
arcaneShard: 2,
},
manaCost: 120,
craftTime: 3,
minFloor: 15,
unlocked: false,
},
bootsBlueprint: {
id: 'bootsBlueprint',
equipmentTypeId: 'travelerBoots',
name: 'Traveler Boots',
description: 'Comfortable boots for long journeys.',
rarity: 'uncommon',
materials: {
manaCrystalDust: 3,
arcaneShard: 1,
},
manaCost: 100,
craftTime: 2,
minFloor: 8,
unlocked: false,
},
ringBlueprint: {
id: 'ringBlueprint',
equipmentTypeId: 'silverRing',
name: 'Silver Ring',
description: 'A silver ring with decent magical conductivity.',
rarity: 'uncommon',
materials: {
manaCrystalDust: 2,
arcaneShard: 1,
},
manaCost: 80,
craftTime: 2,
minFloor: 10,
unlocked: false,
},
amuletBlueprint: {
id: 'amuletBlueprint',
equipmentTypeId: 'silverAmulet',
name: 'Silver Amulet',
description: 'A silver amulet with a small gem.',
rarity: 'uncommon',
materials: {
manaCrystalDust: 3,
arcaneShard: 2,
},
manaCost: 100,
craftTime: 3,
minFloor: 12,
unlocked: false,
},
};
// Helper functions
export function getRecipeByBlueprint(blueprintId: string): CraftingRecipe | undefined {
return CRAFTING_RECIPES[blueprintId];
}
export function canCraftRecipe(
recipe: CraftingRecipe,
materials: Record<string, number>,
rawMana: number
): { canCraft: boolean; missingMaterials: Record<string, number>; missingMana: number } {
const missingMaterials: Record<string, number> = {};
let canCraft = true;
for (const [matId, required] of Object.entries(recipe.materials)) {
const available = materials[matId] || 0;
if (available < required) {
missingMaterials[matId] = required - available;
canCraft = false;
}
}
const missingMana = Math.max(0, recipe.manaCost - rawMana);
if (missingMana > 0) {
canCraft = false;
}
return { canCraft, missingMaterials, missingMana };
}
// Get all recipes available based on unlocked blueprints
export function getAvailableRecipes(unlockedBlueprints: string[]): CraftingRecipe[] {
return unlockedBlueprints
.map(bpId => CRAFTING_RECIPES[bpId])
.filter(Boolean);
}
+846
View File
@@ -0,0 +1,846 @@
// ─── Enchantment Effects Catalogue ────────────────────────────────────────────────
// All available enchantment effects that can be applied to equipment
import type { EquipmentCategory } from './equipment'
// Helper to define allowed equipment categories for each effect type
const ALL_CASTER: EquipmentCategory[] = ['caster']
const CASTER_AND_SWORD: EquipmentCategory[] = ['caster', 'sword']
const WEAPON_EQUIPMENT: EquipmentCategory[] = ['caster', 'catalyst', 'sword'] // All main hand equipment
const CASTER_AND_HANDS: EquipmentCategory[] = ['caster', 'hands']
const BODY_AND_SHIELD: EquipmentCategory[] = ['body', 'shield']
const CASTER_CATALYST_ACCESSORY: EquipmentCategory[] = ['caster', 'catalyst', 'accessory']
const MANA_EQUIPMENT: EquipmentCategory[] = ['caster', 'catalyst', 'head', 'body', 'accessory']
const UTILITY_EQUIPMENT: EquipmentCategory[] = ['caster', 'catalyst', 'head', 'body', 'hands', 'feet', 'accessory']
const ALL_EQUIPMENT: EquipmentCategory[] = ['caster', 'shield', 'catalyst', 'sword', 'head', 'body', 'hands', 'feet', 'accessory']
export type EnchantmentEffectCategory = 'spell' | 'mana' | 'combat' | 'elemental' | 'defense' | 'utility' | 'special'
export interface EnchantmentEffectDef {
id: string;
name: string;
description: string;
category: EnchantmentEffectCategory;
baseCapacityCost: number;
maxStacks: number;
allowedEquipmentCategories: EquipmentCategory[];
effect: {
type: 'spell' | 'bonus' | 'multiplier' | 'special';
spellId?: string;
stat?: string;
value?: number;
specialId?: string;
};
}
export const ENCHANTMENT_EFFECTS: Record<string, EnchantmentEffectDef> = {
// ═══════════════════════════════════════════════════════════════════════════
// SPELL EFFECTS - Only for CASTER equipment (staves, wands, rods, orbs)
// ═══════════════════════════════════════════════════════════════════════════
// Tier 0 - Basic Spells
spell_manaBolt: {
id: 'spell_manaBolt',
name: 'Mana Bolt',
description: 'Grants the ability to cast Mana Bolt (5 base damage, raw mana cost)',
category: 'spell',
baseCapacityCost: 50,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'manaBolt' }
},
spell_manaStrike: {
id: 'spell_manaStrike',
name: 'Mana Strike',
description: 'Grants the ability to cast Mana Strike (8 base damage, raw mana cost)',
category: 'spell',
baseCapacityCost: 40,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'manaStrike' }
},
// Tier 1 - Basic Elemental Spells
spell_fireball: {
id: 'spell_fireball',
name: 'Fireball',
description: 'Grants the ability to cast Fireball (15 fire damage)',
category: 'spell',
baseCapacityCost: 80,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'fireball' }
},
spell_emberShot: {
id: 'spell_emberShot',
name: 'Ember Shot',
description: 'Grants the ability to cast Ember Shot (10 fire damage, fast cast)',
category: 'spell',
baseCapacityCost: 60,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'emberShot' }
},
spell_waterJet: {
id: 'spell_waterJet',
name: 'Water Jet',
description: 'Grants the ability to cast Water Jet (12 water damage)',
category: 'spell',
baseCapacityCost: 70,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'waterJet' }
},
spell_iceShard: {
id: 'spell_iceShard',
name: 'Ice Shard',
description: 'Grants the ability to cast Ice Shard (14 water damage)',
category: 'spell',
baseCapacityCost: 75,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'iceShard' }
},
spell_gust: {
id: 'spell_gust',
name: 'Gust',
description: 'Grants the ability to cast Gust (10 air damage, fast cast)',
category: 'spell',
baseCapacityCost: 60,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'gust' }
},
spell_stoneBullet: {
id: 'spell_stoneBullet',
name: 'Stone Bullet',
description: 'Grants the ability to cast Stone Bullet (16 earth damage)',
category: 'spell',
baseCapacityCost: 80,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'stoneBullet' }
},
spell_lightLance: {
id: 'spell_lightLance',
name: 'Light Lance',
description: 'Grants the ability to cast Light Lance (18 light damage)',
category: 'spell',
baseCapacityCost: 95,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'lightLance' }
},
spell_shadowBolt: {
id: 'spell_shadowBolt',
name: 'Shadow Bolt',
description: 'Grants the ability to cast Shadow Bolt (16 dark damage)',
category: 'spell',
baseCapacityCost: 95,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'shadowBolt' }
},
spell_drain: {
id: 'spell_drain',
name: 'Drain',
description: 'Grants the ability to cast Drain (10 death damage)',
category: 'spell',
baseCapacityCost: 85,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'drain' }
},
// Tier 2 - Advanced Spells
spell_inferno: {
id: 'spell_inferno',
name: 'Inferno',
description: 'Grants the ability to cast Inferno (60 fire damage)',
category: 'spell',
baseCapacityCost: 180,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'inferno' }
},
spell_tidalWave: {
id: 'spell_tidalWave',
name: 'Tidal Wave',
description: 'Grants the ability to cast Tidal Wave (55 water damage)',
category: 'spell',
baseCapacityCost: 175,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'tidalWave' }
},
spell_hurricane: {
id: 'spell_hurricane',
name: 'Hurricane',
description: 'Grants the ability to cast Hurricane (50 air damage)',
category: 'spell',
baseCapacityCost: 170,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'hurricane' }
},
spell_earthquake: {
id: 'spell_earthquake',
name: 'Earthquake',
description: 'Grants the ability to cast Earthquake (70 earth damage)',
category: 'spell',
baseCapacityCost: 200,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'earthquake' }
},
spell_solarFlare: {
id: 'spell_solarFlare',
name: 'Solar Flare',
description: 'Grants the ability to cast Solar Flare (65 light damage)',
category: 'spell',
baseCapacityCost: 190,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'solarFlare' }
},
spell_voidRift: {
id: 'spell_voidRift',
name: 'Void Rift',
description: 'Grants the ability to cast Void Rift (55 dark damage)',
category: 'spell',
baseCapacityCost: 175,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'voidRift' }
},
// Additional Tier 1 Spells
spell_windSlash: {
id: 'spell_windSlash',
name: 'Wind Slash',
description: 'Grants the ability to cast Wind Slash (12 air damage)',
category: 'spell',
baseCapacityCost: 72,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'windSlash' }
},
spell_rockSpike: {
id: 'spell_rockSpike',
name: 'Rock Spike',
description: 'Grants the ability to cast Rock Spike (18 earth damage)',
category: 'spell',
baseCapacityCost: 88,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'rockSpike' }
},
spell_radiance: {
id: 'spell_radiance',
name: 'Radiance',
description: 'Grants the ability to cast Radiance (14 light damage)',
category: 'spell',
baseCapacityCost: 80,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'radiance' }
},
spell_darkPulse: {
id: 'spell_darkPulse',
name: 'Dark Pulse',
description: 'Grants the ability to cast Dark Pulse (12 dark damage)',
category: 'spell',
baseCapacityCost: 68,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'darkPulse' }
},
// Additional Tier 2 Spells
spell_flameWave: {
id: 'spell_flameWave',
name: 'Flame Wave',
description: 'Grants the ability to cast Flame Wave (45 fire damage)',
category: 'spell',
baseCapacityCost: 165,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'flameWave' }
},
spell_iceStorm: {
id: 'spell_iceStorm',
name: 'Ice Storm',
description: 'Grants the ability to cast Ice Storm (50 water damage)',
category: 'spell',
baseCapacityCost: 170,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'iceStorm' }
},
spell_windBlade: {
id: 'spell_windBlade',
name: 'Wind Blade',
description: 'Grants the ability to cast Wind Blade (40 air damage)',
category: 'spell',
baseCapacityCost: 155,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'windBlade' }
},
spell_stoneBarrage: {
id: 'spell_stoneBarrage',
name: 'Stone Barrage',
description: 'Grants the ability to cast Stone Barrage (55 earth damage)',
category: 'spell',
baseCapacityCost: 175,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'stoneBarrage' }
},
spell_divineSmite: {
id: 'spell_divineSmite',
name: 'Divine Smite',
description: 'Grants the ability to cast Divine Smite (55 light damage)',
category: 'spell',
baseCapacityCost: 175,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'divineSmite' }
},
spell_shadowStorm: {
id: 'spell_shadowStorm',
name: 'Shadow Storm',
description: 'Grants the ability to cast Shadow Storm (48 dark damage)',
category: 'spell',
baseCapacityCost: 168,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'shadowStorm' }
},
// Tier 3 - Master Spells
spell_pyroclasm: {
id: 'spell_pyroclasm',
name: 'Pyroclasm',
description: 'Grants the ability to cast Pyroclasm (250 fire damage)',
category: 'spell',
baseCapacityCost: 400,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'pyroclasm' }
},
spell_tsunami: {
id: 'spell_tsunami',
name: 'Tsunami',
description: 'Grants the ability to cast Tsunami (220 water damage)',
category: 'spell',
baseCapacityCost: 380,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'tsunami' }
},
spell_meteorStrike: {
id: 'spell_meteorStrike',
name: 'Meteor Strike',
description: 'Grants the ability to cast Meteor Strike (280 earth damage)',
category: 'spell',
baseCapacityCost: 420,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'meteorStrike' }
},
// ═══════════════════════════════════════════════════════════════════════════
// MANA EFFECTS - Boost mana capacity and regeneration
// ═══════════════════════════════════════════════════════════════════════════
mana_cap_50: {
id: 'mana_cap_50',
name: 'Mana Reserve',
description: '+50 maximum mana',
category: 'mana',
baseCapacityCost: 20,
maxStacks: 3,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'maxMana', value: 50 }
},
mana_cap_100: {
id: 'mana_cap_100',
name: 'Mana Reservoir',
description: '+100 maximum mana',
category: 'mana',
baseCapacityCost: 35,
maxStacks: 3,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'maxMana', value: 100 }
},
mana_regen_1: {
id: 'mana_regen_1',
name: 'Trickle',
description: '+1 mana per hour regeneration',
category: 'mana',
baseCapacityCost: 15,
maxStacks: 5,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'regen', value: 1 }
},
mana_regen_2: {
id: 'mana_regen_2',
name: 'Stream',
description: '+2 mana per hour regeneration',
category: 'mana',
baseCapacityCost: 28,
maxStacks: 4,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'regen', value: 2 }
},
mana_regen_5: {
id: 'mana_regen_5',
name: 'River',
description: '+5 mana per hour regeneration',
category: 'mana',
baseCapacityCost: 50,
maxStacks: 3,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'regen', value: 5 }
},
click_mana_1: {
id: 'click_mana_1',
name: 'Mana Tap',
description: '+1 mana per click',
category: 'mana',
baseCapacityCost: 20,
maxStacks: 5,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'clickMana', value: 1 }
},
click_mana_3: {
id: 'click_mana_3',
name: 'Mana Surge',
description: '+3 mana per click',
category: 'mana',
baseCapacityCost: 35,
maxStacks: 3,
allowedEquipmentCategories: MANA_EQUIPMENT,
effect: { type: 'bonus', stat: 'clickMana', value: 3 }
},
// ═══════════════════════════════════════════════════════════════════════════
// COMBAT EFFECTS - Damage and attack enhancements
// ═══════════════════════════════════════════════════════════════════════════
damage_5: {
id: 'damage_5',
name: 'Minor Power',
description: '+5 base damage',
category: 'combat',
baseCapacityCost: 15,
maxStacks: 5,
allowedEquipmentCategories: CASTER_AND_HANDS,
effect: { type: 'bonus', stat: 'baseDamage', value: 5 }
},
damage_10: {
id: 'damage_10',
name: 'Moderate Power',
description: '+10 base damage',
category: 'combat',
baseCapacityCost: 28,
maxStacks: 4,
allowedEquipmentCategories: CASTER_AND_HANDS,
effect: { type: 'bonus', stat: 'baseDamage', value: 10 }
},
damage_pct_10: {
id: 'damage_pct_10',
name: 'Amplification',
description: '+10% damage',
category: 'combat',
baseCapacityCost: 30,
maxStacks: 3,
allowedEquipmentCategories: CASTER_AND_HANDS,
effect: { type: 'multiplier', stat: 'baseDamage', value: 1.10 }
},
crit_5: {
id: 'crit_5',
name: 'Sharp Edge',
description: '+5% critical hit chance',
category: 'combat',
baseCapacityCost: 20,
maxStacks: 4,
allowedEquipmentCategories: CASTER_AND_HANDS,
effect: { type: 'bonus', stat: 'critChance', value: 0.05 }
},
attack_speed_10: {
id: 'attack_speed_10',
name: 'Swift Casting',
description: '+10% attack speed',
category: 'combat',
baseCapacityCost: 22,
maxStacks: 4,
allowedEquipmentCategories: CASTER_AND_HANDS,
effect: { type: 'multiplier', stat: 'attackSpeed', value: 1.10 }
},
// ═══════════════════════════════════════════════════════════════════════════
// UTILITY EFFECTS - Study speed, insight, meditation
// ═══════════════════════════════════════════════════════════════════════════
meditate_10: {
id: 'meditate_10',
name: 'Meditative Focus',
description: '+10% meditation efficiency',
category: 'utility',
baseCapacityCost: 18,
maxStacks: 5,
allowedEquipmentCategories: ['head', 'body', 'accessory'],
effect: { type: 'multiplier', stat: 'meditationEfficiency', value: 1.10 }
},
study_10: {
id: 'study_10',
name: 'Quick Study',
description: '+10% study speed',
category: 'utility',
baseCapacityCost: 22,
maxStacks: 4,
allowedEquipmentCategories: UTILITY_EQUIPMENT,
effect: { type: 'multiplier', stat: 'studySpeed', value: 1.10 }
},
insight_5: {
id: 'insight_5',
name: 'Insightful',
description: '+5% insight gain',
category: 'utility',
baseCapacityCost: 25,
maxStacks: 4,
allowedEquipmentCategories: ['head', 'accessory'],
effect: { type: 'multiplier', stat: 'insightGain', value: 1.05 }
},
// ═══════════════════════════════════════════════════════════════════════════
// SPECIAL EFFECTS - Unique and powerful effects
// ═══════════════════════════════════════════════════════════════════════════
spell_echo_10: {
id: 'spell_echo_10',
name: 'Echo Chamber',
description: '10% chance to cast a spell twice',
category: 'special',
baseCapacityCost: 60,
maxStacks: 2,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'special', specialId: 'spellEcho10' }
},
guardian_dmg_10: {
id: 'guardian_dmg_10',
name: 'Bane',
description: '+10% damage to guardians',
category: 'special',
baseCapacityCost: 35,
maxStacks: 3,
allowedEquipmentCategories: CASTER_CATALYST_ACCESSORY,
effect: { type: 'multiplier', stat: 'guardianDamage', value: 1.10 }
},
overpower_80: {
id: 'overpower_80',
name: 'Overpower',
description: '+50% damage when mana above 80%',
category: 'special',
baseCapacityCost: 55,
maxStacks: 1,
allowedEquipmentCategories: CASTER_AND_HANDS,
effect: { type: 'special', specialId: 'overpower' }
},
// ═══════════════════════════════════════════════════════════════════════════
// WEAPON MANA EFFECTS - Enchanter level 3+ unlocks these
// These add mana capacity and regeneration to weapons for their enchantments
// ═══════════════════════════════════════════════════════════════════════════
weapon_mana_cap_20: {
id: 'weapon_mana_cap_20',
name: 'Mana Cell',
description: '+20 weapon mana capacity (for weapon enchantments)',
category: 'mana',
baseCapacityCost: 25,
maxStacks: 5,
allowedEquipmentCategories: WEAPON_EQUIPMENT,
effect: { type: 'bonus', stat: 'weaponManaMax', value: 20 }
},
weapon_mana_cap_50: {
id: 'weapon_mana_cap_50',
name: 'Mana Vessel',
description: '+50 weapon mana capacity (for weapon enchantments)',
category: 'mana',
baseCapacityCost: 50,
maxStacks: 3,
allowedEquipmentCategories: WEAPON_EQUIPMENT,
effect: { type: 'bonus', stat: 'weaponManaMax', value: 50 }
},
weapon_mana_cap_100: {
id: 'weapon_mana_cap_100',
name: 'Mana Core',
description: '+100 weapon mana capacity (for weapon enchantments)',
category: 'mana',
baseCapacityCost: 80,
maxStacks: 2,
allowedEquipmentCategories: WEAPON_EQUIPMENT,
effect: { type: 'bonus', stat: 'weaponManaMax', value: 100 }
},
weapon_mana_regen_1: {
id: 'weapon_mana_regen_1',
name: 'Mana Wick',
description: '+1 weapon mana regeneration per hour',
category: 'mana',
baseCapacityCost: 20,
maxStacks: 5,
allowedEquipmentCategories: WEAPON_EQUIPMENT,
effect: { type: 'bonus', stat: 'weaponManaRegen', value: 1 }
},
weapon_mana_regen_2: {
id: 'weapon_mana_regen_2',
name: 'Mana Siphon',
description: '+2 weapon mana regeneration per hour',
category: 'mana',
baseCapacityCost: 35,
maxStacks: 3,
allowedEquipmentCategories: WEAPON_EQUIPMENT,
effect: { type: 'bonus', stat: 'weaponManaRegen', value: 2 }
},
weapon_mana_regen_5: {
id: 'weapon_mana_regen_5',
name: 'Mana Well',
description: '+5 weapon mana regeneration per hour',
category: 'mana',
baseCapacityCost: 60,
maxStacks: 2,
allowedEquipmentCategories: WEAPON_EQUIPMENT,
effect: { type: 'bonus', stat: 'weaponManaRegen', value: 5 }
},
// ═══════════════════════════════════════════════════════════════════════════
// LIGHTNING SPELL EFFECTS - Fast, armor-piercing, harder to dodge
// ═══════════════════════════════════════════════════════════════════════════
spell_spark: {
id: 'spell_spark',
name: 'Spark',
description: 'Grants the ability to cast Spark (8 lightning damage, very fast, armor pierce)',
category: 'spell',
baseCapacityCost: 70,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'spark' }
},
spell_lightningBolt: {
id: 'spell_lightningBolt',
name: 'Lightning Bolt',
description: 'Grants the ability to cast Lightning Bolt (14 lightning damage, armor pierce)',
category: 'spell',
baseCapacityCost: 90,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'lightningBolt' }
},
spell_chainLightning: {
id: 'spell_chainLightning',
name: 'Chain Lightning',
description: 'Grants the ability to cast Chain Lightning (25 lightning damage, hits 3 targets)',
category: 'spell',
baseCapacityCost: 160,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'chainLightning' }
},
spell_stormCall: {
id: 'spell_stormCall',
name: 'Storm Call',
description: 'Grants the ability to cast Storm Call (40 lightning damage, hits 2 targets)',
category: 'spell',
baseCapacityCost: 190,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'stormCall' }
},
spell_thunderStrike: {
id: 'spell_thunderStrike',
name: 'Thunder Strike',
description: 'Grants the ability to cast Thunder Strike (150 lightning damage, 50% armor pierce)',
category: 'spell',
baseCapacityCost: 350,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'thunderStrike' }
},
// ═══════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════
// METAL SPELL EFFECTS - Fire + Earth compound, armor pierce focus
// ═══════════════════════════════════════════════════════════════════════════
spell_metalShard: {
id: 'spell_metalShard',
name: 'Metal Shard',
description: 'Grants the ability to cast Metal Shard (16 metal damage, 25% armor pierce)',
category: 'spell',
baseCapacityCost: 85,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'metalShard' }
},
spell_ironFist: {
id: 'spell_ironFist',
name: 'Iron Fist',
description: 'Grants the ability to cast Iron Fist (28 metal damage, 35% armor pierce)',
category: 'spell',
baseCapacityCost: 120,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'ironFist' }
},
spell_steelTempest: {
id: 'spell_steelTempest',
name: 'Steel Tempest',
description: 'Grants the ability to cast Steel Tempest (55 metal damage, 45% armor pierce)',
category: 'spell',
baseCapacityCost: 190,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'steelTempest' }
},
spell_furnaceBlast: {
id: 'spell_furnaceBlast',
name: 'Furnace Blast',
description: 'Grants the ability to cast Furnace Blast (200 metal damage, 60% armor pierce)',
category: 'spell',
baseCapacityCost: 400,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'furnaceBlast' }
},
// ═══════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════
// SAND SPELL EFFECTS - Earth + Water compound, AOE focus
// ═══════════════════════════════════════════════════════════════════════════
spell_sandBlast: {
id: 'spell_sandBlast',
name: 'Sand Blast',
description: 'Grants the ability to cast Sand Blast (11 sand damage, very fast)',
category: 'spell',
baseCapacityCost: 72,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'sandBlast' }
},
spell_sandstorm: {
id: 'spell_sandstorm',
name: 'Sandstorm',
description: 'Grants the ability to cast Sandstorm (22 sand damage, hits 2 enemies)',
category: 'spell',
baseCapacityCost: 100,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'sandstorm' }
},
spell_desertWind: {
id: 'spell_desertWind',
name: 'Desert Wind',
description: 'Grants the ability to cast Desert Wind (38 sand damage, hits 3 enemies)',
category: 'spell',
baseCapacityCost: 155,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'desertWind' }
},
spell_duneCollapse: {
id: 'spell_duneCollapse',
name: 'Dune Collapse',
description: 'Grants the ability to cast Dune Collapse (100 sand damage, hits 5 enemies)',
category: 'spell',
baseCapacityCost: 300,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'duneCollapse' }
},
// ═══════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════
// MAGIC SWORD ENCHANTMENTS - Elemental weapon effects
// ═══════════════════════════════════════════════════════════════════════════
sword_fire: {
id: 'sword_fire',
name: 'Fire Enchant',
description: 'Enchant blade with fire. Burns enemies over time.',
category: 'elemental',
baseCapacityCost: 40,
maxStacks: 1,
allowedEquipmentCategories: CASTER_AND_SWORD,
effect: { type: 'special', specialId: 'fireBlade' }
},
sword_frost: {
id: 'sword_frost',
name: 'Frost Enchant',
description: 'Enchant blade with frost. Prevents enemy dodge.',
category: 'elemental',
baseCapacityCost: 40,
maxStacks: 1,
allowedEquipmentCategories: CASTER_AND_SWORD,
effect: { type: 'special', specialId: 'frostBlade' }
},
sword_lightning: {
id: 'sword_lightning',
name: 'Lightning Enchant',
description: 'Enchant blade with lightning. Pierces 30% armor.',
category: 'elemental',
baseCapacityCost: 50,
maxStacks: 1,
allowedEquipmentCategories: CASTER_AND_SWORD,
effect: { type: 'special', specialId: 'lightningBlade' }
},
sword_void: {
id: 'sword_void',
name: 'Void Enchant',
description: 'Enchant blade with void. +20% damage bonus.',
category: 'elemental',
baseCapacityCost: 60,
maxStacks: 1,
allowedEquipmentCategories: CASTER_AND_SWORD,
effect: { type: 'special', specialId: 'voidBlade' }
},
};
// ─── Helper Functions ────────────────────────────────────────────────────────────
export function getEnchantmentEffect(id: string): EnchantmentEffectDef | undefined {
return ENCHANTMENT_EFFECTS[id];
}
export function getEffectsForEquipment(equipmentCategory: EquipmentCategory): EnchantmentEffectDef[] {
return Object.values(ENCHANTMENT_EFFECTS).filter(effect =>
effect.allowedEquipmentCategories.includes(equipmentCategory)
);
}
export function canApplyEffect(effectId: string, equipmentCategory: EquipmentCategory): boolean {
const effect = ENCHANTMENT_EFFECTS[effectId];
if (!effect) return false;
return effect.allowedEquipmentCategories.includes(equipmentCategory);
}
export function calculateEffectCapacityCost(effectId: string, stacks: number, efficiencyBonus: number = 0): number {
const effect = ENCHANTMENT_EFFECTS[effectId];
if (!effect) return 0;
let totalCost = 0;
for (let i = 0; i < stacks; i++) {
// Each additional stack costs 20% more
const stackMultiplier = 1 + (i * 0.2);
totalCost += effect.baseCapacityCost * stackMultiplier;
}
// Apply efficiency bonus (reduces cost)
return Math.floor(totalCost * (1 - efficiencyBonus));
}
+468
View File
@@ -0,0 +1,468 @@
// ─── Equipment Types ─────────────────────────────────────────────────────────
export type EquipmentSlot = 'mainHand' | 'offHand' | 'head' | 'body' | 'hands' | 'feet' | 'accessory1' | 'accessory2';
export type EquipmentCategory = 'caster' | 'shield' | 'catalyst' | 'sword' | 'head' | 'body' | 'hands' | 'feet' | 'accessory';
// All equipment slots in order
export const EQUIPMENT_SLOTS: EquipmentSlot[] = ['mainHand', 'offHand', 'head', 'body', 'hands', 'feet', 'accessory1', 'accessory2'];
export interface EquipmentType {
id: string;
name: string;
category: EquipmentCategory;
slot: EquipmentSlot;
baseCapacity: number;
description: string;
baseDamage?: number; // For swords
baseCastSpeed?: number; // For swords (higher = faster)
}
// ─── Equipment Types Definition ─────────────────────────────────────────────
export const EQUIPMENT_TYPES: Record<string, EquipmentType> = {
// ─── Main Hand - Casters ─────────────────────────────────────────────────
basicStaff: {
id: 'basicStaff',
name: 'Basic Staff',
category: 'caster',
slot: 'mainHand',
baseCapacity: 50,
description: 'A simple wooden staff, basic but reliable for channeling mana.',
},
apprenticeWand: {
id: 'apprenticeWand',
name: 'Apprentice Wand',
category: 'caster',
slot: 'mainHand',
baseCapacity: 35,
description: 'A lightweight wand favored by apprentices. Lower capacity but faster to prepare.',
},
oakStaff: {
id: 'oakStaff',
name: 'Oak Staff',
category: 'caster',
slot: 'mainHand',
baseCapacity: 65,
description: 'A sturdy oak staff with decent mana capacity.',
},
crystalWand: {
id: 'crystalWand',
name: 'Crystal Wand',
category: 'caster',
slot: 'mainHand',
baseCapacity: 45,
description: 'A wand tipped with a small crystal. Excellent for elemental enchantments.',
},
arcanistStaff: {
id: 'arcanistStaff',
name: 'Arcanist Staff',
category: 'caster',
slot: 'mainHand',
baseCapacity: 80,
description: 'A staff designed for advanced spellcasters. High capacity for complex enchantments.',
},
battlestaff: {
id: 'battlestaff',
name: 'Battlestaff',
category: 'caster',
slot: 'mainHand',
baseCapacity: 70,
description: 'A reinforced staff suitable for both casting and combat.',
},
// ─── Main Hand - Catalysts ────────────────────────────────────────────────
basicCatalyst: {
id: 'basicCatalyst',
name: 'Basic Catalyst',
category: 'catalyst',
slot: 'mainHand',
baseCapacity: 40,
description: 'A simple catalyst for amplifying magical effects.',
},
fireCatalyst: {
id: 'fireCatalyst',
name: 'Fire Catalyst',
category: 'catalyst',
slot: 'mainHand',
baseCapacity: 55,
description: 'A catalyst attuned to fire magic. Enhances fire enchantments.',
},
voidCatalyst: {
id: 'voidCatalyst',
name: 'Void Catalyst',
category: 'catalyst',
slot: 'mainHand',
baseCapacity: 75,
description: 'A rare catalyst touched by void energy. High capacity but volatile.',
},
// ─── Main Hand - Magic Swords ─────────────────────────────────────────────
// Magic swords have low base damage but high cast speed
// They can be enchanted with elemental effects that use mana over time
ironBlade: {
id: 'ironBlade',
name: 'Iron Blade',
category: 'sword',
slot: 'mainHand',
baseCapacity: 30,
baseDamage: 3,
baseCastSpeed: 4,
description: 'A simple iron sword. Can be enchanted with elemental effects.',
},
steelBlade: {
id: 'steelBlade',
name: 'Steel Blade',
category: 'sword',
slot: 'mainHand',
baseCapacity: 40,
baseDamage: 4,
baseCastSpeed: 4,
description: 'A well-crafted steel sword. Balanced for combat and enchanting.',
},
crystalBlade: {
id: 'crystalBlade',
name: 'Crystal Blade',
category: 'sword',
slot: 'mainHand',
baseCapacity: 55,
baseDamage: 3,
baseCastSpeed: 5,
description: 'A blade made of crystallized mana. Excellent for elemental enchantments.',
},
arcanistBlade: {
id: 'arcanistBlade',
name: 'Arcanist Blade',
category: 'sword',
slot: 'mainHand',
baseCapacity: 65,
baseDamage: 5,
baseCastSpeed: 4,
description: 'A sword forged for battle mages. High capacity for powerful enchantments.',
},
voidBlade: {
id: 'voidBlade',
name: 'Void-Touched Blade',
category: 'sword',
slot: 'mainHand',
baseCapacity: 50,
baseDamage: 6,
baseCastSpeed: 3,
description: 'A blade corrupted by void energy. Powerful but consumes more mana.',
},
// ─── Off Hand - Shields ───────────────────────────────────────────────────
basicShield: {
id: 'basicShield',
name: 'Basic Shield',
category: 'shield',
slot: 'offHand',
baseCapacity: 40,
description: 'A simple wooden shield. Provides basic protection.',
},
reinforcedShield: {
id: 'reinforcedShield',
name: 'Reinforced Shield',
category: 'shield',
slot: 'offHand',
baseCapacity: 55,
description: 'A metal-reinforced shield with enhanced durability and capacity.',
},
runicShield: {
id: 'runicShield',
name: 'Runic Shield',
category: 'shield',
slot: 'offHand',
baseCapacity: 70,
description: 'A shield engraved with protective runes. Excellent for defensive enchantments.',
},
manaShield: {
id: 'manaShield',
name: 'Mana Shield',
category: 'shield',
slot: 'offHand',
baseCapacity: 60,
description: 'A crystalline shield that can store and reflect mana.',
},
// ─── Head ─────────────────────────────────────────────────────────────────
clothHood: {
id: 'clothHood',
name: 'Cloth Hood',
category: 'head',
slot: 'head',
baseCapacity: 25,
description: 'A simple cloth hood. Minimal protection but comfortable.',
},
apprenticeCap: {
id: 'apprenticeCap',
name: 'Apprentice Cap',
category: 'head',
slot: 'head',
baseCapacity: 30,
description: 'The traditional cap of magic apprentices.',
},
wizardHat: {
id: 'wizardHat',
name: 'Wizard Hat',
category: 'head',
slot: 'head',
baseCapacity: 45,
description: 'A classic pointed wizard hat. Decent capacity for headwear.',
},
arcanistCirclet: {
id: 'arcanistCirclet',
name: 'Arcanist Circlet',
category: 'head',
slot: 'head',
baseCapacity: 40,
description: 'A silver circlet worn by accomplished arcanists.',
},
battleHelm: {
id: 'battleHelm',
name: 'Battle Helm',
category: 'head',
slot: 'head',
baseCapacity: 50,
description: 'A sturdy helm for battle mages.',
},
// ─── Body ────────────────────────────────────────────────────────────────
civilianShirt: {
id: 'civilianShirt',
name: 'Civilian Shirt',
category: 'body',
slot: 'body',
baseCapacity: 30,
description: 'A plain shirt with minimal magical properties.',
},
apprenticeRobe: {
id: 'apprenticeRobe',
name: 'Apprentice Robe',
category: 'body',
slot: 'body',
baseCapacity: 45,
description: 'The standard robe for magic apprentices.',
},
scholarRobe: {
id: 'scholarRobe',
name: 'Scholar Robe',
category: 'body',
slot: 'body',
baseCapacity: 55,
description: 'A robe worn by scholars and researchers.',
},
battleRobe: {
id: 'battleRobe',
name: 'Battle Robe',
category: 'body',
slot: 'body',
baseCapacity: 65,
description: 'A reinforced robe designed for combat mages.',
},
arcanistRobe: {
id: 'arcanistRobe',
name: 'Arcanist Robe',
category: 'body',
slot: 'body',
baseCapacity: 80,
description: 'An ornate robe for master arcanists. High capacity for body armor.',
},
// ─── Hands ───────────────────────────────────────────────────────────────
civilianGloves: {
id: 'civilianGloves',
name: 'Civilian Gloves',
category: 'hands',
slot: 'hands',
baseCapacity: 20,
description: 'Simple cloth gloves. Minimal magical capacity.',
},
apprenticeGloves: {
id: 'apprenticeGloves',
name: 'Apprentice Gloves',
category: 'hands',
slot: 'hands',
baseCapacity: 30,
description: 'Basic gloves for handling magical components.',
},
spellweaveGloves: {
id: 'spellweaveGloves',
name: 'Spellweave Gloves',
category: 'hands',
slot: 'hands',
baseCapacity: 40,
description: 'Gloves woven with mana-conductive threads.',
},
combatGauntlets: {
id: 'combatGauntlets',
name: 'Combat Gauntlets',
category: 'hands',
slot: 'hands',
baseCapacity: 35,
description: 'Armored gauntlets for battle mages.',
},
// ─── Feet ────────────────────────────────────────────────────────────────
civilianShoes: {
id: 'civilianShoes',
name: 'Civilian Shoes',
category: 'feet',
slot: 'feet',
baseCapacity: 15,
description: 'Simple leather shoes. No special properties.',
},
apprenticeBoots: {
id: 'apprenticeBoots',
name: 'Apprentice Boots',
category: 'feet',
slot: 'feet',
baseCapacity: 25,
description: 'Basic boots for magic students.',
},
travelerBoots: {
id: 'travelerBoots',
name: 'Traveler Boots',
category: 'feet',
slot: 'feet',
baseCapacity: 30,
description: 'Comfortable boots for long journeys.',
},
battleBoots: {
id: 'battleBoots',
name: 'Battle Boots',
category: 'feet',
slot: 'feet',
baseCapacity: 35,
description: 'Sturdy boots for combat situations.',
},
// ─── Accessories ────────────────────────────────────────────────────────
copperRing: {
id: 'copperRing',
name: 'Copper Ring',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 15,
description: 'A simple copper ring. Basic capacity for accessories.',
},
silverRing: {
id: 'silverRing',
name: 'Silver Ring',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 25,
description: 'A silver ring with decent magical conductivity.',
},
goldRing: {
id: 'goldRing',
name: 'Gold Ring',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 35,
description: 'A gold ring with excellent magical properties.',
},
signetRing: {
id: 'signetRing',
name: 'Signet Ring',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 30,
description: 'A ring bearing a magical sigil.',
},
copperAmulet: {
id: 'copperAmulet',
name: 'Copper Amulet',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 20,
description: 'A simple copper amulet on a leather cord.',
},
silverAmulet: {
id: 'silverAmulet',
name: 'Silver Amulet',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 30,
description: 'A silver amulet with a small gem.',
},
crystalPendant: {
id: 'crystalPendant',
name: 'Crystal Pendant',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 45,
description: 'A pendant with a mana-infused crystal.',
},
manaBrooch: {
id: 'manaBrooch',
name: 'Mana Brooch',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 40,
description: 'A decorative brooch that can hold enchantments.',
},
arcanistPendant: {
id: 'arcanistPendant',
name: 'Arcanist Pendant',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 55,
description: 'A powerful pendant worn by master arcanists.',
},
voidTouchedRing: {
id: 'voidTouchedRing',
name: 'Void-Touched Ring',
category: 'accessory',
slot: 'accessory1',
baseCapacity: 50,
description: 'A ring corrupted by void energy. High capacity but risky.',
},
};
// ─── Helper Functions ─────────────────────────────────────────────────────────
export function getEquipmentType(id: string): EquipmentType | undefined {
return EQUIPMENT_TYPES[id];
}
export function getEquipmentByCategory(category: EquipmentCategory): EquipmentType[] {
return Object.values(EQUIPMENT_TYPES).filter(e => e.category === category);
}
export function getEquipmentBySlot(slot: EquipmentSlot): EquipmentType[] {
return Object.values(EQUIPMENT_TYPES).filter(e => e.slot === slot);
}
export function getAllEquipmentTypes(): EquipmentType[] {
return Object.values(EQUIPMENT_TYPES);
}
// Get valid slots for a category
export function getValidSlotsForCategory(category: EquipmentCategory): EquipmentSlot[] {
switch (category) {
case 'caster':
case 'catalyst':
case 'sword':
return ['mainHand'];
case 'shield':
return ['offHand'];
case 'head':
return ['head'];
case 'body':
return ['body'];
case 'hands':
return ['hands'];
case 'feet':
return ['feet'];
case 'accessory':
return ['accessory1', 'accessory2'];
default:
return [];
}
}
// Check if an equipment type can be equipped in a specific slot
export function canEquipInSlot(equipmentType: EquipmentType, slot: EquipmentSlot): boolean {
const validSlots = getValidSlotsForCategory(equipmentType.category);
return validSlots.includes(slot);
}
+519
View File
@@ -0,0 +1,519 @@
// ─── Familiar Definitions ───────────────────────────────────────────────────────
// Magical companions that provide passive bonuses and active assistance
import type { FamiliarDef, FamiliarAbility } from '../types';
// ─── Familiar Abilities ─────────────────────────────────────────────────────────
const ABILITIES = {
// Combat abilities
damageBonus: (base: number, scaling: number): FamiliarAbility => ({
type: 'damageBonus',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% damage (+${scaling}% per level)`,
}),
critChance: (base: number, scaling: number): FamiliarAbility => ({
type: 'critChance',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% crit chance (+${scaling}% per level)`,
}),
critDamage: (base: number, scaling: number): FamiliarAbility => ({
type: 'critDamage',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% crit damage (+${scaling}% per level)`,
}),
castSpeed: (base: number, scaling: number): FamiliarAbility => ({
type: 'castSpeed',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% cast speed (+${scaling}% per level)`,
}),
elementalBonus: (base: number, scaling: number): FamiliarAbility => ({
type: 'elementalBonus',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% elemental damage (+${scaling}% per level)`,
}),
guardianDamage: (base: number, scaling: number): FamiliarAbility => ({
type: 'guardianDamage',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% damage to guardians (+${scaling}% per level)`,
}),
manaSiphon: (base: number, scaling: number): FamiliarAbility => ({
type: 'manaSiphon',
baseValue: base,
scalingPerLevel: scaling,
desc: `Restore ${base}% of damage as mana (+${scaling}% per level)`,
}),
barrierBreaker: (base: number, scaling: number): FamiliarAbility => ({
type: 'barrierBreaker',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% damage to barriers (+${scaling}% per level)`,
}),
// Mana abilities
manaRegen: (base: number, scaling: number): FamiliarAbility => ({
type: 'manaRegen',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base} mana regen (+${scaling} per level)`,
}),
autoGather: (base: number, scaling: number): FamiliarAbility => ({
type: 'autoGather',
baseValue: base,
scalingPerLevel: scaling,
desc: `Auto-gather ${base} mana/hour (+${scaling} per level)`,
}),
autoConvert: (base: number, scaling: number): FamiliarAbility => ({
type: 'autoConvert',
baseValue: base,
scalingPerLevel: scaling,
desc: `Auto-convert ${base} mana/hour (+${scaling} per level)`,
}),
maxManaBonus: (base: number, scaling: number): FamiliarAbility => ({
type: 'maxManaBonus',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base} max mana (+${scaling} per level)`,
}),
// Support abilities
bonusGold: (base: number, scaling: number): FamiliarAbility => ({
type: 'bonusGold',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% insight gain (+${scaling}% per level)`,
}),
studySpeed: (base: number, scaling: number): FamiliarAbility => ({
type: 'studySpeed',
baseValue: base,
scalingPerLevel: scaling,
desc: `+${base}% study speed (+${scaling}% per level)`,
}),
};
// ─── Familiar Definitions ───────────────────────────────────────────────────────
export const FAMILIARS_DEF: Record<string, FamiliarDef> = {
// === COMMON FAMILIARS (Tier 1) ===
// Mana Wisps - Basic mana helpers
manaWisp: {
id: 'manaWisp',
name: 'Mana Wisp',
desc: 'A gentle spirit of pure mana that drifts lazily through the air.',
role: 'mana',
element: 'raw',
rarity: 'common',
abilities: [
ABILITIES.manaRegen(0.5, 0.1),
],
baseStats: { power: 10, bond: 15 },
unlockCondition: { type: 'mana', value: 100 },
flavorText: 'It hums with quiet contentment, barely visible in dim light.',
},
fireSpark: {
id: 'fireSpark',
name: 'Fire Spark',
desc: 'A tiny ember given life, crackling with barely contained energy.',
role: 'combat',
element: 'fire',
rarity: 'common',
abilities: [
ABILITIES.damageBonus(2, 0.5),
],
baseStats: { power: 12, bond: 10 },
unlockCondition: { type: 'floor', value: 5 },
flavorText: 'It bounces excitedly, leaving scorch marks on everything it touches.',
},
waterDroplet: {
id: 'waterDroplet',
name: 'Water Droplet',
desc: 'A perfect sphere of living water that never seems to evaporate.',
role: 'support',
element: 'water',
rarity: 'common',
abilities: [
ABILITIES.manaRegen(0.3, 0.1),
ABILITIES.manaSiphon(2, 0.5),
],
baseStats: { power: 8, bond: 12 },
unlockCondition: { type: 'floor', value: 3 },
flavorText: 'Ripples spread across its surface with each spell you cast.',
},
earthPebble: {
id: 'earthPebble',
name: 'Earth Pebble',
desc: 'A small stone with a surprisingly friendly personality.',
role: 'guardian',
element: 'earth',
rarity: 'common',
abilities: [
ABILITIES.guardianDamage(3, 0.8),
],
baseStats: { power: 15, bond: 8 },
unlockCondition: { type: 'floor', value: 8 },
flavorText: 'It occasionally rolls itself to a new position when bored.',
},
// === UNCOMMON FAMILIARS (Tier 2) ===
flameImp: {
id: 'flameImp',
name: 'Flame Imp',
desc: 'A mischievous fire spirit that delights in destruction.',
role: 'combat',
element: 'fire',
rarity: 'uncommon',
abilities: [
ABILITIES.damageBonus(4, 0.8),
ABILITIES.elementalBonus(3, 0.6),
],
baseStats: { power: 25, bond: 12 },
unlockCondition: { type: 'floor', value: 15 },
flavorText: 'It cackles with glee whenever you defeat an enemy.',
},
windSylph: {
id: 'windSylph',
name: 'Wind Sylph',
desc: 'An airy spirit that moves like a gentle breeze.',
role: 'support',
element: 'air',
rarity: 'uncommon',
abilities: [
ABILITIES.castSpeed(3, 0.6),
],
baseStats: { power: 20, bond: 15 },
unlockCondition: { type: 'floor', value: 12 },
flavorText: 'Its laughter sounds like wind chimes in a storm.',
},
manaSprite: {
id: 'manaSprite',
name: 'Mana Sprite',
desc: 'A more evolved mana spirit with a playful nature.',
role: 'mana',
element: 'raw',
rarity: 'uncommon',
abilities: [
ABILITIES.manaRegen(1, 0.2),
ABILITIES.autoGather(2, 0.5),
],
baseStats: { power: 18, bond: 18 },
unlockCondition: { type: 'mana', value: 1000 },
flavorText: 'It sometimes tickles your ear with invisible hands.',
},
crystalGolem: {
id: 'crystalGolem',
name: 'Crystal Golem',
desc: 'A small construct made of crystallized mana.',
role: 'guardian',
element: 'crystal',
rarity: 'uncommon',
abilities: [
ABILITIES.guardianDamage(5, 1),
ABILITIES.barrierBreaker(8, 1.5),
],
baseStats: { power: 30, bond: 10 },
unlockCondition: { type: 'floor', value: 20 },
flavorText: 'Light refracts through its body in mesmerizing patterns.',
},
// === RARE FAMILIARS (Tier 3) ===
phoenixHatchling: {
id: 'phoenixHatchling',
name: 'Phoenix Hatchling',
desc: 'A young phoenix, still learning to control its flames.',
role: 'combat',
element: 'fire',
rarity: 'rare',
abilities: [
ABILITIES.damageBonus(6, 1.2),
ABILITIES.critDamage(15, 3),
],
baseStats: { power: 40, bond: 15 },
unlockCondition: { type: 'floor', value: 30 },
flavorText: 'Tiny flames dance around its feathers as it practices flying.',
},
frostWisp: {
id: 'frostWisp',
name: 'Frost Wisp',
desc: 'A spirit of eternal winter, beautiful and deadly.',
role: 'combat',
element: 'water',
rarity: 'rare',
abilities: [
ABILITIES.elementalBonus(8, 1.5),
ABILITIES.castSpeed(4, 0.8),
],
baseStats: { power: 35, bond: 12 },
unlockCondition: { type: 'floor', value: 25 },
flavorText: 'Frost patterns appear on surfaces wherever it lingers.',
},
manaElemental: {
id: 'manaElemental',
name: 'Mana Elemental',
desc: 'A concentrated form of pure magical energy.',
role: 'mana',
element: 'raw',
rarity: 'rare',
abilities: [
ABILITIES.manaRegen(2, 0.4),
ABILITIES.autoGather(5, 1),
ABILITIES.autoConvert(2, 0.5),
],
baseStats: { power: 30, bond: 20 },
unlockCondition: { type: 'mana', value: 5000 },
flavorText: 'Reality seems to bend slightly around its fluctuating form.',
},
shieldGuardian: {
id: 'shieldGuardian',
name: 'Stone Guardian',
desc: 'A loyal protector carved from enchanted stone.',
role: 'guardian',
element: 'earth',
rarity: 'rare',
abilities: [
ABILITIES.guardianDamage(8, 1.5),
ABILITIES.barrierBreaker(12, 2),
],
baseStats: { power: 50, bond: 8 },
unlockCondition: { type: 'floor', value: 35 },
flavorText: 'It stands motionless for hours, then suddenly moves to strike.',
},
// === EPIC FAMILIARS (Tier 4) ===
infernoDrake: {
id: 'infernoDrake',
name: 'Inferno Drake',
desc: 'A small dragon wreathed in eternal flames.',
role: 'combat',
element: 'fire',
rarity: 'epic',
abilities: [
ABILITIES.damageBonus(10, 2),
ABILITIES.elementalBonus(12, 2),
ABILITIES.critChance(3, 0.6),
],
baseStats: { power: 60, bond: 12 },
unlockCondition: { type: 'floor', value: 50 },
flavorText: 'Smoke occasionally drifts from its nostrils as it dreams of conquest.',
},
starlightSerpent: {
id: 'starlightSerpent',
name: 'Starlight Serpent',
desc: 'A serpentine creature formed from captured starlight.',
role: 'support',
element: 'stellar',
rarity: 'epic',
abilities: [
ABILITIES.castSpeed(8, 1.5),
ABILITIES.bonusGold(5, 1),
ABILITIES.manaRegen(1.5, 0.3),
],
baseStats: { power: 45, bond: 25 },
unlockCondition: { type: 'floor', value: 45 },
flavorText: 'It traces constellations in the air with its glowing body.',
},
voidWalker: {
id: 'voidWalker',
name: 'Void Walker',
desc: 'A being that exists partially outside normal reality.',
role: 'mana',
element: 'void',
rarity: 'epic',
abilities: [
ABILITIES.manaRegen(3, 0.6),
ABILITIES.autoGather(10, 2),
ABILITIES.maxManaBonus(50, 10),
],
baseStats: { power: 55, bond: 15 },
unlockCondition: { type: 'floor', value: 55 },
flavorText: 'It sometimes disappears entirely, only to reappear moments later.',
},
ancientGolem: {
id: 'ancientGolem',
name: 'Ancient Golem',
desc: 'A construct from a forgotten age, still following its prime directive.',
role: 'guardian',
element: 'earth',
rarity: 'epic',
abilities: [
ABILITIES.guardianDamage(15, 3),
ABILITIES.barrierBreaker(20, 4),
ABILITIES.damageBonus(5, 1),
],
baseStats: { power: 80, bond: 6 },
unlockCondition: { type: 'floor', value: 60 },
flavorText: 'Ancient runes glow faintly across its weathered surface.',
},
// === LEGENDARY FAMILIARS (Tier 5) ===
primordialPhoenix: {
id: 'primordialPhoenix',
name: 'Primordial Phoenix',
desc: 'An ancient fire bird, reborn countless times through the ages.',
role: 'combat',
element: 'fire',
rarity: 'legendary',
abilities: [
ABILITIES.damageBonus(15, 3),
ABILITIES.elementalBonus(20, 4),
ABILITIES.critDamage(30, 5),
ABILITIES.critChance(5, 1),
],
baseStats: { power: 100, bond: 20 },
unlockCondition: { type: 'pact', value: 25 }, // Guardian floor 25
flavorText: 'Its eyes hold the wisdom of a thousand lifetimes.',
},
leviathanSpawn: {
id: 'leviathanSpawn',
name: 'Leviathan Spawn',
desc: 'The offspring of an ancient sea god, still growing into its power.',
role: 'mana',
element: 'water',
rarity: 'legendary',
abilities: [
ABILITIES.manaRegen(5, 1),
ABILITIES.autoGather(20, 4),
ABILITIES.autoConvert(8, 1.5),
ABILITIES.maxManaBonus(100, 20),
],
baseStats: { power: 90, bond: 18 },
unlockCondition: { type: 'pact', value: 50 },
flavorText: 'The air around it always smells of salt and deep ocean.',
},
celestialGuardian: {
id: 'celestialGuardian',
name: 'Celestial Guardian',
desc: 'A divine protector sent by powers beyond mortal comprehension.',
role: 'guardian',
element: 'light',
rarity: 'legendary',
abilities: [
ABILITIES.guardianDamage(25, 5),
ABILITIES.barrierBreaker(30, 6),
ABILITIES.damageBonus(10, 2),
ABILITIES.critChance(8, 1.5),
],
baseStats: { power: 120, bond: 12 },
unlockCondition: { type: 'pact', value: 75 },
flavorText: 'It radiates an aura of absolute judgment.',
},
voidEmperor: {
id: 'voidEmperor',
name: 'Void Emperor',
desc: 'A ruler from the spaces between dimensions, bound to your service.',
role: 'support',
element: 'void',
rarity: 'legendary',
abilities: [
ABILITIES.castSpeed(15, 3),
ABILITIES.bonusGold(15, 3),
ABILITIES.manaRegen(4, 0.8),
ABILITIES.critChance(8, 1.5),
],
baseStats: { power: 85, bond: 25 },
unlockCondition: { type: 'floor', value: 90 },
flavorText: 'It regards reality with the detached interest of a god.',
},
};
// ─── Helper Functions ───────────────────────────────────────────────────────────
// Get XP required for next familiar level
export function getFamiliarXpRequired(level: number): number {
// Exponential scaling: 100 * 1.5^(level-1)
return Math.floor(100 * Math.pow(1.5, level - 1));
}
// Get bond required for next bond level (1-100)
export function getBondRequired(currentBond: number): number {
// Linear scaling, every 10 bond requires more time
const bondTier = Math.floor(currentBond / 10);
return 100 + bondTier * 50; // Base 100, +50 per tier
}
// Calculate familiar's ability value at given level and ability level
export function getFamiliarAbilityValue(
ability: FamiliarAbility,
familiarLevel: number,
abilityLevel: number
): number {
// Base value + (familiar level bonus) + (ability level bonus)
const familiarBonus = Math.floor(familiarLevel / 10) * ability.scalingPerLevel;
const abilityBonus = (abilityLevel - 1) * ability.scalingPerLevel * 2;
return ability.baseValue + familiarBonus + abilityBonus;
}
// Get all familiars of a specific rarity
export function getFamiliarsByRarity(rarity: FamiliarDef['rarity']): FamiliarDef[] {
return Object.values(FAMILIARS_DEF).filter(f => f.rarity === rarity);
}
// Get all familiars of a specific role
export function getFamiliarsByRole(role: FamiliarRole): FamiliarDef[] {
return Object.values(FAMILIARS_DEF).filter(f => f.role === role);
}
// Check if player meets unlock condition for a familiar
export function canUnlockFamiliar(
familiar: FamiliarDef,
maxFloor: number,
signedPacts: number[],
totalManaGathered: number,
skillsLearned: number
): boolean {
if (!familiar.unlockCondition) return true;
const { type, value } = familiar.unlockCondition;
switch (type) {
case 'floor':
return maxFloor >= value;
case 'pact':
return signedPacts.includes(value);
case 'mana':
return totalManaGathered >= value;
case 'study':
return skillsLearned >= value;
default:
return false;
}
}
// Starting familiar (given to new players)
export const STARTING_FAMILIAR = 'manaWisp';
+471
View File
@@ -0,0 +1,471 @@
// ─── Golem Definitions ─────────────────────────────────────────────────────────
// Golems are magical constructs that fight alongside the player
// They cost mana to summon and maintain
import type { SpellCost } from '../types';
// Golem mana cost helper
function elemCost(element: string, amount: number): SpellCost {
return { type: 'element', element, amount };
}
function rawCost(amount: number): SpellCost {
return { type: 'raw', amount };
}
export interface GolemManaCost {
type: 'raw' | 'element';
element?: string;
amount: number;
}
export interface GolemDef {
id: string;
name: string;
description: string;
baseManaType: string; // The primary mana type this golem uses
summonCost: GolemManaCost[]; // Cost to summon (can be multiple types)
maintenanceCost: GolemManaCost[]; // Cost per hour to maintain
damage: number; // Base damage per attack
attackSpeed: number; // Attacks per hour
hp: number; // Golem HP (for display, they don't take damage)
armorPierce: number; // Armor piercing (0-1)
isAoe: boolean; // Whether golem attacks are AOE
aoeTargets: number; // Number of targets for AOE
unlockCondition: {
type: 'attunement_level' | 'mana_unlocked' | 'dual_attunement';
attunement?: string;
level?: number;
manaType?: string;
attunements?: string[];
levels?: number[];
};
tier: number; // Power tier (1-4)
}
// All golem definitions
export const GOLEMS_DEF: Record<string, GolemDef> = {
// ─── BASE GOLEMS ─────────────────────────────────────────────────────────────
// Earth Golem - Basic, available with Fabricator attunement
earthGolem: {
id: 'earthGolem',
name: 'Earth Golem',
description: 'A sturdy construct of stone and soil. Slow but powerful.',
baseManaType: 'earth',
summonCost: [elemCost('earth', 10)],
maintenanceCost: [elemCost('earth', 0.5)],
damage: 8,
attackSpeed: 1.5,
hp: 50,
armorPierce: 0.15,
isAoe: false,
aoeTargets: 1,
unlockCondition: {
type: 'attunement_level',
attunement: 'fabricator',
level: 2,
},
tier: 1,
},
// ─── ELEMENTAL VARIANT GOLEMS ────────────────────────────────────────────────
// Steel Golem - Metal mana variant
steelGolem: {
id: 'steelGolem',
name: 'Steel Golem',
description: 'Forged from metal, this golem has high armor piercing.',
baseManaType: 'metal',
summonCost: [elemCost('metal', 8), elemCost('earth', 5)],
maintenanceCost: [elemCost('metal', 0.6), elemCost('earth', 0.2)],
damage: 12,
attackSpeed: 1.2,
hp: 60,
armorPierce: 0.35,
isAoe: false,
aoeTargets: 1,
unlockCondition: {
type: 'mana_unlocked',
manaType: 'metal',
},
tier: 2,
},
// Crystal Golem - Crystal mana variant
crystalGolem: {
id: 'crystalGolem',
name: 'Crystal Golem',
description: 'A prismatic construct that deals high damage with precision.',
baseManaType: 'crystal',
summonCost: [elemCost('crystal', 6), elemCost('earth', 3)],
maintenanceCost: [elemCost('crystal', 0.4), elemCost('earth', 0.2)],
damage: 18,
attackSpeed: 1.0,
hp: 40,
armorPierce: 0.25,
isAoe: false,
aoeTargets: 1,
unlockCondition: {
type: 'mana_unlocked',
manaType: 'crystal',
},
tier: 3,
},
// Sand Golem - Sand mana variant
sandGolem: {
id: 'sandGolem',
name: 'Sand Golem',
description: 'A shifting construct of sand particles. Hits multiple enemies.',
baseManaType: 'sand',
summonCost: [elemCost('sand', 8), elemCost('earth', 3)],
maintenanceCost: [elemCost('sand', 0.5), elemCost('earth', 0.2)],
damage: 6,
attackSpeed: 2.0,
hp: 35,
armorPierce: 0.1,
isAoe: true,
aoeTargets: 2,
unlockCondition: {
type: 'mana_unlocked',
manaType: 'sand',
},
tier: 2,
},
// ─── ADVANCED HYBRID GOLEMS ──────────────────────────────────────────────────
// Require Enchanter 5 + Fabricator 5
// Lava Golem - Fire + Earth fusion
lavaGolem: {
id: 'lavaGolem',
name: 'Lava Golem',
description: 'Molten earth and fire combined. Burns enemies over time.',
baseManaType: 'earth',
summonCost: [elemCost('earth', 10), elemCost('fire', 8)],
maintenanceCost: [elemCost('earth', 0.4), elemCost('fire', 0.5)],
damage: 15,
attackSpeed: 1.0,
hp: 70,
armorPierce: 0.2,
isAoe: true,
aoeTargets: 2,
unlockCondition: {
type: 'dual_attunement',
attunements: ['enchanter', 'fabricator'],
levels: [5, 5],
},
tier: 3,
},
// Galvanic Golem - Metal + Lightning fusion
galvanicGolem: {
id: 'galvanicGolem',
name: 'Galvanic Golem',
description: 'A conductive metal construct charged with lightning. Extremely fast attacks.',
baseManaType: 'metal',
summonCost: [elemCost('metal', 8), elemCost('lightning', 6)],
maintenanceCost: [elemCost('metal', 0.3), elemCost('lightning', 0.6)],
damage: 10,
attackSpeed: 3.5,
hp: 45,
armorPierce: 0.45,
isAoe: false,
aoeTargets: 1,
unlockCondition: {
type: 'dual_attunement',
attunements: ['enchanter', 'fabricator'],
levels: [5, 5],
},
tier: 3,
},
// Obsidian Golem - Dark + Earth fusion
obsidianGolem: {
id: 'obsidianGolem',
name: 'Obsidian Golem',
description: 'Volcanic glass animated by shadow. Devastating single-target damage.',
baseManaType: 'earth',
summonCost: [elemCost('earth', 12), elemCost('dark', 6)],
maintenanceCost: [elemCost('earth', 0.3), elemCost('dark', 0.4)],
damage: 25,
attackSpeed: 0.8,
hp: 55,
armorPierce: 0.5,
isAoe: false,
aoeTargets: 1,
unlockCondition: {
type: 'dual_attunement',
attunements: ['enchanter', 'fabricator'],
levels: [5, 5],
},
tier: 4,
},
// Prism Golem - Light + Crystal fusion
prismGolem: {
id: 'prismGolem',
name: 'Prism Golem',
description: 'A radiant crystal construct. Channels light into piercing beams.',
baseManaType: 'crystal',
summonCost: [elemCost('crystal', 10), elemCost('light', 6)],
maintenanceCost: [elemCost('crystal', 0.4), elemCost('light', 0.4)],
damage: 20,
attackSpeed: 1.5,
hp: 50,
armorPierce: 0.35,
isAoe: true,
aoeTargets: 3,
unlockCondition: {
type: 'dual_attunement',
attunements: ['enchanter', 'fabricator'],
levels: [5, 5],
},
tier: 4,
},
// Quicksilver Golem - Water + Metal fusion
quicksilverGolem: {
id: 'quicksilverGolem',
name: 'Quicksilver Golem',
description: 'Liquid metal that flows around defenses. Fast and hard to dodge.',
baseManaType: 'metal',
summonCost: [elemCost('metal', 6), elemCost('water', 6)],
maintenanceCost: [elemCost('metal', 0.3), elemCost('water', 0.3)],
damage: 8,
attackSpeed: 4.0,
hp: 40,
armorPierce: 0.3,
isAoe: false,
aoeTargets: 1,
unlockCondition: {
type: 'dual_attunement',
attunements: ['enchanter', 'fabricator'],
levels: [5, 5],
},
tier: 3,
},
// Voidstone Golem - Void + Earth fusion (ultimate)
voidstoneGolem: {
id: 'voidstoneGolem',
name: 'Voidstone Golem',
description: 'Earth infused with void energy. The ultimate golem construct.',
baseManaType: 'earth',
summonCost: [elemCost('earth', 15), elemCost('void', 8)],
maintenanceCost: [elemCost('earth', 0.3), elemCost('void', 0.6)],
damage: 40,
attackSpeed: 0.6,
hp: 100,
armorPierce: 0.6,
isAoe: true,
aoeTargets: 3,
unlockCondition: {
type: 'dual_attunement',
attunements: ['enchanter', 'fabricator'],
levels: [5, 5],
},
tier: 4,
},
};
// Get golem slots based on Fabricator attunement level
// Level 2 = 1, Level 4 = 2, Level 6 = 3, Level 8 = 4, Level 10 = 5
export function getGolemSlots(fabricatorLevel: number): number {
if (fabricatorLevel < 2) return 0;
return Math.floor(fabricatorLevel / 2);
}
// Check if a golem is unlocked based on player state
export function isGolemUnlocked(
golemId: string,
attunements: Record<string, { active: boolean; level: number }>,
unlockedElements: string[]
): boolean {
const golem = GOLEMS_DEF[golemId];
if (!golem) return false;
const condition = golem.unlockCondition;
switch (condition.type) {
case 'attunement_level':
const attState = attunements[condition.attunement || ''];
return attState?.active && (attState.level || 1) >= (condition.level || 1);
case 'mana_unlocked':
return unlockedElements.includes(condition.manaType || '');
case 'dual_attunement':
if (!condition.attunements || !condition.levels) return false;
return condition.attunements.every((attId, idx) => {
const att = attunements[attId];
return att?.active && (att.level || 1) >= condition.levels![idx];
});
default:
return false;
}
}
// Get all unlocked golems for a player
export function getUnlockedGolems(
attunements: Record<string, { active: boolean; level: number }>,
unlockedElements: string[]
): GolemDef[] {
return Object.values(GOLEMS_DEF).filter(golem =>
isGolemUnlocked(golem.id, attunements, unlockedElements)
);
}
// Calculate golem damage with skill bonuses
export function getGolemDamage(
golemId: string,
skills: Record<string, number>
): number {
const golem = GOLEMS_DEF[golemId];
if (!golem) return 0;
let damage = golem.damage;
// Golem Mastery skill bonus
const masteryBonus = 1 + (skills.golemMastery || 0) * 0.1;
damage *= masteryBonus;
return damage;
}
// Calculate golem attack speed with skill bonuses
export function getGolemAttackSpeed(
golemId: string,
skills: Record<string, number>
): number {
const golem = GOLEMS_DEF[golemId];
if (!golem) return 0;
let speed = golem.attackSpeed;
// Golem Efficiency skill bonus
const efficiencyBonus = 1 + (skills.golemEfficiency || 0) * 0.05;
speed *= efficiencyBonus;
return speed;
}
// Get floors golems can last (base 1, +1 per Golem Longevity skill level)
export function getGolemFloorDuration(skills: Record<string, number>): number {
return 1 + (skills.golemLongevity || 0);
}
// Get maintenance cost multiplier (Golem Siphon reduces by 10% per level)
export function getGolemMaintenanceMultiplier(skills: Record<string, number>): number {
return 1 - (skills.golemSiphon || 0) * 0.1;
}
// Check if player can afford golem summon cost
export function canAffordGolemSummon(
golemId: string,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>
): boolean {
const golem = GOLEMS_DEF[golemId];
if (!golem) return false;
for (const cost of golem.summonCost) {
if (cost.type === 'raw') {
if (rawMana < cost.amount) return false;
} else if (cost.element) {
const elem = elements[cost.element];
if (!elem || !elem.unlocked || elem.current < cost.amount) return false;
}
}
return true;
}
// Deduct golem summon cost from mana pools
export function deductGolemSummonCost(
golemId: string,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>
): { rawMana: number; elements: Record<string, { current: number; max: number; unlocked: boolean }> } {
const golem = GOLEMS_DEF[golemId];
if (!golem) return { rawMana, elements };
let newRawMana = rawMana;
let newElements = { ...elements };
for (const cost of golem.summonCost) {
if (cost.type === 'raw') {
newRawMana -= cost.amount;
} else if (cost.element && newElements[cost.element]) {
newElements = {
...newElements,
[cost.element]: {
...newElements[cost.element],
current: newElements[cost.element].current - cost.amount,
},
};
}
}
return { rawMana: newRawMana, elements: newElements };
}
// Check if player can afford golem maintenance for one tick
export function canAffordGolemMaintenance(
golemId: string,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>,
skills: Record<string, number>
): boolean {
const golem = GOLEMS_DEF[golemId];
if (!golem) return false;
const maintenanceMult = getGolemMaintenanceMultiplier(skills);
for (const cost of golem.maintenanceCost) {
const adjustedAmount = cost.amount * maintenanceMult;
if (cost.type === 'raw') {
if (rawMana < adjustedAmount) return false;
} else if (cost.element) {
const elem = elements[cost.element];
if (!elem || !elem.unlocked || elem.current < adjustedAmount) return false;
}
}
return true;
}
// Deduct golem maintenance cost for one tick
export function deductGolemMaintenance(
golemId: string,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>,
skills: Record<string, number>
): { rawMana: number; elements: Record<string, { current: number; max: number; unlocked: boolean }> } {
const golem = GOLEMS_DEF[golemId];
if (!golem) return { rawMana, elements };
const maintenanceMult = getGolemMaintenanceMultiplier(skills);
let newRawMana = rawMana;
let newElements = { ...elements };
for (const cost of golem.maintenanceCost) {
const adjustedAmount = cost.amount * maintenanceMult;
if (cost.type === 'raw') {
newRawMana -= adjustedAmount;
} else if (cost.element && newElements[cost.element]) {
newElements = {
...newElements,
[cost.element]: {
...newElements[cost.element],
current: newElements[cost.element].current - adjustedAmount,
},
};
}
}
return { rawMana: newRawMana, elements: newElements };
}
+242
View File
@@ -0,0 +1,242 @@
// ─── Loot Drop Definitions ─────────────────────────────────────────────────────
import type { LootDrop } from '../types';
export const LOOT_DROPS: Record<string, LootDrop> = {
// ─── Materials (used for crafting) ───
manaCrystalDust: {
id: 'manaCrystalDust',
name: 'Mana Crystal Dust',
rarity: 'common',
type: 'material',
minFloor: 1,
dropChance: 0.15,
},
arcaneShard: {
id: 'arcaneShard',
name: 'Arcane Shard',
rarity: 'uncommon',
type: 'material',
minFloor: 10,
dropChance: 0.10,
},
elementalCore: {
id: 'elementalCore',
name: 'Elemental Core',
rarity: 'rare',
type: 'material',
minFloor: 25,
dropChance: 0.08,
},
voidEssence: {
id: 'voidEssence',
name: 'Void Essence',
rarity: 'epic',
type: 'material',
minFloor: 50,
dropChance: 0.05,
guardianOnly: true,
},
celestialFragment: {
id: 'celestialFragment',
name: 'Celestial Fragment',
rarity: 'legendary',
type: 'material',
minFloor: 75,
dropChance: 0.02,
guardianOnly: true,
},
// ─── Elemental Essence (grants elemental mana) ───
fireEssenceDrop: {
id: 'fireEssenceDrop',
name: 'Fire Essence',
rarity: 'uncommon',
type: 'essence',
minFloor: 5,
dropChance: 0.12,
amount: { min: 5, max: 15 },
},
waterEssenceDrop: {
id: 'waterEssenceDrop',
name: 'Water Essence',
rarity: 'uncommon',
type: 'essence',
minFloor: 5,
dropChance: 0.12,
amount: { min: 5, max: 15 },
},
airEssenceDrop: {
id: 'airEssenceDrop',
name: 'Air Essence',
rarity: 'uncommon',
type: 'essence',
minFloor: 5,
dropChance: 0.12,
amount: { min: 5, max: 15 },
},
earthEssenceDrop: {
id: 'earthEssenceDrop',
name: 'Earth Essence',
rarity: 'uncommon',
type: 'essence',
minFloor: 5,
dropChance: 0.12,
amount: { min: 5, max: 15 },
},
lightEssenceDrop: {
id: 'lightEssenceDrop',
name: 'Light Essence',
rarity: 'rare',
type: 'essence',
minFloor: 20,
dropChance: 0.08,
amount: { min: 3, max: 10 },
},
darkEssenceDrop: {
id: 'darkEssenceDrop',
name: 'Dark Essence',
rarity: 'rare',
type: 'essence',
minFloor: 20,
dropChance: 0.08,
amount: { min: 3, max: 10 },
},
lifeEssenceDrop: {
id: 'lifeEssenceDrop',
name: 'Life Essence',
rarity: 'epic',
type: 'essence',
minFloor: 40,
dropChance: 0.05,
amount: { min: 2, max: 8 },
},
deathEssenceDrop: {
id: 'deathEssenceDrop',
name: 'Death Essence',
rarity: 'epic',
type: 'essence',
minFloor: 40,
dropChance: 0.05,
amount: { min: 2, max: 8 },
},
// ─── Raw Mana Drops ───
manaOrb: {
id: 'manaOrb',
name: 'Mana Orb',
rarity: 'common',
type: 'gold', // Uses gold type but gives raw mana
minFloor: 1,
dropChance: 0.20,
amount: { min: 10, max: 50 },
},
greaterManaOrb: {
id: 'greaterManaOrb',
name: 'Greater Mana Orb',
rarity: 'uncommon',
type: 'gold',
minFloor: 15,
dropChance: 0.10,
amount: { min: 50, max: 150 },
},
supremeManaOrb: {
id: 'supremeManaOrb',
name: 'Supreme Mana Orb',
rarity: 'rare',
type: 'gold',
minFloor: 35,
dropChance: 0.05,
amount: { min: 100, max: 500 },
},
// ─── Equipment Blueprints ───
staffBlueprint: {
id: 'staffBlueprint',
name: 'Staff Blueprint',
rarity: 'uncommon',
type: 'blueprint',
minFloor: 10,
dropChance: 0.03,
},
wandBlueprint: {
id: 'wandBlueprint',
name: 'Wand Blueprint',
rarity: 'rare',
type: 'blueprint',
minFloor: 20,
dropChance: 0.02,
},
robeBlueprint: {
id: 'robeBlueprint',
name: 'Mage Robe Blueprint',
rarity: 'rare',
type: 'blueprint',
minFloor: 25,
dropChance: 0.02,
},
artifactBlueprint: {
id: 'artifactBlueprint',
name: 'Artifact Blueprint',
rarity: 'legendary',
type: 'blueprint',
minFloor: 60,
dropChance: 0.01,
guardianOnly: true,
},
};
// Rarity colors for UI
export const RARITY_COLORS: Record<string, { color: string; glow: string }> = {
common: { color: '#9CA3AF', glow: '#9CA3AF40' },
uncommon: { color: '#22C55E', glow: '#22C55E40' },
rare: { color: '#3B82F6', glow: '#3B82F640' },
epic: { color: '#A855F7', glow: '#A855F740' },
legendary: { color: '#F59E0B', glow: '#F59E0B60' },
};
// Get loot drops available at a given floor
export function getAvailableDrops(floor: number, isGuardian: boolean): LootDrop[] {
return Object.values(LOOT_DROPS).filter(drop => {
if (drop.minFloor > floor) return false;
if (drop.guardianOnly && !isGuardian) return false;
return true;
});
}
// Roll for loot drops
export function rollLootDrops(
floor: number,
isGuardian: boolean,
luckBonus: number = 0
): Array<{ drop: LootDrop; amount: number }> {
const available = getAvailableDrops(floor, isGuardian);
const drops: Array<{ drop: LootDrop; amount: number }> = [];
for (const drop of available) {
// Calculate adjusted drop chance
let chance = drop.dropChance;
chance *= (1 + luckBonus); // Apply luck bonus
// Guardian floors have 2x drop rate
if (isGuardian) chance *= 2;
// Cap at 50% for any single drop
chance = Math.min(0.5, chance);
if (Math.random() < chance) {
let amount = 1;
// For gold/essence types, roll amount
if (drop.amount) {
amount = Math.floor(
Math.random() * (drop.amount.max - drop.amount.min + 1) + drop.amount.min
);
}
drops.push({ drop, amount });
}
}
return drops;
}