278 lines
8.6 KiB
TypeScript
278 lines
8.6 KiB
TypeScript
// ─── Crafting Loot Inventory System ────────────────────────────────────────────
|
|
// Loot inventory functions
|
|
|
|
import type { LootInventory } from './types';
|
|
import type { CraftingRecipe } from './data/crafting-recipes';
|
|
|
|
// ─── Inventory Queries ──────────────────────────────────────────────────────
|
|
|
|
// Get material count
|
|
export function getMaterialCount(inventory: LootInventory, materialId: string): number {
|
|
return inventory.materials[materialId] || 0;
|
|
}
|
|
|
|
// Check if has blueprint
|
|
export function hasBlueprint(inventory: LootInventory, blueprintId: string): boolean {
|
|
return inventory.blueprints.includes(blueprintId);
|
|
}
|
|
|
|
// Check if has any materials
|
|
export function hasMaterials(inventory: LootInventory): boolean {
|
|
return Object.keys(inventory.materials).length > 0;
|
|
}
|
|
|
|
// Get total number of unique materials
|
|
export function getUniqueMaterialCount(inventory: LootInventory): number {
|
|
return Object.keys(inventory.materials).length;
|
|
}
|
|
|
|
// Get total material stacks (sum of all quantities)
|
|
export function getTotalMaterialStacks(inventory: LootInventory): number {
|
|
return Object.values(inventory.materials || {}).reduce((sum, qty) => sum + qty, 0);
|
|
}
|
|
|
|
// ─── Inventory Modifications ────────────────────────────────────────────────
|
|
|
|
// Add materials to inventory
|
|
export function addMaterial(inventory: LootInventory, materialId: string, amount: number): LootInventory {
|
|
const currentAmount = inventory.materials[materialId] || 0;
|
|
return {
|
|
...inventory,
|
|
materials: {
|
|
...inventory.materials,
|
|
[materialId]: currentAmount + amount,
|
|
},
|
|
};
|
|
}
|
|
|
|
// Add multiple materials at once
|
|
export function addMaterials(
|
|
inventory: LootInventory,
|
|
materials: Record<string, number>
|
|
): LootInventory {
|
|
const newMaterials = { ...inventory.materials };
|
|
for (const [matId, amount] of Object.entries(materials)) {
|
|
newMaterials[matId] = (newMaterials[matId] || 0) + amount;
|
|
}
|
|
return {
|
|
...inventory,
|
|
materials: newMaterials,
|
|
};
|
|
}
|
|
|
|
// Remove materials from inventory
|
|
export function removeMaterial(inventory: LootInventory, materialId: string, amount: number): {
|
|
inventory: LootInventory;
|
|
removed: number;
|
|
} {
|
|
const currentAmount = inventory.materials[materialId] || 0;
|
|
const removed = Math.min(amount, currentAmount);
|
|
const remaining = Math.max(0, currentAmount - amount);
|
|
|
|
const newMaterials = { ...inventory.materials };
|
|
if (remaining <= 0) {
|
|
delete newMaterials[materialId];
|
|
} else {
|
|
newMaterials[materialId] = remaining;
|
|
}
|
|
|
|
return {
|
|
inventory: {
|
|
...inventory,
|
|
materials: newMaterials,
|
|
},
|
|
removed,
|
|
};
|
|
}
|
|
|
|
// Delete materials completely (admin/debug operation)
|
|
export function deleteMaterial(inventory: LootInventory, materialId: string): LootInventory {
|
|
const newMaterials = { ...inventory.materials };
|
|
delete newMaterials[materialId];
|
|
return {
|
|
...inventory,
|
|
materials: newMaterials,
|
|
};
|
|
}
|
|
|
|
// Clear all materials
|
|
export function clearMaterials(inventory: LootInventory): LootInventory {
|
|
return {
|
|
...inventory,
|
|
materials: {},
|
|
};
|
|
}
|
|
|
|
// ─── Blueprint Management ──────────────────────────────────────────────────
|
|
|
|
// Add a blueprint
|
|
export function addBlueprint(inventory: LootInventory, blueprintId: string): LootInventory {
|
|
if (inventory.blueprints.includes(blueprintId)) {
|
|
return inventory;
|
|
}
|
|
return {
|
|
...inventory,
|
|
blueprints: [...inventory.blueprints, blueprintId],
|
|
};
|
|
}
|
|
|
|
// Remove a blueprint
|
|
export function removeBlueprint(inventory: LootInventory, blueprintId: string): LootInventory {
|
|
return {
|
|
...inventory,
|
|
blueprints: inventory.blueprints.filter(id => id !== blueprintId),
|
|
};
|
|
}
|
|
|
|
// Check and remove consumed blueprint (if single-use)
|
|
export function consumeBlueprint(inventory: LootInventory, _blueprintId: string): LootInventory {
|
|
// Blueprints are persistent for now, but this function provides future flexibility
|
|
return inventory;
|
|
}
|
|
|
|
// ─── Recipe Material Checks ─────────────────────────────────────────────────
|
|
|
|
// Check if specific materials are available
|
|
export function checkMaterialsAvailable(
|
|
inventory: LootInventory,
|
|
required: Record<string, number>
|
|
): { hasAll: boolean; missing: Record<string, number> } {
|
|
const missing: Record<string, number> = {};
|
|
let hasAll = true;
|
|
|
|
for (const [matId, amount] of Object.entries(required)) {
|
|
const available = inventory.materials[matId] || 0;
|
|
if (available < amount) {
|
|
missing[matId] = amount - available;
|
|
hasAll = false;
|
|
}
|
|
}
|
|
|
|
return { hasAll, missing };
|
|
}
|
|
|
|
// Deduct recipe materials
|
|
export function deductRecipeMaterials(
|
|
inventory: LootInventory,
|
|
recipeMaterials: Record<string, number>
|
|
): LootInventory {
|
|
const newMaterials = { ...inventory.materials };
|
|
for (const [matId, amount] of Object.entries(recipeMaterials)) {
|
|
const newAmount = (newMaterials[matId] || 0) - amount;
|
|
if (newAmount <= 0) {
|
|
delete newMaterials[matId];
|
|
} else {
|
|
newMaterials[matId] = newAmount;
|
|
}
|
|
}
|
|
return {
|
|
...inventory,
|
|
materials: newMaterials,
|
|
};
|
|
}
|
|
|
|
// ─── Inventory Filters & Sorting ────────────────────────────────────────────
|
|
|
|
export interface MaterialWithInfo {
|
|
id: string;
|
|
quantity: number;
|
|
name: string;
|
|
rarity: string;
|
|
}
|
|
|
|
// Get all materials with their info
|
|
export function getAllMaterials(inventory: LootInventory): MaterialWithInfo[] {
|
|
return Object.entries(inventory.materials).map(([id, quantity]) => ({
|
|
id,
|
|
quantity,
|
|
name: id, // Could be replaced with lookup from LOOT_DROPS
|
|
rarity: 'common', // Could be replaced with lookup
|
|
}));
|
|
}
|
|
|
|
// Filter materials by minimum quantity
|
|
export function filterMaterialsByQuantity(
|
|
inventory: LootInventory,
|
|
minQuantity: number
|
|
): Record<string, number> {
|
|
const filtered: Record<string, number> = {};
|
|
for (const [id, qty] of Object.entries(inventory.materials)) {
|
|
if (qty >= minQuantity) {
|
|
filtered[id] = qty;
|
|
}
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
// ─── Crafting Availability ─────────────────────────────────────────────────
|
|
|
|
// Check if can craft a recipe
|
|
export function canCraftFromInventory(
|
|
inventory: LootInventory,
|
|
recipe: CraftingRecipe,
|
|
currentMana: number
|
|
): { canCraft: boolean; reason?: string; missingMaterials?: Record<string, number>; missingMana?: number } {
|
|
const { hasAll, missing } = checkMaterialsAvailable(inventory, recipe.materials);
|
|
|
|
if (!hasAll) {
|
|
return { canCraft: false, missingMaterials: missing };
|
|
}
|
|
|
|
if (currentMana < recipe.manaCost) {
|
|
return {
|
|
canCraft: false,
|
|
missingMana: recipe.manaCost - currentMana,
|
|
reason: 'Insufficient mana',
|
|
};
|
|
}
|
|
|
|
if (!inventory.blueprints.includes(recipe.id)) {
|
|
return { canCraft: false, reason: 'Blueprint not acquired' };
|
|
}
|
|
|
|
return { canCraft: true };
|
|
}
|
|
|
|
// Get all craftable recipes from inventory
|
|
export function getCraftableRecipes(
|
|
inventory: LootInventory,
|
|
recipes: Record<string, CraftingRecipe>,
|
|
currentMana: number
|
|
): CraftingRecipe[] {
|
|
const craftable: CraftingRecipe[] = [];
|
|
|
|
for (const blueprintId of inventory.blueprints) {
|
|
const recipe = recipes[blueprintId];
|
|
if (!recipe) continue;
|
|
|
|
const { canCraft } = canCraftFromInventory(inventory, recipe, currentMana);
|
|
if (canCraft) {
|
|
craftable.push(recipe);
|
|
}
|
|
}
|
|
|
|
return craftable;
|
|
}
|
|
|
|
// ─── Inventory Statistics ──────────────────────────────────────────────────
|
|
|
|
export interface InventoryStats {
|
|
totalUniqueMaterials: number;
|
|
totalMaterialStacks: number;
|
|
totalBlueprints: number;
|
|
craftableItems: string[];
|
|
}
|
|
|
|
export function getInventoryStats(inventory: LootInventory): InventoryStats {
|
|
const totalUniqueMaterials = Object.keys(inventory.materials).length;
|
|
const totalMaterialStacks = Object.values(inventory.materials || {}).reduce((sum, qty) => sum + qty, 0);
|
|
const totalBlueprints = inventory.blueprints.length;
|
|
|
|
return {
|
|
totalUniqueMaterials,
|
|
totalMaterialStacks,
|
|
totalBlueprints,
|
|
craftableItems: [], // Would need recipes to compute
|
|
};
|
|
}
|