399 lines
11 KiB
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)
|
|
}
|
|
}
|
|
}
|
|
}
|