Merge branch 'main' into claude/error-cause-analysis-lqyuxv
This commit is contained in:
@@ -23,4 +23,9 @@ MonoBehaviour:
|
||||
baseUptime: 60
|
||||
baseRam: 5
|
||||
baseCyclesPerTurn: 3
|
||||
exclusiveCards: []
|
||||
exclusiveCards:
|
||||
- {fileID: 11400000, guid: 0a4f1d1d84564f3cad58b8863e941201, type: 2}
|
||||
- {fileID: 11400000, guid: 14c5d40f866a4cb6b0a6316e01f42891, type: 2}
|
||||
- {fileID: 11400000, guid: 2a0f50d541f046fc99c8fb6deef4f1aa, type: 2}
|
||||
- {fileID: 11400000, guid: 3d57857f3f30419c8be4c44914c5abbe, type: 2}
|
||||
- {fileID: 11400000, guid: 4cae271a0f4146c59aab74e4c9d6d0f8, type: 2}
|
||||
|
||||
@@ -306,4 +306,5 @@ MonoBehaviour:
|
||||
monospaceFont: {fileID: 0}
|
||||
motdBody: unstable userspace detected; keep a rollback shell open.
|
||||
distroDatabase: {fileID: 11400000, guid: 4d88ca5446fa4d80b2fbd4b12697cb7c, type: 2}
|
||||
cardDatabase: {fileID: 11400000, guid: 6a8a9f37c98a4a7a9e67ab15d2d22208, type: 2}
|
||||
featuredUnitPanel: {}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace KernelPanic.Data
|
||||
[SerializeField] private int cycleCost;
|
||||
[SerializeField] private ResolutionTrack resolutionTrack;
|
||||
[SerializeField] private bool distroExclusive;
|
||||
[SerializeField] private bool isToken;
|
||||
[SerializeField, TextArea] private string designNotes;
|
||||
[SerializeField] private CardDefinition upgradeTarget;
|
||||
[SerializeField] private List<CardEffectDefinition> effects = new();
|
||||
|
||||
@@ -28,6 +30,8 @@ namespace KernelPanic.Data
|
||||
public int CycleCost => cycleCost;
|
||||
public ResolutionTrack ResolutionTrack => resolutionTrack;
|
||||
public bool DistroExclusive => distroExclusive;
|
||||
public bool IsToken => isToken;
|
||||
public string DesignNotes => designNotes;
|
||||
public CardDefinition UpgradeTarget => upgradeTarget;
|
||||
public IReadOnlyList<CardEffectDefinition> Effects => effects;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace KernelPanic.Meta
|
||||
public bool starterChosen;
|
||||
public List<string> ownedUnitIds = new();
|
||||
public List<string> bannerPoolIds = new();
|
||||
public List<CardLoadoutSaveEntry> cardLoadouts = new();
|
||||
|
||||
public static SaveData CreateDefault()
|
||||
{
|
||||
@@ -70,6 +71,24 @@ namespace KernelPanic.Meta
|
||||
{
|
||||
ownedUnitIds ??= new List<string>();
|
||||
bannerPoolIds ??= new List<string>();
|
||||
cardLoadouts ??= new List<CardLoadoutSaveEntry>();
|
||||
for (int i = 0; i < cardLoadouts.Count; i++)
|
||||
{
|
||||
cardLoadouts[i] ??= new CardLoadoutSaveEntry();
|
||||
cardLoadouts[i].EnsureLists();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CardLoadoutSaveEntry
|
||||
{
|
||||
public string distroId;
|
||||
public List<string> equippedCardIds = new();
|
||||
|
||||
public void EnsureLists()
|
||||
{
|
||||
equippedCardIds ??= new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace KernelPanic.UI
|
||||
[SerializeField] private FontAsset monospaceFont; // TODO: Assign a real monospace FontAsset when typography assets exist.
|
||||
[SerializeField] private string motdBody = "unstable userspace detected; keep a rollback shell open.";
|
||||
[SerializeField] private DistroDatabase distroDatabase;
|
||||
[SerializeField] private CardDatabase cardDatabase;
|
||||
[SerializeField] private FeaturedUnitPanel featuredUnitPanel = new();
|
||||
[SerializeField] private CollectionScreenController collectionScreen = new();
|
||||
[SerializeField] private StarterSelectionController starterSelection = new();
|
||||
@@ -48,6 +49,7 @@ namespace KernelPanic.UI
|
||||
private VisualElement bootIntroPanel;
|
||||
private VisualElement mainMenuPanel;
|
||||
private VisualElement collectionPanel;
|
||||
private VisualElement runSetupPanel;
|
||||
private VisualElement gachaPanel;
|
||||
private VisualElement settingsPanel;
|
||||
private VisualElement eventBanner;
|
||||
@@ -61,6 +63,9 @@ namespace KernelPanic.UI
|
||||
private Label motdBodyLabel;
|
||||
private VisualElement motdBlock;
|
||||
private Button collectionBackButton;
|
||||
private Button collectionUnitsButton;
|
||||
private Button collectionCardsButton;
|
||||
private Button runSetupBackButton;
|
||||
private Button gachaBackButton;
|
||||
private Button settingsBackButton;
|
||||
private SaveService saveService;
|
||||
@@ -68,6 +73,7 @@ namespace KernelPanic.UI
|
||||
private EntropyWallet wallet;
|
||||
private GachaService gachaService;
|
||||
private PlayerCollection playerCollection;
|
||||
private CardLoadout cardLoadout;
|
||||
private BackgroundLogRingBuffer backgroundLog;
|
||||
private IEventBannerSource eventBannerSource;
|
||||
private int selectedCommandIndex;
|
||||
@@ -76,6 +82,7 @@ namespace KernelPanic.UI
|
||||
private bool cursorVisible;
|
||||
private bool suppressNextClick;
|
||||
private bool warnedUnresolvedSaveId;
|
||||
private bool warnedInvalidLoadoutId;
|
||||
private string bootIntroCopy;
|
||||
private IVisualElementScheduledItem blinkSchedule;
|
||||
private IVisualElementScheduledItem bootIntroSchedule;
|
||||
@@ -92,6 +99,7 @@ namespace KernelPanic.UI
|
||||
saveService = new SaveService();
|
||||
gachaService = new GachaService();
|
||||
playerCollection = new PlayerCollection(); // TODO: Replace with persistent player-collection service composition.
|
||||
cardLoadout = new CardLoadout(playerCollection.OwnedUnits);
|
||||
Initialize(new EntropyWallet()); // TODO: Replace with persistent wallet service composition.
|
||||
BindElements();
|
||||
BindCommandEntries();
|
||||
@@ -137,6 +145,7 @@ namespace KernelPanic.UI
|
||||
bootIntroPanel = root.Q<VisualElement>("BootIntroPanel");
|
||||
mainMenuPanel = root.Q<VisualElement>("MainMenuPanel");
|
||||
collectionPanel = root.Q<VisualElement>("CollectionPanel");
|
||||
runSetupPanel = root.Q<VisualElement>("RunSetupPanel");
|
||||
gachaPanel = root.Q<VisualElement>("GachaPanel");
|
||||
settingsPanel = root.Q<VisualElement>("SettingsPanel");
|
||||
eventBanner = root.Q<VisualElement>("EventBanner");
|
||||
@@ -153,6 +162,9 @@ namespace KernelPanic.UI
|
||||
motdBodyLabel = root.Q<Label>("MotdBodyLabel");
|
||||
|
||||
collectionBackButton = root.Q<Button>("CollectionBackButton");
|
||||
collectionUnitsButton = root.Q<Button>("CollectionUnitsButton");
|
||||
collectionCardsButton = root.Q<Button>("CollectionCardsButton");
|
||||
runSetupBackButton = root.Q<Button>("RunSetupBackButton");
|
||||
gachaBackButton = root.Q<Button>("GachaBackButton");
|
||||
settingsBackButton = root.Q<Button>("SettingsBackButton");
|
||||
}
|
||||
@@ -182,6 +194,9 @@ namespace KernelPanic.UI
|
||||
root.RegisterCallback<KeyDownEvent>(HandleKeyDown);
|
||||
root.RegisterCallback<PointerDownEvent>(HandlePointerDown);
|
||||
collectionBackButton.clicked += ShowMainMenu;
|
||||
collectionUnitsButton.clicked += ShowCollectionUnits;
|
||||
collectionCardsButton.clicked += ShowCollectionCards;
|
||||
runSetupBackButton.clicked += ShowMainMenu;
|
||||
gachaBackButton.clicked += ShowMainMenu;
|
||||
settingsBackButton.clicked += ShowMainMenu;
|
||||
}
|
||||
@@ -191,6 +206,9 @@ namespace KernelPanic.UI
|
||||
root.UnregisterCallback<KeyDownEvent>(HandleKeyDown);
|
||||
root.UnregisterCallback<PointerDownEvent>(HandlePointerDown);
|
||||
collectionBackButton.clicked -= ShowMainMenu;
|
||||
collectionUnitsButton.clicked -= ShowCollectionUnits;
|
||||
collectionCardsButton.clicked -= ShowCollectionCards;
|
||||
runSetupBackButton.clicked -= ShowMainMenu;
|
||||
gachaBackButton.clicked -= ShowMainMenu;
|
||||
settingsBackButton.clicked -= ShowMainMenu;
|
||||
}
|
||||
@@ -217,6 +235,29 @@ namespace KernelPanic.UI
|
||||
gachaService.AddToBannerPool(unit);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < saveData.cardLoadouts.Count; i++)
|
||||
{
|
||||
CardLoadoutSaveEntry entry = saveData.cardLoadouts[i];
|
||||
entry.EnsureLists();
|
||||
List<string> resolvedIds = new();
|
||||
for (int cardIndex = 0; cardIndex < entry.equippedCardIds.Count; cardIndex++)
|
||||
{
|
||||
string resolvedId = ResolveSavedCardId(entry.equippedCardIds[cardIndex]);
|
||||
if (!string.IsNullOrWhiteSpace(resolvedId))
|
||||
{
|
||||
resolvedIds.Add(resolvedId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cardLoadout.TryLoad(entry.distroId, resolvedIds, out bool skippedInvalid) || skippedInvalid)
|
||||
{
|
||||
WarnInvalidLoadoutId();
|
||||
}
|
||||
}
|
||||
|
||||
EnsureLoadoutsForOwnedUnits();
|
||||
SaveCurrentState();
|
||||
}
|
||||
|
||||
private DistroDefinition ResolveSavedDistro(string id)
|
||||
@@ -231,8 +272,37 @@ namespace KernelPanic.UI
|
||||
return unit;
|
||||
}
|
||||
|
||||
private string ResolveSavedCardId(string id)
|
||||
{
|
||||
if (cardDatabase == null)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
CardDefinition card = cardDatabase.FindById(id);
|
||||
if (card == null)
|
||||
{
|
||||
WarnInvalidLoadoutId();
|
||||
return null;
|
||||
}
|
||||
|
||||
return card.Id;
|
||||
}
|
||||
|
||||
private void WarnInvalidLoadoutId()
|
||||
{
|
||||
if (warnedInvalidLoadoutId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
warnedInvalidLoadoutId = true;
|
||||
Debug.LogWarning("Save references an invalid card loadout id; restoring a valid default loadout.");
|
||||
}
|
||||
|
||||
private void HandleMetaStateChanged()
|
||||
{
|
||||
EnsureLoadoutsForOwnedUnits();
|
||||
SaveCurrentState();
|
||||
featuredUnitPanel.Refresh(playerCollection.OwnedUnits);
|
||||
collectionScreen.Refresh(playerCollection.OwnedUnits);
|
||||
@@ -277,6 +347,8 @@ namespace KernelPanic.UI
|
||||
}
|
||||
}
|
||||
|
||||
cardLoadout.WriteTo(saveData.cardLoadouts);
|
||||
|
||||
saveService.Save(saveData);
|
||||
}
|
||||
|
||||
@@ -416,6 +488,30 @@ namespace KernelPanic.UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRunSetupVisible())
|
||||
{
|
||||
if (evt.keyCode == KeyCode.UpArrow)
|
||||
{
|
||||
SelectPackage(selectedPackageIndex - 1);
|
||||
evt.StopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.keyCode == KeyCode.DownArrow)
|
||||
{
|
||||
SelectPackage(selectedPackageIndex + 1);
|
||||
evt.StopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
|
||||
{
|
||||
ToggleSelectedPackage();
|
||||
evt.StopPropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (evt.keyCode == KeyCode.UpArrow)
|
||||
{
|
||||
SelectCommand(selectedCommandIndex - 1);
|
||||
@@ -453,6 +549,22 @@ namespace KernelPanic.UI
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsRunSetupVisible()
|
||||
{
|
||||
return runSetupPanel != null && !runSetupPanel.ClassListContains(HiddenClassName);
|
||||
}
|
||||
|
||||
private void SelectPackage(int index)
|
||||
{
|
||||
if (packageRows.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedPackageIndex = (index + packageRows.Count) % packageRows.Count;
|
||||
RefreshPackageSelection();
|
||||
}
|
||||
|
||||
private void HandlePointerDown(PointerDownEvent evt)
|
||||
{
|
||||
root.Focus();
|
||||
@@ -486,7 +598,8 @@ namespace KernelPanic.UI
|
||||
|
||||
private void HandleStartRunClicked()
|
||||
{
|
||||
SceneLoader.LoadGame();
|
||||
RefreshRunSetup();
|
||||
ShowPanel(runSetupPanel);
|
||||
}
|
||||
|
||||
private void HandleQuitClicked()
|
||||
@@ -510,6 +623,18 @@ namespace KernelPanic.UI
|
||||
ShowPanel(collectionPanel);
|
||||
}
|
||||
|
||||
private void ShowCollectionUnits()
|
||||
{
|
||||
collectionShowingCards = false;
|
||||
RefreshCollection();
|
||||
}
|
||||
|
||||
private void ShowCollectionCards()
|
||||
{
|
||||
collectionShowingCards = true;
|
||||
RefreshCollection();
|
||||
}
|
||||
|
||||
private void ShowGacha()
|
||||
{
|
||||
ShowPanel(gachaPanel);
|
||||
@@ -525,6 +650,7 @@ namespace KernelPanic.UI
|
||||
{
|
||||
mainMenuPanel.EnableInClassList(HiddenClassName, mainMenuPanel != activePanel);
|
||||
collectionPanel.EnableInClassList(HiddenClassName, collectionPanel != activePanel);
|
||||
runSetupPanel.EnableInClassList(HiddenClassName, runSetupPanel != activePanel);
|
||||
gachaPanel.EnableInClassList(HiddenClassName, gachaPanel != activePanel);
|
||||
settingsPanel.EnableInClassList(HiddenClassName, settingsPanel != activePanel);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
|
||||
<ui:VisualElement name="CollectionPanel" class="sub-panel hidden">
|
||||
<ui:Label text="COLLECTION" class="screen-title" />
|
||||
<ui:VisualElement class="collection-actions">
|
||||
<ui:Button name="CollectionUnitsButton" text="units" class="terminal-button collection-action-button" />
|
||||
<ui:Button name="CollectionCardsButton" text="cards" class="terminal-button collection-action-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name="CollectionContent" class="collection-shell">
|
||||
<ui:ScrollView name="CollectionList" class="collection-list" />
|
||||
<ui:VisualElement name="CollectionDetail" class="collection-detail" />
|
||||
|
||||
@@ -481,6 +481,31 @@ Button {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.collection-actions {
|
||||
width: 760px;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.collection-action-button {
|
||||
width: 110px;
|
||||
height: 34px;
|
||||
margin-top: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.collection-action-button.selected {
|
||||
background-color: var(--kp-primary);
|
||||
color: var(--kp-bg);
|
||||
}
|
||||
|
||||
.run-setup-hint {
|
||||
width: 760px;
|
||||
margin-top: -12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.collection-list {
|
||||
width: 300px;
|
||||
margin-right: 16px;
|
||||
@@ -552,6 +577,82 @@ Button {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.package-list {
|
||||
margin-top: 18px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.package-header {
|
||||
color: var(--kp-muted);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.package-notice {
|
||||
color: var(--kp-amber);
|
||||
margin-bottom: 8px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.package-empty {
|
||||
color: var(--kp-muted);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.package-row {
|
||||
min-width: 0;
|
||||
padding: 7px 10px;
|
||||
margin-top: 4px;
|
||||
border-left-width: 2px;
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
.package-row.selected,
|
||||
.package-row.equipped {
|
||||
border-left-color: var(--kp-primary);
|
||||
}
|
||||
|
||||
.package-row.selected {
|
||||
background-color: var(--kp-hover);
|
||||
}
|
||||
|
||||
.package-row.dimmed {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.package-summary {
|
||||
min-width: 0;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.package-marker {
|
||||
width: 34px;
|
||||
min-width: 34px;
|
||||
}
|
||||
|
||||
.package-name {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
color: var(--kp-primary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-unity-text-overflow-position: end;
|
||||
}
|
||||
|
||||
.package-meta {
|
||||
min-width: 96px;
|
||||
color: var(--kp-muted);
|
||||
-unity-text-align: upper-right;
|
||||
}
|
||||
|
||||
.package-description {
|
||||
min-width: 0;
|
||||
color: var(--kp-muted);
|
||||
white-space: normal;
|
||||
margin-left: 34px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
@@ -124,6 +124,15 @@
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:Instance template="CollectionScreen" />
|
||||
<ui:VisualElement name="RunSetupPanel" class="sub-panel hidden">
|
||||
<ui:Label text="NEW RUN" class="screen-title" />
|
||||
<ui:Label text="choose 4 distro cards before booting a run" class="collection-detail-muted run-setup-hint" />
|
||||
<ui:VisualElement name="RunSetupContent" class="collection-shell">
|
||||
<ui:ScrollView name="RunSetupList" class="collection-list" />
|
||||
<ui:VisualElement name="RunSetupDetail" class="collection-detail" />
|
||||
</ui:VisualElement>
|
||||
<ui:Button name="RunSetupBackButton" text="Back" class="terminal-button back-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:Instance template="GachaScreen" />
|
||||
<ui:Instance template="SettingsScreen" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user