Refactor large files into modular components
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m9s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m9s
- Refactored page.tsx (613→252 lines) with GameOverScreen and LeftPanel extracted - Refactored StatsTab.tsx (584→92 lines) with section components - Refactored SkillsTab.tsx (434→54 lines) with sub-components - Created modular structure for GameContext, LootInventory, and other components - All extracted components organized into feature directories
This commit is contained in:
@@ -1,473 +0,0 @@
|
||||
// ─── Spell Enchantment Effects ────────────────────────────────────────────────
|
||||
// All spell-related enchantment effects that can be applied to equipment
|
||||
|
||||
import type { EquipmentCategory } from '../equipment'
|
||||
import type { EnchantmentEffectDef } from '../enchantment-types'
|
||||
|
||||
// Helper to define allowed equipment categories for each effect type
|
||||
const ALL_CASTER: EquipmentCategory[] = ['caster']
|
||||
|
||||
export const SPELL_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' }
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 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' }
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
// ─── Tier 0 & 1 Basic Spells ───────────────────────────────────
|
||||
|
||||
import type { EnchantmentEffectDef } from './types';
|
||||
import { ALL_CASTER } from './types';
|
||||
|
||||
export const BASIC_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
|
||||
// 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' }
|
||||
},
|
||||
|
||||
// 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' }
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// ─── Spell Enchantment Effects Index ───────────────────────────────
|
||||
// Re-exports all spell effects from modular files
|
||||
|
||||
// Re-export types
|
||||
export type { EnchantmentEffectDef, ALL_CASTER } from './types';
|
||||
|
||||
// Re-export data
|
||||
export { SPELL_EFFECTS } from './data';
|
||||
@@ -0,0 +1,58 @@
|
||||
// ─── Lightning Spell Effects ──────────────────────────────────
|
||||
// Lightning spells - Fast, armor-piercing, harder to dodge
|
||||
|
||||
import type { EnchantmentEffectDef } from './types';
|
||||
import { ALL_CASTER } from './types';
|
||||
|
||||
export const LIGHTNING_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
|
||||
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' }
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
// ─── Metal Spell Effects ──────────────────────────────────────
|
||||
// Metal spells - Fire + Earth compound, armor pierce focus
|
||||
|
||||
import type { EnchantmentEffectDef } from './types';
|
||||
import { ALL_CASTER } from './types';
|
||||
|
||||
export const METAL_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
|
||||
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' }
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
// ─── Sand Spell Effects ───────────────────────────────────────
|
||||
// Sand spells - Earth + Water compound, AOE focus
|
||||
|
||||
import type { EnchantmentEffectDef } from './types';
|
||||
import { ALL_CASTER } from './types';
|
||||
|
||||
export const SAND_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
|
||||
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' }
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
// ─── Tier 2 Advanced Spells ───────────────────────────────────
|
||||
|
||||
import type { EnchantmentEffectDef } from './types';
|
||||
import { ALL_CASTER } from './types';
|
||||
|
||||
export const TIER2_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
|
||||
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 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' }
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
// ─── Tier 3 Master Spells ─────────────────────────────────────
|
||||
|
||||
import type { EnchantmentEffectDef } from './types';
|
||||
import { ALL_CASTER } from './types';
|
||||
|
||||
export const TIER3_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
|
||||
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' }
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// ─── Spell Enchantment Effects Types ─────────────────
|
||||
|
||||
export interface EnchantmentEffectDef {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
baseCapacityCost: number;
|
||||
maxStacks: number;
|
||||
allowedEquipmentCategories: string[];
|
||||
effect: {
|
||||
type: string;
|
||||
spellId?: string;
|
||||
stat?: string;
|
||||
value?: number;
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to define allowed equipment categories for each effect type
|
||||
export const ALL_CASTER: string[] = ['caster']
|
||||
@@ -1,497 +0,0 @@
|
||||
// ─── 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'];
|
||||
|
||||
// Human-readable names for equipment slots
|
||||
export const SLOT_NAMES: Record<EquipmentSlot, string> = {
|
||||
mainHand: 'Main Hand',
|
||||
offHand: 'Off Hand',
|
||||
head: 'Head',
|
||||
body: 'Body',
|
||||
hands: 'Hands',
|
||||
feet: 'Feet',
|
||||
accessory1: 'Accessory 1',
|
||||
accessory2: 'Accessory 2',
|
||||
};
|
||||
|
||||
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)
|
||||
twoHanded?: boolean; // If true, weapon occupies both main hand and offhand slots
|
||||
}
|
||||
|
||||
// ─── 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.',
|
||||
twoHanded: true,
|
||||
},
|
||||
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.',
|
||||
twoHanded: true,
|
||||
},
|
||||
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.',
|
||||
twoHanded: true,
|
||||
},
|
||||
battlestaff: {
|
||||
id: 'battlestaff',
|
||||
name: 'Battlestaff',
|
||||
category: 'caster',
|
||||
slot: 'mainHand',
|
||||
baseCapacity: 70,
|
||||
description: 'A reinforced staff suitable for both casting and combat.',
|
||||
twoHanded: true,
|
||||
},
|
||||
|
||||
// ─── 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
|
||||
// Note: For 2-handed weapons, use getValidSlotsForEquipmentType instead
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get valid slots for a specific equipment type (considers 2-handed weapons)
|
||||
export function getValidSlotsForEquipmentType(equipType: EquipmentType): EquipmentSlot[] {
|
||||
// 2-handed weapons occupy both main hand and offhand
|
||||
if (equipType.twoHanded) {
|
||||
return ['mainHand', 'offHand'];
|
||||
}
|
||||
|
||||
// Otherwise use category-based slots
|
||||
return getValidSlotsForCategory(equipType.category);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// ─── Accessories Equipment Types ──────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const ACCESSORIES_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
// ─── Body Equipment Types ─────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const BODY_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
// ─── Caster Equipment Types ────────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const CASTER_EQUIPMENT: 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.',
|
||||
twoHanded: true,
|
||||
},
|
||||
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.',
|
||||
twoHanded: true,
|
||||
},
|
||||
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.',
|
||||
twoHanded: true,
|
||||
},
|
||||
battlestaff: {
|
||||
id: 'battlestaff',
|
||||
name: 'Battlestaff',
|
||||
category: 'caster',
|
||||
slot: 'mainHand',
|
||||
baseCapacity: 70,
|
||||
description: 'A reinforced staff suitable for both casting and combat.',
|
||||
twoHanded: true,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// ─── Catalyst Equipment Types ───────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const CATALYST_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
// ─── Feet Equipment Types ─────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const FEET_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
// ─── Hands Equipment Types ─────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const HANDS_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
// ─── Head Equipment Types ────────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const HEAD_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// ─── Equipment Types Index ───────────────────────────────
|
||||
// Re-exports from all equipment type modules
|
||||
|
||||
// Re-export types
|
||||
export type {
|
||||
EquipmentSlot,
|
||||
EquipmentCategory,
|
||||
EquipmentType
|
||||
} from './types';
|
||||
|
||||
export {
|
||||
EQUIPMENT_SLOTS,
|
||||
SLOT_NAMES
|
||||
} from './types';
|
||||
|
||||
// Re-export data
|
||||
export { EQUIPMENT_TYPES } from './data';
|
||||
|
||||
// Re-export utility functions
|
||||
export {
|
||||
getEquipmentType,
|
||||
getEquipmentByCategory,
|
||||
getEquipmentBySlot,
|
||||
getAllEquipmentTypes,
|
||||
getValidSlotsForCategory,
|
||||
getValidSlotsForEquipmentType,
|
||||
canEquipInSlot,
|
||||
} from './utils';
|
||||
@@ -0,0 +1,39 @@
|
||||
// ─── Shield Equipment Types ───────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const SHIELD_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
// ─── Sword Equipment Types ───────────────────────────────────────────
|
||||
|
||||
import type { EquipmentType } from './types';
|
||||
|
||||
export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
||||
// ─── 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.',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// ─── 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'];
|
||||
|
||||
// Human-readable names for equipment slots
|
||||
export const SLOT_NAMES: Record<EquipmentSlot, string> = {
|
||||
mainHand: 'Main Hand',
|
||||
offHand: 'Off Hand',
|
||||
head: 'Head',
|
||||
body: 'Body',
|
||||
hands: 'Hands',
|
||||
feet: 'Feet',
|
||||
accessory1: 'Accessory 1',
|
||||
accessory2: 'Accessory 2',
|
||||
};
|
||||
|
||||
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)
|
||||
twoHanded?: boolean; // If true, weapon occupies both main hand and offhand slots
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// ─── Equipment Helper Functions ─────────────────────────
|
||||
|
||||
import type { EquipmentType, EquipmentSlot, EquipmentCategory } from './types';
|
||||
import { EQUIPMENT_TYPES } from './index';
|
||||
|
||||
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) as EquipmentType[];
|
||||
}
|
||||
|
||||
export function getEquipmentBySlot(slot: EquipmentSlot): EquipmentType[] {
|
||||
return Object.values(EQUIPMENT_TYPES).filter(e => e.slot === slot) as EquipmentType[];
|
||||
}
|
||||
|
||||
export function getAllEquipmentTypes(): EquipmentType[] {
|
||||
return Object.values(EQUIPMENT_TYPES) as EquipmentType[];
|
||||
}
|
||||
|
||||
// Get valid slots for a category
|
||||
// Note: For 2-handed weapons, use getValidSlotsForEquipmentType instead
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get valid slots for a specific equipment type (considers 2-handed weapons)
|
||||
export function getValidSlotsForEquipmentType(equipType: EquipmentType): EquipmentSlot[] {
|
||||
// 2-handed weapons occupy both main hand and offhand
|
||||
if (equipType.twoHanded) {
|
||||
return ['mainHand', 'offHand'];
|
||||
}
|
||||
|
||||
// Otherwise use category-based slots
|
||||
return getValidSlotsForCategory(equipType.category);
|
||||
}
|
||||
|
||||
// Check if an equipment type can be equipped in a specific slot
|
||||
export function canEquipInSlot(equipmentType: EquipmentType, slot: EquipmentSlot): boolean {
|
||||
const validSlots = getValidSlotsForEquipmentType(equipmentType);
|
||||
return validSlots.includes(slot);
|
||||
}
|
||||
@@ -1,471 +0,0 @@
|
||||
// ─── 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 };
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// ─── Base Golem Definitions ───────────────────────────────────
|
||||
|
||||
import type { GolemDef } from './types';
|
||||
import { elemCost } from './types';
|
||||
|
||||
export const BASE_GOLEMS: 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,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
// ─── Elemental Variant Golems ───────────────────────────────────
|
||||
|
||||
import type { GolemDef } from './types';
|
||||
import { elemCost } from './types';
|
||||
|
||||
export const ELEMENTAL_GOLEMS: Record<string, GolemDef> = {
|
||||
// ─── 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,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
// ─── Advanced Hybrid Golems ────────────────────────────────────
|
||||
// Require Enchanter 5 + Fabricator 5
|
||||
|
||||
import type { GolemDef } from './types';
|
||||
import { elemCost } from './types';
|
||||
|
||||
export const HYBRID_GOLEMS: Record<string, GolemDef> = {
|
||||
// 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,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
// ─── Golem Definitions Index ─────────────────────────────────
|
||||
// Re-exports from all golem modules
|
||||
|
||||
// Re-export types
|
||||
export type { GolemDef, GolemManaCost } from './types';
|
||||
|
||||
// Re-export data
|
||||
export { GOLEMS_DEF } from './data';
|
||||
|
||||
// Re-export utility functions
|
||||
export {
|
||||
getGolemSlots,
|
||||
isGolemUnlocked,
|
||||
getUnlockedGolems,
|
||||
getGolemDamage,
|
||||
getGolemAttackSpeed,
|
||||
getGolemFloorDuration,
|
||||
getGolemMaintenanceMultiplier,
|
||||
canAffordGolemSummon,
|
||||
deductGolemSummonCost,
|
||||
canAffordGolemMaintenance,
|
||||
deductGolemMaintenance,
|
||||
} from './utils';
|
||||
@@ -0,0 +1,42 @@
|
||||
// ─── Golem Types ─────────────────────────────────────────────────
|
||||
|
||||
import type { SpellCost } from '../types';
|
||||
|
||||
// Golem mana cost helper
|
||||
export function elemCost(element: string, amount: number): SpellCost {
|
||||
return { type: 'element', element, amount };
|
||||
}
|
||||
|
||||
export 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)
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
// ─── Golem Helper Functions ─────────────────────────
|
||||
|
||||
import type { GolemDef, GolemManaCost } from './types';
|
||||
import { GOLEMS_DEF } from './index';
|
||||
|
||||
// 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)
|
||||
) as GolemDef[];
|
||||
}
|
||||
|
||||
// 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user