package trade import ( "fmt" "strings" ) // CalculateCoins converts a total copper amount to coin denominations // Converted from C++ Trade::CalculateCoins func CalculateCoins(totalCopper int64) CoinAmounts { coins := CoinAmounts{} remaining := totalCopper // Calculate platinum (1,000,000 copper = 1 platinum) if remaining >= CoinsPlatinumThreshold { coins.Platinum = int32(remaining / CoinsPlatinumThreshold) remaining -= int64(coins.Platinum) * CoinsPlatinumThreshold } // Calculate gold (10,000 copper = 1 gold) if remaining >= CoinsGoldThreshold { coins.Gold = int32(remaining / CoinsGoldThreshold) remaining -= int64(coins.Gold) * CoinsGoldThreshold } // Calculate silver (100 copper = 1 silver) if remaining >= CoinsSilverThreshold { coins.Silver = int32(remaining / CoinsSilverThreshold) remaining -= int64(coins.Silver) * CoinsSilverThreshold } // Remaining is copper if remaining > 0 { coins.Copper = int32(remaining) } return coins } // CoinsToCopper converts coin denominations to total copper func CoinsToCopper(coins CoinAmounts) int64 { total := int64(coins.Copper) total += int64(coins.Silver) * CoinsSilverThreshold total += int64(coins.Gold) * CoinsGoldThreshold total += int64(coins.Platinum) * CoinsPlatinumThreshold return total } // FormatCoins returns a human-readable string representation of coins func FormatCoins(totalCopper int64) string { if totalCopper == 0 { return "0 copper" } coins := CalculateCoins(totalCopper) parts := make([]string, 0, 4) if coins.Platinum > 0 { parts = append(parts, fmt.Sprintf("%d platinum", coins.Platinum)) } if coins.Gold > 0 { parts = append(parts, fmt.Sprintf("%d gold", coins.Gold)) } if coins.Silver > 0 { parts = append(parts, fmt.Sprintf("%d silver", coins.Silver)) } if coins.Copper > 0 { parts = append(parts, fmt.Sprintf("%d copper", coins.Copper)) } return strings.Join(parts, ", ") } // ValidateTradeSlot checks if a slot number is valid for a participant func ValidateTradeSlot(slot int8, maxSlots int8) bool { return slot >= 0 && slot < maxSlots } // ValidateTradeQuantity checks if a quantity is valid for trading func ValidateTradeQuantity(quantity, available int32) bool { return quantity > 0 && quantity <= available } // FormatTradeError returns a formatted error message for trade operations func FormatTradeError(code int32) string { switch code { case TradeResultSuccess: return "Success" case TradeResultAlreadyInTrade: return "Item is already in the trade" case TradeResultNoTrade: return "Item cannot be traded" case TradeResultHeirloom: return "Heirloom item cannot be traded to this player" case TradeResultInvalidSlot: return "Invalid or occupied trade slot" case TradeResultSlotOutOfRange: return "Trade slot is out of range" case TradeResultInsufficientQty: return "Insufficient quantity to trade" default: return fmt.Sprintf("Unknown trade error: %d", code) } } // GetClientMaxSlots returns the maximum trade slots for a client version // Converted from C++ Trade constructor logic func GetClientMaxSlots(clientVersion int32) int8 { if clientVersion <= 561 { return TradeMaxSlotsLegacy } return TradeMaxSlotsDefault } // IsValidTradeState checks if a trade operation is valid for the current state func IsValidTradeState(state TradeState, operation string) bool { switch operation { case "add_item", "remove_item", "add_coins", "remove_coins", "accept": return state == TradeStateActive case "cancel": return state == TradeStateActive case "complete": return state == TradeStateAccepted default: return false } } // GenerateTradeLogEntry creates a log entry for trade operations func GenerateTradeLogEntry(tradeID string, operation string, entityID int32, details interface{}) string { return fmt.Sprintf("[Trade:%s] %s by entity %d: %v", tradeID, operation, entityID, details) } // CompareTradeItems checks if two trade item infos are equivalent func CompareTradeItems(item1, item2 TradeItemInfo) bool { if item1.Item == nil && item2.Item == nil { return item1.Quantity == item2.Quantity } if item1.Item == nil || item2.Item == nil { return false } return item1.Item.GetID() == item2.Item.GetID() && item1.Quantity == item2.Quantity } // CalculateTradeValue estimates the total value of items and coins in a trade // This is a helper function for trade balancing and analysis func CalculateTradeValue(participant *TradeParticipant) map[string]interface{} { value := make(map[string]interface{}) // Add coin value value["coins"] = participant.Coins value["coins_formatted"] = FormatCoins(participant.Coins) // Add item information itemCount := len(participant.Items) value["item_count"] = itemCount if itemCount > 0 { items := make([]map[string]interface{}, 0, itemCount) for slot, itemInfo := range participant.Items { itemData := make(map[string]interface{}) itemData["slot"] = slot itemData["quantity"] = itemInfo.Quantity if itemInfo.Item != nil { itemData["item_id"] = itemInfo.Item.GetID() itemData["item_name"] = itemInfo.Item.GetName() } items = append(items, itemData) } value["items"] = items } return value } // ValidateTradeCompletion checks if a trade is ready to be completed func ValidateTradeCompletion(trade *Trade) []string { errors := make([]string, 0) if trade.GetState() != TradeStateActive { errors = append(errors, "Trade is not in active state") return errors } // Check if both parties have accepted trader1Accepted := trade.HasAcceptedTrade(trade.GetTrader1ID()) trader2Accepted := trade.HasAcceptedTrade(trade.GetTrader2ID()) if !trader1Accepted { errors = append(errors, "Trader 1 has not accepted the trade") } if !trader2Accepted { errors = append(errors, "Trader 2 has not accepted the trade") } // TODO: Add additional validation when entity system is available: // - Verify entities still exist and are online // - Check inventory space for received items // - Validate coin amounts against actual entity wealth // - Check for item/trade restrictions that may have changed return errors }