Add golemancy system, combination skills, and UI redesigns
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 3m17s
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 3m17s
- Remove scroll crafting skill (no consumables in idle game) - Add golem types (Earth, Metal, Crystal) with variants (Lava, Mud, Forge, Storm) - Implement golemancy state in store with summoning/drain mechanics - Add combination skills requiring level 5+ in two attunements: - Enchanter+Fabricator: Enchanted Golems, Capacity Overflow - Invoker+Fabricator: Pact-Bonded Golems, Guardian Infusion - Invoker+Enchanter: Pact Enchantments, Elemental Resonance - Redesign CraftingTab with sub-tabs for Enchanter/Fabricator - Redesign SpireTab to show summoned golems and DPS contribution - Redesign StatsTab with attunement-specific stats sections - Update documentation (README.md, AGENTS.md)
This commit is contained in:
@@ -692,9 +692,9 @@ export const SKILLS_DEF: Record<string, SkillDef> = {
|
||||
enchantSpeed: { name: "Enchant Speed", desc: "-10% enchantment time", cat: "enchant", attunement: 'enchanter', attunementLevel: 1, max: 5, base: 300, studyTime: 4, req: { enchanting: 2 } },
|
||||
|
||||
// Advanced Enchanting (Lv 3)
|
||||
scrollCrafting: { name: "Scroll Crafting", desc: "Create scrolls to store enchantment designs", cat: "enchant", attunement: 'enchanter', attunementLevel: 3, max: 3, base: 500, studyTime: 8, req: { enchanting: 5 } },
|
||||
essenceRefining: { name: "Essence Refining", desc: "+10% enchantment effect power", cat: "enchant", attunement: 'enchanter', attunementLevel: 3, max: 5, base: 450, studyTime: 7, req: { enchanting: 4 } },
|
||||
transferenceMastery: { name: "Transference Mastery", desc: "+25% transference mana conversion", cat: "enchant", attunement: 'enchanter', attunementLevel: 3, max: 3, base: 600, studyTime: 10, req: { enchanting: 5 } },
|
||||
crystalEmbedding: { name: "Crystal Embedding", desc: "Embed elemental crystals in golems for variants", cat: "enchant", attunement: 'enchanter', attunementLevel: 3, max: 1, base: 600, studyTime: 12, req: { enchanting: 4 } },
|
||||
|
||||
// Master Enchanting (Lv 5+)
|
||||
soulBinding: { name: "Soul Binding", desc: "Enchantments persist through loops", cat: "enchant", attunement: 'enchanter', attunementLevel: 5, max: 2, base: 1500, studyTime: 24, req: { essenceRefining: 3 } },
|
||||
@@ -785,6 +785,24 @@ export const SKILLS_DEF: Record<string, SkillDef> = {
|
||||
// Legendary Fabrication (Lv 8+)
|
||||
legendaryCraft: { name: "Legendary Crafting", desc: "Chance for crafted items to gain +1 tier", cat: "fabrication", attunement: 'fabricator', attunementLevel: 8, max: 1, base: 2500, studyTime: 30, req: { alloyMastery: 1, runicCarving: 3 } },
|
||||
awakenedHand: { name: "Awakened Hand", desc: "Craft time -50%, +25% quality", cat: "fabrication", attunement: 'fabricator', attunementLevel: 8, max: 1, base: 3000, studyTime: 36, req: { legendaryCraft: 1 } },
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// COMBINATION SKILLS (Require Level 5+ in two attunements)
|
||||
// Powerful multi-attunement abilities
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Enchanter + Fabricator: Enchanted Golems & Superior Gear
|
||||
enchantedGolems: { name: "Enchanted Golems", desc: "Embed spell crystals in golems for elemental variants", cat: "combination", attunement: 'fabricator', attunementLevel: 5, max: 1, base: 2000, studyTime: 24, req: { golemancy: 1 }, reqAttunements: { enchanter: 5, fabricator: 5 } },
|
||||
capacityOverflow: { name: "Capacity Overflow", desc: "+25% enchantment capacity on fabricated gear", cat: "combination", attunement: 'fabricator', attunementLevel: 5, max: 3, base: 1500, studyTime: 16, req: { fabrication: 5 }, reqAttunements: { enchanter: 5, fabricator: 5 } },
|
||||
runicGolems: { name: "Runic Golems", desc: "Golems gain +50% damage, +30% duration", cat: "combination", attunement: 'fabricator', attunementLevel: 6, max: 1, base: 2500, studyTime: 30, req: { enchantedGolems: 1 }, reqAttunements: { enchanter: 6, fabricator: 6 } },
|
||||
|
||||
// Invoker + Fabricator: Pact-Bonded Golems
|
||||
pactBondedGolems: { name: "Pact-Bonded Golems", desc: "Golems gain bonuses from your signed pacts", cat: "combination", attunement: 'fabricator', attunementLevel: 5, max: 1, base: 2000, studyTime: 24, req: { golemancy: 1 }, reqAttunements: { invoker: 5, fabricator: 5 } },
|
||||
guardianInfusion: { name: "Guardian Infusion", desc: "Golems deal +25% damage to guardian floors", cat: "combination", attunement: 'fabricator', attunementLevel: 6, max: 1, base: 2500, studyTime: 30, req: { pactBondedGolems: 1 }, reqAttunements: { invoker: 6, fabricator: 6 } },
|
||||
|
||||
// Invoker + Enchanter: Pact-Based Enchantments
|
||||
pactEnchantments: { name: "Pact Enchantments", desc: "Unlock pact-specific enchantment effects", cat: "combination", attunement: 'enchanter', attunementLevel: 5, max: 1, base: 2000, studyTime: 24, req: { enchanting: 5 }, reqAttunements: { invoker: 5, enchanter: 5 } },
|
||||
elementalResonance: { name: "Elemental Resonance", desc: "Enchantments gain +20% power per signed pact", cat: "combination", attunement: 'enchanter', attunementLevel: 6, max: 1, base: 2500, studyTime: 30, req: { pactEnchantments: 1 }, reqAttunements: { invoker: 6, enchanter: 6 } },
|
||||
};
|
||||
|
||||
// ─── Prestige Upgrades ────────────────────────────────────────────────────────
|
||||
@@ -831,6 +849,11 @@ export const SKILL_CATEGORIES = [
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
{ id: 'fabrication', name: 'Fabrication', icon: '⚒️', attunement: 'fabricator' },
|
||||
{ id: 'golemancy', name: 'Golemancy', icon: '🗿', attunement: 'fabricator' },
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// COMBINATION (Requires 2+ attunements at level 5+)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
{ id: 'combination', name: 'Combination', icon: '🔮', attunement: null },
|
||||
];
|
||||
|
||||
// ─── Rarity Colors ───────────────────────────────────────────────────────────
|
||||
@@ -936,3 +959,104 @@ export const ELEMENT_ICON_NAMES: Record<string, string> = {
|
||||
void: 'CircleDot',
|
||||
raw: 'Circle',
|
||||
};
|
||||
|
||||
// ─── Golem Definitions ─────────────────────────────────────────────────────────
|
||||
// Golems are channeling summons that deal damage over time with mana drain
|
||||
// Duration = 1 floor base + 1 floor per Fabricator level (max 10)
|
||||
export const GOLEM_DEFS: Record<string, import('./types').GolemDef> = {
|
||||
// Basic Earth Golem - Unlocked with Fabricator attunement
|
||||
earthGolem: {
|
||||
id: 'earthGolem',
|
||||
name: 'Earth Golem',
|
||||
desc: 'A sturdy golem of compacted earth and stone. Slow but powerful.',
|
||||
element: 'earth',
|
||||
baseDamage: 15,
|
||||
baseHP: 100,
|
||||
summonCost: 50,
|
||||
drainRate: 2, // 2 earth mana per hour
|
||||
attackSpeed: 1.5, // 1.5 attacks per hour
|
||||
unlockCondition: 'Unlock Fabricator attunement',
|
||||
requiredManaType: 'earth',
|
||||
},
|
||||
|
||||
// Metal Golem - Requires metal mana (fire + earth)
|
||||
metalGolem: {
|
||||
id: 'metalGolem',
|
||||
name: 'Metal Golem',
|
||||
desc: 'A gleaming construct of forged metal. Faster and deadlier than earth.',
|
||||
element: 'metal',
|
||||
baseDamage: 25,
|
||||
baseHP: 150,
|
||||
summonCost: 100,
|
||||
drainRate: 3, // 3 metal mana per hour
|
||||
attackSpeed: 2, // 2 attacks per hour
|
||||
unlockCondition: 'Unlock Metal mana (fire + earth) and Metalworking skill',
|
||||
requiredManaType: 'metal',
|
||||
},
|
||||
|
||||
// Crystal Golem - Requires crystal mana (advanced)
|
||||
crystalGolem: {
|
||||
id: 'crystalGolem',
|
||||
name: 'Crystal Golem',
|
||||
desc: 'A shimmering guardian of crystallized mana. Fires piercing bolts.',
|
||||
element: 'crystal',
|
||||
baseDamage: 40,
|
||||
baseHP: 120,
|
||||
summonCost: 200,
|
||||
drainRate: 5, // 5 crystal mana per hour
|
||||
attackSpeed: 2.5, // 2.5 attacks per hour
|
||||
unlockCondition: 'Unlock Crystal mana and Golemancy Master skill',
|
||||
requiredManaType: 'crystal',
|
||||
},
|
||||
};
|
||||
|
||||
// ─── Golem Variants (Combination Skills) ───────────────────────────────────────
|
||||
// These variants are created by embedding elemental crystals in golems
|
||||
export const GOLEM_VARIANTS: Record<string, {
|
||||
baseGolem: string;
|
||||
requiredElement: string;
|
||||
name: string;
|
||||
desc: string;
|
||||
damageMultiplier: number;
|
||||
effect: string;
|
||||
}> = {
|
||||
// Earth Golem + Fire = Lava Golem
|
||||
lavaGolem: {
|
||||
baseGolem: 'earthGolem',
|
||||
requiredElement: 'fire',
|
||||
name: 'Lava Golem',
|
||||
desc: 'Molten earth flows through this golem, burning all it touches.',
|
||||
damageMultiplier: 1.5,
|
||||
effect: 'burn',
|
||||
},
|
||||
|
||||
// Earth Golem + Water = Mud Golem
|
||||
mudGolem: {
|
||||
baseGolem: 'earthGolem',
|
||||
requiredElement: 'water',
|
||||
name: 'Mud Golem',
|
||||
desc: 'A viscous, slowing golem that entraps enemies.',
|
||||
damageMultiplier: 1.2,
|
||||
effect: 'slow',
|
||||
},
|
||||
|
||||
// Metal Golem + Fire = Forge Golem
|
||||
forgeGolem: {
|
||||
baseGolem: 'metalGolem',
|
||||
requiredElement: 'fire',
|
||||
name: 'Forge Golem',
|
||||
desc: 'Glowing hot metal radiates intense heat.',
|
||||
damageMultiplier: 1.6,
|
||||
effect: 'burn',
|
||||
},
|
||||
|
||||
// Metal Golem + Air = Storm Golem
|
||||
stormGolem: {
|
||||
baseGolem: 'metalGolem',
|
||||
requiredElement: 'air',
|
||||
name: 'Storm Golem',
|
||||
desc: 'Lightning arcs between metal plates, shocking enemies.',
|
||||
damageMultiplier: 1.4,
|
||||
effect: 'shock',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { GameState, GameAction, StudyTarget, SpellCost, SkillUpgradeChoice, EquipmentSlot, EquipmentInstance, EnchantmentDesign, DesignEffect, AttunementState } from './types';
|
||||
import type { GameState, GameAction, StudyTarget, SpellCost, SkillUpgradeChoice, EquipmentSlot, EquipmentInstance, EnchantmentDesign, DesignEffect, AttunementState, ActiveGolem } from './types';
|
||||
import {
|
||||
ELEMENTS,
|
||||
GUARDIANS,
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
EFFECT_RESEARCH_MAPPING,
|
||||
BASE_UNLOCKED_EFFECTS,
|
||||
ENCHANTING_UNLOCK_EFFECTS,
|
||||
GOLEM_DEFS,
|
||||
GOLEM_VARIANTS,
|
||||
} from './constants';
|
||||
import { computeEffects, hasSpecial, SPECIAL_EFFECTS, type ComputedEffects } from './upgrade-effects';
|
||||
import {
|
||||
@@ -473,6 +475,12 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
|
||||
elementChain: [],
|
||||
decayTimer: 0,
|
||||
},
|
||||
clearedFloors: {},
|
||||
|
||||
// Golemancy (Fabricator summons)
|
||||
activeGolems: [],
|
||||
unlockedGolemTypes: [],
|
||||
golemSummoningProgress: {},
|
||||
|
||||
spells: startSpells,
|
||||
skills: overrides.skills || {},
|
||||
@@ -572,6 +580,13 @@ interface GameStore extends GameState, CraftingActions {
|
||||
// Attunement XP and leveling
|
||||
addAttunementXP: (attunementId: string, amount: number) => void;
|
||||
|
||||
// Golemancy actions
|
||||
summonGolem: (golemId: string, variant?: string) => void;
|
||||
dismissGolem: (golemInstanceId: string) => void;
|
||||
canSummonGolem: (golemId: string) => boolean;
|
||||
getGolemDuration: () => number; // Floors a golem lasts
|
||||
getActiveGolemDPS: () => number; // Total DPS from all active golems
|
||||
|
||||
// Debug functions
|
||||
debugUnlockAttunement: (attunementId: string) => void;
|
||||
debugAddElementalMana: (element: string, amount: number) => void;
|
||||
@@ -1731,6 +1746,125 @@ export const useGameStore = create<GameStore>()(
|
||||
});
|
||||
},
|
||||
|
||||
// Golemancy functions
|
||||
getGolemDuration: () => {
|
||||
const state = get();
|
||||
const fabricatorLevel = state.attunements?.fabricator?.level || 0;
|
||||
// Base 1 floor + 1 floor per fabricator level, max 10
|
||||
return Math.min(10, 1 + fabricatorLevel);
|
||||
},
|
||||
|
||||
canSummonGolem: (golemId: string) => {
|
||||
const state = get();
|
||||
const golemDef = GOLEM_DEFS[golemId];
|
||||
if (!golemDef) return false;
|
||||
|
||||
// Check if player has the golemancy skill
|
||||
if (!state.skills.golemancy) return false;
|
||||
|
||||
// Check if the required mana type is unlocked and has enough
|
||||
const manaType = golemDef.requiredManaType;
|
||||
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)
|
||||
if (state.activeGolems.some(g => g.golemId === golemId)) return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
summonGolem: (golemId: string, variant?: string) => {
|
||||
const state = get();
|
||||
const golemDef = GOLEM_DEFS[golemId];
|
||||
if (!golemDef) return;
|
||||
|
||||
// Double-check requirements
|
||||
if (!state.skills.golemancy) return;
|
||||
const manaType = golemDef.requiredManaType;
|
||||
const elem = state.elements[manaType];
|
||||
if (!elem?.unlocked || elem.current < golemDef.summonCost) return;
|
||||
if (state.activeGolems.some(g => g.golemId === golemId)) return;
|
||||
|
||||
// Deduct mana cost
|
||||
const duration = Math.min(10, 1 + (state.attunements?.fabricator?.level || 0));
|
||||
const maxHP = golemDef.baseHP * (1 + (state.skills.golemVitality || 0) * 0.2);
|
||||
const hasGolemancyMaster = state.skills.golemancyMaster === 1;
|
||||
|
||||
const newGolem: ActiveGolem = {
|
||||
golemId,
|
||||
variant,
|
||||
currentFloor: state.currentFloor,
|
||||
remainingFloors: duration,
|
||||
currentHP: hasGolemancyMaster ? maxHP * 1.5 : maxHP,
|
||||
maxHP: hasGolemancyMaster ? maxHP * 1.5 : maxHP,
|
||||
damageDealt: 0,
|
||||
enchantedWith: [],
|
||||
};
|
||||
|
||||
set({
|
||||
elements: {
|
||||
...state.elements,
|
||||
[manaType]: {
|
||||
...elem,
|
||||
current: elem.current - golemDef.summonCost,
|
||||
},
|
||||
},
|
||||
activeGolems: [...state.activeGolems, newGolem],
|
||||
log: [`🗿 Summoned ${variant ? GOLEM_VARIANTS[variant]?.name || golemDef.name : golemDef.name}! Lasts ${duration} floors.`, ...state.log.slice(0, 49)],
|
||||
});
|
||||
},
|
||||
|
||||
dismissGolem: (golemInstanceId: string) => {
|
||||
const state = get();
|
||||
const golem = state.activeGolems.find(g => `${g.golemId}-${g.currentFloor}` === golemInstanceId);
|
||||
if (!golem) return;
|
||||
|
||||
const golemDef = GOLEM_DEFS[golem.golemId];
|
||||
set({
|
||||
activeGolems: state.activeGolems.filter(g => `${g.golemId}-${g.currentFloor}` !== golemInstanceId),
|
||||
log: [`👋 Dismissed ${golemDef?.name || 'golem'}.`, ...state.log.slice(0, 49)],
|
||||
});
|
||||
},
|
||||
|
||||
getActiveGolemDPS: () => {
|
||||
const state = get();
|
||||
let totalDPS = 0;
|
||||
|
||||
for (const golem of state.activeGolems) {
|
||||
if (golem.currentFloor !== state.currentFloor) continue;
|
||||
|
||||
const golemDef = GOLEM_DEFS[golem.golemId];
|
||||
if (!golemDef) continue;
|
||||
|
||||
let damage = golemDef.baseDamage;
|
||||
|
||||
// Apply variant multiplier
|
||||
if (golem.variant && GOLEM_VARIANTS[golem.variant]) {
|
||||
damage *= GOLEM_VARIANTS[golem.variant].damageMultiplier;
|
||||
}
|
||||
|
||||
// Apply golemancy master bonus
|
||||
if (state.skills.golemancyMaster === 1) {
|
||||
damage *= 1.5;
|
||||
}
|
||||
|
||||
// Apply pact bonuses if pactBondedGolems skill
|
||||
if (state.skills.pactBondedGolems === 1) {
|
||||
damage *= 1 + (state.signedPacts.length * 0.1);
|
||||
}
|
||||
|
||||
// Apply guardian bonus if guardianInfusion skill and guardian floor
|
||||
if (state.skills.guardianInfusion === 1 && GUARDIANS[state.currentFloor]) {
|
||||
damage *= 1.25;
|
||||
}
|
||||
|
||||
// DPS = damage * attack speed
|
||||
totalDPS += damage * golemDef.attackSpeed;
|
||||
}
|
||||
|
||||
return totalDPS;
|
||||
},
|
||||
|
||||
// Debug functions
|
||||
debugUnlockAttunement: (attunementId: string) => {
|
||||
const state = get();
|
||||
|
||||
@@ -107,6 +107,7 @@ export interface SkillDef {
|
||||
max: number;
|
||||
base: number; // Mana cost to start studying
|
||||
req?: Record<string, number>;
|
||||
reqAttunements?: Record<string, number>; // Required attunement levels for combination skills
|
||||
studyTime: number; // Hours needed to study
|
||||
level?: number; // Current level (optional, for UI display)
|
||||
tier?: number; // Skill tier (1-5)
|
||||
@@ -323,6 +324,41 @@ export interface ComboState {
|
||||
decayTimer: number; // Hours until decay starts
|
||||
}
|
||||
|
||||
// ─── Golemancy System ─────────────────────────────────────────────────────────
|
||||
|
||||
// Golem types available for summoning
|
||||
export interface GolemDef {
|
||||
id: string;
|
||||
name: string;
|
||||
desc: string;
|
||||
element: string; // Primary element (earth, metal, etc.)
|
||||
baseDamage: number; // Base damage per attack
|
||||
baseHP: number; // Base HP (for durability)
|
||||
summonCost: number; // Initial mana cost to summon
|
||||
drainRate: number; // Ongoing mana drain per hour
|
||||
attackSpeed: number; // Attacks per hour
|
||||
unlockCondition: string; // Description of how to unlock
|
||||
requiredManaType: string; // Mana type needed for this golem
|
||||
variant?: string; // For upgraded variants (e.g., 'lava' for earth+fire)
|
||||
}
|
||||
|
||||
// Active golem state on a floor
|
||||
export interface ActiveGolem {
|
||||
golemId: string; // Type of golem
|
||||
variant?: string; // Current variant (if upgraded)
|
||||
currentFloor: number; // Floor the golem is on
|
||||
remainingFloors: number; // How many more floors this golem will last
|
||||
currentHP: number; // Current HP
|
||||
maxHP: number; // Max HP
|
||||
damageDealt: number; // Total damage dealt so far
|
||||
enchantedWith?: string[]; // Elemental crystals embedded (for combination skills)
|
||||
}
|
||||
|
||||
// Cleared floors tracking
|
||||
export interface ClearedFloors {
|
||||
[floor: number]: boolean;
|
||||
}
|
||||
|
||||
export interface GameState {
|
||||
// Time
|
||||
day: number;
|
||||
@@ -353,6 +389,12 @@ export interface GameState {
|
||||
currentAction: GameAction;
|
||||
castProgress: number; // Progress towards next spell cast (0-1)
|
||||
combo: ComboState; // Combat combo tracking
|
||||
clearedFloors: ClearedFloors; // Track which floors have been cleared
|
||||
|
||||
// Golemancy (Fabricator summons)
|
||||
activeGolems: ActiveGolem[]; // Currently summoned golems
|
||||
unlockedGolemTypes: string[]; // Golem types that have been unlocked
|
||||
golemSummoningProgress: Record<string, number>; // Progress toward summoning each type
|
||||
|
||||
// Spells
|
||||
spells: Record<string, SpellState>;
|
||||
|
||||
Reference in New Issue
Block a user