# Technical Preferences — IStalk

## Engine

- **Engine:** Unity 6 (6000.0.x)
- **Render Pipeline:** URP 17.3
- **Scripting Backend:** IL2CPP (release), Mono (editor/debug)
- **Target Platform:** PC (Steam) — Windows x64

## Naming Conventions

### C# Code

| Element | Convention | Example |
|---------|-----------|---------|
| Namespace | PascalCase | `IStalk.Network`, `IStalk.Runner` |
| Class / Struct | PascalCase | `RunnerController`, `RelayTransport` |
| Interface | IPascalCase | `IInteractable`, `IWeightCarrier` |
| Public method | PascalCase | `FireLaser()`, `PickupItem()` |
| Private field | _camelCase | `_currentWeight`, `_hVelocity` |
| Serialized field | _camelCase + [SerializeField] | `[SerializeField] private float _moveSpeed;` |
| Constant | UPPER_SNAKE | `MAX_RUNNERS`, `TICK_RATE` |
| Enum | PascalCase (type + values) | `PlayerRole.Eye`, `PlayerRole.Runner` |
| ScriptableObject | PascalCase_SO suffix | `ItemData_SO`, `GlobalRules_SO` |
| Event / Delegate | On + PascalCase | `OnItemPickedUp`, `OnRunnerEliminated` |

### Files and Folders

| Element | Convention | Example |
|---------|-----------|---------|
| Script files | PascalCase.cs | `RunnerController.cs` |
| SO assets | kebab-case | `crowbar-item.asset` |
| Prefabs | PascalCase | `RunnerPlayer.prefab` |
| Scenes | PascalCase | `LobbyScene.unity`, `GameScene.unity` |
| Folders | PascalCase | `Scripts/Network/`, `Scripts/Runner/` |

### Assembly Definitions

| Assembly | Contains |
|----------|----------|
| IStalk.Runtime | All gameplay code |
| IStalk.Editor | Editor tools and inspectors |
| IStalk.Tests | Test scripts |

## Project Structure

```
Assets/
  FishNet/              (networking framework — DO NOT MODIFY)
  Plugins/
    DOTween/
  Scripts/
    Network/            (RelayTransport, RelayClient, LobbyManager)
    Runner/             (RunnerController, RunnerHUD, abilities)
    Eye/                (EyeCameraController, EyeHUD, EyeLaserController)
    Shared/             (WeightComponent, LoSManager, ItemDatabase)
    Data/               (ScriptableObject definitions)
    UI/                 (shared UI utilities)
  Prefabs/
    Player/
    Props/
    UI/
  Resources/
    Items/
      Loot/
      Shrine/
      Traps/
      Rooms/
  Scenes/
    LobbyScene.unity
    GameScene.unity
  Art/
    Materials/
    Textures/
    Models/
```

## Performance Budgets

| Metric | Target | Context |
|--------|--------|---------|
| Frame time | < 16.6ms (60fps) | All gameplay scenarios |
| Physics | < 3ms/frame | Max 5 runners + ~50 props |
| Network tick | 30Hz (33.3ms) | FishNet TimeManager |
| GC allocations | 0 per frame in hot paths | Prediction loop, input, movement |
| Draw calls | < 200 | URP batching with SRP Batcher |
| Memory | < 2GB total | Including all loaded assets |
| Scene load | < 5s | LobbyScene → GameScene transition |

## Allowed Libraries

| Library | Version | Purpose |
|---------|---------|---------|
| FishNet | v4.x | Networking |
| DOTween | 1.x | UI/HUD animation |
| Cinemachine | 3.1.7 | Camera impulse/shake |
| AI Navigation | 2.x | Future AI pathfinding (Alpha) |
| Input System | 1.x | Player input |
| TextMeshPro | 3.x | UI text rendering |

## Forbidden Patterns

- `GameObject.Find()` / `FindObjectOfType()` — use direct references or DI
- `Update()` in NetworkBehaviour without ownership guard — always `if (!IsOwner) return;` for input/camera
- `new` allocations in prediction tick loop — pre-allocate
- `string` concatenation in hot paths — use `StringBuilder` or structured logging
- Coroutines for network timing — use FishNet's `TimeManager` callbacks
- `DontDestroyOnLoad` without singleton guard — exactly one instance
- Raw `Destroy()` on networked objects — use `ServerManager.Despawn()`
- `Start()`/`Awake()` for network initialization — use FishNet lifecycle callbacks
- Hardcoded tuning values — all constants in `GlobalRules_SO`
- `MonoBehaviour` for data containers — use ScriptableObjects

## Code Style

- One class per file (exception: small nested types like RPC data structs)
- `#region` blocks forbidden — use partial classes if a file grows too large
- Max file length: ~300 lines before splitting
- Access modifiers always explicit (no default `internal`)
- Prefer composition over inheritance (except NetworkBehaviour which is required)
- Null checks: use `TryGetComponent()` over `GetComponent()` + null check
- String comparisons: use `StringComparison.Ordinal` for IDs

## Engine Specialists

- **Primary:** Unity 6 (built-in knowledge + FishNet skill)
- **Secondary:** None configured
