diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..d738cea
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,15 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/contentModel.xml
+/.idea.linuxdistro-roguelite.iml
+/modules.xml
+/projectSettingsUpdater.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/Assets/Art.meta b/Assets/Art.meta
deleted file mode 100644
index 68e0e03..0000000
--- a/Assets/Art.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 7b840cced864976439f43974b1e498d3
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data.meta b/Assets/Data.meta
deleted file mode 100644
index 3ef0d3d..0000000
--- a/Assets/Data.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 7ec233d275778bf44b6acde2fd9a0cc2
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Cards/CardDatabase.asset b/Assets/Data/Cards/CardDatabase.asset
index 8eb4db5..fc30a09 100644
--- a/Assets/Data/Cards/CardDatabase.asset
+++ b/Assets/Data/Cards/CardDatabase.asset
@@ -9,7 +9,7 @@ MonoBehaviour:
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 85f230365a0e4f28a4bfbdb0a15115a8, type: 3}
+ m_Script: {fileID: 11500000, guid: fd9e459379f339742a14cf224cd91234, type: 3}
m_Name: CardDatabase
m_EditorClassIdentifier:
allCards:
@@ -29,9 +29,3 @@ MonoBehaviour:
- {fileID: 11400000, guid: c1a50000000000000000000000000003, type: 2}
- {fileID: 11400000, guid: c1a50000000000000000000000000004, type: 2}
- {fileID: 11400000, guid: c1a50000000000000000000000000005, type: 2}
- - {fileID: 11400000, guid: a7d40000000000000000000000000001, type: 2}
- - {fileID: 11400000, guid: a7d40000000000000000000000000002, type: 2}
- - {fileID: 11400000, guid: a7d40000000000000000000000000003, type: 2}
- - {fileID: 11400000, guid: a7d40000000000000000000000000004, type: 2}
- - {fileID: 11400000, guid: a7d40000000000000000000000000005, type: 2}
- - {fileID: 11400000, guid: a7d40000000000000000000000000006, type: 2}
diff --git a/Assets/Data/Distros.meta b/Assets/Data/Distros.meta
deleted file mode 100644
index 9fd6c94..0000000
--- a/Assets/Data/Distros.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: b5d4e612df874934a2fe5e4ce5cf43de
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Art.meta b/Assets/Data/Distros/Art.meta
deleted file mode 100644
index d7a6f11..0000000
--- a/Assets/Data/Distros/Art.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 296f57316cdb4f9ba2227b877b091c23
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Art/fedora.txt.meta b/Assets/Data/Distros/Art/fedora.txt.meta
deleted file mode 100644
index ccb460c..0000000
--- a/Assets/Data/Distros/Art/fedora.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: f2373174b6954f2d9088d83d10e0d9e4
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Art/mint.txt.meta b/Assets/Data/Distros/Art/mint.txt.meta
deleted file mode 100644
index 1c8a843..0000000
--- a/Assets/Data/Distros/Art/mint.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 412c9be402ec4d8c8510e71066ec22ff
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Art/ubuntu.txt.meta b/Assets/Data/Distros/Art/ubuntu.txt.meta
deleted file mode 100644
index fa21f82..0000000
--- a/Assets/Data/Distros/Art/ubuntu.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 975f1f5de289435eb81c91c3eddd31dc
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/DistroDatabase.asset.meta b/Assets/Data/Distros/DistroDatabase.asset.meta
deleted file mode 100644
index 6453a4c..0000000
--- a/Assets/Data/Distros/DistroDatabase.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 4d88ca5446fa4d80b2fbd4b12697cb7c
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Fedora.asset b/Assets/Data/Distros/Fedora.asset
index 799f8ae..eb1ca73 100644
--- a/Assets/Data/Distros/Fedora.asset
+++ b/Assets/Data/Distros/Fedora.asset
@@ -16,7 +16,7 @@ MonoBehaviour:
displayName: Fedora
primaryLanguage: 2
secondaryLanguage: 8
- passive: {fileID: 11400000, guid: a1100000000000000000000000000002, type: 2}
+ passive: {fileID: 11400000, guid: ded62c69f70d38a49b21972d857df4e2, type: 2}
description: First to every new feature. Occasionally first to the bugs that come with it.
playstyleSummary: high-tempo gambler. her first play each turn is pushed hard, but bleeding edge bites back. rust cards are her safety net.
languageBlurbs:
@@ -30,8 +30,8 @@ MonoBehaviour:
baseRam: 5
baseCyclesPerTurn: 3
exclusiveCards:
- - {fileID: 11400000, guid: feda0000000000000000000000000001, type: 2}
- - {fileID: 11400000, guid: feda0000000000000000000000000002, type: 2}
- - {fileID: 11400000, guid: feda0000000000000000000000000003, type: 2}
- - {fileID: 11400000, guid: feda0000000000000000000000000004, type: 2}
- - {fileID: 11400000, guid: feda0000000000000000000000000005, type: 2}
+ - {fileID: 11400000, guid: 93ddfe2f35c5c4447b1e5f49b50c544d, type: 2}
+ - {fileID: 11400000, guid: e6a3f582f36bb2647ab6101d60a05075, type: 2}
+ - {fileID: 11400000, guid: e0a15eebcb5cb7b409a6afe60710e1e1, type: 2}
+ - {fileID: 11400000, guid: fde58599fc89f4f4ab515612f4f481f7, type: 2}
+ - {fileID: 11400000, guid: 7f1a9912cb72c5b448fa2cb101eb9785, type: 2}
diff --git a/Assets/Data/Distros/Fedora.asset.meta b/Assets/Data/Distros/Fedora.asset.meta
deleted file mode 100644
index dda654c..0000000
--- a/Assets/Data/Distros/Fedora.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 245dfb73afe047d5a3a3ef54c30d5135
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Mint.asset b/Assets/Data/Distros/Mint.asset
index ec81959..cb8d112 100644
--- a/Assets/Data/Distros/Mint.asset
+++ b/Assets/Data/Distros/Mint.asset
@@ -16,7 +16,7 @@ MonoBehaviour:
displayName: Mint
primaryLanguage: 3
secondaryLanguage: 4
- passive: {fileID: 11400000, guid: a1100000000000000000000000000003, type: 2}
+ passive: {fileID: 11400000, guid: d97481a7d54be2045a96165d3977c980, type: 2}
description: Comfort incarnate. No surprises, no drama, ever.
playstyleSummary: zero-variance comfort. every number lands at its ceiling, so you always know exactly what a turn does -- at the cost of a high-roll upside.
languageBlurbs:
@@ -30,8 +30,8 @@ MonoBehaviour:
baseRam: 5
baseCyclesPerTurn: 3
exclusiveCards:
- - {fileID: 11400000, guid: c1a50000000000000000000000000001, type: 2}
- - {fileID: 11400000, guid: c1a50000000000000000000000000002, type: 2}
- - {fileID: 11400000, guid: c1a50000000000000000000000000003, type: 2}
- - {fileID: 11400000, guid: c1a50000000000000000000000000004, type: 2}
- - {fileID: 11400000, guid: c1a50000000000000000000000000005, type: 2}
+ - {fileID: 11400000, guid: 258f2221f41c405468e0024e8e769614, type: 2}
+ - {fileID: 11400000, guid: ab370770e5f6c544996be970b87e7801, type: 2}
+ - {fileID: 11400000, guid: cdde7bad9bde5ff44a047263bce1bb4e, type: 2}
+ - {fileID: 11400000, guid: 698dfb4b10be9f2419ca74e0b3443d48, type: 2}
+ - {fileID: 11400000, guid: e6c02c38647698744903032f93fb30da, type: 2}
diff --git a/Assets/Data/Distros/Mint.asset.meta b/Assets/Data/Distros/Mint.asset.meta
deleted file mode 100644
index 0b6c5ea..0000000
--- a/Assets/Data/Distros/Mint.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 366e71361387449abcfaa1a50c9af87b
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Data/Distros/Ubuntu.asset b/Assets/Data/Distros/Ubuntu.asset
index 7189990..47aa428 100644
--- a/Assets/Data/Distros/Ubuntu.asset
+++ b/Assets/Data/Distros/Ubuntu.asset
@@ -16,7 +16,7 @@ MonoBehaviour:
displayName: Ubuntu
primaryLanguage: 3
secondaryLanguage: 4
- passive: {fileID: 11400000, guid: a1100000000000000000000000000001, type: 2}
+ passive: {fileID: 11400000, guid: 19deb73cec3d5dc42a5cc336222ce9df, type: 2}
description: The friendly one. Everything works out of the box, and she will not stop helping.
playstyleSummary: an accessible engine that never runs dry -- cheap python cards and a passive that always finds you the right package.
languageBlurbs:
@@ -30,8 +30,8 @@ MonoBehaviour:
baseRam: 5
baseCyclesPerTurn: 3
exclusiveCards:
- - {fileID: 11400000, guid: 0a4f1d1d84564f3cad58b8863e941201, type: 2}
- - {fileID: 11400000, guid: 14c5d40f866a4cb6b0a6316e01f42891, type: 2}
- - {fileID: 11400000, guid: 2a0f50d541f046fc99c8fb6deef4f1aa, type: 2}
- - {fileID: 11400000, guid: 3d57857f3f30419c8be4c44914c5abbe, type: 2}
- - {fileID: 11400000, guid: 4cae271a0f4146c59aab74e4c9d6d0f8, type: 2}
+ - {fileID: 11400000, guid: f2a229189aa753e48806c9459618f10b, type: 2}
+ - {fileID: 11400000, guid: 1120e87243f2b5c48b6a334855af7937, type: 2}
+ - {fileID: 11400000, guid: f3d46628fc9cc2142ab5dfc5d78b158f, type: 2}
+ - {fileID: 11400000, guid: 6bfadf916238fd74b91e28a875239da4, type: 2}
+ - {fileID: 11400000, guid: 662257d2f1d13554fb1eb6c63257859c, type: 2}
diff --git a/Assets/Data/Distros/Ubuntu.asset.meta b/Assets/Data/Distros/Ubuntu.asset.meta
deleted file mode 100644
index 76aaed8..0000000
--- a/Assets/Data/Distros/Ubuntu.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 1b0892a0d4c34b03a54c3d511ae59356
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/DefaultVolumeProfile.asset b/Assets/DefaultVolumeProfile.asset
index 0cd0e1a..8cea4d2 100644
--- a/Assets/DefaultVolumeProfile.asset
+++ b/Assets/DefaultVolumeProfile.asset
@@ -322,6 +322,9 @@ MonoBehaviour:
highQualityFiltering:
m_OverrideState: 1
m_Value: 0
+ filter:
+ m_OverrideState: 1
+ m_Value: 0
downscale:
m_OverrideState: 1
m_Value: 0
diff --git a/Assets/DefaultVolumeProfile.asset.meta b/Assets/DefaultVolumeProfile.asset.meta
deleted file mode 100644
index 5383e5d..0000000
--- a/Assets/DefaultVolumeProfile.asset.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 3f9215ea0144899419cfbc0957140d3f
-NativeFormatImporter:
- externalObjects: {}
- mainObjectFileID: 11400000
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/InputSystem_Actions.inputactions.meta b/Assets/InputSystem_Actions.inputactions.meta
deleted file mode 100644
index e786d3e..0000000
--- a/Assets/InputSystem_Actions.inputactions.meta
+++ /dev/null
@@ -1,14 +0,0 @@
-fileFormatVersion: 2
-guid: 2bcd2660ca9b64942af0de543d8d7100
-ScriptedImporter:
- internalIDToNameTable: []
- externalObjects: {}
- serializedVersion: 2
- userData:
- assetBundleName:
- assetBundleVariant:
- script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3}
- generateWrapperCode: 0
- wrapperCodePath:
- wrapperClassName:
- wrapperCodeNamespace:
diff --git a/Assets/Prefabs.meta b/Assets/Prefabs.meta
deleted file mode 100644
index 44ef5d3..0000000
--- a/Assets/Prefabs.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: e9c67aa99a5854f4f9d58ccc64ed036c
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scenes.meta b/Assets/Scenes.meta
deleted file mode 100644
index ee46714..0000000
--- a/Assets/Scenes.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 109e9bdd0fcc649c5bc59085c35bdc83
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scenes/GameScene.unity.meta b/Assets/Scenes/GameScene.unity.meta
deleted file mode 100644
index 5d550b1..0000000
--- a/Assets/Scenes/GameScene.unity.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: e4259fa62d854e27af3cc2a10fb210c8
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scenes/MainMenuScene.unity.meta b/Assets/Scenes/MainMenuScene.unity.meta
deleted file mode 100644
index 963ff8e..0000000
--- a/Assets/Scenes/MainMenuScene.unity.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 8c9cfa26abfee488c85f1582747f6a02
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts.meta b/Assets/Scripts.meta
deleted file mode 100644
index a5599af..0000000
--- a/Assets/Scripts.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: c51fb03ac0aab7a4aa1e2b0a87f051a9
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Combat.meta b/Assets/Scripts/Combat.meta
deleted file mode 100644
index 2ffcdf2..0000000
--- a/Assets/Scripts/Combat.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 59b9870361cbdec4b9419c57a3ff71df
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Combat/CardInstance.cs.meta b/Assets/Scripts/Combat/CardInstance.cs.meta
deleted file mode 100644
index 5d67f10..0000000
--- a/Assets/Scripts/Combat/CardInstance.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: c05e61a09404ee046a6771f8ac75803c
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/CombatContext.cs.meta b/Assets/Scripts/Combat/CombatContext.cs.meta
deleted file mode 100644
index 089a01f..0000000
--- a/Assets/Scripts/Combat/CombatContext.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 2eed81ad20b42a447858691a49666804
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/CombatManager.cs.meta b/Assets/Scripts/Combat/CombatManager.cs.meta
deleted file mode 100644
index 65792ef..0000000
--- a/Assets/Scripts/Combat/CombatManager.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 9f87432412de1ee46bf7d4c9c530d4c6
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/CombatantState.cs.meta b/Assets/Scripts/Combat/CombatantState.cs.meta
deleted file mode 100644
index 6ce8109..0000000
--- a/Assets/Scripts/Combat/CombatantState.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 93b1080022652b542903e204d36a64f8
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/DeckController.cs.meta b/Assets/Scripts/Combat/DeckController.cs.meta
deleted file mode 100644
index c2f9ee8..0000000
--- a/Assets/Scripts/Combat/DeckController.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 6683933e853af334ba19e658b13c878d
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/HandController.cs.meta b/Assets/Scripts/Combat/HandController.cs.meta
deleted file mode 100644
index bae4241..0000000
--- a/Assets/Scripts/Combat/HandController.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: bece1c6fddc2c63489bf129bf6295a31
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/ICardEffect.cs.meta b/Assets/Scripts/Combat/ICardEffect.cs.meta
deleted file mode 100644
index 743b679..0000000
--- a/Assets/Scripts/Combat/ICardEffect.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 886be8d59396e5e488eea3ea58445fb4
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/IResolutionTrack.cs.meta b/Assets/Scripts/Combat/IResolutionTrack.cs.meta
deleted file mode 100644
index 401a2f9..0000000
--- a/Assets/Scripts/Combat/IResolutionTrack.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 5d296d1160df6d747adaeec9ef68c710
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/InterpreterQueue.cs.meta b/Assets/Scripts/Combat/InterpreterQueue.cs.meta
deleted file mode 100644
index 20fff14..0000000
--- a/Assets/Scripts/Combat/InterpreterQueue.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 13c99cf765e7e8b48a58afbacc8a5440
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/LazyStack.cs.meta b/Assets/Scripts/Combat/LazyStack.cs.meta
deleted file mode 100644
index a7e65da..0000000
--- a/Assets/Scripts/Combat/LazyStack.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 556859e6fac10a24bb4a60165fd82157
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/NativeTrack.cs.meta b/Assets/Scripts/Combat/NativeTrack.cs.meta
deleted file mode 100644
index d15e847..0000000
--- a/Assets/Scripts/Combat/NativeTrack.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: b662e4e1a38b1554084a063fd3d415e2
\ No newline at end of file
diff --git a/Assets/Scripts/Combat/StatusEffectController.cs.meta b/Assets/Scripts/Combat/StatusEffectController.cs.meta
deleted file mode 100644
index 55dcde8..0000000
--- a/Assets/Scripts/Combat/StatusEffectController.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 5f91b8d9171889f4996bb3106d97f1f6
\ No newline at end of file
diff --git a/Assets/Scripts/Core.meta b/Assets/Scripts/Core.meta
deleted file mode 100644
index ac449c1..0000000
--- a/Assets/Scripts/Core.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: d51c41fa71b1068438855f64f946cc57
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Core/GameEvents.cs.meta b/Assets/Scripts/Core/GameEvents.cs.meta
deleted file mode 100644
index a0e6246..0000000
--- a/Assets/Scripts/Core/GameEvents.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: cf446c6fd7627294c9e2aa23c9b576e7
\ No newline at end of file
diff --git a/Assets/Scripts/Core/Language.cs.meta b/Assets/Scripts/Core/Language.cs.meta
deleted file mode 100644
index 03d1951..0000000
--- a/Assets/Scripts/Core/Language.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: c06078e18f184c84cab3579c0e562dbe
\ No newline at end of file
diff --git a/Assets/Scripts/Core/Rarity.cs.meta b/Assets/Scripts/Core/Rarity.cs.meta
deleted file mode 100644
index c324b28..0000000
--- a/Assets/Scripts/Core/Rarity.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 542a3f97e8074ed428fd7019177f221b
\ No newline at end of file
diff --git a/Assets/Scripts/Core/ResolutionTrack.cs.meta b/Assets/Scripts/Core/ResolutionTrack.cs.meta
deleted file mode 100644
index 0560194..0000000
--- a/Assets/Scripts/Core/ResolutionTrack.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 3b76dcac6b8cae447bb9079460692238
\ No newline at end of file
diff --git a/Assets/Scripts/Core/SceneLoader.cs.meta b/Assets/Scripts/Core/SceneLoader.cs.meta
deleted file mode 100644
index 519c755..0000000
--- a/Assets/Scripts/Core/SceneLoader.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 0d860bbf8e7f4df2a91e2e1e9892d904
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Core/StatusType.cs.meta b/Assets/Scripts/Core/StatusType.cs.meta
deleted file mode 100644
index 4304e27..0000000
--- a/Assets/Scripts/Core/StatusType.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: cbdd46dffec613346ae6191f18a94225
\ No newline at end of file
diff --git a/Assets/Scripts/Core/TurnPhase.cs.meta b/Assets/Scripts/Core/TurnPhase.cs.meta
deleted file mode 100644
index 0289dec..0000000
--- a/Assets/Scripts/Core/TurnPhase.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: e1974726f936d0640943801549b4574e
\ No newline at end of file
diff --git a/Assets/Scripts/Data.meta b/Assets/Scripts/Data.meta
deleted file mode 100644
index ede1e85..0000000
--- a/Assets/Scripts/Data.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: e0a39b97df9ee0641b2bbadf5bca417c
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Data/CardDefinition.cs.meta b/Assets/Scripts/Data/CardDefinition.cs.meta
deleted file mode 100644
index ade0308..0000000
--- a/Assets/Scripts/Data/CardDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 5178747fac4dbfc448ff15d8a687e03c
\ No newline at end of file
diff --git a/Assets/Scripts/Data/CardEffectDefinition.cs.meta b/Assets/Scripts/Data/CardEffectDefinition.cs.meta
deleted file mode 100644
index d01f83e..0000000
--- a/Assets/Scripts/Data/CardEffectDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: fdb3d58d94b76834598e408448e19d2a
\ No newline at end of file
diff --git a/Assets/Scripts/Data/DistroDatabase.cs.meta b/Assets/Scripts/Data/DistroDatabase.cs.meta
deleted file mode 100644
index edaf02c..0000000
--- a/Assets/Scripts/Data/DistroDatabase.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 5af93d291e7d4d51b312bdf1e47dc6ef
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Data/DistroDefinition.cs.meta b/Assets/Scripts/Data/DistroDefinition.cs.meta
deleted file mode 100644
index e8037bd..0000000
--- a/Assets/Scripts/Data/DistroDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: bd550368fd0c75d43a53aa4fb3b6541e
\ No newline at end of file
diff --git a/Assets/Scripts/Data/EnemyDefinition.cs.meta b/Assets/Scripts/Data/EnemyDefinition.cs.meta
deleted file mode 100644
index 9c557ab..0000000
--- a/Assets/Scripts/Data/EnemyDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 8b5b43b43c697c142b33fbfc5160cf90
\ No newline at end of file
diff --git a/Assets/Scripts/Data/StatusEffectDefinition.cs.meta b/Assets/Scripts/Data/StatusEffectDefinition.cs.meta
deleted file mode 100644
index 150a317..0000000
--- a/Assets/Scripts/Data/StatusEffectDefinition.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 1470a223647d6514f9439de7ded702c9
\ No newline at end of file
diff --git a/Assets/Scripts/KernelPanic.Runtime.asmdef.meta b/Assets/Scripts/KernelPanic.Runtime.asmdef.meta
deleted file mode 100644
index 667deb9..0000000
--- a/Assets/Scripts/KernelPanic.Runtime.asmdef.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 6aad857173769044f861a0c6b1318032
-AssemblyDefinitionImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Meta.meta b/Assets/Scripts/Meta.meta
deleted file mode 100644
index 05e8c60..0000000
--- a/Assets/Scripts/Meta.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: ae19044bbccbb074da1d5e38b61e5657
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Meta/EntropyWallet.cs b/Assets/Scripts/Meta/EntropyWallet.cs
index 83aa4db..b59e6f2 100644
--- a/Assets/Scripts/Meta/EntropyWallet.cs
+++ b/Assets/Scripts/Meta/EntropyWallet.cs
@@ -7,6 +7,11 @@ namespace KernelPanic.Meta
{
public int Balance { get; private set; }
+ public void SetBalance(int amount)
+ {
+ Balance = amount < 0 ? 0 : amount;
+ }
+
public void Add(int amount)
{
Balance += amount;
diff --git a/Assets/Scripts/Meta/EntropyWallet.cs.meta b/Assets/Scripts/Meta/EntropyWallet.cs.meta
deleted file mode 100644
index fc4c673..0000000
--- a/Assets/Scripts/Meta/EntropyWallet.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 8b77d0de67282ca4181a7c53feec4343
\ No newline at end of file
diff --git a/Assets/Scripts/Meta/GachaBannerState.cs b/Assets/Scripts/Meta/GachaBannerState.cs
new file mode 100644
index 0000000..567191a
--- /dev/null
+++ b/Assets/Scripts/Meta/GachaBannerState.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+
+namespace KernelPanic.Meta
+{
+ ///
+ /// Stores per-banner counters that must persist between sessions.
+ ///
+ [Serializable]
+ public sealed class GachaBannerState
+ {
+ public string bannerId;
+ public int totalPulls;
+ public int pityCounter;
+ public int fiveStarPityCounter;
+ public bool exhausted;
+ public bool featuredFiveStarGuaranteed;
+ public List guaranteedDistroIds = new();
+
+ public GachaBannerState()
+ {
+ }
+
+ public GachaBannerState(string bannerId)
+ {
+ this.bannerId = bannerId;
+ }
+
+ public void EnsureLists()
+ {
+ guaranteedDistroIds ??= new List();
+ }
+ }
+}
diff --git a/Assets/Scripts/Meta/GachaCurrencyType.cs b/Assets/Scripts/Meta/GachaCurrencyType.cs
new file mode 100644
index 0000000..4023677
--- /dev/null
+++ b/Assets/Scripts/Meta/GachaCurrencyType.cs
@@ -0,0 +1,11 @@
+namespace KernelPanic.Meta
+{
+ ///
+ /// Identifies currencies spent on banner pulls.
+ ///
+ public enum GachaCurrencyType
+ {
+ StandardPull,
+ LimitedPull
+ }
+}
diff --git a/Assets/Scripts/Meta/GachaRewardType.cs b/Assets/Scripts/Meta/GachaRewardType.cs
new file mode 100644
index 0000000..a7dce16
--- /dev/null
+++ b/Assets/Scripts/Meta/GachaRewardType.cs
@@ -0,0 +1,12 @@
+namespace KernelPanic.Meta
+{
+ ///
+ /// Describes the persistent reward category produced by a gacha pull.
+ ///
+ public enum GachaRewardType
+ {
+ Equipment,
+ Distro,
+ FutureStandardFiveStarDistro
+ }
+}
diff --git a/Assets/Scripts/Meta/GachaService.cs b/Assets/Scripts/Meta/GachaService.cs
index 4236f45..438b173 100644
--- a/Assets/Scripts/Meta/GachaService.cs
+++ b/Assets/Scripts/Meta/GachaService.cs
@@ -5,16 +5,150 @@ using KernelPanic.Data;
namespace KernelPanic.Meta
{
///
- /// Provides the contract for spending entropy on persistent unlock pulls.
+ /// Provides persistent banner pull logic and currency accounting.
///
public sealed class GachaService
{
- private readonly List bannerPool = new();
+ public const string BeginnerBannerId = "beginner";
+ public const string StandardBannerId = "standard";
+ public const string LimitedBannerId = "limited";
+ public const int BeginnerMaxPulls = 50;
+ public const int BeginnerSinglePullCost = 1;
+ public const int BeginnerTenPullCost = 8;
+ public const int FourStarHardPity = 10;
+ public const int FiveStarSoftPityStart = 66;
+ public const int FiveStarHardPity = 80;
+ public const int EntropyPerPullToken = 100;
- public int PullTokens => 0; // TODO: Replace with the real pull-token wallet when that meta system exists.
+ private const double FourStarBaseChance = 0.12d;
+ private const double FiveStarBaseChance = 0.008d;
+ private const double FiveStarSoftPityStep = 0.07d;
+ private const double DistroChanceOnFeaturedTier = 0.5d;
+ private const double LimitedStandardFiveStarChance = 1d / 3d;
+
+ private readonly List bannerPool = new();
+ private readonly Dictionary currencyBalances = new();
+ private readonly Random random;
+ private GachaBannerState beginnerState = new(BeginnerBannerId);
+
+ public GachaService()
+ : this(new Random())
+ {
+ }
+
+ public GachaService(Random random)
+ {
+ 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 RootCredits { get; private set; }
public IReadOnlyList BannerPool => bannerPool;
+ public GachaBannerState BeginnerState => beginnerState;
+ public bool IsBeginnerBannerAvailable => !beginnerState.exhausted && beginnerState.totalPulls < BeginnerMaxPulls && bannerPool.Count > 0;
public event Action BannerPoolChanged;
+ public event Action Changed;
+
+ public void LoadProgress(SaveData data)
+ {
+ if (data == null)
+ {
+ return;
+ }
+
+ data.EnsureLists();
+ RootCredits = Math.Max(0, data.rootCredits);
+ SetCurrencyBalanceSilently(GachaCurrencyType.StandardPull, data.standardPullCurrency);
+ SetCurrencyBalanceSilently(GachaCurrencyType.LimitedPull, data.limitedPullCurrency);
+ beginnerState = data.beginnerBannerState ?? new GachaBannerState(BeginnerBannerId);
+ beginnerState.bannerId = BeginnerBannerId;
+ beginnerState.EnsureLists();
+ if (beginnerState.totalPulls >= BeginnerMaxPulls)
+ {
+ beginnerState.exhausted = true;
+ }
+
+ beginnerState.fiveStarPityCounter = Math.Max(0, beginnerState.fiveStarPityCounter);
+
+ Changed?.Invoke();
+ }
+
+ public void WriteProgress(SaveData data)
+ {
+ if (data == null)
+ {
+ return;
+ }
+
+ data.EnsureLists();
+ data.rootCredits = RootCredits;
+ data.standardPullCurrency = GetCurrencyBalance(GachaCurrencyType.StandardPull);
+ data.limitedPullCurrency = GetCurrencyBalance(GachaCurrencyType.LimitedPull);
+ data.beginnerBannerState = CloneState(beginnerState);
+ }
+
+ public int GetCurrencyBalance(GachaCurrencyType currencyType)
+ {
+ return currencyBalances.TryGetValue(currencyType, out int balance) ? balance : 0;
+ }
+
+ public void SetCurrencyBalance(GachaCurrencyType currencyType, int amount)
+ {
+ SetCurrencyBalanceSilently(currencyType, amount);
+ Changed?.Invoke();
+ }
+
+ public void AddCurrency(GachaCurrencyType currencyType, int amount)
+ {
+ if (amount <= 0)
+ {
+ return;
+ }
+
+ SetCurrencyBalance(currencyType, GetCurrencyBalance(currencyType) + amount);
+ }
+
+ public void AddRootCredits(int amount)
+ {
+ if (amount <= 0)
+ {
+ return;
+ }
+
+ RootCredits += amount;
+ Changed?.Invoke();
+ }
+
+ public bool ConvertRootCreditsToEntropy(EntropyWallet wallet, int amount, out string failureReason)
+ {
+ if (wallet == null)
+ {
+ failureReason = "entropy wallet unavailable";
+ return false;
+ }
+
+ if (amount <= 0)
+ {
+ failureReason = "no root-credits selected";
+ return false;
+ }
+
+ if (RootCredits < amount)
+ {
+ failureReason = $"need {amount} root-credits";
+ return false;
+ }
+
+ RootCredits -= amount;
+ wallet.Add(amount);
+ failureReason = null;
+ Changed?.Invoke();
+ return true;
+ }
public void AddToBannerPool(DistroDefinition distro)
{
@@ -25,26 +159,446 @@ namespace KernelPanic.Meta
bannerPool.Add(distro);
BannerPoolChanged?.Invoke();
+ Changed?.Invoke();
+ }
+
+ public void SetBeginnerGuaranteedDistros(IReadOnlyList distros)
+ {
+ beginnerState.guaranteedDistroIds.Clear();
+ if (distros != null)
+ {
+ for (int i = 0; i < distros.Count; i++)
+ {
+ DistroDefinition distro = distros[i];
+ if (distro != null && !string.IsNullOrWhiteSpace(distro.Id))
+ {
+ beginnerState.guaranteedDistroIds.Add(distro.Id);
+ }
+ }
+ }
+
+ Changed?.Invoke();
+ }
+
+ public int GetBeginnerPullCost(int pullCount)
+ {
+ return pullCount == 10 ? BeginnerTenPullCost : BeginnerSinglePullCost;
+ }
+
+ public bool CanPullBeginner(int pullCount, 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 (GetCurrencyBalance(GachaCurrencyType.StandardPull) < cost)
+ {
+ failureReason = $"need {cost} {FormatCurrencyName(GachaCurrencyType.StandardPull)}";
+ return false;
+ }
+
+ failureReason = null;
+ 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);
+ }
+
+ public GachaPullResult PerformBeginnerPull(int pullCount, EntropyWallet wallet, int entropyTokenCount)
+ {
+ if (!CanPullBeginnerWithEntropy(pullCount, wallet, entropyTokenCount, out string failureReason))
+ {
+ return GachaPullResult.Failed(BeginnerBannerId, failureReason);
+ }
+
+ int cost = GetBeginnerPullCost(pullCount);
+ int tokenSpend = cost - entropyTokenCount;
+ if (tokenSpend > 0)
+ {
+ SetCurrencyBalanceSilently(GachaCurrencyType.StandardPull, GetCurrencyBalance(GachaCurrencyType.StandardPull) - tokenSpend);
+ }
+
+ int entropySpent = entropyTokenCount * EntropyPerPullToken;
+ if (entropySpent > 0)
+ {
+ wallet.Spend(entropySpent);
+ }
+
+ List rewards = new();
+ for (int i = 0; i < pullCount; i++)
+ {
+ beginnerState.totalPulls++;
+ rewards.Add(RollBeginnerReward(beginnerState.totalPulls));
+ }
+
+ if (beginnerState.totalPulls >= BeginnerMaxPulls)
+ {
+ beginnerState.exhausted = true;
+ }
+
+ Changed?.Invoke();
+ return new GachaPullResult(true, BeginnerBannerId, GachaCurrencyType.StandardPull, tokenSpend, entropySpent, rewards, null);
+ }
+
+ public static string FormatCurrencyName(GachaCurrencyType currencyType)
+ {
+ return currencyType switch
+ {
+ GachaCurrencyType.LimitedPull => "feature-pull-token",
+ _ => "stable-pull-token"
+ };
+ }
+
+ public static double GetFiveStarChance(GachaBannerState state)
+ {
+ return GetFiveStarChance(state == null ? 0 : state.fiveStarPityCounter);
+ }
+
+ public static double GetFiveStarChance(int fiveStarPityCounter)
+ {
+ int pityPullNumber = Math.Max(0, fiveStarPityCounter) + 1;
+ if (pityPullNumber >= FiveStarHardPity)
+ {
+ return 1d;
+ }
+
+ if (pityPullNumber < FiveStarSoftPityStart)
+ {
+ return FiveStarBaseChance;
+ }
+
+ int softPitySteps = pityPullNumber - FiveStarSoftPityStart + 1;
+ return Math.Min(1d, FiveStarBaseChance + softPitySteps * FiveStarSoftPityStep);
}
public GachaPullResult PerformPull(EntropyWallet wallet, int cost)
{
- throw new NotImplementedException();
+ return PerformBeginnerPull(1);
+ }
+
+ private GachaReward RollBeginnerReward(int pullNumber)
+ {
+ if (pullNumber == 20)
+ {
+ AdvanceFiveStarPityAfterNonFiveStar(beginnerState);
+ return RewardGuaranteedBeginnerDistro(0, "20-pull starter guarantee");
+ }
+
+ if (pullNumber == 40)
+ {
+ AdvanceFiveStarPityAfterNonFiveStar(beginnerState);
+ return RewardGuaranteedBeginnerDistro(1, "40-pull starter guarantee");
+ }
+
+ if (pullNumber == 50)
+ {
+ beginnerState.pityCounter = 0;
+ return GachaReward.FutureStandardFiveStar("future standard 5-star distro", "50-pull beginner capstone");
+ }
+
+ if (TryRollFiveStar(beginnerState, out bool fiveStarPityTriggered))
+ {
+ return RewardRandomFiveStar(fiveStarPityTriggered);
+ }
+
+ bool pityTriggered = beginnerState.pityCounter >= FourStarHardPity - 1;
+ bool hitFourStarOrCharacter = pityTriggered || random.NextDouble() < FourStarBaseChance;
+ if (!hitFourStarOrCharacter)
+ {
+ beginnerState.pityCounter++;
+ return GachaReward.Equipment(3, "3-star equipment", false, false);
+ }
+
+ beginnerState.pityCounter = 0;
+ if (bannerPool.Count > 0 && random.NextDouble() < DistroChanceOnFeaturedTier)
+ {
+ DistroDefinition distro = bannerPool[random.Next(bannerPool.Count)];
+ return GachaReward.DistroReward(distro, 4, pityTriggered, false);
+ }
+
+ return GachaReward.Equipment(4, "4-star equipment", pityTriggered, false);
+ }
+
+ private bool TryRollFiveStar(GachaBannerState state, out bool pityTriggered)
+ {
+ state ??= beginnerState;
+ int pityPullNumber = Math.Max(0, state.fiveStarPityCounter) + 1;
+ pityTriggered = pityPullNumber >= FiveStarSoftPityStart;
+
+ double chance = GetFiveStarChance(state);
+ bool hit = chance >= 1d || random.NextDouble() < chance;
+ if (!hit)
+ {
+ state.fiveStarPityCounter++;
+ return false;
+ }
+
+ state.fiveStarPityCounter = 0;
+ state.pityCounter = 0;
+ return true;
+ }
+
+ private static void AdvanceFiveStarPityAfterNonFiveStar(GachaBannerState state)
+ {
+ if (state != null)
+ {
+ state.fiveStarPityCounter++;
+ }
+ }
+
+ private GachaReward RewardRandomFiveStar(bool pityTriggered)
+ {
+ if (bannerPool.Count == 0 || random.NextDouble() >= DistroChanceOnFeaturedTier)
+ {
+ return GachaReward.Equipment(5, "5-star equipment", pityTriggered, false);
+ }
+
+ DistroDefinition distro = bannerPool.Count == 0 ? null : bannerPool[random.Next(bannerPool.Count)];
+ return GachaReward.DistroReward(distro, 5, pityTriggered, false);
+ }
+
+ public bool ResolveFeaturedTierIsDistro()
+ {
+ return random.NextDouble() < DistroChanceOnFeaturedTier;
+ }
+
+ public bool ResolveLimitedFiveStarUsesStandardPool(GachaBannerState limitedState)
+ {
+ if (limitedState == null)
+ {
+ return false;
+ }
+
+ if (limitedState.featuredFiveStarGuaranteed)
+ {
+ limitedState.featuredFiveStarGuaranteed = false;
+ return false;
+ }
+
+ bool useStandardPool = random.NextDouble() < LimitedStandardFiveStarChance;
+ limitedState.featuredFiveStarGuaranteed = useStandardPool;
+ return useStandardPool;
+ }
+
+ private GachaReward RewardGuaranteedBeginnerDistro(int guaranteeIndex, string reason)
+ {
+ beginnerState.pityCounter = 0;
+ DistroDefinition distro = null;
+ if (guaranteeIndex >= 0 && guaranteeIndex < beginnerState.guaranteedDistroIds.Count)
+ {
+ distro = FindBannerPoolDistro(beginnerState.guaranteedDistroIds[guaranteeIndex]);
+ }
+
+ distro ??= bannerPool.Count == 0 ? null : bannerPool[Math.Min(guaranteeIndex, bannerPool.Count - 1)];
+ return distro == null
+ ? GachaReward.Equipment(4, "4-star equipment", true, true)
+ : GachaReward.DistroReward(distro, 4, true, true, reason);
+ }
+
+ private DistroDefinition FindBannerPoolDistro(string id)
+ {
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ return null;
+ }
+
+ for (int i = 0; i < bannerPool.Count; i++)
+ {
+ DistroDefinition distro = bannerPool[i];
+ if (distro != null && string.Equals(distro.Id, id, StringComparison.OrdinalIgnoreCase))
+ {
+ return distro;
+ }
+ }
+
+ return null;
+ }
+
+ private void SetCurrencyBalanceSilently(GachaCurrencyType currencyType, int amount)
+ {
+ 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);
+ state.EnsureLists();
+ GachaBannerState clone = new(state.bannerId)
+ {
+ totalPulls = state.totalPulls,
+ pityCounter = state.pityCounter,
+ fiveStarPityCounter = state.fiveStarPityCounter,
+ exhausted = state.exhausted,
+ featuredFiveStarGuaranteed = state.featuredFiveStarGuaranteed
+ };
+ clone.guaranteedDistroIds.AddRange(state.guaranteedDistroIds);
+ return clone;
}
}
///
- /// Represents the result of a persistent unlock pull.
+ /// Describes one reward entry from one pull.
+ ///
+ public readonly struct GachaReward
+ {
+ private GachaReward(
+ GachaRewardType rewardType,
+ int starRating,
+ string displayName,
+ DistroDefinition distro,
+ bool pityTriggered,
+ bool guaranteed,
+ string guaranteeReason)
+ {
+ RewardType = rewardType;
+ StarRating = starRating;
+ DisplayName = displayName;
+ Distro = distro;
+ PityTriggered = pityTriggered;
+ Guaranteed = guaranteed;
+ GuaranteeReason = guaranteeReason;
+ }
+
+ public GachaRewardType RewardType { get; }
+ public int StarRating { get; }
+ public string DisplayName { get; }
+ public DistroDefinition Distro { get; }
+ public bool PityTriggered { get; }
+ public bool Guaranteed { get; }
+ public string GuaranteeReason { get; }
+
+ public static GachaReward Equipment(int starRating, string displayName, bool pityTriggered, bool guaranteed)
+ {
+ return new GachaReward(GachaRewardType.Equipment, starRating, displayName, null, pityTriggered, guaranteed, null);
+ }
+
+ public static GachaReward DistroReward(DistroDefinition distro, int starRating, bool pityTriggered, bool guaranteed, string guaranteeReason = null)
+ {
+ string displayName = distro == null ? "unknown distro" : DistroDisplayName(distro);
+ return new GachaReward(GachaRewardType.Distro, starRating, displayName, distro, pityTriggered, guaranteed, guaranteeReason);
+ }
+
+ public static GachaReward FutureStandardFiveStar(string displayName, string guaranteeReason)
+ {
+ return new GachaReward(GachaRewardType.FutureStandardFiveStarDistro, 5, displayName, null, true, true, guaranteeReason);
+ }
+
+ private static string DistroDisplayName(DistroDefinition distro)
+ {
+ return string.IsNullOrWhiteSpace(distro.DisplayName) ? distro.name : distro.DisplayName;
+ }
+ }
+
+ ///
+ /// Represents the result of a banner pull request.
///
public readonly struct GachaPullResult
{
- public GachaPullResult(bool success, string unlockId)
+ public GachaPullResult(
+ bool success,
+ string bannerId,
+ GachaCurrencyType currencyType,
+ int currencySpent,
+ int entropySpent,
+ IReadOnlyList rewards,
+ string failureReason)
{
Success = success;
- UnlockId = unlockId;
+ BannerId = bannerId;
+ CurrencyType = currencyType;
+ CurrencySpent = currencySpent;
+ EntropySpent = entropySpent;
+ Rewards = rewards ?? Array.Empty();
+ FailureReason = failureReason;
+ UnlockId = Rewards.Count > 0 && Rewards[0].Distro != null ? Rewards[0].Distro.Id : null;
}
public bool Success { get; }
+ public string BannerId { get; }
+ public GachaCurrencyType CurrencyType { get; }
+ public int CurrencySpent { get; }
+ public int EntropySpent { get; }
+ public IReadOnlyList Rewards { get; }
+ public string FailureReason { get; }
public string UnlockId { get; }
+
+ public static GachaPullResult Failed(string bannerId, string reason)
+ {
+ return new GachaPullResult(false, bannerId, GachaCurrencyType.StandardPull, 0, 0, Array.Empty(), reason);
+ }
}
}
diff --git a/Assets/Scripts/Meta/GachaService.cs.meta b/Assets/Scripts/Meta/GachaService.cs.meta
deleted file mode 100644
index 098ee49..0000000
--- a/Assets/Scripts/Meta/GachaService.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: f62e697e985a8a44ca26ccb8e853ee66
\ No newline at end of file
diff --git a/Assets/Scripts/Meta/IEventBannerSource.cs.meta b/Assets/Scripts/Meta/IEventBannerSource.cs.meta
deleted file mode 100644
index 6e0db88..0000000
--- a/Assets/Scripts/Meta/IEventBannerSource.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 542dd3144a914ea5b1e0c0e21db5ec6b
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Meta/PlayerCollection.cs b/Assets/Scripts/Meta/PlayerCollection.cs
index a39c10d..69d9959 100644
--- a/Assets/Scripts/Meta/PlayerCollection.cs
+++ b/Assets/Scripts/Meta/PlayerCollection.cs
@@ -13,7 +13,6 @@ namespace KernelPanic.Meta
private int featuredIndex;
// TODO: Populate owned units from SaveService during bootstrap.
- // TODO: Add newly unlocked units from GachaService rewards when pulls exist.
public IReadOnlyList OwnedUnits => ownedUnits;
public DistroDefinition FeaturedUnit => ownedUnits.Count == 0 ? null : ownedUnits[Math.Clamp(featuredIndex, 0, ownedUnits.Count - 1)];
@@ -21,7 +20,7 @@ namespace KernelPanic.Meta
public void Add(DistroDefinition unit)
{
- if (unit == null || ownedUnits.Contains(unit))
+ if (unit == null)
{
return;
}
@@ -32,6 +31,26 @@ namespace KernelPanic.Meta
Changed?.Invoke();
}
+ public int GetOwnedCount(string unitId)
+ {
+ if (string.IsNullOrWhiteSpace(unitId))
+ {
+ return 0;
+ }
+
+ int count = 0;
+ for (int i = 0; i < ownedUnits.Count; i++)
+ {
+ DistroDefinition unit = ownedUnits[i];
+ if (unit != null && string.Equals(unit.Id, unitId, StringComparison.OrdinalIgnoreCase))
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
public void SelectNextFeatured()
{
if (ownedUnits.Count < 2)
diff --git a/Assets/Scripts/Meta/PlayerCollection.cs.meta b/Assets/Scripts/Meta/PlayerCollection.cs.meta
deleted file mode 100644
index eeac422..0000000
--- a/Assets/Scripts/Meta/PlayerCollection.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 4a10ff65b6e9478d9fe638f8a39f8144
diff --git a/Assets/Scripts/Meta/SaveService.cs b/Assets/Scripts/Meta/SaveService.cs
index 3aa4ae2..62d5f8c 100644
--- a/Assets/Scripts/Meta/SaveService.cs
+++ b/Assets/Scripts/Meta/SaveService.cs
@@ -58,8 +58,13 @@ namespace KernelPanic.Meta
public sealed class SaveData
{
public bool starterChosen;
+ public int entropyBalance;
+ public int rootCredits;
+ public int standardPullCurrency;
+ public int limitedPullCurrency;
public List ownedUnitIds = new();
public List bannerPoolIds = new();
+ public GachaBannerState beginnerBannerState = new(GachaService.BeginnerBannerId);
public LastRunLoadoutSaveEntry lastRunLoadout = new();
public static SaveData CreateDefault()
@@ -71,6 +76,9 @@ namespace KernelPanic.Meta
{
ownedUnitIds ??= new List();
bannerPoolIds ??= new List();
+ beginnerBannerState ??= new GachaBannerState(GachaService.BeginnerBannerId);
+ beginnerBannerState.bannerId = GachaService.BeginnerBannerId;
+ beginnerBannerState.EnsureLists();
lastRunLoadout ??= new LastRunLoadoutSaveEntry();
lastRunLoadout.EnsureLists();
}
diff --git a/Assets/Scripts/Meta/SaveService.cs.meta b/Assets/Scripts/Meta/SaveService.cs.meta
deleted file mode 100644
index aa82016..0000000
--- a/Assets/Scripts/Meta/SaveService.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 752a83f338afd024b8e728a1921e38ee
\ No newline at end of file
diff --git a/Assets/Scripts/Run.meta b/Assets/Scripts/Run.meta
deleted file mode 100644
index c0ea1d4..0000000
--- a/Assets/Scripts/Run.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 9ae21840b594d4147a8f5497c992b4fa
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Run/RewardService.cs.meta b/Assets/Scripts/Run/RewardService.cs.meta
deleted file mode 100644
index 7277a39..0000000
--- a/Assets/Scripts/Run/RewardService.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: a06eada6fbafd6e44ae1d21238a64750
\ No newline at end of file
diff --git a/Assets/Scripts/Run/RunConfig.cs.meta b/Assets/Scripts/Run/RunConfig.cs.meta
deleted file mode 100644
index c8b050b..0000000
--- a/Assets/Scripts/Run/RunConfig.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 0c3d34a2ce0e53b4b8f9a77feb60fb95
\ No newline at end of file
diff --git a/Assets/Scripts/Run/RunManager.cs.meta b/Assets/Scripts/Run/RunManager.cs.meta
deleted file mode 100644
index 304b6b9..0000000
--- a/Assets/Scripts/Run/RunManager.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 234cc6c5ca577b64ea44020f5514cebe
\ No newline at end of file
diff --git a/Assets/Scripts/UI.meta b/Assets/Scripts/UI.meta
deleted file mode 100644
index 649c90c..0000000
--- a/Assets/Scripts/UI.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 84a74d1de7c84009a2cf84394e8a9fa2
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/UI/AsciiArtFitter.cs.meta b/Assets/Scripts/UI/AsciiArtFitter.cs.meta
deleted file mode 100644
index 71deae5..0000000
--- a/Assets/Scripts/UI/AsciiArtFitter.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 6f677d4ac6de4dca8e92a3efa687ff1b
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/UI/BackgroundLogRingBuffer.cs.meta b/Assets/Scripts/UI/BackgroundLogRingBuffer.cs.meta
deleted file mode 100644
index 44de24e..0000000
--- a/Assets/Scripts/UI/BackgroundLogRingBuffer.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 03ad9dd5bf1d46ee9cd2a6f54d17b5fa
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/UI/BootLogCopy.cs.meta b/Assets/Scripts/UI/BootLogCopy.cs.meta
deleted file mode 100644
index 9499cb7..0000000
--- a/Assets/Scripts/UI/BootLogCopy.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: e05527a70e234c16af2f9089012721b8
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/UI/DistroArtPresenter.cs.meta b/Assets/Scripts/UI/DistroArtPresenter.cs.meta
deleted file mode 100644
index 5791635..0000000
--- a/Assets/Scripts/UI/DistroArtPresenter.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 4bf334307f22465cad853cb6b3757468
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/UI/FeaturedUnitPanel.cs.meta b/Assets/Scripts/UI/FeaturedUnitPanel.cs.meta
deleted file mode 100644
index f9419c6..0000000
--- a/Assets/Scripts/UI/FeaturedUnitPanel.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: c7f7ef4e16694ed9bd746278b0ddc4d8
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/UI/GachaScreenController.cs b/Assets/Scripts/UI/GachaScreenController.cs
new file mode 100644
index 0000000..5f06901
--- /dev/null
+++ b/Assets/Scripts/UI/GachaScreenController.cs
@@ -0,0 +1,487 @@
+using System;
+using System.Collections.Generic;
+using KernelPanic.Data;
+using KernelPanic.Meta;
+using UnityEngine;
+using UnityEngine.TextCore.Text;
+using UnityEngine.UIElements;
+
+namespace KernelPanic.UI
+{
+ ///
+ /// Renders banner selection and pull controls for the gacha screen.
+ ///
+ [Serializable]
+ public sealed class GachaScreenController
+ {
+ private const string SelectedClassName = "selected";
+
+ private readonly List bannerRows = new();
+ private readonly string[] bannerIds =
+ {
+ GachaService.BeginnerBannerId,
+ GachaService.StandardBannerId,
+ GachaService.LimitedBannerId
+ };
+
+ private VisualElement bannerList;
+ private VisualElement bannerDetail;
+ private DistroDatabase distroDatabase;
+ private FontAsset monospaceFont;
+ private GachaService gachaService;
+ private PlayerCollection playerCollection;
+ private EntropyWallet wallet;
+ private Action onChanged;
+ private Action requestRootCreditExchange;
+ private int selectedBannerIndex;
+ private int pendingPullCount;
+ private int pendingMissingTokens;
+ private bool hasOpened;
+ private string resultBannerId;
+ private string resultText;
+
+ public void Bind(VisualElement root, DistroDatabase database, FontAsset artFont, GachaService service, PlayerCollection collection, EntropyWallet entropyWallet, Action changedCallback, Action rootCreditExchangeCallback)
+ {
+ distroDatabase = database;
+ monospaceFont = artFont;
+ gachaService = service;
+ playerCollection = collection;
+ wallet = entropyWallet;
+ onChanged = changedCallback;
+ requestRootCreditExchange = rootCreditExchangeCallback;
+ bannerList = root.Q("GachaBannerList");
+ bannerDetail = root.Q("GachaBannerDetail");
+ }
+
+ public void Open()
+ {
+ if (!hasOpened)
+ {
+ selectedBannerIndex = 0;
+ hasOpened = true;
+ }
+
+ Refresh();
+ }
+
+ public void Refresh()
+ {
+ if (bannerList == null || bannerDetail == null || gachaService == null)
+ {
+ return;
+ }
+
+ bannerRows.Clear();
+ bannerList.Clear();
+
+ for (int i = 0; i < bannerIds.Length; i++)
+ {
+ int index = i;
+ VisualElement row = BuildBannerRow(bannerIds[i]);
+ row.RegisterCallback(_ => SelectBanner(index));
+ bannerList.Add(row);
+ bannerRows.Add(row);
+ }
+
+ selectedBannerIndex = Mathf.Clamp(selectedBannerIndex, 0, bannerRows.Count - 1);
+ ApplySelectedBanner();
+ }
+
+ public void HandleKeyDown(KeyDownEvent evt)
+ {
+ // Pulls are deliberately click-only to avoid accidental currency spend.
+ }
+
+ private VisualElement BuildBannerRow(string bannerId)
+ {
+ VisualElement row = new();
+ row.AddToClassList("gacha-banner-row");
+
+ row.Add(new Label(">") { name = $"GachaBannerCursor_{bannerId}" });
+ row.ElementAt(0).AddToClassList("command-cursor");
+
+ row.Add(new Label(GetBannerTitle(bannerId)) { name = $"GachaBannerName_{bannerId}" });
+ row.ElementAt(1).AddToClassList("command-text");
+
+ row.Add(new Label(GetBannerStatus(bannerId)) { name = $"GachaBannerStatus_{bannerId}" });
+ row.ElementAt(2).AddToClassList("command-description");
+
+ return row;
+ }
+
+ private void SelectBanner(int index)
+ {
+ if (bannerRows.Count == 0)
+ {
+ return;
+ }
+
+ selectedBannerIndex = (index + bannerRows.Count) % bannerRows.Count;
+ ApplySelectedBanner();
+ }
+
+ private void ApplySelectedBanner()
+ {
+ for (int i = 0; i < bannerRows.Count; i++)
+ {
+ bool selected = i == selectedBannerIndex;
+ bannerRows[i].EnableInClassList(SelectedClassName, selected);
+ bannerRows[i].Q