Implement many UX improvements, packages and their assets and a UI overhaul in collection

This commit is contained in:
2026-07-09 10:18:24 +02:00
parent 61030d84b7
commit 844e81488c
111 changed files with 4480 additions and 258 deletions
+170 -49
View File
@@ -11,13 +11,117 @@
</component>
<component name="ChangeListManager">
<list default="true" id="4222a6b8-add3-4f43-9c42-fd6bb5a76169" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Apt.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Apt.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/AptFast.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/AptFast.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Auditd.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Auditd.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Ccache.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Ccache.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/CinnamonPackage.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/CinnamonPackage.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Coreutils.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Coreutils.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Cron.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Cron.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Curl.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Curl.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/DNF.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/DNF.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Dbus.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Dbus.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Docker.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Docker.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Ext4.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Ext4.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Firewalld.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Firewalld.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Git.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Git.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Glibc.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Glibc.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Htop.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Htop.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Less.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Less.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Logrotate.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Logrotate.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Lvm.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Lvm.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Make.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Make.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Nano.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Nano.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Nginx.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Nginx.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Ntp.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Ntp.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/OpenSSL.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/OpenSSL.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/PackageDatabase.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/PackageDatabase.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Polkit.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Polkit.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Rsync.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Rsync.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SELinuxPackage.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SELinuxPackage.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SnapStore.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SnapStore.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Sshd.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Sshd.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Swapfile.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Swapfile.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Syslog.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Syslog.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SystemdCore.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SystemdCore.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SystemdTimer.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/SystemdTimer.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/TimeshiftPackage.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/TimeshiftPackage.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Udev.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Udev.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Vim.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Vim.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Wget.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Wget.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Zlib.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Zlib.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Zram.asset" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Data/Packages/Zram.asset.meta" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Scripts/Data/PackageDatabase.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Scripts/Data/PackageDefinition.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/PackageLoadout.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/PackageScrapper.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/PackageTuning.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/PackageUpgrader.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.linuxdistrosbuttheyarebreedable/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.linuxdistrosbuttheyarebreedable/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Resources/CombatScene.uss" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Resources/CombatScene.uss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Data/Distros/Ubuntu.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Distros/Ubuntu.asset" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scenes/MainMenuScene.unity" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scenes/MainMenuScene.unity" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CardEffects.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CardEffects.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatManager.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatantState.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatantState.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/DamagePipeline.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/DamagePipeline.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Core/GameEvents.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Core/GameEvents.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaCurrencyType.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaCurrencyType.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaPullContext.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaPullContext.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaRewardType.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaRewardType.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/GachaService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/PlayerCollection.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/PlayerCollection.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/PullResolver.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/PullResolver.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Meta/SaveService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Meta/SaveService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Run/RunConfig.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Run/RunConfig.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Run/RunContext.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Run/RunContext.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Run/RunManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Run/RunManager.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/CollectionScreenController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/CollectionScreenController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/CombatSceneController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/CombatSceneController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/FeaturedUnitPanel.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/FeaturedUnitPanel.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/GachaScreenController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/GachaScreenController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/GamblingSceneController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/GamblingSceneController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/MainMenuController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/MainMenuController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/UI/MainMenu.uss" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/UI/MainMenu.uss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/UI/MainMenu.uxml" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/UI/MainMenu.uxml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -63,6 +167,13 @@
<setting file="file://$PROJECT_DIR$/Assets/Data/Distros/Fedora.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Distros/Mint.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Distros/Ubuntu.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/Apt.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/AptFast.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/CinnamonPackage.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/Make.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/SELinuxPackage.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/Syslog.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Packages/Wget.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Passives/AptUpdate.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Passives/BleedingEdge.asset" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Data/Passives/ItJustWorks.asset" root0="FORCE_HIGHLIGHTING" />
@@ -83,23 +194,23 @@
<component name="MetaFilesCheckinStateConfiguration" checkMetaFiles="true" />
<component name="NextEditCompletionFeaturesState">
<decayedCancelled>
<entry key="MS100" value="1.000000000000699" />
<entry key="MS500" value="1.0163073776265223" />
<entry key="S2" value="5.108221812019007" />
<entry key="S5" value="21.770600885518434" />
<entry key="S10" value="47.09517013998605" />
<entry key="S30" value="93.9785706567626" />
<entry key="S60" value="119.48644415542124" />
<entry key="M2" value="160.68491265036215" />
<entry key="M5" value="278.40590534440275" />
<entry key="M10" value="395.77142962415076" />
<entry key="M15" value="485.9382710538006" />
<entry key="M30" value="720.5335135398915" />
<entry key="H1" value="1054.9046690258854" />
<entry key="H2" value="1414.4969635593438" />
<entry key="H4" value="1731.0633909446262" />
<entry key="D1" value="3286.6219915604" />
<entry key="W1" value="4660.98507014222" />
<entry key="MS100" value="1.4963024003289789" />
<entry key="MS500" value="5.493825018740593" />
<entry key="S2" value="16.7623810091312" />
<entry key="S5" value="30.131569239395123" />
<entry key="S10" value="42.28180741555183" />
<entry key="S30" value="57.034967620889354" />
<entry key="S60" value="62.13535362816746" />
<entry key="M2" value="65.05702793593957" />
<entry key="M5" value="68.80146390839094" />
<entry key="M10" value="73.0920556177788" />
<entry key="M15" value="75.72809697957445" />
<entry key="M30" value="88.95784666638842" />
<entry key="H1" value="204.48905359593877" />
<entry key="H2" value="565.8614083894486" />
<entry key="H4" value="1095.0876588283427" />
<entry key="D1" value="3089.1206296820615" />
<entry key="W1" value="4685.751345931765" />
</decayedCancelled>
<decayedSelected>
<entry key="MS100" value="0.0" />
@@ -109,35 +220,35 @@
<entry key="S10" value="0.0" />
<entry key="S30" value="0.0" />
<entry key="S60" value="0.0" />
<entry key="M2" value="2.5063439983015887E-240" />
<entry key="M5" value="1.4441631903175532E-96" />
<entry key="M10" value="1.2017334106687881E-48" />
<entry key="M15" value="1.130330440943858E-32" />
<entry key="M30" value="1.0631703388171175E-16" />
<entry key="H1" value="1.0321150378964974E-8" />
<entry key="H2" value="1.0705766421479234E-4" />
<entry key="H4" value="0.014144219737749934" />
<entry key="D1" value="1.4628029396241715" />
<entry key="W1" value="3.4598536273655087" />
<entry key="M2" value="2.5828778732379276E-268" />
<entry key="M5" value="9.222348906385734E-108" />
<entry key="M10" value="3.0368320510667516E-54" />
<entry key="M15" value="2.0970744528210016E-36" />
<entry key="M30" value="1.4481283797362389E-18" />
<entry key="H1" value="1.2045649226616757E-9" />
<entry key="H2" value="3.65736931549959E-5" />
<entry key="H4" value="0.008267125324716448" />
<entry key="D1" value="1.3375655629148175" />
<entry key="W1" value="3.4158969636928482" />
</decayedSelected>
<decayedShown>
<entry key="MS100" value="0.9526379980446108" />
<entry key="MS500" value="1.0065360445565774" />
<entry key="S2" value="5.098242978856544" />
<entry key="S5" value="21.754020235151167" />
<entry key="S10" value="47.07706009383575" />
<entry key="S30" value="93.9663762674251" />
<entry key="S60" value="119.47863856671924" />
<entry key="M2" value="160.67946494648172" />
<entry key="M5" value="278.4016956102392" />
<entry key="M10" value="395.76819463253577" />
<entry key="M15" value="485.9354965404165" />
<entry key="M30" value="720.531339531474" />
<entry key="H1" value="1054.9030513551575" />
<entry key="H2" value="1414.4959842118083" />
<entry key="H4" value="1731.0768609975287" />
<entry key="D1" value="3288.0844643497253" />
<entry key="W1" value="4664.444843440584" />
<entry key="MS100" value="1.4096752248792137" />
<entry key="MS500" value="5.428036231472652" />
<entry key="S2" value="16.712977941585102" />
<entry key="S5" value="30.091551042144605" />
<entry key="S10" value="42.251172488375474" />
<entry key="S30" value="57.02028505912365" />
<entry key="S60" value="62.127231458210936" />
<entry key="M2" value="65.05274498886968" />
<entry key="M5" value="68.79965480133401" />
<entry key="M10" value="73.09110313975116" />
<entry key="M15" value="75.72744303179836" />
<entry key="M30" value="88.95747776256005" />
<entry key="H1" value="204.48869117823617" />
<entry key="H2" value="565.860985887298" />
<entry key="H4" value="1095.0954876479952" />
<entry key="D1" value="3090.4578859402736" />
<entry key="W1" value="4689.167162525937" />
</decayedShown>
</component>
<component name="ProjectColorInfo">{
@@ -228,7 +339,8 @@
<workItem from="1783372397891" duration="989000" />
<workItem from="1783410502976" duration="23430000" />
<workItem from="1783507489679" duration="5909000" />
<workItem from="1783519887449" duration="4333000" />
<workItem from="1783519887449" duration="6425000" />
<workItem from="1783578446731" duration="5031000" />
</task>
<task id="LOCAL-00001" summary="implement main screen">
<option name="closed" value="true" />
@@ -414,7 +526,15 @@
<option name="project" value="LOCAL" />
<updated>1783503035031</updated>
</task>
<option name="localTasksCounter" value="24" />
<task id="LOCAL-00024" summary="make small changes to design. Implement crits.">
<option name="closed" value="true" />
<created>1783531936700</created>
<option name="number" value="00024" />
<option name="presentableId" value="LOCAL-00024" />
<option name="project" value="LOCAL" />
<updated>1783531936701</updated>
</task>
<option name="localTasksCounter" value="25" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -452,7 +572,8 @@
<MESSAGE value="Implement cutscene for gambling and make it dopamine-inducing" />
<MESSAGE value="changed most TODOs to TODONEs" />
<MESSAGE value="Implement Java and Rust cards alongside some smaller changes" />
<option name="LAST_COMMIT_MESSAGE" value="Implement Java and Rust cards alongside some smaller changes" />
<MESSAGE value="make small changes to design. Implement crits." />
<option name="LAST_COMMIT_MESSAGE" value="make small changes to design. Implement crits." />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
+1 -1
View File
@@ -28,7 +28,7 @@ MonoBehaviour:
accentColor: {r: 0.9137255, g: 0.32941177, b: 0.1254902, a: 1}
baseUptime: 60
baseRam: 5
baseCyclesPerTurn: 3
baseCyclesPerTurn: 4
exclusiveCards:
- {fileID: 11400000, guid: 9347bcc42fb6834f1907a2c0b661ff95, type: 2}
- {fileID: 11400000, guid: 1dbc28e480e52d97b9501262af30cc78, type: 2}
+19
View File
@@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Apt
id: pkg_ubuntu_apt
displayName: apt
slot: 2
rarity: 5
description: Each time you play your 3rd card in a TURN, add a random 0-cost card of your languages to hand; on Ubuntu also refund 1 Cycle.
flavourText: There is always one more dependency.
designNotes: Ubuntu 5-star Daemon. TURN third-card trigger. requiresSystem: card-generation + per-turn-card-count. On Ubuntu refunds 1 Cycle.
intendedDistroId: ubuntu
requiresSystem:
- card-generation
- per-turn-card-count
offDistroEffect: {kind: 10, amount: 1, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 10, amount: 1, threshold: 3, refundCycle: 1, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cef0ecd4cdaa4fd09c2c8578263acd20
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: AptFast
m_EditorClassIdentifier:
id: pkg_apt_fast
displayName: apt-fast
slot: 1
rarity: 3
description: Start of each WAVE, gain +1 Cycle that WAVE's first TURN.
flavourText: Parallel downloads, parallel consequences.
designNotes: Generic 3-star Runtime powerspike. First-turn WAVE cycle hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 18, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 18, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96d18432766243138d800f9f544bc934
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Auditd
m_EditorClassIdentifier:
id: pkg_auditd
displayName: auditd
slot: 2
rarity: 3
description: Start of each TURN, if you have no debuffs, gain 3 Shield.
flavourText: Logs suspicious activity, including competence.
designNotes: Generic 3-star Daemon powerspike. Uses status descriptor harmful query.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 23, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 23, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a8e4cfd86f554fa19198551a7d5ed8ab
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Ccache
m_EditorClassIdentifier:
id: pkg_ccache
displayName: ccache
slot: 1
rarity: 3
description: Every 4th card played in a WAVE costs 0.
flavourText: Why compile twice when you can be smug once?
designNotes: Generic 3-star Runtime powerspike. Existing nth-card WAVE free-cost hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 8, amount: 0, threshold: 4, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 8, amount: 0, threshold: 4, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f68f89f3d4554063a891c30b1c387692
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: CinnamonPackage
id: pkg_mint_cinnamon
displayName: Cinnamon
slot: 1
rarity: 4
description: JavaScript cards deal +3 flat on Mint, +2 off-distro.
flavourText: Comfortable, polished, and a little too smug.
designNotes: Mint 4-star Runtime. Flat add through DamagePipeline additive step. On Mint +3; off-distro +2.
intendedDistroId: mint
requiresSystem:
- damage-pipeline-additive
offDistroEffect: {kind: 11, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 11, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5b13be9296e347459ed3659e1855a5d3
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Coreutils
m_EditorClassIdentifier:
id: pkg_coreutils
displayName: coreutils
slot: 0
rarity: 1
description: +2 max Uptime (COMBAT).
flavourText: The box of tiny tools that somehow runs everything.
designNotes: Generic 1-star Kernel baseline. COMBAT stat package. On/off distro identical.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 1, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7422da9aba294553a6009eb3c01498e1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Cron
id: pkg_cron
displayName: cron
slot: 2
rarity: 1
description: Every 3rd TURN, gain 2 Shield.
flavourText: Eventually, it helps.
designNotes: Generic 1-star Daemon. TURN counter shield. On/off distro identical.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 6, amount: 2, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 6, amount: 2, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0b6678d8a231464b8ff257ece3dcfeae
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Curl
id: pkg_curl
displayName: curl
slot: 1
rarity: 1
description: Draw 1 extra card at the start of each WAVE.
flavourText: It downloads help, mostly by accident.
designNotes: Generic 1-star Runtime. WAVE start draw. On/off distro identical.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 3, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 3, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 415021c139b54ff1a6b9cc2e5aede209
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+19
View File
@@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: DNF
id: pkg_fedora_dnf
displayName: DNF
slot: 1
rarity: 5
description: On Fedora, Bleeding Edge applies to the 2nd card each TURN. Also builds a stacking +2 damage buff this WAVE.
flavourText: Fast, sharp, occasionally on fire.
designNotes: Fedora 5-star Runtime. Ties into Bleeding Edge crash logic; on Fedora enables 2nd-card passive discount+bonus. Off-distro only uses the WAVE stacking +2 damage buff hook.
intendedDistroId: fedora
requiresSystem:
- fedora-passive-hook
- per-wave-damage-buff
offDistroEffect: {kind: 14, amount: 2, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 14, amount: 2, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 1}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f47028f76c0843629789bf128bae4c3d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Dbus
m_EditorClassIdentifier:
id: pkg_dbus
displayName: dbus
slot: 2
rarity: 1
description: First time you play a queued (InterpreterQueue) card each WAVE, gain 2 Shield.
flavourText: Messages everywhere, consensus nowhere.
designNotes: Generic 1-star Daemon. InterpreterQueue play hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 21, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 21, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e90cac6fdbde448a9eb7432889a0ec39
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+18
View File
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Docker
id: pkg_docker
displayName: docker
slot: 1
rarity: 3
description: At the start of each WAVE, add a random 0-cost basic card of the distro's languages to hand.
flavourText: Works on my daemon.
designNotes: Generic 3-star Runtime powerspike. WAVE start generation. requiresSystem: card-generation; uses existing generated-card pool when available, TODO logged if not.
intendedDistroId:
requiresSystem:
- card-generation
offDistroEffect: {kind: 4, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 4, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a392e5d09c864940bde926a68f4681a7
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Ext4
m_EditorClassIdentifier:
id: pkg_ext4
displayName: ext4
slot: 0
rarity: 2
description: +4 max Uptime (COMBAT).
flavourText: Journaling your mistakes so recovery has something to read.
designNotes: Generic 2-star Kernel. COMBAT stat package.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 1, amount: 4, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 4, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 309a914d85d544d4bd2e19b35d40768e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Firewalld
m_EditorClassIdentifier:
id: pkg_firewalld
displayName: firewalld
slot: 2
rarity: 2
description: The first debuff applied to you each WAVE is reduced by 1 stack.
flavourText: Blocks everything except the one port you forgot about.
designNotes: Generic 2-star Daemon. TODO status prevention/rewrite hook missing.
intendedDistroId:
requiresSystem: [status-apply-prevention-hook]
offDistroEffect: {kind: 0, amount: 0, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 0, amount: 0, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f09c5c4fed8547a7ade6e4c1129acf08
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Git
id: pkg_git
displayName: git
slot: 1
rarity: 2
description: The first card played each WAVE costs 1 less.
flavourText: Everyone blames the branch.
designNotes: Generic 2-star Runtime. WAVE first-card economy. On/off distro identical.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 5, amount: 1, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 5, amount: 1, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 36e380dd7864488a9544eff6d0aa15d5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Glibc
m_EditorClassIdentifier:
id: pkg_glibc
displayName: glibc
slot: 0
rarity: 1
description: +2 max Uptime (COMBAT).
flavourText: Everything links against it, including your bad decisions.
designNotes: Generic 1-star Kernel. COMBAT stat package. Upgrade scaling increases amount at runtime.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 1, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 03b4a29f7baa4a4c8cc5945a508b64a5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Htop
m_EditorClassIdentifier:
id: pkg_htop
displayName: htop
slot: 0
rarity: 2
description: +5 max Uptime (COMBAT).
flavourText: Green bars make the panic feel managed.
designNotes: Generic 2-star Kernel. COMBAT stat package. On/off distro identical.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 1, amount: 5, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 5, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b3e47005ebdf42788204e5b5677f9002
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Less
m_EditorClassIdentifier:
id: pkg_less
displayName: less
slot: 1
rarity: 1
description: Once per WAVE, the first card drawn is free to play (0 cost) that TURN.
flavourText: Less is more, except when it is just a pager.
designNotes: Generic 1-star Runtime. TODO free-cast/draw-tag hook missing.
intendedDistroId:
requiresSystem: [free-cast-drawn-card-hook]
offDistroEffect: {kind: 0, amount: 0, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 0, amount: 0, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 76af631396664958a398073c6021a626
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+18
View File
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Logrotate
id: pkg_logrotate
displayName: logrotate
slot: 2
rarity: 2
description: When you exhaust a card, gain 1 Shield.
flavourText: Deletes the evidence, keeps the casing.
designNotes: Generic 2-star Daemon. Exhaust hook; current authored exhaust path is dnf autoremove.
intendedDistroId:
requiresSystem:
- exhaust-hook
offDistroEffect: {kind: 7, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 7, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0f2a212cc70d422189f26c0314853ef4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Lvm
m_EditorClassIdentifier:
id: pkg_lvm
displayName: lvm
slot: 0
rarity: 3
description: +8 max Uptime (COMBAT).
flavourText: Logical volumes, illogical confidence.
designNotes: Generic 3-star Kernel powerspike. COMBAT stat package.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 1, amount: 8, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 8, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f30e0284fa5a45aaa7507089a31aaf88
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Make
m_EditorClassIdentifier:
id: pkg_make
displayName: make
slot: 1
rarity: 2
description: The first Native card each WAVE deals +3.
flavourText: Tab-sensitive violence.
designNotes: Generic 2-star Runtime. Routes through existing flat effect bonus before first Native card resolves.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 20, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 20, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ed56f679bb7b4db9ad7884bfeb7f1660
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Nano
m_EditorClassIdentifier:
id: pkg_nano
displayName: nano
slot: 1
rarity: 1
description: The first card played each WAVE costs 1 less.
flavourText: Exit instructions included, panic reduced.
designNotes: Generic 1-star Runtime. Existing first-card-per-WAVE cost hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 5, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 5, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 47286d5a87334b1fa303a583a9b1085e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Nginx
id: pkg_nginx
displayName: nginx
slot: 2
rarity: 3
description: Every 3rd card played in a WAVE costs 0.
flavourText: Reverse proxies your budget.
designNotes: Generic 3-star Daemon powerspike. WAVE card counter cost override.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 8, amount: 0, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 8, amount: 0, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e4cb1d11f3dd44878111ce3b5f89ee8c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Ntp
m_EditorClassIdentifier:
id: pkg_ntp
displayName: ntp
slot: 0
rarity: 2
description: +3 max Uptime (COMBAT); first TURN of each WAVE, gain 2 Shield.
flavourText: Corrects clock drift and, briefly, life choices.
designNotes: Generic 2-star Kernel. Uses MaxUptime now; first-turn shield TODO until multi-effect packages exist.
intendedDistroId:
requiresSystem: [multi-effect-package]
offDistroEffect: {kind: 1, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c9c4f393b7a4dad874f50d02984d4d5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: OpenSSL
m_EditorClassIdentifier:
id: pkg_openssl
displayName: openssl
slot: 0
rarity: 1
description: +1 max Uptime (COMBAT), +1 Shield at WAVE start.
flavourText: The lock icon is mostly vibes, but the vibes are encrypted.
designNotes: Generic 1-star Kernel. Uses MaxUptime now; WAVE shield portion TODO until multi-effect packages exist.
intendedDistroId:
requiresSystem: [multi-effect-package]
offDistroEffect: {kind: 1, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 1, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2320e53b9b8a4f5184025ddba1581f65
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 04c3b1510a7d45f3aba1ee24fe0cbc12, type: 3}
m_Name: PackageDatabase
allPackages:
- {fileID: 11400000, guid: 7422da9aba294553a6009eb3c01498e1, type: 2}
- {fileID: 11400000, guid: b3e47005ebdf42788204e5b5677f9002, type: 2}
- {fileID: 11400000, guid: 7b77920d46a0434ab4e772339293d991, type: 2}
- {fileID: 11400000, guid: 415021c139b54ff1a6b9cc2e5aede209, type: 2}
- {fileID: 11400000, guid: 36e380dd7864488a9544eff6d0aa15d5, type: 2}
- {fileID: 11400000, guid: a392e5d09c864940bde926a68f4681a7, type: 2}
- {fileID: 11400000, guid: 0b6678d8a231464b8ff257ece3dcfeae, type: 2}
- {fileID: 11400000, guid: 0f2a212cc70d422189f26c0314853ef4, type: 2}
- {fileID: 11400000, guid: e4cb1d11f3dd44878111ce3b5f89ee8c, type: 2}
- {fileID: 11400000, guid: ccaa32454a304e1f96d6d340b9c1900e, type: 2}
- {fileID: 11400000, guid: cef0ecd4cdaa4fd09c2c8578263acd20, type: 2}
- {fileID: 11400000, guid: 5b13be9296e347459ed3659e1855a5d3, type: 2}
- {fileID: 11400000, guid: 07554ac885824a6e8e49d7d39cf2fa30, type: 2}
- {fileID: 11400000, guid: c9b1e99838c74709aafe6d659b41f231, type: 2}
- {fileID: 11400000, guid: f47028f76c0843629789bf128bae4c3d, type: 2}
- {fileID: 11400000, guid: 03b4a29f7baa4a4c8cc5945a508b64a5, type: 2}
- {fileID: 11400000, guid: 40d2efb5aa8d4b85a4bdc7b3cb7781d8, type: 2}
- {fileID: 11400000, guid: 2320e53b9b8a4f5184025ddba1581f65, type: 2}
- {fileID: 11400000, guid: 309a914d85d544d4bd2e19b35d40768e, type: 2}
- {fileID: 11400000, guid: a48fbfda32824a7cb127318c93602420, type: 2}
- {fileID: 11400000, guid: 5c9c4f393b7a4dad874f50d02984d4d5, type: 2}
- {fileID: 11400000, guid: 0a11dac6bc6d435192a262eb1d8ac685, type: 2}
- {fileID: 11400000, guid: f30e0284fa5a45aaa7507089a31aaf88, type: 2}
- {fileID: 11400000, guid: 19d4c144fb5c4ae3b0bccb4b048aaa96, type: 2}
- {fileID: 11400000, guid: 47286d5a87334b1fa303a583a9b1085e, type: 2}
- {fileID: 11400000, guid: 76af631396664958a398073c6021a626, type: 2}
- {fileID: 11400000, guid: a3b1582341434a8981c802f8d7da2420, type: 2}
- {fileID: 11400000, guid: b47b1186672c45e19012b4e74c8b0b93, type: 2}
- {fileID: 11400000, guid: ed56f679bb7b4db9ad7884bfeb7f1660, type: 2}
- {fileID: 11400000, guid: 96d18432766243138d800f9f544bc934, type: 2}
- {fileID: 11400000, guid: f68f89f3d4554063a891c30b1c387692, type: 2}
- {fileID: 11400000, guid: 302d3da8597941ea99c6d507fa9d3d28, type: 2}
- {fileID: 11400000, guid: 177e5f43f6544549bf12e02ec05ec833, type: 2}
- {fileID: 11400000, guid: e90cac6fdbde448a9eb7432889a0ec39, type: 2}
- {fileID: 11400000, guid: f3174349716e4db6a7ffed8205f2a541, type: 2}
- {fileID: 11400000, guid: f09c5c4fed8547a7ade6e4c1129acf08, type: 2}
- {fileID: 11400000, guid: 159fca0abb7249e1be485aaac5f83fbe, type: 2}
- {fileID: 11400000, guid: ac481beaf7e6453d82a439d54c2fb2f3, type: 2}
- {fileID: 11400000, guid: a8e4cfd86f554fa19198551a7d5ed8ab, type: 2}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: efca08b3991e42e5b1e03878507496a6
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Polkit
m_EditorClassIdentifier:
id: pkg_polkit
displayName: polkit
slot: 2
rarity: 2
description: First time you gain Shield each TURN, gain +2 extra.
flavourText: Permission denied, but with a helpful dialog nobody reads.
designNotes: Generic 2-star Daemon. Existing first shield each TURN bonus hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 13, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 13, amount: 2, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 159fca0abb7249e1be485aaac5f83fbe
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Rsync
m_EditorClassIdentifier:
id: pkg_rsync
displayName: rsync
slot: 1
rarity: 2
description: At WAVE start, draw 1 extra card.
flavourText: Efficiently copies hope from the deck into your hand.
designNotes: Generic 2-star Runtime. WAVE draw hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 3, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 3, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b47b1186672c45e19012b4e74c8b0b93
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+18
View File
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: SELinuxPackage
id: pkg_fedora_selinux
displayName: SELinux
slot: 2
rarity: 4
description: First time you gain Shield each TURN, gain +5 extra on Fedora, +3 off-distro.
flavourText: It denied the damage and filed a report.
designNotes: Fedora 4-star Daemon. TURN first-shield trigger. On Fedora +5; off-distro +3.
intendedDistroId: fedora
requiresSystem:
- shield-gain-hook
offDistroEffect: {kind: 13, amount: 3, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 13, amount: 5, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c9b1e99838c74709aafe6d659b41f231
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+18
View File
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: SnapStore
id: pkg_ubuntu_snap_store
displayName: Snap Store
slot: 1
rarity: 4
description: First cards played each TURN cost 1 less; first 2 on Ubuntu, first 1 off-distro.
flavourText: Starts instantly after a brief geological pause.
designNotes: Ubuntu 4-star Runtime. TURN card-count cost discount. On Ubuntu threshold 2; off-distro threshold 1.
intendedDistroId: ubuntu
requiresSystem:
- per-turn-card-count
offDistroEffect: {kind: 9, amount: 1, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 9, amount: 1, threshold: 2, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ccaa32454a304e1f96d6d340b9c1900e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Sshd
m_EditorClassIdentifier:
id: pkg_sshd
displayName: sshd
slot: 2
rarity: 2
description: Every 3rd TURN, gain 3 Shield.
flavourText: Open port, closed heart.
designNotes: Generic 2-star Daemon. Existing nth-TURN shield hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 6, amount: 3, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 6, amount: 3, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f3174349716e4db6a7ffed8205f2a541
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Swapfile
m_EditorClassIdentifier:
id: pkg_swapfile
displayName: swapfile
slot: 0
rarity: 2
description: +1 max RAM (COMBAT).
flavourText: RAM, but with the emotional latency of a spinning disk.
designNotes: Generic 2-star Kernel. COMBAT RAM stat package.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 19, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 19, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a48fbfda32824a7cb127318c93602420
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Syslog
m_EditorClassIdentifier:
id: pkg_syslog
displayName: syslog
slot: 2
rarity: 1
description: When you exhaust a card, gain 1 Shield.
flavourText: Writes down the problem, then pretends that helped.
designNotes: Generic 1-star Daemon. Existing exhaust shield hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 7, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 7, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 302d3da8597941ea99c6d507fa9d3d28
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: SystemdCore
m_EditorClassIdentifier:
id: pkg_systemd_core
displayName: systemd-core
slot: 0
rarity: 3
description: +1 max Cycles (COMBAT).
flavourText: Yes, it manages that too.
designNotes: Generic 3-star Kernel powerspike. COMBAT max Cycle package. On/off distro identical.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 2, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 2, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b77920d46a0434ab4e772339293d991
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: SystemdTimer
m_EditorClassIdentifier:
id: pkg_systemd_timer
displayName: systemd-timer
slot: 2
rarity: 3
description: Every 3rd card played in a WAVE, gain 1 Cycle.
flavourText: Cron with a committee and better unit files.
designNotes: Generic 3-star Daemon powerspike. Nth-card WAVE cycle hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 24, amount: 1, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 24, amount: 1, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac481beaf7e6453d82a439d54c2fb2f3
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: TimeshiftPackage
id: pkg_mint_timeshift
displayName: Timeshift
slot: 0
rarity: 5
description: First time each WAVE Uptime would drop below a threshold, restore to it; 50% + cleanse on Mint, 30% off-distro.
flavourText: Undo button for bad life choices.
designNotes: Mint 5-star Kernel. WAVE damage/heal hook through DamagePipeline event; status cleanse on Mint only.
intendedDistroId: mint
requiresSystem:
- damage-pipeline-hook
- status-cleanse
offDistroEffect: {kind: 12, amount: 0, threshold: 30, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 12, amount: 0, threshold: 50, refundCycle: 0, cleanseDebuffs: 1, enableFedoraSecondCardPassive: 0}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 07554ac885824a6e8e49d7d39cf2fa30
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Udev
m_EditorClassIdentifier:
id: pkg_udev
displayName: udev
slot: 2
rarity: 1
description: Every 4th TURN, draw 1 card.
flavourText: A device appeared, so naturally you drew paperwork.
designNotes: Generic 1-star Daemon. TURN draw hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 22, amount: 1, threshold: 4, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 22, amount: 1, threshold: 4, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 177e5f43f6544549bf12e02ec05ec833
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Vim
m_EditorClassIdentifier:
id: pkg_vim
displayName: vim
slot: 1
rarity: 2
description: First card played each TURN costs 1 less.
flavourText: Modal editing for modal spending.
designNotes: Generic 2-star Runtime. Existing first-card-per-TURN cost hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 9, amount: 1, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 9, amount: 1, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a3b1582341434a8981c802f8d7da2420
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Wget
m_EditorClassIdentifier:
id: pkg_wget
displayName: wget
slot: 1
rarity: 1
description: Draw 1 extra card on the first TURN of the first WAVE.
flavourText: Downloads one more thing before anyone reads the flags.
designNotes: Generic 1-star Runtime. First WAVE opening draw hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 17, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 17, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 19d4c144fb5c4ae3b0bccb4b048aaa96
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Zlib
m_EditorClassIdentifier:
id: pkg_zlib
displayName: zlib
slot: 0
rarity: 1
description: Start each WAVE with 3 Shield.
flavourText: Compresses incoming panic into a smaller panic.
designNotes: Generic 1-star Kernel. WAVE shield hook.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 15, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 15, amount: 3, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 40d2efb5aa8d4b85a4bdc7b3cb7781d8
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
m_Name: Zram
m_EditorClassIdentifier:
id: pkg_zram
displayName: zram
slot: 0
rarity: 3
description: +1 max Cycles (COMBAT).
flavourText: Compress the pain until it becomes throughput.
designNotes: Generic 3-star Kernel powerspike. COMBAT cycle stat package.
intendedDistroId:
requiresSystem: []
offDistroEffect: {kind: 2, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
onDistroEffect: {kind: 2, amount: 1, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0a11dac6bc6d435192a262eb1d8ac685
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
+1
View File
@@ -274,5 +274,6 @@ MonoBehaviour:
motdBody: unstable userspace detected; keep a rollback shell open.
distroDatabase: {fileID: 11400000, guid: 4d88ca5446fa4d80b2fbd4b12697cb7c, type: 2}
cardDatabase: {fileID: 11400000, guid: 1632132e32bfc31e8a293140f5908aa3, type: 2}
packageDatabase: {fileID: 11400000, guid: efca08b3991e42e5b1e03878507496a6, type: 2}
languageDeckDatabase: {fileID: 11400000, guid: a7d20000000000000000000000000001, type: 2}
featuredUnitPanel: {}
+4 -6
View File
@@ -84,8 +84,7 @@ namespace KernelPanic.Combat
return;
}
context.Source.Shield += amount;
context.CombatManager.ReportEffectResult($"gained {amount} shield");
context.CombatManager.GrantPlayerShield(amount, "card");
}
}
@@ -110,8 +109,7 @@ namespace KernelPanic.Combat
return;
}
context.Source.Shield += amount;
context.CombatManager.ReportEffectResult($"gained {amount} shield");
context.CombatManager.GrantPlayerShield(amount, "card");
}
}
@@ -502,8 +500,7 @@ namespace KernelPanic.Combat
continue;
}
context.Source.Shield += overkill;
context.CombatManager.ReportEffectResult($"overkill -> {overkill} shield");
context.CombatManager.GrantPlayerShield(overkill, "overkill");
}
}
}
@@ -572,6 +569,7 @@ namespace KernelPanic.Combat
if (context.HandController.Remove(junk))
{
context.DeckController.Exhaust(junk);
context.CombatManager.HandleCardExhausted(junk);
int cycleGain = UpgradeMath.ScaleAmount(_cycleGain, context.Card);
context.Source.Cycles += cycleGain;
context.CombatManager.ReportEffectResult($"autoremove exhausted {GetCardName(junk)}: +{cycleGain} cycle");
+401 -1
View File
@@ -41,6 +41,12 @@ namespace KernelPanic.Combat
private int fedoraCardsDiscountedThisTurn;
private int fedoraCrashChance = 10;
private int cardsPlayedThisTurn;
private int cardsPlayedThisWave;
private int turnNumberThisWave;
private bool packageShieldBonusTriggeredThisTurn;
private bool packageTimeshiftTriggeredThisWave;
private bool packageInterpreterQueueShieldTriggeredThisWave;
private bool packageNativeDamageTriggeredThisWave;
private int javaCardsPlayedThisCombat;
private int javaCardsDiscountThisTurn;
private int rawhideBonusCharges;
@@ -72,6 +78,16 @@ namespace KernelPanic.Combat
runManager = GetComponent<RunManager>();
}
private void OnEnable()
{
GameEvents.DamageDealt += HandlePackageDamageDealt;
}
private void OnDisable()
{
GameEvents.DamageDealt -= HandlePackageDamageDealt;
}
public void StartCombat()
{
if (runConfig == null)
@@ -197,6 +213,7 @@ namespace KernelPanic.Combat
ResolutionTrack track = card.Definition.ResolutionTrack;
card.MarkPlayedThisTurn(cardsPlayedThisTurn == 0);
cardsPlayedThisTurn++;
cardsPlayedThisWave++;
if (card.Definition.Language == Language.Java)
{
javaCardsPlayedThisCombat++;
@@ -256,6 +273,7 @@ namespace KernelPanic.Combat
}
playerState.DamageMultiplierPercent = 100;
ApplyPackageCardPlayedEffects(card);
pendingTargetCard = null;
TryApplyUbuntuEmptyHandRefill();
@@ -281,6 +299,7 @@ namespace KernelPanic.Combat
cost -= javaCardsPlayedThisCombat + javaCardsDiscountThisTurn;
}
cost = ApplyPackageCostRules(card, cost);
return Mathf.Max(0, cost);
}
@@ -360,6 +379,16 @@ namespace KernelPanic.Combat
ReportEffectResult($"next {charges} enemy attack(s) halved");
}
public void HandleCardExhausted(CardInstance card)
{
if (card == null || !HasPackageEffect(PackageEffectKind.ExhaustShield, out PackageEffectData effect))
{
return;
}
GrantPlayerShield(Mathf.Max(0, effect.Amount), "logrotate");
}
public void ScheduleUpdateManagerRepeat(int damageAmount)
{
delayedEffects.Add(DelayedCombatEffect.UpdateManagerRepeat(damageAmount, 2));
@@ -536,6 +565,12 @@ namespace KernelPanic.Combat
fedoraCrashChance = 10;
fedoraCardsDiscountedThisTurn = 0;
cardsPlayedThisTurn = 0;
cardsPlayedThisWave = 0;
turnNumberThisWave = 0;
packageShieldBonusTriggeredThisTurn = false;
packageTimeshiftTriggeredThisWave = false;
packageInterpreterQueueShieldTriggeredThisWave = false;
packageNativeDamageTriggeredThisWave = false;
javaCardsPlayedThisCombat = 0;
javaCardsDiscountThisTurn = 0;
rawhideBonusCharges = 0;
@@ -545,6 +580,7 @@ namespace KernelPanic.Combat
RandomRoll.Seed(runConfig.RunSeed);
playerState = new CombatantState(runManager.EffectiveMaxUptime(), runManager.EffectiveRam(), runManager.EffectiveMaxCycles());
ApplyVersionState(playerState);
ApplyPackageState(playerState);
yield return StartWaveSequenced(preservePlayerUptime: false);
Log($"booted {runConfig.Distro.DisplayName} with {runManager.RunDeck.Count} cards");
StateChanged?.Invoke();
@@ -557,6 +593,7 @@ namespace KernelPanic.Combat
{
playerState = new CombatantState(runManager.EffectiveMaxUptime(), runManager.EffectiveRam(), runManager.EffectiveMaxCycles());
ApplyVersionState(playerState);
ApplyPackageState(playerState);
}
else
{
@@ -570,11 +607,16 @@ namespace KernelPanic.Combat
playerState.IsDefeated = false;
playerState.MutableStatuses.Clear();
ApplyVersionState(playerState);
ApplyPackageState(playerState);
}
handController = new HandController(playerState.Ram);
selectedEnemyIndex = -1;
pendingTargetCard = null;
cardsPlayedThisWave = 0;
turnNumberThisWave = 0;
packageShieldBonusTriggeredThisTurn = false;
packageTimeshiftTriggeredThisWave = false;
List<CardInstance> startingCards = new();
for (int i = 0; i < runManager.RunDeck.Count; i++)
@@ -594,6 +636,7 @@ namespace KernelPanic.Combat
PickEnemyIntents();
StateChanged?.Invoke();
yield return DrawOpeningHandSequenced();
ApplyPackageWaveStartEffects();
skipNextAllocateDraw = true;
}
@@ -607,7 +650,10 @@ namespace KernelPanic.Combat
playerState.Cycles = playerState.MaxCycles;
fedoraCardsDiscountedThisTurn = 0;
cardsPlayedThisTurn = 0;
turnNumberThisWave++;
packageShieldBonusTriggeredThisTurn = false;
javaCardsDiscountThisTurn = 0;
ApplyPackageTurnStartEffects();
if (nextTurnCycleBonus > 0)
{
playerState.Cycles += nextTurnCycleBonus;
@@ -852,7 +898,7 @@ namespace KernelPanic.Combat
return false;
}
int limit = runConfig.DistroVersion >= 5 ? 2 : 1;
int limit = runConfig.DistroVersion >= 5 || HasOnDistroPackageEffect(PackageEffectKind.DnfFedoraPassive, out PackageEffectData dnfEffect) && dnfEffect.EnableFedoraSecondCardPassive ? 2 : 1;
return fedoraCardsDiscountedThisTurn < limit;
}
@@ -870,6 +916,360 @@ namespace KernelPanic.Combat
}
}
private int ApplyPackageCostRules(CardInstance card, int cost)
{
if (card == null || cost <= 0)
{
return cost;
}
IReadOnlyList<PackageInstance> packages = runConfig?.EquippedPackages;
if (packages == null)
{
return cost;
}
int nextTurnCard = cardsPlayedThisTurn + 1;
int nextWaveCard = cardsPlayedThisWave + 1;
for (int i = 0; i < packages.Count; i++)
{
PackageInstance package = packages[i];
if (package?.Definition == null)
{
continue;
}
PackageEffectData effect = package.EffectFor(runConfig?.Distro?.Id);
switch (effect.Kind)
{
case PackageEffectKind.FirstCardEachWaveCostReduction:
if (nextWaveCard == 1)
{
cost -= Mathf.Max(0, effect.Amount);
}
break;
case PackageEffectKind.EveryNthCardEachWaveFree:
if (effect.Threshold > 0 && nextWaveCard % effect.Threshold == 0)
{
cost = 0;
}
break;
case PackageEffectKind.FirstCardsEachTurnCostReduction:
if (nextTurnCard <= Mathf.Max(0, effect.Threshold))
{
cost -= Mathf.Max(0, effect.Amount);
}
break;
}
}
return cost;
}
private void ApplyPackageState(CombatantState state)
{
if (state == null)
{
return;
}
state.JavaScriptFlatDamageBonus = 0;
IReadOnlyList<PackageInstance> packages = runConfig?.EquippedPackages;
if (packages == null)
{
return;
}
for (int i = 0; i < packages.Count; i++)
{
PackageEffectData effect = packages[i] == null ? default : packages[i].EffectFor(runConfig?.Distro?.Id);
if (effect.Kind == PackageEffectKind.JavaScriptFlatDamage)
{
state.JavaScriptFlatDamageBonus += Mathf.Max(0, effect.Amount);
}
}
}
private void ApplyPackageWaveStartEffects()
{
IReadOnlyList<PackageInstance> packages = runConfig?.EquippedPackages;
if (packages == null)
{
return;
}
for (int i = 0; i < packages.Count; i++)
{
PackageInstance package = packages[i];
if (package?.Definition == null)
{
continue;
}
PackageEffectData effect = package.EffectFor(runConfig?.Distro?.Id);
switch (effect.Kind)
{
case PackageEffectKind.WaveDraw:
StartCoroutine(DrawCardsToHandSequenced(Mathf.Max(0, effect.Amount), package.Definition.DisplayName));
break;
case PackageEffectKind.WaveStartShield:
GrantPlayerShield(Mathf.Max(0, effect.Amount), package.Definition.DisplayName);
break;
case PackageEffectKind.FirstTurnEachWaveShield:
if (turnNumberThisWave == 0)
{
GrantPlayerShield(Mathf.Max(0, effect.Amount), package.Definition.DisplayName);
}
break;
case PackageEffectKind.FirstTurnFirstWaveDraw:
if (CurrentWaveNumber == 1 && turnNumberThisWave == 0)
{
StartCoroutine(DrawCardsToHandSequenced(Mathf.Max(0, effect.Amount), package.Definition.DisplayName));
}
break;
case PackageEffectKind.WaveGenerateBasicCard:
GeneratePackageLanguageCard(package.Definition.DisplayName);
break;
}
}
}
private void ApplyPackageTurnStartEffects()
{
if (HasPackageEffect(PackageEffectKind.EveryNthTurnShield, out PackageEffectData effect)
&& effect.Threshold > 0
&& turnNumberThisWave % effect.Threshold == 0)
{
GrantPlayerShield(Mathf.Max(0, effect.Amount), "cron");
}
if (HasPackageEffect(PackageEffectKind.EveryNthTurnDraw, out effect)
&& effect.Threshold > 0
&& turnNumberThisWave % effect.Threshold == 0)
{
StartCoroutine(DrawCardsToHandSequenced(Mathf.Max(0, effect.Amount), "udev"));
}
if (turnNumberThisWave == 1
&& HasPackageEffect(PackageEffectKind.FirstTurnEachWaveCycle, out effect)
&& effect.Amount > 0)
{
playerState.Cycles += effect.Amount;
Log($"apt-fast: gained {effect.Amount} Cycle");
}
if (HasPackageEffect(PackageEffectKind.StartTurnNoDebuffShield, out effect)
&& !HasHarmfulStatus(playerState))
{
GrantPlayerShield(Mathf.Max(0, effect.Amount), "auditd");
}
}
private void ApplyPackageCardPlayedEffects(CardInstance card)
{
if (card == null || playerState == null)
{
return;
}
if (HasPackageEffect(PackageEffectKind.ThirdCardEachTurnGenerate, out PackageEffectData aptEffect)
&& cardsPlayedThisTurn == Mathf.Max(1, aptEffect.Threshold))
{
GeneratePackageLanguageCard("apt");
if (aptEffect.RefundCycle)
{
playerState.Cycles += 1;
Log("apt refunded 1 Cycle");
}
}
if (HasPackageEffect(PackageEffectKind.DnfFedoraPassive, out PackageEffectData dnfEffect)
&& cardsPlayedThisTurn == 1
&& dnfEffect.Amount > 0)
{
playerState.FlatEffectBonus += dnfEffect.Amount;
Log($"DNF wave buff +{dnfEffect.Amount} damage");
}
if (!packageInterpreterQueueShieldTriggeredThisWave
&& card.Definition.ResolutionTrack == ResolutionTrack.InterpreterQueue
&& HasPackageEffect(PackageEffectKind.FirstInterpreterQueueCardEachWaveShield, out PackageEffectData queueEffect))
{
packageInterpreterQueueShieldTriggeredThisWave = true;
GrantPlayerShield(Mathf.Max(0, queueEffect.Amount), "dbus");
}
if (!packageNativeDamageTriggeredThisWave
&& card.Definition.ResolutionTrack == ResolutionTrack.Native
&& HasPackageEffect(PackageEffectKind.FirstNativeCardEachWaveFlatDamage, out PackageEffectData nativeEffect)
&& nativeEffect.Amount > 0)
{
packageNativeDamageTriggeredThisWave = true;
playerState.FlatEffectBonus += nativeEffect.Amount;
Log($"make: first Native card +{nativeEffect.Amount} damage");
}
if (HasPackageEffect(PackageEffectKind.EveryNthCardEachWaveCycle, out PackageEffectData cycleEffect)
&& cycleEffect.Threshold > 0
&& cardsPlayedThisWave % cycleEffect.Threshold == 0
&& cycleEffect.Amount > 0)
{
playerState.Cycles += cycleEffect.Amount;
Log($"systemd-timer: gained {cycleEffect.Amount} Cycle");
}
}
public void GrantPlayerShield(int amount, string label)
{
if (playerState == null || amount <= 0)
{
return;
}
int total = amount;
if (!packageShieldBonusTriggeredThisTurn
&& HasPackageEffect(PackageEffectKind.FirstShieldEachTurnBonus, out PackageEffectData effect))
{
int bonus = Mathf.Max(0, effect.Amount);
if (bonus > 0)
{
total += bonus;
packageShieldBonusTriggeredThisTurn = true;
Log($"SELinux shield +{bonus}");
}
}
playerState.Shield += total;
if (!string.IsNullOrWhiteSpace(label))
{
Log($"{label}: gained {total} Shield");
}
}
private void GeneratePackageLanguageCard(string label)
{
if (handController == null || handController.RemainingRam <= 0)
{
return;
}
Language language = RandomRoll.RollRange(0, 1, new RollContext(playerState)) == 0
? runConfig.PrimaryLanguage
: runConfig.SecondaryLanguage;
if (!TryCreateGeneratedCard(language, Rarity.Common, out CardInstance generated))
{
Log($"{label}: TODO card-generation pool unavailable");
return;
}
if (handController.Add(generated))
{
Log($"{label}: generated {GetCardName(generated)} at 0c");
StateChanged?.Invoke();
}
}
private void HandlePackageDamageDealt(DamageDealtEvent payload)
{
if (payload.Target != playerState
|| payload.UptimeDamage <= 0
|| packageTimeshiftTriggeredThisWave
|| !HasPackageEffect(PackageEffectKind.WaveThresholdRestore, out PackageEffectData effect))
{
return;
}
int thresholdPercent = Mathf.Clamp(effect.Threshold, 1, 100);
int restoreTarget = Mathf.CeilToInt(playerState.MaxUptime * (thresholdPercent / 100f));
if (playerState.CurrentUptime >= restoreTarget)
{
return;
}
packageTimeshiftTriggeredThisWave = true;
playerState.CurrentUptime = restoreTarget;
if (effect.CleanseDebuffs)
{
statusEffects.CleanseHarmful(playerState);
}
Log($"Timeshift restored uptime to {thresholdPercent}%");
StateChanged?.Invoke();
}
private bool HasOnDistroPackageEffect(PackageEffectKind kind, out PackageEffectData effect)
{
effect = default;
IReadOnlyList<PackageInstance> packages = runConfig?.EquippedPackages;
if (packages == null)
{
return false;
}
for (int i = 0; i < packages.Count; i++)
{
PackageInstance package = packages[i];
if (package?.Definition == null || !package.Definition.IsIntendedFor(runConfig?.Distro?.Id))
{
continue;
}
effect = package.EffectFor(runConfig?.Distro?.Id);
if (effect.Kind == kind)
{
return true;
}
}
return false;
}
private bool HasPackageEffect(PackageEffectKind kind, out PackageEffectData effect)
{
effect = default;
IReadOnlyList<PackageInstance> packages = runConfig?.EquippedPackages;
if (packages == null)
{
return false;
}
for (int i = 0; i < packages.Count; i++)
{
PackageInstance package = packages[i];
if (package?.Definition == null)
{
continue;
}
effect = package.EffectFor(runConfig?.Distro?.Id);
if (effect.Kind == kind)
{
return true;
}
}
return false;
}
private static bool HasHarmfulStatus(CombatantState state)
{
if (state == null)
{
return false;
}
for (int i = 0; i < state.Statuses.Count; i++)
{
if (!StatusEffectController.GetDescriptor(state.Statuses[i].Type).IsBeneficial)
{
return true;
}
}
return false;
}
private static bool IsCrashImmune(CardInstance card)
{
string id = card?.Definition?.Id;
+1
View File
@@ -30,6 +30,7 @@ namespace KernelPanic.Combat
public bool IgnoreDamageMultipliers { get; set; }
public bool AllowFlatDamageBuffs { get; set; }
public int FlatEffectBonus { get; set; }
public int JavaScriptFlatDamageBonus { get; set; }
public int DamageMultiplierPercent { get; set; } = 100;
public int IncomingAttackHalfCharges { get; set; }
public IReadOnlyList<StatusInstance> Statuses => _statuses;
+11
View File
@@ -64,6 +64,7 @@ namespace KernelPanic.Combat
}
int amount = Mathf.Max(0, request.Amount);
amount = ApplyFlatAdditions(amount, request);
amount = ApplyMultipliers(amount, request);
amount = ApplyCrit(amount, request, out bool wasCritical);
amount = ApplyResistancesAndWeaknesses(amount, request);
@@ -95,6 +96,16 @@ namespace KernelPanic.Combat
return Mathf.RoundToInt(amount * (multiplier / 100f));
}
private static int ApplyFlatAdditions(int amount, DamageRequest request)
{
if (request.Source != null && request.Language == Language.JavaScript)
{
amount += Mathf.Max(0, request.Source.JavaScriptFlatDamageBonus);
}
return amount;
}
private static int ApplyCrit(int amount, DamageRequest request, out bool wasCritical)
{
wasCritical = false;
+69
View File
@@ -0,0 +1,69 @@
using System.Collections.Generic;
using UnityEngine;
namespace KernelPanic.Data
{
public sealed class PackageDatabase : ScriptableObject
{
[SerializeField] private List<PackageDefinition> allPackages = new();
public IReadOnlyList<PackageDefinition> AllPackages => allPackages;
public PackageDefinition FindById(string id)
{
if (string.IsNullOrWhiteSpace(id))
{
return null;
}
for (int i = 0; i < allPackages.Count; i++)
{
PackageDefinition package = allPackages[i];
if (package != null && string.Equals(package.Id, id, System.StringComparison.OrdinalIgnoreCase))
{
return package;
}
}
return null;
}
public PackageDefinition FindByRarity(int rarity, int index)
{
int safeRarity = Mathf.Clamp(rarity, 1, 5);
int matchIndex = 0;
for (int i = 0; i < allPackages.Count; i++)
{
PackageDefinition package = allPackages[i];
if (package == null || package.Rarity != safeRarity)
{
continue;
}
if (matchIndex == index)
{
return package;
}
matchIndex++;
}
return null;
}
public int CountByRarity(int rarity)
{
int safeRarity = Mathf.Clamp(rarity, 1, 5);
int count = 0;
for (int i = 0; i < allPackages.Count; i++)
{
if (allPackages[i] != null && allPackages[i].Rarity == safeRarity)
{
count++;
}
}
return count;
}
}
}
+166
View File
@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace KernelPanic.Data
{
public enum PackageSlot
{
Kernel,
Runtime,
Daemon
}
public enum PackageEffectKind
{
None = 0,
MaxUptime = 1,
MaxCycles = 2,
WaveDraw = 3,
WaveGenerateBasicCard = 4,
FirstCardEachWaveCostReduction = 5,
EveryNthTurnShield = 6,
ExhaustShield = 7,
EveryNthCardEachWaveFree = 8,
FirstCardsEachTurnCostReduction = 9,
ThirdCardEachTurnGenerate = 10,
JavaScriptFlatDamage = 11,
WaveThresholdRestore = 12,
FirstShieldEachTurnBonus = 13,
DnfFedoraPassive = 14,
WaveStartShield = 15,
FirstTurnEachWaveShield = 16,
FirstTurnFirstWaveDraw = 17,
FirstTurnEachWaveCycle = 18,
MaxRam = 19,
FirstNativeCardEachWaveFlatDamage = 20,
FirstInterpreterQueueCardEachWaveShield = 21,
EveryNthTurnDraw = 22,
StartTurnNoDebuffShield = 23,
EveryNthCardEachWaveCycle = 24
}
[Serializable]
public struct PackageEffectData
{
[SerializeField] private PackageEffectKind kind;
[SerializeField] private int amount;
[SerializeField] private int threshold;
[SerializeField] private bool refundCycle;
[SerializeField] private bool cleanseDebuffs;
[SerializeField] private bool enableFedoraSecondCardPassive;
public PackageEffectData(PackageEffectKind kind, int amount, int threshold, bool refundCycle, bool cleanseDebuffs, bool enableFedoraSecondCardPassive)
{
this.kind = kind;
this.amount = amount;
this.threshold = threshold;
this.refundCycle = refundCycle;
this.cleanseDebuffs = cleanseDebuffs;
this.enableFedoraSecondCardPassive = enableFedoraSecondCardPassive;
}
public PackageEffectKind Kind => kind;
public int Amount => amount;
public int Threshold => threshold;
public bool RefundCycle => refundCycle;
public bool CleanseDebuffs => cleanseDebuffs;
public bool EnableFedoraSecondCardPassive => enableFedoraSecondCardPassive;
}
/// <summary>
/// Defines persistent meta packages equipped into distro Kernel/Runtime/Daemon slots.
/// Package loadouts are meta progression and carry into every run for that distro.
/// Card loadouts remain run setup only.
/// </summary>
public sealed class PackageDefinition : ScriptableObject
{
[SerializeField] private string id;
[SerializeField] private string displayName;
[SerializeField] private PackageSlot slot;
[SerializeField, Range(1, 5)] private int rarity = 1;
[SerializeField, TextArea] private string description;
[SerializeField] private string flavourText;
[SerializeField, TextArea] private string designNotes;
[SerializeField] private string intendedDistroId;
[SerializeField] private List<string> requiresSystem = new();
[SerializeField] private PackageEffectData offDistroEffect;
[SerializeField] private PackageEffectData onDistroEffect;
public string Id => id;
public string DisplayName => displayName;
public PackageSlot Slot => slot;
public int Rarity => Mathf.Clamp(rarity, 1, 5);
public string Description => description;
public string FlavourText => flavourText;
public string FlavorText => flavourText;
public string DesignNotes => designNotes;
public string IntendedDistroId => intendedDistroId;
public IReadOnlyList<string> RequiresSystem => requiresSystem;
public PackageEffectData OffDistroEffect => offDistroEffect;
public PackageEffectData OnDistroEffect => onDistroEffect;
public bool IsIntendedFor(string distroId)
{
return !string.IsNullOrWhiteSpace(intendedDistroId)
&& string.Equals(intendedDistroId, distroId, StringComparison.OrdinalIgnoreCase);
}
public PackageEffectData EffectFor(string distroId)
{
return IsIntendedFor(distroId) ? onDistroEffect : offDistroEffect;
}
}
[Serializable]
public sealed class PackageInstance
{
public PackageInstance(PackageDefinition definition, int upgradeLevel)
{
Definition = definition;
UpgradeLevel = Mathf.Clamp(upgradeLevel, 0, KernelPanic.Meta.PackageTuning.MaxPackageLevel);
}
public PackageDefinition Definition { get; }
public int UpgradeLevel { get; }
public PackageEffectData EffectFor(string distroId)
{
PackageEffectData effect = Definition == null ? default : Definition.EffectFor(distroId);
return PackageEffectScaling.Scale(effect, UpgradeLevel);
}
}
public static class PackageEffectScaling
{
public static PackageEffectData Scale(PackageEffectData effect, int upgradeLevel)
{
int level = Mathf.Clamp(upgradeLevel, 0, KernelPanic.Meta.PackageTuning.MaxPackageLevel);
if (level <= 0 || effect.Kind == PackageEffectKind.None)
{
return effect;
}
int amount = effect.Amount + GetAmountPerLevel(effect.Kind, effect.Amount) * level;
return new PackageEffectData(effect.Kind, amount, effect.Threshold, effect.RefundCycle, effect.CleanseDebuffs, effect.EnableFedoraSecondCardPassive);
}
private static int GetAmountPerLevel(PackageEffectKind kind, int baseAmount)
{
return kind switch
{
PackageEffectKind.MaxUptime => Mathf.Max(1, Mathf.CeilToInt(Mathf.Max(1, baseAmount) * 0.5f)),
PackageEffectKind.MaxCycles => 1,
PackageEffectKind.MaxRam => 1,
PackageEffectKind.WaveDraw => 1,
PackageEffectKind.FirstTurnFirstWaveDraw => 1,
PackageEffectKind.FirstTurnEachWaveCycle => 1,
PackageEffectKind.FirstCardsEachTurnCostReduction => 1,
PackageEffectKind.FirstCardEachWaveCostReduction => 1,
PackageEffectKind.FirstNativeCardEachWaveFlatDamage => 1,
PackageEffectKind.JavaScriptFlatDamage => 1,
_ => 1
};
}
}
}
+7 -2
View File
@@ -5,7 +5,12 @@ namespace KernelPanic.Meta
/// </summary>
public enum GachaCurrencyType
{
StandardPull,
LimitedPull
StandardPull
}
public enum PullPaymentSource
{
Commits,
Entropy
}
}
+7 -5
View File
@@ -16,9 +16,9 @@ namespace KernelPanic.Meta
public static bool HasPendingPull => pendingPull != null;
public static bool HasCompletedPull => completedPull != null;
public static void SetPending(string bannerId, int pullCount, int entropyTokenCount, DistroDatabase distroDatabase, string focusUnitId)
public static void SetPending(string bannerId, int pullCount, int entropyTokenCount, DistroDatabase distroDatabase, PackageDatabase packageDatabase, string focusUnitId)
{
pendingPull = new PendingGachaPull(bannerId, pullCount, entropyTokenCount, distroDatabase, focusUnitId);
pendingPull = new PendingGachaPull(bannerId, pullCount, entropyTokenCount, distroDatabase, packageDatabase, focusUnitId);
completedPull = null;
}
@@ -44,12 +44,13 @@ namespace KernelPanic.Meta
public sealed class PendingGachaPull
{
public PendingGachaPull(string bannerId, int pullCount, int entropyTokenCount, DistroDatabase distroDatabase, string focusUnitId)
public PendingGachaPull(string bannerId, int pullCount, int entropyTokenCount, DistroDatabase distroDatabase, PackageDatabase packageDatabase, string focusUnitId)
{
BannerId = string.IsNullOrWhiteSpace(bannerId) ? GachaService.BeginnerBannerId : bannerId;
PullCount = Math.Max(1, pullCount);
EntropyTokenCount = Math.Max(0, entropyTokenCount);
DistroDatabase = distroDatabase;
PackageDatabase = packageDatabase;
FocusUnitId = focusUnitId;
}
@@ -57,6 +58,7 @@ namespace KernelPanic.Meta
public int PullCount { get; }
public int EntropyTokenCount { get; }
public DistroDatabase DistroDatabase { get; }
public PackageDatabase PackageDatabase { get; }
public string FocusUnitId { get; }
}
@@ -131,7 +133,7 @@ namespace KernelPanic.Meta
public bool PityTriggered { get; }
public bool IsCharacter => RewardType == GachaRewardType.Distro || RewardType == GachaRewardType.FutureStandardFiveStarDistro;
public string TypeText => IsCharacter ? "character" : "equipment";
public string TypeText => IsCharacter ? "character" : "package";
public string StatusText
{
@@ -173,7 +175,7 @@ namespace KernelPanic.Meta
return "unknown result";
}
if (rewardType != GachaRewardType.Equipment)
if (rewardType != GachaRewardType.Package)
{
return displayName;
}
+1 -1
View File
@@ -5,7 +5,7 @@ namespace KernelPanic.Meta
/// </summary>
public enum GachaRewardType
{
Equipment,
Package,
Distro,
FutureStandardFiveStarDistro
}
+72 -87
View File
@@ -14,11 +14,12 @@ namespace KernelPanic.Meta
public const string LimitedBannerId = "limited";
public const int BeginnerMaxPulls = 50;
public const int BeginnerSinglePullCost = 1;
public const int BeginnerTenPullCost = 8;
public const int BeginnerTenPullCost = 10;
public const int FourStarHardPity = 10;
public const int FiveStarSoftPityStart = 66;
public const int FiveStarHardPity = 80;
public const int EntropyPerPullToken = 100;
public const int EntropyPerCommit = 100;
public const int RootCreditsToEntropy = 1;
public const int TestRootCreditsBalance = 10000;
private const double FourStarBaseChance = 0.12d;
@@ -41,11 +42,10 @@ namespace KernelPanic.Meta
{
this._random = random ?? new Random();
_currencyBalances[GachaCurrencyType.StandardPull] = 0;
_currencyBalances[GachaCurrencyType.LimitedPull] = 0;
}
public int PullTokens => GetCurrencyBalance(GachaCurrencyType.StandardPull);
public int LimitedPullTokens => GetCurrencyBalance(GachaCurrencyType.LimitedPull);
public int Commits => PullTokens;
public int RootCredits { get; private set; }
public IReadOnlyList<DistroDefinition> BannerPool => _bannerPool;
public GachaBannerState BeginnerState => _beginnerState;
@@ -62,9 +62,8 @@ namespace KernelPanic.Meta
}
data.EnsureLists();
RootCredits = Math.Max(TestRootCreditsBalance, data.rootCredits);
SetCurrencyBalanceSilently(GachaCurrencyType.StandardPull, data.standardPullCurrency);
SetCurrencyBalanceSilently(GachaCurrencyType.LimitedPull, data.limitedPullCurrency);
RootCredits = Math.Max(0, data.rootCredits);
SetCurrencyBalanceSilently(GachaCurrencyType.StandardPull, data.commitsBalance);
_beginnerState = data.beginnerBannerState ?? new GachaBannerState(BeginnerBannerId);
_beginnerState.bannerId = BeginnerBannerId;
_beginnerState.EnsureLists();
@@ -87,8 +86,7 @@ namespace KernelPanic.Meta
data.EnsureLists();
data.rootCredits = RootCredits;
data.standardPullCurrency = GetCurrencyBalance(GachaCurrencyType.StandardPull);
data.limitedPullCurrency = GetCurrencyBalance(GachaCurrencyType.LimitedPull);
data.commitsBalance = GetCurrencyBalance(GachaCurrencyType.StandardPull);
data.beginnerBannerState = CloneState(_beginnerState);
}
@@ -125,6 +123,11 @@ namespace KernelPanic.Meta
}
public bool ConvertRootCreditsToEntropy(EntropyWallet wallet, int amount, out string failureReason)
{
return BuyEntropy(wallet, amount, out failureReason);
}
public bool BuyEntropy(EntropyWallet wallet, int rootCredits, out string failureReason)
{
if (wallet == null)
{
@@ -132,20 +135,20 @@ namespace KernelPanic.Meta
return false;
}
if (amount <= 0)
if (rootCredits <= 0)
{
failureReason = "no root-credits selected";
failureReason = "no Root Credits selected";
return false;
}
if (RootCredits < amount)
if (RootCredits < rootCredits)
{
failureReason = $"need {amount} root-credits";
failureReason = $"need {rootCredits} Root Credits";
return false;
}
RootCredits -= amount;
wallet.Add(amount);
RootCredits -= rootCredits;
wallet.Add(rootCredits * RootCreditsToEntropy);
failureReason = null;
Changed?.Invoke();
return true;
@@ -187,6 +190,11 @@ namespace KernelPanic.Meta
}
public bool CanPullBeginner(int pullCount, out string failureReason)
{
return CanAffordPull(pullCount, null, PullPaymentSource.Commits, out failureReason);
}
public bool CanAffordPull(int pullCount, EntropyWallet wallet, PullPaymentSource source, out string failureReason)
{
if (pullCount != 1 && pullCount != 10)
{
@@ -208,49 +216,73 @@ namespace KernelPanic.Meta
}
int cost = GetBeginnerPullCost(pullCount);
if (GetCurrencyBalance(GachaCurrencyType.StandardPull) < cost)
if (source == PullPaymentSource.Commits && GetCurrencyBalance(GachaCurrencyType.StandardPull) < cost)
{
failureReason = $"need {cost} {FormatCurrencyName(GachaCurrencyType.StandardPull)}";
return false;
}
int entropyCost = cost * EntropyPerCommit;
if (source == PullPaymentSource.Entropy && (wallet == null || wallet.Balance < entropyCost))
{
failureReason = $"need {entropyCost} Entropy";
return false;
}
failureReason = null;
return true;
}
public bool PayForPull(int pullCount, EntropyWallet wallet, PullPaymentSource source, out int commitsSpent, out int entropySpent, out string failureReason)
{
commitsSpent = 0;
entropySpent = 0;
if (!CanAffordPull(pullCount, wallet, source, out failureReason))
{
return false;
}
int cost = GetBeginnerPullCost(pullCount);
if (source == PullPaymentSource.Commits)
{
commitsSpent = cost;
SetCurrencyBalanceSilently(GachaCurrencyType.StandardPull, GetCurrencyBalance(GachaCurrencyType.StandardPull) - commitsSpent);
}
else
{
entropySpent = cost * EntropyPerCommit;
wallet.Spend(entropySpent);
}
return true;
}
public int GetMissingPullTokens(GachaCurrencyType currencyType, int pullCost)
{
return Math.Max(0, pullCost - GetCurrencyBalance(currencyType));
}
public bool CanCoverMissingPullTokensWithEntropy(EntropyWallet wallet, int missingPullTokens)
{
return missingPullTokens <= 0 || (wallet != null && wallet.Balance >= missingPullTokens * EntropyPerPullToken);
}
public GachaPullResult PerformBeginnerPull(int pullCount)
{
return PerformBeginnerPull(pullCount, null, 0);
return PerformBeginnerPull(pullCount, null, PullPaymentSource.Commits);
}
public GachaPullResult PerformBeginnerPull(int pullCount, EntropyWallet wallet, int entropyTokenCount)
{
if (!CanPullBeginnerWithEntropy(pullCount, wallet, entropyTokenCount, out string failureReason))
PullPaymentSource source = entropyTokenCount > 0 ? PullPaymentSource.Entropy : PullPaymentSource.Commits;
return PerformBeginnerPull(pullCount, wallet, source);
}
public GachaPullResult PerformBeginnerPull(int pullCount, EntropyWallet wallet, PullPaymentSource source)
{
if (!CanAffordPull(pullCount, wallet, source, out string failureReason))
{
return GachaPullResult.Failed(BeginnerBannerId, failureReason);
}
int cost = GetBeginnerPullCost(pullCount);
int tokenSpend = cost - entropyTokenCount;
if (tokenSpend > 0)
if (!PayForPull(pullCount, wallet, source, out int commitSpend, out int entropySpent, out failureReason))
{
SetCurrencyBalanceSilently(GachaCurrencyType.StandardPull, GetCurrencyBalance(GachaCurrencyType.StandardPull) - tokenSpend);
}
int entropySpent = entropyTokenCount * EntropyPerPullToken;
if (entropySpent > 0)
{
wallet.Spend(entropySpent);
return GachaPullResult.Failed(BeginnerBannerId, failureReason);
}
List<GachaReward> rewards = new();
@@ -266,14 +298,13 @@ namespace KernelPanic.Meta
}
Changed?.Invoke();
return new GachaPullResult(true, BeginnerBannerId, GachaCurrencyType.StandardPull, tokenSpend, entropySpent, rewards, null);
return new GachaPullResult(true, BeginnerBannerId, GachaCurrencyType.StandardPull, commitSpend, entropySpent, rewards, null);
}
public static string FormatCurrencyName(GachaCurrencyType currencyType)
{
return currencyType switch
{
GachaCurrencyType.LimitedPull => "Root Credits",
_ => "Commits"
};
}
@@ -335,7 +366,7 @@ namespace KernelPanic.Meta
if (!hitFourStarOrCharacter)
{
_beginnerState.pityCounter++;
return GachaReward.Equipment(3, "3-star equipment", false, false);
return GachaReward.Package(3, "3-star package", false, false);
}
_beginnerState.pityCounter = 0;
@@ -345,7 +376,7 @@ namespace KernelPanic.Meta
return GachaReward.DistroReward(distro, 4, pityTriggered, false);
}
return GachaReward.Equipment(4, "4-star equipment", pityTriggered, false);
return GachaReward.Package(4, "4-star package", pityTriggered, false);
}
private bool TryRollFiveStar(GachaBannerState state, out bool pityTriggered)
@@ -379,7 +410,7 @@ namespace KernelPanic.Meta
{
if (_bannerPool.Count == 0 || _random.NextDouble() >= DistroChanceOnFeaturedTier)
{
return GachaReward.Equipment(5, "5-star equipment", pityTriggered, false);
return GachaReward.Package(5, "5-star package", pityTriggered, false);
}
DistroDefinition distro = _bannerPool.Count == 0 ? null : _bannerPool[_random.Next(_bannerPool.Count)];
@@ -420,7 +451,7 @@ namespace KernelPanic.Meta
distro ??= _bannerPool.Count == 0 ? null : _bannerPool[Math.Min(guaranteeIndex, _bannerPool.Count - 1)];
return distro == null
? GachaReward.Equipment(4, "4-star equipment", true, true)
? GachaReward.Package(4, "4-star package", true, true)
: GachaReward.DistroReward(distro, 4, true, true, reason);
}
@@ -448,52 +479,6 @@ namespace KernelPanic.Meta
_currencyBalances[currencyType] = Math.Max(0, amount);
}
private bool CanPullBeginnerWithEntropy(int pullCount, EntropyWallet wallet, int entropyTokenCount, out string failureReason)
{
if (pullCount != 1 && pullCount != 10)
{
failureReason = "only single and ten pulls are supported";
return false;
}
if (!IsBeginnerBannerAvailable)
{
failureReason = "beginner banner unavailable";
return false;
}
int remainingPulls = BeginnerMaxPulls - _beginnerState.totalPulls;
if (pullCount > remainingPulls)
{
failureReason = $"only {remainingPulls} beginner pull(s) remain";
return false;
}
int cost = GetBeginnerPullCost(pullCount);
if (entropyTokenCount < 0 || entropyTokenCount > cost)
{
failureReason = "invalid entropy substitute amount";
return false;
}
int tokenSpend = cost - entropyTokenCount;
if (GetCurrencyBalance(GachaCurrencyType.StandardPull) < tokenSpend)
{
failureReason = $"need {tokenSpend} {FormatCurrencyName(GachaCurrencyType.StandardPull)}";
return false;
}
int entropySpend = entropyTokenCount * EntropyPerPullToken;
if (entropySpend > 0 && (wallet == null || wallet.Balance < entropySpend))
{
failureReason = $"need {entropySpend} entropy";
return false;
}
failureReason = null;
return true;
}
private static GachaBannerState CloneState(GachaBannerState state)
{
state ??= new GachaBannerState(BeginnerBannerId);
@@ -542,9 +527,9 @@ namespace KernelPanic.Meta
public bool Guaranteed { get; }
public string GuaranteeReason { get; }
public static GachaReward Equipment(int starRating, string displayName, bool pityTriggered, bool guaranteed)
public static GachaReward Package(int starRating, string displayName, bool pityTriggered, bool guaranteed)
{
return new GachaReward(GachaRewardType.Equipment, starRating, displayName, null, pityTriggered, guaranteed, null);
return new GachaReward(GachaRewardType.Package, starRating, displayName, null, pityTriggered, guaranteed, null);
}
public static GachaReward DistroReward(DistroDefinition distro, int starRating, bool pityTriggered, bool guaranteed, string guaranteeReason = null)
+190
View File
@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using KernelPanic.Data;
namespace KernelPanic.Meta
{
public enum PackageLoadoutFailureReason
{
None,
WrongSlot,
NotOwned,
SlotOccupied,
NotEquipped
}
/// <summary>
/// Pure meta model for persistent package slots. Unlike CardLoadout, this is not
/// run-only: equipped package ids are saved per distro and reused in every run.
/// </summary>
public sealed class PackageLoadout
{
private readonly PlayerCollection _collection;
private readonly PackageDatabase _database;
private readonly Dictionary<string, Dictionary<PackageSlot, string>> _equippedByDistro = new(StringComparer.OrdinalIgnoreCase);
public PackageLoadout(PlayerCollection collection, PackageDatabase database)
{
_collection = collection;
_database = database;
}
public IReadOnlyDictionary<PackageSlot, string> GetEquippedPackageIds(string distroId)
{
return !string.IsNullOrWhiteSpace(distroId) && _equippedByDistro.TryGetValue(distroId, out Dictionary<PackageSlot, string> equipped)
? equipped
: Empty;
}
public PackageDefinition GetEquippedPackage(string distroId, PackageSlot slot)
{
if (string.IsNullOrWhiteSpace(distroId)
|| !_equippedByDistro.TryGetValue(distroId, out Dictionary<PackageSlot, string> equipped)
|| !equipped.TryGetValue(slot, out string packageId))
{
return null;
}
return _database == null ? null : _database.FindById(packageId);
}
public IReadOnlyList<PackageDefinition> GetEquippedPackages(string distroId)
{
List<PackageDefinition> packages = new();
AddIfEquipped(distroId, PackageSlot.Kernel, packages);
AddIfEquipped(distroId, PackageSlot.Runtime, packages);
AddIfEquipped(distroId, PackageSlot.Daemon, packages);
return packages;
}
public IReadOnlyList<PackageInstance> GetEquippedPackageInstances(string distroId)
{
List<PackageInstance> packages = new();
AddInstanceIfEquipped(distroId, PackageSlot.Kernel, packages);
AddInstanceIfEquipped(distroId, PackageSlot.Runtime, packages);
AddInstanceIfEquipped(distroId, PackageSlot.Daemon, packages);
return packages;
}
public bool IsEquipped(string packageId)
{
if (string.IsNullOrWhiteSpace(packageId))
{
return false;
}
foreach (KeyValuePair<string, Dictionary<PackageSlot, string>> distro in _equippedByDistro)
{
foreach (KeyValuePair<PackageSlot, string> slot in distro.Value)
{
if (string.Equals(slot.Value, packageId, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
public bool TryEquip(string distroId, PackageSlot slot, string packageId, out PackageLoadoutFailureReason reason)
{
PackageDefinition package = _database == null ? null : _database.FindById(packageId);
if (package == null || _collection == null || !_collection.IsPackageOwned(package.Id))
{
reason = PackageLoadoutFailureReason.NotOwned;
return false;
}
if (package.Slot != slot)
{
reason = PackageLoadoutFailureReason.WrongSlot;
return false;
}
Dictionary<PackageSlot, string> equipped = EnsureDistro(distroId);
if (equipped.TryGetValue(slot, out string current) && !string.Equals(current, package.Id, StringComparison.OrdinalIgnoreCase))
{
reason = PackageLoadoutFailureReason.SlotOccupied;
return false;
}
equipped[slot] = package.Id;
reason = PackageLoadoutFailureReason.None;
return true;
}
public bool TryUnequip(string distroId, PackageSlot slot, out PackageLoadoutFailureReason reason)
{
if (string.IsNullOrWhiteSpace(distroId)
|| !_equippedByDistro.TryGetValue(distroId, out Dictionary<PackageSlot, string> equipped)
|| !equipped.Remove(slot))
{
reason = PackageLoadoutFailureReason.NotEquipped;
return false;
}
reason = PackageLoadoutFailureReason.None;
return true;
}
public void Load(string distroId, IEnumerable<PackageLoadoutSlotSaveEntry> slots)
{
if (string.IsNullOrWhiteSpace(distroId))
{
return;
}
Dictionary<PackageSlot, string> equipped = EnsureDistro(distroId);
equipped.Clear();
foreach (PackageLoadoutSlotSaveEntry slotEntry in slots ?? Array.Empty<PackageLoadoutSlotSaveEntry>())
{
PackageDefinition package = _database == null ? null : _database.FindById(slotEntry?.packageId);
if (package == null || !_collection.IsPackageOwned(package.Id) || package.Slot != slotEntry.slot)
{
continue;
}
equipped[package.Slot] = package.Id;
}
}
public void Clear()
{
_equippedByDistro.Clear();
}
private void AddIfEquipped(string distroId, PackageSlot slot, List<PackageDefinition> packages)
{
PackageDefinition package = GetEquippedPackage(distroId, slot);
if (package != null)
{
packages.Add(package);
}
}
private void AddInstanceIfEquipped(string distroId, PackageSlot slot, List<PackageInstance> packages)
{
PackageDefinition definition = GetEquippedPackage(distroId, slot);
OwnedPackageInstance owned = definition == null ? null : _collection.GetOwnedPackage(definition.Id);
if (owned != null)
{
packages.Add(owned.ToRuntimeInstance());
}
}
private Dictionary<PackageSlot, string> EnsureDistro(string distroId)
{
string key = string.IsNullOrWhiteSpace(distroId) ? string.Empty : distroId;
if (!_equippedByDistro.TryGetValue(key, out Dictionary<PackageSlot, string> equipped))
{
equipped = new Dictionary<PackageSlot, string>();
_equippedByDistro[key] = equipped;
}
return equipped;
}
private static readonly IReadOnlyDictionary<PackageSlot, string> Empty = new Dictionary<PackageSlot, string>();
}
}
+64
View File
@@ -0,0 +1,64 @@
using System;
using KernelPanic.Data;
namespace KernelPanic.Meta
{
public enum PackageScrapFailureReason
{
None,
NotOwned,
Equipped
}
public readonly struct PackageScrapResult
{
public PackageScrapResult(bool success, PackageScrapFailureReason failureReason, int cacheGranted, int bandwidthRefunded)
{
Success = success;
FailureReason = failureReason;
CacheGranted = cacheGranted;
BandwidthRefunded = bandwidthRefunded;
}
public bool Success { get; }
public PackageScrapFailureReason FailureReason { get; }
public int CacheGranted { get; }
public int BandwidthRefunded { get; }
}
public static class PackageScrapper
{
public static PackageScrapResult Scrap(PlayerCollection collection, PackageLoadout loadout, SaveData wallet, string packageId)
{
if (collection == null || wallet == null || string.IsNullOrWhiteSpace(packageId))
{
return new PackageScrapResult(false, PackageScrapFailureReason.NotOwned, 0, 0);
}
OwnedPackageInstance package = collection.GetOwnedPackage(packageId);
if (package == null)
{
return new PackageScrapResult(false, PackageScrapFailureReason.NotOwned, 0, 0);
}
if (loadout != null && loadout.IsEquipped(packageId))
{
return new PackageScrapResult(false, PackageScrapFailureReason.Equipped, 0, 0);
}
PackageDefinition definition = package.Definition;
int rarity = definition == null ? 1 : definition.Rarity;
int cache = PackageTuning.GetCacheForRarity(rarity) + PackageTuning.GetRefundedInvestedCache(package.UpgradeLevel, rarity);
int bandwidth = PackageTuning.GetRefundedInvestedBandwidth(package.UpgradeLevel, rarity);
if (!collection.RemovePackage(packageId))
{
return new PackageScrapResult(false, PackageScrapFailureReason.NotOwned, 0, 0);
}
wallet.cacheBalance += Math.Max(0, cache);
wallet.bandwidthBalance += Math.Max(0, bandwidth);
return new PackageScrapResult(true, PackageScrapFailureReason.None, cache, bandwidth);
}
}
}
+69
View File
@@ -0,0 +1,69 @@
using System;
using UnityEngine;
namespace KernelPanic.Meta
{
public static class PackageTuning
{
public const int MaxPackageLevel = 5;
public const float RefundFraction = 1f;
public const float RarityCostMultiplier = 1f;
private static readonly int[] CachePerRarityValues = { 5, 12, 30, 80, 200 };
private static readonly int[] UpgradeCacheCostValues = { 10, 25, 50, 90, 150 };
private static readonly int[] UpgradeBandwidthCostValues = { 20, 50, 100, 180, 300 };
public static int GetCacheForRarity(int rarity)
{
return CachePerRarityValues[Mathf.Clamp(rarity, 1, CachePerRarityValues.Length) - 1];
}
public static int GetUpgradeCacheCost(int nextLevel, int rarity)
{
return ApplyRarityMultiplier(UpgradeCacheCostValues[Mathf.Clamp(nextLevel, 1, UpgradeCacheCostValues.Length) - 1], rarity);
}
public static int GetUpgradeBandwidthCost(int nextLevel, int rarity)
{
return ApplyRarityMultiplier(UpgradeBandwidthCostValues[Mathf.Clamp(nextLevel, 1, UpgradeBandwidthCostValues.Length) - 1], rarity);
}
public static int GetInvestedCache(int level, int rarity)
{
int total = 0;
for (int nextLevel = 1; nextLevel <= Mathf.Clamp(level, 0, MaxPackageLevel); nextLevel++)
{
total += GetUpgradeCacheCost(nextLevel, rarity);
}
return total;
}
public static int GetInvestedBandwidth(int level, int rarity)
{
int total = 0;
for (int nextLevel = 1; nextLevel <= Mathf.Clamp(level, 0, MaxPackageLevel); nextLevel++)
{
total += GetUpgradeBandwidthCost(nextLevel, rarity);
}
return total;
}
public static int GetRefundedInvestedCache(int level, int rarity)
{
return Mathf.RoundToInt(GetInvestedCache(level, rarity) * RefundFraction);
}
public static int GetRefundedInvestedBandwidth(int level, int rarity)
{
return Mathf.RoundToInt(GetInvestedBandwidth(level, rarity) * RefundFraction);
}
private static int ApplyRarityMultiplier(int baseCost, int rarity)
{
float multiplier = RarityCostMultiplier <= 0f ? 1f : RarityCostMultiplier;
return Math.Max(0, Mathf.RoundToInt(baseCost * multiplier));
}
}
}
+72
View File
@@ -0,0 +1,72 @@
using System;
using KernelPanic.Data;
namespace KernelPanic.Meta
{
public enum PackageUpgradeFailureReason
{
None,
NotOwned,
MaxLevel,
InsufficientCache,
InsufficientBandwidth
}
public readonly struct PackageUpgradeResult
{
public PackageUpgradeResult(bool success, PackageUpgradeFailureReason failureReason, int cacheCost, int bandwidthCost, int newLevel)
{
Success = success;
FailureReason = failureReason;
CacheCost = cacheCost;
BandwidthCost = bandwidthCost;
NewLevel = newLevel;
}
public bool Success { get; }
public PackageUpgradeFailureReason FailureReason { get; }
public int CacheCost { get; }
public int BandwidthCost { get; }
public int NewLevel { get; }
}
public static class PackageUpgrader
{
public static event Action<OwnedPackageInstance> Upgraded;
public static PackageUpgradeResult TryUpgrade(OwnedPackageInstance packageInstance, SaveData wallet)
{
if (packageInstance == null || wallet == null)
{
return new PackageUpgradeResult(false, PackageUpgradeFailureReason.NotOwned, 0, 0, 0);
}
int currentLevel = Math.Max(0, packageInstance.UpgradeLevel);
if (currentLevel >= PackageTuning.MaxPackageLevel)
{
return new PackageUpgradeResult(false, PackageUpgradeFailureReason.MaxLevel, 0, 0, currentLevel);
}
int nextLevel = currentLevel + 1;
PackageDefinition definition = packageInstance.Definition;
int rarity = definition == null ? 1 : definition.Rarity;
int cacheCost = PackageTuning.GetUpgradeCacheCost(nextLevel, rarity);
int bandwidthCost = PackageTuning.GetUpgradeBandwidthCost(nextLevel, rarity);
if (wallet.cacheBalance < cacheCost)
{
return new PackageUpgradeResult(false, PackageUpgradeFailureReason.InsufficientCache, cacheCost, bandwidthCost, currentLevel);
}
if (wallet.bandwidthBalance < bandwidthCost)
{
return new PackageUpgradeResult(false, PackageUpgradeFailureReason.InsufficientBandwidth, cacheCost, bandwidthCost, currentLevel);
}
wallet.cacheBalance -= cacheCost;
wallet.bandwidthBalance -= bandwidthCost;
packageInstance.SetUpgradeLevel(nextLevel);
Upgraded?.Invoke(packageInstance);
return new PackageUpgradeResult(true, PackageUpgradeFailureReason.None, cacheCost, bandwidthCost, nextLevel);
}
}
}
+123
View File
@@ -10,11 +10,14 @@ namespace KernelPanic.Meta
public sealed class PlayerCollection
{
private readonly List<DistroDefinition> _ownedUnits = new();
private readonly List<OwnedPackageInstance> _ownedPackages = new();
private readonly Dictionary<string, int> _versions = new(StringComparer.OrdinalIgnoreCase);
private int _featuredIndex;
// TODO: Populate owned units from SaveService during bootstrap.
public IReadOnlyList<DistroDefinition> OwnedUnits => _ownedUnits;
public IReadOnlyList<OwnedPackageInstance> OwnedPackageInstances => _ownedPackages;
public IReadOnlyList<PackageDefinition> OwnedPackages => BuildOwnedPackageDefinitions();
public DistroDefinition FeaturedUnit => _ownedUnits.Count == 0 ? null : _ownedUnits[Math.Clamp(_featuredIndex, 0, _ownedUnits.Count - 1)];
public event Action Changed;
@@ -34,6 +37,35 @@ namespace KernelPanic.Meta
Add(unit, version, false);
}
public void AddPackage(PackageDefinition package)
{
AddPackage(package, 0, true);
}
public void AddPackageSilently(PackageDefinition package)
{
AddPackage(package, 0, false);
}
public void AddPackageSilently(PackageDefinition package, int upgradeLevel)
{
AddPackage(package, upgradeLevel, false);
}
private void AddPackage(PackageDefinition package, int upgradeLevel, bool notify)
{
if (package == null || IsPackageOwned(package.Id))
{
return;
}
_ownedPackages.Add(new OwnedPackageInstance(package, upgradeLevel));
if (notify)
{
Changed?.Invoke();
}
}
private void Add(DistroDefinition unit, int version, bool notify)
{
if (unit == null)
@@ -82,6 +114,60 @@ namespace KernelPanic.Meta
return count;
}
public bool IsPackageOwned(string packageId)
{
if (string.IsNullOrWhiteSpace(packageId))
{
return false;
}
for (int i = 0; i < _ownedPackages.Count; i++)
{
OwnedPackageInstance package = _ownedPackages[i];
if (package != null && string.Equals(package.PackageId, packageId, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public OwnedPackageInstance GetOwnedPackage(string packageId)
{
if (string.IsNullOrWhiteSpace(packageId))
{
return null;
}
for (int i = 0; i < _ownedPackages.Count; i++)
{
OwnedPackageInstance package = _ownedPackages[i];
if (package != null && string.Equals(package.PackageId, packageId, StringComparison.OrdinalIgnoreCase))
{
return package;
}
}
return null;
}
public bool RemovePackage(string packageId)
{
for (int i = _ownedPackages.Count - 1; i >= 0; i--)
{
OwnedPackageInstance package = _ownedPackages[i];
if (package != null && string.Equals(package.PackageId, packageId, StringComparison.OrdinalIgnoreCase))
{
_ownedPackages.RemoveAt(i);
Changed?.Invoke();
return true;
}
}
return false;
}
public int GetVersion(string unitId)
{
if (string.IsNullOrWhiteSpace(unitId))
@@ -131,5 +217,42 @@ namespace KernelPanic.Meta
_featuredIndex = (_featuredIndex + 1) % _ownedUnits.Count;
}
private IReadOnlyList<PackageDefinition> BuildOwnedPackageDefinitions()
{
List<PackageDefinition> definitions = new();
for (int i = 0; i < _ownedPackages.Count; i++)
{
if (_ownedPackages[i]?.Definition != null)
{
definitions.Add(_ownedPackages[i].Definition);
}
}
return definitions;
}
}
public sealed class OwnedPackageInstance
{
public OwnedPackageInstance(PackageDefinition definition, int upgradeLevel)
{
Definition = definition;
UpgradeLevel = Math.Max(0, Math.Min(PackageTuning.MaxPackageLevel, upgradeLevel));
}
public PackageDefinition Definition { get; }
public string PackageId => Definition == null ? string.Empty : Definition.Id;
public int UpgradeLevel { get; private set; }
public PackageInstance ToRuntimeInstance()
{
return new PackageInstance(Definition, UpgradeLevel);
}
public void SetUpgradeLevel(int level)
{
UpgradeLevel = Math.Max(0, Math.Min(PackageTuning.MaxPackageLevel, level));
}
}
}
+1 -1
View File
@@ -93,7 +93,7 @@ namespace KernelPanic.Meta
owned.merges = Math.Max(0, owned.merges) + merges;
if (GachaTuning.DupeConsolationBandwidth > 0)
{
context.SaveData.standardPullCurrency += GachaTuning.DupeConsolationBandwidth;
context.SaveData.bandwidthBalance += GachaTuning.DupeConsolationBandwidth;
}
context.Collection?.AddSilently(unit, version);
+219
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using KernelPanic.Data;
using UnityEngine;
namespace KernelPanic.Meta
@@ -60,16 +61,28 @@ namespace KernelPanic.Meta
public bool starterChosen;
public int entropyBalance;
public int rootCredits = GachaService.TestRootCreditsBalance;
public int commitsBalance;
public int bandwidthBalance;
// Legacy conflated balance. Older builds used this for both Commits and Bandwidth.
// Migration preserves it as Bandwidth and starts Commits at 0 because the source is ambiguous.
public int standardPullCurrency;
// Legacy pull-token field kept only so older saves deserialize cleanly.
public int limitedPullCurrency;
public int cacheBalance;
public int packageMerges;
// Legacy global balance kept only so older saves deserialize cleanly.
// New duplicate-pull merges live on OwnedUnitSaveEntry.merges.
public int merges;
public List<OwnedUnitSaveEntry> ownedUnits = new();
public List<OwnedPackageSaveEntry> ownedPackages = new();
public List<string> ownedPackageIds = new();
public List<PackageLoadoutSaveEntry> packageLoadouts = new();
public List<string> ownedUnitIds = new();
public List<string> bannerPoolIds = new();
public GachaBannerState beginnerBannerState = new(GachaService.BeginnerBannerId);
public LastRunLoadoutSaveEntry lastRunLoadout = new();
public string lastRunDistroId;
public List<DistroBestWaveSaveEntry> distroBestWaves = new();
public static SaveData CreateDefault()
{
@@ -79,19 +92,56 @@ namespace KernelPanic.Meta
public void EnsureLists()
{
ownedUnits ??= new List<OwnedUnitSaveEntry>();
ownedPackages ??= new List<OwnedPackageSaveEntry>();
ownedPackageIds ??= new List<string>();
packageLoadouts ??= new List<PackageLoadoutSaveEntry>();
ownedUnitIds ??= new List<string>();
bannerPoolIds ??= new List<string>();
distroBestWaves ??= new List<DistroBestWaveSaveEntry>();
beginnerBannerState ??= new GachaBannerState(GachaService.BeginnerBannerId);
beginnerBannerState.bannerId = GachaService.BeginnerBannerId;
beginnerBannerState.EnsureLists();
lastRunLoadout ??= new LastRunLoadoutSaveEntry();
lastRunLoadout.EnsureLists();
if (string.IsNullOrWhiteSpace(lastRunDistroId) && !string.IsNullOrWhiteSpace(lastRunLoadout.distroId))
{
lastRunDistroId = lastRunLoadout.distroId;
}
MigrateLegacyConflatedCurrency();
entropyBalance = Math.Max(0, entropyBalance);
rootCredits = Math.Max(0, rootCredits);
commitsBalance = Math.Max(0, commitsBalance);
bandwidthBalance = Math.Max(0, bandwidthBalance);
merges = Math.Max(0, merges);
packageMerges = Math.Max(0, packageMerges);
cacheBalance = Math.Max(0, cacheBalance);
MigrateLegacyOwnedUnitIds();
MigrateLegacyOwnedPackageIds();
NormalizeOwnedUnits();
NormalizeOwnedPackages();
NormalizePackageLoadouts();
NormalizeDistroBestWaves();
MigrateLegacyGlobalMerges();
}
private void MigrateLegacyConflatedCurrency()
{
if (standardPullCurrency <= 0)
{
return;
}
if (bandwidthBalance <= 0)
{
bandwidthBalance = Math.Max(0, standardPullCurrency);
Debug.Log("Migrated legacy standardPullCurrency to Bandwidth. Commits defaulted to 0 because the old field conflated pull tokens and upgrade currency.");
}
standardPullCurrency = 0;
limitedPullCurrency = 0;
}
public OwnedUnitSaveEntry FindOwnedUnit(string id)
{
if (string.IsNullOrWhiteSpace(id))
@@ -176,6 +226,133 @@ namespace KernelPanic.Meta
}
}
private void NormalizeOwnedPackages()
{
HashSet<string> seen = new(StringComparer.OrdinalIgnoreCase);
for (int i = ownedPackages.Count - 1; i >= 0; i--)
{
OwnedPackageSaveEntry entry = ownedPackages[i];
if (entry == null || string.IsNullOrWhiteSpace(entry.id) || !seen.Add(entry.id))
{
ownedPackages.RemoveAt(i);
continue;
}
entry.upgradeLevel = Math.Max(0, Math.Min(PackageTuning.MaxPackageLevel, entry.upgradeLevel));
}
ownedPackageIds.Clear();
for (int i = 0; i < ownedPackages.Count; i++)
{
ownedPackageIds.Add(ownedPackages[i].id);
}
}
public int GetBestWave(string distroId)
{
if (string.IsNullOrWhiteSpace(distroId))
{
return 0;
}
EnsureLists();
for (int i = 0; i < distroBestWaves.Count; i++)
{
DistroBestWaveSaveEntry entry = distroBestWaves[i];
if (entry != null && string.Equals(entry.distroId, distroId, StringComparison.OrdinalIgnoreCase))
{
return Math.Max(0, entry.bestWave);
}
}
return 0;
}
public void RecordRunStats(string distroId, int waveReached)
{
if (string.IsNullOrWhiteSpace(distroId))
{
return;
}
EnsureLists();
lastRunDistroId = distroId;
lastRunLoadout ??= new LastRunLoadoutSaveEntry();
lastRunLoadout.distroId = distroId;
int safeWave = Math.Max(0, waveReached);
for (int i = 0; i < distroBestWaves.Count; i++)
{
DistroBestWaveSaveEntry entry = distroBestWaves[i];
if (entry != null && string.Equals(entry.distroId, distroId, StringComparison.OrdinalIgnoreCase))
{
entry.bestWave = Math.Max(entry.bestWave, safeWave);
return;
}
}
distroBestWaves.Add(new DistroBestWaveSaveEntry { distroId = distroId, bestWave = safeWave });
}
private void NormalizeDistroBestWaves()
{
HashSet<string> seen = new(StringComparer.OrdinalIgnoreCase);
for (int i = distroBestWaves.Count - 1; i >= 0; i--)
{
DistroBestWaveSaveEntry entry = distroBestWaves[i];
if (entry == null || string.IsNullOrWhiteSpace(entry.distroId) || !seen.Add(entry.distroId))
{
distroBestWaves.RemoveAt(i);
continue;
}
entry.bestWave = Math.Max(0, entry.bestWave);
}
}
private void MigrateLegacyOwnedPackageIds()
{
for (int i = 0; i < ownedPackageIds.Count; i++)
{
string id = ownedPackageIds[i];
if (string.IsNullOrWhiteSpace(id) || HasOwnedPackageEntry(id))
{
continue;
}
ownedPackages.Add(new OwnedPackageSaveEntry { id = id, upgradeLevel = 0 });
}
}
private bool HasOwnedPackageEntry(string id)
{
for (int i = 0; i < ownedPackages.Count; i++)
{
OwnedPackageSaveEntry entry = ownedPackages[i];
if (entry != null && string.Equals(entry.id, id, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private void NormalizePackageLoadouts()
{
HashSet<string> seenDistros = new(StringComparer.OrdinalIgnoreCase);
for (int i = packageLoadouts.Count - 1; i >= 0; i--)
{
PackageLoadoutSaveEntry entry = packageLoadouts[i];
if (entry == null || string.IsNullOrWhiteSpace(entry.distroId) || !seenDistros.Add(entry.distroId))
{
packageLoadouts.RemoveAt(i);
continue;
}
entry.EnsureLists();
}
}
private void MigrateLegacyGlobalMerges()
{
if (merges <= 0 || ownedUnits.Count == 0)
@@ -223,6 +400,13 @@ namespace KernelPanic.Meta
public int merges;
}
[Serializable]
public sealed class OwnedPackageSaveEntry
{
public string id;
public int upgradeLevel;
}
[Serializable]
public sealed class LastRunLoadoutSaveEntry
{
@@ -234,4 +418,39 @@ namespace KernelPanic.Meta
cardIds ??= new List<string>();
}
}
[Serializable]
public sealed class DistroBestWaveSaveEntry
{
public string distroId;
public int bestWave;
}
[Serializable]
public sealed class PackageLoadoutSaveEntry
{
public string distroId;
public List<PackageLoadoutSlotSaveEntry> slots = new();
public void EnsureLists()
{
slots ??= new List<PackageLoadoutSlotSaveEntry>();
HashSet<PackageSlot> seen = new();
for (int i = slots.Count - 1; i >= 0; i--)
{
PackageLoadoutSlotSaveEntry slot = slots[i];
if (slot == null || string.IsNullOrWhiteSpace(slot.packageId) || !seen.Add(slot.slot))
{
slots.RemoveAt(i);
}
}
}
}
[Serializable]
public sealed class PackageLoadoutSlotSaveEntry
{
public PackageSlot slot;
public string packageId;
}
}

Some files were not shown because too many files have changed in this diff Show More