eq2go/internal/collections/collections.go

499 lines
12 KiB
Go

package collections
import (
"fmt"
"strconv"
"strings"
"time"
)
// NewCollection creates a new collection instance
func NewCollection() *Collection {
return &Collection{
collectionItems: make([]CollectionItem, 0),
rewardItems: make([]CollectionRewardItem, 0),
selectableRewardItems: make([]CollectionRewardItem, 0),
lastModified: time.Now(),
}
}
// NewCollectionFromData creates a collection from another collection (copy constructor)
func NewCollectionFromData(source *Collection) *Collection {
if source == nil {
return nil
}
source.mu.RLock()
defer source.mu.RUnlock()
collection := &Collection{
id: source.id,
name: source.name,
category: source.category,
level: source.level,
rewardCoin: source.rewardCoin,
rewardXP: source.rewardXP,
completed: source.completed,
saveNeeded: source.saveNeeded,
collectionItems: make([]CollectionItem, len(source.collectionItems)),
rewardItems: make([]CollectionRewardItem, len(source.rewardItems)),
selectableRewardItems: make([]CollectionRewardItem, len(source.selectableRewardItems)),
lastModified: time.Now(),
}
// Deep copy collection items
copy(collection.collectionItems, source.collectionItems)
// Deep copy reward items
copy(collection.rewardItems, source.rewardItems)
// Deep copy selectable reward items
copy(collection.selectableRewardItems, source.selectableRewardItems)
return collection
}
// SetID sets the collection ID
func (c *Collection) SetID(id int32) {
c.mu.Lock()
defer c.mu.Unlock()
c.id = id
}
// SetName sets the collection name
func (c *Collection) SetName(name string) {
c.mu.Lock()
defer c.mu.Unlock()
if len(name) > MaxCollectionNameLength {
name = name[:MaxCollectionNameLength]
}
c.name = name
}
// SetCategory sets the collection category
func (c *Collection) SetCategory(category string) {
c.mu.Lock()
defer c.mu.Unlock()
if len(category) > MaxCollectionCategoryLength {
category = category[:MaxCollectionCategoryLength]
}
c.category = category
}
// SetLevel sets the collection level
func (c *Collection) SetLevel(level int8) {
c.mu.Lock()
defer c.mu.Unlock()
c.level = level
}
// SetCompleted sets the collection completion status
func (c *Collection) SetCompleted(completed bool) {
c.mu.Lock()
defer c.mu.Unlock()
c.completed = completed
c.lastModified = time.Now()
}
// SetSaveNeeded sets whether the collection needs to be saved
func (c *Collection) SetSaveNeeded(saveNeeded bool) {
c.mu.Lock()
defer c.mu.Unlock()
c.saveNeeded = saveNeeded
}
// SetRewardCoin sets the coin reward amount
func (c *Collection) SetRewardCoin(coin int64) {
c.mu.Lock()
defer c.mu.Unlock()
c.rewardCoin = coin
}
// SetRewardXP sets the XP reward amount
func (c *Collection) SetRewardXP(xp int64) {
c.mu.Lock()
defer c.mu.Unlock()
c.rewardXP = xp
}
// AddCollectionItem adds a required item to the collection
func (c *Collection) AddCollectionItem(item CollectionItem) {
c.mu.Lock()
defer c.mu.Unlock()
c.collectionItems = append(c.collectionItems, item)
}
// AddRewardItem adds a reward item to the collection
func (c *Collection) AddRewardItem(item CollectionRewardItem) {
c.mu.Lock()
defer c.mu.Unlock()
c.rewardItems = append(c.rewardItems, item)
}
// AddSelectableRewardItem adds a selectable reward item to the collection
func (c *Collection) AddSelectableRewardItem(item CollectionRewardItem) {
c.mu.Lock()
defer c.mu.Unlock()
c.selectableRewardItems = append(c.selectableRewardItems, item)
}
// GetID returns the collection ID
func (c *Collection) GetID() int32 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.id
}
// GetName returns the collection name
func (c *Collection) GetName() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.name
}
// GetCategory returns the collection category
func (c *Collection) GetCategory() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.category
}
// GetLevel returns the collection level
func (c *Collection) GetLevel() int8 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.level
}
// GetCompleted returns whether the collection is completed
func (c *Collection) GetCompleted() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.completed
}
// GetSaveNeeded returns whether the collection needs to be saved
func (c *Collection) GetSaveNeeded() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.saveNeeded
}
// GetRewardCoin returns the coin reward amount
func (c *Collection) GetRewardCoin() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.rewardCoin
}
// GetRewardXP returns the XP reward amount
func (c *Collection) GetRewardXP() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.rewardXP
}
// GetCollectionItems returns a copy of the collection items
func (c *Collection) GetCollectionItems() []CollectionItem {
c.mu.RLock()
defer c.mu.RUnlock()
items := make([]CollectionItem, len(c.collectionItems))
copy(items, c.collectionItems)
return items
}
// GetRewardItems returns a copy of the reward items
func (c *Collection) GetRewardItems() []CollectionRewardItem {
c.mu.RLock()
defer c.mu.RUnlock()
items := make([]CollectionRewardItem, len(c.rewardItems))
copy(items, c.rewardItems)
return items
}
// GetSelectableRewardItems returns a copy of the selectable reward items
func (c *Collection) GetSelectableRewardItems() []CollectionRewardItem {
c.mu.RLock()
defer c.mu.RUnlock()
items := make([]CollectionRewardItem, len(c.selectableRewardItems))
copy(items, c.selectableRewardItems)
return items
}
// NeedsItem checks if the collection needs a specific item
func (c *Collection) NeedsItem(itemID int32) bool {
c.mu.RLock()
defer c.mu.RUnlock()
if c.completed {
return false
}
for _, item := range c.collectionItems {
if item.ItemID == itemID {
return item.Found == ItemNotFound
}
}
return false
}
// GetCollectionItemByItemID returns the collection item for a specific item ID
func (c *Collection) GetCollectionItemByItemID(itemID int32) *CollectionItem {
c.mu.RLock()
defer c.mu.RUnlock()
for i := range c.collectionItems {
if c.collectionItems[i].ItemID == itemID {
return &c.collectionItems[i]
}
}
return nil
}
// GetIsReadyToTurnIn checks if all required items have been found
func (c *Collection) GetIsReadyToTurnIn() bool {
c.mu.RLock()
defer c.mu.RUnlock()
if c.completed {
return false
}
for _, item := range c.collectionItems {
if item.Found == ItemNotFound {
return false
}
}
return true
}
// MarkItemFound marks an item as found in the collection
func (c *Collection) MarkItemFound(itemID int32) bool {
c.mu.Lock()
defer c.mu.Unlock()
if c.completed {
return false
}
for i := range c.collectionItems {
if c.collectionItems[i].ItemID == itemID && c.collectionItems[i].Found == ItemNotFound {
c.collectionItems[i].Found = ItemFound
c.saveNeeded = true
c.lastModified = time.Now()
return true
}
}
return false
}
// GetProgress returns the completion progress as a percentage
func (c *Collection) GetProgress() float64 {
c.mu.RLock()
defer c.mu.RUnlock()
if len(c.collectionItems) == 0 {
return 0.0
}
foundCount := 0
for _, item := range c.collectionItems {
if item.Found == ItemFound {
foundCount++
}
}
return float64(foundCount) / float64(len(c.collectionItems)) * 100.0
}
// GetFoundItemsCount returns the number of found items
func (c *Collection) GetFoundItemsCount() int {
c.mu.RLock()
defer c.mu.RUnlock()
count := 0
for _, item := range c.collectionItems {
if item.Found == ItemFound {
count++
}
}
return count
}
// GetTotalItemsCount returns the total number of required items
func (c *Collection) GetTotalItemsCount() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.collectionItems)
}
// GetCollectionInfo returns detailed collection information
func (c *Collection) GetCollectionInfo() CollectionInfo {
c.mu.RLock()
defer c.mu.RUnlock()
return CollectionInfo{
ID: c.id,
Name: c.name,
Category: c.category,
Level: c.level,
Completed: c.completed,
ReadyToTurnIn: c.getIsReadyToTurnInNoLock(),
ItemsFound: c.getFoundItemsCountNoLock(),
ItemsTotal: len(c.collectionItems),
RewardCoin: c.rewardCoin,
RewardXP: c.rewardXP,
RewardItems: append([]CollectionRewardItem(nil), c.rewardItems...),
SelectableRewards: append([]CollectionRewardItem(nil), c.selectableRewardItems...),
RequiredItems: append([]CollectionItem(nil), c.collectionItems...),
}
}
// GetCollectionProgress returns detailed progress information
func (c *Collection) GetCollectionProgress() CollectionProgress {
c.mu.RLock()
defer c.mu.RUnlock()
var foundItems, neededItems []CollectionItem
for _, item := range c.collectionItems {
if item.Found == ItemFound {
foundItems = append(foundItems, item)
} else {
neededItems = append(neededItems, item)
}
}
return CollectionProgress{
CollectionID: c.id,
Name: c.name,
Category: c.category,
Level: c.level,
Completed: c.completed,
ReadyToTurnIn: c.getIsReadyToTurnInNoLock(),
Progress: c.getProgressNoLock(),
ItemsFound: foundItems,
ItemsNeeded: neededItems,
LastUpdated: c.lastModified,
}
}
// LoadFromRewardData loads reward data into the collection
func (c *Collection) LoadFromRewardData(rewards []CollectionRewardData) error {
c.mu.Lock()
defer c.mu.Unlock()
for _, reward := range rewards {
switch strings.ToLower(reward.RewardType) {
case strings.ToLower(RewardTypeItem):
itemID, err := strconv.ParseInt(reward.RewardValue, 10, 32)
if err != nil {
return fmt.Errorf("invalid item ID in reward: %s", reward.RewardValue)
}
c.rewardItems = append(c.rewardItems, CollectionRewardItem{
ItemID: int32(itemID),
Quantity: reward.Quantity,
})
case strings.ToLower(RewardTypeSelectable):
itemID, err := strconv.ParseInt(reward.RewardValue, 10, 32)
if err != nil {
return fmt.Errorf("invalid item ID in selectable reward: %s", reward.RewardValue)
}
c.selectableRewardItems = append(c.selectableRewardItems, CollectionRewardItem{
ItemID: int32(itemID),
Quantity: reward.Quantity,
})
case strings.ToLower(RewardTypeCoin):
coin, err := strconv.ParseInt(reward.RewardValue, 10, 64)
if err != nil {
return fmt.Errorf("invalid coin amount in reward: %s", reward.RewardValue)
}
c.rewardCoin = coin
case strings.ToLower(RewardTypeXP):
xp, err := strconv.ParseInt(reward.RewardValue, 10, 64)
if err != nil {
return fmt.Errorf("invalid XP amount in reward: %s", reward.RewardValue)
}
c.rewardXP = xp
default:
return fmt.Errorf("unknown reward type: %s", reward.RewardType)
}
}
return nil
}
// Validate checks if the collection data is valid
func (c *Collection) Validate() error {
c.mu.RLock()
defer c.mu.RUnlock()
if c.id <= 0 {
return fmt.Errorf("collection ID must be positive")
}
if strings.TrimSpace(c.name) == "" {
return fmt.Errorf("collection name cannot be empty")
}
if len(c.collectionItems) == 0 {
return fmt.Errorf("collection must have at least one required item")
}
// Check for duplicate item IDs
itemIDs := make(map[int32]bool)
for _, item := range c.collectionItems {
if itemIDs[item.ItemID] {
return fmt.Errorf("duplicate item ID in collection: %d", item.ItemID)
}
itemIDs[item.ItemID] = true
if item.ItemID <= 0 {
return fmt.Errorf("collection item ID must be positive: %d", item.ItemID)
}
}
return nil
}
// Helper methods (no lock versions for internal use)
func (c *Collection) getIsReadyToTurnInNoLock() bool {
if c.completed {
return false
}
for _, item := range c.collectionItems {
if item.Found == ItemNotFound {
return false
}
}
return true
}
func (c *Collection) getFoundItemsCountNoLock() int {
count := 0
for _, item := range c.collectionItems {
if item.Found == ItemFound {
count++
}
}
return count
}
func (c *Collection) getProgressNoLock() float64 {
if len(c.collectionItems) == 0 {
return 0.0
}
foundCount := c.getFoundItemsCountNoLock()
return float64(foundCount) / float64(len(c.collectionItems)) * 100.0
}