180 lines
6.7 KiB
Markdown
180 lines
6.7 KiB
Markdown
# 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
|