finished main-menu so far
This commit is contained in:
@@ -29,3 +29,9 @@ MonoBehaviour:
|
|||||||
- {fileID: 11400000, guid: c1a50000000000000000000000000003, type: 2}
|
- {fileID: 11400000, guid: c1a50000000000000000000000000003, type: 2}
|
||||||
- {fileID: 11400000, guid: c1a50000000000000000000000000004, type: 2}
|
- {fileID: 11400000, guid: c1a50000000000000000000000000004, type: 2}
|
||||||
- {fileID: 11400000, guid: c1a50000000000000000000000000005, type: 2}
|
- {fileID: 11400000, guid: c1a50000000000000000000000000005, type: 2}
|
||||||
|
- {fileID: 11400000, guid: a7d40000000000000000000000000001, type: 2}
|
||||||
|
- {fileID: 11400000, guid: a7d40000000000000000000000000002, type: 2}
|
||||||
|
- {fileID: 11400000, guid: a7d40000000000000000000000000003, type: 2}
|
||||||
|
- {fileID: 11400000, guid: a7d40000000000000000000000000004, type: 2}
|
||||||
|
- {fileID: 11400000, guid: a7d40000000000000000000000000005, type: 2}
|
||||||
|
- {fileID: 11400000, guid: a7d40000000000000000000000000006, type: 2}
|
||||||
|
|||||||
@@ -307,4 +307,5 @@ MonoBehaviour:
|
|||||||
motdBody: unstable userspace detected; keep a rollback shell open.
|
motdBody: unstable userspace detected; keep a rollback shell open.
|
||||||
distroDatabase: {fileID: 11400000, guid: 4d88ca5446fa4d80b2fbd4b12697cb7c, type: 2}
|
distroDatabase: {fileID: 11400000, guid: 4d88ca5446fa4d80b2fbd4b12697cb7c, type: 2}
|
||||||
cardDatabase: {fileID: 11400000, guid: 6a8a9f37c98a4a7a9e67ab15d2d22208, type: 2}
|
cardDatabase: {fileID: 11400000, guid: 6a8a9f37c98a4a7a9e67ab15d2d22208, type: 2}
|
||||||
|
languageDeckDatabase: {fileID: 11400000, guid: a7d20000000000000000000000000001, type: 2}
|
||||||
featuredUnitPanel: {}
|
featuredUnitPanel: {}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace KernelPanic.Data
|
|||||||
[SerializeField] private bool distroExclusive;
|
[SerializeField] private bool distroExclusive;
|
||||||
[SerializeField] private bool isToken;
|
[SerializeField] private bool isToken;
|
||||||
[SerializeField, TextArea] private string designNotes;
|
[SerializeField, TextArea] private string designNotes;
|
||||||
|
[SerializeField] private List<string> requiresSystem = new();
|
||||||
[SerializeField] private CardDefinition upgradeTarget;
|
[SerializeField] private CardDefinition upgradeTarget;
|
||||||
[SerializeField] private List<CardEffectDefinition> effects = new();
|
[SerializeField] private List<CardEffectDefinition> effects = new();
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ namespace KernelPanic.Data
|
|||||||
public bool DistroExclusive => distroExclusive;
|
public bool DistroExclusive => distroExclusive;
|
||||||
public bool IsToken => isToken;
|
public bool IsToken => isToken;
|
||||||
public string DesignNotes => designNotes;
|
public string DesignNotes => designNotes;
|
||||||
|
public IReadOnlyList<string> RequiresSystem => requiresSystem;
|
||||||
public CardDefinition UpgradeTarget => upgradeTarget;
|
public CardDefinition UpgradeTarget => upgradeTarget;
|
||||||
public IReadOnlyList<CardEffectDefinition> Effects => effects;
|
public IReadOnlyList<CardEffectDefinition> Effects => effects;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using KernelPanic.Core;
|
||||||
using KernelPanic.Data;
|
using KernelPanic.Data;
|
||||||
|
using KernelPanic.Meta;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.TextCore.Text;
|
using UnityEngine.TextCore.Text;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
@@ -8,7 +10,7 @@ using UnityEngine.UIElements;
|
|||||||
namespace KernelPanic.UI
|
namespace KernelPanic.UI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renders the owned-units list and detail readout on the collection screen.
|
/// Renders Collection tabs and the pushed read-only card subview.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class CollectionScreenController
|
public sealed class CollectionScreenController
|
||||||
@@ -18,20 +20,116 @@ namespace KernelPanic.UI
|
|||||||
private VisualElement list;
|
private VisualElement list;
|
||||||
private VisualElement detail;
|
private VisualElement detail;
|
||||||
private FontAsset monospaceFont;
|
private FontAsset monospaceFont;
|
||||||
|
private LanguageDeckDatabase languageDeckDatabase;
|
||||||
|
private CardDatabase cardDatabase;
|
||||||
|
private PlayerCollection playerCollection;
|
||||||
private IReadOnlyList<DistroDefinition> units = Array.Empty<DistroDefinition>();
|
private IReadOnlyList<DistroDefinition> units = Array.Empty<DistroDefinition>();
|
||||||
|
private IReadOnlyList<LanguageCatalogEntry> languages = LanguageCatalog.All;
|
||||||
private IReadOnlyList<CardEntry> cards = Array.Empty<CardEntry>();
|
private IReadOnlyList<CardEntry> cards = Array.Empty<CardEntry>();
|
||||||
private int selectedIndex;
|
private CollectionMode mode;
|
||||||
|
private CollectionMode returnMode;
|
||||||
|
private int selectedUnitIndex;
|
||||||
|
private int selectedLanguageIndex;
|
||||||
|
private int selectedCardIndex;
|
||||||
|
private string emptyCardMessage = "no cards installed";
|
||||||
|
|
||||||
public void Bind(VisualElement root, FontAsset artFont)
|
public event Action ViewChanged;
|
||||||
|
|
||||||
|
public void Bind(VisualElement root, FontAsset artFont, LanguageDeckDatabase deckDatabase, CardDatabase cardsDatabase, PlayerCollection collection)
|
||||||
{
|
{
|
||||||
monospaceFont = artFont;
|
monospaceFont = artFont;
|
||||||
|
languageDeckDatabase = deckDatabase;
|
||||||
|
cardDatabase = cardsDatabase;
|
||||||
|
playerCollection = collection;
|
||||||
list = root.Q<VisualElement>("CollectionList");
|
list = root.Q<VisualElement>("CollectionList");
|
||||||
detail = root.Q<VisualElement>("CollectionDetail");
|
detail = root.Q<VisualElement>("CollectionDetail");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsCardSubview => mode == CollectionMode.CardSubview;
|
||||||
|
|
||||||
|
public string CurrentTitle { get; private set; } = "$ ls ~/collection";
|
||||||
|
public string CurrentHint { get; private set; } = "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select";
|
||||||
|
|
||||||
public void RefreshUnits(IReadOnlyList<DistroDefinition> ownedUnits)
|
public void RefreshUnits(IReadOnlyList<DistroDefinition> ownedUnits)
|
||||||
{
|
{
|
||||||
units = ownedUnits ?? Array.Empty<DistroDefinition>();
|
units = ownedUnits ?? Array.Empty<DistroDefinition>();
|
||||||
|
ShowUnits();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowUnits()
|
||||||
|
{
|
||||||
|
mode = CollectionMode.Units;
|
||||||
|
CurrentTitle = "$ ls ~/collection";
|
||||||
|
CurrentHint = "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select";
|
||||||
|
RenderUnits();
|
||||||
|
ViewChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowLanguages()
|
||||||
|
{
|
||||||
|
mode = CollectionMode.Languages;
|
||||||
|
CurrentTitle = "$ ls ~/collection/languages";
|
||||||
|
CurrentHint = "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select";
|
||||||
|
RenderLanguages();
|
||||||
|
ViewChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BackFromSubview()
|
||||||
|
{
|
||||||
|
if (!IsCardSubview)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnMode == CollectionMode.Languages)
|
||||||
|
{
|
||||||
|
ShowLanguages();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowUnits();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectRelative(int delta)
|
||||||
|
{
|
||||||
|
if (mode == CollectionMode.Units)
|
||||||
|
{
|
||||||
|
SelectUnit(selectedUnitIndex + delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == CollectionMode.Languages)
|
||||||
|
{
|
||||||
|
SelectLanguage(selectedLanguageIndex + delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectCard(selectedCardIndex + delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ActivateSelected()
|
||||||
|
{
|
||||||
|
if (mode == CollectionMode.Units && units.Count > 0)
|
||||||
|
{
|
||||||
|
OpenUnitCards(units[selectedUnitIndex]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == CollectionMode.Languages && languages.Count > 0)
|
||||||
|
{
|
||||||
|
LanguageCatalogEntry language = languages[selectedLanguageIndex];
|
||||||
|
if (LanguageUnlock.IsUnlocked(language.Language, playerCollection))
|
||||||
|
{
|
||||||
|
OpenLanguageDeck(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderUnits()
|
||||||
|
{
|
||||||
if (list == null || detail == null)
|
if (list == null || detail == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -48,7 +146,7 @@ namespace KernelPanic.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedIndex = Mathf.Clamp(selectedIndex, 0, units.Count - 1);
|
selectedUnitIndex = Mathf.Clamp(selectedUnitIndex, 0, units.Count - 1);
|
||||||
for (int i = 0; i < units.Count; i++)
|
for (int i = 0; i < units.Count; i++)
|
||||||
{
|
{
|
||||||
int index = i;
|
int index = i;
|
||||||
@@ -62,21 +160,20 @@ namespace KernelPanic.UI
|
|||||||
name.AddToClassList("collection-row-name");
|
name.AddToClassList("collection-row-name");
|
||||||
name.style.color = new StyleColor(unit.AccentColor);
|
name.style.color = new StyleColor(unit.AccentColor);
|
||||||
|
|
||||||
Label languages = new(DistroPresentation.FormatLanguages(unit));
|
Label unitLanguages = new(DistroPresentation.FormatLanguages(unit));
|
||||||
languages.AddToClassList("collection-row-languages");
|
unitLanguages.AddToClassList("collection-row-languages");
|
||||||
|
|
||||||
row.Add(name);
|
row.Add(name);
|
||||||
row.Add(languages);
|
row.Add(unitLanguages);
|
||||||
list.Add(row);
|
list.Add(row);
|
||||||
rows.Add(row);
|
rows.Add(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectUnit(selectedIndex);
|
SelectUnit(selectedUnitIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshCards(DistroDefinition featuredUnit)
|
private void RenderLanguages()
|
||||||
{
|
{
|
||||||
cards = BuildCardEntries(featuredUnit);
|
|
||||||
if (list == null || detail == null)
|
if (list == null || detail == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -84,43 +181,32 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
rows.Clear();
|
rows.Clear();
|
||||||
list.Clear();
|
list.Clear();
|
||||||
|
selectedLanguageIndex = Mathf.Clamp(selectedLanguageIndex, 0, languages.Count - 1);
|
||||||
|
|
||||||
if (cards.Count == 0)
|
for (int i = 0; i < languages.Count; i++)
|
||||||
{
|
|
||||||
detail.Clear();
|
|
||||||
detail.Add(new Label("no cards installed") { name = "CollectionEmptyTitle" });
|
|
||||||
detail.Add(new Label("featured distro cards appear here") { name = "CollectionEmptyHint" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedIndex = Mathf.Clamp(selectedIndex, 0, cards.Count - 1);
|
|
||||||
for (int i = 0; i < cards.Count; i++)
|
|
||||||
{
|
{
|
||||||
int index = i;
|
int index = i;
|
||||||
CardEntry entry = cards[i];
|
LanguageCatalogEntry language = languages[i];
|
||||||
|
bool unlocked = LanguageUnlock.IsUnlocked(language.Language, playerCollection);
|
||||||
VisualElement row = new();
|
VisualElement row = new();
|
||||||
row.AddToClassList("collection-row");
|
row.AddToClassList("collection-row");
|
||||||
row.RegisterCallback<PointerEnterEvent>(_ => SelectCard(index));
|
row.EnableInClassList("locked", !unlocked);
|
||||||
row.RegisterCallback<ClickEvent>(_ => SelectCard(index));
|
row.RegisterCallback<PointerEnterEvent>(_ => SelectLanguage(index));
|
||||||
|
row.RegisterCallback<ClickEvent>(_ => SelectLanguage(index));
|
||||||
|
|
||||||
Label name = new(GetCardDisplayName(entry.Card));
|
Label name = new(unlocked ? language.DisplayName : $"{language.DisplayName} [locked]");
|
||||||
name.AddToClassList("collection-row-name");
|
name.AddToClassList("collection-row-name");
|
||||||
|
|
||||||
Label meta = new($"{DistroPresentation.DisplayName(entry.Owner)} / {FormatCardMeta(entry.Card)}");
|
Label tag = new(language.IdentityTag);
|
||||||
meta.AddToClassList("collection-row-languages");
|
tag.AddToClassList("collection-row-languages");
|
||||||
|
|
||||||
row.Add(name);
|
row.Add(name);
|
||||||
row.Add(meta);
|
row.Add(tag);
|
||||||
list.Add(row);
|
list.Add(row);
|
||||||
rows.Add(row);
|
rows.Add(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectCard(selectedIndex);
|
SelectLanguage(selectedLanguageIndex);
|
||||||
}
|
|
||||||
|
|
||||||
public void Refresh(IReadOnlyList<DistroDefinition> ownedUnits)
|
|
||||||
{
|
|
||||||
RefreshUnits(ownedUnits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectUnit(int index)
|
private void SelectUnit(int index)
|
||||||
@@ -130,13 +216,16 @@ namespace KernelPanic.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedIndex = Mathf.Clamp(index, 0, units.Count - 1);
|
selectedUnitIndex = Mathf.Clamp(index, 0, units.Count - 1);
|
||||||
for (int i = 0; i < rows.Count; i++)
|
ApplySelection(selectedUnitIndex);
|
||||||
{
|
RenderUnitDetail(units[selectedUnitIndex]);
|
||||||
rows[i].EnableInClassList("selected", i == selectedIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderDetail(units[selectedIndex]);
|
private void SelectLanguage(int index)
|
||||||
|
{
|
||||||
|
selectedLanguageIndex = Mathf.Clamp(index, 0, languages.Count - 1);
|
||||||
|
ApplySelection(selectedLanguageIndex);
|
||||||
|
RenderLanguageDetail(languages[selectedLanguageIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectCard(int index)
|
private void SelectCard(int index)
|
||||||
@@ -146,16 +235,20 @@ namespace KernelPanic.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedIndex = Mathf.Clamp(index, 0, cards.Count - 1);
|
selectedCardIndex = Mathf.Clamp(index, 0, cards.Count - 1);
|
||||||
|
ApplySelection(selectedCardIndex);
|
||||||
|
RenderCardDetail(cards[selectedCardIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplySelection(int selectedIndex)
|
||||||
|
{
|
||||||
for (int i = 0; i < rows.Count; i++)
|
for (int i = 0; i < rows.Count; i++)
|
||||||
{
|
{
|
||||||
rows[i].EnableInClassList("selected", i == selectedIndex);
|
rows[i].EnableInClassList("selected", i == selectedIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderCardDetail(cards[selectedIndex]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderDetail(DistroDefinition unit)
|
private void RenderUnitDetail(DistroDefinition unit)
|
||||||
{
|
{
|
||||||
detail.Clear();
|
detail.Clear();
|
||||||
|
|
||||||
@@ -182,7 +275,6 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
VisualElement details = new();
|
VisualElement details = new();
|
||||||
details.AddToClassList("collection-detail-values");
|
details.AddToClassList("collection-detail-values");
|
||||||
|
|
||||||
details.Add(BuildDetailLine("lang", DistroPresentation.FormatLanguages(unit)));
|
details.Add(BuildDetailLine("lang", DistroPresentation.FormatLanguages(unit)));
|
||||||
details.Add(BuildDetailLine("uptime", unit.BaseUptime.ToString()));
|
details.Add(BuildDetailLine("uptime", unit.BaseUptime.ToString()));
|
||||||
details.Add(BuildDetailLine("ram", unit.BaseRam.ToString()));
|
details.Add(BuildDetailLine("ram", unit.BaseRam.ToString()));
|
||||||
@@ -190,6 +282,118 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
readout.Add(details);
|
readout.Add(details);
|
||||||
detail.Add(readout);
|
detail.Add(readout);
|
||||||
|
detail.Add(BuildSubviewCommand($"> cat ~/units/{unit.Id}/cards", "exclusive packages", HasListableCards(unit), () => OpenUnitCards(unit), "no packages installed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderLanguageDetail(LanguageCatalogEntry language)
|
||||||
|
{
|
||||||
|
detail.Clear();
|
||||||
|
bool unlocked = LanguageUnlock.IsUnlocked(language.Language, playerCollection);
|
||||||
|
|
||||||
|
Label name = new(language.DisplayName);
|
||||||
|
name.AddToClassList("collection-detail-name");
|
||||||
|
detail.Add(name);
|
||||||
|
detail.Add(BuildDetailLine("track", language.ResolutionTrack.ToString()));
|
||||||
|
|
||||||
|
Label how = new(language.HowItWorks);
|
||||||
|
how.AddToClassList("collection-detail-description");
|
||||||
|
detail.Add(how);
|
||||||
|
|
||||||
|
if (!unlocked)
|
||||||
|
{
|
||||||
|
string hintText = string.IsNullOrWhiteSpace(language.UnlockHint)
|
||||||
|
? $"unlock: own a distro that speaks {language.DisplayName} ({FormatSupportingDistros(language)})"
|
||||||
|
: language.UnlockHint;
|
||||||
|
Label hint = new(hintText);
|
||||||
|
hint.AddToClassList("package-notice");
|
||||||
|
detail.Add(hint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
detail.Add(BuildSubviewCommand($"> cat ~/lang/{GetLanguageId(language.Language)}/deck", "starter deck", true, () => OpenLanguageDeck(language), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenUnitCards(DistroDefinition unit)
|
||||||
|
{
|
||||||
|
if (!HasListableCards(unit))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
returnMode = CollectionMode.Units;
|
||||||
|
cards = BuildUnitCardEntries(unit);
|
||||||
|
emptyCardMessage = "no packages installed";
|
||||||
|
CurrentTitle = $"$ cat ~/units/{unit.Id}/cards";
|
||||||
|
CurrentHint = "[esc] back [arrows] navigate";
|
||||||
|
RenderCardSubview();
|
||||||
|
ViewChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenLanguageDeck(LanguageCatalogEntry language)
|
||||||
|
{
|
||||||
|
returnMode = CollectionMode.Languages;
|
||||||
|
cards = BuildStarterDeckEntries(language);
|
||||||
|
emptyCardMessage = "starter deck not yet available";
|
||||||
|
CurrentTitle = $"$ cat ~/lang/{GetLanguageId(language.Language)}/deck";
|
||||||
|
CurrentHint = "[esc] back [arrows] navigate";
|
||||||
|
RenderCardSubview();
|
||||||
|
ViewChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderCardSubview()
|
||||||
|
{
|
||||||
|
mode = CollectionMode.CardSubview;
|
||||||
|
rows.Clear();
|
||||||
|
list.Clear();
|
||||||
|
detail.Clear();
|
||||||
|
selectedCardIndex = Mathf.Clamp(selectedCardIndex, 0, Math.Max(0, cards.Count - 1));
|
||||||
|
|
||||||
|
if (cards.Count == 0)
|
||||||
|
{
|
||||||
|
detail.Add(new Label(emptyCardMessage) { name = "CollectionEmptyTitle" });
|
||||||
|
detail.Add(new Label("check back after deck data is installed") { name = "CollectionEmptyHint" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cards.Count; i++)
|
||||||
|
{
|
||||||
|
int index = i;
|
||||||
|
CardEntry entry = cards[i];
|
||||||
|
VisualElement row = BuildCardRow(entry);
|
||||||
|
row.RegisterCallback<PointerEnterEvent>(_ => SelectCard(index));
|
||||||
|
row.RegisterCallback<ClickEvent>(_ => SelectCard(index));
|
||||||
|
list.Add(row);
|
||||||
|
rows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectCard(selectedCardIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualElement BuildCardRow(CardEntry entry)
|
||||||
|
{
|
||||||
|
VisualElement row = new();
|
||||||
|
row.AddToClassList("package-row");
|
||||||
|
row.AddToClassList("collection-card-row");
|
||||||
|
|
||||||
|
VisualElement summary = new();
|
||||||
|
summary.AddToClassList("package-summary");
|
||||||
|
|
||||||
|
Label count = new(entry.Count > 1 ? $"x{entry.Count}" : string.Empty);
|
||||||
|
count.AddToClassList("package-marker");
|
||||||
|
Label name = new(GetCardDisplayName(entry.Card));
|
||||||
|
name.AddToClassList("package-name");
|
||||||
|
Label meta = new(FormatCardMeta(entry.Card));
|
||||||
|
meta.AddToClassList("package-meta");
|
||||||
|
|
||||||
|
summary.Add(count);
|
||||||
|
summary.Add(name);
|
||||||
|
summary.Add(meta);
|
||||||
|
row.Add(summary);
|
||||||
|
|
||||||
|
Label description = new(entry.Card == null || string.IsNullOrWhiteSpace(entry.Card.Description) ? "--" : entry.Card.Description);
|
||||||
|
description.AddToClassList("package-description");
|
||||||
|
row.Add(description);
|
||||||
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderCardDetail(CardEntry entry)
|
private void RenderCardDetail(CardEntry entry)
|
||||||
@@ -204,6 +408,7 @@ namespace KernelPanic.UI
|
|||||||
Label description = new(card == null || string.IsNullOrWhiteSpace(card.Description) ? "--" : card.Description);
|
Label description = new(card == null || string.IsNullOrWhiteSpace(card.Description) ? "--" : card.Description);
|
||||||
description.AddToClassList("collection-detail-description");
|
description.AddToClassList("collection-detail-description");
|
||||||
detail.Add(description);
|
detail.Add(description);
|
||||||
|
|
||||||
if (card != null && !string.IsNullOrWhiteSpace(card.FlavorText))
|
if (card != null && !string.IsNullOrWhiteSpace(card.FlavorText))
|
||||||
{
|
{
|
||||||
Label flavor = new(card.FlavorText);
|
Label flavor = new(card.FlavorText);
|
||||||
@@ -211,11 +416,10 @@ namespace KernelPanic.UI
|
|||||||
detail.Add(flavor);
|
detail.Add(flavor);
|
||||||
}
|
}
|
||||||
|
|
||||||
detail.Add(BuildDetailLine("owner", DistroPresentation.DisplayName(entry.Owner)));
|
|
||||||
detail.Add(BuildDetailLine("lang", card == null ? "--" : card.Language.ToString()));
|
detail.Add(BuildDetailLine("lang", card == null ? "--" : card.Language.ToString()));
|
||||||
detail.Add(BuildDetailLine("rarity", card == null ? "--" : card.Rarity.ToString()));
|
detail.Add(BuildDetailLine("rarity", card == null ? "--" : card.Rarity.ToString()));
|
||||||
detail.Add(BuildDetailLine("cost", card == null ? "--" : card.CycleCost.ToString()));
|
detail.Add(BuildDetailLine("cost", card == null ? "--" : card.CycleCost.ToString()));
|
||||||
detail.Add(BuildDetailLine("type", card != null && card.IsToken ? "token" : "loadout"));
|
detail.Add(BuildDetailLine("track", card == null ? "--" : card.ResolutionTrack.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VisualElement BuildDetailLine(string key, string value)
|
private static VisualElement BuildDetailLine(string key, string value)
|
||||||
@@ -233,6 +437,32 @@ namespace KernelPanic.UI
|
|||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VisualElement BuildSubviewCommand(string command, string description, bool enabled, Action action, string disabledDescription)
|
||||||
|
{
|
||||||
|
VisualElement row = new();
|
||||||
|
row.AddToClassList("command-row");
|
||||||
|
row.AddToClassList("collection-command-row");
|
||||||
|
row.EnableInClassList("disabled-row", !enabled);
|
||||||
|
|
||||||
|
Label cursor = new(">");
|
||||||
|
cursor.AddToClassList("command-cursor");
|
||||||
|
Label commandLabel = new(command);
|
||||||
|
commandLabel.AddToClassList("command-text");
|
||||||
|
Label descriptionLabel = new(enabled ? description : disabledDescription);
|
||||||
|
descriptionLabel.AddToClassList("command-description");
|
||||||
|
|
||||||
|
row.Add(cursor);
|
||||||
|
row.Add(commandLabel);
|
||||||
|
row.Add(descriptionLabel);
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
row.RegisterCallback<ClickEvent>(_ => action?.Invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddPassiveDetails(VisualElement target, DistroDefinition unit)
|
private static void AddPassiveDetails(VisualElement target, DistroDefinition unit)
|
||||||
{
|
{
|
||||||
if (unit.Passive == null)
|
if (unit.Passive == null)
|
||||||
@@ -256,28 +486,105 @@ namespace KernelPanic.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<CardEntry> BuildCardEntries(DistroDefinition featuredUnit)
|
private static IReadOnlyList<CardEntry> BuildUnitCardEntries(DistroDefinition unit)
|
||||||
{
|
{
|
||||||
List<CardEntry> entries = new();
|
List<CardEntry> entries = new();
|
||||||
if (featuredUnit == null)
|
if (unit == null)
|
||||||
{
|
{
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int cardIndex = 0; cardIndex < featuredUnit.ExclusiveCards.Count; cardIndex++)
|
for (int cardIndex = 0; cardIndex < unit.ExclusiveCards.Count; cardIndex++)
|
||||||
{
|
{
|
||||||
CardDefinition card = featuredUnit.ExclusiveCards[cardIndex];
|
CardDefinition card = unit.ExclusiveCards[cardIndex];
|
||||||
|
if (card == null || card.IsToken)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.Add(new CardEntry(unit, card, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<CardEntry> BuildStarterDeckEntries(LanguageCatalogEntry language)
|
||||||
|
{
|
||||||
|
LanguageDeckDefinition deck = languageDeckDatabase == null ? null : languageDeckDatabase.FindByLanguage(language.Language);
|
||||||
|
if (deck == null)
|
||||||
|
{
|
||||||
|
return BuildRegisteredStarterDeckEntries(language.Language);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CardEntry> entries = new();
|
||||||
|
for (int i = 0; i < deck.Entries.Count; i++)
|
||||||
|
{
|
||||||
|
LanguageDeckDefinition.LanguageDeckEntry deckEntry = deck.Entries[i];
|
||||||
|
if (deckEntry.Card == null || deckEntry.Count <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.Add(new CardEntry(null, deckEntry.Card, deckEntry.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<CardEntry> BuildRegisteredStarterDeckEntries(Language language)
|
||||||
|
{
|
||||||
|
if (cardDatabase == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<CardEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return language switch
|
||||||
|
{
|
||||||
|
Language.Python => BuildRegisteredStarterDeckEntries(
|
||||||
|
("lang_py_print", 2),
|
||||||
|
("lang_py_import_antigravity", 1),
|
||||||
|
("lang_py_for_loop", 1)),
|
||||||
|
Language.JavaScript => BuildRegisteredStarterDeckEntries(
|
||||||
|
("lang_js_console_log", 2),
|
||||||
|
("lang_js_fetch", 1),
|
||||||
|
("lang_js_typeof", 1)),
|
||||||
|
_ => Array.Empty<CardEntry>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<CardEntry> BuildRegisteredStarterDeckEntries(params (string Id, int Count)[] cardRefs)
|
||||||
|
{
|
||||||
|
List<CardEntry> entries = new();
|
||||||
|
for (int i = 0; i < cardRefs.Length; i++)
|
||||||
|
{
|
||||||
|
CardDefinition card = cardDatabase.FindById(cardRefs[i].Id);
|
||||||
if (card == null)
|
if (card == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.Add(new CardEntry(featuredUnit, card));
|
entries.Add(new CardEntry(null, card, cardRefs[i].Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasListableCards(DistroDefinition unit)
|
||||||
|
{
|
||||||
|
return BuildUnitCardEntries(unit).Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatSupportingDistros(LanguageCatalogEntry language)
|
||||||
|
{
|
||||||
|
List<string> names = new();
|
||||||
|
for (int i = 0; i < language.SupportingDistros.Count; i++)
|
||||||
|
{
|
||||||
|
names.Add(language.SupportingDistros[i].DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(", ", names);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetCardDisplayName(CardDefinition card)
|
private static string GetCardDisplayName(CardDefinition card)
|
||||||
{
|
{
|
||||||
if (card == null)
|
if (card == null)
|
||||||
@@ -293,16 +600,36 @@ namespace KernelPanic.UI
|
|||||||
return card == null ? "--" : $"{card.Language} / {card.CycleCost}c";
|
return card == null ? "--" : $"{card.Language} / {card.CycleCost}c";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetLanguageId(Language language)
|
||||||
|
{
|
||||||
|
return language switch
|
||||||
|
{
|
||||||
|
Language.CPlusPlus => "cpp",
|
||||||
|
Language.JavaScript => "javascript",
|
||||||
|
Language.TypeScript => "typescript",
|
||||||
|
_ => language.ToString().ToLowerInvariant()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CollectionMode
|
||||||
|
{
|
||||||
|
Units,
|
||||||
|
Languages,
|
||||||
|
CardSubview
|
||||||
|
}
|
||||||
|
|
||||||
private readonly struct CardEntry
|
private readonly struct CardEntry
|
||||||
{
|
{
|
||||||
public CardEntry(DistroDefinition owner, CardDefinition card)
|
public CardEntry(DistroDefinition owner, CardDefinition card, int count)
|
||||||
{
|
{
|
||||||
Owner = owner;
|
Owner = owner;
|
||||||
Card = card;
|
Card = card;
|
||||||
|
Count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DistroDefinition Owner { get; }
|
public DistroDefinition Owner { get; }
|
||||||
public CardDefinition Card { get; }
|
public CardDefinition Card { get; }
|
||||||
|
public int Count { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace KernelPanic.UI
|
|||||||
[SerializeField] private string motdBody = "unstable userspace detected; keep a rollback shell open.";
|
[SerializeField] private string motdBody = "unstable userspace detected; keep a rollback shell open.";
|
||||||
[SerializeField] private DistroDatabase distroDatabase;
|
[SerializeField] private DistroDatabase distroDatabase;
|
||||||
[SerializeField] private CardDatabase cardDatabase;
|
[SerializeField] private CardDatabase cardDatabase;
|
||||||
|
[SerializeField] private LanguageDeckDatabase languageDeckDatabase;
|
||||||
[SerializeField] private FeaturedUnitPanel featuredUnitPanel = new();
|
[SerializeField] private FeaturedUnitPanel featuredUnitPanel = new();
|
||||||
[SerializeField] private CollectionScreenController collectionScreen = new();
|
[SerializeField] private CollectionScreenController collectionScreen = new();
|
||||||
[SerializeField] private StarterSelectionController starterSelection = new();
|
[SerializeField] private StarterSelectionController starterSelection = new();
|
||||||
@@ -45,6 +46,7 @@ namespace KernelPanic.UI
|
|||||||
private readonly List<CommandMenuEntry> commandEntries = new();
|
private readonly List<CommandMenuEntry> commandEntries = new();
|
||||||
private readonly List<VisualElement> packageRows = new();
|
private readonly List<VisualElement> packageRows = new();
|
||||||
private readonly List<VisualElement> loadoutRows = new();
|
private readonly List<VisualElement> loadoutRows = new();
|
||||||
|
private readonly List<Language> selectedRunLanguages = new();
|
||||||
private UIDocument document;
|
private UIDocument document;
|
||||||
private VisualElement root;
|
private VisualElement root;
|
||||||
private VisualElement shellRoot;
|
private VisualElement shellRoot;
|
||||||
@@ -58,6 +60,7 @@ namespace KernelPanic.UI
|
|||||||
private VisualElement backgroundLogLayer;
|
private VisualElement backgroundLogLayer;
|
||||||
private ScrollView runSetupList;
|
private ScrollView runSetupList;
|
||||||
private VisualElement runSetupDetail;
|
private VisualElement runSetupDetail;
|
||||||
|
private ScrollView runSetupPackageScroll;
|
||||||
private Label appIdLabel;
|
private Label appIdLabel;
|
||||||
private Label entropyLabel;
|
private Label entropyLabel;
|
||||||
private Label pullTokensLabel;
|
private Label pullTokensLabel;
|
||||||
@@ -67,7 +70,7 @@ namespace KernelPanic.UI
|
|||||||
private Label motdBodyLabel;
|
private Label motdBodyLabel;
|
||||||
private VisualElement motdBlock;
|
private VisualElement motdBlock;
|
||||||
private Button collectionUnitsButton;
|
private Button collectionUnitsButton;
|
||||||
private Button collectionCardsButton;
|
private Button collectionLanguagesButton;
|
||||||
private readonly ScreenFrameController runSetupFrame = new();
|
private readonly ScreenFrameController runSetupFrame = new();
|
||||||
private readonly ScreenFrameController collectionFrame = new();
|
private readonly ScreenFrameController collectionFrame = new();
|
||||||
private readonly ScreenFrameController gachaFrame = new();
|
private readonly ScreenFrameController gachaFrame = new();
|
||||||
@@ -83,7 +86,7 @@ namespace KernelPanic.UI
|
|||||||
private int selectedCommandIndex;
|
private int selectedCommandIndex;
|
||||||
private int selectedPackageIndex;
|
private int selectedPackageIndex;
|
||||||
private string runSetupNotice;
|
private string runSetupNotice;
|
||||||
private bool collectionShowingCards;
|
private bool collectionShowingLanguages;
|
||||||
private float bootIntroElapsed;
|
private float bootIntroElapsed;
|
||||||
private int bootIntroCharacterCount;
|
private int bootIntroCharacterCount;
|
||||||
private bool cursorVisible;
|
private bool cursorVisible;
|
||||||
@@ -111,7 +114,7 @@ namespace KernelPanic.UI
|
|||||||
BindCommandEntries();
|
BindCommandEntries();
|
||||||
RegisterCommandEntryCallbacks();
|
RegisterCommandEntryCallbacks();
|
||||||
featuredUnitPanel.Bind(root, monospaceFont);
|
featuredUnitPanel.Bind(root, monospaceFont);
|
||||||
collectionScreen.Bind(root, monospaceFont);
|
collectionScreen.Bind(root, monospaceFont, languageDeckDatabase, cardDatabase, playerCollection);
|
||||||
BindScreenFrames();
|
BindScreenFrames();
|
||||||
starterSelection.Bind(root, distroDatabase, HandleStarterConfirmed);
|
starterSelection.Bind(root, distroDatabase, HandleStarterConfirmed);
|
||||||
LoadMetaState();
|
LoadMetaState();
|
||||||
@@ -171,13 +174,13 @@ namespace KernelPanic.UI
|
|||||||
motdBodyLabel = root.Q<Label>("MotdBodyLabel");
|
motdBodyLabel = root.Q<Label>("MotdBodyLabel");
|
||||||
|
|
||||||
collectionUnitsButton = root.Q<Button>("CollectionUnitsButton");
|
collectionUnitsButton = root.Q<Button>("CollectionUnitsButton");
|
||||||
collectionCardsButton = root.Q<Button>("CollectionCardsButton");
|
collectionLanguagesButton = root.Q<Button>("CollectionLanguagesButton");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BindScreenFrames()
|
private void BindScreenFrames()
|
||||||
{
|
{
|
||||||
runSetupFrame.Bind(runSetupPanel, "$ ./start_run --configure", "[esc] back [arrows] navigate [enter] select [b] boot run", ShowMainMenu);
|
runSetupFrame.Bind(runSetupPanel, "$ ./start_run --configure", "[esc] back [arrows] navigate [enter] select [b] boot run", ShowMainMenu);
|
||||||
collectionFrame.Bind(collectionPanel, "$ ls ~/collection", "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select", ShowMainMenu);
|
collectionFrame.Bind(collectionPanel, "$ ls ~/collection", "[esc] back [left/right] tabs [tab] tabs [arrows] navigate [enter] select", HandleCollectionBack);
|
||||||
gachaFrame.Bind(gachaPanel, "$ curl gacha.sh | sh", "[esc] back", ShowMainMenu);
|
gachaFrame.Bind(gachaPanel, "$ curl gacha.sh | sh", "[esc] back", ShowMainMenu);
|
||||||
settingsFrame.Bind(settingsPanel, "$ dpkg-reconfigure kernel-panic", "[esc] back", ShowMainMenu);
|
settingsFrame.Bind(settingsPanel, "$ dpkg-reconfigure kernel-panic", "[esc] back", ShowMainMenu);
|
||||||
}
|
}
|
||||||
@@ -206,16 +209,18 @@ namespace KernelPanic.UI
|
|||||||
{
|
{
|
||||||
root.RegisterCallback<KeyDownEvent>(HandleKeyDown);
|
root.RegisterCallback<KeyDownEvent>(HandleKeyDown);
|
||||||
root.RegisterCallback<PointerDownEvent>(HandlePointerDown);
|
root.RegisterCallback<PointerDownEvent>(HandlePointerDown);
|
||||||
|
collectionScreen.ViewChanged += SyncCollectionFrame;
|
||||||
collectionUnitsButton.RegisterCallback<ClickEvent>(HandleCollectionUnitsTabClicked);
|
collectionUnitsButton.RegisterCallback<ClickEvent>(HandleCollectionUnitsTabClicked);
|
||||||
collectionCardsButton.RegisterCallback<ClickEvent>(HandleCollectionCardsTabClicked);
|
collectionLanguagesButton.RegisterCallback<ClickEvent>(HandleCollectionLanguagesTabClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UnregisterCallbacks()
|
private void UnregisterCallbacks()
|
||||||
{
|
{
|
||||||
root.UnregisterCallback<KeyDownEvent>(HandleKeyDown);
|
root.UnregisterCallback<KeyDownEvent>(HandleKeyDown);
|
||||||
root.UnregisterCallback<PointerDownEvent>(HandlePointerDown);
|
root.UnregisterCallback<PointerDownEvent>(HandlePointerDown);
|
||||||
|
collectionScreen.ViewChanged -= SyncCollectionFrame;
|
||||||
collectionUnitsButton.UnregisterCallback<ClickEvent>(HandleCollectionUnitsTabClicked);
|
collectionUnitsButton.UnregisterCallback<ClickEvent>(HandleCollectionUnitsTabClicked);
|
||||||
collectionCardsButton.UnregisterCallback<ClickEvent>(HandleCollectionCardsTabClicked);
|
collectionLanguagesButton.UnregisterCallback<ClickEvent>(HandleCollectionLanguagesTabClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleCollectionUnitsTabClicked(ClickEvent evt)
|
private void HandleCollectionUnitsTabClicked(ClickEvent evt)
|
||||||
@@ -224,9 +229,9 @@ namespace KernelPanic.UI
|
|||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleCollectionCardsTabClicked(ClickEvent evt)
|
private void HandleCollectionLanguagesTabClicked(ClickEvent evt)
|
||||||
{
|
{
|
||||||
ShowCollectionCards();
|
ShowCollectionLanguages();
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,6 +473,13 @@ namespace KernelPanic.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.keyCode == KeyCode.Escape && IsCollectionVisible() && collectionScreen.BackFromSubview())
|
||||||
|
{
|
||||||
|
SyncCollectionFrame();
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (evt.keyCode == KeyCode.Escape && IsSubScreenVisible())
|
if (evt.keyCode == KeyCode.Escape && IsSubScreenVisible())
|
||||||
{
|
{
|
||||||
ShowMainMenu();
|
ShowMainMenu();
|
||||||
@@ -501,12 +513,35 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
if (IsCollectionVisible())
|
if (IsCollectionVisible())
|
||||||
{
|
{
|
||||||
if (evt.keyCode == KeyCode.LeftArrow || evt.keyCode == KeyCode.RightArrow || evt.keyCode == KeyCode.Tab)
|
if (!collectionScreen.IsCardSubview &&
|
||||||
|
(evt.keyCode == KeyCode.LeftArrow || evt.keyCode == KeyCode.RightArrow || evt.keyCode == KeyCode.Tab))
|
||||||
{
|
{
|
||||||
ToggleCollectionTab();
|
ToggleCollectionTab();
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.keyCode == KeyCode.UpArrow)
|
||||||
|
{
|
||||||
|
collectionScreen.SelectRelative(-1);
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.keyCode == KeyCode.DownArrow)
|
||||||
|
{
|
||||||
|
collectionScreen.SelectRelative(1);
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
|
||||||
|
{
|
||||||
|
collectionScreen.ActivateSelected();
|
||||||
|
SyncCollectionFrame();
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.keyCode == KeyCode.UpArrow)
|
if (evt.keyCode == KeyCode.UpArrow)
|
||||||
@@ -568,6 +603,7 @@ namespace KernelPanic.UI
|
|||||||
{
|
{
|
||||||
packageRows.Clear();
|
packageRows.Clear();
|
||||||
loadoutRows.Clear();
|
loadoutRows.Clear();
|
||||||
|
selectedRunLanguages.Clear();
|
||||||
cardLoadout.ClearAll();
|
cardLoadout.ClearAll();
|
||||||
runSetupList?.Clear();
|
runSetupList?.Clear();
|
||||||
runSetupDetail?.Clear();
|
runSetupDetail?.Clear();
|
||||||
@@ -634,6 +670,8 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
selectedPackageIndex = (index + packageRows.Count) % packageRows.Count;
|
selectedPackageIndex = (index + packageRows.Count) % packageRows.Count;
|
||||||
cardLoadout.ClearLoadout(playerCollection.OwnedUnits[selectedPackageIndex]);
|
cardLoadout.ClearLoadout(playerCollection.OwnedUnits[selectedPackageIndex]);
|
||||||
|
selectedRunLanguages.Clear();
|
||||||
|
runSetupPackageScroll = null;
|
||||||
runSetupNotice = null;
|
runSetupNotice = null;
|
||||||
RefreshPackageSelection();
|
RefreshPackageSelection();
|
||||||
}
|
}
|
||||||
@@ -650,6 +688,7 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
private void RenderSelectedPackageDetail()
|
private void RenderSelectedPackageDetail()
|
||||||
{
|
{
|
||||||
|
Vector2 previousScrollOffset = runSetupPackageScroll == null ? Vector2.zero : runSetupPackageScroll.scrollOffset;
|
||||||
runSetupDetail?.Clear();
|
runSetupDetail?.Clear();
|
||||||
loadoutRows.Clear();
|
loadoutRows.Clear();
|
||||||
if (runSetupDetail == null || selectedPackageIndex < 0 || selectedPackageIndex >= playerCollection.OwnedUnits.Count)
|
if (runSetupDetail == null || selectedPackageIndex < 0 || selectedPackageIndex >= playerCollection.OwnedUnits.Count)
|
||||||
@@ -704,9 +743,31 @@ namespace KernelPanic.UI
|
|||||||
packageList.ElementAt(packageList.childCount - 1).AddToClassList("package-empty");
|
packageList.ElementAt(packageList.childCount - 1).AddToClassList("package-empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (equippedCardIds.Count >= CardLoadout.MaxEquippedCards)
|
||||||
|
{
|
||||||
|
AddRunLanguageSelection(packageList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedRunLanguages.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
packageScroll.Add(packageList);
|
packageScroll.Add(packageList);
|
||||||
runSetupDetail.Add(packageScroll);
|
runSetupDetail.Add(packageScroll);
|
||||||
runSetupDetail.Add(BuildBootRow(unit, equippedCardIds.Count > 0));
|
runSetupDetail.Add(BuildBootRow(unit, CanBootRun(equippedCardIds)));
|
||||||
|
runSetupPackageScroll = packageScroll;
|
||||||
|
RestoreRunSetupPackageScroll(previousScrollOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestoreRunSetupPackageScroll(Vector2 offset)
|
||||||
|
{
|
||||||
|
if (runSetupPackageScroll == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runSetupPackageScroll.scrollOffset = offset;
|
||||||
|
runSetupPackageScroll.schedule.Execute(() => runSetupPackageScroll.scrollOffset = offset).StartingIn(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private VisualElement BuildRunSetupReadout(DistroDefinition unit)
|
private VisualElement BuildRunSetupReadout(DistroDefinition unit)
|
||||||
@@ -815,6 +876,80 @@ namespace KernelPanic.UI
|
|||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddRunLanguageSelection(VisualElement packageList)
|
||||||
|
{
|
||||||
|
IReadOnlyList<LanguageCatalogEntry> availableLanguages = GetAvailableRunLanguages();
|
||||||
|
packageList.Add(new Label("programming languages") { name = "RunSetupLanguagesHeader" });
|
||||||
|
packageList.ElementAt(packageList.childCount - 1).AddToClassList("package-header");
|
||||||
|
packageList.ElementAt(packageList.childCount - 1).AddToClassList("run-language-header");
|
||||||
|
|
||||||
|
if (selectedRunLanguages.Count < 2)
|
||||||
|
{
|
||||||
|
packageList.Add(new Label($"select {2 - selectedRunLanguages.Count} more language(s) before booting") { name = "RunSetupLanguagesNotice" });
|
||||||
|
packageList.ElementAt(packageList.childCount - 1).AddToClassList("package-notice");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableLanguages.Count == 0)
|
||||||
|
{
|
||||||
|
packageList.Add(new Label("no programming languages unlocked") { name = "RunSetupLanguagesEmpty" });
|
||||||
|
packageList.ElementAt(packageList.childCount - 1).AddToClassList("package-empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < availableLanguages.Count; i++)
|
||||||
|
{
|
||||||
|
LanguageCatalogEntry language = availableLanguages[i];
|
||||||
|
packageList.Add(BuildRunLanguageRow(language, IsRunLanguageSelected(language.Language)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualElement BuildRunLanguageRow(LanguageCatalogEntry language, bool selected)
|
||||||
|
{
|
||||||
|
VisualElement row = new();
|
||||||
|
row.AddToClassList("package-row");
|
||||||
|
row.AddToClassList("run-language-row");
|
||||||
|
row.EnableInClassList("equipped", selected);
|
||||||
|
row.RegisterCallback<ClickEvent>(_ => ToggleRunLanguage(language.Language));
|
||||||
|
|
||||||
|
VisualElement summary = new();
|
||||||
|
summary.AddToClassList("package-summary");
|
||||||
|
summary.Add(new Label(selected ? "[x]" : "[ ]") { name = "RunSetupLanguageMarker" });
|
||||||
|
summary.ElementAt(0).AddToClassList("package-marker");
|
||||||
|
|
||||||
|
summary.Add(new Label(language.DisplayName) { name = "RunSetupLanguageName" });
|
||||||
|
summary.ElementAt(1).AddToClassList("package-name");
|
||||||
|
|
||||||
|
summary.Add(new Label(language.ResolutionTrack.ToString()) { name = "RunSetupLanguageTrack" });
|
||||||
|
summary.ElementAt(2).AddToClassList("package-meta");
|
||||||
|
row.Add(summary);
|
||||||
|
|
||||||
|
row.Add(new Label(language.IdentityTag) { name = "RunSetupLanguageDescription" });
|
||||||
|
row.ElementAt(1).AddToClassList("package-description");
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleRunLanguage(Language language)
|
||||||
|
{
|
||||||
|
if (IsRunLanguageSelected(language))
|
||||||
|
{
|
||||||
|
selectedRunLanguages.Remove(language);
|
||||||
|
runSetupNotice = null;
|
||||||
|
RenderSelectedPackageDetail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRunLanguages.Count >= 2)
|
||||||
|
{
|
||||||
|
runSetupNotice = "language limit reached";
|
||||||
|
RenderSelectedPackageDetail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedRunLanguages.Add(language);
|
||||||
|
runSetupNotice = null;
|
||||||
|
RenderSelectedPackageDetail();
|
||||||
|
}
|
||||||
|
|
||||||
private void ToggleCardInSelectedLoadout(DistroDefinition unit, CardDefinition card)
|
private void ToggleCardInSelectedLoadout(DistroDefinition unit, CardDefinition card)
|
||||||
{
|
{
|
||||||
runSetupNotice = TryToggleLoadoutCard(unit, card);
|
runSetupNotice = TryToggleLoadoutCard(unit, card);
|
||||||
@@ -851,6 +986,39 @@ namespace KernelPanic.UI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<LanguageCatalogEntry> GetAvailableRunLanguages()
|
||||||
|
{
|
||||||
|
List<LanguageCatalogEntry> available = new();
|
||||||
|
IReadOnlyList<LanguageCatalogEntry> languages = LanguageCatalog.All;
|
||||||
|
for (int i = 0; i < languages.Count; i++)
|
||||||
|
{
|
||||||
|
if (LanguageUnlock.IsUnlocked(languages[i].Language, playerCollection))
|
||||||
|
{
|
||||||
|
available.Add(languages[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsRunLanguageSelected(Language language)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < selectedRunLanguages.Count; i++)
|
||||||
|
{
|
||||||
|
if (selectedRunLanguages[i] == language)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanBootRun(IReadOnlyList<string> equippedCardIds)
|
||||||
|
{
|
||||||
|
return equippedCardIds.Count >= CardLoadout.MaxEquippedCards && selectedRunLanguages.Count == 2;
|
||||||
|
}
|
||||||
|
|
||||||
private static string FormatLoadoutFailure(CardLoadoutFailureReason reason)
|
private static string FormatLoadoutFailure(CardLoadoutFailureReason reason)
|
||||||
{
|
{
|
||||||
return reason switch
|
return reason switch
|
||||||
@@ -890,9 +1058,18 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
DistroDefinition unit = playerCollection.OwnedUnits[selectedPackageIndex];
|
DistroDefinition unit = playerCollection.OwnedUnits[selectedPackageIndex];
|
||||||
IReadOnlyList<string> equippedCardIds = cardLoadout.GetEquippedCardIds(unit.Id);
|
IReadOnlyList<string> equippedCardIds = cardLoadout.GetEquippedCardIds(unit.Id);
|
||||||
if (equippedCardIds.Count < 1)
|
if (equippedCardIds.Count < CardLoadout.MaxEquippedCards)
|
||||||
{
|
{
|
||||||
runSetupNotice = "boot: no packages staged";
|
runSetupNotice = equippedCardIds.Count == 0
|
||||||
|
? "boot: no packages staged"
|
||||||
|
: $"boot: stage {CardLoadout.MaxEquippedCards} packages";
|
||||||
|
RenderSelectedPackageDetail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRunLanguages.Count < 2)
|
||||||
|
{
|
||||||
|
runSetupNotice = "boot: select 2 programming languages";
|
||||||
RenderSelectedPackageDetail();
|
RenderSelectedPackageDetail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -950,6 +1127,7 @@ namespace KernelPanic.UI
|
|||||||
private void ShowMainMenu()
|
private void ShowMainMenu()
|
||||||
{
|
{
|
||||||
cardLoadout.ClearAll();
|
cardLoadout.ClearAll();
|
||||||
|
selectedRunLanguages.Clear();
|
||||||
runSetupNotice = null;
|
runSetupNotice = null;
|
||||||
ShowPanel(mainMenuPanel);
|
ShowPanel(mainMenuPanel);
|
||||||
root.Focus();
|
root.Focus();
|
||||||
@@ -961,35 +1139,50 @@ namespace KernelPanic.UI
|
|||||||
ShowPanel(collectionPanel);
|
ShowPanel(collectionPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowCollectionUnits()
|
private void HandleCollectionBack()
|
||||||
{
|
{
|
||||||
collectionShowingCards = false;
|
if (collectionScreen.BackFromSubview())
|
||||||
collectionUnitsButton.EnableInClassList(SelectedClassName, true);
|
{
|
||||||
collectionCardsButton.EnableInClassList(SelectedClassName, false);
|
SyncCollectionFrame();
|
||||||
collectionFrame.SetTitle("$ ls ~/collection");
|
return;
|
||||||
collectionScreen.RefreshUnits(playerCollection.OwnedUnits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowCollectionCards()
|
ShowMainMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowCollectionUnits()
|
||||||
{
|
{
|
||||||
collectionShowingCards = true;
|
collectionShowingLanguages = false;
|
||||||
|
collectionUnitsButton.EnableInClassList(SelectedClassName, true);
|
||||||
|
collectionLanguagesButton.EnableInClassList(SelectedClassName, false);
|
||||||
|
collectionScreen.RefreshUnits(playerCollection.OwnedUnits);
|
||||||
|
SyncCollectionFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowCollectionLanguages()
|
||||||
|
{
|
||||||
|
collectionShowingLanguages = true;
|
||||||
collectionUnitsButton.EnableInClassList(SelectedClassName, false);
|
collectionUnitsButton.EnableInClassList(SelectedClassName, false);
|
||||||
collectionCardsButton.EnableInClassList(SelectedClassName, true);
|
collectionLanguagesButton.EnableInClassList(SelectedClassName, true);
|
||||||
DistroDefinition featuredUnit = playerCollection.FeaturedUnit;
|
collectionScreen.ShowLanguages();
|
||||||
string distroId = featuredUnit == null ? "--" : featuredUnit.Id;
|
SyncCollectionFrame();
|
||||||
collectionFrame.SetTitle($"$ dpkg -l --distro={distroId}");
|
|
||||||
collectionScreen.RefreshCards(playerCollection.FeaturedUnit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleCollectionTab()
|
private void ToggleCollectionTab()
|
||||||
{
|
{
|
||||||
if (collectionShowingCards)
|
if (collectionShowingLanguages)
|
||||||
{
|
{
|
||||||
ShowCollectionUnits();
|
ShowCollectionUnits();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowCollectionCards();
|
ShowCollectionLanguages();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SyncCollectionFrame()
|
||||||
|
{
|
||||||
|
collectionFrame.SetTitle(collectionScreen.CurrentTitle);
|
||||||
|
collectionFrame.SetHint(collectionScreen.CurrentHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowGacha()
|
private void ShowGacha()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<ui:VisualElement class="screen-frame-content">
|
<ui:VisualElement class="screen-frame-content">
|
||||||
<ui:VisualElement class="collection-actions">
|
<ui:VisualElement class="collection-actions">
|
||||||
<ui:Button name="CollectionUnitsButton" text="units" focusable="false" class="collection-tab" />
|
<ui:Button name="CollectionUnitsButton" text="units" focusable="false" class="collection-tab" />
|
||||||
<ui:Button name="CollectionCardsButton" text="cards" focusable="false" class="collection-tab" />
|
<ui:Button name="CollectionLanguagesButton" text="languages" focusable="false" class="collection-tab" />
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
<ui:VisualElement name="CollectionContent" class="collection-shell">
|
<ui:VisualElement name="CollectionContent" class="collection-shell">
|
||||||
<ui:ScrollView name="CollectionList" class="collection-list" />
|
<ui:ScrollView name="CollectionList" class="collection-list" />
|
||||||
|
|||||||
+23
-1
@@ -528,9 +528,11 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.collection-tab {
|
.collection-tab {
|
||||||
width: 92px;
|
min-width: 92px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
border-bottom-width: 2px;
|
border-bottom-width: 2px;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -612,6 +614,10 @@ Button {
|
|||||||
border-left-color: var(--kp-primary);
|
border-left-color: var(--kp-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collection-row.locked {
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
.collection-row-name {
|
.collection-row-name {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
-unity-font-style: bold;
|
-unity-font-style: bold;
|
||||||
@@ -635,6 +641,18 @@ Button {
|
|||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collection-command-row {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection-command-row .command-description {
|
||||||
|
width: 190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection-card-row {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-section-title {
|
.detail-section-title {
|
||||||
color: var(--kp-primary);
|
color: var(--kp-primary);
|
||||||
-unity-font-style: bold;
|
-unity-font-style: bold;
|
||||||
@@ -667,6 +685,10 @@ Button {
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.run-language-header {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.package-notice {
|
.package-notice {
|
||||||
color: var(--kp-amber);
|
color: var(--kp-amber);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|||||||
Reference in New Issue
Block a user