Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions AI/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@

## [Unreleased]

### Added — Editable bot tier display names (Rookie / Rival / Ace)

- **Scope:** component:PlayerVsBot
- **What:** Each bot tier on BotDifficultyConfig now has a Display Name field. Defaults are Rookie, Rival, Ace, and Flawless so they do not collide with song difficulties (Easy / Normal / Hard / Expert). GameSession.ApplyBotGameMode stores SelectedBotLabel; the title menu, transition screen, and result banner all read that label instead of the enum name.
- **Why:** "Easy" was used for both a bot tier and a chart difficulty, which made the transition screen hard to read, and designers could not rename tiers without a code change.
- **Impact:** PlayerVsBot v0.2.0. TitleScreenController gained a Bot Difficulty Config slot. Blank display names fall back to BotDifficultyDefaults, never the raw enum string.
- **Refs:** BotDifficultySettings.cs, BotDifficultyDefaults.cs, BotDifficultyConfig.cs, GameSession.cs, TitleScreenController.cs, TransitionScreenController.cs, TwoPlayerGameCoordinator.cs, PlayerVsBot.md

### Fixed — Player side synthwave reacts to the song in VS Bot / 2P

- **Scope:** component:PlayerVsBot
- **What:** TwoPlayerGameCoordinator now pulses P1's synthwave floor from P1 OnBeat (P2 already had this) and retargets the shared AudioFFTAnalyzer to P1's playing AudioSource. P2 PlaySync never starts a clip, so the analyzer had been listening to a silent source.
- **Why:** Only the bot side looked reactive during split screen matches.
- **Impact:** Both backgrounds follow the track in 1P and 2P / VS Bot. No scene rewiring required.
- **Refs:** TwoPlayerGameCoordinator.cs, AudioFFTAnalyzer.cs, TreeRhythmController.cs

### Added — Player vs Bot mode (rule-based P2)

