# Conan Exiles dedicated server module.
#
# Conan Exiles Dedicated Server (Steam app 443030) — Funcom rolled out the
# UE5 "Enhanced" remaster as the default branch on 2026-05-05. The UE4 build
# remains available indefinitely as Steam beta branch `conan-exiles-legacy`
# (description: "UE4 version of Conan Exiles Dedicated Server"). Both ride
# the same appid; only the branch differs. Default install pulls Enhanced.
#
# Hosting matrix as of 2026-05-10:
#   Enhanced (UE5, public branch) — NATIVE LINUX. Funcom shipped a real Linux
#     dedicated-server depot (443032, ~4.27 GiB) alongside the 2026-05-05
#     Enhanced launch. Binary at
#     ConanSandbox/Binaries/Linux/ConanSandboxServer-Linux-Shipping. We run
#     it directly — no Wine, no Xvfb. Config dir is Config/LinuxServer/.
#   Legacy (UE4, conan-exiles-legacy beta) — STILL WINDOWS-ONLY. No Linux
#     depot exists for this branch; we keep the Wine + Xvfb path for it.
#     Binary at ConanSandbox/Binaries/Win64/ConanSandboxServer-Win64-Shipping.exe,
#     config dir Config/WindowsServer/. Same pattern as Empyrion / V Rising /
#     Sons of the Forest / Windrose.
# The container ships with both runtimes (Wine + UE5 Linux deps) so a single
# image can host either edition; entrypoint picks the launch mode from the
# EDITION config_value.
#
# Memory footprint: Enhanced UE5 runs ~2-4x heavier than UE4 Legacy on
# populated worlds. Defaults bumped accordingly (see `resources:` below).
#
# RCON is real Source-RCON on 25575/tcp — AMP's template, community
# servers, and the rcon plugin that ships with Conan all speak it. We
# surface it via our source_rcon adapter; the tracker's redial-on-EOF
# logic covers any idle disconnects (same fix landed for Palworld).
#
# Reference: https://github.com/CubeCoders/AMPTemplates (conan-exiles.kvp,
# conan-exilesports.json, conan-exilesupdates.json, conan-exilesconfig.json)
# Reference: https://low.ms/knowledgebase/conan-exiles-enhanced-update

id: conan-exiles
name: "Conan Exiles"
version: 0.3.0
authors:
  - panel contributors

supported_modes:
  - docker

