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