309 lines
7.9 KiB
TypeScript
309 lines
7.9 KiB
TypeScript
// Import the InventoryItem interface
|
|
import type { InventoryItem } from "@/data/enemies";
|
|
import { Quest } from "@/interface/quest.interface";
|
|
|
|
// Define the user data structure
|
|
export interface UserStats {
|
|
name: string; // Add this line
|
|
level: number;
|
|
exp: number;
|
|
expToNextLevel: number;
|
|
job: string | null;
|
|
title: string | null;
|
|
hp: number;
|
|
maxHp: number;
|
|
mp: number;
|
|
maxMp: number;
|
|
fatigue: number;
|
|
gold: number; // Add gold currency
|
|
stats: {
|
|
str: number;
|
|
agi: number;
|
|
per: number;
|
|
int: number;
|
|
vit: number;
|
|
};
|
|
statPoints: number;
|
|
equipment: Equipment[];
|
|
quests: Quest[];
|
|
completedQuests: string[];
|
|
inventory: InventoryItem[]; // Add inventory array
|
|
}
|
|
|
|
export interface Equipment {
|
|
id: string;
|
|
name: string;
|
|
rarity: "Common" | "Uncommon" | "Rare" | "Epic" | "Legendary";
|
|
stats: string[];
|
|
setBonus: string;
|
|
slot: string;
|
|
equipped: boolean;
|
|
}
|
|
|
|
// Add some sample quests to the initial user stats
|
|
export const initialUserStats: UserStats = {
|
|
name: "", // Add this line with empty string as default
|
|
level: 1,
|
|
exp: 0,
|
|
expToNextLevel: 100, // Level 1 needs 100 XP to reach level 2
|
|
job: null,
|
|
title: null,
|
|
hp: 100,
|
|
maxHp: 100,
|
|
mp: 10,
|
|
maxMp: 10,
|
|
fatigue: 0,
|
|
gold: 0, // Start with 0 gold
|
|
stats: {
|
|
str: 10,
|
|
agi: 10,
|
|
per: 10,
|
|
int: 10,
|
|
vit: 10,
|
|
},
|
|
statPoints: 0,
|
|
equipment: [],
|
|
quests: [], // Removed initial quests
|
|
completedQuests: [],
|
|
inventory: [
|
|
{
|
|
id: "item-health-potion",
|
|
name: "Health Potion",
|
|
type: "Consumable",
|
|
rarity: "Common",
|
|
description: "Restores 100 HP when consumed.",
|
|
quantity: 3,
|
|
},
|
|
{
|
|
id: "item-mana-potion",
|
|
name: "Mana Potion",
|
|
type: "Consumable",
|
|
rarity: "Common",
|
|
description: "Restores 50 MP when consumed.",
|
|
quantity: 2,
|
|
},
|
|
],
|
|
};
|
|
|
|
// Save user data to localStorage
|
|
export const saveUserStats = (stats: UserStats): void => {
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem("soloLevelUpUserStats", JSON.stringify(stats));
|
|
}
|
|
};
|
|
|
|
// Load user data from localStorage
|
|
export const loadUserStats = (): UserStats => {
|
|
if (typeof window !== "undefined") {
|
|
const savedStats = localStorage.getItem("soloLevelUpUserStats");
|
|
if (savedStats) {
|
|
return JSON.parse(savedStats);
|
|
}
|
|
}
|
|
return initialUserStats;
|
|
};
|
|
|
|
// Calculate XP needed for next level
|
|
export const calculateExpToNextLevel = (level: number): number => {
|
|
return Math.floor(100 * Math.pow(1.1, level - 1));
|
|
};
|
|
|
|
// Update the levelUp function to automatically add 1 point to all stats
|
|
export const levelUp = (stats: UserStats): UserStats => {
|
|
const newLevel = stats.level + 1;
|
|
|
|
// Automatically increase all stats by 1 point
|
|
const newStats = {
|
|
str: stats.stats.str + 1,
|
|
agi: stats.stats.agi + 1,
|
|
per: stats.stats.per + 1,
|
|
int: stats.stats.int + 1,
|
|
vit: stats.stats.vit + 1,
|
|
};
|
|
|
|
// HP and MP increases based on level and vitality/intelligence
|
|
const newMaxHp = Math.floor(100 + newLevel * 10 + newStats.vit * 5);
|
|
const newMaxMp = Math.floor(10 + newLevel * 2 + newStats.int * 2);
|
|
|
|
return {
|
|
...stats,
|
|
level: newLevel,
|
|
exp: stats.exp - stats.expToNextLevel,
|
|
expToNextLevel: calculateExpToNextLevel(newLevel),
|
|
maxHp: newMaxHp,
|
|
hp: newMaxHp, // Fully heal on level up
|
|
maxMp: newMaxMp,
|
|
mp: newMaxMp, // Fully restore MP on level up
|
|
stats: newStats,
|
|
// No longer adding stat points since we automatically increase all stats
|
|
};
|
|
};
|
|
|
|
// Add experience points
|
|
export const addExperience = (stats: UserStats, exp: number): UserStats => {
|
|
let updatedStats = { ...stats, exp: stats.exp + exp };
|
|
|
|
// Check if leveled up
|
|
while (updatedStats.exp >= updatedStats.expToNextLevel) {
|
|
updatedStats = levelUp(updatedStats);
|
|
}
|
|
|
|
return updatedStats;
|
|
};
|
|
|
|
// Allocate stat point
|
|
export const allocateStat = (
|
|
stats: UserStats,
|
|
stat: keyof UserStats["stats"]
|
|
): UserStats => {
|
|
if (stats.statPoints <= 0) return stats;
|
|
|
|
const newStats = { ...stats };
|
|
newStats.stats[stat]++;
|
|
newStats.statPoints--;
|
|
|
|
// Update derived stats
|
|
if (stat === "vit") {
|
|
newStats.maxHp = Math.floor(
|
|
100 + newStats.level * 10 + newStats.stats.vit * 5
|
|
);
|
|
}
|
|
if (stat === "int") {
|
|
newStats.maxMp = Math.floor(
|
|
10 + newStats.level * 2 + newStats.stats.int * 2
|
|
);
|
|
}
|
|
|
|
return newStats;
|
|
};
|
|
|
|
// Remove stat point (for UI)
|
|
export const deallocateStat = (
|
|
stats: UserStats,
|
|
stat: keyof UserStats["stats"]
|
|
): UserStats => {
|
|
if (stats.stats[stat] <= 10) return stats; // Can't go below base value
|
|
|
|
const newStats = { ...stats };
|
|
newStats.stats[stat]--;
|
|
newStats.statPoints++;
|
|
|
|
// Update derived stats
|
|
if (stat === "vit") {
|
|
newStats.maxHp = Math.floor(
|
|
100 + newStats.level * 10 + newStats.stats.vit * 5
|
|
);
|
|
}
|
|
if (stat === "int") {
|
|
newStats.maxMp = Math.floor(
|
|
10 + newStats.level * 2 + newStats.stats.int * 2
|
|
);
|
|
}
|
|
|
|
return newStats;
|
|
};
|
|
|
|
// Add item to inventory
|
|
export const addItemToInventory = (
|
|
stats: UserStats,
|
|
item: InventoryItem
|
|
): UserStats => {
|
|
const newStats = { ...stats };
|
|
const existingItem = newStats.inventory.find((i) => i.id === item.id);
|
|
|
|
if (existingItem && existingItem.quantity) {
|
|
// If item exists and has quantity, increment quantity
|
|
existingItem.quantity += item.quantity || 1;
|
|
} else {
|
|
// Otherwise add as new item
|
|
newStats.inventory.push({
|
|
...item,
|
|
quantity: item.quantity || 1,
|
|
});
|
|
}
|
|
|
|
return newStats;
|
|
};
|
|
|
|
// Remove item from inventory
|
|
export const removeItemFromInventory = (
|
|
stats: UserStats,
|
|
itemId: string,
|
|
quantity = 1
|
|
): UserStats => {
|
|
const newStats = { ...stats };
|
|
const itemIndex = newStats.inventory.findIndex((i) => i.id === itemId);
|
|
|
|
if (itemIndex === -1) return stats;
|
|
|
|
const item = newStats.inventory[itemIndex];
|
|
|
|
if (item.quantity && item.quantity > quantity) {
|
|
// Reduce quantity if there are more than requested
|
|
item.quantity -= quantity;
|
|
} else {
|
|
// Remove item completely
|
|
newStats.inventory.splice(itemIndex, 1);
|
|
}
|
|
|
|
return newStats;
|
|
};
|
|
|
|
// Use consumable item
|
|
export const useConsumableItem = (
|
|
stats: UserStats,
|
|
itemId: string
|
|
): UserStats => {
|
|
const newStats = { ...stats };
|
|
const item = newStats.inventory.find((i) => i.id === itemId);
|
|
|
|
if (!item || item.type !== "Consumable") return stats;
|
|
|
|
// First try specific item IDs for built-in items
|
|
switch (item.id) {
|
|
case "item-health-potion":
|
|
newStats.hp = Math.min(newStats.maxHp, newStats.hp + 100);
|
|
break;
|
|
case "item-mana-potion":
|
|
newStats.mp = Math.min(newStats.maxMp, newStats.mp + 50);
|
|
break;
|
|
case "item-focus-potion":
|
|
// This would need a temporary buff system to be implemented
|
|
break;
|
|
default:
|
|
// For custom/generated items, check name patterns instead of relying on specific IDs
|
|
if (
|
|
item.name.toLowerCase().includes("health") ||
|
|
item.name.toLowerCase().includes("healing") ||
|
|
item.name.toLowerCase().includes("hp")
|
|
) {
|
|
// Default healing amount (can be customized based on rarity)
|
|
let healAmount = 50;
|
|
if (item.rarity === "Uncommon") healAmount = 100;
|
|
if (item.rarity === "Rare") healAmount = 200;
|
|
if (item.rarity === "Epic") healAmount = 350;
|
|
if (item.rarity === "Legendary") healAmount = 500;
|
|
|
|
newStats.hp = Math.min(newStats.maxHp, newStats.hp + healAmount);
|
|
} else if (
|
|
item.name.toLowerCase().includes("mana") ||
|
|
item.name.toLowerCase().includes("mp")
|
|
) {
|
|
// Default mana restoration amount
|
|
let manaAmount = 25;
|
|
if (item.rarity === "Uncommon") manaAmount = 50;
|
|
if (item.rarity === "Rare") manaAmount = 100;
|
|
if (item.rarity === "Epic") manaAmount = 175;
|
|
if (item.rarity === "Legendary") manaAmount = 250;
|
|
|
|
newStats.mp = Math.min(newStats.maxMp, newStats.mp + manaAmount);
|
|
} else {
|
|
// Unknown consumable effect
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
// Remove one of the item
|
|
return removeItemFromInventory(newStats, itemId, 1);
|
|
};
|