namespace Theriapolis.Core; public static class C { // World map (the persistent continental grid) public const int WORLD_WIDTH_TILES = 256; public const int WORLD_HEIGHT_TILES = 256; public const int WORLD_TILE_PIXELS = 32; // px per world tile at 1:1 zoom // Macro template (authored skeleton) public const int MACRO_GRID_WIDTH = 32; public const int MACRO_GRID_HEIGHT = 32; // => each macro cell covers WORLD_WIDTH_TILES/MACRO_GRID_WIDTH world tiles // (currently 256/32 = 8 tiles per cell) // Tactical map (streamed) public const int TACTICAL_PER_WORLD_TILE = 32; // 1 tactical tile == 1 world pixel public const int TACTICAL_CHUNK_SIZE = 64; // tactical tiles per chunk side public const int TACTICAL_WINDOW_WORLD_TILES = 3; // 3x3 world tiles kept live // Generation public const int WORLDGEN_BUDGET_SECONDS = 60; // Border distortion — coastal domain-warp band (tiles deep on each side of land/ocean boundary) public const int COAST_BAND_WIDTH = 12; // ElevationGen — continent mask domain-warp amplitude (tiles). // Displaces the ellipse falloff coordinates by up to this many tiles, // creating organic coastal excursions instead of a smooth ellipse edge. public const float COAST_WARP_AMP = 45f; // ElevationGen — macro-cell border warp (Addendum A §1 primary mechanism). // Displaces the per-tile macro-cell lookup position by a smooth noise // field so macro cell boundaries become wiggly curves instead of // grid-aligned lines. MACRO_WARP_AMPLITUDE is the maximum displacement // in tiles; MACRO_WARP_FREQUENCY is cycles per tile of the warp noise. // With amplitude 24 and frequency 0.012 (period ≈ 83 tiles), macro cell // boundaries wobble by up to ~¾ of a cell's width over multi-cell scales, // which (combined with the soft macro clamp in ElevationGenStage) is // enough to dissolve the grid-aligned rectangular coastlines that the // hard clamp used to produce at mountain/ocean interfaces. public const float MACRO_WARP_AMPLITUDE = 24f; public const float MACRO_WARP_FREQUENCY = 0.012f; // RNG sub-stream offset for continent-mask domain warp (must not collide with others) public const ulong RNG_COAST_WARP = 0xC4571UL; // RNG sub-stream offset for macro-cell border warp public const ulong RNG_MACRO_WARP = 0xA1B2C3D4UL; // WaterBodyClamp — minimum ocean tiles between continent and map edge public const int OCEAN_BORDER_WIDTH = 2; // RNG sub-stream offsets (named, never collide) public const ulong RNG_TERRAIN = 0x7E22A11UL; public const ulong RNG_MOISTURE = 0xDEADBEEFUL; public const ulong RNG_TEMP = 0x7E39UL; public const ulong RNG_BORDER = 0xB07DE5UL; public const ulong RNG_COAST = 0xC0A57UL; public const ulong RNG_HYDRO = 0xD7A14A6EUL; public const ulong RNG_SETTLE = 0x5E771EUL; public const ulong RNG_ROAD = 0x7047EUL; public const ulong RNG_RAIL = 0x7A11UL; public const ulong RNG_FACTION = 0xFAC71074UL; public const ulong RNG_POI = 0x901F1UL; public const ulong RNG_WEATHER = 0x4EA7EUL; public const ulong RNG_TACTICAL = 0x7AC71CA1UL; // ── Phase 2+3: Additional RNG sub-streams ────────────────────────────── public const ulong RNG_LAKE = 0x1A4EUL; public const ulong RNG_MEANDER = 0xE41DE7UL; public const ulong RNG_HABITAT = 0x4AB17A7UL; public const ulong RNG_ANCHOR = 0xA1C407UL; public const ulong RNG_SETTLE_ATTR = 0x5A774UL; public const ulong RNG_TRADE = 0x74ADE5UL; public const ulong RNG_ENCOUNTER = 0xE1C017UL; // ── Phase 2: Hydrology ───────────────────────────────────────────────── public const int RIVER_MIN_FLOW_ACCUM = 1500; // tiles of upstream catchment to become a river public const int RIVER_MAX_COUNT = 150; // hard cap on total rivers to prevent perf explosion public const int RIVER_MAJOR_THRESHOLD = 8000; // flow accumulation for "major river" public const int RIVER_MODERATE_THRESHOLD = 2000; // flow accumulation for "river" (vs stream) public const float RIVER_CARVE_DEPTH = 0.02f; // elevation reduction along river paths public const int LAKE_MIN_AREA = 12; // tiles; smaller basins stay dry public const float MEANDER_AMP_FLAT = 5f; // max world-pixel lateral offset on plains public const float MEANDER_AMP_MOUNTAIN = 1.5f; // max world-pixel lateral offset in mountains public const float MEANDER_FREQ = 0.08f; // noise frequency for meander offset public const int SPLINE_SUBDIVISIONS = 4; // Catmull-Rom subdivisions per control point public const float RDP_TOLERANCE = 2.0f; // Ramer-Douglas-Peucker LOD tolerance (world px) // ── Phase 3: Settlements ─────────────────────────────────────────────── public const int SETTLE_TIER1_COUNT = 1; public const int SETTLE_TIER2_MIN = 4; public const int SETTLE_TIER2_MAX = 6; public const int SETTLE_TIER3_MIN = 15; public const int SETTLE_TIER3_MAX = 25; public const int SETTLE_TIER4_MIN = 40; public const int SETTLE_TIER4_MAX = 80; public const int SETTLE_TIER5_MIN = 100; public const int SETTLE_TIER5_MAX = 200; // Tile-denominated minimum separations. Halved from their 512×512 baseline // (was 120/60/20/8/5 with ANCHOR_MIN_DIST=80) to densify the 256×256 map — // at the original spacing, ~60% of the map ran out of room for settlements // and Thornfield's constraint region often became unsatisfiable. Preserves // the SETTLE_TIER*_MIN/MAX counts, so the smaller world packs the same // target settlement population more tightly. public const int SETTLE_MIN_DIST_TIER1 = 60; public const int SETTLE_MIN_DIST_TIER2 = 30; public const int SETTLE_MIN_DIST_TIER3 = 10; public const int SETTLE_MIN_DIST_TIER4 = 4; public const int SETTLE_MIN_DIST_TIER5 = 3; public const float ANCHOR_MIN_DIST = 40f; // ── Phase 3: Infrastructure ──────────────────────────────────────────── public const float ROAD_SHORTCUT_FRACTION = 0.30f; public const float BRIDGE_COST = 50f; public const float CROSSING_COST = 20f; public const float SETBACK_COST_SCALE = 8f; public const int SETBACK_DISTANCE = 4; public const float RAIL_BRIDGE_COST = 80f; // Feature gate for the entire rail subsystem. When false, RailNetworkGenStage // early-returns, world.Rails stays empty, HasRail/RailroadAdjacent/RailDir // are never set, and no settlement gets HasRailStation = true. Downstream // consumers (road costs, cleanup, trade routes, rendering) all handle an // empty rail list gracefully, so this flag is a safe on/off switch. // static readonly (not const) so the guard evaluates at runtime — avoids // CS0162 unreachable-code warnings and prevents stale inlining in // downstream assemblies. public static readonly bool ENABLE_RAIL = false; // Max deflection angle (degrees) allowed at any vertex of a rail tile path. // Heavy rail cars can't corner sharply, so the rail pipeline elides // vertices whose turn exceeds this cap when a passable shortcut exists. // 45° grid moves give turns of 0°, 45°, 90°, 135° — 75° permits only the // first two and forces 90°/135° corners to be smoothed. public const float MAX_RAIL_TURN_DEGREES = 75f; public const float EXISTING_ROAD_COST = 0.1f; // cost to travel an existing road tile (vs ~3–10 for new terrain) public const float EXISTING_RAIL_COST = 0.1f; // cost to travel an existing rail tile public const int SETTLEMENT_HALO_RADIUS = 1; // tiles: no existing-road/rail discount within this Chebyshev distance of path endpoints (prevents fan convergence) public const float SETTLEMENT_CONNECT_DIST = 64f; // world pixels (~2 tiles): max endpoint distance for a settlement to count as visually connected public const float BRIDGE_DECK_HALF_LENGTH = 10f; // world pixels walked along road from crossing to place deck ends // ── Phase 3: Polyline Cleanup ───────────────────────────────────────── public const float POLYLINE_SNAP_ENDPOINT_DIST = 160f; // world pixels (~5 tiles): cluster nearby endpoints public const float POLYLINE_SNAP_BODY_DIST = 128f; // world pixels (~4 tiles): snap endpoint to polyline body (T-junction) public const float POLYLINE_MERGE_DIST = 80f; // world pixels (~2.5 tiles): merge parallel overlapping segments public const int POLYLINE_MAX_TRIM_POINTS = 20; // max points to search when trimming overshoots // ── Phase 3: Factions ────────────────────────────────────────────────── public const float FACTION_INFLUENCE_RADIUS = 60f; public const float FACTION_DECAY_RATE = 0.015f; // ── Phase 3: PoIs ────────────────────────────────────────────────────── public const int POI_MIN_DIST_FROM_SETTLE = 6; public const int POI_MIN_DIST_FROM_POI = 4; // ── Phase 4: Tactical streaming ──────────────────────────────────────── // Sub-streams of RNG_TACTICAL for the deterministic chunk passes. Each // chunk gen pass uses ForSubsystem(worldSeed ^ subStream ^ chunkHash) // so adjacent chunks see independent random scatters. public const ulong RNG_TACTICAL_GROUND = 0x7AC71C01UL; public const ulong RNG_TACTICAL_SCATTER = 0x7AC71C02UL; public const ulong RNG_TACTICAL_SPAWN = 0x7AC71C03UL; // Chunk cache size. The 3×3 world-tile window typically touches at most // 9 chunks (each chunk = 2×2 world tiles at 32 tactical-per-world / 64 chunk side). // 16 gives a little slack so the player crossing a tile boundary doesn't // immediately evict + re-generate a chunk that was just visible. public const int CHUNK_CACHE_SOFT_MAX = 16; // ── Phase 4: Actor + clock ───────────────────────────────────────────── // Travel time on the world map. With WORLD_TILE_PIXELS=32 and a road, // this is 8 * 0.5 = 4 in-game seconds per world pixel ≈ 128 sec/tile. // 256-tile world ≈ 32_768 sec across (~9h) on roads, ~18h cross-country. public const float BASE_SEC_PER_WORLD_PIXEL = 8f; public const float ROAD_SPEED_MULT = 0.5f; public const int TACTICAL_STEP_SECONDS = 10; public const ulong RNG_ACTOR_ID = 0xAC704DUL; // World-pixel travel speed for the player on the world map (pixels per // second of real time). Independent of the in-game clock advancement; // this just controls how fast the sprite slides along the path. public const float PLAYER_TRAVEL_PX_PER_SEC = 80f; // Tactical-mode WASD movement speed in tactical tiles per real second. // Continuous (sub-pixel) motion replaces the discrete one-tile step that // looked chunky at high zoom — at CAMERA_MAX_ZOOM=16, a 1-tile jump was // a visible 16-screen-pixel hop; now the player slides smoothly. // 40 px/sec is brisk-walk pace: at zoom 16 that's 640 screen px/sec // (half the window width per second), at zoom 3 (tactical threshold) // it's 120 screen px/sec. public const float TACTICAL_PLAYER_PX_PER_SEC = 3f; // ── Phase 4: Save (bumped to v8 in Phase 7 M0) ──────────────────────── public const int SAVE_SCHEMA_VERSION = 8; public const int SAVE_SLOT_COUNT = 10; /// /// Minimum readable save version. Below this, the loader refuses with /// "this save predates Phase 5 — start a new game from the same seed". /// Bump only when older saves can no longer be migrated meaningfully. /// v5 is still accepted (Phase 6 M2 adds an additive V5→V6 migration). /// public const int SAVE_SCHEMA_MIN_VERSION = 5; // ── Phase 4: Player marker ───────────────────────────────────────────── // Player marker diameter, in *screen* pixels. The sprite renderer // counter-scales by 1/Zoom so the marker stays this size at every zoom // level — visible-but-not-huge on the world map, and not screen-filling // when the player walks around in tactical at CAMERA_MAX_ZOOM. public const int PLAYER_MARKER_SCREEN_PX = 48; // ── Phase 4: Camera / zoom ───────────────────────────────────────────── // Zoom = screen pixels per world pixel. At Zoom=1, one tactical tile is // one screen pixel; world tiles (32 world px wide) span 32 screen pixels. // // CAMERA_TACTICAL_THRESHOLD = 32 — tactical kicks in exactly when each // tactical tile maps to TACTICAL_TILE_SPRITE_PX screen pixels, so the // 32×32 sprite art renders 1:1 at the threshold and upscales smoothly // up to CAMERA_MAX_ZOOM (3.125× upscale at full zoom). // // CAMERA_MAX_ZOOM = 100 — at 1280px window that's ~12.8 tactical tiles // visible across the screen at max zoom: comfortable for inspecting an // individual building or NPC. public const float CAMERA_MIN_ZOOM = 0.01f; public const float CAMERA_MAX_ZOOM = 100.0f; public const float CAMERA_TACTICAL_THRESHOLD = 32.0f; // ── Phase 4: Tactical art ────────────────────────────────────────────── // Source resolution of every tactical tile sprite (surface + decoration). // The renderer scales each 32×32 source down to a 1×1 world-pixel cell so // the existing coord system ("1 tactical tile = 1 world pixel") stays // intact. With CAMERA_TACTICAL_THRESHOLD = 32, the sprite displays at // native 1:1 the moment tactical mode engages. public const int TACTICAL_TILE_SPRITE_PX = 32; // ── Phase 5: RNG sub-streams ─────────────────────────────────────────── public const ulong RNG_CHARACTER = 0xC4A2AC7EUL; // character creation rolls + starting equipment public const ulong RNG_STAT_ROLL = 0x57A7507UL; // 4d6-drop-lowest re-rolls in char creation public const ulong RNG_COMBAT = 0xC0B47UL; // per-encounter dice public const ulong RNG_NPC_SPAWN = 0xA7C2AUL; // per-NPC variation when instantiating from chunk spawn list public const ulong RNG_LOOT = 0x107EUL; // post-encounter drops // ── Phase 5: Encounter triggering ───────────────────────────────────── // Hostile NPCs auto-trigger combat on LOS within this radius. public const int ENCOUNTER_TRIGGER_TILES = 8; // Friendly / Neutral NPCs show "[F] Talk to ..." prompt within this radius. public const int INTERACT_PROMPT_TILES = 2; // Encounter ends when all hostiles are out of sight + this far for this many turns. public const int ENCOUNTER_DISENGAGE_TILES = 16; public const int ENCOUNTER_DISENGAGE_TURNS = 3; // ── Phase 5: Combat resolver ────────────────────────────────────────── public const int AC_FLOOR = 5; public const int AC_CEILING = 30; public const int HP_MAX = 999; public const int DEATH_SAVES_TO_DIE = 3; public const int DEATH_SAVES_TO_STABLE = 3; public const int CRIT_NATURAL = 20; // ── Phase 5: Encumbrance ────────────────────────────────────────────── public const float ENCUMBRANCE_SOFT_MULT = 1.0f; // ≥1.0× capacity → speed -10ft public const float ENCUMBRANCE_HARD_MULT = 1.5f; // ≥1.5× capacity → speed halved + disadvantage // ── Phase 5: Difficulty scaling (danger zones) ──────────────────────── // Per-chunk DangerZone (0..4) drives which template each SpawnKind picks. public const int DANGER_DIST_FROM_START_PER_ZONE = 50; // tiles per +1 zone increment public const int DANGER_DIST_FROM_ROAD_THRESHOLD = 8; // further than this = +1 zone public const int DANGER_DIST_FROM_SETTLE_THRESHOLD = 16; // further than this = +1 zone public const int DANGER_ZONE_MIN = 0; public const int DANGER_ZONE_MAX = 4; // ── Phase 5: Save (will bump SAVE_SCHEMA_VERSION to 5 in M2) ────────── public const string SAVE_SLOT_AUTOSAVE_COMBAT = "autosave_combat"; // ── Phase 6 M0: Settlement stamping ─────────────────────────────────── /// SeededRng sub-stream for procedural Tier 2–5 layout rolls. public const ulong RNG_BUILDING_LAYOUT = 0xB1D106UL; /// Smallest acceptable footprint dimension for a building template. public const int BUILDING_MIN_W_TILES = 4; public const int BUILDING_MIN_H_TILES = 3; /// Don't stamp scatter or walls within this halo of any door tile. public const int BUILDING_DOOR_HALO_TILES = 2; /// Minimum gap (in tiles) between adjacent procedural buildings. public const int SETTLEMENT_BUILDING_GAP_MIN = 2; // ── Phase 6 M2: Reputation ──────────────────────────────────────────── /// Minimum / maximum reputation value (per-faction and per-NPC personal disposition). public const int REP_MIN = -100; public const int REP_MAX = 100; // Disposition tier thresholds — *inclusive lower bound* of each band so // a single ascending if-cascade picks them out via `score >= threshold`. // Per reputation.md §I-2 the bands are -100..-76 Nemesis, -75..-51 // Hostile, -50..-26 Antagonistic, -25..-1 Unfriendly, 0 Neutral, // +1..+25 Favorable, +26..+50 Friendly, +51..+75 Allied, +76..+100 Champion. public const int REP_HOSTILE_THRESHOLD = -75; public const int REP_ANTAGONISTIC_THRESHOLD = -50; public const int REP_UNFRIENDLY_THRESHOLD = -25; public const int REP_FAVORABLE_THRESHOLD = 1; public const int REP_FRIENDLY_THRESHOLD = 26; public const int REP_ALLIED_THRESHOLD = 51; public const int REP_CHAMPION_THRESHOLD = 76; // ── Phase 6 M5: Reputation propagation ──────────────────────────────── /// SeededRng sub-stream for propagation coin-flips (frontier delivery). public const ulong RNG_REP_PROPAGATION = 0x1EFA77UL; // Distance-band radii in world tiles. Zero (origin) = full magnitude. public const int REP_ADJACENT_DIST_TILES = 20; public const int REP_REGIONAL_DIST_TILES = 60; public const int REP_CONTINENTAL_DIST_TILES = 200; // Decay multipliers per band, expressed as integer percentages. public const int REP_DECAY_AT_ORIGIN_PCT = 100; public const int REP_DECAY_ADJACENT_PCT = 80; public const int REP_DECAY_REGIONAL_PCT = 60; public const int REP_DECAY_CONTINENTAL_PCT = 40; public const int REP_DECAY_FRONTIER_PCT = 20; /// Coin-flip probability that a frontier settlement actually receives the news at all. public const int REP_FRONTIER_DELIVERY_PROB_PCT = 50; /// /// Magnitudes at or above this threshold (positive or negative) bypass /// distance decay AND frontier coin-flips: NEMESIS / CHAMPION events /// propagate at full magnitude, continent-wide, immediately. Per /// reputation.md §I-2. /// public const int REP_EXTREME_BYPASS_MAGNITUDE = 50; // ── Phase 6 M3: Dialogue ────────────────────────────────────────────── /// SeededRng sub-stream for in-dialogue skill-check rolls. public const ulong RNG_DIALOGUE = 0xD1A106EUL; /// Cap on options shown per dialogue node. public const int DIALOGUE_MAX_OPTIONS_PER_NODE = 6; /// Lines of scrollback retained inside the dialogue panel. public const int DIALOGUE_HISTORY_LINES = 50; // ── Phase 6 M4: Quests ──────────────────────────────────────────────── /// SeededRng sub-stream for quest-step random outcomes. public const ulong RNG_QUEST = 0x9E57E0UL; /// Sanity cap on simultaneously active quests. public const int QUEST_MAX_ACTIVE = 20; /// Cap on completed-quest history shown in the journal. public const int QUEST_LOG_COMPLETED_LIMIT = 100; /// Tile radius for "enter_anchor" quest triggers (matches plan §4.4). public const int QUEST_ENTER_ANCHOR_RADIUS_TILES = 4; /// Tile radius for "enter_role_proximity" quest triggers. public const int QUEST_ENTER_ROLE_RADIUS_TILES = 2; // ── Phase 6.5 M0: Levelling ─────────────────────────────────────────── /// SeededRng sub-stream for HP rolls and other per-level random outcomes. public const ulong RNG_LEVELUP = 0x1E7E107UL; /// /// Levels that grant an Ability Score Improvement choice (player picks /// +2 to one stat or +1 to two stats). Standard d20 schedule. /// The XP-to-next-level table itself lives in /// (canonical, 1-indexed). /// public static readonly int[] ASI_LEVELS = new[] { 4, 8, 12, 16, 19 }; /// Level at which the player picks a subclass. public const int SUBCLASS_SELECTION_LEVEL = 3; /// Hard cap on ability scores below level 20. public const int ABILITY_SCORE_CAP_PRE_L20 = 20; /// Hard cap on ability scores at level 20 (a couple of capstone features push this). public const int ABILITY_SCORE_CAP_AT_L20 = 22; /// /// Maximum supported character level. Phase 6.5 wires the engine for /// 1..20 but only ships full mechanical effect for levels 1..15; /// levels 16..20 use the same machinery but feature-defs may stub. /// public const int CHARACTER_LEVEL_MAX = 20; // ── Phase 6.5 M5: Hybrid passing detection ──────────────────────────── /// SeededRng sub-stream for hybrid scent-detection rolls. public const ulong RNG_PASSING = 0x9A55E5UL; /// WIS save DC the NPC rolls to detect a hybrid PC's scent. public const int HYBRID_DETECTION_DC = 12; /// /// CHA Deception DC the PC's "I'm passing" counter-roll uses. Standard /// case: equal to the NPC's detection DC. theriapolis-rpg-clades.md /// notes a stricter DC for an even split — Phase 6.5 simplification: /// dominant lineage is always considered ≥ 80% expressive (the player /// chooses dominance and accepts that consequence). /// public const int HYBRID_DECEPTION_DC = 12; // ── Phase 6.5 M7: Betrayal cascade magnitudes ───────────────────────── /// /// Threshold magnitude (inclusive) for a "minor" betrayal. Drives the /// cascade tier in — /// any with /// magnitude < 0 picks the most severe matching tier /// (most-negative wins). /// public const int BETRAYAL_MAGNITUDE_MINOR = -10; public const int BETRAYAL_MAGNITUDE_MODERATE = -25; public const int BETRAYAL_MAGNITUDE_MAJOR = -50; public const int BETRAYAL_MAGNITUDE_CRITICAL = -75; /// Faction-standing impact at each betrayal tier (signed; cascade through opposition). public const int BETRAYAL_FACTION_DELTA_MINOR = -5; public const int BETRAYAL_FACTION_DELTA_MODERATE = -15; public const int BETRAYAL_FACTION_DELTA_MAJOR = -30; public const int BETRAYAL_FACTION_DELTA_CRITICAL = -50; // ── Phase 7: RNG sub-streams ────────────────────────────────────────── /// Per-PoI dungeon room-graph generation: room count, branching, special-room slots. public const ulong RNG_DUNGEON_LAYOUT = 0xD06E07AUL; /// Within a layout, picking which room template fills each role-eligible slot. public const ulong RNG_ROOM_PICK = 0x40072EUL; /// Per-room spawn selection (which NPC template fills each encounter slot). public const ulong RNG_DUNGEON_POPULATE = 0x70757UL; /// Per-container loot rolls. Distinct from (encounter drops). public const ulong RNG_DUNGEON_LOOT = 0xD0717EUL; // ── Phase 7: Dungeon generation ─────────────────────────────────────── public const int DUNGEON_SMALL_ROOMS_MIN = 3; public const int DUNGEON_SMALL_ROOMS_MAX = 5; public const int DUNGEON_MED_ROOMS_MIN = 6; public const int DUNGEON_MED_ROOMS_MAX = 10; public const int DUNGEON_LARGE_ROOMS_MIN = 11; public const int DUNGEON_LARGE_ROOMS_MAX = 20; /// Reject-and-retry ceiling on the layout-builder. Beyond this we fall back to a guaranteed-valid linear layout. public const int DUNGEON_LAYOUT_MAX_ATTEMPTS = 8; /// Rooms snap their AABB to a multiple of this many tactical tiles. public const int ROOM_GRID_SNAP_TILES = 16; public const int ROOM_CORRIDOR_MIN_W = 2; public const int ROOM_CORRIDOR_MAX_W = 3; /// Minimum gap (in tiles) between adjacent rooms before a corridor is required. public const int ROOM_INTER_ROOM_GAP_TILES = 2; /// Tactical-tile padding around the room-AABB union when sizing the dungeon's tile array. public const int DUNGEON_AABB_PADDING = 8; // ── Phase 7: Loot band selection (PoI LevelBand → loot table tier) ──── public const float LOOT_TABLE_BAND_T1_THRESHOLD = 0.0f; // level band 0-1 → t1 public const float LOOT_TABLE_BAND_T2_THRESHOLD = 2.0f; // level band 2 → t2 public const float LOOT_TABLE_BAND_T3_THRESHOLD = 3.0f; // level band 3 → t3 // ── Phase 7: Clade-responsive movement multipliers ──────────────────── /// Soft mismatch (cervid antler clearance for Large PCs, bovid space for Small PCs). public const float MOVE_COST_MISMATCH_LIGHT = 1.2f; /// Medium mismatch (Med-Large PCs in Mustelid tunnels, Small PCs in Ursid halls). public const float MOVE_COST_MISMATCH_MED = 1.5f; /// Heavy mismatch / squeezing (Large PCs in Mustelid tunnels — the canonical example). public const float MOVE_COST_MISMATCH_HEAVY = 2.0f; // ── Phase 7: Locked door + trap DCs ─────────────────────────────────── public const int LOCK_DC_TRIVIAL = 10; public const int LOCK_DC_EASY = 12; public const int LOCK_DC_MEDIUM = 15; public const int LOCK_DC_HARD = 18; public const int TRAP_DC_TRIVIAL = 10; public const int TRAP_DC_EASY = 12; public const int TRAP_DC_MEDIUM = 15; /// Tripwire trap damage on disarm-fail: 1d6 piercing. public const int TRAP_DAMAGE_DICE_TRIPWIRE = 1; public const int TRAP_DAMAGE_DIE_TRIPWIRE = 6; /// /// Bonus XP awarded on full dungeon clear, expressed as a multiplier of /// the dungeon's largest single-NPC XpAward. 1.0 means "doubling the /// boss kill". Tunable post-playtest. /// public const float DUNGEON_CLEAR_XP_BONUS_FRACTION = 1.0f; }