From a2c9af7d45c8b185a3be68a75ec46d104783de1f Mon Sep 17 00:00:00 2001 From: zhipu Date: Thu, 26 Mar 2026 14:58:07 +0000 Subject: [PATCH] Remove familiar system to focus on guardian pacts - Remove familiar-slice.ts, familiars.ts data file, and FamiliarTab.tsx - Remove all familiar types from types.ts (FamiliarRole, FamiliarAbilityType, etc.) - Remove familiar state from GameState interface - Remove familiar bonuses from combat calculations - Remove familiar XP tracking from tick function - Remove familiar tab from page.tsx UI This simplifies the game to focus on guardian pacts as the primary progression mechanic, avoiding the parallel familiar system that diluted the importance of pacts. --- src/app/page.tsx | 8 +- src/components/game/tabs/FamiliarTab.tsx | 582 ----------------------- src/lib/game/data/familiars.ts | 498 ------------------- src/lib/game/familiar-slice.ts | 367 -------------- src/lib/game/store.ts | 93 +--- src/lib/game/types.ts | 69 --- 6 files changed, 5 insertions(+), 1612 deletions(-) delete mode 100644 src/components/game/tabs/FamiliarTab.tsx delete mode 100644 src/lib/game/data/familiars.ts delete mode 100644 src/lib/game/familiar-slice.ts diff --git a/src/app/page.tsx b/src/app/page.tsx index d39243e..29230ab 100755 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -13,7 +13,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { RotateCcw } from 'lucide-react'; import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab } from '@/components/game/tabs'; -import { FamiliarTab } from '@/components/game/tabs/FamiliarTab'; import { ComboMeter, ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game'; import { LootInventoryDisplay } from '@/components/game/LootInventory'; import { AchievementsDisplay } from '@/components/game/AchievementsDisplay'; @@ -227,13 +226,12 @@ export default function ManaLoopGame() { {/* Right Panel - Tabs */}
- + ⚔️ Spire 📚 Skills ✨ Spells 🔧 Craft 🔬 Lab - 🐾 Familiar 📊 Stats 📖 Grimoire @@ -266,10 +264,6 @@ export default function ManaLoopGame() { - - - - = { - fire: Flame, - water: Droplet, - air: Wind, - earth: Mountain, - light: Sun, - dark: Moon, - life: Leaf, - death: Skull, - mental: Brain, - transference: Link, - force: Force, - blood: Droplets, - metal: Shield, - wood: TreeDeciduous, - sand: Hourglass, - crystal: Gem, - stellar: Star, - void: CircleDot, - raw: Circle, -}; - -// Rarity colors -const RARITY_COLORS: Record = { - common: 'text-gray-400 border-gray-600', - uncommon: 'text-green-400 border-green-600', - rare: 'text-blue-400 border-blue-600', - epic: 'text-purple-400 border-purple-600', - legendary: 'text-amber-400 border-amber-600', -}; - -const RARITY_BG: Record = { - common: 'bg-gray-900/50', - uncommon: 'bg-green-900/20', - rare: 'bg-blue-900/20', - epic: 'bg-purple-900/20', - legendary: 'bg-amber-900/20', -}; - -// Role icons -const ROLE_ICONS: Record = { - combat: Sword, - mana: Sparkles, - support: Heart, - guardian: ShieldCheck, -}; - -// Ability type icons -const ABILITY_ICONS: Record = { - damageBonus: Sword, - manaRegen: Sparkles, - autoGather: Zap, - critChance: Star, - castSpeed: Clock, - manaShield: Shield, - elementalBonus: Flame, - lifeSteal: Heart, - bonusGold: TrendingUp, - autoConvert: Wand2, - thorns: ShieldCheck, -}; - -interface FamiliarTabProps { - store: GameState & { - setActiveFamiliar: (index: number, active: boolean) => void; - setFamiliarNickname: (index: number, nickname: string) => void; - summonFamiliar: (familiarId: string) => void; - upgradeFamiliarAbility: (index: number, abilityType: FamiliarAbilityType) => void; - getActiveFamiliarBonuses: () => ReturnType['getActiveFamiliarBonuses'] extends () => infer R ? R : never; - getAvailableFamiliars: () => string[]; - }; -} - -export function FamiliarTab({ store }: FamiliarTabProps) { - const [selectedFamiliar, setSelectedFamiliar] = useState(null); - const [nicknameInput, setNicknameInput] = useState(''); - - const familiars = store.familiars; - const activeFamiliarSlots = store.activeFamiliarSlots; - const activeCount = familiars.filter(f => f.active).length; - const availableFamiliars = store.getAvailableFamiliars(); - const familiarBonuses = store.getActiveFamiliarBonuses(); - - // Format XP display - const formatXp = (current: number, level: number) => { - const required = getFamiliarXpRequired(level); - return `${current}/${required}`; - }; - - // Get familiar definition - const getFamiliarDef = (instance: FamiliarInstance): FamiliarDef | undefined => { - return FAMILIARS_DEF[instance.familiarId]; - }; - - // Render a single familiar card - const renderFamiliarCard = (instance: FamiliarInstance, index: number) => { - const def = getFamiliarDef(instance); - if (!def) return null; - - const ElementIcon = ELEMENT_ICONS[def.element] || Circle; - const RoleIcon = ROLE_ICONS[def.role] || Sparkles; - const xpRequired = getFamiliarXpRequired(instance.level); - const xpPercent = Math.min(100, (instance.experience / xpRequired) * 100); - const bondPercent = instance.bond; - const isSelected = selectedFamiliar === index; - - return ( - setSelectedFamiliar(isSelected ? null : index)} - > - -
-
-
- -
-
- - {instance.nickname || def.name} - -
- - Lv.{instance.level} - {instance.active && ( - Active - )} -
-
-
- - {def.rarity} - -
-
- - {/* XP Bar */} -
-
- XP - {formatXp(instance.experience, instance.level)} -
- -
- - {/* Bond Bar */} -
-
- - Bond - - {bondPercent.toFixed(0)}% -
- -
- - {/* Abilities Preview */} -
- {instance.abilities.slice(0, 3).map(ability => { - const abilityDef = def.abilities.find(a => a.type === ability.type); - if (!abilityDef) return null; - const AbilityIcon = ABILITY_ICONS[ability.type] || Zap; - const value = getFamiliarAbilityValue(abilityDef, instance.level, ability.level); - - return ( - - - - - {ability.type === 'damageBonus' || ability.type === 'elementalBonus' || - ability.type === 'castSpeed' || ability.type === 'critChance' || - ability.type === 'lifeSteal' || ability.type === 'thorns' || - ability.type === 'bonusGold' - ? `+${value.toFixed(1)}%` - : `+${value.toFixed(1)}`} - - - -

{abilityDef.desc}

-

Level {ability.level}/10

-
-
- ); - })} -
-
-
- ); - }; - - // Render selected familiar details - const renderFamiliarDetails = () => { - if (selectedFamiliar === null || selectedFamiliar >= familiars.length) return null; - - const instance = familiars[selectedFamiliar]; - const def = getFamiliarDef(instance); - if (!def) return null; - - const ElementIcon = ELEMENT_ICONS[def.element] || Circle; - - return ( - - -
- Familiar Details - -
-
- - {/* Name and nickname */} -
-
-
- -
-
-

