Skip to content
Merged
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
7 changes: 7 additions & 0 deletions AI/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@
- **Impact:** Tutorial v0.6.0. Lesson step copy stays on `DefaultTutorialLesson.asset`; visual chrome moves to the guide style asset.
- **Refs:** `TutorialGuideStyle.cs`, `TutorialLayoutBuilder.cs`, `TutorialController.cs`, `TutorialBootstrapRefs.cs`, `TutorialEditorTools.cs`, `Tutorial.md`

### Added — Score record flow on the results screen
- **Scope:** component:ResultScreen · session
- **What:** A third button, RECORD, sits between CONTINUE and QUIT. It opens an arcade name entry (Left/Right walk the glyph grid, Place inserts, DONE or Enter saves, Back deletes then cancels), writes the run to `score_records.json` in `Application.persistentDataPath`, and then shows the generated id (`tf-001`) so the player can photograph it. Continue and Quit on that confirmation are the player's final answer and take the same route the results buttons would have taken.
- **Why:** Players had no way to leave their name on a run, and the results web app needs a stable per-run id to look a run up.
- **Impact:** Available on any panel the player can navigate, which covers 1P, Player 1 in VS Bot, both players in human 2P, and battle mode. The bot's spectator panel passes `allowNavigation: false` and keeps the two-button layout. Record is never reported to `TwoPlayerGameCoordinator` as a Continue, and after a save the results buttons are re-armed so a 2P selection mismatch can still be resolved. Campus text comes from `Resources/CampusBuildConfig`, so a cabinet swaps campus by editing one asset. JSON fields are `id`, `name`, `score`, `date`, `campus`, `lastPlaced`.
- **Refs:** `ScoreRecord.cs`, `ScoreRecordStore.cs`, `Campus.cs`, `CampusBuildConfig.cs`, `RecordNameEntryController.cs`, `RecordConfirmController.cs`, `RecordFlowLayoutBuilder.cs`, `ResultScreenController.cs`, `ResultScreenLayoutBuilder.cs`

### Changed — Tutorial drops the synthwave sky and restores the note hit bar
- **Scope:** component:Tutorial
- **What:** New `TutorialPresentationGuards` runs on every tutorial play: it forces `SynthwaveSky` off and re-creates the missing `NoteHitSpot` hit bar on the note path's hit waypoint. The bar reuses GameScene's own `Assets/Materials/NoteHitSpot.mat` (now carried on `TutorialBootstrapRefs` so play mode never touches AssetDatabase) with GameScene's exact scale `2.5 × 0.04 × 0.08` and layer, so it is the same thin bar the real level uses rather than a tutorial-only style.
Expand Down
16 changes: 16 additions & 0 deletions Assets/Resources/CampusBuildConfig.asset
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 43c0ab061fa14d129717dae1adfc6af5, type: 3}
m_Name: CampusBuildConfig
m_EditorClassIdentifier:
_campus: 0
_displayName: Indianapolis
8 changes: 8 additions & 0 deletions Assets/Resources/CampusBuildConfig.asset.meta

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

10 changes: 10 additions & 0 deletions Assets/Session/Campus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** <summary>
* Physical campus a cabinet build belongs to. The value is set once per build
* on <see cref="CampusBuildConfig"/>; scenes and gameplay code never hardcode it.
* </summary>
*/
public enum Campus
{
Indianapolis = 0,
WestLafayette = 1
}
11 changes: 11 additions & 0 deletions Assets/Session/Campus.cs.meta

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

73 changes: 73 additions & 0 deletions Assets/Session/CampusBuildConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using UnityEngine;

/** <summary>
* Build-specific campus identity for score records.
* Swap this asset (or its <see cref="Campus"/> value) between the Indianapolis
* and West Lafayette cabinets. Gameplay code only reads <see cref="DisplayName"/>.
* </summary>
*/
[CreateAssetMenu(menuName = "Treeformance/Campus Build Config", fileName = "CampusBuildConfig")]
public class CampusBuildConfig : ScriptableObject
{
/** <summary>Resources path this config is loaded from at runtime.</summary> */
public const string ResourcesPath = "CampusBuildConfig";

private const string IndianapolisDisplayName = "Indianapolis";
private const string WestLafayetteDisplayName = "West Lafayette";

[Tooltip("Which campus this build represents.")]
[SerializeField] private Campus _campus = Campus.Indianapolis;

[Tooltip("Exact string written into the campus field of score_records.json.")]
[SerializeField] private string _displayName = IndianapolisDisplayName;

/** <summary>Enum value for this build.</summary> */
public Campus Campus => _campus;

/** <summary>Value written to the JSON <c>campus</c> field.</summary> */
public string DisplayName =>
string.IsNullOrWhiteSpace(_displayName)
? DefaultDisplayName(_campus)
: _displayName.Trim();

/** <summary>
* Loads the config from Resources. If the asset is missing, returns a runtime
* instance for the default campus so a record still saves with a valid campus
* string instead of an empty one.
* </summary>
*/
public static CampusBuildConfig LoadOrDefault()
{
var config = Resources.Load<CampusBuildConfig>(ResourcesPath);
if (config != null)
return config;

Debug.LogWarning(
$"[CampusBuildConfig] No Resources/{ResourcesPath} asset found, defaulting to " +
$"{DefaultDisplayName(Campus.Indianapolis)}. Create one via " +
"Create > Treeformance > Campus Build Config and put it in a Resources folder.");

var fallback = CreateInstance<CampusBuildConfig>();
fallback._campus = Campus.Indianapolis;
fallback._displayName = DefaultDisplayName(Campus.Indianapolis);
return fallback;
}

/** <summary>Canonical display name for each campus.</summary> */
public static string DefaultDisplayName(Campus campus) =>
campus switch
{
Campus.WestLafayette => WestLafayetteDisplayName,
_ => IndianapolisDisplayName
};

#if UNITY_EDITOR
// Clearing the field in the Inspector refills it with the campus default,
// so the JSON campus value can never end up blank.
private void OnValidate()
{
if (string.IsNullOrWhiteSpace(_displayName))
_displayName = DefaultDisplayName(_campus);
}
#endif
}
11 changes: 11 additions & 0 deletions Assets/Session/CampusBuildConfig.cs.meta

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

38 changes: 38 additions & 0 deletions Assets/Session/ScoreRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;

/** <summary>
* One saved run written to score_records.json. Field names and types are the
* contract the results web app reads, so do not rename them:
* <code>
* {
* "id": "tf-002",
* "name": "HARPE",
* "score": 1515,
* "date": "2026-07-24",
* "campus": "West Lafayette",
* "lastPlaced": [4, 6, 18]
* }
* </code>
* </summary>
*/
[Serializable]
public class ScoreRecord
{
public string id;
public string name;
public long score;
public string date;
public string campus;
public int[] lastPlaced;
}

/** <summary>
* JsonUtility cannot deserialize a top-level JSON array, so reads and writes go
* through this wrapper. The file on disk is still a raw <c>[...]</c> array.
* </summary>
*/
[Serializable]
public class ScoreRecordFile
{
public ScoreRecord[] records = Array.Empty<ScoreRecord>();
}
11 changes: 11 additions & 0 deletions Assets/Session/ScoreRecord.cs.meta

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

Loading