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

- 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:
Z User
2026-03-28 07:56:52 +00:00
parent 3c79e66b87
commit 17c6d5652d
9 changed files with 1596 additions and 139 deletions

View File

@@ -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',
},
};

View File

@@ -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();

View File

@@ -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>;