eq2go/internal/transmute/manager.go

351 lines
10 KiB
Go

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
)