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) } // Player interface for language-related player operations type Player interface { GetCharacterID() int32 GetName() string GetLanguages() *PlayerLanguagesList KnowsLanguage(languageID int32) bool LearnLanguage(languageID int32) error ForgetLanguage(languageID int32) error SendMessage(message string) } // Client interface for language-related client operations type Client interface { GetPlayer() *Player 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 type LanguageAware interface { GetKnownLanguages() *PlayerLanguagesList KnowsLanguage(languageID int32) bool GetPrimaryLanguage() int32 SetPrimaryLanguage(languageID int32) CanUnderstand(languageID int32) bool } // 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 type PlayerLanguageAdapter struct { player *Player languages *PlayerLanguagesList primaryLang int32 manager *Manager logger Logger } // NewPlayerLanguageAdapter creates a new player language adapter func NewPlayerLanguageAdapter(player *Player, manager *Manager, logger Logger) *PlayerLanguageAdapter { return &PlayerLanguageAdapter{ player: player, languages: manager.CreatePlayerLanguagesList(), primaryLang: LanguageIDCommon, // Default to common manager: manager, logger: logger, } } // 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 %s set primary language to %s (%d)", pla.player.GetName(), 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 %s learned language %s (%d)", pla.player.GetName(), 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 %s forgot language %s (%d)", pla.player.GetName(), langName, languageID) } return nil } // LoadPlayerLanguages loads the player's languages from database func (pla *PlayerLanguageAdapter) LoadPlayerLanguages(database Database) error { if database == nil { return fmt.Errorf("database is nil") } playerID := pla.player.GetCharacterID() 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 to player %s: %v", lang.GetID(), pla.player.GetName(), 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 %s", len(languages), pla.player.GetName()) } return nil } // SavePlayerLanguages saves the player's languages to database func (pla *PlayerLanguageAdapter) SavePlayerLanguages(database Database) error { if database == nil { return fmt.Errorf("database is nil") } playerID := pla.player.GetCharacterID() 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 %s", pla.player.GetName()) } 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) } // Check if speaker knows the language if !speaker.KnowsLanguage(languageID) { return "", fmt.Errorf("speaker does not know language %s", language.GetName()) } // Record language usage 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 } // Check if listener knows the language if listener.KnowsLanguage(languageID) { return message } // Scramble the message for unknown languages return clp.GetLanguageSkramble(message, 0.0) } // 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) } } } }