# OGG — Dev Notes & Systems Guide > Boomer shooter. Cruelty Squad vibes. Jank is a feature. > This doc covers all current systems. --- ## Scene Hierarchy ``` Player [CharacterController, FirstPersonController, SimpleCrosshair] │ [Player, PlayerHUD, Inventory, WeaponManager, BootsEffect] └── Camera [Camera, AudioListener, CameraShake, WeaponBob] ``` Pickups are standalone GameObjects placed anywhere in the scene — **not** children of the player. --- ## Scripts Overview | Script | Lives On | What it does | |---|---|---| | `FirstPersonController` | Player | WASD move, mouse look, jump, sprint, cursor lock | | `Player` | Player | Health + stamina stats, drain/regen logic, sprint gating | | `PlayerHUD` | Player | Health bar, stamina bar, speedometer (IMGUI) | | `SimpleGun` | Weapon prefabs | Shooting, ammo, recoil, muzzle flash, bullet decals | | `CameraShake` | Camera | Singleton shake impulses, called by SimpleGun on fire | | `WeaponBob` | Camera | Procedural weapon sway while moving | | `WeaponViewmodel` | Weapon prefabs | Per-weapon position/rotation/scale in the player's hands | | `SimpleCrosshair` | Player | Draws the crosshair via OnGUI | | `EnemyHealth` | Enemy objects | Takes damage from SimpleGun raycasts, flashes red, dies | | `EnemySpawner` | Any object | Spawns enemies at runtime | | `HumanoidEnemy` | Enemy objects | Enemy AI / behaviour | | `ItemDefinition` | ScriptableObject asset | Data definition for any item: name, icon, type, weapon prefab, equippable flag | | `PickupItem` | Pickup GameObjects | Spin/bob animation, proximity prompt, E-to-collect | | `Inventory` | Player | Item list, [I] HUD, right-click context menu, equip logic | | `WeaponManager` | Player | Instantiates weapon prefabs, handles slot switching | | `BootsEffect` | Player | Watches inventory for Bunny Hop Boots equipped state, applies stamina boost | | `StaminaBoostPickup` | Pickup GameObjects | Legacy — superseded by BootsEffect. Left in for reference. | --- ## 1. Player Stats — `Player` + `PlayerHUD` `Player.cs` is the data container for all player stats. `PlayerHUD.cs` reads from it and draws the HUD. **Health fields:** - `maxHealth` / `health` — enemies subtract from `health` directly, no auto-regen **Stamina fields:** - `maxStamina` / `stamina` — depletes while sprinting - `staminaDrain` — units lost per second while sprinting (default 22) - `staminaRegen` — units gained per second when resting (default 12) - `regenDelay` — seconds of non-sprinting before regen starts (default 1.2) - When stamina hits 0 the player is **exhausted** — sprint locked out until stamina fully refills **HUD Layout:** ``` [bottom-left corner] SPEED 42.3 units / sec HEALTH 85 / 100 [████████████░░░] STAMINA 62 / 100 [██████░░░░░░░░░] ``` **Speedometer:** - Sits above the health/stamina bars, bottom-left - Speed calculated from actual position delta per frame (horizontal only) - Colour shifts: green → yellow → red as speed increases - Thresholds configurable in Inspector (`speedoFastThreshold`, `speedoCritThreshold`) - Can be toggled off via `showSpeedometer` bool --- ## 2. Movement — `FirstPersonController` Standard FPS controller. **Inspector fields:** - `walkSpeed` / `runSpeed` — intentionally high (50/80) due to CharacterController collision tuning - `mouseSensitivity` — linear, no acceleration - `playerCamera` — auto-found if empty, best to assign explicitly **Controls:** | Key | Action | |---|---| | W A S D / Arrows | Move | | Left Shift | Sprint (drains stamina) | | Space | Jump | | Mouse | Look | | Escape | Unlock cursor | | Left Click (unlocked) | Re-lock cursor | --- ## 3. Shooting — `SimpleGun` Raycast-based hitscan. No projectiles. **Key fields:** - `damage` — per shot - `range` — raycast distance in units - `fireRate` — shots per second - `isAutomatic` — hold vs click - `recoilKickback` / `recoilKickUp` — feel tuning - `fpsCam` — auto-found if null **Internals:** - Muzzle flash: child Quad + Point Light, shown ~50ms per shot - Bullet decals: pooled (30 max), oldest reused when full - Spark particles: spawned and self-destroyed on impact - Calls `CameraShake.Instance.Shake()` on every shot - Shooting blocked when inventory is open - Position driven by `WeaponManager.weaponPositionOffset` each frame so recoil applies on top correctly **Ammo:** `currentAmmo` / `maxAmmo`. Press **R** to reload (instant). --- ## 4. Item Definitions — `ItemDefinition` (ScriptableObject) **Create:** Right-click in Project → **Create → OGG → Item Definition** | Field | Notes | |---|---| | `itemName` | Display name everywhere — must be unique | | `icon` | Sprite shown in inventory slots | | `type` | `Misc`, `Consumable`, or `Weapon` | | `isEquippable` | Tick for non-weapon items that can be equipped (e.g. boots) — shows equip button in inventory | | `weaponPrefab` | Weapon type only — prefab spawned in weapon holder when equipped | | `description` | Shown in inventory detail panel | --- ## 5. Pickups — `PickupItem` Attach to any GameObject to make it a spinning, bobbing pickup. **Setup:** 1. Place a GameObject in the scene with any mesh 2. Add `PickupItem` component 3. Drag an `ItemDefinition` into the `Definition` slot **Inspector fields:** | Field | Notes | |---|---| | `definition` | The ItemDefinition asset | | `spinSpeed` | Degrees/sec Y rotation (default 120) | | `bobHeight` | Vertical travel in units (default 0.18) | | `bobSpeed` | Bob cycles per second (default 2.2) | | `pickupRadius` | Proximity trigger distance | | `pickupKey` | Default E | | `pickupParticlesPrefab` | Optional — spawned on collect | | `pickupSound` | Optional — played on collect | **On pickup:** Calls `Inventory.AddItem(definition, autoEquip: true)` — picking up a weapon automatically equips it. --- ## 6. Inventory — `Inventory` Lives on the Player. Press **I** to open/close. Cursor unlocks when open. **HUD Layout:** ``` ┌──────────────────────────────┐ │ [ INVENTORY ] │ ← header, shows active weapon top-right ├──────────────────────────────┤ │ [ ] [ ] [ ] [ ] │ │ [ ] [ ] [ ] [ ] │ ← item grid (4×5 by default) ├──────────────────────────────┤ │ SELECTED: Item Name │ ← detail panel │ Description │ │ ▶ EQUIPPED [ F ] UNEQUIP│ ← shown for weapons AND equippable items └──────────────────────────────┘ ``` **Right-click context menu** on any slot: - **Equip** / **Unequip** — shown for weapons and items with `isEquippable = true` - **Inspect** — selects the item in the detail panel - **Drop** — removes from inventory **Controls:** | Input | Action | |---|---| | I | Toggle inventory open/close | | Click slot | Select item | | Double-click weapon slot | Equip it | | Right-click slot | Open context menu | | F | Equip selected item | | Scroll Wheel (closed) | Cycle weapons | | 1–9 (closed) | Equip weapon by slot number | **Slot colours:** - Dark grey = empty - Dark green = has item - Bright green = selected - Orange = equipped **Useful methods:** ```csharp inventory.HasItem("Medkit") // bool inventory.CountOf("Grenade") // int inventory.RemoveItem("Medkit", 1) // bool inventory.AddItem(definition) // add by ItemDefinition inventory.AddItem("Raw Name", sprite) // add by name inventory.IsOpen // bool — checked by gun + WeaponManager ``` --- ## 7. Weapon Manager — `WeaponManager` Lives on the Player. Manages weapon instances and which is active. **Setup:** - Add `WeaponManager` to Player - Set `Weapon Holder` to the Camera transform - Any `SimpleGun` child of the holder is auto-registered but starts **inactive** — player must pick up and equip from inventory **Weapon Position fields:** - `weaponPositionOffset` / `weaponRotationOffset` / `weaponScale` — global fallback if the weapon has no `WeaponViewmodel` component - Per-weapon positioning is preferred — add `WeaponViewmodel` to the weapon prefab instead **Live positioning workflow (in Editor):** 1. Hit Play, equip the gun 2. On WeaponManager Inspector → click **"Select Active Weapon in Scene"** 3. Use gizmos in Scene view to position it 4. Click **"Sync Transform → Offset Fields"** — saves to the `WeaponViewmodel` on the prefab automatically 5. Stop Play — values persist **Useful methods:** ```csharp weaponManager.EquipByIndex(0) weaponManager.EquipByName("Gun Splat") weaponManager.CycleWeapon(1) // next weaponManager.CycleWeapon(-1) // previous weaponManager.ActiveWeaponName // string weaponManager.Unequip() // holster all ``` --- ## 8. WeaponViewmodel — `WeaponViewmodel` Add to a **weapon prefab** to define how it sits in the player's hands. WeaponManager reads from it on equip instead of using the global offset. **Fields:** - `positionOffset` — local position relative to weapon holder - `rotationOffset` — euler angles - `scale` — local scale Each weapon prefab gets its own values, so Gun Splat can sit differently from any future weapon. --- ## 9. Equippable Items — `BootsEffect` **Add `BootsEffect` to the Player** for the Bunny Hop Boots to work. Lives on the Player. Watches `Inventory.items` each frame for the boots' `isEquipped` state and adjusts `Player.maxStamina` accordingly. | State | Effect | |---|---| | Boots equipped | `maxStamina` → 200, stamina topped up | | Boots unequipped | `maxStamina` → 100, stamina clamped back down | The stamina bar stretches/shrinks live to reflect the new max. **Inspector fields:** - `bootsItemName` — must match the ItemDefinition `itemName` exactly (default `"Bunny Hop Boots"`) - `boostedMaxStamina` — default 200 --- ## Setup Scripts (Editor Menu: OGG → Setup) ### Create Gun Splat Weapon Creates: - `Assets/Prefabs/Weapons/GunSplat.prefab` — held weapon with `SimpleGun` - `Assets/Prefabs/Pickups/GunSplat_Pickup.prefab` — world pickup with spin/bob - `Assets/Items/GunSplat_Item.asset` — `ItemDefinition` (type = Weapon) ### Create Bunny Hop Boots Creates: - `Assets/Items/BhopBoots_Item.asset` — `ItemDefinition` (type = Misc, isEquippable = true) - `Assets/Prefabs/Pickups/BhopBoots_Pickup.prefab` — world pickup **After running either script:** drag the pickup prefab into your scene. --- ## Adding a New Weapon — Step by Step 1. Create an `ItemDefinition` asset — set `type = Weapon`, assign your gun prefab to `weaponPrefab` 2. Add `SimpleGun` to the gun prefab root 3. Add `WeaponViewmodel` to the gun prefab — set position/rotation/scale or dial it in with the live Editor workflow 4. Create a pickup: any mesh + `PickupItem` component + drag in the ItemDefinition 5. Place the pickup in the scene --- ## Adding a New Equippable Item 1. Create an `ItemDefinition` — set `type = Misc`, tick `isEquippable = true` 2. Write a component that lives on the Player, watches `inventory.items` for `entry.isEquipped`, and applies/removes the effect 3. Place a pickup in the scene referencing the definition --- ## Adding a Consumable ```csharp Inventory inv = GetComponent(); if (inv.HasItem("Medkit")) { health += 50f; inv.RemoveItem("Medkit"); } ``` --- ## Known Rough Edges - **No weapon switch animation** — instant swap. Hook into `WeaponManager.EquipByIndex()` when ready. - **No save system** — inventory is runtime only, nothing persists between sessions. - **Starting gun** — any `SimpleGun` that's a child of the Camera at Start gets auto-registered but starts inactive. Player needs to find it as a pickup in the scene. - **Inventory is IMGUI** — not Canvas. Performance fine at this scale. All data logic is decoupled from drawing so a Canvas swap later would be straightforward. - **`SimpleGun` assumes it's always active** — non-SimpleGun weapons will slot-switch fine but need their own input handling to shoot. --- ## Controls Reference | Key | Action | |---|---| | W A S D | Move | | Left Shift | Sprint | | Space | Jump | | Left Click | Shoot | | Right Click | Context menu (inventory open) | | R | Reload | | E | Pick up nearby item | | I | Open / close inventory | | F | Equip selected item | | Scroll Wheel | Cycle weapons | | 1 – 9 | Equip weapon by slot | | Escape | Unlock cursor |