- {def.name} -

- {instance.nickname && ( -

"{instance.nickname}"

- )} -
-
- - {/* Nickname input */} -
- setNicknameInput(e.target.value)} - placeholder="Set nickname..." - className="h-8 text-sm bg-gray-800 border-gray-600" - /> - -
-
- - {/* Description */} -
- {def.desc} -
- - {/* Stats */} -
-
- Level: - {instance.level}/100 -
-
- Bond: - {instance.bond.toFixed(0)}% -
-
- Role: - {def.role} -
-
- Element: - {def.element} -
-
- - - - {/* Abilities */} -
-

Abilities

- {instance.abilities.map(ability => { - const abilityDef = def.abilities.find(a => a.type === ability.type); - if (!abilityDef) return null; - const AbilityIcon = ABILITY_ICONS[ability.type] || Zap; - const value = getFamiliarAbilityValue(abilityDef, instance.level, ability.level); - const upgradeCost = ability.level * 100; - const canUpgrade = instance.experience >= upgradeCost && ability.level < 10; - - return ( -
-
-
- - - {ability.type.replace(/([A-Z])/g, ' $1').trim()} - -
- Lv.{ability.level}/10 -
-

{abilityDef.desc}

-
- - Current: +{value.toFixed(2)} - {ability.type === 'damageBonus' || ability.type === 'elementalBonus' || - ability.type === 'castSpeed' || ability.type === 'critChance' || - ability.type === 'lifeSteal' || ability.type === 'thorns' || - ability.type === 'bonusGold' ? '%' : ''} - - {ability.level < 10 && ( - - )} -
-
- ); - })} -
- - {/* Activate/Deactivate */} - - - {/* Flavor text */} - {def.flavorText && ( -

- "{def.flavorText}" -

- )} -
-
- ); - }; - - // Render summonable familiars - const renderSummonableFamiliars = () => { - if (availableFamiliars.length === 0) return null; - - return ( - - - - - Available to Summon ({availableFamiliars.length}) - - - - -
- {availableFamiliars.map(familiarId => { - const def = FAMILIARS_DEF[familiarId]; - if (!def) return null; - - const ElementIcon = ELEMENT_ICONS[def.element] || Circle; - const RoleIcon = ROLE_ICONS[def.role] || Sparkles; - - return ( -
-
- -
-
- {def.name} -
-
- - {def.role} -
-
-
- -
- ); - })} -
-
-
-
- ); - }; - - // Render active bonuses - const renderActiveBonuses = () => { - const hasBonuses = Object.entries(familiarBonuses).some(([key, value]) => { - if (key === 'damageMultiplier' || key === 'castSpeedMultiplier' || - key === 'elementalDamageMultiplier' || key === 'insightMultiplier') { - return value > 1; - } - return value > 0; - }); - - if (!hasBonuses) return null; - - return ( - - - - - Active Familiar Bonuses - - - -
- {familiarBonuses.damageMultiplier > 1 && ( -
- - +{((familiarBonuses.damageMultiplier - 1) * 100).toFixed(0)}% DMG -
- )} - {familiarBonuses.manaRegenBonus > 0 && ( -
- - +{familiarBonuses.manaRegenBonus.toFixed(1)} regen -
- )} - {familiarBonuses.autoGatherRate > 0 && ( -
- - +{familiarBonuses.autoGatherRate.toFixed(1)}/hr gather -
- )} - {familiarBonuses.critChanceBonus > 0 && ( -
- - +{familiarBonuses.critChanceBonus.toFixed(1)}% crit -
- )} - {familiarBonuses.castSpeedMultiplier > 1 && ( -
- - +{((familiarBonuses.castSpeedMultiplier - 1) * 100).toFixed(0)}% speed -
- )} - {familiarBonuses.elementalDamageMultiplier > 1 && ( -
- - +{((familiarBonuses.elementalDamageMultiplier - 1) * 100).toFixed(0)}% elem -
- )} - {familiarBonuses.lifeStealPercent > 0 && ( -
- - +{familiarBonuses.lifeStealPercent.toFixed(0)}% lifesteal -
- )} - {familiarBonuses.insightMultiplier > 1 && ( -
- - +{((familiarBonuses.insightMultiplier - 1) * 100).toFixed(0)}% insight -
- )} -
-
-
- ); - }; - - return ( - -
- {/* Owned Familiars */} - - -
- - - Your Familiars ({familiars.length}) - -
- Active Slots: {activeCount}/{activeFamiliarSlots} -
-
-
- - {familiars.length > 0 ? ( -
- {familiars.map((instance, index) => renderFamiliarCard(instance, index))} -
- ) : ( -
- No familiars yet. Progress through the game to summon companions! -
- )} -
-
- - {/* Active Bonuses */} - {renderActiveBonuses()} - - {/* Selected Familiar Details */} - {renderFamiliarDetails()} - - {/* Summonable Familiars */} - {renderSummonableFamiliars()} - - {/* Familiar Guide */} - - - - - Familiar Guide - - - -
-
-

Acquiring Familiars

-

Familiars become available to summon as you progress through floors, gather mana, and sign pacts with guardians. Higher rarity familiars are unlocked later.

-
-
-

Leveling & Bond

-

Active familiars gain XP from combat, gathering, and time. Higher bond increases their power and XP gain. Upgrade abilities using XP to boost their effects.

-
-
-

Roles

-

- Combat - Damage and crit bonuses
- Mana - Regeneration and auto-gathering
- Support - Speed and utility
- Guardian - Defense and shields -

-
-
-

Active Slots

-

You can have 1 familiar active by default. Upgrade through prestige to unlock more active slots for stacking bonuses.

-
-
-
-
-
-
- ); -} diff --git a/src/lib/game/data/familiars.ts b/src/lib/game/data/familiars.ts deleted file mode 100644 index 433b499..0000000 --- a/src/lib/game/data/familiars.ts +++ /dev/null @@ -1,498 +0,0 @@ -// ─── Familiar Definitions ─────────────────────────────────────────────────────── -// Magical companions that provide passive bonuses and active assistance - -import type { FamiliarDef, FamiliarAbility } from '../types'; - -// ─── Familiar Abilities ───────────────────────────────────────────────────────── - -const ABILITIES = { - // Combat abilities - damageBonus: (base: number, scaling: number): FamiliarAbility => ({ - type: 'damageBonus', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base}% damage (+${scaling}% per level)`, - }), - - critChance: (base: number, scaling: number): FamiliarAbility => ({ - type: 'critChance', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base}% crit chance (+${scaling}% per level)`, - }), - - castSpeed: (base: number, scaling: number): FamiliarAbility => ({ - type: 'castSpeed', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base}% cast speed (+${scaling}% per level)`, - }), - - elementalBonus: (base: number, scaling: number): FamiliarAbility => ({ - type: 'elementalBonus', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base}% elemental damage (+${scaling}% per level)`, - }), - - lifeSteal: (base: number, scaling: number): FamiliarAbility => ({ - type: 'lifeSteal', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base}% life steal (+${scaling}% per level)`, - }), - - thorns: (base: number, scaling: number): FamiliarAbility => ({ - type: 'thorns', - baseValue: base, - scalingPerLevel: scaling, - desc: `Reflect ${base}% damage taken (+${scaling}% per level)`, - }), - - // Mana abilities - manaRegen: (base: number, scaling: number): FamiliarAbility => ({ - type: 'manaRegen', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base} mana regen (+${scaling} per level)`, - }), - - autoGather: (base: number, scaling: number): FamiliarAbility => ({ - type: 'autoGather', - baseValue: base, - scalingPerLevel: scaling, - desc: `Auto-gather ${base} mana/hour (+${scaling} per level)`, - }), - - autoConvert: (base: number, scaling: number): FamiliarAbility => ({ - type: 'autoConvert', - baseValue: base, - scalingPerLevel: scaling, - desc: `Auto-convert ${base} mana/hour (+${scaling} per level)`, - }), - - manaShield: (base: number, scaling: number): FamiliarAbility => ({ - type: 'manaShield', - baseValue: base, - scalingPerLevel: scaling, - desc: `Shield absorbs ${base} damage, costs 1 mana per ${base} damage`, - }), - - // Support abilities - bonusGold: (base: number, scaling: number): FamiliarAbility => ({ - type: 'bonusGold', - baseValue: base, - scalingPerLevel: scaling, - desc: `+${base}% insight gain (+${scaling}% per level)`, - }), -}; - -// ─── Familiar Definitions ─────────────────────────────────────────────────────── - -export const FAMILIARS_DEF: Record = { - // === COMMON FAMILIARS (Tier 1) === - - // Mana Wisps - Basic mana helpers - manaWisp: { - id: 'manaWisp', - name: 'Mana Wisp', - desc: 'A gentle spirit of pure mana that drifts lazily through the air.', - role: 'mana', - element: 'raw', - rarity: 'common', - abilities: [ - ABILITIES.manaRegen(0.5, 0.1), - ], - baseStats: { power: 10, bond: 15 }, - unlockCondition: { type: 'mana', value: 100 }, - flavorText: 'It hums with quiet contentment, barely visible in dim light.', - }, - - fireSpark: { - id: 'fireSpark', - name: 'Fire Spark', - desc: 'A tiny ember given life, crackling with barely contained energy.', - role: 'combat', - element: 'fire', - rarity: 'common', - abilities: [ - ABILITIES.damageBonus(2, 0.5), - ], - baseStats: { power: 12, bond: 10 }, - unlockCondition: { type: 'floor', value: 5 }, - flavorText: 'It bounces excitedly, leaving scorch marks on everything it touches.', - }, - - waterDroplet: { - id: 'waterDroplet', - name: 'Water Droplet', - desc: 'A perfect sphere of living water that never seems to evaporate.', - role: 'support', - element: 'water', - rarity: 'common', - abilities: [ - ABILITIES.manaRegen(0.3, 0.1), - ABILITIES.lifeSteal(1, 0.2), - ], - baseStats: { power: 8, bond: 12 }, - unlockCondition: { type: 'floor', value: 3 }, - flavorText: 'Ripples spread across its surface with each spell you cast.', - }, - - earthPebble: { - id: 'earthPebble', - name: 'Earth Pebble', - desc: 'A small stone with a surprisingly friendly personality.', - role: 'guardian', - element: 'earth', - rarity: 'common', - abilities: [ - ABILITIES.thorns(2, 0.5), - ], - baseStats: { power: 15, bond: 8 }, - unlockCondition: { type: 'floor', value: 8 }, - flavorText: 'It occasionally rolls itself to a new position when bored.', - }, - - // === UNCOMMON FAMILIARS (Tier 2) === - - flameImp: { - id: 'flameImp', - name: 'Flame Imp', - desc: 'A mischievous fire spirit that delights in destruction.', - role: 'combat', - element: 'fire', - rarity: 'uncommon', - abilities: [ - ABILITIES.damageBonus(4, 0.8), - ABILITIES.elementalBonus(3, 0.6), - ], - baseStats: { power: 25, bond: 12 }, - unlockCondition: { type: 'floor', value: 15 }, - flavorText: 'It cackles with glee whenever you defeat an enemy.', - }, - - windSylph: { - id: 'windSylph', - name: 'Wind Sylph', - desc: 'An airy spirit that moves like a gentle breeze.', - role: 'support', - element: 'air', - rarity: 'uncommon', - abilities: [ - ABILITIES.castSpeed(3, 0.6), - ], - baseStats: { power: 20, bond: 15 }, - unlockCondition: { type: 'floor', value: 12 }, - flavorText: 'Its laughter sounds like wind chimes in a storm.', - }, - - manaSprite: { - id: 'manaSprite', - name: 'Mana Sprite', - desc: 'A more evolved mana spirit with a playful nature.', - role: 'mana', - element: 'raw', - rarity: 'uncommon', - abilities: [ - ABILITIES.manaRegen(1, 0.2), - ABILITIES.autoGather(2, 0.5), - ], - baseStats: { power: 18, bond: 18 }, - unlockCondition: { type: 'mana', value: 1000 }, - flavorText: 'It sometimes tickles your ear with invisible hands.', - }, - - crystalGolem: { - id: 'crystalGolem', - name: 'Crystal Golem', - desc: 'A small construct made of crystallized mana.', - role: 'guardian', - element: 'crystal', - rarity: 'uncommon', - abilities: [ - ABILITIES.thorns(5, 1), - ABILITIES.manaShield(10, 2), - ], - baseStats: { power: 30, bond: 10 }, - unlockCondition: { type: 'floor', value: 20 }, - flavorText: 'Light refracts through its body in mesmerizing patterns.', - }, - - // === RARE FAMILIARS (Tier 3) === - - phoenixHatchling: { - id: 'phoenixHatchling', - name: 'Phoenix Hatchling', - desc: 'A young phoenix, still learning to control its flames.', - role: 'combat', - element: 'fire', - rarity: 'rare', - abilities: [ - ABILITIES.damageBonus(6, 1.2), - ABILITIES.lifeSteal(3, 0.5), - ], - baseStats: { power: 40, bond: 15 }, - unlockCondition: { type: 'floor', value: 30 }, - flavorText: 'Tiny flames dance around its feathers as it practices flying.', - }, - - frostWisp: { - id: 'frostWisp', - name: 'Frost Wisp', - desc: 'A spirit of eternal winter, beautiful and deadly.', - role: 'combat', - element: 'water', - rarity: 'rare', - abilities: [ - ABILITIES.elementalBonus(8, 1.5), - ABILITIES.castSpeed(4, 0.8), - ], - baseStats: { power: 35, bond: 12 }, - unlockCondition: { type: 'floor', value: 25 }, - flavorText: 'Frost patterns appear on surfaces wherever it lingers.', - }, - - manaElemental: { - id: 'manaElemental', - name: 'Mana Elemental', - desc: 'A concentrated form of pure magical energy.', - role: 'mana', - element: 'raw', - rarity: 'rare', - abilities: [ - ABILITIES.manaRegen(2, 0.4), - ABILITIES.autoGather(5, 1), - ABILITIES.autoConvert(2, 0.5), - ], - baseStats: { power: 30, bond: 20 }, - unlockCondition: { type: 'mana', value: 5000 }, - flavorText: 'Reality seems to bend slightly around its fluctuating form.', - }, - - shieldGuardian: { - id: 'shieldGuardian', - name: 'Shield Guardian', - desc: 'A loyal protector carved from enchanted stone.', - role: 'guardian', - element: 'earth', - rarity: 'rare', - abilities: [ - ABILITIES.thorns(8, 1.5), - ABILITIES.manaShield(20, 4), - ], - baseStats: { power: 50, bond: 8 }, - unlockCondition: { type: 'floor', value: 35 }, - flavorText: 'It stands motionless for hours, then suddenly moves to block danger.', - }, - - // === EPIC FAMILIARS (Tier 4) === - - infernoDrake: { - id: 'infernoDrake', - name: 'Inferno Drake', - desc: 'A small dragon wreathed in eternal flames.', - role: 'combat', - element: 'fire', - rarity: 'epic', - abilities: [ - ABILITIES.damageBonus(10, 2), - ABILITIES.elementalBonus(12, 2), - ABILITIES.critChance(3, 0.6), - ], - baseStats: { power: 60, bond: 12 }, - unlockCondition: { type: 'floor', value: 50 }, - flavorText: 'Smoke occasionally drifts from its nostrils as it dreams of conquest.', - }, - - starlightSerpent: { - id: 'starlightSerpent', - name: 'Starlight Serpent', - desc: 'A serpentine creature formed from captured starlight.', - role: 'support', - element: 'stellar', - rarity: 'epic', - abilities: [ - ABILITIES.castSpeed(8, 1.5), - ABILITIES.bonusGold(5, 1), - ABILITIES.manaRegen(1.5, 0.3), - ], - baseStats: { power: 45, bond: 25 }, - unlockCondition: { type: 'floor', value: 45 }, - flavorText: 'It traces constellations in the air with its glowing body.', - }, - - voidWalker: { - id: 'voidWalker', - name: 'Void Walker', - desc: 'A being that exists partially outside normal reality.', - role: 'mana', - element: 'void', - rarity: 'epic', - abilities: [ - ABILITIES.manaRegen(3, 0.6), - ABILITIES.autoGather(10, 2), - ABILITIES.manaShield(15, 3), - ], - baseStats: { power: 55, bond: 15 }, - unlockCondition: { type: 'floor', value: 55 }, - flavorText: 'It sometimes disappears entirely, only to reappear moments later.', - }, - - ancientGolem: { - id: 'ancientGolem', - name: 'Ancient Golem', - desc: 'A construct from a forgotten age, still following its prime directive.', - role: 'guardian', - element: 'earth', - rarity: 'epic', - abilities: [ - ABILITIES.thorns(15, 3), - ABILITIES.manaShield(30, 5), - ABILITIES.damageBonus(5, 1), - ], - baseStats: { power: 80, bond: 6 }, - unlockCondition: { type: 'floor', value: 60 }, - flavorText: 'Ancient runes glow faintly across its weathered surface.', - }, - - // === LEGENDARY FAMILIARS (Tier 5) === - - primordialPhoenix: { - id: 'primordialPhoenix', - name: 'Primordial Phoenix', - desc: 'An ancient fire bird, reborn countless times through the ages.', - role: 'combat', - element: 'fire', - rarity: 'legendary', - abilities: [ - ABILITIES.damageBonus(15, 3), - ABILITIES.elementalBonus(20, 4), - ABILITIES.lifeSteal(8, 1.5), - ABILITIES.critChance(5, 1), - ], - baseStats: { power: 100, bond: 20 }, - unlockCondition: { type: 'pact', value: 25 }, // Guardian floor 25 - flavorText: 'Its eyes hold the wisdom of a thousand lifetimes.', - }, - - leviathanSpawn: { - id: 'leviathanSpawn', - name: 'Leviathan Spawn', - desc: 'The offspring of an ancient sea god, still growing into its power.', - role: 'mana', - element: 'water', - rarity: 'legendary', - abilities: [ - ABILITIES.manaRegen(5, 1), - ABILITIES.autoGather(20, 4), - ABILITIES.autoConvert(8, 1.5), - ABILITIES.manaShield(25, 5), - ], - baseStats: { power: 90, bond: 18 }, - unlockCondition: { type: 'pact', value: 50 }, - flavorText: 'The air around it always smells of salt and deep ocean.', - }, - - celestialGuardian: { - id: 'celestialGuardian', - name: 'Celestial Guardian', - desc: 'A divine protector sent by powers beyond mortal comprehension.', - role: 'guardian', - element: 'light', - rarity: 'legendary', - abilities: [ - ABILITIES.thorns(25, 5), - ABILITIES.manaShield(50, 10), - ABILITIES.damageBonus(10, 2), - ABILITIES.lifeSteal(5, 1), - ], - baseStats: { power: 120, bond: 12 }, - unlockCondition: { type: 'pact', value: 75 }, - flavorText: 'It radiates an aura of absolute protection and quiet judgment.', - }, - - voidEmperor: { - id: 'voidEmperor', - name: 'Void Emperor', - desc: 'A ruler from the spaces between dimensions, bound to your service.', - role: 'support', - element: 'void', - rarity: 'legendary', - abilities: [ - ABILITIES.castSpeed(15, 3), - ABILITIES.bonusGold(15, 3), - ABILITIES.manaRegen(4, 0.8), - ABILITIES.critChance(8, 1.5), - ], - baseStats: { power: 85, bond: 25 }, - unlockCondition: { type: 'floor', value: 90 }, - flavorText: 'It regards reality with the detached interest of a god.', - }, -}; - -// ─── Helper Functions ─────────────────────────────────────────────────────────── - -// Get XP required for next familiar level -export function getFamiliarXpRequired(level: number): number { - // Exponential scaling: 100 * 1.5^(level-1) - return Math.floor(100 * Math.pow(1.5, level - 1)); -} - -// Get bond required for next bond level (1-100) -export function getBondRequired(currentBond: number): number { - // Linear scaling, every 10 bond requires more time - const bondTier = Math.floor(currentBond / 10); - return 100 + bondTier * 50; // Base 100, +50 per tier -} - -// Calculate familiar's ability value at given level and ability level -export function getFamiliarAbilityValue( - ability: FamiliarAbility, - familiarLevel: number, - abilityLevel: number -): number { - // Base value + (familiar level bonus) + (ability level bonus) - const familiarBonus = Math.floor(familiarLevel / 10) * ability.scalingPerLevel; - const abilityBonus = (abilityLevel - 1) * ability.scalingPerLevel * 2; - return ability.baseValue + familiarBonus + abilityBonus; -} - -// Get all familiars of a specific rarity -export function getFamiliarsByRarity(rarity: FamiliarDef['rarity']): FamiliarDef[] { - return Object.values(FAMILIARS_DEF).filter(f => f.rarity === rarity); -} - -// Get all familiars of a specific role -export function getFamiliarsByRole(role: FamiliarRole): FamiliarDef[] { - return Object.values(FAMILIARS_DEF).filter(f => f.role === role); -} - -// Check if player meets unlock condition for a familiar -export function canUnlockFamiliar( - familiar: FamiliarDef, - maxFloor: number, - signedPacts: number[], - totalManaGathered: number, - skillsLearned: number -): boolean { - if (!familiar.unlockCondition) return true; - - const { type, value } = familiar.unlockCondition; - - switch (type) { - case 'floor': - return maxFloor >= value; - case 'pact': - return signedPacts.includes(value); - case 'mana': - return totalManaGathered >= value; - case 'study': - return skillsLearned >= value; - default: - return false; - } -} - -// Starting familiar (given to new players) -export const STARTING_FAMILIAR = 'manaWisp'; diff --git a/src/lib/game/familiar-slice.ts b/src/lib/game/familiar-slice.ts deleted file mode 100644 index 8f06c69..0000000 --- a/src/lib/game/familiar-slice.ts +++ /dev/null @@ -1,367 +0,0 @@ -// ─── Familiar Slice ───────────────────────────────────────────────────────────── -// Actions and computations for the familiar system - -import type { GameState, FamiliarInstance, FamiliarAbilityType } from './types'; -import { FAMILIARS_DEF, getFamiliarXpRequired, getFamiliarAbilityValue, canUnlockFamiliar, STARTING_FAMILIAR } from './data/familiars'; -import { HOURS_PER_TICK } from './constants'; - -// ─── Familiar Actions Interface ───────────────────────────────────────────────── - -export interface FamiliarActions { - // Summoning and management - summonFamiliar: (familiarId: string) => void; - setActiveFamiliar: (instanceIndex: number, active: boolean) => void; - setFamiliarNickname: (instanceIndex: number, nickname: string) => void; - - // Progression - gainFamiliarXp: (amount: number, source: 'combat' | 'gather' | 'meditate' | 'study' | 'time') => void; - upgradeFamiliarAbility: (instanceIndex: number, abilityType: FamiliarAbilityType) => void; - - // Computation - getActiveFamiliarBonuses: () => FamiliarBonuses; - getAvailableFamiliars: () => string[]; -} - -// ─── Computed Bonuses ─────────────────────────────────────────────────────────── - -export interface FamiliarBonuses { - damageMultiplier: number; - manaRegenBonus: number; - autoGatherRate: number; - autoConvertRate: number; - critChanceBonus: number; - castSpeedMultiplier: number; - elementalDamageMultiplier: number; - lifeStealPercent: number; - thornsPercent: number; - insightMultiplier: number; - manaShieldAmount: number; -} - -export const DEFAULT_FAMILIAR_BONUSES: FamiliarBonuses = { - damageMultiplier: 1, - manaRegenBonus: 0, - autoGatherRate: 0, - autoConvertRate: 0, - critChanceBonus: 0, - castSpeedMultiplier: 1, - elementalDamageMultiplier: 1, - lifeStealPercent: 0, - thornsPercent: 0, - insightMultiplier: 1, - manaShieldAmount: 0, -}; - -// ─── Familiar Slice Factory ───────────────────────────────────────────────────── - -export function createFamiliarSlice( - set: (fn: (state: GameState) => Partial) => void, - get: () => GameState -): FamiliarActions { - return { - // Summon a new familiar - summonFamiliar: (familiarId: string) => { - const state = get(); - const familiarDef = FAMILIARS_DEF[familiarId]; - if (!familiarDef) return; - - // Check if already owned - if (state.familiars.some(f => f.familiarId === familiarId)) return; - - // Check unlock condition - if (!canUnlockFamiliar( - familiarDef, - state.maxFloorReached, - state.signedPacts, - state.totalManaGathered, - Object.keys(state.skills).length - )) return; - - // Create new familiar instance - const newInstance: FamiliarInstance = { - familiarId, - level: 1, - bond: 0, - experience: 0, - abilities: familiarDef.abilities.map(a => ({ - type: a.type, - level: 1, - })), - active: false, - }; - - // Add to familiars list - set((s) => ({ - familiars: [...s.familiars, newInstance], - log: [`🌟 ${familiarDef.name} has answered your call!`, ...s.log.slice(0, 49)], - })); - }, - - // Set a familiar as active/inactive - setActiveFamiliar: (instanceIndex: number, active: boolean) => { - const state = get(); - if (instanceIndex < 0 || instanceIndex >= state.familiars.length) return; - - const activeCount = state.familiars.filter(f => f.active).length; - - // Check if we have slots available - if (active && activeCount >= state.activeFamiliarSlots) { - // Deactivate another familiar first - const newFamiliars = [...state.familiars]; - const activeIndex = newFamiliars.findIndex(f => f.active); - if (activeIndex >= 0) { - newFamiliars[activeIndex] = { ...newFamiliars[activeIndex], active: false }; - } - newFamiliars[instanceIndex] = { ...newFamiliars[instanceIndex], active }; - set({ familiars: newFamiliars }); - } else { - // Just toggle the familiar - const newFamiliars = [...state.familiars]; - newFamiliars[instanceIndex] = { ...newFamiliars[instanceIndex], active }; - set({ familiars: newFamiliars }); - } - }, - - // Set a familiar's nickname - setFamiliarNickname: (instanceIndex: number, nickname: string) => { - const state = get(); - if (instanceIndex < 0 || instanceIndex >= state.familiars.length) return; - - const newFamiliars = [...state.familiars]; - newFamiliars[instanceIndex] = { - ...newFamiliars[instanceIndex], - nickname: nickname || undefined - }; - set({ familiars: newFamiliars }); - }, - - // Grant XP to all active familiars - gainFamiliarXp: (amount: number, _source: 'combat' | 'gather' | 'meditate' | 'study' | 'time') => { - const state = get(); - if (state.familiars.length === 0) return; - - const newFamiliars = [...state.familiars]; - let leveled = false; - - for (let i = 0; i < newFamiliars.length; i++) { - const familiar = newFamiliars[i]; - if (!familiar.active) continue; - - const def = FAMILIARS_DEF[familiar.familiarId]; - if (!def) continue; - - // Apply bond multiplier to XP gain - const bondMultiplier = 1 + (familiar.bond / 100); - const xpGain = Math.floor(amount * bondMultiplier); - - let newExp = familiar.experience + xpGain; - let newLevel = familiar.level; - - // Check for level ups - while (newLevel < 100 && newExp >= getFamiliarXpRequired(newLevel)) { - newExp -= getFamiliarXpRequired(newLevel); - newLevel++; - leveled = true; - } - - // Gain bond passively - const newBond = Math.min(100, familiar.bond + 0.01); - - newFamiliars[i] = { - ...familiar, - level: newLevel, - experience: newExp, - bond: newBond, - }; - } - - set({ - familiars: newFamiliars, - totalFamiliarXpEarned: state.totalFamiliarXpEarned + amount, - ...(leveled ? { log: ['📈 Your familiar has grown stronger!', ...state.log.slice(0, 49)] } : {}), - }); - }, - - // Upgrade a familiar's ability - upgradeFamiliarAbility: (instanceIndex: number, abilityType: FamiliarAbilityType) => { - const state = get(); - if (instanceIndex < 0 || instanceIndex >= state.familiars.length) return; - - const familiar = state.familiars[instanceIndex]; - const def = FAMILIARS_DEF[familiar.familiarId]; - if (!def) return; - - // Find the ability - const abilityIndex = familiar.abilities.findIndex(a => a.type === abilityType); - if (abilityIndex < 0) return; - - const ability = familiar.abilities[abilityIndex]; - if (ability.level >= 10) return; // Max level - - // Cost: level * 100 XP - const cost = ability.level * 100; - if (familiar.experience < cost) return; - - // Upgrade - const newAbilities = [...familiar.abilities]; - newAbilities[abilityIndex] = { ...ability, level: ability.level + 1 }; - - const newFamiliars = [...state.familiars]; - newFamiliars[instanceIndex] = { - ...familiar, - abilities: newAbilities, - experience: familiar.experience - cost, - }; - - set({ familiars: newFamiliars }); - }, - - // Get total bonuses from active familiars - getActiveFamiliarBonuses: (): FamiliarBonuses => { - const state = get(); - const bonuses = { ...DEFAULT_FAMILIAR_BONUSES }; - - for (const familiar of state.familiars) { - if (!familiar.active) continue; - - const def = FAMILIARS_DEF[familiar.familiarId]; - if (!def) continue; - - // Bond multiplier: up to 50% bonus at max bond - const bondMultiplier = 1 + (familiar.bond / 200); - - for (const abilityInst of familiar.abilities) { - const abilityDef = def.abilities.find(a => a.type === abilityInst.type); - if (!abilityDef) continue; - - const value = getFamiliarAbilityValue(abilityDef, familiar.level, abilityInst.level) * bondMultiplier; - - switch (abilityInst.type) { - case 'damageBonus': - bonuses.damageMultiplier += value / 100; - break; - case 'manaRegen': - bonuses.manaRegenBonus += value; - break; - case 'autoGather': - bonuses.autoGatherRate += value; - break; - case 'autoConvert': - bonuses.autoConvertRate += value; - break; - case 'critChance': - bonuses.critChanceBonus += value; - break; - case 'castSpeed': - bonuses.castSpeedMultiplier += value / 100; - break; - case 'elementalBonus': - bonuses.elementalDamageMultiplier += value / 100; - break; - case 'lifeSteal': - bonuses.lifeStealPercent += value; - break; - case 'thorns': - bonuses.thornsPercent += value; - break; - case 'bonusGold': - bonuses.insightMultiplier += value / 100; - break; - case 'manaShield': - bonuses.manaShieldAmount += value; - break; - } - } - } - - return bonuses; - }, - - // Get list of available (unlocked but not owned) familiars - getAvailableFamiliars: (): string[] => { - const state = get(); - const owned = new Set(state.familiars.map(f => f.familiarId)); - - return Object.values(FAMILIARS_DEF) - .filter(f => - !owned.has(f.id) && - canUnlockFamiliar( - f, - state.maxFloorReached, - state.signedPacts, - state.totalManaGathered, - Object.keys(state.skills).length - ) - ) - .map(f => f.id); - }, - }; -} - -// ─── Familiar Tick Processing ─────────────────────────────────────────────────── - -// Process familiar-related tick effects (called from main tick) -export function processFamiliarTick( - state: Pick, - familiarBonuses: FamiliarBonuses -): { rawMana: number; elements: GameState['elements']; totalManaGathered: number; gatherLog?: string } { - let rawMana = state.rawMana; - let elements = state.elements; - let totalManaGathered = state.totalManaGathered; - let gatherLog: string | undefined; - - // Auto-gather from familiars - if (familiarBonuses.autoGatherRate > 0) { - const gathered = familiarBonuses.autoGatherRate * HOURS_PER_TICK; - rawMana += gathered; - totalManaGathered += gathered; - if (gathered >= 1) { - gatherLog = `✨ Familiars gathered ${Math.floor(gathered)} mana`; - } - } - - // Auto-convert from familiars - if (familiarBonuses.autoConvertRate > 0) { - const convertAmount = Math.min( - familiarBonuses.autoConvertRate * HOURS_PER_TICK, - Math.floor(rawMana / 5) // 5 raw mana per element - ); - - if (convertAmount > 0) { - // Find unlocked elements with space - const unlockedElements = Object.entries(elements) - .filter(([, e]) => e.unlocked && e.current < e.max) - .sort((a, b) => (b[1].max - b[1].current) - (a[1].max - a[1].current)); - - if (unlockedElements.length > 0) { - const [targetId, targetState] = unlockedElements[0]; - const canConvert = Math.min(convertAmount, targetState.max - targetState.current); - rawMana -= canConvert * 5; - elements = { - ...elements, - [targetId]: { ...targetState, current: targetState.current + canConvert }, - }; - } - } - } - - return { rawMana, elements, totalManaGathered, gatherLog }; -} - -// Grant starting familiar to new players -export function grantStartingFamiliar(): FamiliarInstance[] { - const starterDef = FAMILIARS_DEF[STARTING_FAMILIAR]; - if (!starterDef) return []; - - return [{ - familiarId: STARTING_FAMILIAR, - level: 1, - bond: 0, - experience: 0, - abilities: starterDef.abilities.map(a => ({ - type: a.type, - level: 1, - })), - active: true, // Start with familiar active - }]; -} diff --git a/src/lib/game/store.ts b/src/lib/game/store.ts index 6225ab8..f450012 100755 --- a/src/lib/game/store.ts +++ b/src/lib/game/store.ts @@ -32,14 +32,6 @@ import { type CraftingActions } from './crafting-slice'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects'; -import { - createFamiliarSlice, - processFamiliarTick, - grantStartingFamiliar, - type FamiliarActions, - type FamiliarBonuses, - DEFAULT_FAMILIAR_BONUSES, -} from './familiar-slice'; import { createNavigationSlice, type NavigationActions, @@ -280,20 +272,14 @@ function makeInitial(overrides: Partial = {}): GameState { totalSpellsCast: 0, totalCraftsCompleted: 0, - // Familiars - familiars: grantStartingFamiliar(), - activeFamiliarSlots: 1, - familiarSummonProgress: 0, - totalFamiliarXpEarned: 0, - - log: ['✨ The loop begins. You start with a Basic Staff (Mana Bolt) and civilian clothes. A friendly Mana Wisp floats nearby. Gather your strength, mage.'], + log: ['✨ The loop begins. You start with a Basic Staff (Mana Bolt) and civilian clothes. Gather your strength, mage.'], loopInsight: 0, }; } // ─── Game Store ─────────────────────────────────────────────────────────────── -interface GameStore extends GameState, CraftingActions, FamiliarActions, NavigationActions, StudyActions { +interface GameStore extends GameState, CraftingActions, NavigationActions, StudyActions { // Actions tick: () => void; gatherMana: () => void; @@ -329,7 +315,6 @@ export const useGameStore = create()( persist( (set, get) => ({ ...makeInitial(), - ...createFamiliarSlice(set, get), ...createNavigationSlice(set, get), ...createStudySlice(set, get), @@ -359,16 +344,8 @@ export const useGameStore = create()( // Compute unified effects (includes skill upgrades AND equipment enchantments) const effects = getUnifiedEffects(state); - // Compute familiar bonuses - const familiarBonuses = state.familiars.length > 0 - ? (() => { - const slice = createFamiliarSlice(set, get); - return slice.getActiveFamiliarBonuses(); - })() - : DEFAULT_FAMILIAR_BONUSES; - const maxMana = computeMaxMana(state, effects); - const baseRegen = computeRegen(state, effects) + familiarBonuses.manaRegenBonus; + const baseRegen = computeRegen(state, effects); // Time progression let hour = state.hour + HOURS_PER_TICK; @@ -431,18 +408,7 @@ export const useGameStore = create()( // Mana regeneration let rawMana = Math.min(state.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana); let totalManaGathered = state.totalManaGathered; - - // Familiar auto-gather and auto-convert let elements = state.elements; - if (familiarBonuses.autoGatherRate > 0 || familiarBonuses.autoConvertRate > 0) { - const familiarUpdates = processFamiliarTick( - { rawMana, elements, totalManaGathered, familiars: state.familiars, activeFamiliarSlots: state.activeFamiliarSlots }, - familiarBonuses - ); - rawMana = Math.min(familiarUpdates.rawMana, maxMana); - elements = familiarUpdates.elements; - totalManaGathered = familiarUpdates.totalManaGathered; - } // Study progress let currentStudyTarget = state.currentStudyTarget; @@ -623,7 +589,7 @@ export const useGameStore = create()( // Compute attack speed from quickCast skill and upgrades const baseAttackSpeed = 1 + (state.skills.quickCast || 0) * 0.05; - const totalAttackSpeed = baseAttackSpeed * effects.attackSpeedMultiplier * familiarBonuses.castSpeedMultiplier; + const totalAttackSpeed = baseAttackSpeed * effects.attackSpeedMultiplier; // Process each active spell for (const { spellId, equipmentId } of activeSpells) { @@ -707,15 +673,6 @@ export const useGameStore = create()( log = [`💥 Combo Master! Triple damage!`, ...log.slice(0, 49)]; } - // Familiar bonuses - dmg *= familiarBonuses.damageMultiplier; - dmg *= familiarBonuses.elementalDamageMultiplier; - - // Familiar crit chance bonus - if (Math.random() < familiarBonuses.critChanceBonus / 100) { - dmg *= 1.5; - } - // Spell echo - chance to cast again const echoChance = (skills.spellEcho || 0) * 0.1; if (Math.random() < echoChance) { @@ -730,12 +687,6 @@ export const useGameStore = create()( rawMana = Math.min(rawMana + healAmount, maxMana); } - // Familiar lifesteal - if (familiarBonuses.lifeStealPercent > 0) { - const healAmount = dmg * (familiarBonuses.lifeStealPercent / 100); - rawMana = Math.min(rawMana + healAmount, maxMana); - } - // Track total damage for achievements totalDamageDealt += dmg; @@ -899,36 +850,6 @@ export const useGameStore = create()( return; } - // Grant XP to active familiars based on activity - let familiars = state.familiars; - if (familiars.some(f => f.active)) { - let xpGain = 0; - let xpSource: 'combat' | 'gather' | 'meditate' | 'study' | 'time' = 'time'; - - if (state.currentAction === 'climb') { - xpGain = 2 * HOURS_PER_TICK; // 2 XP per hour in combat - xpSource = 'combat'; - } else if (state.currentAction === 'meditate') { - xpGain = 1 * HOURS_PER_TICK; - xpSource = 'meditate'; - } else if (state.currentAction === 'study') { - xpGain = 1.5 * HOURS_PER_TICK; - xpSource = 'study'; - } else { - xpGain = 0.5 * HOURS_PER_TICK; // Passive XP - } - - // Update familiar XP and bond - familiars = familiars.map(f => { - if (!f.active) return f; - const bondMultiplier = 1 + (f.bond / 100); - const xpGained = Math.floor(xpGain * bondMultiplier); - const newXp = f.experience + xpGained; - const newBond = Math.min(100, f.bond + 0.02); // Slow bond gain - return { ...f, experience: newXp, bond: newBond }; - }); - } - set({ day, hour, @@ -955,7 +876,6 @@ export const useGameStore = create()( achievements, totalDamageDealt, totalSpellsCast, - familiars, consecutiveStudyHours, studyStartedAt, ...craftingUpdates, @@ -1738,11 +1658,6 @@ export const useGameStore = create()( totalDamageDealt: state.totalDamageDealt, totalSpellsCast: state.totalSpellsCast, totalCraftsCompleted: state.totalCraftsCompleted, - // Familiars - familiars: state.familiars, - activeFamiliarSlots: state.activeFamiliarSlots, - familiarSummonProgress: state.familiarSummonProgress, - totalFamiliarXpEarned: state.totalFamiliarXpEarned, }), } ) diff --git a/src/lib/game/types.ts b/src/lib/game/types.ts index 1dd3f43..fef717a 100755 --- a/src/lib/game/types.ts +++ b/src/lib/game/types.ts @@ -314,69 +314,6 @@ export interface BlueprintDef { learned: boolean; } -// ─── Familiar System ─────────────────────────────────────────────────────────── - -// Familiar role determines their primary function -export type FamiliarRole = 'combat' | 'mana' | 'support' | 'guardian'; - -// Familiar ability types -export type FamiliarAbilityType = - | 'damageBonus' // +X% damage - | 'manaRegen' // +X mana regen - | 'autoGather' // Gathers mana automatically - | 'critChance' // +X% crit chance - | 'castSpeed' // +X% cast speed - | 'manaShield' // Absorbs damage, costs mana - | 'elementalBonus' // +X% elemental damage - | 'lifeSteal' // Heal on hit - | 'bonusGold' // +X% insight gain - | 'autoConvert' // Auto-converts mana to elements - | 'thorns'; // Reflects damage - -export interface FamiliarAbility { - type: FamiliarAbilityType; - baseValue: number; // Base effect value - scalingPerLevel: number; // How much it increases per familiar level - desc: string; -} - -// Familiar definition (static data) -export interface FamiliarDef { - id: string; - name: string; - desc: string; - role: FamiliarRole; - element: string; // Associated element - rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary'; - abilities: FamiliarAbility[]; - baseStats: { - power: number; // Affects ability strength - bond: number; // How fast bond grows - }; - unlockCondition?: { - type: 'floor' | 'pact' | 'mana' | 'study'; - value: number; - }; - flavorText?: string; -} - -// Familiar instance (player's owned familiar) -export interface FamiliarInstance { - familiarId: string; // Reference to FamiliarDef - level: number; // 1-100 - bond: number; // 0-100, affects power multiplier - experience: number; // XP towards next level - abilities: Array<{ - type: FamiliarAbilityType; - level: number; // Ability level (1-10) - }>; - active: boolean; // Is this familiar currently summoned? - nickname?: string; // Optional custom name -} - -// Familiar experience gain sources -export type FamiliarXpSource = 'combat' | 'gather' | 'meditate' | 'study' | 'time'; - export type GameAction = 'meditate' | 'climb' | 'study' | 'craft' | 'repair' | 'convert' | 'design' | 'prepare' | 'enchant'; export interface ScheduleBlock { @@ -502,12 +439,6 @@ export interface GameState { totalSpellsCast: number; // For spell achievements totalCraftsCompleted: number; // For craft achievements - // Familiars - familiars: FamiliarInstance[]; // Owned familiars - activeFamiliarSlots: number; // How many familiars can be active (default 1) - familiarSummonProgress: number; // Progress toward summoning new familiar (0-100) - totalFamiliarXpEarned: number; // Lifetime XP earned for familiars - // Log log: string[];