351 lines
10 KiB
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
|
|
)
|