2025-08-25 16:35:55 -05:00

360 lines
9.6 KiB
Go

package actions
import (
"fmt"
"dk/internal/models/items"
"dk/internal/models/users"
)
// UserEquipItem calculates equipment updates for a user equipping an item.
// Returns map of database field updates without modifying the user struct.
func UserEquipItem(user *users.User, item *items.Item) (map[string]any, error) {
if !item.IsEquippable() && !item.IsSlottable() {
return nil, fmt.Errorf("item %s is not equippable", item.Name)
}
updates := make(map[string]any)
// Calculate stat changes by removing old item and adding new item
newStats := calculateNewStats(user, item)
// Update equipment slots and stats based on item type
switch item.Type {
case items.TypeWeapon:
updates["weapon_id"] = item.ID
updates["weapon_name"] = item.Name
case items.TypeArmor:
updates["armor_id"] = item.ID
updates["armor_name"] = item.Name
case items.TypeShield:
updates["shield_id"] = item.ID
updates["shield_name"] = item.Name
case items.TypeAccessory:
// Find first available slot or replace slot 1
if user.Slot1ID == 0 {
updates["slot_1_id"] = item.ID
updates["slot_1_name"] = item.Name
} else if user.Slot2ID == 0 {
updates["slot_2_id"] = item.ID
updates["slot_2_name"] = item.Name
} else if user.Slot3ID == 0 {
updates["slot_3_id"] = item.ID
updates["slot_3_name"] = item.Name
} else {
// All slots full, replace slot 1
updates["slot_1_id"] = item.ID
updates["slot_1_name"] = item.Name
}
}
// Apply stat changes
updates["attack"] = newStats.Attack
updates["defense"] = newStats.Defense
updates["strength"] = newStats.Strength
updates["dexterity"] = newStats.Dexterity
updates["max_hp"] = newStats.MaxHP
updates["max_mp"] = newStats.MaxMP
updates["exp_bonus"] = newStats.ExpBonus
updates["gold_bonus"] = newStats.GoldBonus
return updates, nil
}
// UserEquipAccessoryToSlot equips an accessory to a specific slot (1-3)
func UserEquipAccessoryToSlot(user *users.User, item *items.Item, slot int) (map[string]any, error) {
if !item.IsSlottable() {
return nil, fmt.Errorf("item %s is not slottable", item.Name)
}
if slot < 1 || slot > 3 {
return nil, fmt.Errorf("invalid slot number: %d", slot)
}
updates := make(map[string]any)
newStats := calculateNewStatsForSlot(user, item, slot)
// Update the specific slot
switch slot {
case 1:
updates["slot_1_id"] = item.ID
updates["slot_1_name"] = item.Name
case 2:
updates["slot_2_id"] = item.ID
updates["slot_2_name"] = item.Name
case 3:
updates["slot_3_id"] = item.ID
updates["slot_3_name"] = item.Name
}
// Apply stat changes
updates["attack"] = newStats.Attack
updates["defense"] = newStats.Defense
updates["strength"] = newStats.Strength
updates["dexterity"] = newStats.Dexterity
updates["max_hp"] = newStats.MaxHP
updates["max_mp"] = newStats.MaxMP
updates["exp_bonus"] = newStats.ExpBonus
updates["gold_bonus"] = newStats.GoldBonus
return updates, nil
}
// UserUnequipItem removes an equipped item and recalculates stats
func UserUnequipItem(user *users.User, itemType int) (map[string]any, error) {
updates := make(map[string]any)
switch itemType {
case items.TypeWeapon:
if user.WeaponID == 0 {
return nil, fmt.Errorf("no weapon equipped")
}
updates["weapon_id"] = 0
updates["weapon_name"] = ""
case items.TypeArmor:
if user.ArmorID == 0 {
return nil, fmt.Errorf("no armor equipped")
}
updates["armor_id"] = 0
updates["armor_name"] = ""
case items.TypeShield:
if user.ShieldID == 0 {
return nil, fmt.Errorf("no shield equipped")
}
updates["shield_id"] = 0
updates["shield_name"] = ""
default:
return nil, fmt.Errorf("invalid item type for unequip: %d", itemType)
}
// Recalculate all stats after unequipping
newStats := recalculateAllStats(user, itemType, 0)
updates["attack"] = newStats.Attack
updates["defense"] = newStats.Defense
updates["strength"] = newStats.Strength
updates["dexterity"] = newStats.Dexterity
updates["max_hp"] = newStats.MaxHP
updates["max_mp"] = newStats.MaxMP
updates["exp_bonus"] = newStats.ExpBonus
updates["gold_bonus"] = newStats.GoldBonus
return updates, nil
}
// UserUnequipAccessory removes an accessory from a specific slot
func UserUnequipAccessory(user *users.User, slot int) (map[string]any, error) {
if slot < 1 || slot > 3 {
return nil, fmt.Errorf("invalid slot number: %d", slot)
}
updates := make(map[string]any)
// Check if slot has an item
switch slot {
case 1:
if user.Slot1ID == 0 {
return nil, fmt.Errorf("slot 1 is empty")
}
updates["slot_1_id"] = 0
updates["slot_1_name"] = ""
case 2:
if user.Slot2ID == 0 {
return nil, fmt.Errorf("slot 2 is empty")
}
updates["slot_2_id"] = 0
updates["slot_2_name"] = ""
case 3:
if user.Slot3ID == 0 {
return nil, fmt.Errorf("slot 3 is empty")
}
updates["slot_3_id"] = 0
updates["slot_3_name"] = ""
}
// Recalculate stats after removing accessory
newStats := recalculateAllStats(user, items.TypeAccessory, slot)
updates["attack"] = newStats.Attack
updates["defense"] = newStats.Defense
updates["strength"] = newStats.Strength
updates["dexterity"] = newStats.Dexterity
updates["max_hp"] = newStats.MaxHP
updates["max_mp"] = newStats.MaxMP
updates["exp_bonus"] = newStats.ExpBonus
updates["gold_bonus"] = newStats.GoldBonus
return updates, nil
}
// Stats represents calculated user stats
type Stats struct {
Attack int
Defense int
Strength int
Dexterity int
MaxHP int
MaxMP int
ExpBonus int
GoldBonus int
}
// calculateNewStats calculates new stats when equipping an item
func calculateNewStats(user *users.User, newItem *items.Item) Stats {
stats := Stats{
Attack: user.Attack,
Defense: user.Defense,
Strength: user.Strength,
Dexterity: user.Dexterity,
MaxHP: user.MaxHP,
MaxMP: user.MaxMP,
ExpBonus: user.ExpBonus,
GoldBonus: user.GoldBonus,
}
// Remove old item stats if slot occupied
switch newItem.Type {
case items.TypeWeapon:
if user.WeaponID != 0 {
if oldItem, err := items.Find(user.WeaponID); err == nil {
removeItemStats(&stats, oldItem)
}
}
case items.TypeArmor:
if user.ArmorID != 0 {
if oldItem, err := items.Find(user.ArmorID); err == nil {
removeItemStats(&stats, oldItem)
}
}
case items.TypeShield:
if user.ShieldID != 0 {
if oldItem, err := items.Find(user.ShieldID); err == nil {
removeItemStats(&stats, oldItem)
}
}
}
// Add new item stats
addItemStats(&stats, newItem)
return stats
}
// calculateNewStatsForSlot calculates stats when equipping to a specific accessory slot
func calculateNewStatsForSlot(user *users.User, newItem *items.Item, slot int) Stats {
stats := Stats{
Attack: user.Attack,
Defense: user.Defense,
Strength: user.Strength,
Dexterity: user.Dexterity,
MaxHP: user.MaxHP,
MaxMP: user.MaxMP,
ExpBonus: user.ExpBonus,
GoldBonus: user.GoldBonus,
}
// Remove old accessory stats from the specified slot
var oldItemID int
switch slot {
case 1:
oldItemID = user.Slot1ID
case 2:
oldItemID = user.Slot2ID
case 3:
oldItemID = user.Slot3ID
}
if oldItemID != 0 {
if oldItem, err := items.Find(oldItemID); err == nil {
removeItemStats(&stats, oldItem)
}
}
// Add new item stats
addItemStats(&stats, newItem)
return stats
}
// recalculateAllStats recalculates all stats from scratch, excluding specified item
func recalculateAllStats(user *users.User, excludeType, excludeSlot int) Stats {
// Start with base stats (these would come from class/level progression)
// For now, using current stats minus all equipment bonuses
stats := Stats{
Attack: 0, // Base attack from strength/level
Defense: 0, // Base defense from dexterity/level
Strength: user.Strength,
Dexterity: user.Dexterity,
MaxHP: user.MaxHP,
MaxMP: user.MaxMP,
ExpBonus: 0, // Base exp bonus
GoldBonus: 0, // Base gold bonus
}
// Add weapon stats (unless being removed)
if user.WeaponID != 0 && excludeType != items.TypeWeapon {
if item, err := items.Find(user.WeaponID); err == nil {
addItemStats(&stats, item)
}
}
// Add armor stats (unless being removed)
if user.ArmorID != 0 && excludeType != items.TypeArmor {
if item, err := items.Find(user.ArmorID); err == nil {
addItemStats(&stats, item)
}
}
// Add shield stats (unless being removed)
if user.ShieldID != 0 && excludeType != items.TypeShield {
if item, err := items.Find(user.ShieldID); err == nil {
addItemStats(&stats, item)
}
}
// Add accessory stats (unless specific slot being removed)
if user.Slot1ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 1) {
if item, err := items.Find(user.Slot1ID); err == nil {
addItemStats(&stats, item)
}
}
if user.Slot2ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 2) {
if item, err := items.Find(user.Slot2ID); err == nil {
addItemStats(&stats, item)
}
}
if user.Slot3ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 3) {
if item, err := items.Find(user.Slot3ID); err == nil {
addItemStats(&stats, item)
}
}
return stats
}
// addItemStats adds an item's stats to the current stats
func addItemStats(stats *Stats, item *items.Item) {
stats.Attack += item.Attack
stats.Defense += item.Defense
stats.Strength += item.Strength
stats.Dexterity += item.Dexterity
stats.MaxHP += item.MaxHP
stats.MaxMP += item.MaxMP
stats.ExpBonus += item.ExpBonus
stats.GoldBonus += item.GoldBonus
}
// removeItemStats removes an item's stats from the current stats
func removeItemStats(stats *Stats, item *items.Item) {
stats.Attack -= item.Attack
stats.Defense -= item.Defense
stats.Strength -= item.Strength
stats.Dexterity -= item.Dexterity
stats.MaxHP -= item.MaxHP
stats.MaxMP -= item.MaxMP
stats.ExpBonus -= item.ExpBonus
stats.GoldBonus -= item.GoldBonus
}