runtime:
  docker:
    # Built locally from ./Dockerfile. Rebuild with:
    #   docker build -t panel-conan-exiles:latest modules/conan-exiles
    image: panel-conan-exiles:latest
    # Host networking -- container shares the host net namespace, so
    # any port the game/mod binds is directly on the LAN. AMP-like
    # model. Multi-instance safety comes from env-driven per-instance
    # ports (panel allocator + env: mapping on each ports entry).
    network_mode: host
    browseable_root: /game-saves
    browseable_roots:
      - name: "Saves & Configs"
        path: /game-saves
        hint: "ServerSettings.ini, Game.ini, ConanSandbox/Saved/ world data"
      - name: "Game Files"
        path: /game
        hint: "ConanSandbox/Binaries/Linux/ for Enhanced (UE5 native), ConanSandbox/Binaries/Win64/ for Legacy (UE4 under Wine). Engine/, ConanSandbox/Mods/ (Workshop pak drops). Workshop downloads land at /game/steamapps/workshop/content/440900/<id>/ via the shared volume mount."
    # Pre-declare every env key that any config_value below maps to. The
    # agent's resolve.go ONLY propagates config_values whose key is
    # already present in `docker.env` — keys not listed here get silently
    # filtered out. Defaults below are starting values for fresh instances;
    # operator edits in the panel's Config tab override per-instance.
    # Empty-string defaults mean "leave the INI alone" (entrypoint stamps
    # only non-empty values, so unset fields fall back to game defaults
    # or operator's manual ServerSettings.ini edits).
    env:
      # Steamworks client runtime init context — the dedicated-server app
      # (443030) reports itself to Steam as Conan Exiles (440900). AMP
      # sets this too; the server refuses to join the Steam master-server
      # listing without it.
      STEAM_APP_ID: "440900"

      # EDITION — see config_values block below. Default `enhanced` (UE5).
      # Pre-declared here so resolve.go propagates the operator's choice
      # into the container env (the agent's docker.env filter only forwards
      # config_values whose key already appears in this map). The entrypoint
      # logs it so operators can grep boot output to confirm what's running.
      EDITION: "enhanced"

      # --- Identity ---
      # All identity fields default to EMPTY. The entrypoint only stamps
      # non-empty values into [ServerSettings] of ServerSettings.ini, so a
      # recreate with empty env preserves whatever the operator set
      # previously via the Files tab. CLI-launch args have hardcoded
      # fallbacks in entrypoint.sh (e.g. ServerName falls back to
      # "panel Conan Exiles" for the Steam server-name flag only when
      # SERVER_NAME is genuinely unset).
      SERVER_NAME: ""
      SERVER_PASSWORD: ""
      ADMIN_PASSWORD: ""
      RCON_PASSWORD: ""
      MAX_PLAYERS: ""
      SERVER_REGION: ""
      MAX_NUDITY: ""
      SERVER_COMMUNITY: ""

      # --- Map ---
      # /Game/Maps/ConanSandbox/ConanSandbox             = Exiled Lands (default)
      # /Game/DLC_EXT/DLC_Siptah/Maps/DLC_Isle_of_Siptah = Isle of Siptah
      # SERVER_MAP keeps a default because Conan REQUIRES a map URL on the
      # CLI; empty would mean "use last used", which is fine but surprising
      # for new instances.
      SERVER_MAP: "/Game/Maps/ConanSandbox/ConanSandbox"
      CUSTOM_MAP: ""

      # --- Anti-cheat ---
      # Empty means "leave INI default" (entrypoint skips stamping). Conan's
      # game default is BattlEye=False, VAC=True — same as ours used to be.
      ENABLE_BATTLEYE: ""
      ENABLE_VAC: ""

      # --- Persistence / online ---
      PVP_ENABLED: ""
      AVATARS_ENABLED: ""
      CONTAINERS_IGNORE_OWNERSHIP: ""
      CAN_DAMAGE_PLAYER_OWNED_STRUCTURES: ""
      DYNAMIC_BUILDING_DAMAGE: ""

      # --- Gameplay multipliers (empty = use game default) ---
      PLAYER_XP_RATE: ""
      PLAYER_XP_KILL: ""
      PLAYER_XP_HARVEST: ""
      PLAYER_XP_CRAFT: ""
      PLAYER_XP_TIME: ""
      PLAYER_DAMAGE_MULTIPLIER: ""
      PLAYER_DAMAGE_TAKEN: ""
      PLAYER_HEALTH_REGEN: ""
      PLAYER_STAMINA_COST: ""
      PLAYER_SPRINT_SPEED: ""
      PLAYER_MOVEMENT_SPEED: ""
      PLAYER_ENCUMBRANCE: ""
      NPC_DAMAGE_MULTIPLIER: ""
      NPC_DAMAGE_TAKEN: ""
      NPC_HEALTH_MULTIPLIER: ""
      NPC_RESPAWN_MULTIPLIER: ""
      NPC_MAX_SPAWN_CAP: ""
      STRUCTURE_DAMAGE_MULTIPLIER: ""
      STRUCTURE_HEALTH_MULTIPLIER: ""
      LANDCLAIM_RADIUS_MULTIPLIER: ""
      AVATAR_DOME_DURATION: ""
      AVATAR_DOME_DAMAGE: ""
      FRIENDLY_FIRE_DAMAGE: ""

      # --- Pre-declared so allocator-derived ports survive resolve.go's filter. ---
      GAME_PORT: "7777"
      PINGER_PORT: "7778"
      QUERY_PORT: "27015"
      RCON_PORT: "25575"
    volumes:
      - { type: volume, name: "panel-$INSTANCE_ID-saves", container: "/game-saves" }
      - { type: volume, name: "panel-$INSTANCE_ID-game",  container: "/game" }
      - { target: "$DATA_PATH/logs", container: "/game-saves/logs" }
      # Shared Steam-Workshop content volume — exact same pattern as DayZ
      # (modules/dayz/module.yaml mounts panel-dayz-workshop at
      # /game/steamapps/workshop). Volume's root contains a flat
      # `content/<appid>/<id>/` tree (no extra prefix); mounting at
      # /game/steamapps/workshop makes it appear as
      #   /game/steamapps/workshop/content/440900/<id>/<file>.pak
      # — under the /game declared root, so FsList/FsSymlink work without
      # extra browseable_roots. The SteamCMD sidecar uses a different deep
      # path on its end (/conan-workshop/steamapps/workshop) so its
      # +force_install_dir matches; see conanmods.go's
      # runSteamCMDConanWorkshopDownloadOnceStreaming for the args. Both
      # the sidecar and the game container are talking to the same volume,
      # they just label the mount point differently.
      - { type: volume, name: "panel-conan-workshop",      container: "/game/steamapps/workshop" }

