eq2go/internal/languages/interfaces.go

399 lines
11 KiB
Go

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 ...interface{})
LogError(message string, args ...interface{})
LogDebug(message string, args ...interface{})
LogWarning(message string, args ...interface{})
}
// 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 interface{}) {
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)
}
}
}
}