package languages import "fmt" // Database interface for language persistence type Database interface { LoadAllLanguages() ([]*Language, error) SaveLanguage(language *Language) error DeleteLanguage(languageID int32) error LoadPlayerLanguages(playerID int32) ([]*Language, error) SavePlayerLanguage(playerID int32, languageID int32) error DeletePlayerLanguage(playerID int32, languageID int32) error } // Logger interface for language logging type Logger interface { LogInfo(message string, args ...any) LogError(message string, args ...any) LogDebug(message string, args ...any) LogWarning(message string, args ...any) } // Entity interface for language-related entity operations // This interface should be implemented by Player, NPC, and Bot types type Entity interface { GetID() int32 GetName() string IsPlayer() bool IsNPC() bool IsBot() bool } // Player interface for language-related player operations // This interface should be implemented by Player types type Player interface { Entity GetCharacterID() int32 SendMessage(message string) } // Client interface for language-related client operations type Client interface { GetVersion() int16 SendLanguageUpdate(languageData []byte) error } // LanguageProvider interface for systems that provide language functionality type LanguageProvider interface { GetMasterLanguagesList() *MasterLanguagesList GetLanguage(languageID int32) *Language GetLanguageByName(name string) *Language CreatePlayerLanguagesList() *PlayerLanguagesList } // LanguageAware interface for entities that can understand languages // This interface should be implemented by players who have language capabilities type LanguageAware interface { GetKnownLanguages() *PlayerLanguagesList KnowsLanguage(languageID int32) bool GetPrimaryLanguage() int32 SetPrimaryLanguage(languageID int32) CanUnderstand(languageID int32) bool LearnLanguage(languageID int32) error ForgetLanguage(languageID int32) error } // LanguageHandler interface for handling language events type LanguageHandler interface { OnLanguageLearned(player Player, languageID int32) error OnLanguageForgotten(player Player, languageID int32) error OnLanguageUsed(player Player, languageID int32, message string) error } // ChatProcessor interface for processing multilingual chat type ChatProcessor interface { ProcessMessage(speaker Player, message string, languageID int32) (string, error) FilterMessage(listener Player, message string, languageID int32) string GetLanguageSkramble(message string, comprehension float32) string } // PlayerLanguageAdapter provides language functionality for players // This adapter can be embedded in player structs to provide language capabilities type PlayerLanguageAdapter struct { languages *PlayerLanguagesList primaryLang int32 manager *Manager logger Logger } // NewPlayerLanguageAdapter creates a new player language adapter func NewPlayerLanguageAdapter(manager *Manager, logger Logger) *PlayerLanguageAdapter { adapter := &PlayerLanguageAdapter{ languages: manager.CreatePlayerLanguagesList(), primaryLang: LanguageIDCommon, // Default to common manager: manager, logger: logger, } // Ensure common language is always known if commonLang := manager.GetLanguage(LanguageIDCommon); commonLang != nil { adapter.languages.Add(commonLang.Copy()) } return adapter } // GetKnownLanguages returns the player's known languages func (pla *PlayerLanguageAdapter) GetKnownLanguages() *PlayerLanguagesList { return pla.languages } // KnowsLanguage checks if the player knows a specific language func (pla *PlayerLanguageAdapter) KnowsLanguage(languageID int32) bool { return pla.languages.HasLanguage(languageID) } // GetPrimaryLanguage returns the player's primary language func (pla *PlayerLanguageAdapter) GetPrimaryLanguage() int32 { return pla.primaryLang } // SetPrimaryLanguage sets the player's primary language func (pla *PlayerLanguageAdapter) SetPrimaryLanguage(languageID int32) { // Only allow setting to a known language if pla.languages.HasLanguage(languageID) { pla.primaryLang = languageID if pla.logger != nil { lang := pla.manager.GetLanguage(languageID) langName := "Unknown" if lang != nil { langName = lang.GetName() } pla.logger.LogDebug("Player set primary language to %s (%d)", langName, languageID) } } } // CanUnderstand checks if the player can understand a language func (pla *PlayerLanguageAdapter) CanUnderstand(languageID int32) bool { // Common language is always understood if languageID == LanguageIDCommon { return true } // Check if player knows the language return pla.languages.HasLanguage(languageID) } // LearnLanguage teaches the player a new language func (pla *PlayerLanguageAdapter) LearnLanguage(languageID int32) error { // Get the language from master list language := pla.manager.GetLanguage(languageID) if language == nil { return fmt.Errorf("language with ID %d does not exist", languageID) } // Check if already known if pla.languages.HasLanguage(languageID) { return fmt.Errorf("player already knows language %s", language.GetName()) } // Create a copy for the player playerLang := language.Copy() playerLang.SetSaveNeeded(true) // Add to player's languages if err := pla.languages.Add(playerLang); err != nil { return fmt.Errorf("failed to add language to player: %w", err) } // Record usage statistics pla.manager.RecordLanguageUsage(languageID) if pla.logger != nil { pla.logger.LogInfo("Player learned language %s (%d)", language.GetName(), languageID) } return nil } // ForgetLanguage makes the player forget a language func (pla *PlayerLanguageAdapter) ForgetLanguage(languageID int32) error { // Cannot forget common language if languageID == LanguageIDCommon { return fmt.Errorf("cannot forget common language") } // Check if player knows the language if !pla.languages.HasLanguage(languageID) { return fmt.Errorf("player does not know language %d", languageID) } // Get language name for logging language := pla.manager.GetLanguage(languageID) langName := "Unknown" if language != nil { langName = language.GetName() } // Remove from player's languages if !pla.languages.RemoveLanguage(languageID) { return fmt.Errorf("failed to remove language from player") } // Reset primary language if this was it if pla.primaryLang == languageID { pla.primaryLang = LanguageIDCommon } if pla.logger != nil { pla.logger.LogInfo("Player forgot language %s (%d)", langName, languageID) } return nil } // LoadPlayerLanguages loads the player's languages from database func (pla *PlayerLanguageAdapter) LoadPlayerLanguages(database Database, playerID int32) error { if database == nil { return fmt.Errorf("database is nil") } languages, err := database.LoadPlayerLanguages(playerID) if err != nil { return fmt.Errorf("failed to load player languages: %w", err) } // Clear current languages pla.languages.Clear() // Add loaded languages for _, lang := range languages { if err := pla.languages.Add(lang); err != nil && pla.logger != nil { pla.logger.LogWarning("Failed to add loaded language %d: %v", lang.GetID(), err) } } // Ensure player knows common language if !pla.languages.HasLanguage(LanguageIDCommon) { commonLang := pla.manager.GetLanguage(LanguageIDCommon) if commonLang != nil { playerCommon := commonLang.Copy() pla.languages.Add(playerCommon) } } if pla.logger != nil { pla.logger.LogDebug("Loaded %d languages for player", len(languages)) } return nil } // SavePlayerLanguages saves the player's languages to database func (pla *PlayerLanguageAdapter) SavePlayerLanguages(database Database, playerID int32) error { if database == nil { return fmt.Errorf("database is nil") } languages := pla.languages.GetAllLanguages() // Save each language that needs saving for _, lang := range languages { if lang.GetSaveNeeded() { if err := database.SavePlayerLanguage(playerID, lang.GetID()); err != nil { return fmt.Errorf("failed to save player language %d: %w", lang.GetID(), err) } lang.SetSaveNeeded(false) } } if pla.logger != nil { pla.logger.LogDebug("Saved languages for player") } return nil } // ChatLanguageProcessor handles multilingual chat processing type ChatLanguageProcessor struct { manager *Manager logger Logger } // NewChatLanguageProcessor creates a new chat language processor func NewChatLanguageProcessor(manager *Manager, logger Logger) *ChatLanguageProcessor { return &ChatLanguageProcessor{ manager: manager, logger: logger, } } // ProcessMessage processes a chat message in a specific language func (clp *ChatLanguageProcessor) ProcessMessage(speaker Player, message string, languageID int32) (string, error) { if speaker == nil { return "", fmt.Errorf("speaker cannot be nil") } // Validate language exists language := clp.manager.GetLanguage(languageID) if language == nil { return "", fmt.Errorf("language %d does not exist", languageID) } // Record language usage (we can't check if speaker knows the language without extending the interface) clp.manager.RecordLanguageUsage(languageID) return message, nil } // FilterMessage filters a message for a listener based on language comprehension func (clp *ChatLanguageProcessor) FilterMessage(listener Player, message string, languageID int32) string { if listener == nil { return message } // Common language is always understood if languageID == LanguageIDCommon { return message } // For now, we'll always return the message since we can't check language knowledge // This would need integration with a PlayerLanguageAdapter or similar return message } // GetLanguageSkramble scrambles a message based on comprehension level func (clp *ChatLanguageProcessor) GetLanguageSkramble(message string, comprehension float32) string { if comprehension >= 1.0 { return message } if comprehension <= 0.0 { // Complete scramble - replace with gibberish runes := []rune(message) scrambled := make([]rune, len(runes)) for i, r := range runes { if r == ' ' { scrambled[i] = ' ' } else if r >= 'a' && r <= 'z' { scrambled[i] = 'a' + rune((int(r-'a')+7)%26) } else if r >= 'A' && r <= 'Z' { scrambled[i] = 'A' + rune((int(r-'A')+7)%26) } else { scrambled[i] = r } } return string(scrambled) } // Partial comprehension - scramble some words // This is a simplified implementation return message } // LanguageEventAdapter handles language-related events type LanguageEventAdapter struct { handler LanguageHandler logger Logger } // NewLanguageEventAdapter creates a new language event adapter func NewLanguageEventAdapter(handler LanguageHandler, logger Logger) *LanguageEventAdapter { return &LanguageEventAdapter{ handler: handler, logger: logger, } } // ProcessLanguageEvent processes a language-related event func (lea *LanguageEventAdapter) ProcessLanguageEvent(eventType string, player Player, languageID int32, data any) { if lea.handler == nil { return } switch eventType { case "language_learned": if err := lea.handler.OnLanguageLearned(player, languageID); err != nil && lea.logger != nil { lea.logger.LogError("Language learned handler failed: %v", err) } case "language_forgotten": if err := lea.handler.OnLanguageForgotten(player, languageID); err != nil && lea.logger != nil { lea.logger.LogError("Language forgotten handler failed: %v", err) } case "language_used": if message, ok := data.(string); ok { if err := lea.handler.OnLanguageUsed(player, languageID, message); err != nil && lea.logger != nil { lea.logger.LogError("Language used handler failed: %v", err) } } } }