# Conan Exiles ports (matches AMP template):
#   7777/both (tcp+udp) — main game traffic + client mod downloads
#   7778/udp            — pinger (game+1 offset; server browser probe)
#   27015/udp           — Steam query
#   25575/tcp           — RCON (internal)
ports:
  # Conan binds the game socket on UDP `-Port=N` and the pinger
  # (server-browser probe) on UDP N+1 — internally derived, no override
  # flag exists. The panel allocator scans the agent window per-port; with
  # no prior holes the first three rows land on consecutive numbers
  # N, N+1, N+2 — so PINGER_PORT == GAME_PORT+1 and Conan's auto-derived
  # pinger lands on the row we forward.
  #
  # `game` MUST be UDP — Conan's game socket is UDP-only. (AMP's template
  # historically listed it as tcp+udp because of optional in-game mod
  # downloads, but the dedicated-server depot never opens a TCP listener
  # on -Port; the opnfwd orchestrator forwards by declared proto, so a
  # tcp row here ends up forwarding a dead TCP port and leaves the real
  # UDP game socket unforwarded — players can't join, server doesn't
  # register on the master server.)
  - { name: game,   proto: udp, default: 7777,  required: true,  env: GAME_PORT }
  - { name: pinger, proto: udp, default: 7778,  required: true,  env: PINGER_PORT }
  - { name: query,  proto: udp, default: 27015, required: true,  env: QUERY_PORT }
  - { name: rcon,   proto: tcp, default: 25575, internal: true,  env: RCON_PORT }

resources:
  # Bumped for Enhanced UE5 (was 6144 / 12288 for UE4). Funcom + community
  # benchmarks have populated Enhanced worlds at 2-4x the UE4 footprint —
  # 8 GB minimum keeps a small server alive, 24 GB recommended for a
  # populated long-running build-heavy server.
  min_ram_mb: 8192
  recommended_ram_mb: 24576

# Per-instance generated RCON secret — overrides env RCON_PASSWORD at
# create so every instance gets a unique password (see pkg/module
# GenerateSecrets). Operators can still override via the RCON Password
# config field.
secrets:
  - name: RCON_PASSWORD
    description: "Conan Exiles Source-RCON password — generated per instance"
    generated: true

rcon:
  adapter: source_rcon
  host_port: rcon
  auth: source_rcon_login
  password_secret: RCON_PASSWORD
  # Legacy fallback only (pre-generated-secret instances).
  password_literal: "panel_rcon"
  commands:
    # Conan's RCON commands. `listplayers` is the roster query; `broadcast`
    # sends an in-game message; `kick`/`ban` take a player # identifier.
    list: "listplayers"
    broadcast: "broadcast {msg}"
    kick: "kickplayer {player}"
    ban: "banplayer {player}"
    save: "saveworld"
    shutdown: "shutdown"

state_sources:
  - type: rcon
    command: "listplayers"
    every: 60s

# Log-line event patterns. AMP's template is the source of these regexes —
# tested against real Conan Exiles boot output. Go regex = RE2; convert
# `(?<name>...)` → `(?P<name>...)`.
events:
  join:
    pattern: 'LogNet: Join succeeded: (?P<name>.+?#\d+)'
    kind: join
  leave:
    pattern: 'LogNet: Player disconnected: (?P<name>.+?#\d+)'
    kind: leave
  chat:
    pattern: 'ChatWindow: Character (?P<name>.+?) said: (?P<msg>.+)'
    kind: chat

