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 }