diff --git a/Assets/Scenes/GameScene.unity b/Assets/Scenes/GameScene.unity
new file mode 100644
index 0000000..68730bc
--- /dev/null
+++ b/Assets/Scenes/GameScene.unity
@@ -0,0 +1,231 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 9
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 3
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 12
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 0
+ m_EnableRealtimeLightmaps: 0
+ m_LightingDataAsset: {fileID: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ maxJobWorkers: 0
+ preserveTilesOutsideBounds: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &100000
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 100004}
+ - component: {fileID: 100003}
+ - component: {fileID: 100001}
+ - component: {fileID: 100002}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &100001
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ m_Enabled: 1
+--- !u!114 &100002
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_RenderShadows: 1
+ m_RequiresDepthTextureOption: 2
+ m_RequiresOpaqueTextureOption: 2
+ m_CameraType: 0
+ m_Cameras: []
+ m_RendererIndex: -1
+ m_VolumeLayerMask:
+ serializedVersion: 2
+ m_Bits: 1
+ m_VolumeTrigger: {fileID: 0}
+ m_RenderPostProcessing: 0
+ m_Antialiasing: 0
+ m_AntialiasingQuality: 2
+ m_StopNaN: 0
+ m_Dithering: 0
+ m_ClearDepth: 1
+ m_AllowXRRendering: 1
+ m_RequiresDepthTexture: 0
+ m_RequiresColorTexture: 0
+ m_Version: 2
+--- !u!20 &100003
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 2
+ m_BackGroundColor: {r: 0.031, g: 0.047, b: 0.059, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 34
+ orthographic: 1
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 0
+ m_HDR: 1
+ m_AllowMSAA: 0
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 0
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &100004
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &200000
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 200001}
+ m_Layer: 0
+ m_Name: GameRoot
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &200001
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 200000}
+ 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_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Scenes/GameScene.unity.meta b/Assets/Scenes/GameScene.unity.meta
new file mode 100644
index 0000000..5d550b1
--- /dev/null
+++ b/Assets/Scenes/GameScene.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e4259fa62d854e27af3cc2a10fb210c8
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/MainMenuScene.unity
similarity index 72%
rename from Assets/Scenes/SampleScene.unity
rename to Assets/Scenes/MainMenuScene.unity
index 7b420f6..39bacd8 100644
--- a/Assets/Scenes/SampleScene.unity
+++ b/Assets/Scenes/MainMenuScene.unity
@@ -84,8 +84,8 @@ LightmapSettings:
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
- m_PVRFilterTypeIndirect: 0
- m_PVRFilterTypeAO: 0
+ m_PVRFilterTypeIndirect: 5
+ m_PVRFilterTypeAO: 2
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
@@ -192,7 +192,7 @@ Camera:
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 2
- m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_BackGroundColor: {r: 0.031, g: 0.047, b: 0.059, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
@@ -232,6 +232,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
@@ -240,7 +241,7 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!1 &619394800
+--- !u!1 &1100000000
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -248,100 +249,24 @@ GameObject:
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- - component: {fileID: 619394802}
- - component: {fileID: 619394801}
- m_Layer: 0
- m_Name: Global Light 2D
+ - component: {fileID: 1100000001}
+ - component: {fileID: 1100000002}
+ - component: {fileID: 1100000003}
+ m_Layer: 5
+ m_Name: MainMenuUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
---- !u!114 &619394801
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 619394800}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 073797afb82c5a1438f328866b10b3f0, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- m_ComponentVersion: 1
- m_LightType: 4
- m_BlendStyleIndex: 0
- m_FalloffIntensity: 0.5
- m_Color: {r: 1, g: 1, b: 1, a: 1}
- m_Intensity: 1
- m_LightVolumeIntensity: 1
- m_LightVolumeIntensityEnabled: 0
- m_ApplyToSortingLayers: 00000000
- m_LightCookieSprite: {fileID: 0}
- m_DeprecatedPointLightCookieSprite: {fileID: 0}
- m_LightOrder: 0
- m_OverlapOperation: 0
- m_NormalMapDistance: 3
- m_NormalMapQuality: 2
- m_UseNormalMap: 0
- m_ShadowIntensityEnabled: 0
- m_ShadowIntensity: 0.75
- m_ShadowVolumeIntensityEnabled: 0
- m_ShadowVolumeIntensity: 0.75
- m_Vertices:
- - position: {x: 0.9985302, y: 0.9985302, z: 0}
- color: {r: 0.70710677, g: 0.70710677, b: 0, a: 0}
- uv: {x: 0, y: 0}
- - position: {x: 0.9985302, y: 0.9985302, z: 0}
- color: {r: 0, g: 0, b: 0, a: 1}
- uv: {x: 0, y: 0}
- - position: {x: -0.9985302, y: 0.9985302, z: 0}
- color: {r: -0.70710677, g: 0.70710677, b: 0, a: 0}
- uv: {x: 0, y: 0}
- - position: {x: -0.9985302, y: 0.9985302, z: 0}
- color: {r: 0, g: 0, b: 0, a: 1}
- uv: {x: 0, y: 0}
- - position: {x: -0.99853003, y: -0.9985304, z: 0}
- color: {r: -0.70710665, g: -0.7071069, b: 0, a: 0}
- uv: {x: 0, y: 0}
- - position: {x: -0.99853003, y: -0.9985304, z: 0}
- color: {r: 0, g: 0, b: 0, a: 1}
- uv: {x: 0, y: 0}
- - position: {x: 0.99853003, y: -0.9985304, z: 0}
- color: {r: 0.70710665, g: -0.7071069, b: 0, a: 0}
- uv: {x: 0, y: 0}
- - position: {x: 0.99853003, y: -0.9985304, z: 0}
- color: {r: 0, g: 0, b: 0, a: 1}
- uv: {x: 0, y: 0}
- - position: {x: 0, y: 0, z: 0}
- color: {r: 0, g: 0, b: 0, a: 1}
- uv: {x: 0, y: 0}
- m_Triangles: 030001000800020000000100030002000100050003000800040002000300050004000300070005000800060004000500070006000500010007000800000006000700010000000700
- m_LocalBounds:
- m_Center: {x: 0, y: -0.00000011920929, z: 0}
- m_Extent: {x: 0.9985302, y: 0.99853027, z: 0}
- m_PointLightInnerAngle: 360
- m_PointLightOuterAngle: 360
- m_PointLightInnerRadius: 0
- m_PointLightOuterRadius: 1
- m_ShapeLightParametricSides: 5
- m_ShapeLightParametricAngleOffset: 0
- m_ShapeLightParametricRadius: 1
- m_ShapeLightFalloffSize: 0.5
- m_ShapeLightFalloffOffset: {x: 0, y: 0}
- m_ShapePath:
- - {x: -0.5, y: -0.5, z: 0}
- - {x: 0.5, y: -0.5, z: 0}
- - {x: 0.5, y: 0.5, z: 0}
- - {x: -0.5, y: 0.5, z: 0}
---- !u!4 &619394802
+--- !u!4 &1100000001
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 619394800}
+ m_GameObject: {fileID: 1100000000}
+ 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}
@@ -350,3 +275,34 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1100000002
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1100000000}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_PanelSettings: {fileID: 11400000, guid: 7d68bd7978394c8295debbd403a39d80, type: 2}
+ m_ParentUI: {fileID: 0}
+ sourceAsset: {fileID: 9197481963319205126, guid: a720a33f8c2b4dfe9d8197772dc0ab21, type: 3}
+ m_SortingOrder: 0
+--- !u!114 &1100000003
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1100000000}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 27a7f29e93cb4c55beac52f1e32a1bf5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ monospaceFont: {fileID: 0}
+ featuredUnitPanel:
+ featuredUnits: []
diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/MainMenuScene.unity.meta
similarity index 100%
rename from Assets/Scenes/SampleScene.unity.meta
rename to Assets/Scenes/MainMenuScene.unity.meta
diff --git a/Assets/Scripts/Core/SceneLoader.cs b/Assets/Scripts/Core/SceneLoader.cs
new file mode 100644
index 0000000..0f36ef2
--- /dev/null
+++ b/Assets/Scripts/Core/SceneLoader.cs
@@ -0,0 +1,30 @@
+using UnityEngine.SceneManagement;
+
+namespace KernelPanic.Core
+{
+ ///
+ /// Provides scene navigation entry points for the runtime shell.
+ ///
+ public static class SceneLoader
+ {
+ public const string MainMenuSceneName = "MainMenuScene";
+ public const string GameSceneName = "GameScene";
+
+ public static void LoadMainMenu()
+ {
+ LoadScene(MainMenuSceneName);
+ }
+
+ public static void LoadGame()
+ {
+ LoadScene(GameSceneName);
+ }
+
+ // Static for now because menu navigation has no instance state; a loading-screen service can replace this hook later.
+ private static void LoadScene(string sceneName)
+ {
+ // TODO: Route async progress into a loading screen once the UI shell has one.
+ SceneManager.LoadSceneAsync(sceneName);
+ }
+ }
+}
diff --git a/Assets/Scripts/Core/SceneLoader.cs.meta b/Assets/Scripts/Core/SceneLoader.cs.meta
new file mode 100644
index 0000000..519c755
--- /dev/null
+++ b/Assets/Scripts/Core/SceneLoader.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0d860bbf8e7f4df2a91e2e1e9892d904
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Data/DistroDefinition.cs b/Assets/Scripts/Data/DistroDefinition.cs
index 43e0a83..ff6f480 100644
--- a/Assets/Scripts/Data/DistroDefinition.cs
+++ b/Assets/Scripts/Data/DistroDefinition.cs
@@ -13,6 +13,8 @@ namespace KernelPanic.Data
[SerializeField] private string displayName;
[SerializeField] private Language primaryLanguage;
[SerializeField] private Language secondaryLanguage;
+ [SerializeField] private string passiveName;
+ [SerializeField] private Color accentColor = new(0.36f, 1f, 0.57f);
[SerializeField] private int baseUptime;
[SerializeField] private int baseRam;
[SerializeField] private int baseCyclesPerTurn;
@@ -22,6 +24,8 @@ namespace KernelPanic.Data
public string DisplayName => displayName;
public Language PrimaryLanguage => primaryLanguage;
public Language SecondaryLanguage => secondaryLanguage;
+ public string PassiveName => passiveName;
+ public Color AccentColor => accentColor;
public int BaseUptime => baseUptime;
public int BaseRam => baseRam;
public int BaseCyclesPerTurn => baseCyclesPerTurn;
diff --git a/Assets/Scripts/Meta/GachaService.cs b/Assets/Scripts/Meta/GachaService.cs
index ced3450..3c16949 100644
--- a/Assets/Scripts/Meta/GachaService.cs
+++ b/Assets/Scripts/Meta/GachaService.cs
@@ -7,6 +7,8 @@ namespace KernelPanic.Meta
///
public sealed class GachaService
{
+ public int PullTokens => 0; // TODO: Replace with the real pull-token wallet when that meta system exists.
+
public GachaPullResult PerformPull(EntropyWallet wallet, int cost)
{
throw new NotImplementedException();
diff --git a/Assets/Scripts/Meta/IEventBannerSource.cs b/Assets/Scripts/Meta/IEventBannerSource.cs
new file mode 100644
index 0000000..c2c02d9
--- /dev/null
+++ b/Assets/Scripts/Meta/IEventBannerSource.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace KernelPanic.Meta
+{
+ ///
+ /// Provides optional live-event banner copy for shell surfaces.
+ ///
+ public interface IEventBannerSource
+ {
+ EventBannerContent GetBanner();
+ }
+
+ ///
+ /// Describes an optional event banner message and remaining duration.
+ ///
+ public sealed class EventBannerContent
+ {
+ public EventBannerContent(string text, TimeSpan? remainingTime)
+ {
+ Text = text;
+ RemainingTime = remainingTime;
+ }
+
+ public string Text { get; }
+ public TimeSpan? RemainingTime { get; }
+ }
+}
diff --git a/Assets/Scripts/Meta/IEventBannerSource.cs.meta b/Assets/Scripts/Meta/IEventBannerSource.cs.meta
new file mode 100644
index 0000000..6e0db88
--- /dev/null
+++ b/Assets/Scripts/Meta/IEventBannerSource.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 542dd3144a914ea5b1e0c0e21db5ec6b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/UI.meta b/Assets/Scripts/UI.meta
new file mode 100644
index 0000000..649c90c
--- /dev/null
+++ b/Assets/Scripts/UI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 84a74d1de7c84009a2cf84394e8a9fa2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/UI/BootLogCopy.cs b/Assets/Scripts/UI/BootLogCopy.cs
new file mode 100644
index 0000000..7d3ba5f
--- /dev/null
+++ b/Assets/Scripts/UI/BootLogCopy.cs
@@ -0,0 +1,22 @@
+namespace KernelPanic.UI
+{
+ ///
+ /// Contains cosmetic terminal boot-log copy for the main menu shell.
+ ///
+ public static class BootLogCopy
+ {
+ public static readonly string[] Lines =
+ {
+ "[ 0.000000] kernel-panic: booting userland shell",
+ "[ 0.071004] cpu: isolating run scheduler",
+ "[ 0.184331] entropy: wallet device mounted readonly",
+ "[ 0.264700] tty1: spawning command menu",
+ "[ 0.391226] units: probing installed distros",
+ "[ 0.447801] collection: no render pass requested",
+ "[ 0.521332] gacha: token api unavailable",
+ "[ 0.684012] events: banner source disconnected",
+ "[ 0.812445] ui: reduced motion preference loaded",
+ "[ 1.000000] shell: ready"
+ };
+ }
+}
diff --git a/Assets/Scripts/UI/BootLogCopy.cs.meta b/Assets/Scripts/UI/BootLogCopy.cs.meta
new file mode 100644
index 0000000..9499cb7
--- /dev/null
+++ b/Assets/Scripts/UI/BootLogCopy.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e05527a70e234c16af2f9089012721b8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/UI/FeaturedUnitPanel.cs b/Assets/Scripts/UI/FeaturedUnitPanel.cs
new file mode 100644
index 0000000..3b3a0ed
--- /dev/null
+++ b/Assets/Scripts/UI/FeaturedUnitPanel.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using KernelPanic.Data;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace KernelPanic.UI
+{
+ ///
+ /// Renders the main menu's neofetch-style featured unit panel.
+ ///
+ [Serializable]
+ public sealed class FeaturedUnitPanel
+ {
+ private const string EmptyAccent = "5cff91";
+ private const string EmptyAscii = " _\n | |\n ___| |__\n / __| '_ \\\n \\__ \\ | | |\n |___/_| |_|";
+
+ [SerializeField] private List featuredUnits = new();
+
+ private VisualElement panel;
+ private Label titleLabel;
+ private Label asciiLabel;
+ private Label unitNameLabel;
+ private Label languagesLabel;
+ private Label passiveLabel;
+ private Label bestWaveLabel;
+ private Label emptyTitleLabel;
+ private Label emptyHintLabel;
+ private VisualElement populatedState;
+ private VisualElement emptyState;
+ private int selectedIndex;
+
+ public void Bind(VisualElement root)
+ {
+ panel = root.Q("FeaturedUnitPanel");
+ titleLabel = root.Q