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