package config import ( "os" "gopkg.in/yaml.v3" ) type Config struct { TickLength int `yaml:"tick_length"` StartingRoom int `yaml:"starting_room"` StartupValidation ValidationConfig `yaml:"startup_validation"` GameConstants GameConstants `yaml:"game_constants"` DefaultColors ColorsConfig `yaml:"default_colors"` ConstantColors ColorsConfig `yaml:"constant_colors"` TLS TLSConfig `yaml:"tls"` Telnet TelnetConfig `yaml:"telnet"` TelnetTLS TelnetTLSConfig `yaml:"telnet_tls"` HTTP HTTPConfig `yaml:"http"` HTTPS HTTPSConfig `yaml:"https"` AdminHTTP AdminHTTPConfig `yaml:"admin_http"` AdminHTTPS AdminHTTPSConfig `yaml:"admin_https"` } // GameConstants holds tunable game-wide numbers that would otherwise be // hardcoded in the engine. type GameConstants struct { // ShopDefaultRestock is the fallback restock interval (in ticks) for a shop // item that does not set its own restock_ticks. ShopDefaultRestock float64 `yaml:"shop_default_restock"` } type ColorsConfig map[string]string func DefaultColors() ColorsConfig { return ColorsConfig{ "room_name": "74 bold", "room_number": "F0", "room_desc": "FF", "direction": "49", "exit_direction": "49", "exit_name": "74", "protected_mob": "off", "mob": "off", "damage_dealt": "B4", "damage_taken": "A7", "xp": "B3", "level_up": "B4 bold", "item": "6E", "player_name": "74", "death": "A7 bold", "victory": "6C", "miss": "F5", "error": "A7", "say": "FF", "global": "49", "dialog": "6E", "dialog_options": "bold", "command": "0A bold", "broadcast": "89", "sequence": "off", "fire": "B4", "eat_food": "6C", "drop_message": "89", "currency_pickup": "B3", "visual_tick": "49 dim", "visual_first_tick": "B4 bold", "battery": "6C", "tech_depleted": "A7", "science_mod": "8B", "assassin_task": "8B", "dim": "F5 dim", "warning": "B3", "farm_grow": "6C", "farm_disease": "A7", "map_at": "B4 bold", "map_blocked": "A7", "map_agility": "43", "safespot_alert": "A7 bold", } } // DefaultConstantColors returns the built-in specs for the colors that the // engine renders conditionally and that players can never override (HP-bar // tiers, level-difference bands, table-header chrome, status indicators, etc). // Admins may override any of these via the constant_colors section of // config.yaml; players have no access to them. func DefaultConstantColors() ColorsConfig { return ColorsConfig{ // HP / status bars (cmd_score.go, render_combat.go). "hp_bar_high": "6C", "hp_bar_med": "B3", "hp_bar_low": "A7", "hp_bar_empty": "ED dim", // Task worksite progress bar (sys_labor.go) — same tiering rules // as the HP bar but a different low-color so labor tasks read // green/yellow/orange rather than green/yellow/red. It reuses // hp_bar_empty for the empty/unfilled cells. "task_bar_high": "6C", "task_bar_med": "B3", "task_bar_low": "43", // Score-panel other bars and tech/buff indicators (cmd_score.go). "battery_bar": "6E", "run_energy_bar": "6C", "active_tech_on": "6C", "active_tech_status": "6C", "active_tech_stat": "6E", "score_max_value": "FA", // Skill-level tier color on the score panel (cmd_score.go). "skill_lvl_max": "B4", "skill_lvl_high": "B3", "skill_lvl_mid": "74", "skill_lvl_low": "49", "skill_lvl_base": "42", // Level-difference band on mobs/players (render_color.go). "level_diff_equal": "FA", "level_diff_up_small": "B3", "level_diff_up_big": "A7", "level_diff_down_small": "6E", "level_diff_down_big": "41", // Tech list status / name styling (cmd_tech.go). "tech_status_on": "6C", "tech_status_off": "F0", "tech_status_locked": "A7", "tech_name_unlocked": "49", "tech_name_locked": "F0", // Shared table-heading chrome (cmd_skills.go, cmd_mods.go, // cmd_option.go, cmd_aps.go). "table_header_label": "49", "table_header_value": "FA", "table_header_detail": "F5", "table_header_xp": "B3", "table_header_desc": "FF", // APS / look inline accents. "aps_here": "6C", "mob_hp_inline": "A7", "protected_label": "FA", "god_tag_label": "bold", // Production menu degraded/locked rows (production_menu.go). "production_locked": "F0", } } type ValidationConfig struct { RootRooms []int `yaml:"root_rooms"` IgnoreUnreachable []int `yaml:"ignore_unreachable"` } type TelnetConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } type TLSConfig struct { CertFile string `yaml:"cert_file"` KeyFile string `yaml:"key_file"` } type TelnetTLSConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } type HTTPConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } type HTTPSConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } type AdminHTTPConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } type AdminHTTPSConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` } func Default() *Config { return &Config{ TickLength: 600, StartingRoom: 1001, StartupValidation: ValidationConfig{ RootRooms: []int{1}, IgnoreUnreachable: []int{}, }, GameConstants: GameConstants{ ShopDefaultRestock: 1000, }, DefaultColors: DefaultColors(), ConstantColors: DefaultConstantColors(), TLS: TLSConfig{ CertFile: "", KeyFile: "", }, Telnet: TelnetConfig{ Enabled: true, Port: 4000, }, TelnetTLS: TelnetTLSConfig{ Enabled: false, Port: 4001, }, HTTP: HTTPConfig{ Enabled: false, Port: 8080, }, HTTPS: HTTPSConfig{ Enabled: false, Port: 8443, }, AdminHTTP: AdminHTTPConfig{ Enabled: false, Port: 9091, }, AdminHTTPS: AdminHTTPSConfig{ Enabled: false, Port: 9090, }, } } func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } cfg := Default() if err := yaml.Unmarshal(data, cfg); err != nil { return nil, err } return cfg, nil } func SaveDefault(path string) error { cfg := Default() data, err := yaml.Marshal(cfg) if err != nil { return err } return os.WriteFile(path, data, 0644) }