poe2-bot/docs/simulator.md
2026-03-07 09:53:57 -05:00

6.7 KiB
Raw Blame History

Nexus.Simulator — Standalone Game World

Purpose

Test bot systems (combat, navigation, threat assessment) without the real game. Replaces the memory-reading pipeline with a procedural game world. Bot systems run unmodified — they see identical GameState objects and emit actions to IInputController.

Architecture

SimWorld (game tick loop)
    │
    ├── SimPoller (60Hz background thread)
    │   ├── FlushToWorld() → transfer input to SimWorld
    │   ├── Tick(dt) → advance simulation
    │   ├── SimStateBuilder.Build() → SimWorld → GameState
    │   └── Push to GameDataCache
    │
    ├── SimInputController (captures bot actions)
    │   ├── WASD → MoveDirection vector (45° isometric conversion)
    │   ├── Skills → QueueSkill(scanCode, targetWorldPos)
    │   ├── Mouse → track position, screen↔world conversion
    │   └── Flash timers for input visualization
    │
    ├── Bot Logic Thread (60Hz)
    │   ├── GameStateEnricher.Enrich(state)
    │   ├── All 6 systems: Threat, Movement, Navigation, Combat, Resource, Loot
    │   ├── NavigationController.Update()
    │   └── ExecuteActions() → SimInputController
    │
    └── Render Thread (ImGui + Veldrid)
        ├── TerrainRenderer (diamond cells, isometric)
        ├── EntityRenderer (player, enemies, health bars)
        ├── EffectRenderer (melee cones, AOE circles, projectile lines)
        ├── PathRenderer (A* waypoints)
        ├── InputOverlayRenderer (keyboard + mouse state)
        └── DebugPanel (system toggles, stats, spawn controls)

SimWorld — Game Loop

Tick (dt-based, 60Hz)

1. CheckAndExpandTerrain() → expand when player within 50 cells of edge
2. MovePlayer(dt) → WASD direction × speed × dt, collision with terrain
3. ProcessSkills() → dequeue skill casts, dispatch by scan code
4. UpdateProjectiles(dt) → move, check terrain/enemy collisions
5. UpdateEffects(dt) → decay visual effects (0.3s duration)
6. UpdateEnemies(dt) → AI state machine per enemy
7. UpdateRespawns(dt) → cull far enemies, spawn new groups

Terrain

  • Procedural: all walkable with scattered obstacles (rock clusters, wall segments, pillars)
  • 500×500 initial grid, WorldToGrid = 23/250
  • Infinite expansion: Expands 250 cells per side when player within 50 cells of edge
  • Preserves existing data via array copy with offset adjustment

Player

  • Position (Vector2), Health/Mana with regen (5 HP/s, 10 MP/s)
  • Move speed: 400 world units/s
  • Collision: slide-along-X / slide-along-Y fallback if direct move blocked

Skills

Scan Code Type Behavior
Q (0x10), R (0x13) AOE Damage all enemies within 250u of target position
E (0x12), T (0x14) Projectile Spawn projectile, 1200 speed, 800 range, 80u hit radius
LMB, RMB Melee 150u cone, 120° angle from player toward target

Base damage: 200 per hit. Configurable via SimConfig.

Enemy AI

State machine per SimEnemy:

Idle → wander randomly within 200u of spawn, new target every 2-5s
  │ player within 600u (aggro range)
  ▼
Chasing → move toward player at 75% player speed
  │ player within 100u (attack range)
  ▼
Attacking → stand still, deal 30 damage every 1.5s
  │ player escapes attack range
  ▼ back to Chasing
  │ health ≤ 0
  ▼
Dead → visible for 2s → queue for respawn (5s delay)

Enemy Spawning

  • Groups: 3-7 enemies per spawn, leader keeps rolled rarity, rest are Normal
  • Rarity distribution: 70% Normal, 20% Magic, 8% Rare, 2% Unique
  • HP multipliers: Magic=1.5×, Rare=3×, Unique=5× base (200)
  • Spawn ring: 800-2000 world units from player
  • Direction bias: ±90° cone ahead of player's movement direction
  • Culling: Remove enemies > 3000u from player
  • Population: Maintain 25 enemies, spawn new groups as needed

Bridge Layer

SimPoller

Replaces MemoryPoller. Background thread at 60Hz:

  1. FlushToWorld() — transfer accumulated input
  2. world.Tick(dt) — advance simulation (dt clamped to 0.1s max)
  3. SimStateBuilder.Build() — convert to GameState
  4. Push to GameDataCache (same fields as production)

SimStateBuilder

Converts SimWorld state → GameState:

  • Each SimEnemy → EntitySnapshot (with rarity, threat level, AI state, HP)
  • SimPlayer → PlayerState (position, vitals, skills)
  • Camera matrix: orthographic projection (12800×7200 world units → 2560×1440 screen)

SimInputController

Implements IInputController, captures actions instead of sending Win32 input:

  • WASD → direction vector (with 45° isometric inversion)
  • Skills → SimWorld.QueueSkill(scanCode, worldPos)
  • Mouse → screen position tracking, inverse camera transform for world coords
  • Input visualization: flash timers for keyboard/mouse overlay

Rendering

ViewTransform (Isometric Camera)

45° counter-clockwise rotation matching the game's camera:

World → Grid: gx = worldX × WorldToGrid
Grid → Screen: rx = (gx - gy) × cos(45°)
               ry = -(gx + gy) × cos(45°)
Screen = canvasOrigin + viewOffset + (rx, ry) × zoom

Renderers

Renderer Draws
TerrainRenderer Diamond cells (rotated grid), explored overlay, minimap
EntityRenderer Player (green circle), enemies (colored by rarity), health/mana bars
EffectRenderer Melee cones (red triangle fan), AOE circles (blue), projectile lines (cyan)
PathRenderer Cyan waypoint lines and dots from A* path
InputOverlayRenderer Keyboard (3 rows: 1-5, QWERT, ASDF) + mouse (L/R/M buttons)
DebugPanel Pause/speed, player stats, enemy counts, system toggles, threat info

VeldridImGuiRenderer

Custom ImGui backend for Veldrid 4.9.0 + D3D11:

  • HLSL shaders compiled at runtime via D3DCompiler P/Invoke
  • Dynamic vertex/index buffers, font texture from ImGui atlas
  • Alpha blending pipeline with scissor rect support

SimConfig

Terrain:     500×500, WorldToGrid=23/250, ExpandThreshold=50, ExpandAmount=250
Player:      Speed=400, HP=1000, MP=500, HPRegen=5/s, MPRegen=10/s
Enemies:     Count=25, Aggro=600u, Attack=100u, Speed=75%, HP=200, Damage=30
Spawning:    Ring=800-2000u, Groups=3-7, Cull=3000u
Skills:      Melee=150u/120°, AOE=250u, Projectile=1200speed/800range, Damage=200
Rarity:      Normal=70%, Magic=20%, Rare=8%, Unique=2%
Simulation:  SpeedMultiplier=1×, Pauseable

Running

dotnet run --project src/Nexus.Simulator

Dependencies: Core, Data, Systems, Pathfinding, ImGui.NET, Veldrid, Veldrid.StartupUtilities Does NOT depend on: Memory, Input, Screen, Game, Bot, Ui, Trade