Remove movement speed, fix golemancy, cleanup
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m56s

- Remove movement speed references (spire_runner, swift_descent enchantments)
- Remove slow effect from Mud Golem variant (changed to drown effect)
- Delete unused attunements.ts file (legacy code)
- Add max golem limit: 1 per Fabricator attunement level
- Add max 1 golem of each type restriction
- Display mana/hr and damage/hr in golemancy UI
- Display active golem limit in header
- Show special effects for golem variants
- Keep expeditious_retreat enchantment for teleporting down floors

🤖 Generated with [Claude Code](https://claude.ai/code)
This commit is contained in:
Z User
2026-03-28 12:16:08 +00:00
parent f07454e024
commit 50a3704a7d
5 changed files with 54 additions and 594 deletions

View File

@@ -984,6 +984,18 @@ export function CraftingTab({ store }: CraftingTabProps) {
</div>
</div>
{/* Additional Stats */}
<div className="grid grid-cols-2 gap-2 text-xs mb-2 p-2 bg-gray-900/50 rounded">
<div className="flex justify-between">
<span className="text-gray-400">DMG/hr:</span>
<span className="text-red-300 font-semibold">{golemDef.baseDamage * golemDef.attackSpeed}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-400">Mana/hr:</span>
<span className="text-blue-300 font-semibold">{golemDef.drainRate} {elementDef?.name || golemDef.element}</span>
</div>
</div>
<Button
className="w-full"
size="sm"
@@ -1014,10 +1026,10 @@ export function CraftingTab({ store }: CraftingTabProps) {
<CardTitle className="text-amber-400 text-sm flex items-center justify-between">
<span className="flex items-center gap-2">
<span className="text-lg"></span>
Active Golems ({activeGolems.length})
Active Golems ({activeGolems.length}/{Math.max(1, store.attunements?.fabricator?.level || 0)})
</span>
{golemancySkill >= 1 && (
<span className="text-xs text-gray-400">Duration: {golemDuration} floors</span>
<span className="text-xs text-gray-400">Duration: {golemDuration} floors | Max 1 per Fabricator level</span>
)}
</CardTitle>
</CardHeader>
@@ -1089,7 +1101,32 @@ export function CraftingTab({ store }: CraftingTabProps) {
/>
</div>
<div className="text-xs text-gray-400">
{/* Stats */}
<div className="grid grid-cols-2 gap-2 text-xs mt-2 pt-2 border-t border-gray-700">
<div>
<span className="text-gray-400">Mana/hr:</span>{' '}
<span className="text-blue-300">{golemDef?.drainRate || 0} {ELEMENTS[golemDef?.requiredManaType || 'earth']?.name || 'mana'}</span>
</div>
<div>
<span className="text-gray-400">DMG/hr:</span>{' '}
<span className="text-red-300">
{(() => {
const dmg = (variantDef?.damageMultiplier || 1) * (golemDef?.baseDamage || 0);
const speed = golemDef?.attackSpeed || 1;
return fmt(dmg * speed);
})()}
<span className="text-gray-500 text-xs">({golemDef?.attackSpeed || 1}×{golemDef?.baseDamage || 0})</span>
</span>
</div>
</div>
{variantDef && (
<div className="text-xs mt-2 p-1.5 bg-gray-900/50 rounded">
<span className="text-purple-400">Effect:</span> <span className="text-gray-300 capitalize">{variantDef.effect}</span>
</div>
)}
<div className="text-xs text-gray-400 mt-2">
<span className="text-green-400">Total Damage:</span> {fmt(golem.damageDealt)}
</div>
</div>

View File

@@ -1,567 +0,0 @@
// ─── Attunement System ─────────────────────────────────────────────────────────
// Attunements are powerful magical bonds tied to specific body locations
// Each grants a unique capability, primary mana type, and skill tree
import type { SkillDef } from './types';
// ─── Body Slots ───────────────────────────────────────────────────────────────
export type AttunementSlot =
| 'rightHand'
| 'leftHand'
| 'head'
| 'back'
| 'chest'
| 'leftLeg'
| 'rightLeg';
export const ATTUNEMENT_SLOTS: AttunementSlot[] = [
'rightHand',
'leftHand',
'head',
'back',
'chest',
'leftLeg',
'rightLeg',
];
// Slot display names
export const ATTUNEMENT_SLOT_NAMES: Record<AttunementSlot, string> = {
rightHand: 'Right Hand',
leftHand: 'Left Hand',
head: 'Head',
back: 'Back',
chest: 'Heart',
leftLeg: 'Left Leg',
rightLeg: 'Right Leg',
};
// ─── Mana Types ───────────────────────────────────────────────────────────────
export type ManaType =
// Primary mana types from attunements
| 'transference' // Enchanter - moving/enchanting
| 'form' // Caster - shaping spells
| 'vision' // Seer - perception/revelation
| 'barrier' // Warden - protection/defense
| 'flow' // Strider - movement/swiftness
| 'stability' // Anchor - grounding/endurance
// Guardian pact types (Invoker)
| 'fire'
| 'water'
| 'earth'
| 'air'
| 'light'
| 'dark'
| 'life'
| 'death'
// Raw mana
| 'raw';
// ─── Attunement Types ─────────────────────────────────────────────────────────
export type AttunementType =
| 'enchanter'
| 'caster'
| 'seer'
| 'warden'
| 'invoker'
| 'strider'
| 'anchor';
// ─── Attunement Definition ────────────────────────────────────────────────────
export interface AttunementDef {
id: AttunementType;
name: string;
slot: AttunementSlot;
description: string;
capability: string; // What this attunement unlocks
primaryManaType: ManaType | null; // null for Invoker (uses guardian types)
rawManaRegen: number; // Base raw mana regen bonus
autoConvertRate: number; // Raw mana -> primary mana per hour
skills: Record<string, SkillDef>; // Attunement-specific skills
icon: string; // Lucide icon name
color: string; // Theme color
}
// ─── Attunement State ─────────────────────────────────────────────────────────
export interface AttunementState {
unlocked: boolean;
level: number; // Attunement level (from challenges)
manaPool: number; // Current primary mana
maxMana: number; // Max primary mana pool
}
// ─── Attunement Definitions ───────────────────────────────────────────────────
export const ATTUNEMENTS: Record<AttunementType, AttunementDef> = {
// ═══════════════════════════════════════════════════════════════════════════
// ENCHANTER - Right Hand
// The starting attunement. Grants access to enchanting and transference magic.
// ═══════════════════════════════════════════════════════════════════════════
enchanter: {
id: 'enchanter',
name: 'Enchanter',
slot: 'rightHand',
description: 'Channel mana through your right hand to imbue equipment with magical properties.',
capability: 'Unlock enchanting. Apply enchantments using transference mana.',
primaryManaType: 'transference',
rawManaRegen: 0.5,
autoConvertRate: 0.2, // 0.2 transference per hour per raw regen
icon: 'Wand2',
color: '#8B5CF6', // Purple
skills: {
// Core enchanting skills
enchanting: {
name: 'Enchanting',
desc: 'Apply magical effects to equipment',
cat: 'enchanter',
max: 10,
base: 100,
studyTime: 8,
},
efficientEnchant: {
name: 'Efficient Enchanting',
desc: 'Reduce enchantment mana costs',
cat: 'enchanter',
max: 5,
base: 200,
studyTime: 12,
req: { enchanting: 3 },
},
disenchanting: {
name: 'Disenchanting',
desc: 'Remove enchantments and recover some mana',
cat: 'enchanter',
max: 5,
base: 150,
studyTime: 10,
req: { enchanting: 2 },
},
enchantSpeed: {
name: 'Swift Enchanting',
desc: 'Faster enchantment application',
cat: 'enchanter',
max: 5,
base: 175,
studyTime: 10,
req: { enchanting: 2 },
},
transferenceMastery: {
name: 'Transference Mastery',
desc: 'Increased transference mana pool and regen',
cat: 'enchanter',
max: 10,
base: 250,
studyTime: 15,
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// CASTER - Left Hand
// Shapes raw mana into spell patterns. Enhanced spell damage.
// ═══════════════════════════════════════════════════════════════════════════
caster: {
id: 'caster',
name: 'Caster',
slot: 'leftHand',
description: 'Shape mana into devastating spell patterns through your left hand.',
capability: 'Form mana shaping. +25% spell damage bonus.',
primaryManaType: 'form',
rawManaRegen: 0.3,
autoConvertRate: 0.15,
icon: 'Hand',
color: '#3B82F6', // Blue
skills: {
spellShaping: {
name: 'Spell Shaping',
desc: 'Increase spell damage and efficiency',
cat: 'caster',
max: 10,
base: 100,
studyTime: 8,
},
quickCast: {
name: 'Quick Cast',
desc: 'Faster spell casting speed',
cat: 'caster',
max: 10,
base: 120,
studyTime: 8,
},
spellEcho: {
name: 'Spell Echo',
desc: 'Chance to cast spells twice',
cat: 'caster',
max: 5,
base: 300,
studyTime: 15,
req: { spellShaping: 5 },
},
formMastery: {
name: 'Form Mastery',
desc: 'Increased form mana pool and regen',
cat: 'caster',
max: 10,
base: 250,
studyTime: 15,
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// SEER - Head
// Perception and revelation. Critical hit bonus and weakness detection.
// ═══════════════════════════════════════════════════════════════════════════
seer: {
id: 'seer',
name: 'Seer',
slot: 'head',
description: 'See beyond the veil. Reveal hidden truths and enemy weaknesses.',
capability: 'Reveal floor weaknesses. +20% critical hit chance.',
primaryManaType: 'vision',
rawManaRegen: 0.2,
autoConvertRate: 0.1,
icon: 'Eye',
color: '#F59E0B', // Amber
skills: {
insight: {
name: 'Insight',
desc: 'Increased critical hit chance',
cat: 'seer',
max: 10,
base: 100,
studyTime: 8,
},
revealWeakness: {
name: 'Reveal Weakness',
desc: 'Show enemy elemental weaknesses',
cat: 'seer',
max: 5,
base: 200,
studyTime: 12,
},
criticalMastery: {
name: 'Critical Mastery',
desc: 'Increased critical damage multiplier',
cat: 'seer',
max: 5,
base: 250,
studyTime: 15,
req: { insight: 5 },
},
visionMastery: {
name: 'Vision Mastery',
desc: 'Increased vision mana pool and regen',
cat: 'seer',
max: 10,
base: 250,
studyTime: 15,
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// WARDEN - Back
// Mana efficiency and resource management (no player health mechanics)
// ═══════════════════════════════════════════════════════════════════════════
warden: {
id: 'warden',
name: 'Warden',
slot: 'back',
description: 'Master the flow of mana. Reduce costs and extend your reserves.',
capability: 'Reduced mana costs. Extended mana reserves.',
primaryManaType: 'barrier',
rawManaRegen: 0.25,
autoConvertRate: 0.12,
icon: 'Shield',
color: '#10B981', // Green
skills: {
manaEfficiency: {
name: 'Mana Efficiency',
desc: 'Reduced mana costs for all actions',
cat: 'warden',
max: 10,
base: 100,
studyTime: 8,
},
manaReserves: {
name: 'Mana Reserves',
desc: 'Increased max mana pool',
cat: 'warden',
max: 10,
base: 150,
studyTime: 10,
},
manaRetention: {
name: 'Mana Retention',
desc: 'Reduced mana loss on loop reset',
cat: 'warden',
max: 5,
base: 300,
studyTime: 15,
req: { manaEfficiency: 5 },
},
barrierMastery: {
name: 'Barrier Mastery',
desc: 'Increased barrier mana pool and regen',
cat: 'warden',
max: 10,
base: 250,
studyTime: 15,
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// INVOKER - Chest/Heart
// Pact with guardians. No primary mana - uses guardian elemental types.
// ═══════════════════════════════════════════════════════════════════════════
invoker: {
id: 'invoker',
name: 'Invoker',
slot: 'chest',
description: 'Form pacts with spire guardians and channel their elemental power.',
capability: 'Pact with guardians. Gain mana types from pacted guardians.',
primaryManaType: null, // Uses guardian types instead
rawManaRegen: 0.4,
autoConvertRate: 0, // No auto-convert; mana comes from guardian pacts
icon: 'Heart',
color: '#EF4444', // Red
skills: {
pactMaking: {
name: 'Pact Making',
desc: 'Form stronger pacts with guardians',
cat: 'invoker',
max: 10,
base: 100,
studyTime: 8,
},
guardianChannel: {
name: 'Guardian Channeling',
desc: 'Channel guardian powers more effectively',
cat: 'invoker',
max: 10,
base: 150,
studyTime: 10,
},
elementalBurst: {
name: 'Elemental Burst',
desc: 'Unleash stored guardian energy',
cat: 'invoker',
max: 5,
base: 300,
studyTime: 15,
req: { pactMaking: 5, guardianChannel: 3 },
},
soulResonance: {
name: 'Soul Resonance',
desc: 'Deep bond with pacted guardians',
cat: 'invoker',
max: 5,
base: 400,
studyTime: 20,
req: { pactMaking: 8 },
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// STRIDER - Left Leg
// Movement and swiftness. Attack speed and mobility.
// ═══════════════════════════════════════════════════════════════════════════
strider: {
id: 'strider',
name: 'Strider',
slot: 'leftLeg',
description: 'Move with supernatural speed and grace.',
capability: 'Enhanced mobility. +15% attack speed.',
primaryManaType: 'flow',
rawManaRegen: 0.3,
autoConvertRate: 0.15,
icon: 'Zap',
color: '#06B6D4', // Cyan
skills: {
swiftness: {
name: 'Swiftness',
desc: 'Increased attack and movement speed',
cat: 'strider',
max: 10,
base: 100,
studyTime: 8,
},
evasive: {
name: 'Fluid Motion',
desc: 'Increased combo duration before decay',
cat: 'strider',
max: 5,
base: 200,
studyTime: 12,
},
momentum: {
name: 'Momentum',
desc: 'Build speed over consecutive attacks',
cat: 'strider',
max: 5,
base: 250,
studyTime: 15,
req: { swiftness: 5 },
},
flowMastery: {
name: 'Flow Mastery',
desc: 'Increased flow mana pool and regen',
cat: 'strider',
max: 10,
base: 250,
studyTime: 15,
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// ANCHOR - Right Leg
// Stability and endurance. Max mana and knockback resistance.
// ═══════════════════════════════════════════════════════════════════════════
anchor: {
id: 'anchor',
name: 'Anchor',
slot: 'rightLeg',
description: 'Stand firm against any force. Your foundation is unshakeable.',
capability: 'Increased stability. +100 max mana.',
primaryManaType: 'stability',
rawManaRegen: 0.35,
autoConvertRate: 0.18,
icon: 'Mountain',
color: '#78716C', // Stone gray
skills: {
grounding: {
name: 'Grounding',
desc: 'Increased max mana and stability',
cat: 'anchor',
max: 10,
base: 100,
studyTime: 8,
},
endurance: {
name: 'Endurance',
desc: 'Reduced mana costs when below 50% mana',
cat: 'anchor',
max: 5,
base: 200,
studyTime: 12,
},
ironWill: {
name: 'Iron Will',
desc: 'Prevent mana drain effects',
cat: 'anchor',
max: 5,
base: 250,
studyTime: 15,
req: { grounding: 5 },
},
stabilityMastery: {
name: 'Stability Mastery',
desc: 'Increased stability mana pool and regen',
cat: 'anchor',
max: 10,
base: 250,
studyTime: 15,
},
},
},
};
// ─── Helper Functions ─────────────────────────────────────────────────────────
/**
* Get the attunement for a specific body slot
*/
export function getAttunementForSlot(slot: AttunementSlot): AttunementDef | undefined {
return Object.values(ATTUNEMENTS).find(a => a.slot === slot);
}
/**
* Get the starting attunement (Enchanter - right hand)
*/
export function getStartingAttunement(): AttunementDef {
return ATTUNEMENTS.enchanter;
}
/**
* Check if an attunement is unlocked for the player
*/
export function isAttunementUnlocked(
attunementStates: Record<AttunementType, AttunementState>,
attunementType: AttunementType
): boolean {
return attunementStates[attunementType]?.unlocked ?? false;
}
/**
* Get total raw mana regen from all unlocked attunements
*/
export function getTotalAttunementRegen(
attunementStates: Record<AttunementType, AttunementState>
): number {
let total = 0;
for (const [type, state] of Object.entries(attunementStates)) {
if (state.unlocked) {
const def = ATTUNEMENTS[type as AttunementType];
if (def) {
total += def.rawManaRegen * (1 + state.level * 0.1); // +10% per level
}
}
}
return total;
}
/**
* Get mana type display name
*/
export function getManaTypeName(type: ManaType): string {
const names: Record<ManaType, string> = {
raw: 'Raw Mana',
transference: 'Transference',
form: 'Form',
vision: 'Vision',
barrier: 'Barrier',
flow: 'Flow',
stability: 'Stability',
fire: 'Fire',
water: 'Water',
earth: 'Earth',
air: 'Air',
light: 'Light',
dark: 'Dark',
life: 'Life',
death: 'Death',
};
return names[type] || type;
}
/**
* Get mana type color
*/
export function getManaTypeColor(type: ManaType): string {
const colors: Record<ManaType, string> = {
raw: '#A78BFA', // Light purple
transference: '#8B5CF6', // Purple
form: '#3B82F6', // Blue
vision: '#F59E0B', // Amber
barrier: '#10B981', // Green
flow: '#06B6D4', // Cyan
stability: '#78716C', // Stone
fire: '#EF4444', // Red
water: '#3B82F6', // Blue
earth: '#A16207', // Brown
air: '#94A3B8', // Slate
light: '#FCD34D', // Yellow
dark: '#6B7280', // Gray
life: '#22C55E', // Green
death: '#7C3AED', // Violet
};
return colors[type] || '#A78BFA';
}

View File

@@ -1130,9 +1130,9 @@ export const GOLEM_VARIANTS: Record<string, {
baseGolem: 'earthGolem',
requiredElement: 'water',
name: 'Mud Golem',
desc: 'A viscous, slowing golem that entraps enemies.',
desc: 'A viscous golem that suffocates enemies in clinging mud.',
damageMultiplier: 1.2,
effect: 'slow',
effect: 'drown',
},
// Metal Golem + Fire = Forge Golem

View File

@@ -583,26 +583,6 @@ export const ENCHANTMENT_EFFECTS: Record<string, EnchantmentEffectDef> = {
allowedEquipmentCategories: LEGS_ONLY,
effect: { type: 'special', specialId: 'expeditiousRetreat' }
},
swift_descent: {
id: 'swift_descent',
name: 'Swift Descent',
description: '+20% faster floor descent when exiting spire',
category: 'utility',
baseCapacityCost: 30,
maxStacks: 3,
allowedEquipmentCategories: LEGS_ONLY,
effect: { type: 'bonus', stat: 'descentSpeed', value: 20 }
},
spire_runner: {
id: 'spire_runner',
name: 'Spire Runner',
description: '+10% movement speed in spire (faster floor transitions)',
category: 'utility',
baseCapacityCost: 25,
maxStacks: 4,
allowedEquipmentCategories: ['legs', 'feet'],
effect: { type: 'multiplier', stat: 'spireSpeed', value: 1.10 }
},
};
// ─── Helper Functions ────────────────────────────────────────────────────────────

View File

@@ -1970,9 +1970,14 @@ export const useGameStore = create<GameStore>()(
const elem = state.elements[manaType];
if (!elem?.unlocked || elem.current < golemDef.summonCost) return false;
// Check if we already have this type of golem active (one of each type)
// Check if we already have this type of golem active (max 1 of each type)
if (state.activeGolems.some(g => g.golemId === golemId)) return false;
// Check max golem limit: 1 per Fabricator attunement level
const fabricatorLevel = state.attunements?.fabricator?.level || 0;
const maxGolems = Math.max(1, fabricatorLevel); // 1 golem per fabricator level, minimum 1
if (state.activeGolems.length >= maxGolems) return false;
return true;
},
@@ -1988,8 +1993,13 @@ export const useGameStore = create<GameStore>()(
if (!elem?.unlocked || elem.current < golemDef.summonCost) return;
if (state.activeGolems.some(g => g.golemId === golemId)) return;
// Check max golem limit
const fabricatorLevel = state.attunements?.fabricator?.level || 0;
const maxGolems = Math.max(1, fabricatorLevel);
if (state.activeGolems.length >= maxGolems) return;
// Deduct mana cost
const duration = Math.min(10, 1 + (state.attunements?.fabricator?.level || 0));
const duration = Math.min(10, 1 + fabricatorLevel);
const maxHP = golemDef.baseHP * (1 + (state.skills.golemVitality || 0) * 0.2);
const hasGolemancyMaster = state.skills.golemancyMaster === 1;