# Two update branches on the same appid. Operator picks via the Update
# button in the panel UI — `enhanced` is the default (UE5, public branch);
# `legacy` pins to UE4 via the `conan-exiles-legacy` Steam beta branch
# (Funcom's announced indefinite UE4 archive).
update_providers:
  # `id` is also the user-facing label in the UI (no separate label field on
  # this struct yet; see pkg/module/manifest.go UpdateProvider). Choose
  # short ids that read well in the Update dropdown.
  - id: enhanced
    kind: steamcmd
    app_id: "443030"
    platform: linux            # Enhanced (UE5) has a real Linux depot (443032, ~4.27 GiB) since 2026-05-05 — run native, no Wine.
    install_path: /game
    # Conan's `app_update 443030 validate` fails on empty volumes with
    # "Missing configuration" (4/4 retries). Plain `app_update 443030`
    # succeeds. Skip validate so first install just works; operators can
    # trigger a manual Update later to re-validate existing files.
    skip_validate: true
  - id: legacy
    kind: steamcmd
    app_id: "443030"
    beta: conan-exiles-legacy   # confirmed branch name via app_info_print 2026-05-07
    platform: windows          # Legacy (UE4) has NO Linux depot — this is the only remaining Wine path in the module.
    install_path: /game
    skip_validate: true

# UI-driven configuration. Each `key` here maps 1:1 to an env key declared
# above; the entrypoint reads non-empty env values and stamps them into
# [ServerSettings] in ServerSettings.ini at every boot (idempotent — same
# value re-stamped is a no-op). Empty strings mean "leave the INI alone";
# operator edits via the Files tab to ServerSettings.ini are preserved for
# any field whose corresponding config_value is left blank.
#
# The most-tuned fields are at the top (no advanced flag); niche / tuning
# multipliers are tagged `advanced: true` so the basic Config tab stays
# scannable on a phone. AMP's official template surfaces ~11 fields; we
# expose a superset (~30) covering identity, anti-cheat, PvP, and the
# common gameplay knobs — beyond that, edit ServerSettings.ini directly.
config_values:
  # ===== Edition (UE4 vs UE5) =====
  # Pick at create time. Drives:
  #   1. Which Steam branch downloads on first install (auto-update picks
  #      the matching update_provider via dispatch.go's edition-aware
  #      auto-update logic — `enhanced` → public branch, `legacy` →
  #      `conan-exiles-legacy` Steam beta).
  #   2. Which Workshop mods the panel's Mods tab surfaces — Steam tags
  #      every published item as either "Enhanced" or "Legacy"; the panel's
  #      workshop search adds `requiredtags[]=Enhanced|Legacy` per the
  #      instance's edition. UE5 servers don't see UE4-only mods (they
  #      load silently as compatible-format files but don't actually run
  #      because the .pak ABI changed).
  # Operators changing edition after creation: change this value, then
  # click Update (which now uses the matching branch). Existing world
  # data is preserved.
  - key: EDITION
    label: "Edition"
    description: "Conan Exiles version. Enhanced (UE5, 2026-05+) is Funcom's current default. Legacy (UE4) is the indefinitely-archived prior build — pick this only if your players need UE4 mods that haven't been ported yet. Switching after creation requires clicking Update; saves are preserved across editions."
    default: "enhanced"
    options:
      - { value: "enhanced", label: "Enhanced (UE5, default)" }
      - { value: "legacy",   label: "Legacy (UE4 — Steam beta branch)" }

  # ===== Identity =====
  - key: SERVER_NAME
    label: "Server Name"
    description: "Name shown in the server browser and steam community list."
    default: "panel Conan Exiles"
  - key: SERVER_PASSWORD
    label: "Server Password"
    description: "Password required to join the server. Leave blank for an open server."
    default: ""
  - key: ADMIN_PASSWORD
    label: "Admin Password"
    description: "Lets the operator open the in-game admin panel via 'Make Me Admin' on a logged-in client. Stamped into [ServerSettings] AdminPassword= on every restart. Leave blank to leave the existing INI value alone."
    default: ""
  - key: RCON_PASSWORD
    label: "RCON Password"
    description: "Source-RCON password used by the panel for player listing, broadcast, kick, ban, and saveworld. Avoid spaces or quotes. Leave blank to use the per-instance generated secret (recommended)."
    default: ""
  - key: MAX_PLAYERS
    label: "Max Players"
    description: "Player slot limit. Conan's official cap is 70; communities run 40-60 for most build densities."
    default: "40"
  - key: SERVER_REGION
    label: "Server Region"
    description: "Region tag used for server-browser filtering. Picks up the client's regional preferences."
    default: "1"
    options:
      - { value: "0", label: "Europe" }
      - { value: "1", label: "North America" }
      - { value: "2", label: "Asia" }
      - { value: "3", label: "Australia" }
      - { value: "4", label: "South America" }
      - { value: "5", label: "Japan" }

  # ===== Map =====
  - key: SERVER_MAP
    label: "Map"
    description: "Map loaded at server start. For a custom map, choose 'Custom' and fill in the path below."
    default: "/Game/Maps/ConanSandbox/ConanSandbox"
    options:
      - { value: "/Game/Maps/ConanSandbox/ConanSandbox",            label: "Exiled Lands (default)" }
      - { value: "/Game/DLC_EXT/DLC_Siptah/Maps/DLC_Isle_of_Siptah", label: "Isle of Siptah" }
      - { value: "{{CUSTOM_MAP}}",                                  label: "Custom (use Custom Map below)" }
  - key: CUSTOM_MAP
    label: "Custom Map Path"
    description: "Path to a custom map's UE asset. Only used when Map is set to 'Custom'. Example: /Game/Mods/Savage_Wilds/Savage_Wilds"
    default: ""

  # ===== Anti-cheat =====
  - key: ENABLE_BATTLEYE
    label: "Enable BattlEye"
    description: "BattlEye anti-cheat. Off by default — works under Wine but adds a startup handshake that occasionally races on cold boots."
    default: "False"
    options:
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }
  - key: ENABLE_VAC
    label: "Enable Valve Anti-Cheat"
    description: "Steam VAC. Generally safe to leave on for public servers."
    default: "True"
    options:
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }

  # ===== PvP / world =====
  - key: PVP_ENABLED
    label: "PvP Enabled"
    description: "Master toggle for player-vs-player damage. Time-window restrictions are configured in ServerSettings.ini directly (PVPRestrict* keys); this just flips the global switch."
    default: ""
    options:
      - { value: "",      label: "(leave INI default)" }
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }
  - key: AVATARS_ENABLED
    label: "Avatars Enabled"
    description: "Allow players to summon god avatars. Off in many private cluster setups to prevent griefing."
    default: ""
    options:
      - { value: "",      label: "(leave INI default)" }
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }
  - key: CONTAINERS_IGNORE_OWNERSHIP
    label: "Containers Ignore Ownership"
    description: "If true, players can open any container regardless of owner. Useful for co-op-friendly servers; disable for strict PvP."
    default: ""
    options:
      - { value: "",      label: "(leave INI default)" }
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }
  - key: CAN_DAMAGE_PLAYER_OWNED_STRUCTURES
    label: "Allow Building Damage"
    description: "Master toggle for player-owned structure damage. Combine with the dynamic decay setting for a softer PvP feel."
    default: ""
    options:
      - { value: "",      label: "(leave INI default)" }
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }
  - key: DYNAMIC_BUILDING_DAMAGE
    label: "Dynamic Building Damage"
    description: "Decays vacant structures over time. Helps clean up abandoned bases on long-running servers."
    default: ""
    options:
      - { value: "",      label: "(leave INI default)" }
      - { value: "True",  label: "On" }
      - { value: "False", label: "Off" }
  - key: FRIENDLY_FIRE_DAMAGE
    label: "Friendly Fire Damage Multiplier"
    description: "0.0–1.0+ multiplier on damage between clan members. Default 0.25."
    default: ""

  # ===== Niche knobs (advanced) =====
  - key: MAX_NUDITY
    label: "Max Nudity"
    description: "0=none, 1=partial, 2=full. Affects the default character render plus what skins players can wear."
    default: "0"
    advanced: true
    options:
      - { value: "0", label: "None" }
      - { value: "1", label: "Partial" }
      - { value: "2", label: "Full" }
  - key: SERVER_COMMUNITY
    label: "Server Community"
    description: "0=Purist, 1=Relaxed, 2=Hard, 3=Hardcore, 4=Roleplaying, 5=Experimental. Just a tag in the server browser."
    default: "0"
    advanced: true
    options:
      - { value: "0", label: "Purist" }
      - { value: "1", label: "Relaxed" }
      - { value: "2", label: "Hard" }
      - { value: "3", label: "Hardcore" }
      - { value: "4", label: "Roleplaying" }
      - { value: "5", label: "Experimental" }

  # ===== XP rates (advanced) =====
  - key: PLAYER_XP_RATE
    label: "XP — Overall Rate Multiplier"
    description: "Overall multiplier applied on top of every XP source. Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_XP_KILL
    label: "XP — Kill Multiplier"
    description: "XP multiplier per kill. Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_XP_HARVEST
    label: "XP — Harvest Multiplier"
    description: "XP multiplier per harvest tick. Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_XP_CRAFT
    label: "XP — Craft Multiplier"
    description: "XP multiplier per crafted item. Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_XP_TIME
    label: "XP — Time Multiplier"
    description: "Idle / time-based XP. Default 1.0."
    default: ""
    advanced: true

  # ===== Player tuning (advanced) =====
  - key: PLAYER_DAMAGE_MULTIPLIER
    label: "Player Damage Output"
    description: "Damage dealt by players. Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_DAMAGE_TAKEN
    label: "Player Damage Taken"
    description: "Damage received by players. Lower = tougher players."
    default: ""
    advanced: true
  - key: PLAYER_HEALTH_REGEN
    label: "Player Health Regen Speed"
    description: "Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_STAMINA_COST
    label: "Player Stamina Cost"
    description: "Multiplier on stamina drained by sprint/jump/swim. Lower = forgiving."
    default: ""
    advanced: true
  - key: PLAYER_SPRINT_SPEED
    label: "Player Sprint Speed Scale"
    description: "Default 1.0. Bumping past 1.5 makes ranged combat awkward."
    default: ""
    advanced: true
  - key: PLAYER_MOVEMENT_SPEED
    label: "Player Movement Speed Scale"
    description: "Default 1.0."
    default: ""
    advanced: true
  - key: PLAYER_ENCUMBRANCE
    label: "Player Encumbrance Multiplier"
    description: "Carry weight cap multiplier. Default 1.0."
    default: ""
    advanced: true

  # ===== NPC tuning (advanced) =====
  - key: NPC_DAMAGE_MULTIPLIER
    label: "NPC Damage Output"
    description: "Damage dealt by hostile NPCs. Default 1.0."
    default: ""
    advanced: true
  - key: NPC_DAMAGE_TAKEN
    label: "NPC Damage Taken"
    description: "Damage NPCs receive. Lower = bullet-sponge enemies."
    default: ""
    advanced: true
  - key: NPC_HEALTH_MULTIPLIER
    label: "NPC Health Multiplier"
    description: "Base HP scaling. Default 1.0."
    default: ""
    advanced: true
  - key: NPC_RESPAWN_MULTIPLIER
    label: "NPC Respawn Multiplier"
    description: "How fast world spawns refill. >1.0 = faster respawn."
    default: ""
    advanced: true
  - key: NPC_MAX_SPAWN_CAP
    label: "NPC Max Spawn Cap Multiplier"
    description: "Cap on simultaneous active world NPCs. Lower = less server CPU under load."
    default: ""
    advanced: true

  # ===== Structures / land claim (advanced) =====
  - key: STRUCTURE_DAMAGE_MULTIPLIER
    label: "Structure Damage Multiplier"
    description: "Damage dealt to player-built structures. Default 1.0."
    default: ""
    advanced: true
  - key: STRUCTURE_HEALTH_MULTIPLIER
    label: "Structure Health Multiplier"
    description: "Base HP of placed pieces. >1.0 = sturdier bases."
    default: ""
    advanced: true
  - key: LANDCLAIM_RADIUS_MULTIPLIER
    label: "Landclaim Radius Multiplier"
    description: "How big the no-build bubble around each placeable is. <1.0 lets settlements pack tighter."
    default: ""
    advanced: true
  - key: AVATAR_DOME_DURATION
    label: "Avatar Dome Duration Multiplier"
    description: "How long avatar shields hold. Default 1.0."
    default: ""
    advanced: true
  - key: AVATAR_DOME_DAMAGE
    label: "Avatar Dome Damage Multiplier"
    description: "Damage vs. avatar shields. Default 1.0."
    default: ""
    advanced: true

# --- Manifest-driven UI metadata (WI-07) --------------------------
# Consumed by the CONTROLLER/dashboard only (the controller loads its
# own ./modules copy at startup); the agent ignores these at runtime.
# ready_pattern is a JavaScript regex (slash-delimited when it needs
# flags) moved verbatim from the dashboard's READY_PATTERNS map.
ready_pattern: '/LogGameState: Match State Changed from WaitingToStart to InProgress/i'
appearance:
  emoji: "⚔️"
  grad: "linear-gradient(135deg,#5a1a00,#c2410c)"
  art: "/game-art/conan-exiles.jpg"
  steam: "440900"