- **Scope:** session · integration · component:TitleScreen · song-select · component:PlayerVsBot
- **What:** Enabled title-tree VS Bot with an Easy/Medium/Hard difficulty sub-tree (same hanging-tree layout as mode select; Perfect remains in config/enum but is not offered in the menu). Selection is stored on GameSession (SelectedGameMode, SelectedBotDifficulty) and drives the existing split-screen GameScene. BotPlayerController disables P2 Input System input, rolls once per decision against rates on BotDifficultyConfig (seed values centralized in BotDifficultyDefaults), and submits through TreeRhythmController.SubmitGameplayActionAsync. Results label P2 as Bot with the chosen tier; only P1 confirms Continue/Quit. Song-select hides the P2 difficulty row in bot mode (bot mirrors P1 beatmap tier).
- **Why:** Opponent practice without duplicating the two-player gameplay stack; error percent editable in Inspector with no inline hardcodes.
- **Impact:** Menu to GameSession to TwoPlayerGameCoordinator; docs in AI/components/PlayerVsBot.md.
- **Refs:** Assets/Bot/*, GameSession.cs, TwoPlayerGameCoordinator.cs, TreeRhythmController.cs, TitleScreenController.cs, BotDifficultyConfig.asset

### Added — End-of-level tree QR share on Result Screen

- **Scope:** component:ResultScreen · integration
Expand Down
3 changes: 2 additions & 1 deletion AI/COMPONENT_INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Each component has a self-contained doc that lists its own files, tokens, animat
| Score HUD *(reference / "score meter")* | [components/ScoreHUD.md](./components/ScoreHUD.md) | Component | `Assets/UI/` | Stable | 1.1.0 | score, hud, combo, panel, meter |
| Main Menu | [components/MainMenu.md](./components/MainMenu.md) | Component | `Assets/UI/Style1/MainMenu/` | Stable | 1.0.0 | menu, navigation, play, songs |
| Song Carousel | [components/SongCarousel.md](./components/SongCarousel.md) | Component | `Assets/UI/Style1/SongCarousel/` | Stable | 1.0.0 | carousel, songs, selection, scroll |
| **Title Screen** | [components/TitleScreen.md](./components/TitleScreen.md) | Component | `Assets/Menu/TitleScreen/` | Beta | 0.1.0 | title, menu, 1P, 2P, pushdown-automata |
| **Title Screen** | [components/TitleScreen.md](./components/TitleScreen.md) | Component | `Assets/Menu/TitleScreen/` | Beta | 0.2.2 | title, menu, 1P, 2P, vs-bot, tutorial, pushdown-automata |
| **Player vs Bot** | [components/PlayerVsBot.md](./components/PlayerVsBot.md) | Integration | `Assets/Bot/` | Beta | 0.2.0 | bot, difficulty, two-player, session, tier-names |
| **Song Select** | [components/SongSelect.md](./components/SongSelect.md) | Component | `Assets/Menu/SongSelect/` | Beta | 0.1.0 | song-select, carousel, difficulty, 2P, transition |
| Style 1 Button *(primitive)* | [components/Style1Button.md](./components/Style1Button.md) | Primitive | `Assets/UI/Style1/` | Stable | 1.0.0 | button, nav, press, hover |
| UI Component Gallery *(tool / board)* | [components/ComponentGallery.md](./components/ComponentGallery.md) | Tool | `Assets/UI/Style1/ComponentGallery/` | Stable | 1.1.0 | board, gallery, showcase, catalog |
Expand Down
3 changes: 3 additions & 0 deletions AI/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Per-component docs tell you **which code files to read**. You can also **see and
| **Animations dead (hover/press/intro)** | [WORKFLOW.md → Interactive UI checklist](./WORKFLOW.md#interactive-ui-checklist-r-34) → R-34 | `UIEventSystemUtil`, `Style1ButtonInput`, scene EventSystem |
| **View / test a component live in Unity** | [components/README.md → Accessing in Unity](./components/README.md#accessing-a-component-in-unity) → that component's doc (**Editor & menus** / **Demo**) → [ComponentGallery.md](./components/ComponentGallery.md) if using the board | none — use `Rhythm → Create … Demo Scene` and/or `Rhythm → Setup UI Component Gallery` |
| **Modify the title screen** | [components/TitleScreen.md](./components/TitleScreen.md) | files listed in that doc |
| **Player vs Bot / bot difficulty** | [components/PlayerVsBot.md](./components/PlayerVsBot.md)[TitleScreen.md](./components/TitleScreen.md) | `BotPlayerController`, `GameSession`, `TwoPlayerGameCoordinator` |
| **Modify song select / carousel / difficulty** | [components/SongSelect.md](./components/SongSelect.md) | files listed in that doc |
| **Modify an existing component** | that component's doc in [components/](./components/) | only the files in that doc's "Files" section |
| **Touch layout / Awake / OnValidate / Initialize / scene UI** | [WORKFLOW.md → Runtime layout & wiring](./WORKFLOW.md#runtime-layout--wiring-mandatory)[RULES.md](./RULES.md) R-25–R-32 | component doc Files + composition root if wiring |
Expand Down Expand Up @@ -91,6 +92,8 @@ This is the **single source of truth for what exists**. Each row links to a self
| --- | --- | --- | --- | --- |
| Score HUD *(reference)* | [components/ScoreHUD.md](./components/ScoreHUD.md) | Component | Stable | score, hud, combo, panel, meter |
| Main Menu | [components/MainMenu.md](./components/MainMenu.md) | Component | Stable | menu, navigation, play, songs |
| Title Screen | [components/TitleScreen.md](./components/TitleScreen.md) | Component | Beta | title, menu, 1P, 2P, vs-bot |
| Player vs Bot | [components/PlayerVsBot.md](./components/PlayerVsBot.md) | Integration | Beta | bot, difficulty, two-player |
| Song Carousel | [components/SongCarousel.md](./components/SongCarousel.md) | Component | Stable | carousel, songs, selection, scroll |
| Style 1 Button | [components/Style1Button.md](./components/Style1Button.md) | Primitive | Stable | button, nav, press, hover |
| UI Component Gallery | [components/ComponentGallery.md](./components/ComponentGallery.md) | Tool | Stable | board, gallery, showcase, catalog |
Expand Down
22 changes: 17 additions & 5 deletions AI/components/MainMenu.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ scene load, no `SceneTransitionManager`. `MenuSceneController` owns that swap.
| `_titleGroup` | `CanvasGroup` | Wraps all title-state Canvas elements; fades out on ShowSongSelect |
| `_songSelectGroup` | `CanvasGroup` | Wraps all song-select Canvas elements; fades in on ShowSongSelect |
| `_beatTimer` | `MenuBeatTimer` | Stopped on ShowTitle; started by `CarouselState` after song preview |
| `_tutorialSceneName` | `string` | Scene loaded on Tutorial? → Yes (default `"Tutorial"`). Must be in Build Settings when ready. |
| `_background` | `SynthwaveBackgroundController` | Reference retained for future color-cycle toggling if needed |

---
Expand All @@ -76,16 +77,27 @@ scene load, no `SceneTransitionManager`. `MenuSceneController` owns that swap.

```csharp
public void ShowSongSelect(int playerCount);
public void StartTutorial(int playerCount);
public void ShowTitle();
```

### ShowSongSelect(int playerCount)

1. `GameSession.Instance.PlayerCount = playerCount`
2. Animate `_headerLabel` text → "SELECT SONG" via [header-label-swap](../ANIMATION_INDEX.md#header-label-swap) (alpha 0→0→1, 0.4s total)
3. `_titleGroup` fades out (0.20s, InQuad)
4. `_songSelectGroup` fades in (0.25s, OutQuad)
5. `_songSelect.InitialiseAndShow(playerCount)`
1. Guard against duplicate transitions
2. `GameSession.Instance.PlayerCount = playerCount`, `WantsTutorial = false`
3. Animate `_headerLabel` text → "SELECT SONG" via [header-label-swap](../ANIMATION_INDEX.md#header-label-swap) (alpha 0→0→1, 0.4s total)
4. `_titleGroup` fades out (0.20s, InQuad)
5. `_songSelectGroup` fades in (0.25s, OutQuad)
6. `_songSelect.ShowForPlayerCount(playerCount)` (or `InitialiseAndShow` equivalent)

### StartTutorial(int playerCount)

1. Guard against duplicate transitions
2. `GameSession.Instance.PlayerCount = playerCount`, `WantsTutorial = true`
3. If `_tutorialSceneName` is in Build Settings → fade via `SceneTransitionManager` into that scene
4. If not yet in Build Settings → log a warning and fall back to `ShowSongSelect` so the flow stays playable while the tutorial scene is built

Wire the real tutorial by creating a scene named `Tutorial` (or change **Tutorial Scene Name** on `MenuSceneController`) and adding it to **File → Build Settings**.

### ShowTitle()

Expand Down
152 changes: 152 additions & 0 deletions AI/components/PlayerVsBot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Player vs Bot

> Rule-based P2 opponent that reuses the existing split-screen Player vs Player layout.
> Pattern: session config + ScriptableObject difficulty table + driver over existing P2 controller
> Status: `Beta` · Version: `0.2.0`
> Routed from [../INDEX.md](../INDEX.md).
---

## Summary

| Field | Value |
| --- | --- |
| **Mode** | `GameMode.PlayerVsBot` on `GameSession` |
| **Layout** | Same two-screen GameScene as human 2P (`PlayerCount = 2`) |
| **Bot driver** | `BotPlayerController` (auto-added on P2 `TreeRhythmController`) |
| **Config asset** | `Assets/ScriptableObjects/Configs/BotDifficultyConfig.asset` |
| **Seed defaults** | `BotDifficultyDefaults` (asset creation / missing-tier fallback only) |
| **Menu entry** | Title tree: PLAY → **VS Bot** → Rookie / Rival / Ace → Tutorial? |
| **Tier names** | `Display Name` per tier on the config asset (renameable, distinct from song difficulty) |

---

## Files *(scoped reading)*

| File | Role |
| --- | --- |
| `Assets/Bot/BotPlayerController.cs` | Decision loop: one roll → delay → wait for note → submit |
| `Assets/Bot/BotDifficulty.cs` | Easy / Medium / Hard / Perfect enum |
| `Assets/Bot/BotDifficultySettings.cs` | Per-tier serializable display name + error rate + delay range |
| `Assets/Bot/BotDifficultyConfig.cs` | ScriptableObject table read at runtime |
| `Assets/Bot/BotDifficultyDefaults.cs` | Single place for seed/fallback rates **and default tier names** |
| `Assets/Bot/Editor/BotSetupEditorTools.cs` | `Rhythm → Setup Bot Difficulty Config` |
| `Assets/Bot/Editor/BotDecisionSimulationTests.cs` | `Rhythm → Bot → Simulate Error Rates` |
| `Assets/Session/GameMode.cs` | SinglePlayer / PlayerVsPlayer / PlayerVsBot |
| `Assets/Session/GameSession.cs` | Persists mode + bot tier + resolved `SelectedBotLabel` across scenes |
| `Assets/Integration/TwoPlayerGameCoordinator.cs` | Enables bot, disables P2 input, result labels, wires P1+P2 synthwave to the playing song |
| `Assets/Integration/TreeRhythmController.cs` | Bot API: decision events, required action, submit; exposes `SongAudioSource` |
| `Assets/Scripts/AudioFFTAnalyzer.cs` | FFT beat detection; `SetSource` retargets analysis to P1's audible song |
| `Assets/Menu/TitleScreen/TitleScreenController.cs` | VS Bot + difficulty sub-tree |

---

## Menu flow

```
Main Menu (title BST)
PLAY → 1P | VS Bot | 2P
Rookie | Rival | Ace (labels from config; hanging tree as mode select)
Tutorial? → Yes / No
Song select (P2 row hidden; bot mirrors P1 song difficulty)
GameScene split-screen (P1 human, P2 bot)
```

Perfect exists on the config/enum for later but is **not** offered in the title menu.

---


| Value | Where it lives |
| --- | --- |
| Error rates / decision delays | `BotDifficultyConfig` asset (Inspector) |
| Bot tier names shown on screen | `BotDifficultyConfig``Display Name``GetDisplayName` |
| Seed values when creating that asset | `BotDifficultyDefaults` only |
| Selected mode + bot tier + label | `GameSession` (`ApplyBotGameMode(difficulty, label)`) |
| Correct BST action | `TreeNavigator.GetRequiredAction``BSTInsertionRules` |

Gameplay scripts (`BotPlayerController`, coordinator) must call `GetSettings` / session APIs — never inline `0.60f` style rates.
UI must read `GetDisplayName` or `GameSession.SelectedBotLabel` — never `BotDifficulty.ToString()`.

---

## Decision source of truth

1. **Correct action:** `TreeNavigator.GetRequiredAction()``BSTInsertionRules.GetRequiredAction`
2. **Submit path:** `TreeRhythmController.SubmitGameplayActionAsync``HandleLanePressed``RhythmManager.TryConsumeHit``TreeNavigator.TryAction`
3. **Decision event:** `TreeRhythmController.OnBotDecisionPoint` (once per meaningful tree step; not per frame)
4. **Intentional errors:** same invalid-action path as a human (score / feedback / error count)

---

## Inspector setup

1. Open **GameScene**.
2. Select **TwoPlayerCoordinator**.
3. Assign **Bot Difficulty Config**`BotDifficultyConfig` asset
(or run **Rhythm → Setup Bot Difficulty Config**).
4. Leave **Bot Controller** empty (auto-added on P2 at runtime).
5. Tune rates/delays on the config asset in the Inspector.
6. Open **Menu** and assign the same asset on **TitleScreenController → Bot Difficulty Config**
so the VS Bot nodes show the configured names. Unassigned falls back to
`BotDifficultyDefaults` names, which means asset renames would not appear.

### Default serialized rates *(from BotDifficultyDefaults / asset)*

| Tier (enum) | Default display name | Error rate | Min delay | Max delay | In title menu? |
| --- | --- | --- | --- | --- | --- |
| Easy | **Rookie** | 0.60 | 1.00 s | 2.00 s | Yes |
| Medium | **Rival** | 0.35 | 0.70 s | 1.50 s | Yes |
| Hard | **Ace** | 0.10 | 0.40 s | 1.00 s | Yes |
| Perfect | **Flawless** | 0.00 | 0.25 s | 0.75 s | No (config only) |

The `BotDifficulty` enum keeps its Easy/Medium/Hard/Perfect members (serialization stability);
only the **displayed** name is data. Defaults avoid the song difficulty words
(`Difficulty`: Easy / Normal / Hard / Expert) so "Hard bot" and "Hard chart" never collide.

**To rename a bot:** open `BotDifficultyConfig.asset` → edit **Display Name** on that tier.
The menu node, transition screen and result banner all update from that one field.

---

## Editor menus

| Menu | Purpose |
| --- | --- |
| `Rhythm → Setup Bot Difficulty Config` | Create/repair asset (incl. backfilling names) + assign to `TwoPlayerGameCoordinator` and `TitleScreenController` in the open scene |
| `Rhythm → Bot → Simulate Error Rates (1000 rolls)` | Monte-Carlo check of configured probabilities |

---

## Mode separation

| Mode | P1 | P2 |
| --- | --- | --- |
| Single Player | Human, full screen | Inactive |
| Player vs Player | Human | Human input enabled |
| Player vs Bot | Human | Input disabled; `BotPlayerController` drives actions |

---

## Results

- P1 banner sub-label: `Player 1`
- P2 banner sub-label: `Bot — {SelectedBotLabel}` (e.g. `Bot — Rival`)
- Transition screen shows `BOT {SELECTEDBOTLABEL}` next to the chart difficulty
- Only P1 confirms Continue / Quit (bot has no menu input)

---

## Quick test plan

1. PLAY → VS Bot → Rival → Tutorial? No → song → play: split screen, P1 human, P2 moves alone.
2. Rookie / Ace: bot still plays; Console `[Bot]` logs show matching `errorRate` in Editor.
3. Player vs Player: both humans; bot disabled.
4. Single Player: unchanged full-screen.
5. `Rhythm → Bot → Simulate Error Rates`: measured rates near configured values.
6. Restart / return to menu: no duplicate bot decisions; fresh match uses current session difficulty.
7. Rename a tier's **Display Name** on the config: menu node, transition screen and result banner all show the new name.
36 changes: 25 additions & 11 deletions AI/components/TitleScreen.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> World-space BST tree entry point. Player navigates a 3D node tree (PLAY / SETTINGS / CREDITS)
> via a generic level-stack system; mode select (1P / VS AI / 2P) is a sub-tree pushed on Confirm.
> Pattern: [World-Space Tree Navigation](../PATTERNS.md) · Status: `Beta` · Version: `0.2.0`
> Pattern: [World-Space Tree Navigation](../PATTERNS.md) · Status: `Beta` · Version: `0.2.2`
> Routed from [../INDEX.md](../INDEX.md).
---
Expand All @@ -17,10 +17,10 @@
| **Prefab** | — (procedural; tree nodes are world-space GameObjects) |
| **Pattern** | PushdownAutomata + World-Space Tree Navigation |
| **Status** | Beta |
| **Version** | 0.2.0 |
| **Version** | 0.2.2 |
| **Author** | Pranav Sukesh |
| **Last Updated** | 2026-07-05 |
| **Tags** | title, menu, navigation, bst, tree, world-space, pushdown-automata |
| **Last Updated** | 2026-07-18 |
| **Tags** | title, menu, navigation, bst, tree, world-space, pushdown-automata, vs-bot |

---

Expand All @@ -45,7 +45,8 @@
- `Style1Palette` — all colors applied via `MaterialPropertyBlock` on world-space nodes
- `Style1Typography` — TMP labels on nodes and Canvas header
- `IMenuInputProvider` / `UnityMenuInputProvider` — input abstraction
- `GameSession` — persists PlayerCount across scenes
- `GameSession` — persists `PlayerCount`, `SelectedGameMode`, `SelectedBotDifficulty`, `WantsTutorial`
- [PlayerVsBot.md](./PlayerVsBot.md) — VS Bot difficulty sub-tree + session wiring
- PrimeTween, UniTask, TextMeshPro

---
Expand All @@ -67,16 +68,29 @@ The header label ("TREEFORMANCE") is owned by `MenuSceneController` on the Canva

```
PLAY [SubTreeRoot = mode-select]
├── SETTINGS [OnConfirmLeaf = placeholder]
└── CREDITS [OnConfirmLeaf = placeholder]
├── SETTINGS [Disabled placeholder]
└── CREDITS [Disabled placeholder]
mode-select sub-tree:
1P [OnConfirmLeaf = MenuSceneController.ShowSongSelect(1)]
├── VS AI [OnConfirmLeaf = noop]
└── 2P [OnConfirmLeaf = MenuSceneController.ShowSongSelect(2)]
1P [SubTreeRoot = tutorial-prompt for SinglePlayer]
├── VS Bot [SubTreeRoot = bot-difficulty]
└── 2P [SubTreeRoot = tutorial-prompt for PlayerVsPlayer]
bot-difficulty sub-tree (after VS Bot) — same hanging-tree layout as mode select:
Medium [SubTreeRoot = tutorial-prompt for BotDifficulty.Medium]
├── Easy [SubTreeRoot = tutorial for Easy]
└── Hard [SubTreeRoot = tutorial for Hard]
(Perfect is kept in BotDifficultyConfig / enum but not shown in the menu yet)
tutorial-prompt sub-tree (after choosing a mode / bot difficulty):
Tutorial? [Disabled — question / title only]
├── Yes [OnConfirmLeaf → ApplyHumanGameMode / ApplyBotGameMode + StartTutorial]
└── No [OnConfirmLeaf → ApplyHumanGameMode / ApplyBotGameMode + ShowSongSelect]
```

Adding a Settings sub-tree in the future requires only adding `SubTreeRoot` to the SETTINGS node data — no state class changes, no controller changes.
Back from Tutorial? pops to the previous level. Back from bot difficulty pops to mode select.
`GameSession.SelectedGameMode`, `SelectedBotDifficulty`, and `PlayerCount` persist into GameScene.
Player vs Bot reuses the existing two-screen layout (`PlayerCount = 2`) with `BotPlayerController` driving P2.

---

Expand Down
8 changes: 8 additions & 0 deletions Assets/Bot.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading