fix(#170): wire fabricator crafting completion + bonus enchantments + remove dead code
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m22s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m22s
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Circular Dependencies
|
# Circular Dependencies
|
||||||
Generated: 2026-05-27T13:57:23.187Z
|
Generated: 2026-05-27T17:14:06.661Z
|
||||||
|
|
||||||
No circular dependencies found. ✅
|
No circular dependencies found. ✅
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"generated": "2026-05-27T13:57:21.361Z",
|
"generated": "2026-05-27T17:14:04.806Z",
|
||||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -336,6 +336,7 @@ Mana-Loop/
|
|||||||
│ │ │ ├── combat-actions.ts
|
│ │ │ ├── combat-actions.ts
|
||||||
│ │ │ ├── combat-state.types.ts
|
│ │ │ ├── combat-state.types.ts
|
||||||
│ │ │ ├── combatStore.ts
|
│ │ │ ├── combatStore.ts
|
||||||
|
│ │ │ ├── crafting-equipment-tick.ts
|
||||||
│ │ │ ├── crafting-initial-state.ts
|
│ │ │ ├── crafting-initial-state.ts
|
||||||
│ │ │ ├── craftingStore.ts
|
│ │ │ ├── craftingStore.ts
|
||||||
│ │ │ ├── craftingStore.types.ts
|
│ │ │ ├── craftingStore.types.ts
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// ─── Equipment Crafting System ──────────────────────────────────────────────
|
// ─── Equipment Crafting System ──────────────────────────────────────────────
|
||||||
// Equipment crafting functions
|
// Equipment crafting functions
|
||||||
|
|
||||||
import type { EquipmentInstance, EquipmentCraftingProgress } from './types';
|
import type { EquipmentInstance, EquipmentCraftingProgress, AppliedEnchantment } from './types';
|
||||||
import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes';
|
import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes';
|
||||||
import { EQUIPMENT_TYPES } from './data/equipment';
|
import { EQUIPMENT_TYPES } from './data/equipment';
|
||||||
import { ok, fail, ErrorCode } from './utils/result';
|
import { ok, fail, ErrorCode } from './utils/result';
|
||||||
@@ -92,20 +92,22 @@ const BASE_EQUIPMENT_QUALITY = 100;
|
|||||||
|
|
||||||
export function completeEquipmentCrafting(
|
export function completeEquipmentCrafting(
|
||||||
blueprintId: string,
|
blueprintId: string,
|
||||||
recipe: CraftingRecipe
|
recipe: CraftingRecipe,
|
||||||
|
bonusEnchantments: AppliedEnchantment[] = []
|
||||||
): Result<{ instanceId: string; instance: EquipmentInstance; logMessage: string }> {
|
): Result<{ instanceId: string; instance: EquipmentInstance; logMessage: string }> {
|
||||||
const equipType = EQUIPMENT_TYPES[recipe.equipmentTypeId];
|
const equipType = EQUIPMENT_TYPES[recipe.equipmentTypeId];
|
||||||
if (!equipType) return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`);
|
if (!equipType) return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`);
|
||||||
|
|
||||||
const instanceId = `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
const instanceId = `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
const usedCapacity = bonusEnchantments.reduce((sum, e) => sum + e.actualCost, 0);
|
||||||
return ok({
|
return ok({
|
||||||
instanceId,
|
instanceId,
|
||||||
instance: {
|
instance: {
|
||||||
instanceId,
|
instanceId,
|
||||||
typeId: recipe.equipmentTypeId,
|
typeId: recipe.equipmentTypeId,
|
||||||
name: recipe.name,
|
name: recipe.name,
|
||||||
enchantments: [],
|
enchantments: bonusEnchantments,
|
||||||
usedCapacity: 0,
|
usedCapacity,
|
||||||
totalCapacity: equipType.baseCapacity,
|
totalCapacity: equipType.baseCapacity,
|
||||||
rarity: recipe.rarity,
|
rarity: recipe.rarity,
|
||||||
quality: BASE_EQUIPMENT_QUALITY,
|
quality: BASE_EQUIPMENT_QUALITY,
|
||||||
|
|||||||
@@ -4,16 +4,13 @@ import type { EquipmentType } from './types';
|
|||||||
|
|
||||||
export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
||||||
// ─── Main Hand - Magic Swords ─────────────────────────────────────
|
// ─── Main Hand - Magic Swords ─────────────────────────────────────
|
||||||
// Magic swords have low base damage but high cast speed
|
// Magic swords can be enchanted with elemental effects that use mana over time
|
||||||
// They can be enchanted with elemental effects that use mana over time
|
|
||||||
ironBlade: {
|
ironBlade: {
|
||||||
id: 'ironBlade',
|
id: 'ironBlade',
|
||||||
name: 'Iron Blade',
|
name: 'Iron Blade',
|
||||||
category: 'sword',
|
category: 'sword',
|
||||||
slot: 'mainHand',
|
slot: 'mainHand',
|
||||||
baseCapacity: 30,
|
baseCapacity: 30,
|
||||||
baseDamage: 3,
|
|
||||||
baseCastSpeed: 4,
|
|
||||||
description: 'A simple iron sword. Can be enchanted with elemental effects.',
|
description: 'A simple iron sword. Can be enchanted with elemental effects.',
|
||||||
},
|
},
|
||||||
steelBlade: {
|
steelBlade: {
|
||||||
@@ -22,8 +19,6 @@ export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
|||||||
category: 'sword',
|
category: 'sword',
|
||||||
slot: 'mainHand',
|
slot: 'mainHand',
|
||||||
baseCapacity: 40,
|
baseCapacity: 40,
|
||||||
baseDamage: 4,
|
|
||||||
baseCastSpeed: 4,
|
|
||||||
description: 'A well-crafted steel sword. Balanced for combat and enchanting.',
|
description: 'A well-crafted steel sword. Balanced for combat and enchanting.',
|
||||||
},
|
},
|
||||||
crystalBlade: {
|
crystalBlade: {
|
||||||
@@ -32,8 +27,6 @@ export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
|||||||
category: 'sword',
|
category: 'sword',
|
||||||
slot: 'mainHand',
|
slot: 'mainHand',
|
||||||
baseCapacity: 55,
|
baseCapacity: 55,
|
||||||
baseDamage: 3,
|
|
||||||
baseCastSpeed: 5,
|
|
||||||
description: 'A blade made of crystallized mana. Excellent for elemental enchantments.',
|
description: 'A blade made of crystallized mana. Excellent for elemental enchantments.',
|
||||||
},
|
},
|
||||||
arcanistBlade: {
|
arcanistBlade: {
|
||||||
@@ -42,8 +35,6 @@ export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
|||||||
category: 'sword',
|
category: 'sword',
|
||||||
slot: 'mainHand',
|
slot: 'mainHand',
|
||||||
baseCapacity: 65,
|
baseCapacity: 65,
|
||||||
baseDamage: 5,
|
|
||||||
baseCastSpeed: 4,
|
|
||||||
description: 'A sword forged for battle mages. High capacity for powerful enchantments.',
|
description: 'A sword forged for battle mages. High capacity for powerful enchantments.',
|
||||||
},
|
},
|
||||||
voidBlade: {
|
voidBlade: {
|
||||||
@@ -52,8 +43,6 @@ export const SWORD_EQUIPMENT: Record<string, EquipmentType> = {
|
|||||||
category: 'sword',
|
category: 'sword',
|
||||||
slot: 'mainHand',
|
slot: 'mainHand',
|
||||||
baseCapacity: 50,
|
baseCapacity: 50,
|
||||||
baseDamage: 6,
|
|
||||||
baseCastSpeed: 3,
|
|
||||||
description: 'A blade corrupted by void energy. Powerful but consumes more mana.',
|
description: 'A blade corrupted by void energy. Powerful but consumes more mana.',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,5 @@ export interface EquipmentType {
|
|||||||
slot: EquipmentSlot;
|
slot: EquipmentSlot;
|
||||||
baseCapacity: number;
|
baseCapacity: number;
|
||||||
description: string;
|
description: string;
|
||||||
baseDamage?: number; // For swords
|
|
||||||
baseCastSpeed?: number; // For swords (higher = faster)
|
|
||||||
twoHanded?: boolean; // If true, weapon occupies both main hand and offhand slots
|
twoHanded?: boolean; // If true, weapon occupies both main hand and offhand slots
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 5,
|
craftTime: 5,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+20% Cast Speed, +15% Spell Damage',
|
gearTrait: '+20% Cast Speed, +15% Spell Damage',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'damage_10', stacks: 1, actualCost: 28 },
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'arcanistBlade',
|
id: 'arcanistBlade',
|
||||||
@@ -30,6 +34,11 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 7,
|
craftTime: 7,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+15% Spell Damage, +15% Cast Speed, +10% Enchantment Power',
|
gearTrait: '+15% Spell Damage, +15% Cast Speed, +10% Enchantment Power',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'damage_10', stacks: 1, actualCost: 28 },
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
{ effectId: 'metal_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'voidBlade',
|
id: 'voidBlade',
|
||||||
@@ -43,6 +52,10 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 6,
|
craftTime: 6,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+25% Spell Damage, +10% Attack Speed',
|
gearTrait: '+25% Spell Damage, +10% Attack Speed',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'damage_10', stacks: 1, actualCost: 28 },
|
||||||
|
{ effectId: 'dark_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'battleHelm',
|
id: 'battleHelm',
|
||||||
@@ -56,6 +69,10 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 4,
|
craftTime: 4,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+10% Spell Damage, +15% Attack Speed',
|
gearTrait: '+10% Spell Damage, +15% Attack Speed',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'damage_5', stacks: 1, actualCost: 15 },
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'battleRobe',
|
id: 'battleRobe',
|
||||||
@@ -69,6 +86,10 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 5,
|
craftTime: 5,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+15% Cast Speed, +10% Evasion',
|
gearTrait: '+15% Cast Speed, +10% Evasion',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'battleBoots',
|
id: 'battleBoots',
|
||||||
@@ -82,6 +103,9 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 3,
|
craftTime: 3,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+12% Cast Speed, +8% Attack Speed',
|
gearTrait: '+12% Cast Speed, +8% Attack Speed',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'combatGauntlets',
|
id: 'combatGauntlets',
|
||||||
@@ -95,6 +119,9 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 3,
|
craftTime: 3,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+10% Attack Speed, +10% Cast Speed',
|
gearTrait: '+10% Attack Speed, +10% Cast Speed',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'runicShield',
|
id: 'runicShield',
|
||||||
@@ -108,6 +135,10 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 5,
|
craftTime: 5,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+20% Defensive Enchantment Power, +25 Earth Mana Capacity',
|
gearTrait: '+20% Defensive Enchantment Power, +25 Earth Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'earth_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'manaShield',
|
id: 'manaShield',
|
||||||
@@ -121,5 +152,9 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 6,
|
craftTime: 6,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+15% Spell Damage, +15% Defensive Enchantment Power',
|
gearTrait: '+15% Spell Damage, +15% Defensive Enchantment Power',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
{ effectId: 'damage_5', stacks: 1, actualCost: 15 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// ─── Fabricator Recipe Types ───────────────────────────────────────────────────
|
// ─── Fabricator Recipe Types ───────────────────────────────────────────────────
|
||||||
// Shared types for fabricator and material recipes to avoid circular dependencies.
|
// Shared types for fabricator and material recipes to avoid circular dependencies.
|
||||||
|
|
||||||
import type { EquipmentSlot } from './equipment/types';
|
import type { EquipmentSlot } from './equipment/types';
|
||||||
|
import type { AppliedEnchantment } from '../types/equipment';
|
||||||
|
|
||||||
export interface FabricatorRecipe {
|
export interface FabricatorRecipe {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -23,6 +23,8 @@ export interface FabricatorRecipe {
|
|||||||
rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
|
rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
|
||||||
/** Flavor text describing the gear's properties */
|
/** Flavor text describing the gear's properties */
|
||||||
gearTrait: string;
|
gearTrait: string;
|
||||||
|
/** Actual enchantments applied to the crafted instance (empty = no bonus effects) */
|
||||||
|
bonusEnchantments?: AppliedEnchantment[];
|
||||||
/** Recipe type: 'equipment' (default) or 'material' */
|
/** Recipe type: 'equipment' (default) or 'material' */
|
||||||
recipeType?: 'equipment' | 'material';
|
recipeType?: 'equipment' | 'material';
|
||||||
/** For material recipes: the material ID produced */
|
/** For material recipes: the material ID produced */
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 3,
|
craftTime: 3,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+25 Earth Mana Capacity',
|
gearTrait: '+25 Earth Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'earth_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'earthChest',
|
id: 'earthChest',
|
||||||
@@ -38,6 +41,10 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 6,
|
craftTime: 6,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+50 Earth Mana Capacity, +10% Earth Mana Regen',
|
gearTrait: '+50 Earth Mana Capacity, +10% Earth Mana Regen',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'earth_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
{ effectId: 'mana_regen_1', stacks: 1, actualCost: 15 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'earthBoots',
|
id: 'earthBoots',
|
||||||
@@ -51,6 +58,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 2,
|
craftTime: 2,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+20 Earth Mana Capacity',
|
gearTrait: '+20 Earth Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'earth_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ─── Metal Gear (Fire+Earth — balanced offense/defense) ──────────────
|
// ─── Metal Gear (Fire+Earth — balanced offense/defense) ──────────────
|
||||||
@@ -66,6 +76,10 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 5,
|
craftTime: 5,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+15% Enchantment Power, +20 Metal Mana Capacity',
|
gearTrait: '+15% Enchantment Power, +20 Metal Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'damage_5', stacks: 1, actualCost: 15 },
|
||||||
|
{ effectId: 'metal_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'metalShield',
|
id: 'metalShield',
|
||||||
@@ -79,6 +93,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 5,
|
craftTime: 5,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+15% Enchantment Capacity',
|
gearTrait: '+15% Enchantment Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'metalGloves',
|
id: 'metalGloves',
|
||||||
@@ -92,6 +109,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 3,
|
craftTime: 3,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+10% Enchantment Power, +15 Metal Mana Capacity',
|
gearTrait: '+10% Enchantment Power, +15 Metal Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'metal_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ─── Crystal Gear (Sand+Sand+Light — high enchantment capacity) ──────
|
// ─── Crystal Gear (Sand+Sand+Light — high enchantment capacity) ──────
|
||||||
@@ -107,6 +127,10 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 6,
|
craftTime: 6,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+40% enchantment capacity',
|
gearTrait: '+40% enchantment capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
{ effectId: 'mana_regen_1', stacks: 1, actualCost: 15 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'crystalRing',
|
id: 'crystalRing',
|
||||||
@@ -120,6 +144,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 3,
|
craftTime: 3,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+15% enchantment capacity',
|
gearTrait: '+15% enchantment capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'crystalAmulet',
|
id: 'crystalAmulet',
|
||||||
@@ -133,6 +160,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 4,
|
craftTime: 4,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+10% all enchantment effects',
|
gearTrait: '+10% all enchantment effects',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'crystal_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ─── Sand Gear (Earth+Water — lightweight, agile) ────────────────────
|
// ─── Sand Gear (Earth+Water — lightweight, agile) ────────────────────
|
||||||
@@ -148,6 +178,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 2,
|
craftTime: 2,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+15% cast speed, +10% evasion',
|
gearTrait: '+15% cast speed, +10% evasion',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sandGloves',
|
id: 'sandGloves',
|
||||||
@@ -161,6 +194,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 2,
|
craftTime: 2,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+10% cast speed',
|
gearTrait: '+10% cast speed',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sandVest',
|
id: 'sandVest',
|
||||||
@@ -174,6 +210,10 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 4,
|
craftTime: 4,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+20% cast speed, +5% evasion',
|
gearTrait: '+20% cast speed, +5% evasion',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'attack_speed_10', stacks: 1, actualCost: 22 },
|
||||||
|
{ effectId: 'sand_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ─── Branch recipes ───────────────────────────────────────────────────
|
// ─── Branch recipes ───────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 3,
|
craftTime: 3,
|
||||||
rarity: 'uncommon',
|
rarity: 'uncommon',
|
||||||
gearTrait: '+15% Mana Regen, +20 Earth Mana Capacity',
|
gearTrait: '+15% Mana Regen, +20 Earth Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_regen_1', stacks: 1, actualCost: 15 },
|
||||||
|
{ effectId: 'earth_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'arcanistStaff',
|
id: 'arcanistStaff',
|
||||||
@@ -30,6 +34,10 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 8,
|
craftTime: 8,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+30% Enchantment Capacity, +20% Mana Regen',
|
gearTrait: '+30% Enchantment Capacity, +20% Mana Regen',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_100', stacks: 1, actualCost: 35 },
|
||||||
|
{ effectId: 'mana_regen_2', stacks: 1, actualCost: 28 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'battlestaff',
|
id: 'battlestaff',
|
||||||
@@ -43,6 +51,11 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 6,
|
craftTime: 6,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+10% Enchantment Power, +10% Cast Speed',
|
gearTrait: '+10% Enchantment Power, +10% Cast Speed',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'damage_5', stacks: 1, actualCost: 15 },
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
{ effectId: 'metal_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'arcanistCirclet',
|
id: 'arcanistCirclet',
|
||||||
@@ -56,6 +69,10 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 4,
|
craftTime: 4,
|
||||||
rarity: 'rare',
|
rarity: 'rare',
|
||||||
gearTrait: '+20% Enchantment Power, +15 Light Mana Capacity',
|
gearTrait: '+20% Enchantment Power, +15 Light Mana Capacity',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
{ effectId: 'light_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'arcanistRobe',
|
id: 'arcanistRobe',
|
||||||
@@ -69,6 +86,10 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 8,
|
craftTime: 8,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+35% Enchantment Capacity, +10% all Mana Regen',
|
gearTrait: '+35% Enchantment Capacity, +10% all Mana Regen',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_100', stacks: 1, actualCost: 35 },
|
||||||
|
{ effectId: 'mana_regen_5', stacks: 1, actualCost: 50 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'voidCatalyst',
|
id: 'voidCatalyst',
|
||||||
@@ -82,6 +103,11 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 7,
|
craftTime: 7,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+30% Dark Enchantment Power, +15% Spell Damage',
|
gearTrait: '+30% Dark Enchantment Power, +15% Spell Damage',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_100', stacks: 1, actualCost: 35 },
|
||||||
|
{ effectId: 'damage_10', stacks: 1, actualCost: 28 },
|
||||||
|
{ effectId: 'dark_cap_10', stacks: 1, actualCost: 30 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'arcanistPendant',
|
id: 'arcanistPendant',
|
||||||
@@ -95,5 +121,9 @@ export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
|||||||
craftTime: 5,
|
craftTime: 5,
|
||||||
rarity: 'epic',
|
rarity: 'epic',
|
||||||
gearTrait: '+15% all enchantment effects, +10% Mana Regen',
|
gearTrait: '+15% all enchantment effects, +10% Mana Regen',
|
||||||
|
bonusEnchantments: [
|
||||||
|
{ effectId: 'mana_cap_50', stacks: 1, actualCost: 20 },
|
||||||
|
{ effectId: 'mana_regen_2', stacks: 1, actualCost: 28 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// ─── Equipment Crafting Tick ─────────────────────────────────────────────────
|
||||||
|
// Handles advancing equipment crafting progress and completing crafted items.
|
||||||
|
// Extracted from craftingStore.ts to stay under the 400-line file limit.
|
||||||
|
|
||||||
|
import type { CraftingState } from './craftingStore.types';
|
||||||
|
import * as CraftingEquipment from '../crafting-equipment';
|
||||||
|
import { CRAFTING_RECIPES } from '../data/crafting-recipes';
|
||||||
|
import { FABRICATOR_RECIPES } from '../data/fabricator-recipes';
|
||||||
|
import { HOURS_PER_TICK } from '../constants';
|
||||||
|
import type { AppliedEnchantment } from '../types/equipment';
|
||||||
|
import { useCombatStore } from './combatStore';
|
||||||
|
|
||||||
|
export interface CraftingTickResult {
|
||||||
|
completed: boolean;
|
||||||
|
logMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processEquipmentCraftingTick(
|
||||||
|
state: CraftingState,
|
||||||
|
set: (partial: Partial<CraftingState>) => void,
|
||||||
|
): CraftingTickResult {
|
||||||
|
const progress = state.equipmentCraftingProgress;
|
||||||
|
if (!progress) return { completed: false };
|
||||||
|
|
||||||
|
const newProgress = progress.progress + HOURS_PER_TICK;
|
||||||
|
|
||||||
|
// Still in progress — just advance
|
||||||
|
if (newProgress < progress.required) {
|
||||||
|
set({ equipmentCraftingProgress: { ...progress, progress: newProgress } });
|
||||||
|
return { completed: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crafting complete — resolve the recipe and create instance
|
||||||
|
const isFabricator = progress.blueprintId.startsWith('fabricator-');
|
||||||
|
|
||||||
|
if (isFabricator) {
|
||||||
|
const recipeId = progress.blueprintId.replace('fabricator-', '');
|
||||||
|
const recipe = FABRICATOR_RECIPES.find(r => r.id === recipeId);
|
||||||
|
if (!recipe) {
|
||||||
|
set({ equipmentCraftingProgress: null });
|
||||||
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
|
return { completed: false, logMessage: '🔨 Crafting failed: recipe not found.' };
|
||||||
|
}
|
||||||
|
const bonusEnchantments: AppliedEnchantment[] = recipe.bonusEnchantments
|
||||||
|
? recipe.bonusEnchantments.map(e => ({ ...e }))
|
||||||
|
: [];
|
||||||
|
const result = CraftingEquipment.completeEquipmentCrafting(
|
||||||
|
progress.blueprintId,
|
||||||
|
recipe as any,
|
||||||
|
bonusEnchantments,
|
||||||
|
);
|
||||||
|
if (result.success) {
|
||||||
|
const { instanceId, instance, logMessage } = result.data;
|
||||||
|
set({
|
||||||
|
equipmentInstances: { ...state.equipmentInstances, [instanceId]: instance },
|
||||||
|
equipmentCraftingProgress: null,
|
||||||
|
});
|
||||||
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
|
return { completed: true, logMessage };
|
||||||
|
} else {
|
||||||
|
set({ equipmentCraftingProgress: null });
|
||||||
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
|
return { completed: false, logMessage: `🔨 Crafting failed: ${result.error}` };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Blueprint crafting
|
||||||
|
const blueprint = CRAFTING_RECIPES[progress.blueprintId];
|
||||||
|
if (!blueprint) {
|
||||||
|
set({ equipmentCraftingProgress: null });
|
||||||
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
|
return { completed: false, logMessage: '🔨 Crafting failed: blueprint not found.' };
|
||||||
|
}
|
||||||
|
const result = CraftingEquipment.completeEquipmentCrafting(
|
||||||
|
progress.blueprintId,
|
||||||
|
blueprint,
|
||||||
|
);
|
||||||
|
if (result.success) {
|
||||||
|
const { instanceId, instance, logMessage } = result.data;
|
||||||
|
set({
|
||||||
|
equipmentInstances: { ...state.equipmentInstances, [instanceId]: instance },
|
||||||
|
equipmentCraftingProgress: null,
|
||||||
|
});
|
||||||
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
|
return { completed: true, logMessage };
|
||||||
|
} else {
|
||||||
|
set({ equipmentCraftingProgress: null });
|
||||||
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
|
return { completed: false, logMessage: `🔨 Crafting failed: ${result.error}` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
makeFabricatorProgress,
|
makeFabricatorProgress,
|
||||||
} from '../crafting-fabricator';
|
} from '../crafting-fabricator';
|
||||||
import { craftMaterial as craftMaterialAction } from '../crafting-actions/crafting-material-actions';
|
import { craftMaterial as craftMaterialAction } from '../crafting-actions/crafting-material-actions';
|
||||||
|
import { processEquipmentCraftingTick } from './crafting-equipment-tick';
|
||||||
|
|
||||||
export const useCraftingStore = create<CraftingStore>()(
|
export const useCraftingStore = create<CraftingStore>()(
|
||||||
persist(
|
persist(
|
||||||
@@ -363,6 +364,11 @@ export const useCraftingStore = create<CraftingStore>()(
|
|||||||
return { unlockedEffects: Array.from(existing) };
|
return { unlockedEffects: Array.from(existing) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
processEquipmentCraftingTick: (): { completed: boolean; logMessage?: string } => {
|
||||||
|
const state = get();
|
||||||
|
return processEquipmentCraftingTick(state, set as unknown as (partial: Partial<CraftingState>) => void);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export interface CraftingActions {
|
|||||||
resetEnchantmentSelection: () => void;
|
resetEnchantmentSelection: () => void;
|
||||||
clearLastError: () => void;
|
clearLastError: () => void;
|
||||||
unlockEffects: (effectIds: string[]) => void;
|
unlockEffects: (effectIds: string[]) => void;
|
||||||
|
processEquipmentCraftingTick: () => { completed: boolean; logMessage?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CraftingStore = CraftingState & CraftingActions;
|
export type CraftingStore = CraftingState & CraftingActions;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// ─── Game Store (Coordinator) ─────────────────────────────────────────────────
|
// ─── Game Store (Coordinator) ──────────────────────────────────────
|
||||||
// Manages: day, hour, incursionStrength, containmentWards
|
// Manages: day, hour, incursionStrength, containmentWards
|
||||||
// Orchestrates tick across all stores via a read → compute → write pipeline.
|
// Orchestrates tick across all stores via read → compute → write pipeline.
|
||||||
|
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
@@ -11,13 +11,7 @@ import { computeEquipmentEffects } from '../effects';
|
|||||||
import type { ComputedEffects } from '../effects/upgrade-effects.types';
|
import type { ComputedEffects } from '../effects/upgrade-effects.types';
|
||||||
|
|
||||||
import { computeDisciplineEffects } from '../effects/discipline-effects';
|
import { computeDisciplineEffects } from '../effects/discipline-effects';
|
||||||
import {
|
import { computeMaxMana, computeRegen, getMeditationBonus, getIncursionStrength, calcInsight } from '../utils';
|
||||||
computeMaxMana,
|
|
||||||
computeRegen,
|
|
||||||
getMeditationBonus,
|
|
||||||
getIncursionStrength,
|
|
||||||
calcInsight
|
|
||||||
} from '../utils';
|
|
||||||
import { useUIStore } from './uiStore';
|
import { useUIStore } from './uiStore';
|
||||||
import { usePrestigeStore } from './prestigeStore';
|
import { usePrestigeStore } from './prestigeStore';
|
||||||
import { useManaStore } from './manaStore';
|
import { useManaStore } from './manaStore';
|
||||||
@@ -161,7 +155,6 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incursion
|
|
||||||
const incursionStrength = getIncursionStrength(day, hour);
|
const incursionStrength = getIncursionStrength(day, hour);
|
||||||
|
|
||||||
// Meditation bonus tracking
|
// Meditation bonus tracking
|
||||||
@@ -208,7 +201,7 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
|||||||
});
|
});
|
||||||
let totalManaGathered = ctx.mana.totalManaGathered;
|
let totalManaGathered = ctx.mana.totalManaGathered;
|
||||||
|
|
||||||
// Convert action — delegate to manaStore
|
// Convert action
|
||||||
if (ctx.combat.currentAction === 'convert') {
|
if (ctx.combat.currentAction === 'convert') {
|
||||||
const convertResult = useManaStore.getState().processConvertAction(rawMana);
|
const convertResult = useManaStore.getState().processConvertAction(rawMana);
|
||||||
if (convertResult) {
|
if (convertResult) {
|
||||||
@@ -217,7 +210,7 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pact ritual progress
|
// Pact ritual
|
||||||
if (ctx.prestige.pactRitualFloor !== null) {
|
if (ctx.prestige.pactRitualFloor !== null) {
|
||||||
const guardian = getGuardianForFloor(ctx.prestige.pactRitualFloor);
|
const guardian = getGuardianForFloor(ctx.prestige.pactRitualFloor);
|
||||||
if (guardian) {
|
if (guardian) {
|
||||||
@@ -253,7 +246,7 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discipline tick — process active disciplines (XP accrual + mana drain)
|
// Discipline tick
|
||||||
const disciplineResult = useDisciplineStore.getState().processTick({
|
const disciplineResult = useDisciplineStore.getState().processTick({
|
||||||
rawMana,
|
rawMana,
|
||||||
elements,
|
elements,
|
||||||
@@ -355,6 +348,14 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equipment crafting tick — advance progress and complete when done
|
||||||
|
if (ctx.combat.currentAction === 'craft') {
|
||||||
|
const craftingResult = useCraftingStore.getState().processEquipmentCraftingTick();
|
||||||
|
if (craftingResult.logMessage) {
|
||||||
|
addLog(craftingResult.logMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Phase 3: Write — batch all state updates ─────────────────────────
|
// ── Phase 3: Write — batch all state updates ─────────────────────────
|
||||||
writes.game = { day, hour, incursionStrength };
|
writes.game = { day, hour, incursionStrength };
|
||||||
writes.mana = {
|
writes.mana = {
|
||||||
|
|||||||
Reference in New Issue
Block a user