All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m28s
- Fix transference mana display (show unlocked elements even with 0 mana) - Remove blocking/dodging mechanics (player has no health): - Replace Seer's foresight with criticalMastery - Replace Warden's defensive skills with mana efficiency skills - Replace Strider's evasive with fluidMotion - Add guardian barriers (50% of HP, doesn't regenerate) - Add floor HP regeneration (scales with floor level, 0 for guardians) - Implement climb-down mechanic: - Cannot switch away from climb while above floor 1 - Must fight through each floor to exit - Exit Spire button triggers descent - Update UI to show barrier bar and descent status
568 lines
18 KiB
TypeScript
Executable File
568 lines
18 KiB
TypeScript
Executable File
// ─── 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';
|
|
}
|