package transmute import ( "fmt" "sync" "time" ) // Manager provides high-level management of the transmutation system type Manager struct { transmuter *Transmuter database Database requestTimeout time.Duration cleanupTicker *time.Ticker mutex sync.RWMutex // Statistics totalTransmutes int64 successfulTransmutes int64 failedTransmutes int64 materialCounts map[int32]int64 // Material ID -> count produced } // NewManager creates a new transmutation manager func NewManager(database Database, itemMaster ItemMaster, spellMaster SpellMaster, packetBuilder PacketBuilder) *Manager { transmuter := NewTransmuter(itemMaster, spellMaster, packetBuilder) manager := &Manager{ transmuter: transmuter, database: database, requestTimeout: 5 * time.Minute, // Requests expire after 5 minutes materialCounts: make(map[int32]int64), } // Start cleanup routine manager.cleanupTicker = time.NewTicker(1 * time.Minute) go manager.cleanupRoutine() return manager } // Initialize loads transmuting data from database func (m *Manager) Initialize() error { return m.transmuter.LoadTransmutingTiers(m.database) } // CreateItemRequest creates a new transmutation item selection request func (m *Manager) CreateItemRequest(client Client, player Player) (int32, error) { return m.transmuter.CreateItemRequest(client, player) } // HandleItemResponse handles the player's item selection response func (m *Manager) HandleItemResponse(client Client, player Player, requestID int32, itemID int32) error { return m.transmuter.HandleItemResponse(client, player, requestID, itemID) } // HandleConfirmResponse handles the player's confirmation response func (m *Manager) HandleConfirmResponse(client Client, player Player, itemID int32) error { return m.transmuter.HandleConfirmResponse(client, player, itemID) } // CompleteTransmutation completes the transmutation process func (m *Manager) CompleteTransmutation(client Client, player Player) error { m.mutex.Lock() m.totalTransmutes++ m.mutex.Unlock() err := m.transmuter.CompleteTransmutation(client, player) m.mutex.Lock() if err != nil { m.failedTransmutes++ } else { m.successfulTransmutes++ } m.mutex.Unlock() return err } // IsItemTransmutable checks if an item can be transmuted func (m *Manager) IsItemTransmutable(item Item) bool { return m.transmuter.IsItemTransmutable(item) } // GetTransmutingTiers returns the current transmuting tiers func (m *Manager) GetTransmutingTiers() []*TransmutingTier { return m.transmuter.GetTransmutingTiers() } // ReloadTransmutingTiers reloads transmuting tiers from database func (m *Manager) ReloadTransmutingTiers() error { return m.transmuter.LoadTransmutingTiers(m.database) } // GetStatistics returns transmutation statistics func (m *Manager) GetStatistics() map[string]interface{} { m.mutex.RLock() defer m.mutex.RUnlock() stats := make(map[string]interface{}) stats["total_transmutes"] = m.totalTransmutes stats["successful_transmutes"] = m.successfulTransmutes stats["failed_transmutes"] = m.failedTransmutes if m.totalTransmutes > 0 { stats["success_rate"] = float64(m.successfulTransmutes) / float64(m.totalTransmutes) * 100 } // Copy material counts materialStats := make(map[int32]int64) for matID, count := range m.materialCounts { materialStats[matID] = count } stats["material_counts"] = materialStats return stats } // RecordMaterialProduced records that a material was produced (for statistics) func (m *Manager) RecordMaterialProduced(materialID int32, count int32) { m.mutex.Lock() defer m.mutex.Unlock() m.materialCounts[materialID] += int64(count) } // GetMaterialProductionCount returns how many of a material have been produced func (m *Manager) GetMaterialProductionCount(materialID int32) int64 { m.mutex.RLock() defer m.mutex.RUnlock() return m.materialCounts[materialID] } // ResetStatistics resets all statistics func (m *Manager) ResetStatistics() { m.mutex.Lock() defer m.mutex.Unlock() m.totalTransmutes = 0 m.successfulTransmutes = 0 m.failedTransmutes = 0 m.materialCounts = make(map[int32]int64) } // ValidateTransmutingSetup validates that all transmuting tiers are properly configured func (m *Manager) ValidateTransmutingSetup() []string { tiers := m.GetTransmutingTiers() issues := make([]string, 0) if len(tiers) == 0 { issues = append(issues, "No transmuting tiers configured") return issues } // Check for gaps or overlaps in level ranges for i, tier := range tiers { if tier.MinLevel <= 0 { issues = append(issues, fmt.Sprintf("Tier %d has invalid min level: %d", i, tier.MinLevel)) } if tier.MaxLevel < tier.MinLevel { issues = append(issues, fmt.Sprintf("Tier %d has max level (%d) less than min level (%d)", i, tier.MaxLevel, tier.MinLevel)) } if tier.FragmentID <= 0 { issues = append(issues, fmt.Sprintf("Tier %d has invalid fragment ID: %d", i, tier.FragmentID)) } if tier.PowderID <= 0 { issues = append(issues, fmt.Sprintf("Tier %d has invalid powder ID: %d", i, tier.PowderID)) } if tier.InfusionID <= 0 { issues = append(issues, fmt.Sprintf("Tier %d has invalid infusion ID: %d", i, tier.InfusionID)) } if tier.ManaID <= 0 { issues = append(issues, fmt.Sprintf("Tier %d has invalid mana ID: %d", i, tier.ManaID)) } // Check for overlaps with other tiers for j, otherTier := range tiers { if i != j { if tier.MinLevel <= otherTier.MaxLevel && tier.MaxLevel >= otherTier.MinLevel { issues = append(issues, fmt.Sprintf("Tier %d (levels %d-%d) overlaps with tier %d (levels %d-%d)", i, tier.MinLevel, tier.MaxLevel, j, otherTier.MinLevel, otherTier.MaxLevel)) } } } } return issues } // GetTierForItemLevel returns the transmuting tier for a given item level func (m *Manager) GetTierForItemLevel(itemLevel int32) *TransmutingTier { tiers := m.GetTransmutingTiers() for _, tier := range tiers { if tier.MinLevel <= itemLevel && tier.MaxLevel >= itemLevel { return tier } } return nil } // GetTransmutableItems returns all transmutable items from a player's inventory func (m *Manager) GetTransmutableItems(player Player) []Item { itemList := player.GetItemList() transmutable := make([]Item, 0) for _, item := range itemList { if item != nil && m.IsItemTransmutable(item) { transmutable = append(transmutable, item) } } return transmutable } // CalculateRequiredSkill calculates the transmuting skill required for an item func (m *Manager) CalculateRequiredSkill(item Item) int32 { itemLevel := item.GetAdventureDefaultLevel() if itemLevel <= 5 { return 0 } return (itemLevel - 5) * 5 } // CanPlayerTransmuteItem checks if a player can transmute a specific item func (m *Manager) CanPlayerTransmuteItem(player Player, item Item) (bool, string) { if !m.IsItemTransmutable(item) { return false, fmt.Sprintf("%s is not transmutable", item.GetName()) } requiredSkill := m.CalculateRequiredSkill(item) skill := player.GetSkillByName("Transmuting") currentSkill := int32(0) if skill != nil { currentSkill = skill.GetCurrentValue() + player.GetStat(ItemStatTransmuting) } if currentSkill < requiredSkill { return false, fmt.Sprintf("Need %d Transmuting skill, have %d", requiredSkill, currentSkill) } return true, "" } // cleanupRoutine runs periodically to cleanup expired requests func (m *Manager) cleanupRoutine() { for range m.cleanupTicker.C { // TODO: Implement request cleanup based on timestamps // For now, this is a placeholder for future cleanup logic // In a full implementation, we'd track request timestamps // and remove requests older than the timeout period } } // Shutdown gracefully shuts down the manager func (m *Manager) Shutdown() { if m.cleanupTicker != nil { m.cleanupTicker.Stop() } } // ProcessCommand handles transmutation-related commands func (m *Manager) ProcessCommand(command string, args []string, client Client, player Player) (string, error) { switch command { case "stats": return m.handleStatsCommand(args) case "validate": return m.handleValidateCommand(args) case "reload": return m.handleReloadCommand(args) case "tiers": return m.handleTiersCommand(args) default: return "", fmt.Errorf("unknown transmute command: %s", command) } } // handleStatsCommand shows transmutation statistics func (m *Manager) handleStatsCommand(args []string) (string, error) { stats := m.GetStatistics() result := "Transmutation Statistics:\n" result += fmt.Sprintf("Total Transmutes: %d\n", stats["total_transmutes"]) result += fmt.Sprintf("Successful: %d\n", stats["successful_transmutes"]) result += fmt.Sprintf("Failed: %d\n", stats["failed_transmutes"]) if successRate, exists := stats["success_rate"]; exists { result += fmt.Sprintf("Success Rate: %.1f%%\n", successRate) } return result, nil } // handleValidateCommand validates the transmuting setup func (m *Manager) handleValidateCommand(args []string) (string, error) { issues := m.ValidateTransmutingSetup() if len(issues) == 0 { return "Transmuting setup is valid.", nil } result := fmt.Sprintf("Found %d issues with transmuting setup:\n", len(issues)) for i, issue := range issues { result += fmt.Sprintf("%d. %s\n", i+1, issue) } return result, nil } // handleReloadCommand reloads transmuting data func (m *Manager) handleReloadCommand(args []string) (string, error) { err := m.ReloadTransmutingTiers() if err != nil { return "", fmt.Errorf("failed to reload transmuting tiers: %w", err) } return "Transmuting tiers reloaded successfully.", nil } // handleTiersCommand shows transmuting tier information func (m *Manager) handleTiersCommand(args []string) (string, error) { tiers := m.GetTransmutingTiers() if len(tiers) == 0 { return "No transmuting tiers configured.", nil } result := fmt.Sprintf("Transmuting Tiers (%d):\n", len(tiers)) for i, tier := range tiers { result += fmt.Sprintf("%d. Levels %d-%d: Fragment(%d) Powder(%d) Infusion(%d) Mana(%d)\n", i+1, tier.MinLevel, tier.MaxLevel, tier.FragmentID, tier.PowderID, tier.InfusionID, tier.ManaID) } return result, nil } // Constants for stat types - these would typically be defined elsewhere const ( ItemStatTransmuting = 1 // Placeholder - actual value depends on stat system )