diff --git a/Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs b/Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs new file mode 100644 index 0000000..50c073c --- /dev/null +++ b/Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs @@ -0,0 +1,242 @@ +using System.Globalization; +using System.Threading; +using NUnit.Framework; +using Nova.Gameplay; + +namespace Nova.Gameplay.Tests +{ + /// + /// Contract tests for , the pure brain + /// of the resource bar (issue #137): the three-way storage state against + /// the derived ceiling, the composed one-line model (value pairs, warning + /// text, severity), the culture-independent German number grouping and + /// the right-docked zone math. The bar's whole contract is pinned here + /// because the IMGUI component above it makes no decisions of its own. + /// + [TestFixture] + public class ResourceBarPresenterTests + { + // ---------------------------------------------------------------- + // Storage state + // ---------------------------------------------------------------- + + [Test] + public void EvaluateStorageState_BalanceBelowCeiling_IsNormal() + { + Assert.AreEqual( + StorageCeilingState.BelowCeiling, + ResourceBarPresenter.EvaluateStorageState(2318, 3000)); + } + + [Test] + public void EvaluateStorageState_BalanceExactlyOnCeiling_IsAtCeiling() + { + // The canonical post-change opening: 3.000 start against a 3.000 + // HQ ceiling — fresh income forfeits from the first deposit. + Assert.AreEqual( + StorageCeilingState.AtCeiling, + ResourceBarPresenter.EvaluateStorageState(3000, 3000)); + } + + [Test] + public void EvaluateStorageState_BalanceAboveCeiling_IsAboveCeiling() + { + // The #131 opening: 3.000 start against the old 2.000 HQ ceiling — + // the excess decays per second and the bar must say why. + Assert.AreEqual( + StorageCeilingState.AboveCeiling, + ResourceBarPresenter.EvaluateStorageState(3000, 2000)); + } + + [Test] + public void EvaluateStorageState_ZeroAgainstZero_IsNormalNotFull() + { + // A slot without any account building has no ceiling and no + // balance — "0 / 0" must not read as a full store. + Assert.AreEqual( + StorageCeilingState.BelowCeiling, + ResourceBarPresenter.EvaluateStorageState(0, 0)); + } + + [Test] + public void EvaluateStorageState_BalanceWithoutAnyCeiling_IsAboveCeiling() + { + // No completed HQ: the derived ceiling is 0, so every credit is + // excess and decays. + Assert.AreEqual( + StorageCeilingState.AboveCeiling, + ResourceBarPresenter.EvaluateStorageState(500, 0)); + } + + [Test] + public void EvaluateStorageState_NegativeInputs_AreClampedToZero() + { + Assert.AreEqual( + StorageCeilingState.BelowCeiling, + ResourceBarPresenter.EvaluateStorageState(-5, -10)); + Assert.AreEqual( + StorageCeilingState.AboveCeiling, + ResourceBarPresenter.EvaluateStorageState(5, -10)); + } + + // ---------------------------------------------------------------- + // Formatting + // ---------------------------------------------------------------- + + [Test] + public void FormatAetherium_ShowsBalanceAndCeilingWithGermanGrouping() + { + Assert.AreEqual( + "Aetherium 2.318 / 3.000", + ResourceBarPresenter.FormatAetherium(2318, 3000)); + } + + [Test] + public void FormatAetherium_GroupsEveryMagnitude() + { + Assert.AreEqual("Aetherium 0 / 0", ResourceBarPresenter.FormatAetherium(0, 0)); + Assert.AreEqual("Aetherium 5 / 12", ResourceBarPresenter.FormatAetherium(5, 12)); + Assert.AreEqual("Aetherium 999 / 1.000", ResourceBarPresenter.FormatAetherium(999, 1000)); + Assert.AreEqual( + "Aetherium 1.234.567 / 9.000.000", + ResourceBarPresenter.FormatAetherium(1234567, 9000000)); + Assert.AreEqual( + "Aetherium 9.223.372.036.854.775.807 / 9.223.372.036.854.775.807", + ResourceBarPresenter.FormatAetherium(long.MaxValue, long.MaxValue)); + } + + [Test] + public void FormatAetherium_UnderForeignCulture_KeepsGermanDots() + { + // The digits are assembled one by one precisely so a host culture + // cannot leak in — pin that with an en-US ambient culture. + CultureInfo previous = Thread.CurrentThread.CurrentCulture; + try + { + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US"); + Assert.AreEqual( + "Aetherium 2.318 / 3.000", + ResourceBarPresenter.FormatAetherium(2318, 3000)); + } + finally + { + Thread.CurrentThread.CurrentCulture = previous; + } + } + + [Test] + public void FormatPower_ShowsProvidedAgainstRequired() + { + Assert.AreEqual("Strom 130/80", ResourceBarPresenter.FormatPower(130, 80)); + Assert.AreEqual("Strom 60/80", ResourceBarPresenter.FormatPower(60, 80)); + } + + // ---------------------------------------------------------------- + // The composed model + // ---------------------------------------------------------------- + + [Test] + public void BuildModel_NormalState_HasNoWarning() + { + ResourceBarModel model = ResourceBarPresenter.BuildModel(2318, 3000, 130, 80); + + Assert.AreEqual("Aetherium 2.318 / 3.000", model.AetheriumText); + Assert.AreEqual("Strom 130/80", model.PowerText); + Assert.IsNull(model.WarningText); + Assert.AreEqual(StorageCeilingState.BelowCeiling, model.StorageState); + Assert.IsFalse(model.IsLowPower); + Assert.IsFalse(model.IsCritical); + } + + [Test] + public void BuildModel_ExactlyBalancedGrid_IsNotADeficit() + { + // The sim's own rule (IsLowPower: required > provided) — an + // exactly-balanced grid runs at full speed and must not warn. + ResourceBarModel model = ResourceBarPresenter.BuildModel(2318, 3000, 80, 80); + + Assert.IsFalse(model.IsLowPower); + Assert.IsNull(model.WarningText); + } + + [Test] + public void BuildModel_FullStore_WarnsAmber() + { + ResourceBarModel model = ResourceBarPresenter.BuildModel(3000, 3000, 130, 80); + + Assert.AreEqual(StorageCeilingState.AtCeiling, model.StorageState); + Assert.AreEqual("Lager voll — Einnahmen verfallen", model.WarningText); + Assert.IsFalse(model.IsCritical, "a full store stops income but burns nothing — amber, not red"); + } + + [Test] + public void BuildModel_Overflow_WarnsCriticalAndNamesTheAction() + { + ResourceBarModel model = ResourceBarPresenter.BuildModel(3000, 2000, 130, 80); + + Assert.AreEqual(StorageCeilingState.AboveCeiling, model.StorageState); + Assert.AreEqual("Überschuss verfällt — Lager bauen!", model.WarningText); + Assert.IsTrue(model.IsCritical); + } + + [Test] + public void BuildModel_PowerDeficit_WarnsCriticalAndNamesTheConsequences() + { + ResourceBarModel model = ResourceBarPresenter.BuildModel(2318, 3000, 60, 80); + + Assert.IsTrue(model.IsLowPower); + Assert.AreEqual("Strom 60/80", model.PowerText); + Assert.AreEqual("Strommangel — Produktion ½ · Reparatur ½ · Radar aus", model.WarningText); + Assert.IsTrue(model.IsCritical); + } + + [Test] + public void BuildModel_FullStoreAndDeficit_JoinsBothWarningsStorageFirst() + { + ResourceBarModel model = ResourceBarPresenter.BuildModel(3000, 3000, 60, 80); + + Assert.AreEqual( + "Lager voll — Einnahmen verfallen | Strommangel — Produktion ½ · Reparatur ½ · Radar aus", + model.WarningText); + Assert.IsTrue(model.IsCritical, "the deficit is an active penalty — the join escalates to red"); + } + + // ---------------------------------------------------------------- + // Zone math + // ---------------------------------------------------------------- + + [Test] + public void TopRightZone_DocksToTheRightMargin() + { + HudRect zone = ResourceBarPresenter.TopRightZone( + screenWidth: 1280f, top: 31f, contentWidth: 400f, height: 22f, margin: 8f); + + Assert.AreEqual(1280f - 8f - 400f, zone.X); + Assert.AreEqual(31f, zone.Y); + Assert.AreEqual(400f, zone.Width); + Assert.AreEqual(22f, zone.Height); + } + + [Test] + public void TopRightZone_ContentWiderThanScreen_ClampsToTheMargins() + { + HudRect zone = ResourceBarPresenter.TopRightZone( + screenWidth: 300f, top: 31f, contentWidth: 900f, height: 22f, margin: 8f); + + Assert.AreEqual(8f, zone.X, "grows leftward from the margin instead of running off the right edge"); + Assert.AreEqual(300f - 16f, zone.Width); + } + + [Test] + public void TopRightZone_DegenerateInputs_NeverGoNegative() + { + HudRect zone = ResourceBarPresenter.TopRightZone( + screenWidth: 0f, top: -5f, contentWidth: -10f, height: -1f, margin: 8f); + + Assert.GreaterOrEqual(zone.X, 0f); + Assert.GreaterOrEqual(zone.Y, 0f); + Assert.GreaterOrEqual(zone.Width, 0f); + Assert.GreaterOrEqual(zone.Height, 0f); + } + } +} diff --git a/Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs.meta b/Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs.meta new file mode 100644 index 0000000..aaf8653 --- /dev/null +++ b/Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 77dd8a8871344738ab452d10ac924e67 \ No newline at end of file diff --git a/Assets/_Project/Data/Maps/MAP_Glutrinne.asset b/Assets/_Project/Data/Maps/MAP_Glutrinne.asset index b80c671..406d5fc 100644 --- a/Assets/_Project/Data/Maps/MAP_Glutrinne.asset +++ b/Assets/_Project/Data/Maps/MAP_Glutrinne.asset @@ -25,3 +25,13 @@ MonoBehaviour: - {x: 24, y: 40} - {x: 100, y: 84} - {x: 62, y: 62} + - {x: 54, y: 54} + - {x: 70, y: 70} + - {x: 54, y: 70} + - {x: 70, y: 54} + - {x: 44, y: 24} + - {x: 80, y: 100} + - {x: 44, y: 80} + - {x: 80, y: 44} + - {x: 24, y: 96} + - {x: 100, y: 28} diff --git a/Assets/_Project/Editor/BootstrapSceneGenerator.cs b/Assets/_Project/Editor/BootstrapSceneGenerator.cs index dfd9e40..852883e 100644 --- a/Assets/_Project/Editor/BootstrapSceneGenerator.cs +++ b/Assets/_Project/Editor/BootstrapSceneGenerator.cs @@ -393,6 +393,14 @@ private static GameObject CreateUiObject(MatchRunner runner, Camera camera) WireReference(hud, "_runner", runner); WireReference(hud, "_input", input); + // The resource bar is added LAST on purpose: AddComponent order is + // the OnGUI paint order, so it draws over the debug status line + // instead of under it. It takes no _input — it is a pure readout, + // deliberately absent from IsPointerOverHud, and swallows no + // clicks (the reasoning sits in ResourceBarHud's docstring). + ResourceBarHud resourceBar = uiObject.AddComponent(); + WireReference(resourceBar, "_runner", runner); + return uiObject; } diff --git a/Assets/_Project/Scenes/Bootstrap.unity b/Assets/_Project/Scenes/Bootstrap.unity index 0806275..82f21b2 100644 --- a/Assets/_Project/Scenes/Bootstrap.unity +++ b/Assets/_Project/Scenes/Bootstrap.unity @@ -119,7 +119,7 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} ---- !u!1 &128460811 +--- !u!1 &105837768 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -127,120 +127,151 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 128460815} - - component: {fileID: 128460813} - - component: {fileID: 128460812} - - component: {fileID: 128460814} + - component: {fileID: 105837771} + - component: {fileID: 105837770} + - component: {fileID: 105837769} m_Layer: 0 - m_Name: Main Camera - m_TagString: MainCamera + m_Name: IngameMusic + m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &128460812 +--- !u!114 &105837769 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 128460811} + m_GameObject: {fileID: 105837768} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 7e207447d841f8c67cd26bb722acdaf5, type: 3} + m_Script: {fileID: 11500000, guid: cdcf36c4668804ae6a8c2889ba4c9d33, type: 3} m_Name: - m_EditorClassIdentifier: Nova.Presentation::Nova.Presentation.RtsCameraController - _mapWidth: 128 - _mapHeight: 128 - _panSpeed: 18 - _panSpeedHeightScale: 3 - _edgePanEnabled: 1 - _edgePanMargin: 12 - _zoomSpeed: 60 - _minHeight: 12 - _maxHeight: 90 - _pitchDegrees: 55 - _rotationSpeed: 90 - _middleDragDegreesPerPixel: 0.35 - _startFocusX: 8 - _startFocusZ: 6 - _startHeight: 42 - _startYawDegrees: 45 ---- !u!20 &128460813 -Camera: + m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.MusicDirector + _source: {fileID: 105837770} + _runner: {fileID: 966459987} + _bootstrap: {fileID: 966459986} + _menu: {fileID: 1530823775} + _playlist: + - {fileID: 8300000, guid: 3c291cb887cd427f8fe213d0cc97e7ba, type: 3} + - {fileID: 8300000, guid: 159885d03b3d4674a15ae77a1275fd85, type: 3} + - {fileID: 8300000, guid: 8605bd0c749044cbaaa4a64ec990b5b5, type: 3} + _fadeSeconds: 1.25 + _trackGapSeconds: 2 +--- !u!82 &105837770 +AudioSource: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 128460811} + m_GameObject: {fileID: 105837768} m_Enabled: 1 - serializedVersion: 2 - m_ClearFlags: 2 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} - m_projectionMatrixMode: 1 - m_GateFitMode: 2 - m_FOVAxisMode: 0 - m_Iso: 200 - m_ShutterSpeed: 0.005 - m_Aperture: 16 - m_FocusDistance: 10 - m_FocalLength: 50 - m_BladeCount: 5 - m_Curvature: {x: 2, y: 11} - m_BarrelClipping: 0.25 - m_Anamorphism: 0 - m_SensorSize: {x: 36, y: 24} - m_LensShift: {x: 0, y: 0} - m_NormalizedViewPortRect: + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: -161172014317909320, guid: 7831b040d10e94eb9bddf12783bcee5e, type: 2} + m_audioClip: {fileID: 0} + m_Resource: {fileID: 0} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 - near clip plane: 0.3 - far clip plane: 1000 - field of view: 60 - orthographic: 0 - orthographic size: 5 - m_Depth: 0 - m_CullingMask: + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingPath: -1 - m_TargetTexture: {fileID: 0} - m_TargetDisplay: 0 - m_TargetEye: 3 - m_HDR: 1 - m_AllowMSAA: 1 - m_AllowDynamicResolution: 0 - m_ForceIntoRT: 0 - m_OcclusionCulling: 1 - m_StereoConvergence: 10 - m_StereoSeparation: 0.022 ---- !u!81 &128460814 -AudioListener: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 128460811} - m_Enabled: 1 ---- !u!4 &128460815 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!4 &105837771 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 128460811} + m_GameObject: {fileID: 105837768} serializedVersion: 2 - m_LocalRotation: {x: 0.5, y: 0, z: 0, w: 0.8660254} - m_LocalPosition: {x: 64, y: 60, z: -20} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &214746646 +--- !u!1 &250056992 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -248,21 +279,22 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 214746661} - - component: {fileID: 214746660} - - component: {fileID: 214746659} - - component: {fileID: 214746658} - - component: {fileID: 214746657} - - component: {fileID: 214746656} - - component: {fileID: 214746655} - - component: {fileID: 214746654} - - component: {fileID: 214746653} - - component: {fileID: 214746652} - - component: {fileID: 214746651} - - component: {fileID: 214746650} - - component: {fileID: 214746649} - - component: {fileID: 214746648} - - component: {fileID: 214746647} + - component: {fileID: 250057008} + - component: {fileID: 250057007} + - component: {fileID: 250057006} + - component: {fileID: 250057005} + - component: {fileID: 250057004} + - component: {fileID: 250057003} + - component: {fileID: 250057002} + - component: {fileID: 250057001} + - component: {fileID: 250057000} + - component: {fileID: 250056999} + - component: {fileID: 250056998} + - component: {fileID: 250056997} + - component: {fileID: 250056996} + - component: {fileID: 250056995} + - component: {fileID: 250056994} + - component: {fileID: 250056993} m_Layer: 0 m_Name: UI m_TagString: Untagged @@ -270,92 +302,112 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &214746647 +--- !u!114 &250056993 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 250056992} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e920fc747d1c406fb1fa611b6934aa74, type: 3} + m_Name: + m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.ResourceBarHud + _runner: {fileID: 966459987} + _uiScale: 1.5 + _fontSize: 13 + _margin: 8 + _topOffset: 31 + _barHeight: 22 + _warningColor: {r: 1, g: 0.72, b: 0.25, a: 1} + _criticalColor: {r: 1, g: 0.38, b: 0.32, a: 1} +--- !u!114 &250056994 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 743caecd4c5e6831d67dc968613cbfc6, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.DebugHud - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} _visible: 0 _uiScale: 1.5 _fontSize: 13 ---- !u!114 &214746648 +--- !u!114 &250056995 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 4839e6caa28240839b51760036ea05f0, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.PauseMenuHud - _runner: {fileID: 2042920973} - _menu: {fileID: 1629940937} - _matchFrame: {fileID: 214746649} - _input: {fileID: 214746660} + _runner: {fileID: 966459987} + _menu: {fileID: 1530823775} + _matchFrame: {fileID: 250056996} + _input: {fileID: 250057007} _uiScale: 1.5 _panelWidth: 340 ---- !u!114 &214746649 +--- !u!114 &250056996 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9a8f5ae5c96d7491494f6d2974f32f3b, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.MatchFrameHud - _runner: {fileID: 2042920973} - _bootstrap: {fileID: 2042920972} - _menu: {fileID: 1629940937} - _views: {fileID: 2042920974} + _runner: {fileID: 966459987} + _bootstrap: {fileID: 966459986} + _menu: {fileID: 1530823775} + _views: {fileID: 966459988} _uiScale: 1.5 _panelWidth: 340 ---- !u!114 &214746650 +--- !u!114 &250056997 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 3fbdd2dfe068b4dc0a6f71442dc18c31, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.HealthBarHud - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} _uiScale: 1.5 _barWidth: 26 _barHeight: 4 _unitHeightOffset: 1.6 _buildingHeightOffset: 3.4 ---- !u!114 &214746651 +--- !u!114 &250056998 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 885e2e9fac8724df08b326c82e983510, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.MinimapHud - _runner: {fileID: 2042920973} - _buildMenu: {fileID: 214746654} + _runner: {fileID: 966459987} + _buildMenu: {fileID: 250057001} _uiScale: 1.5 _mapSize: 168 _margin: 8 @@ -365,92 +417,92 @@ MonoBehaviour: _unitDotSize: 3 _buildingDotSize: 4.5 _radarPingColor: {r: 1, g: 0.55, b: 0.1, a: 1} ---- !u!114 &214746652 +--- !u!114 &250056999 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 956e9df8ef76b491abc04e94045ee0d7, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.FogOfWarOverlayView - _runner: {fileID: 2042920973} + _runner: {fileID: 966459987} _unexploredAlpha: 0.85 _exploredAlpha: 0.45 _overlayHeight: 0.04 ---- !u!114 &214746653 +--- !u!114 &250057000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 79cf751f533e746e993958254d4d427e, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.CommandCardHud - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} - _buildMenu: {fileID: 214746654} - _bootstrap: {fileID: 2042920972} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} + _buildMenu: {fileID: 250057001} + _bootstrap: {fileID: 966459986} _uiScale: 1.5 _panelWidth: 236 _panelMargin: 8 ---- !u!114 &214746654 +--- !u!114 &250057001 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 22628296e159b4d5a8e9c701b125cee0, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.BuildMenuHud - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} _uiScale: 1.5 _buttonWidth: 108 _buttonHeight: 62 _barMargin: 8 _hintMinSeconds: 6 _hintMaxSeconds: 90 ---- !u!114 &214746655 +--- !u!114 &250057002 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: a2b97178f25d64f78897be61bbbb70dd, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.ConstructionSiteMarkerView - _runner: {fileID: 2042920973} + _runner: {fileID: 966459987} _markerHeight: 0.07 _buildingColor: {r: 0.35, g: 0.9, b: 0.55, a: 0.55} _pausedColor: {r: 1, g: 0.62, b: 0.15, a: 1} _pulseSpeed: 4 ---- !u!114 &214746656 +--- !u!114 &250057003 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 60b14ad0059d4ecbb3bb0f16e8f28aaf, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.BuildZoneOverlayView - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} _buildableColor: serializedVersion: 2 rgba: 442164806 @@ -459,81 +511,82 @@ MonoBehaviour: rgba: 740857599 _overlayHeight: 0.05 _repaintIntervalSeconds: 0.25 ---- !u!114 &214746657 +--- !u!114 &250057004 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2cef43f34213c4155a30156e42d58fa4, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.PlacementGhostView - _input: {fileID: 214746660} + _input: {fileID: 250057007} _ghostHeight: 0.09 _validColor: {r: 0.3, g: 1, b: 0.4, a: 1} _invalidColor: {r: 1, g: 0.3, b: 0.25, a: 1} ---- !u!114 &214746658 +--- !u!114 &250057005 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f602abebcfdbc4649af799cbeddc3fcc, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.RallyFlagView - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} _markerHeight: 0.07 _flagSize: 1 _lineWidth: 0.18 _rallyColor: {r: 1, g: 0.82, b: 0.25, a: 1} ---- !u!114 &214746659 +--- !u!114 &250057006 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: bc78ab4760d9d417bb2865e8edf60d3d, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.SelectionMarkerView - _runner: {fileID: 2042920973} - _input: {fileID: 214746660} - _views: {fileID: 2042920974} + _runner: {fileID: 966459987} + _input: {fileID: 250057007} + _views: {fileID: 966459988} _markerHeight: 0.06 _unitMarkerSize: 2.4 _buildingMarkerMargin: 0.4 _selectionColor: {r: 0.35, g: 1, b: 0.4, a: 1} ---- !u!114 &214746660 +--- !u!114 &250057007 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 39a0624d641e60a06323d84ab3edc209, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.RtsDeviceInput - _runner: {fileID: 2042920973} - _camera: {fileID: 128460813} - _buildMenu: {fileID: 214746654} - _commandCard: {fileID: 214746653} + _runner: {fileID: 966459987} + _camera: {fileID: 536714283} + _buildMenu: {fileID: 250057001} + _commandCard: {fileID: 250057000} _minimap: {fileID: 0} _menu: {fileID: 0} _groundPlaneY: 0 _dragThresholdPixels: 8 _pickRadiusWorld: 1.5 _fieldPickRadiusWorld: 2 + _doubleClickSeconds: 0.35 _buildingDefId: 5 _altBuildingDefId: 7 _unitDefId: 2 @@ -550,13 +603,13 @@ MonoBehaviour: _lightTankDefId: 15 _battleTankDefId: 16 _artilleryDefId: 17 ---- !u!4 &214746661 +--- !u!4 &250057008 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 214746646} + m_GameObject: {fileID: 250056992} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -565,7 +618,7 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &526112586 +--- !u!1 &380136776 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -573,187 +626,35 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 526112589} - - component: {fileID: 526112588} - - component: {fileID: 526112587} + - component: {fileID: 380136779} + - component: {fileID: 380136778} + - component: {fileID: 380136777} m_Layer: 0 - m_Name: IngameMusic + m_Name: Audio m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &526112587 +--- !u!114 &380136777 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 526112586} + m_GameObject: {fileID: 380136776} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: cdcf36c4668804ae6a8c2889ba4c9d33, type: 3} + m_Script: {fileID: 11500000, guid: 209209a86486d4b3a8bed38f9ae33c50, type: 3} m_Name: - m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.MusicDirector - _source: {fileID: 526112588} - _runner: {fileID: 2042920973} - _bootstrap: {fileID: 2042920972} - _menu: {fileID: 1629940937} - _playlist: - - {fileID: 8300000, guid: 3c291cb887cd427f8fe213d0cc97e7ba, type: 3} - - {fileID: 8300000, guid: 159885d03b3d4674a15ae77a1275fd85, type: 3} - - {fileID: 8300000, guid: 8605bd0c749044cbaaa4a64ec990b5b5, type: 3} - _fadeSeconds: 1.25 - _trackGapSeconds: 2 ---- !u!82 &526112588 -AudioSource: + m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.SfxSettingsBridge +--- !u!114 &380136778 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 526112586} - m_Enabled: 1 - serializedVersion: 4 - OutputAudioMixerGroup: {fileID: -161172014317909320, guid: 7831b040d10e94eb9bddf12783bcee5e, type: 2} - m_audioClip: {fileID: 0} - m_Resource: {fileID: 0} - m_PlayOnAwake: 1 - m_Volume: 1 - m_Pitch: 1 - Loop: 0 - Mute: 0 - Spatialize: 0 - SpatializePostEffects: 0 - Priority: 128 - DopplerLevel: 1 - MinDistance: 1 - MaxDistance: 500 - Pan2D: 0 - rolloffMode: 0 - BypassEffects: 0 - BypassListenerEffects: 0 - BypassReverbZones: 0 - rolloffCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 1 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - panLevelCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - spreadCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - reverbZoneMixCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 ---- !u!4 &526112589 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 526112586} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &802321239 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 802321242} - - component: {fileID: 802321241} - - component: {fileID: 802321240} - m_Layer: 0 - m_Name: Audio - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &802321240 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 802321239} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 209209a86486d4b3a8bed38f9ae33c50, type: 3} - m_Name: - m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.SfxSettingsBridge ---- !u!114 &802321241 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 802321239} + m_GameObject: {fileID: 380136776} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5dc2508a98b444101ad6cef0e77cec67, type: 3} @@ -777,13 +678,13 @@ MonoBehaviour: _weaponsGroup: {fileID: -2573851237862643720, guid: 7831b040d10e94eb9bddf12783bcee5e, type: 2} _unitsGroup: {fileID: -3201287591632689540, guid: 7831b040d10e94eb9bddf12783bcee5e, type: 2} _uiGroup: {fileID: 2761139959417956413, guid: 7831b040d10e94eb9bddf12783bcee5e, type: 2} ---- !u!4 &802321242 +--- !u!4 &380136779 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 802321239} + m_GameObject: {fileID: 380136776} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -792,7 +693,7 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &954524122 +--- !u!1 &536714281 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -800,50 +701,120 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 954524124} - - component: {fileID: 954524123} + - component: {fileID: 536714285} + - component: {fileID: 536714283} + - component: {fileID: 536714282} + - component: {fileID: 536714284} m_Layer: 0 - m_Name: Map - m_TagString: Untagged + m_Name: Main Camera + m_TagString: MainCamera m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &954524123 +--- !u!114 &536714282 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 954524122} + m_GameObject: {fileID: 536714281} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9b36a2ce3f98b46249dd0de03a577164, type: 3} + m_Script: {fileID: 11500000, guid: 7e207447d841f8c67cd26bb722acdaf5, type: 3} m_Name: - m_EditorClassIdentifier: Nova.Presentation::Nova.Presentation.Maps.GlutrinneBlockoutView - _bootstrap: {fileID: 2042920972} - _groundRenderer: {fileID: 1162144974} - _sandColor: {r: 0.72, g: 0.6, b: 0.42, a: 1} - _crystalColor: {r: 0.2, g: 0.85, b: 0.9, a: 1} - _edgeColor: {r: 0.28, g: 0.24, b: 0.2, a: 1} - _rockColor: {r: 0.36, g: 0.31, b: 0.26, a: 1} - _groundTextureTiling: {x: 32, y: 32} ---- !u!4 &954524124 + m_EditorClassIdentifier: Nova.Presentation::Nova.Presentation.RtsCameraController + _mapWidth: 128 + _mapHeight: 128 + _panSpeed: 18 + _panSpeedHeightScale: 3 + _edgePanEnabled: 1 + _edgePanMargin: 12 + _zoomSpeed: 60 + _minHeight: 12 + _maxHeight: 90 + _pitchDegrees: 55 + _rotationSpeed: 90 + _middleDragDegreesPerPixel: 0.35 + _startFocusX: 8 + _startFocusZ: 6 + _startHeight: 42 + _startYawDegrees: 45 +--- !u!20 &536714283 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 536714281} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!81 &536714284 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 536714281} + m_Enabled: 1 +--- !u!4 &536714285 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 954524122} + m_GameObject: {fileID: 536714281} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalRotation: {x: 0.5, y: 0, z: 0, w: 0.8660254} + m_LocalPosition: {x: 64, y: 60, z: -20} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1162144973 +--- !u!1 &620007468 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -851,10 +822,10 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1162144977} - - component: {fileID: 1162144976} - - component: {fileID: 1162144975} - - component: {fileID: 1162144974} + - component: {fileID: 620007472} + - component: {fileID: 620007471} + - component: {fileID: 620007470} + - component: {fileID: 620007469} m_Layer: 0 m_Name: Ground m_TagString: Untagged @@ -862,13 +833,13 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 2147483647 m_IsActive: 1 ---- !u!23 &1162144974 +--- !u!23 &620007469 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1162144973} + m_GameObject: {fileID: 620007468} m_Enabled: 1 m_CastShadows: 1 m_ReceiveShadows: 1 @@ -911,13 +882,13 @@ MeshRenderer: m_SortingOrder: 0 m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!64 &1162144975 +--- !u!64 &620007470 MeshCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1162144973} + m_GameObject: {fileID: 620007468} m_Material: {fileID: 0} m_IncludeLayers: serializedVersion: 2 @@ -933,21 +904,21 @@ MeshCollider: m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} ---- !u!33 &1162144976 +--- !u!33 &620007471 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1162144973} + m_GameObject: {fileID: 620007468} m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &1162144977 +--- !u!4 &620007472 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1162144973} + m_GameObject: {fileID: 620007468} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 64, y: 0, z: 64} @@ -956,7 +927,7 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1309622895 +--- !u!1 &966459985 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -964,65 +935,92 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1309622898} - - component: {fileID: 1309622897} - - component: {fileID: 1309622896} + - component: {fileID: 966459990} + - component: {fileID: 966459987} + - component: {fileID: 966459986} + - component: {fileID: 966459989} + - component: {fileID: 966459988} m_Layer: 0 - m_Name: VersionBadge + m_Name: Match m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &1309622896 +--- !u!114 &966459986 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1309622895} + m_GameObject: {fileID: 966459985} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b5291f1c2dd449aea7a5874cd8d818bb, type: 3} + m_Script: {fileID: 11500000, guid: 0543a6c6c2d8cbd976ba484a58c2ddc5, type: 3} m_Name: - m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.VersionBadge - _document: {fileID: 1309622897} - _font: {fileID: 12800000, guid: f028379ddea954641b86d6e9d5be7473, type: 3} - _fontSize: 12 - _marginLeft: 10 - _marginBottom: 8 - _textColor: {r: 0.88, g: 0.91, b: 0.95, a: 1} - _opacity: 0.55 ---- !u!114 &1309622897 + m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.Match.MatchBootstrap + AutoStart: 0 + _seed: 16032251723485610609 + _mapWidth: 128 + _mapHeight: 128 + _entityCapacity: 1024 + UseDefinitionStats: 1 +--- !u!114 &966459987 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1309622895} + m_GameObject: {fileID: 966459985} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} + m_Script: {fileID: 11500000, guid: 3b9301a799fe64f85afe9906dc15bc39, type: 3} m_Name: - m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument - m_PanelSettings: {fileID: 11400000, guid: db0593cf705514e1aa6af7b41cca6743, type: 2} - m_ParentUI: {fileID: 0} - sourceAsset: {fileID: 0} - m_SortingOrder: 0 - m_Position: 0 - m_WorldSpaceSizeMode: 1 - m_WorldSpaceWidth: 1920 - m_WorldSpaceHeight: 1080 - m_PivotReferenceSize: 0 - m_Pivot: 0 - m_WorldSpaceCollider: {fileID: 0} ---- !u!4 &1309622898 + m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.Match.MatchRunner + _seed: 12345 + _mapWidth: 128 + _mapHeight: 128 + _maxUnits: 2048 +--- !u!114 &966459988 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 966459985} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bad3b0760ec204c94bb15e3918bbc921, type: 3} + m_Name: + m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.Match.UnitViewManager + _matchRunner: {fileID: 966459987} + _unitPrefab: {fileID: 0} + _combatEffects: {fileID: 966459989} + _assetMappings: {fileID: 11400000, guid: 54dfdf235acc54eecbef61df13a6b356, type: 2} + _interpolationSpeed: 25 + _viewerTeamOverride: -1 + _unknownPlayerColor: {r: 0.62, g: 0.62, b: 0.62, a: 1} + _damagedColor: {r: 0.42, g: 0.05, b: 0.05, a: 1} + _healthTintFloor: 0.25 +--- !u!114 &966459989 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 966459985} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4cba4e364582443899c3490f303f72f, type: 3} + m_Name: + m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.CombatFeedback.CombatEffectController +--- !u!4 &966459990 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1309622895} + m_GameObject: {fileID: 966459985} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -1031,7 +1029,7 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1521561486 +--- !u!1 &1216497469 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1039,96 +1037,50 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1521561488} - - component: {fileID: 1521561487} + - component: {fileID: 1216497471} + - component: {fileID: 1216497470} m_Layer: 0 - m_Name: Directional Light + m_Name: Map m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!108 &1521561487 -Light: +--- !u!114 &1216497470 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1521561486} + m_GameObject: {fileID: 1216497469} m_Enabled: 1 - serializedVersion: 13 - m_Type: 1 - m_Color: {r: 1, g: 0.87, b: 0.7, a: 1} - m_Intensity: 1.15 - m_Range: 10 - m_SpotAngle: 30 - m_InnerSpotAngle: 21.802082 - m_CookieSize2D: {x: 0.5, y: 0.5} - m_Shadows: - m_Type: 2 - m_Resolution: -1 - m_CustomResolution: -1 - m_Strength: 0.85 - m_Bias: 0.05 - m_NormalBias: 0.4 - m_NearPlane: 0.2 - m_CullingMatrixOverride: - e00: 1 - e01: 0 - e02: 0 - e03: 0 - e10: 0 - e11: 1 - e12: 0 - e13: 0 - e20: 0 - e21: 0 - e22: 1 - e23: 0 - e30: 0 - e31: 0 - e32: 0 - e33: 1 - m_UseCullingMatrixOverride: 0 - m_Cookie: {fileID: 0} - m_DrawHalo: 0 - m_Flare: {fileID: 0} - m_RenderMode: 0 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingLayerMask: 1 - m_Lightmapping: 4 - m_LightShadowCasterMode: 0 - m_AreaSize: {x: 1, y: 1} - m_BounceIntensity: 1 - m_ColorTemperature: 6570 - m_UseColorTemperature: 0 - m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} - m_UseBoundingSphereOverride: 0 - m_UseViewFrustumForShadowCasterCull: 1 - m_ForceVisible: 0 - m_ShapeRadius: 0.025 - m_ShadowAngle: 0 - m_LightUnit: 1 - m_LuxAtDistance: 1 - m_EnableSpotReflector: 1 ---- !u!4 &1521561488 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b36a2ce3f98b46249dd0de03a577164, type: 3} + m_Name: + m_EditorClassIdentifier: Nova.Presentation::Nova.Presentation.Maps.GlutrinneBlockoutView + _bootstrap: {fileID: 966459986} + _groundRenderer: {fileID: 620007469} + _sandColor: {r: 0.72, g: 0.6, b: 0.42, a: 1} + _crystalColor: {r: 0.2, g: 0.85, b: 0.9, a: 1} + _edgeColor: {r: 0.28, g: 0.24, b: 0.2, a: 1} + _rockColor: {r: 0.36, g: 0.31, b: 0.26, a: 1} + _groundTextureTiling: {x: 32, y: 32} +--- !u!4 &1216497471 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1521561486} + m_GameObject: {fileID: 1216497469} serializedVersion: 2 - m_LocalRotation: {x: 0.12450414, y: -0.8617815, z: 0.25527132, w: 0.42031893} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1629940936 +--- !u!1 &1530823774 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1136,11 +1088,11 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1629940941} - - component: {fileID: 1629940940} - - component: {fileID: 1629940939} - - component: {fileID: 1629940938} - - component: {fileID: 1629940937} + - component: {fileID: 1530823779} + - component: {fileID: 1530823778} + - component: {fileID: 1530823777} + - component: {fileID: 1530823776} + - component: {fileID: 1530823775} m_Layer: 0 m_Name: MainMenu m_TagString: Untagged @@ -1148,23 +1100,23 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &1629940937 +--- !u!114 &1530823775 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1629940936} + m_GameObject: {fileID: 1530823774} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e77b731ae61b8471192a9ee5a7727fdb, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.MainMenuController - _document: {fileID: 1629940940} - _bootstrap: {fileID: 2042920972} - _music: {fileID: 1629940938} - _cameraRig: {fileID: 128460812} - _gameplayHudRoot: {fileID: 214746646} + _document: {fileID: 1530823778} + _bootstrap: {fileID: 966459986} + _music: {fileID: 1530823776} + _cameraRig: {fileID: 536714282} + _gameplayHudRoot: {fileID: 250056992} _keyArt: {fileID: 2800000, guid: 0f0988a5c327e451a990f97269d3ceee, type: 3} _titleFont: {fileID: 12800000, guid: b95db3c2c8b9045b683866f321683fad, type: 3} _bodyFont: {fileID: 12800000, guid: f028379ddea954641b86d6e9d5be7473, type: 3} @@ -1191,28 +1143,28 @@ MonoBehaviour: _disabledTextColor: {r: 0.52, g: 0.55, b: 0.6, a: 1} _disabledEdgeColor: {r: 0.34, g: 0.38, b: 0.44, a: 0.45} _saveIntervalSeconds: 0.35 ---- !u!114 &1629940938 +--- !u!114 &1530823776 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1629940936} + m_GameObject: {fileID: 1530823774} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: bccc1231bdd82465fa987e6ad5bb61e2, type: 3} m_Name: m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.MenuMusicPlayer - _source: {fileID: 1629940939} + _source: {fileID: 1530823777} _clip: {fileID: 8300000, guid: 85a05f77cb9f94a3684e85a7325e0184, type: 3} _fadeOutSeconds: 1.25 ---- !u!82 &1629940939 +--- !u!82 &1530823777 AudioSource: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1629940936} + m_GameObject: {fileID: 1530823774} m_Enabled: 1 serializedVersion: 4 OutputAudioMixerGroup: {fileID: -161172014317909320, guid: 7831b040d10e94eb9bddf12783bcee5e, type: 2} @@ -1303,13 +1255,13 @@ AudioSource: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 ---- !u!114 &1629940940 +--- !u!114 &1530823778 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1629940936} + m_GameObject: {fileID: 1530823774} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} @@ -1326,13 +1278,13 @@ MonoBehaviour: m_PivotReferenceSize: 0 m_Pivot: 0 m_WorldSpaceCollider: {fileID: 0} ---- !u!4 &1629940941 +--- !u!4 &1530823779 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1629940936} + m_GameObject: {fileID: 1530823774} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -1341,7 +1293,7 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &2042920971 +--- !u!1 &1883692213 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1349,94 +1301,164 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 2042920976} - - component: {fileID: 2042920973} - - component: {fileID: 2042920972} - - component: {fileID: 2042920975} - - component: {fileID: 2042920974} + - component: {fileID: 1883692216} + - component: {fileID: 1883692215} + - component: {fileID: 1883692214} m_Layer: 0 - m_Name: Match + m_Name: VersionBadge m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &2042920972 +--- !u!114 &1883692214 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2042920971} + m_GameObject: {fileID: 1883692213} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 0543a6c6c2d8cbd976ba484a58c2ddc5, type: 3} + m_Script: {fileID: 11500000, guid: b5291f1c2dd449aea7a5874cd8d818bb, type: 3} m_Name: - m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.Match.MatchBootstrap - AutoStart: 0 - _seed: 16032251723485610609 - _mapWidth: 128 - _mapHeight: 128 - _entityCapacity: 1024 - UseDefinitionStats: 1 ---- !u!114 &2042920973 + m_EditorClassIdentifier: Nova.Presentation.UI::Nova.Presentation.UI.VersionBadge + _document: {fileID: 1883692215} + _font: {fileID: 12800000, guid: f028379ddea954641b86d6e9d5be7473, type: 3} + _fontSize: 12 + _marginLeft: 10 + _marginBottom: 8 + _textColor: {r: 0.88, g: 0.91, b: 0.95, a: 1} + _opacity: 0.55 +--- !u!114 &1883692215 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2042920971} + m_GameObject: {fileID: 1883692213} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 3b9301a799fe64f85afe9906dc15bc39, type: 3} + m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} m_Name: - m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.Match.MatchRunner - _seed: 12345 - _mapWidth: 128 - _mapHeight: 128 - _maxUnits: 2048 ---- !u!114 &2042920974 -MonoBehaviour: + m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument + m_PanelSettings: {fileID: 11400000, guid: db0593cf705514e1aa6af7b41cca6743, type: 2} + m_ParentUI: {fileID: 0} + sourceAsset: {fileID: 0} + m_SortingOrder: 0 + m_Position: 0 + m_WorldSpaceSizeMode: 1 + m_WorldSpaceWidth: 1920 + m_WorldSpaceHeight: 1080 + m_PivotReferenceSize: 0 + m_Pivot: 0 + m_WorldSpaceCollider: {fileID: 0} +--- !u!4 &1883692216 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2042920971} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: bad3b0760ec204c94bb15e3918bbc921, type: 3} - m_Name: - m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.Match.UnitViewManager - _matchRunner: {fileID: 2042920973} - _unitPrefab: {fileID: 0} - _combatEffects: {fileID: 2042920975} - _assetMappings: {fileID: 11400000, guid: 54dfdf235acc54eecbef61df13a6b356, type: 2} - _interpolationSpeed: 25 - _viewerTeamOverride: -1 - _unknownPlayerColor: {r: 0.62, g: 0.62, b: 0.62, a: 1} - _damagedColor: {r: 0.42, g: 0.05, b: 0.05, a: 1} - _healthTintFloor: 0.25 ---- !u!114 &2042920975 -MonoBehaviour: + m_GameObject: {fileID: 1883692213} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2123726880 +GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2042920971} + serializedVersion: 6 + m_Component: + - component: {fileID: 2123726882} + - component: {fileID: 2123726881} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &2123726881 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2123726880} m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f4cba4e364582443899c3490f303f72f, type: 3} - m_Name: - m_EditorClassIdentifier: Nova.Gameplay::Nova.Gameplay.CombatFeedback.CombatEffectController ---- !u!4 &2042920976 + serializedVersion: 13 + m_Type: 1 + m_Color: {r: 1, g: 0.87, b: 0.7, a: 1} + m_Intensity: 1.15 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.802082 + m_CookieSize2D: {x: 0.5, y: 0.5} + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 0.85 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ForceVisible: 0 + m_ShapeRadius: 0.025 + m_ShadowAngle: 0 + m_LightUnit: 1 + m_LuxAtDistance: 1 + m_EnableSpotReflector: 1 +--- !u!4 &2123726882 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2042920971} + m_GameObject: {fileID: 2123726880} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: 0.12450414, y: -0.8617815, z: 0.25527132, w: 0.42031893} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 @@ -1447,13 +1469,13 @@ Transform: SceneRoots: m_ObjectHideFlags: 0 m_Roots: - - {fileID: 128460815} - - {fileID: 1521561488} - - {fileID: 1162144977} - - {fileID: 802321242} - - {fileID: 2042920976} - - {fileID: 954524124} - - {fileID: 214746661} - - {fileID: 1629940941} - - {fileID: 526112589} - - {fileID: 1309622898} + - {fileID: 536714285} + - {fileID: 2123726882} + - {fileID: 620007472} + - {fileID: 380136779} + - {fileID: 966459990} + - {fileID: 1216497471} + - {fileID: 250057008} + - {fileID: 1530823779} + - {fileID: 105837771} + - {fileID: 1883692216} diff --git a/Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs b/Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs new file mode 100644 index 0000000..84aedef --- /dev/null +++ b/Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs @@ -0,0 +1,236 @@ +using System; +using System.Text; + +namespace Nova.Gameplay +{ + /// + /// Where the Aetherium balance stands against the slot's DERIVED storage + /// ceiling (16.4, #53, D-024): below it (normal), exactly at it (fresh + /// income forfeits — the player must build storage) or above it (the + /// excess decays once per second — the #131 opening situation and the + /// D-106 destruction case). The three-way split exists because the two + /// warning situations carry DIFFERENT truths: "at" loses future income, + /// "above" actively burns the existing balance. + /// + public enum StorageCeilingState + { + BelowCeiling = 0, + AtCeiling = 1, + AboveCeiling = 2, + } + + /// + /// The one-line resource bar's view model for a single frame: the two + /// value segments (always present), the optional warning segment and the + /// state flags the IMGUI component one layer up turns into colours. Built + /// once per frame by ; the + /// component renders it without any further decisions. + /// + public struct ResourceBarModel + { + /// "Aetherium 2.318 / 3.000" — balance AND ceiling, German thousands grouping. + public string AetheriumText; + + /// "Strom 130/80" — provided against required, the DebugHud/FormatPowerBalance convention. + public string PowerText; + + /// The warning segment, or null when nothing needs acting on. Both warnings join with , storage first. + public string WarningText; + + /// The storage situation behind . + public StorageCeilingState StorageState; + + /// Mirror of the sim's IsLowPower (required > provided); colours the power segment. + public bool IsLowPower; + + /// + /// Severity of : false renders it amber (a + /// full store — income stops, nothing burns yet), true renders it red + /// (an overflow decaying per second or a power deficit already taxing + /// production, repair and radar). + /// + public bool IsCritical; + } + + /// + /// The testable brain of the resource bar (issue #137): maps the local + /// slot's economy readout — credits, the DERIVED storage ceiling and the + /// power balance — to the one line the bar shows, so the IMGUI component + /// one layer up (ResourceBarHud, Nova.Presentation.UI) only renders + /// and never decides. Plain Unity-free class on purpose, the same + /// precedent as : no MonoBehaviour, no + /// UnityEngine types, so EditMode tests cover the whole mapping. + /// + /// THE CEILING IS ALWAYS AN INPUT, NEVER A CONSTANT. The bar's whole + /// point is the pair "balance / ceiling", and the ceiling is + /// EconomySystem.CapacityFor(slot) — derived from the living + /// building stock on every read (a completed HQ provides the base, every + /// completed Storage adds its bonus, a destroyed one drops it). The + /// presenter takes it as a parameter so a rule change (e.g. the HQ base + /// capacity rising to 3.000) cannot strand a stale number in the UI. + /// + /// + /// WHAT THE PLAYER NEEDS, NOT EVERY INTERNAL NUMBER: the power segment + /// shows provided against required — the planning pair — and reserves the + /// deficit's CONSEQUENCES for the warning segment, where they name what + /// the sim actually does under low power: production runs at + /// PlayerEconomyState.ProductionSpeedMultiplierQ16 (exact ½), + /// repair at ConstructionSystem.LowPowerRepairRateHpPerTick + /// (5 instead of 10 HP/tick) and the radar/minimap goes dark + /// (FogOfWarSystem's low-power early-out, 16.6 C4). Free headroom + /// is the pair's subtraction and stays implicit — the build bar's hover + /// already names it where a building is actually placed. + /// + /// + /// Culture independence: the German grouping dots are assembled digit by + /// digit (same algorithm as 's field + /// reserve line), so the output is byte-identical under ANY ambient + /// culture — a build on an en-US host must not render "2,318". + /// + /// + public static class ResourceBarPresenter + { + /// The separator between the bar's segments (and between two simultaneous warnings) — the DebugHud status strip's convention. + public const string SegmentSeparator = " | "; + + /// Warning while the balance sits exactly ON the ceiling: fresh income forfeits until storage is built (amber — nothing burns yet). + public const string StorageFullWarning = "Lager voll — Einnahmen verfallen"; + + /// Warning while the balance sits ABOVE the ceiling: the excess decays 25%/s (D-024/D-106), so the warning names the action (red). + public const string StorageOverflowWarning = "Überschuss verfällt — Lager bauen!"; + + /// Warning under low power, naming the three live consequences so the rule stops being invisible (16.6 C4, Sprint-16 production factor). + public const string LowPowerWarning = "Strommangel — Produktion ½ · Reparatur ½ · Radar aus"; + + /// + /// The storage situation, with the two edges the account can sit on: + /// only when a ceiling + /// actually exists (capacity > 0) — "0 / 0" is a slot without any + /// account building, not a full store, and must not warn. A balance + /// above the ceiling is always , + /// including capacity 0 (no completed HQ: everything is excess). + /// Negative inputs are clamped to zero — the sim never produces them, + /// but a presentation function defends its own contract. + /// + public static StorageCeilingState EvaluateStorageState(long credits, long capacity) + { + if (credits < 0) credits = 0; + if (capacity < 0) capacity = 0; + if (credits > capacity) return StorageCeilingState.AboveCeiling; + if (credits == capacity && capacity > 0) return StorageCeilingState.AtCeiling; + return StorageCeilingState.BelowCeiling; + } + + /// + /// The whole line's model from the slot's raw economy readout. The + /// low-power flag mirrors the sim's own rule EXACTLY + /// (PlayerEconomyState.IsLowPower: required > provided — + /// an exactly-balanced grid is NOT a deficit). The warning joins both + /// active warnings storage-first: the overflow is the one bleeding + /// resources per second, so it leads. + /// + public static ResourceBarModel BuildModel( + long credits, long capacity, int powerProvided, int powerRequired) + { + StorageCeilingState storageState = EvaluateStorageState(credits, capacity); + bool isLowPower = powerRequired > powerProvided; + + string warning = null; + if (storageState == StorageCeilingState.AtCeiling) + { + warning = StorageFullWarning; + } + else if (storageState == StorageCeilingState.AboveCeiling) + { + warning = StorageOverflowWarning; + } + if (isLowPower) + { + warning = warning == null + ? LowPowerWarning + : warning + SegmentSeparator + LowPowerWarning; + } + + return new ResourceBarModel + { + AetheriumText = FormatAetherium(credits, capacity), + PowerText = FormatPower(powerProvided, powerRequired), + WarningText = warning, + StorageState = storageState, + IsLowPower = isLowPower, + // Amber is reserved for the single "full store" state; any + // active penalty (decay or deficit) escalates to red. + IsCritical = storageState == StorageCeilingState.AboveCeiling || isLowPower, + }; + } + + /// + /// The Aetherium segment: "Aetherium 2.318 / 3.000" — balance AND + /// ceiling, because a bare "2.318" says nothing (issue #137) and the + /// "2.318 / 3.000" pair reads at a glance. German thousands grouping, + /// assembled digit by digit (culture-independent, see class remarks). + /// + public static string FormatAetherium(long credits, long capacity) + { + var builder = new StringBuilder(32); + builder.Append("Aetherium "); + AppendGroupedDe(builder, credits); + builder.Append(" / "); + AppendGroupedDe(builder, capacity); + return builder.ToString(); + } + + /// + /// The power segment: "Strom 130/80" — provided against required, the + /// convention of + /// and the DebugHud status strip, so the same grid reads identically + /// on every surface. + /// + public static string FormatPower(int powerProvided, int powerRequired) + { + return $"Strom {powerProvided}/{powerRequired}"; + } + + /// + /// The bar's zone: right-docked in the top screen area, below the + /// status strip. Content-sized, clamped to the width between the side + /// margins — on a window too narrow for the content the bar grows + /// leftward from the right margin instead of running off the edge. + /// Pure math in the currency, converted to + /// UnityEngine.Rect at the IMGUI boundary (the HudLayoutMath + /// precedent). + /// + public static HudRect TopRightZone( + float screenWidth, float top, float contentWidth, float height, float margin) + { + if (margin < 0f) margin = 0f; + float available = Math.Max(0f, screenWidth - 2f * margin); + float width = Math.Min(Math.Max(0f, contentWidth), available); + return new HudRect( + margin + Math.Max(0f, available - width), + Math.Max(0f, top), + width, + Math.Max(0f, height)); + } + + /// Decimal digits with the German '.' group separator; balances are never negative, so a non-positive input renders as "0". + private static void AppendGroupedDe(StringBuilder builder, long value) + { + if (value <= 0) + { + builder.Append('0'); + return; + } + + int digitCount = 1; + for (long rest = value; rest >= 10; rest /= 10) digitCount++; + for (int i = 0; i < digitCount; i++) + { + if (i > 0 && (digitCount - i) % 3 == 0) builder.Append('.'); + long divisor = 1; + for (int d = 1; d < digitCount - i; d++) divisor *= 10; + builder.Append((char)('0' + (int)(value / divisor % 10))); + } + } + } +} diff --git a/Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs.meta b/Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs.meta new file mode 100644 index 0000000..e2babb4 --- /dev/null +++ b/Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d210f6920ce1430e988b1f3d5a96de03 \ No newline at end of file diff --git a/Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs b/Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs new file mode 100644 index 0000000..3f8d381 --- /dev/null +++ b/Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs @@ -0,0 +1,217 @@ +using UnityEngine; +using Nova.Gameplay; +using Nova.Gameplay.Match; +using Nova.Simulation.Economy; + +namespace Nova.Presentation.UI +{ + /// + /// The always-on resource bar (issue #137): one line, top-right below the + /// status strip, answering three things the running match previously kept + /// invisible outside the F3 debug panel — the Aetherium balance AGAINST + /// its storage ceiling ("Aetherium 2.318 / 3.000", D-024: what lies above + /// the ceiling is forfeit), the power balance with its deficit state + /// (16.6 C4: production and repair halve, the radar goes dark) and a + /// warning segment for the moments a number alone is not enough (store + /// full, overflow decaying, power deficit). All formatting and state + /// decisions live in the Unity-free + /// (Nova.Gameplay, EditMode-tested); this component only reads the sim, + /// caches one model per frame and paints it. + /// + /// STRICTLY READ-ONLY, like the DebugHud: every value is copied out of + /// per frame. The ceiling is read + /// through on every model rebuild + /// — never a cached or hard-coded number, because the ceiling moves with + /// the living building stock (and with economy rule changes). + /// + /// + /// CLICK BEHAVIOUR — A DELIBERATE DECISION: the bar is a pure readout + /// with NO interactive element, so it registers no hit test with + /// RtsDeviceInput.IsPointerOverHud and swallows no click. It draws only + /// GUI.Box/GUI.Label, which never claim the mouse in IMGUI (only + /// interactive controls do), so a click on the bar reaches the world + /// exactly like a click on the DebugHud status strip above it — the + /// established behaviour of every non-interactive HUD readout in this + /// cockpit. (Registering a hit test would require editing RtsDeviceInput, + /// which this slice must not touch — and there is nothing here a click + /// could meaningfully press.) + /// + /// + /// LAYOUT: explicit content-width rects, no GUILayout stacking — the + /// "EstimateHeight must mirror OnGUI row for row" trap documented in + /// CommandCardHud cannot snap here because there is no hidden height + /// mirror: the bar is exactly one row of a fixed serialized height, its + /// width is measured from the real styles (GUIStyle.CalcSize) and docked + /// right via . The vertical + /// dock sits below the DebugHud status strip; + /// mirrors that strip's serialized defaults (8 px margin + 13+6 px height + /// + 4 px gap = 31) and is inspector-adjustable if the strip ever moves. + /// The model is rebuilt at most once per frame — OnGUI runs twice + /// (Layout + Repaint) and CapacityFor re-scans the entity store on every + /// call (the DebugHud census sets the caching precedent). + /// + /// + [DisallowMultipleComponent] + public sealed class ResourceBarHud : MonoBehaviour + { + /// Horizontal inset between the chrome frame and the first/last segment. + private const float ContentInset = 8f; + + [Header("Wiring (scene generator)")] + [SerializeField] private MatchRunner _runner; + + [Header("Presentation")] + [Tooltip("Whole bar is scaled by this factor, matching the DebugHud/BuildMenuHud convention for Retina displays.")] + [SerializeField] private float _uiScale = 1.5f; + [SerializeField] private int _fontSize = 13; + [SerializeField] private float _margin = 8f; + [Tooltip("Top edge in scaled GUI space. Docks below the DebugHud status strip (its defaults: 8 margin + 19 height + 4 gap = 31); adjust here if the strip moves.")] + [SerializeField] private float _topOffset = 31f; + [Tooltip("One row of text plus air — fixed, so no height estimate ever has to mirror the drawing code.")] + [SerializeField] private float _barHeight = 22f; + + [Header("Warning colours")] + [Tooltip("Amber: the store is full — income stops, but nothing burns yet.")] + [SerializeField] private Color _warningColor = new Color(1f, 0.72f, 0.25f); + [Tooltip("Red: an active penalty — the overflow decays per second, or the grid is in deficit.")] + [SerializeField] private Color _criticalColor = new Color(1f, 0.38f, 0.32f); + + private GUIStyle _valueStyle; + private GUIStyle _alertValueStyle; + private GUIStyle _warningStyle; + private GUIStyle _criticalStyle; + + // Reused content shells for the width measurement — no per-frame + // GUIContent allocations (the styles are measured with the very + // content instance that is drawn). + private readonly GUIContent _aetheriumContent = new GUIContent(); + private readonly GUIContent _powerContent = new GUIContent(); + private readonly GUIContent _warningContent = new GUIContent(); + + // One model per frame: OnGUI fires Layout AND Repaint, and + // CapacityFor re-sweeps the entity store on every call. + private ResourceBarModel _model; + private int _modelFrame = -1; + + private void Awake() + { + if (_runner == null) _runner = FindAnyObjectByType(); + } + + private void OnGUI() + { + if (_runner == null) return; + EconomySystem economy = _runner.Economy; + if (economy == null) return; // menu, or the match is not initialized yet + + EnsureStyles(); + + float scale = Mathf.Max(1f, _uiScale); + Matrix4x4 previousMatrix = GUI.matrix; + GUI.matrix = Matrix4x4.Scale(new Vector3(scale, scale, 1f)); + + DrawBar(ModelForCurrentFrame(economy), scale); + + GUI.matrix = previousMatrix; + } + + /// + /// The frame's model: the local slot's credits, ceiling and power + /// balance, copied out of the sim (the HUD reads, never writes) and + /// mapped by the pure presenter. Rebuilt at most once per frame. + /// + private ResourceBarModel ModelForCurrentFrame(EconomySystem economy) + { + if (_modelFrame == Time.frameCount) return _model; + _modelFrame = Time.frameCount; + + byte slot = _runner.Session != null ? _runner.Session.LocalSlot : (byte)0; + PlayerEconomyState playerEconomy = economy.GetPlayerEconomy(slot); + _model = ResourceBarPresenter.BuildModel( + playerEconomy.AetheriumCredits, + economy.CapacityFor(slot), + playerEconomy.PowerProvided, + playerEconomy.PowerRequired); + return _model; + } + + /// + /// Chrome frame plus the two (three under warning) text segments, + /// right-docked at the measured content width. Segments carry the + /// separator as a prefix, so the separator takes its segment's + /// colour and never needs a fifth label. + /// + private void DrawBar(in ResourceBarModel model, float scale) + { + _aetheriumContent.text = model.AetheriumText; + _powerContent.text = ResourceBarPresenter.SegmentSeparator + model.PowerText; + GUIStyle powerStyle = model.IsLowPower ? _alertValueStyle : _valueStyle; + _warningContent.text = model.WarningText != null + ? ResourceBarPresenter.SegmentSeparator + model.WarningText + : null; + + float aetheriumWidth = _valueStyle.CalcSize(_aetheriumContent).x; + float powerWidth = powerStyle.CalcSize(_powerContent).x; + float warningWidth = model.WarningText != null ? WarningStyle(model).CalcSize(_warningContent).x : 0f; + float contentWidth = ContentInset + aetheriumWidth + powerWidth + warningWidth + ContentInset; + + Rect zone = ToRect(ResourceBarPresenter.TopRightZone( + HudLayout.GuiWidth(scale), _topOffset, contentWidth, _barHeight, _margin)); + GUI.Box(zone, GUIContent.none, HudChrome.PanelStyle); + + float x = zone.x + ContentInset; + GUI.Label(new Rect(x, zone.y, aetheriumWidth, zone.height), _aetheriumContent, _valueStyle); + x += aetheriumWidth; + GUI.Label(new Rect(x, zone.y, powerWidth, zone.height), _powerContent, powerStyle); + if (model.WarningText != null) + { + x += powerWidth; + GUI.Label(new Rect(x, zone.y, warningWidth, zone.height), _warningContent, WarningStyle(model)); + } + } + + /// Amber for "store full", red for an active penalty (overflow decay or power deficit) — the model's IsCritical flag decides. + private GUIStyle WarningStyle(in ResourceBarModel model) + { + return model.IsCritical ? _criticalStyle : _warningStyle; + } + + private void EnsureStyles() + { + if (_valueStyle == null) + { + _valueStyle = new GUIStyle(GUI.skin.label) + { + fontSize = _fontSize, + alignment = TextAnchor.MiddleLeft, + wordWrap = false, + padding = new RectOffset(0, 0, 0, 0), + }; + } + if (_alertValueStyle == null) + { + // The power pair itself goes red in a deficit: the deficit is + // the number's meaning, not an annotation beside it. + _alertValueStyle = new GUIStyle(_valueStyle); + _alertValueStyle.normal.textColor = _criticalColor; + } + if (_warningStyle == null) + { + _warningStyle = new GUIStyle(_valueStyle) { fontStyle = FontStyle.Bold }; + _warningStyle.normal.textColor = _warningColor; + } + if (_criticalStyle == null) + { + _criticalStyle = new GUIStyle(_valueStyle) { fontStyle = FontStyle.Bold }; + _criticalStyle.normal.textColor = _criticalColor; + } + } + + private static Rect ToRect(HudRect zone) => new Rect(zone.X, zone.Y, zone.Width, zone.Height); + + private void OnDestroy() + { + HudChrome.DestroyShared(); + } + } +} diff --git a/Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs.meta b/Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs.meta new file mode 100644 index 0000000..72ce78d --- /dev/null +++ b/Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e920fc747d1c406fb1fa611b6934aa74 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f2c56..9ecbb7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,22 @@ die Versionierung folgt (in der aktuellen Doku-Phase) dem Dokumentationsstand de spielerisch abgenommen und kein Meilenstein-Nachweis ### Hinzugefügt +- **Eine Ressourcenleiste zeigt endlich, was die Wirtschaft tut (#137).** Aus der + Proberunde vom 31.08.2026: „Mir fehlt ein globales Overlay, in dem man sieht, + wie viel Strom man hat, vor allem auch wie viel Lagerplatz man noch hat." + Bisher stand der Kontostand nur in der Baukarte, wo er Knöpfe sperrt, und + Strom samt Lagerdecke ausschließlich in der DebugHud — teils erst hinter `F3`, + also in einem Entwicklerwerkzeug. **Die Lagerdecke stand nirgends im + Spiel-UI.** Damit waren drei Regeln unsichtbar, die aktiv in die Partie + eingreifen: der Verfall über der Decke (D-024), der Strommangel mit halbierter + Reparaturrate und abgeschaltetem Radar (16.6/C4), und das Startguthaben über + der Decke (#131). Die neue Leiste zeigt Aetherium als Bestand **und** Decke + (`2.318 / 3.000` liest sich sofort, `2.318` allein sagt nichts), die Stromlage + und einen Warnzustand, wenn die Decke erreicht ist — der Moment, in dem der + Spieler ein Lager bauen muss. Die Formatier- und Zustandslogik liegt als reine + Funktionen in `ResourceBarPresenter` und ist mit 19 EditMode-Tests belegt; + reine Präsentation, kein Simulationseingriff, die Leiste schluckt bewusst + keine Klicks - **Die Auswahl ist benutzbar geworden (#50).** Aus dem Betatest: „Weil ich den Pionier in der Gruppe nicht wiederfand, konnte ich nicht bauen." Paket 21.5 hatte die Aufstellung geliefert, aber man konnte sie nicht anfassen. Jetzt diff --git a/reports/v8.6.0/sprint-23/12-kimi-hud.md b/reports/v8.6.0/sprint-23/12-kimi-hud.md new file mode 100644 index 0000000..cb47dc0 --- /dev/null +++ b/reports/v8.6.0/sprint-23/12-kimi-hud.md @@ -0,0 +1,421 @@ +# kimi-agent report + +- when: 2026-08-31T07:50:30Z +- backend: cc +- model: k3[1m] +- mode: rw +- dir: /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud +- run: /Users/denniswestermann/.agent-runs/20260831-095030-81372 + +## Task + +Du arbeitest an "Project Nova" / HashKrieg, einem Unity-RTS mit deterministischer, +ganzzahliger Simulation. Doku und Berichte: Deutsch. Code und Docstrings: +Englisch, wie im Bestand. + +**ARBEITSVERZEICHNIS — der einzige Pfad, unter dem du liest und schreibst:** + + /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud + +Daneben liegt eine Arbeitskopie unter `/Volumes/2TB_CodingProjekte/Coding_Projekte/Project Nova`. +**Fass die nicht an, weder lesend noch schreibend.** + +## Der Auftrag — Issue #137, lies ihn zuerst + +Der Inhaber nach seiner Runde am 31.08.2026 auf Build `97e5459`: + +> „Mir fehlt ein globales Overlay, in dem man sieht, wie viel Strom man hat, vor +> allem auch wie viel Lagerplatz man noch hat." + +Heute erfährt der Spieler seinen Zustand nur an Stellen, die dafür nicht gemacht +sind: den Kontostand in der Baukarte (`BuildMenuHud.cs:285`, wo er Knöpfe +sperrt), Strom und Lagerdecke ausschließlich in der DebugHud, teils erst hinter +`F3` — einem Entwicklerwerkzeug. **Die Lagerdecke steht nirgends im Spiel-UI.** + +Damit sind drei Regeln, die aktiv in die Partie eingreifen, unsichtbar: + +- die **Lagerdecke** — was darüber liegt, verfällt (D-024) +- der **Strommangel** — halbiert die Reparaturrate und schaltet den Radar ab + (Sprint 16.6, C4) +- das **Startguthaben über der Decke** — der Kontostand fällt in den ersten + Sekunden, ohne dass irgendetwas sagt warum (#131) + +Du baust die Anzeige, die alle drei sichtbar macht. + +## Was zu liefern ist + +Eine dauerhaft sichtbare Leiste, die in einer Zeile beantwortet: + +- **Aetherium: Bestand UND Decke.** `2.318 / 3.000` liest sich sofort; + `2.318` allein sagt nichts. Die Decke ist `EconomySystem.CapacityFor(slot)` — + sie wächst mit jedem Lager und fällt, wenn eines zerstört wird. +- **Strom: Erzeugung gegen Verbrauch**, mit erkennbarem Mangelzustand. Sieh im + `EconomySystem` nach, wie die Stromlage geführt wird (`RecomputePower`), und + zeig, was der Spieler zum Handeln braucht — nicht jede interne Zahl. +- **Ein Warnzustand**, wenn die Decke erreicht ist. Das ist der Moment, in dem + der Spieler etwas tun muss (ein Lager bauen), und der einzige, in dem eine + Zahl allein nicht reicht. + +> **Ein anderer Worker ändert parallel die Wirtschaft:** die HQ-Grunddecke +> steigt auf 3.000, und Sammler halten bei vollem Lager an. Lies die Decke +> deshalb **immer** über `CapacityFor` und schreib nirgends eine Zahl fest — +> sonst zeigt deine Leiste morgen etwas Falsches. + +## Wo es hingehört + +Es gibt heute **keine** Ressourcenleiste; du legst eine neue Komponente an. +Sieh dir an, wie die bestehenden HUD-Komponenten in die Szene kommen +(`Assets/_Project/Editor/BootstrapSceneGenerator.cs` verdrahtet sie) und +**melde im Report, welche Verdrahtung nötig ist** — die Editor-Datei gehört dir +nicht, ich ziehe sie nach. + +Platz ist da: seit Paket 21.8 sitzt unten links die Versionsanzeige, Baukarte +und Befehlskarte haben feste Ecken. + +## Die zwei Fallen, die im Bestand mehrfach zugeschnappt sind + +1. **`EstimateHeight` bildet die Höhenrechnung von `OnGUI` Zeile für Zeile + nach.** Der Kommentar an der Stelle dokumentiert den Fehler wörtlich + („~40 px short … visible, but not clickable"). Wer eine Zeile hinzufügt und + die Rechnung nicht mitzieht, baut eine Fläche, die man sieht, aber nicht + trifft. +2. **Jede neue Trefferfläche gehört in `IsPointerOverHud`**, sonst schlagen + Klicks hinter dem Panel in die Welt durch und deselektieren. + +Ob dich Punkt 1 und 2 betreffen, hängt davon ab, ob deine Leiste anklickbar +wird. Eine reine Anzeige braucht kein `IsPointerOverHud` — aber sie darf auch +keine Klicks schlucken. **Entscheide bewusst und schreib es in den Docstring.** + +## Schreibhoheit — verbindlich + +ERLAUBT: + Assets/_Project/Scripts/Presentation/UI/ nur deine NEUE Datei + Assets/_Project/Scripts/Gameplay/UI/ reine Hilfsfunktionen, falls nötig + Assets/Tests/EditMode/Gameplay/ Tests für die reinen Funktionen + reports/v8.6.0/sprint-23/ nur deine eigenen Dateien + +VERBOTEN: + Assets/_Project/Scripts/Simulation/** komplett, ohne Ausnahme + Assets/_Project/Scripts/Presentation/UI/BuildMenuHud.cs + Assets/_Project/Scripts/Presentation/UI/BuildZoneOverlayView.cs + dort arbeitet ein anderer Worker + Assets/_Project/Scripts/Presentation/UI/DebugHud.cs + Assets/_Project/Editor/ melden statt ändern + Assets/_Project/Scripts/Gameplay/Match/ Presentation/Maps/ + tools/** quality/** .github/** + CHANGELOG.md VERSION ROADMAP.md README.md plans/** global.json + +**Reine Präsentation. Kein Simulationseingriff, keine Regeländerung, +`RulesHash64` bewegt sich nicht.** Wenn dir eine Lösung einfällt, die die +Simulation braucht, ist die Lösung falsch — melde es. + +**Den CHANGELOG fasst du nicht an.** Vorschlagstext in den Report. + +## Verifikation + + "/Volumes/2TB_CodingProjekte/Coding_Projekte/Project Nova/.dotnet/dotnet" test tools/Nova.SimRunner.Tests/Nova.SimRunner.Tests.csproj -c Release + +Ausgangsstand **739/739 grün** — er muss sich **nicht bewegen**. Diese Kette +kompiliert `Presentation/` gar nicht mit; sie belegt hier nur, dass nichts in +die Simulation durchgeschlagen ist. Vorher und nachher fahren. + +Unity hast du nicht. **Schreib die Formatier- und Zustandslogik deshalb als +reine, Unity-freie Funktionen** und teste die in `Assets/Tests/EditMode/Gameplay/` +— genau wie es Paket 21.5 mit `CommandCardPresenter` vorgemacht hat. Was in +`OnGUI` hängt, kann niemand testen; was daneben liegt, schon. + +Neue `.cs` unter `Assets/` brauchen eine `.meta`-Schwester: +`fileFormatVersion: 2` plus `guid:` mit 32 neuen Hex-Zeichen. + +## Was du NICHT tust + +- Kein `git commit`, `git push`, `git add`, kein PR, kein `gh`-Aufruf. +- Keine Subagenten. + +## Report + +Markdown nach `reports/v8.6.0/sprint-23/`. Struktur: + + 1. Was die Leiste zeigt und warum genau das + 2. Der Warnzustand: wann er anspringt, wie er aussieht + 3. **Welche Verdrahtung im Szenengenerator ich nachziehen muss** — genau + 4. Welche reinen Funktionen du getestet hast + 5. Testlauf vorher / nachher; was unbelegt bleibt + 6. CHANGELOG-Vorschlagstext + +Schließe mit: + + STATUS: DONE | BLOCKED + - Befund 1 + - Befund 2 + - Befund 3 + +## Output + +# Ressourcenleiste — Issue #137 (Sprint 23, Paket „HUD“) + +**Neue Dateien** (alle mit `.meta`-Schwester, neue GUIDs): + +- `Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs` — die IMGUI-Komponente +- `Assets/_Project/Scripts/Gameplay/UI/ResourceBarPresenter.cs` — das reine, + Unity-freie Gehirn (Muster: Paket 21.5, `CommandCardPresenter`) +- `Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs` — 19 EditMode-Tests + +Keine bestehende Datei wurde angefasst. `Simulation/**` unberührt, `RulesHash64` +bewegt sich nicht, reine Präsentation. + +## 1. Was die Leiste zeigt und warum genau das + +Eine Zeile, dauerhaft sichtbar, oben rechts direkt unter der Statuszeile der +DebugHud (F3 bleibt unangetastet): + +``` +Aetherium 2.318 / 3.000 | Strom 130/80 | +``` + +- **Aetherium als Paar `Bestand / Decke`.** `2.318` allein sagt nichts — + genau der Befund des Inhabers. Die Decke wird bei jedem Modell-Neuaufbau + (einmal pro Frame) über `EconomySystem.CapacityFor(slot)` gelesen, **nie** + als Konstante: steigt die HQ-Grunddecke durch den parallelen Wirtschafts-PR + auf 3.000 oder fällt die Decke durch einen zerstörten Speicher, zeigt die + Leiste es automatisch richtig. Tausendergruppierung deutsch (`2.318`), + Ziffer für Ziffer zusammengesetzt — kulturunabhängig, ein en-US-Host rendert + kein `2,318` (Algorithmus-Präzedenzfall: `CommandCardPresenter`). +- **Strom als Paar `Erzeugung / Verbrauch`** — die Konvention, die + `CommandCardPresenter.FormatPowerBalance` und die DebugHud-Statuszeile + bereits sprechen, damit dasselbe Netz auf jeder Fläche gleich liest. Die + freie Differenz ist bewusst **keine** dritte Zahl: sie steckt als Subtraktion + im Paar, und die Baukarte nennt „frei" bereits dort, wo der Spieler baut + (Hover). Der Mangelzustand färbt das Strompaar rot und löst das Warnsegment + aus (s. 2) — das ist das Erkennbare, nicht eine weitere Zahl. +- **Warnsegment** nur dann, wenn eine Zahl allein nicht reicht: Lager voll, + Überschuss über der Decke, Strommangel. + +**Bewusste Entscheidungen, im Docstring der Komponente festgehalten:** + +- **Keine Klickfläche, kein `IsPointerOverHud`-Eintrag.** Die Leiste bietet + nichts, was ein Klick drücken könnte; sie zeichnet ausschließlich + `GUI.Box`/`GUI.Label`, die in IMGUI die Maus nie beanspruchen. Klicks fallen + hindurch in die Welt — exakt wie über der DebugHud-Statuszeile. (Ein + Hit-Test-Eintrag hätte ohnehin `RtsDeviceInput.cs` gebraucht, das außerhalb + meiner Schreibhoheit liegt.) +- **Die `EstimateHeight`-Falle betrifft diese Leiste nicht** — begründet im + Docstring: kein GUILayout-Stapeln, feste Höhe (`_barHeight = 22`), Breite + per `GUIStyle.CalcSize` aus den echten Styles gemessen. Es gibt keine + Höhenrechnung, die von der Zeichnung abdriften könnte. +- Platzierung: rechts gedockt (`ResourceBarPresenter.TopRightZone`, getestet), + `_topOffset = 31` spiegelt die Standardwerte der Statuszeile (8 Rand + 13+6 + Höhe + 4 Lücke) und ist im Inspector nachziehbar. Unten links sitzt die + Versionsanzeige, Baukarte unten mittig, Befehlskarte unten rechts, Minimap + links — oben rechts ist die freie klassische RTS-Ecke. + +## 2. Der Warnzustand: wann er anspringt, wie er aussieht + +Drei Zustände, ein Segment (fett, farbig); die Schwere-Regel: **rot schlägt +amber** (`IsCritical = Überschuss || Strommangel`). + +| Zustand | Auslöser | Text | Farbe | +|---|---|---|---| +| Lager voll | `Bestand == Decke > 0` | `Lager voll — Einnahmen verfallen` | amber | +| Überschuss | `Bestand > Decke` | `Überschuss verfällt — Lager bauen!` | rot | +| Strommangel | `required > provided` | `Strommangel — Produktion ½ · Reparatur ½ · Radar aus` | rot | + +- **Lager voll** ist der Moment, in dem der Spieler handeln muss, bevor etwas + verfällt — deshalb amber, nicht rot: nichts brennt noch. Nach der + Parallel-Änderung (Start 3.000 = Decke 3.000) steht die Warnung ab Sekunde 0 + auf dem Schirm und der haltende Sammler erklärt sich dem Spieler selbst. +- **Überschuss** ist die #131-Lage (heute 3.000 > 2.000) und der + D-106-Zerstörungsfall: der Decay (25 %/s) läuft, und ohne diese Zeile fällt + der Kontostand kommentarlos. Der Text nennt die Aktion. +- **Strommangel** folgt exakt der Sim-Regel (`PlayerEconomyState.IsLowPower`: + streng `required > provided` — ein exakt ausgeglichenes Netz 80/80 ist + **kein** Mangel). Der Text nennt alle drei Konsequenzen, die die Sim tatsächlich + zieht, damit die Regel aufhört unsichtbar zu sein: Produktion ½ + (`ProductionSpeedMultiplierQ16`, exakt 0.5 in Q16.16), Reparatur ½ + (`LowPowerRepairRateHpPerTick` 5 statt 10, Sprint 16.6 C4), Radar/Minimap + dunkel (`FogOfWarSystem`-Low-Power-Early-Out). +- **Gleichzeitigkeit:** Lager-Warnung führt (sie blutet pro Sekunde), Join mit + ` | `. Zusätzlich färbt sich bei Mangel das Strompaar selbst rot. +- **Randfälle (getestet):** `0 / 0` (Slot ohne Kontogebäude) ist **kein** + Warnzustand; `Bestand > 0` bei Decke 0 (kein fertiges HQ) ist Überschuss; + negative Eingänge werden zu 0 geklemmt. + +## 3. Verdrahtung im Szenengenerator (nachzuziehen) + +In `Assets/_Project/Editor/BootstrapSceneGenerator.cs`, Methode +`CreateUiObject`, direkt hinter dem DebugHud-Block (~Zeile 392–394) — die +Leiste gehört auf dasselbe `uiObject` wie die übrigen HUD-Komponenten: + +```csharp +ResourceBarHud resourceBar = uiObject.AddComponent(); +WireReference(resourceBar, "_runner", runner); +``` + +- **Mehr ist nicht nötig.** Kein `_input`, keine weiteren Referenzen; alle + übrigen Felder sind Präsentations-Defaults. `Awake` hat denselben + `FindAnyObjectByType()`-Fallback wie die Bestandskomponenten, + die Verdrahtung ist also auch eine reine Konventionsfrage. +- **Kein** Eintrag in `RtsDeviceInput.IsPointerOverHud` nötig (reine Anzeige, + schluckt keine Klicks — s. 1). `_buildMenu`-ähnliche Rückverdrahtung entfällt. +- Die Position hinter `DebugHud` im AddComponent-Lauf ergibt nebenbei die + richtige Malreihenfolge: die Leiste zeichnet nach der Statuszeile und läge + im Überlappungsfall obenauf. +- Danach die Szene neu erzeugen: **Tools → Project Nova → Create Bootstrap + Scene** (die Komponente lebt per `AddComponent` in der generierten Szene). +- Bekannte Nachbarschaft, bewusst in Kauf genommen: bei geöffnetem F3-Panel + **und** sehr schmalem Fenster (≲ 1.100 GUI-Pixel) **und** zwei aktiven + Warnungen gleichzeitig kann die Leiste das Debug-Panel überlappen. F3 ist + Entwicklersicht; die Leiste weicht dafür keiner Spiel-Fläche aus. + +## 4. Welche reinen Funktionen ich getestet habe + +`ResourceBarPresenterTests` — 19 Tests über fünf Flächen: + +- **`EvaluateStorageState(credits, capacity)`** — below / at / above, die + #131-Lage (3.000/2.000), die Nach-Änderungs-Lage (3.000/3.000), `0/0` ohne + Warnung, Bestand ohne Decke, negative Eingänge geklemmt. +- **`FormatAetherium`** — Paarschreibweise mit deutscher Gruppierung über alle + Größen (0, einstellig, 999→1.000-Grenze, Millionen, `long.MaxValue`), plus + ein Test, der unter erzwungener en-US-Kultur `2.318` statt `2,318` pinnt. +- **`FormatPower`** — `Erzeugung/Verbrauch`-Paar. +- **`BuildModel`** — Normalzustand ohne Warnung; exakt ausgeglichenes Netz + (80/80) ist kein Mangel; voll → amber; Überschuss → rot + Aktionstext; + Mangel → rot + Konsequenztext; Join-Reihenfolge Lager-vor-Strom und + Schwere-Eskalation bei Gleichzeitigkeit. +- **`TopRightZone`** — Rechtsdock, Breiten-Clamp auf die Seitenränder bei + schmalen Fenstern, degenerierte Eingänge nie negativ. + +## 5. Testlauf vorher / nachher; was unbelegt bleibt + +- **Pflichtkette** (`Nova.SimRunner.Tests`, Release): **vorher 739/739 grün, + nachher 739/739 grün** — unbewegt, wie gefordert. Erwartbar und der Sinn + der Kette an dieser Stelle: sie kompiliert `Presentation/` nicht mit, + belegt aber, dass nichts in die Simulation durchgeschlagen ist. +- **Zusätzlich ohne Unity verifiziert:** Presenter, `HudLayoutMath` und die + **echte, unveränderte NUnit-Testdatei** in einem Scratch-Projekt (net8.0, + NUnit 3.14 aus dem NuGet-Cache) kompiliert und ausgeführt: **19/19 grün**. + NUnit 3.14 statt 4.2.2, weil Unitys Test-Framework 1.7.0 einen + NUnit-3.5-Fork mit klassischer Assert-API nutzt — gegen NUnit 4.2.2 + scheitern auch die Bestandstests (`Assert.IsTrue` u. a. sind dort + `ClassicAssert`); das ist ein Artefakt der Prüfumgebung, nicht des Codes. + Das Scratch-Verzeichnis wurde anschließend aus dem Repo entfernt. +- **Unbelegt bleibt:** `ResourceBarHud.cs` selbst kompiliert erst in Unity + (UnityEngine-API; die Datei hält sich strikt an die Aufrufmuster der + Bestandskomponenten). Am lebenden Bildschirm zu prüfen: das optische Bild + (Farben amber/rot, Abstände), der Klick-Durchfall in die Welt und die + o. g. F3-Nachbarschaft auf schmalen Fenstern. Ändern sich die + Serialize-Defaults der DebugHud-Statuszeile, ist `_topOffset` im Inspector + nachzuziehen (im Tooltip und Docstring vermerkt). + +## 6. CHANGELOG-Vorschlagstext + +Unter `## [Unreleased]`, neue Kategorie: + +```markdown +### Hinzugefügt +- **Ressourcenleiste oben rechts (#137):** eine dauerhaft sichtbare Zeile, die + Aetherium als `Bestand / Lagerdecke` (live über `CapacityFor`, wächst und + fällt mit dem Gebäudebestand), Strom als `Erzeugung / Verbrauch` und einen + Warnzustand zeigt — `Lager voll` (Einnahmen verfallen), `Überschuss + verfällt` (D-024/#131, inkl. der Startlage über der Decke) und + `Strommangel` mit seinen Konsequenzen (Produktion ½ · Reparatur ½ · Radar + aus, Sprint 16.6 C4). Reine Anzeige ohne Klickfläche; Formatierung und + Zustandslogik als Unity-freie Funktionen (`ResourceBarPresenter`) mit 19 + EditMode-Tests. Verdrahtung im Szenengenerator, Szene neu erzeugen. +``` + +## Befunde + +STATUS: DONE +- Pflichtkette vorher/nachher unverändert bei 739/739 grün; `Simulation/**` nicht angerührt, `RulesHash64` unbewegt — reine Präsentation. +- 19 neue EditMode-Tests für den Presenter hier real ausgeführt (Scratch net8.0/NUnit 3.14): 19/19 grün; die HUD-Komponente selbst kompiliert erst in Unity — Optik und Klick-Durchfall bleiben am Bildschirm zu verifizieren. +- Verdrahtung ist eine `AddComponent`- + eine `WireReference`-Zeile in `CreateUiObject` hinter dem DebugHud-Block (Schritt-für-Schritt in Abschnitt 3), danach Szene neu generieren; kein `IsPointerOverHud`-Eintrag nötig (reine Anzeige, Entscheidung im Docstring begründet). +- Bestandsdopplung bleibt: die DebugHud-Statuszeile zeigt weiterhin Credits/Strom in Kurzform ohne Decke — DebugHud.cs war mir verboten; wer die Zeile schlankt, kann das nach dem Merge tun. + +## Exit + +exit=0 + +## Progress + + [00:30] · Ich lese zuerst den Bestand: EconomySystem, bestehende HUD-Komponenten und die Muster aus Paket 21.5. + [00:33] Bash ls "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Prese… + [00:33] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Simulation + [00:42] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Simulatio… + [00:58] · Jetzt die bestehenden HUD-Muster: DebugHud, BuildMenuHud und der CommandCardPresenter als Vorbild für reine Funktionen. + [00:58] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [00:58] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Gameplay/… + [01:09] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [01:10] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [01:23] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets + [01:23] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Simulation + [01:33] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [01:33] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [01:46] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Editor/BootstrapS… + [01:56] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [01:56] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [06:30] · Jetzt die Details: Reparatur-Konstanten, HudLayoutMath, Test-Muster, Meta-Format und der Reports-Ordner. + [06:34] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Simulatio… + [06:34] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Gameplay/… + [06:34] Bash ls "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/reports/v8.6.0/sprint-23/" &&… + [06:58] Read /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/reports/v8.6.0/sprint-23/12-kimi-… + [07:00] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Gameplay/… + [07:24] Bash sed -n 920,935p "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/… + [07:31] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets + [07:42] Grep /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Simulatio… + [07:57] · Alle Flächen bestätigt. Ich starte den Basis-Testlauf (739/739 erwartet) im Hintergrund und schreibe währenddessen den reinen Presenter. + [07:57] Bash "/Volumes/2TB_CodingProjekte/Coding_Projekte/Project Nova/.dotnet/dotnet" test tools/Nova… + [07:57] agent + Baseline test run before changes (agent) + [08:14] agent = Baseline test run before changes (completed) + [08:14] agent -> full output: /private/tmp/claude-501/-Volumes-2TB-CodingProjekte-Coding-Projekte-nova-wt-hud/2a913e06-2dee-42e2-b7b7-67be844023ad/tasks/btl8aonlg.output + [08:16] TaskCreate Pure Unity-free formatting/state logic for the resource bar in Assets/_Project/Scripts/Ga… + [08:16] TaskCreate IMGUI component in Assets/_Project/Scripts/Presentation/UI/ResourceBarHud.cs + .meta, rea… + [08:16] TaskCreate Assets/Tests/EditMode/Gameplay/ResourceBarPresenterTests.cs + .meta + [08:17] TaskCreate Re-run Nova.SimRunner.Tests (must stay 739/739), write report to reports/v8.6.0/sprint-23… + [08:29] Bash tail -6 /private/tmp/claude-501/-Volumes-2TB-CodingProjekte-Coding-Projekte-nova-wt-hud/2… + [11:16] · Basis: **739/739 grün**. Jetzt schreibe ich den reinen Presenter. + [11:16] TaskUpdate {"status": "in_progress", "taskId": "1"} + [12:44] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Gameplay/… + [12:54] Bash for i in 1 2 3 4; do uuidgen | tr -d '-' | tr 'A-F' 'a-f'; done + [13:11] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Gameplay/… + [13:55] · Presenter steht. Jetzt die HUD-Komponente. + [13:55] TaskUpdate {"status": "completed", "taskId": "1"} + [13:55] TaskUpdate {"status": "in_progress", "taskId": "2"} + [15:06] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [15:20] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/_Project/Scripts/Presentat… + [15:29] TaskUpdate {"status": "completed", "taskId": "2"} + [15:30] TaskUpdate {"status": "in_progress", "taskId": "3"} + [15:43] · Jetzt die EditMode-Tests nach dem Muster der `CommandCardPresenterTests`. + [16:33] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/Tests/EditMode/Gameplay/Re… + [16:46] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/Assets/Tests/EditMode/Gameplay/Re… + [17:12] · Jetzt die Verifikation der reinen Logik ohne Unity: Scratch-Projekt im erlaubten Baum, das die echten Dateien kompiliert und die Logik fährt. Zuerst prüfen, wa… + [17:12] Bash grep -n "^using\|SimDefinitions\|MinimapHud\|UnityEngine" "/Volumes/2TB_CodingProjekte/Co… + [17:32] Bash mkdir -p "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" &… + [18:17] Write /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar/Program.cs + [18:29] Bash cd "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" && "/Vo… + [18:56] · Alle 37 Scratch-Assertions grün — Presenter kompiliert und verhält sich exakt wie spezifiziert. Jetzt prüfe ich noch, ob die NUnit-Testdatei selbst kompiliert … + [18:58] Bash grep -n "PackageReference" "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/tools… + [19:21] Bash cd "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" && cat … + [19:56] · 46 Fehler — das ist der NUnit-4-Classic-API-Bruch (`Assert.IsTrue` & Co. wanderten nach `ClassicAssert`). Unitys Test-Framework ist ein NUnit-3.5-Fork mit klas… + [19:56] Bash ls ~/.nuget/packages/nunit/ 2>/dev/null; echo ---; grep -rn "Assert\.\(Is\|Are\|Greater\)… + [20:10] Bash cd "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" && sed … + [20:40] · Kompiliert sauber. Noch stärker: die echten NUnit-Tests ausführen — Adapter und Test-SDK liegen im Cache. + [20:45] Bash cd "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" && cat … + [21:04] Bash cd "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" && sed … + [21:29] · **19/19 grün** — die echte NUnit-Testdatei läuft. Scratch aufräumen, dann der Pflicht-Testlauf. + [21:31] Bash rm -rf "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" && … + [21:46] Bash rm -rf "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" + [22:03] Bash mv "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/.scratch-resourcebar" /tmp/no… + [22:14] · Repo sauber. Jetzt der Nachher-Lauf der Pflichtkette: + [22:14] Bash "/Volumes/2TB_CodingProjekte/Coding_Projekte/Project Nova/.dotnet/dotnet" test tools/Nova… + [22:17] agent + Post-change run of the required test suite (agent) + [24:17] · 739/739 — unverändert. Noch zwei Detailprüfungen für den Report: CHANGELOG-Stil und die Panel-Breite der Befehlskarte (F3-Überschneidungsfrage). + [24:18] Bash head -40 "/Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/CHANGELOG.md"; echo ---… + [24:36] TaskUpdate {"status": "completed", "taskId": "3"} + [24:36] TaskUpdate {"status": "in_progress", "taskId": "4"} + [26:37] Edit /Volumes/2TB_CodingProjekte/Coding_Projekte/nova-wt/hud/reports/v8.6.0/sprint-23/12-kimi-… + [26:46] · Abschlusskontrolle: nur meine Dateien, keine Streuung. + [26:46] Bash git status --porcelain + [26:53] TaskUpdate {"status": "completed", "taskId": "4"} + [27:27] · Fertig. Zusammenfassung der Lieferung für Issue #137: **Drei neue Dateien (plus `.meta`-Schwestern), nichts Bestehendes angerührt:** 1. **`ResourceBarHud.cs`**… + [27:27] finished