package control import ( "fmt" "sync" nigiri "git.sharkk.net/Sharkk/Nigiri" ) var ( store *nigiri.BaseStore[Control] db *nigiri.Collection global *Control mu sync.RWMutex ) // Control represents the game control settings type Control struct { ID int `json:"id"` WorldSize int `json:"world_size"` Open int `json:"open"` AdminEmail string `json:"admin_email"` Class1Name string `json:"class_1_name"` Class2Name string `json:"class_2_name"` Class3Name string `json:"class_3_name"` } // Init sets up the Nigiri store for control settings func Init(collection *nigiri.Collection) { db = collection store = nigiri.NewBaseStore[Control]() // Load or create the singleton control instance all := store.GetAll() if len(all) == 0 { // Create default control settings global = New() global.ID = 1 store.Add(1, global) } else { // Use the first (and only) control entry for _, ctrl := range all { global = ctrl break } // Apply defaults for any missing fields defaults := New() if global.WorldSize == 0 { global.WorldSize = defaults.WorldSize } if global.Class1Name == "" { global.Class1Name = defaults.Class1Name } if global.Class2Name == "" { global.Class2Name = defaults.Class2Name } if global.Class3Name == "" { global.Class3Name = defaults.Class3Name } store.Update(global.ID, global) } } // New creates a new Control with sensible defaults func New() *Control { return &Control{ ID: 1, // Singleton WorldSize: 200, Open: 1, AdminEmail: "", Class1Name: "Mage", Class2Name: "Warrior", Class3Name: "Paladin", } } // Get returns the global control instance (thread-safe) func Get() *Control { mu.RLock() defer mu.RUnlock() if global == nil { panic("control not initialized - call Initialize first") } return global } // Set updates the global control instance (thread-safe) func Set(control *Control) error { mu.Lock() defer mu.Unlock() control.ID = 1 // Ensure it's always ID 1 (singleton) if err := control.Validate(); err != nil { return err } if err := store.Update(1, control); err != nil { return err } global = control return nil } // Update updates specific fields of the control settings func Update(updater func(*Control)) error { mu.Lock() defer mu.Unlock() // Create a copy to work with updated := *global updater(&updated) if err := updated.Validate(); err != nil { return err } if err := store.Update(1, &updated); err != nil { return err } global = &updated return nil } // Validate checks if control settings have valid values func (c *Control) Validate() error { if c.WorldSize <= 0 || c.WorldSize > 10000 { return fmt.Errorf("WorldSize must be between 1 and 10000") } if c.Open != 0 && c.Open != 1 { return fmt.Errorf("Open must be 0 or 1") } if c.Class1Name == "" { return fmt.Errorf("Class1Name cannot be empty") } if c.Class2Name == "" { return fmt.Errorf("Class2Name cannot be empty") } if c.Class3Name == "" { return fmt.Errorf("Class3Name cannot be empty") } return nil } // IsOpen returns true if the game world is open for new players func (c *Control) IsOpen() bool { return c.Open == 1 } // SetOpen sets whether the game world is open for new players func SetOpen(open bool) error { return Update(func(c *Control) { if open { c.Open = 1 } else { c.Open = 0 } }) } // GetClassNames returns all class names as a slice func (c *Control) GetClassNames() []string { classes := make([]string, 0, 3) if c.Class1Name != "" { classes = append(classes, c.Class1Name) } if c.Class2Name != "" { classes = append(classes, c.Class2Name) } if c.Class3Name != "" { classes = append(classes, c.Class3Name) } return classes } // GetClassName returns the name of a specific class (1-3) func (c *Control) GetClassName(classNum int) string { switch classNum { case 1: return c.Class1Name case 2: return c.Class2Name case 3: return c.Class3Name default: return "" } } // IsValidClassName returns true if the given name matches one of the configured classes func (c *Control) IsValidClassName(name string) bool { if name == "" { return false } return name == c.Class1Name || name == c.Class2Name || name == c.Class3Name } // GetClassNumber returns the class number (1-3) for a given class name, or 0 if not found func (c *Control) GetClassNumber(name string) int { if name == c.Class1Name && name != "" { return 1 } if name == c.Class2Name && name != "" { return 2 } if name == c.Class3Name && name != "" { return 3 } return 0 } // HasAdminEmail returns true if an admin email is configured func (c *Control) HasAdminEmail() bool { return c.AdminEmail != "" } // IsWorldSizeValid returns true if the world size is within reasonable bounds func (c *Control) IsWorldSizeValid() bool { return c.WorldSize > 0 && c.WorldSize <= 10000 } // GetWorldRadius returns the world radius (half the world size) func (c *Control) GetWorldRadius() int { return c.WorldSize / 2 } // IsWithinWorldBounds returns true if the given coordinates are within world bounds func (c *Control) IsWithinWorldBounds(x, y int) bool { radius := c.GetWorldRadius() return x >= -radius && x <= radius && y >= -radius && y <= radius } // GetWorldBounds returns the minimum and maximum coordinates for the world func (c *Control) GetWorldBounds() (minX, minY, maxX, maxY int) { radius := c.GetWorldRadius() return -radius, -radius, radius, radius } // Legacy compatibility functions (will be removed later) func Load(filename string) error { // No longer needed - Nigiri handles this return nil } func Save() error { // No longer needed - Nigiri handles this return nil }