feat: Add attunement system foundation
Some checks failed
Build and Publish Mana Loop Docker Image / build-and-publish (push) Has been cancelled
Some checks failed
Build and Publish Mana Loop Docker Image / build-and-publish (push) Has been cancelled
- Create attunements.ts with 7 attunement types: - Enchanter (right hand) - Transference mana, enchanting - Caster (left hand) - Form mana, spell damage - Seer (head) - Vision mana, critical hits - Warden (back) - Barrier mana, defense - Invoker (chest) - Guardian pact mana types - Strider (left leg) - Flow mana, speed - Anchor (right leg) - Stability mana, max mana - Add AttunementState, ManaType types - Add attunement fields to GameState - Update SkillDef to include attunement field
This commit is contained in:
567
src/lib/game/attunements.ts
Normal file
567
src/lib/game/attunements.ts
Normal file
@@ -0,0 +1,567 @@
|
||||
// ─── 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,
|
||||
},
|
||||
foresight: {
|
||||
name: 'Foresight',
|
||||
desc: 'Chance to anticipate and dodge attacks',
|
||||
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
|
||||
// Protection and defense. Damage reduction and shields.
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
warden: {
|
||||
id: 'warden',
|
||||
name: 'Warden',
|
||||
slot: 'back',
|
||||
description: 'Shield yourself with protective wards and barriers.',
|
||||
capability: 'Generate protective shields. -10% damage taken.',
|
||||
primaryManaType: 'barrier',
|
||||
rawManaRegen: 0.25,
|
||||
autoConvertRate: 0.12,
|
||||
icon: 'Shield',
|
||||
color: '#10B981', // Green
|
||||
skills: {
|
||||
warding: {
|
||||
name: 'Warding',
|
||||
desc: 'Generate protective shields',
|
||||
cat: 'warden',
|
||||
max: 10,
|
||||
base: 100,
|
||||
studyTime: 8,
|
||||
},
|
||||
fortitude: {
|
||||
name: 'Fortitude',
|
||||
desc: 'Reduce damage taken',
|
||||
cat: 'warden',
|
||||
max: 10,
|
||||
base: 150,
|
||||
studyTime: 10,
|
||||
},
|
||||
reflection: {
|
||||
name: 'Reflection',
|
||||
desc: 'Chance to reflect damage to attacker',
|
||||
cat: 'warden',
|
||||
max: 5,
|
||||
base: 300,
|
||||
studyTime: 15,
|
||||
req: { warding: 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: 'Evasive',
|
||||
desc: 'Chance to avoid damage',
|
||||
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';
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
// ─── Game Types ───────────────────────────────────────────────────────────────
|
||||
|
||||
import type { AttunementType, AttunementState, ManaType } from './attunements';
|
||||
|
||||
// Re-export attunement types
|
||||
export type { AttunementType, AttunementState, ManaType, AttunementSlot } from './attunements';
|
||||
export { ATTUNEMENT_SLOTS } from './attunements';
|
||||
|
||||
export type ElementCategory = 'base' | 'utility' | 'composite' | 'exotic';
|
||||
|
||||
// Equipment slots for the equipment system
|
||||
@@ -86,6 +92,8 @@ export interface SkillDef {
|
||||
tierUp?: string; // Skill ID this evolves into at max level
|
||||
baseSkill?: string; // Original skill ID this evolved from
|
||||
tierMultiplier?: number; // Multiplier for each tier (default 2)
|
||||
attunement?: AttunementType; // Which attunement this skill belongs to (if any)
|
||||
manaType?: ManaType; // Primary mana type used (if attunement-specific)
|
||||
}
|
||||
|
||||
// Skill upgrade choices at milestones (level 5 and level 10)
|
||||
@@ -423,6 +431,13 @@ export interface GameState {
|
||||
memorySlots: number;
|
||||
memories: string[];
|
||||
|
||||
// Attunement System
|
||||
attunements: Record<AttunementType, AttunementState>; // Attunement unlock and state
|
||||
attunementSkills: Record<string, number>; // Attunement-specific skill levels
|
||||
attunementSkillProgress: Record<string, number>; // Progress for attunement skills
|
||||
primaryMana: Record<ManaType, number>; // Primary mana pools by type
|
||||
primaryManaMax: Record<ManaType, number>; // Max primary mana by type
|
||||
|
||||
// Incursion
|
||||
incursionStrength: number;
|
||||
containmentWards: number;
|
||||
|
||||
Reference in New Issue
Block a user