212 lines
4.7 KiB
Go

package control
import (
"encoding/json"
"fmt"
"os"
"sync"
)
var (
global *Control
configPath string
mu sync.RWMutex
)
func init() {
global = New()
}
// Control represents the game control settings
type Control struct {
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"`
}
// New creates a new Control with sensible defaults
func New() *Control {
return &Control{
WorldSize: 200,
Open: 1,
AdminEmail: "",
Class1Name: "Mage",
Class2Name: "Warrior",
Class3Name: "Paladin",
}
}
// Load loads control settings from JSON file into global instance
func Load(filename string) error {
mu.Lock()
configPath = filename
mu.Unlock()
data, err := os.ReadFile(filename)
if err != nil {
if os.IsNotExist(err) {
return nil // Keep defaults
}
return fmt.Errorf("failed to read config file: %w", err)
}
if len(data) == 0 {
return nil
}
control := &Control{}
if err := json.Unmarshal(data, control); err != nil {
return fmt.Errorf("failed to parse config: %w", err)
}
// Apply defaults for any missing fields
defaults := New()
if control.WorldSize == 0 {
control.WorldSize = defaults.WorldSize
}
if control.Class1Name == "" {
control.Class1Name = defaults.Class1Name
}
if control.Class2Name == "" {
control.Class2Name = defaults.Class2Name
}
if control.Class3Name == "" {
control.Class3Name = defaults.Class3Name
}
mu.Lock()
global = control
mu.Unlock()
return nil
}
// Save saves global control settings to the loaded path
func Save() error {
mu.RLock()
defer mu.RUnlock()
if configPath == "" {
return fmt.Errorf("no config path set, call Load() first")
}
data, err := json.MarshalIndent(global, "", "\t")
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(configPath, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}
// Get returns the global control instance (thread-safe)
func Get() *Control {
mu.RLock()
defer mu.RUnlock()
return global
}
// Set updates the global control instance (thread-safe)
func Set(control *Control) {
mu.Lock()
defer mu.Unlock()
global = control
}
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")
}
return nil
}
// IsOpen returns true if the game world is open for new players
func (c *Control) IsOpen() bool {
return c.Open == 1
}
// 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
}