Implement animations, pipeline for duplicates and merging.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace KernelPanic.Meta
|
||||
{
|
||||
public static class DistroVersionCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, string[]> ReleaseLabels = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["ubuntu"] = new[] { "20.04", "21.04", "22.04", "23.04", "24.04" },
|
||||
["fedora"] = new[] { "38", "39", "40", "41", "42" },
|
||||
["mint"] = new[] { "21", "21.2", "21.3", "22", "22.1" }
|
||||
};
|
||||
|
||||
public static string GetReleaseLabel(string unitId, int version)
|
||||
{
|
||||
int safeVersion = Math.Max(1, Math.Min(GachaTuning.MaxVersion, version));
|
||||
if (!string.IsNullOrWhiteSpace(unitId) && ReleaseLabels.TryGetValue(unitId, out string[] labels) && safeVersion <= labels.Length)
|
||||
{
|
||||
return labels[safeVersion - 1];
|
||||
}
|
||||
|
||||
return $"V{safeVersion}";
|
||||
}
|
||||
|
||||
public static string GetEffectSummary(string unitId, int version)
|
||||
{
|
||||
int safeVersion = Math.Max(1, Math.Min(GachaTuning.MaxVersion, version));
|
||||
return (unitId ?? string.Empty).ToLowerInvariant() switch
|
||||
{
|
||||
"ubuntu" => safeVersion switch
|
||||
{
|
||||
1 => "start of turn: apt update chooses the cheaper of top 2 draw cards",
|
||||
2 => "apt update looks at top 3 instead of top 2",
|
||||
3 => "ask ubuntu draws 3 instead of 2",
|
||||
4 => "apt update card costs 1 less this turn",
|
||||
5 => "once per combat, empty hand refills up to RAM",
|
||||
_ => "--"
|
||||
},
|
||||
"fedora" => safeVersion switch
|
||||
{
|
||||
1 => "first card each turn costs 1 less and deals +50%, with crash risk",
|
||||
2 => "first-card damage bonus rises to +75%",
|
||||
3 => "rawhide TODO: double permanent damage growth on non-crash first-card turn",
|
||||
4 => "crashes grant +1 Cycle that turn",
|
||||
5 => "first two cards each turn get the discount, bonus, and independent crash rolls",
|
||||
_ => "--"
|
||||
},
|
||||
"mint" => safeVersion switch
|
||||
{
|
||||
1 => "random values roll max; no crits; ignores multiplicative damage buffs",
|
||||
2 => "fixed effect values are treated as +2",
|
||||
3 => "timeshift snapshot TODO: stronger restore when delayed restore exists",
|
||||
4 => "flat +damage can apply; multiplicative crit/mult remains ignored",
|
||||
5 => "TODO: re-resolve reduced effect when enemy reduction/coercion exists",
|
||||
_ => "--"
|
||||
},
|
||||
_ => safeVersion == 1 ? "base release" : $"TODO: V{safeVersion} release effect"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace KernelPanic.Meta
|
||||
{
|
||||
/// <summary>
|
||||
/// Tunable gacha progression amounts. Kept in code until designers need asset-driven economy tuning.
|
||||
/// </summary>
|
||||
public static class GachaTuning
|
||||
{
|
||||
public const int MaxVersion = 5;
|
||||
public const int MergesPerDupe = 20;
|
||||
public const int MergesOnCharacterBonus = 10;
|
||||
public const float MergesMaxVersionOverflowMultiplier = 1.5f;
|
||||
public static int DupeConsolationBandwidth => 0;
|
||||
public static readonly int[] VersionCosts = { 50, 120, 250, 500 };
|
||||
|
||||
public static int GetVersionUpgradeCost(int targetVersion)
|
||||
{
|
||||
if (targetVersion <= 1 || targetVersion > MaxVersion)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index = targetVersion - 2;
|
||||
return index >= 0 && index < VersionCosts.Length ? VersionCosts[index] : VersionCosts[VersionCosts.Length - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ namespace KernelPanic.Meta
|
||||
public sealed class PlayerCollection
|
||||
{
|
||||
private readonly List<DistroDefinition> _ownedUnits = new();
|
||||
private readonly Dictionary<string, int> _versions = new(StringComparer.OrdinalIgnoreCase);
|
||||
private int _featuredIndex;
|
||||
|
||||
// TODO: Populate owned units from SaveService during bootstrap.
|
||||
@@ -19,16 +20,46 @@ namespace KernelPanic.Meta
|
||||
public event Action Changed;
|
||||
|
||||
public void Add(DistroDefinition unit)
|
||||
{
|
||||
Add(unit, 1);
|
||||
}
|
||||
|
||||
public void Add(DistroDefinition unit, int version)
|
||||
{
|
||||
Add(unit, version, true);
|
||||
}
|
||||
|
||||
public void AddSilently(DistroDefinition unit, int version)
|
||||
{
|
||||
Add(unit, version, false);
|
||||
}
|
||||
|
||||
private void Add(DistroDefinition unit, int version, bool notify)
|
||||
{
|
||||
if (unit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsOwned(unit.Id))
|
||||
{
|
||||
SetVersion(unit.Id, version, notify);
|
||||
return;
|
||||
}
|
||||
|
||||
// Persistence is wired by the owner so this domain class stays IO-free.
|
||||
_ownedUnits.Add(unit);
|
||||
_versions[unit.Id] = Math.Max(1, version);
|
||||
_featuredIndex = Math.Clamp(_featuredIndex, 0, _ownedUnits.Count - 1);
|
||||
Changed?.Invoke();
|
||||
if (notify)
|
||||
{
|
||||
Changed?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsOwned(string unitId)
|
||||
{
|
||||
return GetOwnedCount(unitId) > 0;
|
||||
}
|
||||
|
||||
public int GetOwnedCount(string unitId)
|
||||
@@ -51,6 +82,46 @@ namespace KernelPanic.Meta
|
||||
return count;
|
||||
}
|
||||
|
||||
public int GetVersion(string unitId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(unitId))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _versions.TryGetValue(unitId, out int version) ? Math.Max(1, version) : IsOwned(unitId) ? 1 : 0;
|
||||
}
|
||||
|
||||
public void SetVersion(string unitId, int version)
|
||||
{
|
||||
SetVersion(unitId, version, true);
|
||||
}
|
||||
|
||||
public void SetVersionSilently(string unitId, int version)
|
||||
{
|
||||
SetVersion(unitId, version, false);
|
||||
}
|
||||
|
||||
private void SetVersion(string unitId, int version, bool notify)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(unitId) || !IsOwned(unitId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int safeVersion = Math.Max(1, version);
|
||||
if (_versions.TryGetValue(unitId, out int oldVersion) && oldVersion == safeVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_versions[unitId] = safeVersion;
|
||||
if (notify)
|
||||
{
|
||||
Changed?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectNextFeatured()
|
||||
{
|
||||
if (_ownedUnits.Count < 2)
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using KernelPanic.Core;
|
||||
using KernelPanic.Data;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KernelPanic.Meta
|
||||
{
|
||||
public static class PullResolver
|
||||
{
|
||||
public static event Action<PullResolutionResult> PullResolved;
|
||||
|
||||
public static PullResolutionResult Resolve(IEnumerable<DistroDefinition> pullResults, PullResolutionContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
context.SaveData ??= SaveData.CreateDefault();
|
||||
context.SaveData.EnsureLists();
|
||||
|
||||
List<PullResolutionOutcome> outcomes = new();
|
||||
if (pullResults != null)
|
||||
{
|
||||
foreach (DistroDefinition unit in pullResults)
|
||||
{
|
||||
outcomes.Add(ResolveOne(unit, context));
|
||||
}
|
||||
}
|
||||
|
||||
PullResolutionResult result = new(outcomes);
|
||||
PullResolved?.Invoke(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PullResolutionResult Resolve(IEnumerable<string> pullResultIds, PullResolutionContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
List<DistroDefinition> units = new();
|
||||
if (pullResultIds != null)
|
||||
{
|
||||
foreach (string id in pullResultIds)
|
||||
{
|
||||
units.Add(context.DistroDatabase == null ? null : context.DistroDatabase.FindById(id));
|
||||
}
|
||||
}
|
||||
|
||||
return Resolve(units, context);
|
||||
}
|
||||
|
||||
public static PullResolutionResult ResolveAndSave(IEnumerable<DistroDefinition> pullResults, PullResolutionContext context, SaveService saveService)
|
||||
{
|
||||
PullResolutionResult result = Resolve(pullResults, context);
|
||||
saveService?.Save(context.SaveData);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PullResolutionResult ResolveAndSave(IEnumerable<string> pullResultIds, PullResolutionContext context, SaveService saveService)
|
||||
{
|
||||
PullResolutionResult result = Resolve(pullResultIds, context);
|
||||
saveService?.Save(context.SaveData);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static PullResolutionOutcome ResolveOne(DistroDefinition unit, PullResolutionContext context)
|
||||
{
|
||||
if (unit == null || string.IsNullOrWhiteSpace(unit.Id))
|
||||
{
|
||||
return new PullResolutionOutcome(null, PullOutcomeKind.Invalid, 0, Array.Empty<Language>());
|
||||
}
|
||||
|
||||
OwnedUnitSaveEntry owned = context.SaveData.FindOwnedUnit(unit.Id);
|
||||
bool ownedInCollection = context.Collection != null && context.Collection.IsOwned(unit.Id);
|
||||
if (owned == null && !ownedInCollection)
|
||||
{
|
||||
IReadOnlyList<Language> before = SnapshotUnlockedLanguages(context.Collection);
|
||||
context.SaveData.AddOwnedUnit(unit.Id, 1);
|
||||
context.Collection?.AddSilently(unit, 1);
|
||||
IReadOnlyList<Language> unlocked = NewlyUnlockedLanguages(before, context.Collection, unit);
|
||||
return new PullResolutionOutcome(unit.Id, PullOutcomeKind.Granted, 0, unlocked);
|
||||
}
|
||||
|
||||
owned ??= context.SaveData.AddOwnedUnit(unit.Id, context.Collection?.GetVersion(unit.Id) ?? 1);
|
||||
int version = Mathf.Clamp(owned.version, 1, GachaTuning.MaxVersion);
|
||||
owned.version = version;
|
||||
bool overflow = version >= GachaTuning.MaxVersion;
|
||||
int merges = CalculateDupeMerges(overflow, unit.Id, context.FocusUnitId);
|
||||
context.SaveData.merges = Math.Max(0, context.SaveData.merges) + merges;
|
||||
if (GachaTuning.DupeConsolationBandwidth > 0)
|
||||
{
|
||||
context.SaveData.standardPullCurrency += GachaTuning.DupeConsolationBandwidth;
|
||||
}
|
||||
|
||||
context.Collection?.AddSilently(unit, version);
|
||||
return new PullResolutionOutcome(unit.Id, overflow ? PullOutcomeKind.DupeOverflow : PullOutcomeKind.Dupe, merges, Array.Empty<Language>());
|
||||
}
|
||||
|
||||
private static int CalculateDupeMerges(bool overflow, string unitId, string focusUnitId)
|
||||
{
|
||||
float baseAmount = GachaTuning.MergesPerDupe;
|
||||
if (overflow)
|
||||
{
|
||||
baseAmount *= GachaTuning.MergesMaxVersionOverflowMultiplier;
|
||||
}
|
||||
|
||||
int merges = Mathf.RoundToInt(baseAmount);
|
||||
if (!string.IsNullOrWhiteSpace(focusUnitId) && string.Equals(unitId, focusUnitId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
merges += GachaTuning.MergesOnCharacterBonus;
|
||||
}
|
||||
|
||||
return Math.Max(1, merges);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Language> SnapshotUnlockedLanguages(PlayerCollection collection)
|
||||
{
|
||||
List<Language> unlocked = new();
|
||||
if (collection == null)
|
||||
{
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
IReadOnlyList<LanguageCatalogEntry> languages = LanguageCatalog.All;
|
||||
for (int i = 0; i < languages.Count; i++)
|
||||
{
|
||||
if (LanguageUnlock.IsUnlocked(languages[i].Language, collection))
|
||||
{
|
||||
unlocked.Add(languages[i].Language);
|
||||
}
|
||||
}
|
||||
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Language> NewlyUnlockedLanguages(IReadOnlyList<Language> before, PlayerCollection collection, DistroDefinition unit)
|
||||
{
|
||||
List<Language> unlocked = new();
|
||||
if (collection == null || unit == null)
|
||||
{
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
AddNewlyUnlockedLanguage(unit.PrimaryLanguage, before, collection, unlocked);
|
||||
AddNewlyUnlockedLanguage(unit.SecondaryLanguage, before, collection, unlocked);
|
||||
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
private static void AddNewlyUnlockedLanguage(Language language, IReadOnlyList<Language> before, PlayerCollection collection, List<Language> unlocked)
|
||||
{
|
||||
if (!ContainsLanguage(unlocked, language) && !ContainsLanguage(before, language) && LanguageUnlock.IsUnlocked(language, collection))
|
||||
{
|
||||
unlocked.Add(language);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ContainsLanguage(IReadOnlyList<Language> languages, Language language)
|
||||
{
|
||||
for (int i = 0; i < languages.Count; i++)
|
||||
{
|
||||
if (languages[i] == language)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PullResolutionContext
|
||||
{
|
||||
public PullResolutionContext(SaveData saveData, PlayerCollection collection, DistroDatabase distroDatabase = null, string focusUnitId = null)
|
||||
{
|
||||
SaveData = saveData;
|
||||
Collection = collection;
|
||||
DistroDatabase = distroDatabase;
|
||||
FocusUnitId = focusUnitId;
|
||||
}
|
||||
|
||||
public SaveData SaveData { get; set; }
|
||||
public PlayerCollection Collection { get; }
|
||||
public DistroDatabase DistroDatabase { get; }
|
||||
public string FocusUnitId { get; }
|
||||
}
|
||||
|
||||
public sealed class PullResolutionResult
|
||||
{
|
||||
public PullResolutionResult(IReadOnlyList<PullResolutionOutcome> outcomes)
|
||||
{
|
||||
Outcomes = outcomes ?? Array.Empty<PullResolutionOutcome>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<PullResolutionOutcome> Outcomes { get; }
|
||||
}
|
||||
|
||||
public readonly struct PullResolutionOutcome
|
||||
{
|
||||
public PullResolutionOutcome(string unitId, PullOutcomeKind kind, int mergesAwarded, IReadOnlyList<Language> languagesNewlyUnlocked)
|
||||
{
|
||||
UnitId = unitId;
|
||||
Kind = kind;
|
||||
MergesAwarded = mergesAwarded;
|
||||
LanguagesNewlyUnlocked = languagesNewlyUnlocked ?? Array.Empty<Language>();
|
||||
}
|
||||
|
||||
public string UnitId { get; }
|
||||
public PullOutcomeKind Kind { get; }
|
||||
public int MergesAwarded { get; }
|
||||
public IReadOnlyList<Language> LanguagesNewlyUnlocked { get; }
|
||||
}
|
||||
|
||||
public enum PullOutcomeKind
|
||||
{
|
||||
Invalid,
|
||||
Granted,
|
||||
Dupe,
|
||||
DupeOverflow
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,8 @@ namespace KernelPanic.Meta
|
||||
public int rootCredits = GachaService.TestRootCreditsBalance;
|
||||
public int standardPullCurrency;
|
||||
public int limitedPullCurrency;
|
||||
public int merges;
|
||||
public List<OwnedUnitSaveEntry> ownedUnits = new();
|
||||
public List<string> ownedUnitIds = new();
|
||||
public List<string> bannerPoolIds = new();
|
||||
public GachaBannerState beginnerBannerState = new(GachaService.BeginnerBannerId);
|
||||
@@ -74,6 +76,7 @@ namespace KernelPanic.Meta
|
||||
|
||||
public void EnsureLists()
|
||||
{
|
||||
ownedUnits ??= new List<OwnedUnitSaveEntry>();
|
||||
ownedUnitIds ??= new List<string>();
|
||||
bannerPoolIds ??= new List<string>();
|
||||
beginnerBannerState ??= new GachaBannerState(GachaService.BeginnerBannerId);
|
||||
@@ -81,7 +84,114 @@ namespace KernelPanic.Meta
|
||||
beginnerBannerState.EnsureLists();
|
||||
lastRunLoadout ??= new LastRunLoadoutSaveEntry();
|
||||
lastRunLoadout.EnsureLists();
|
||||
merges = Math.Max(0, merges);
|
||||
MigrateLegacyOwnedUnitIds();
|
||||
NormalizeOwnedUnits();
|
||||
}
|
||||
|
||||
public OwnedUnitSaveEntry FindOwnedUnit(string id)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
EnsureLists();
|
||||
for (int i = 0; i < ownedUnits.Count; i++)
|
||||
{
|
||||
OwnedUnitSaveEntry entry = ownedUnits[i];
|
||||
if (entry != null && string.Equals(entry.id, id, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsUnitOwned(string id)
|
||||
{
|
||||
return FindOwnedUnit(id) != null;
|
||||
}
|
||||
|
||||
public OwnedUnitSaveEntry AddOwnedUnit(string id, int version)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
EnsureLists();
|
||||
OwnedUnitSaveEntry existing = FindOwnedUnit(id);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.version = Math.Max(1, Math.Min(GachaTuning.MaxVersion, existing.version));
|
||||
return existing;
|
||||
}
|
||||
|
||||
OwnedUnitSaveEntry entry = new()
|
||||
{
|
||||
id = id,
|
||||
version = Math.Max(1, Math.Min(GachaTuning.MaxVersion, version))
|
||||
};
|
||||
ownedUnits.Add(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void MigrateLegacyOwnedUnitIds()
|
||||
{
|
||||
for (int i = 0; i < ownedUnitIds.Count; i++)
|
||||
{
|
||||
string id = ownedUnitIds[i];
|
||||
if (string.IsNullOrWhiteSpace(id) || HasOwnedUnitEntry(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ownedUnits.Add(new OwnedUnitSaveEntry
|
||||
{
|
||||
id = id,
|
||||
version = 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void NormalizeOwnedUnits()
|
||||
{
|
||||
HashSet<string> seen = new(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = ownedUnits.Count - 1; i >= 0; i--)
|
||||
{
|
||||
OwnedUnitSaveEntry entry = ownedUnits[i];
|
||||
if (entry == null || string.IsNullOrWhiteSpace(entry.id) || !seen.Add(entry.id))
|
||||
{
|
||||
ownedUnits.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.version = Math.Max(1, Math.Min(GachaTuning.MaxVersion, entry.version));
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasOwnedUnitEntry(string id)
|
||||
{
|
||||
for (int i = 0; i < ownedUnits.Count; i++)
|
||||
{
|
||||
OwnedUnitSaveEntry entry = ownedUnits[i];
|
||||
if (entry != null && string.Equals(entry.id, id, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class OwnedUnitSaveEntry
|
||||
{
|
||||
public string id;
|
||||
public int version = 1;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KernelPanic.Meta
|
||||
{
|
||||
public static class VersionUpgrader
|
||||
{
|
||||
public static event Action<VersionUpgradeResult> UnitUpgraded;
|
||||
|
||||
public static bool TryUpgrade(string unitId, SaveData state, PlayerCollection collection, out VersionUpgradeResult result)
|
||||
{
|
||||
result = TryUpgrade(unitId, state, collection);
|
||||
return result.Success;
|
||||
}
|
||||
|
||||
public static VersionUpgradeResult TryUpgradeAndSave(string unitId, SaveData state, PlayerCollection collection, SaveService saveService)
|
||||
{
|
||||
VersionUpgradeResult result = TryUpgrade(unitId, state, collection);
|
||||
if (result.Success)
|
||||
{
|
||||
saveService?.Save(state);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static VersionUpgradeResult TryUpgrade(string unitId, SaveData state, PlayerCollection collection)
|
||||
{
|
||||
state ??= SaveData.CreateDefault();
|
||||
state.EnsureLists();
|
||||
|
||||
OwnedUnitSaveEntry entry = state.FindOwnedUnit(unitId);
|
||||
if (entry == null && collection != null && collection.IsOwned(unitId))
|
||||
{
|
||||
entry = state.AddOwnedUnit(unitId, collection.GetVersion(unitId));
|
||||
}
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
return VersionUpgradeResult.Failed(unitId, VersionUpgradeFailureReason.NotOwned);
|
||||
}
|
||||
|
||||
int currentVersion = Mathf.Clamp(entry.version, 1, GachaTuning.MaxVersion);
|
||||
if (currentVersion >= GachaTuning.MaxVersion)
|
||||
{
|
||||
return VersionUpgradeResult.Failed(unitId, VersionUpgradeFailureReason.MaxVersion, currentVersion, currentVersion, 0);
|
||||
}
|
||||
|
||||
int targetVersion = currentVersion + 1;
|
||||
int cost = GachaTuning.GetVersionUpgradeCost(targetVersion);
|
||||
if (state.merges < cost)
|
||||
{
|
||||
return VersionUpgradeResult.Failed(unitId, VersionUpgradeFailureReason.InsufficientMerges, currentVersion, targetVersion, cost);
|
||||
}
|
||||
|
||||
state.merges -= cost;
|
||||
entry.version = targetVersion;
|
||||
collection?.SetVersionSilently(unitId, targetVersion);
|
||||
|
||||
VersionUpgradeResult result = VersionUpgradeResult.Upgraded(unitId, currentVersion, targetVersion, cost, state.merges);
|
||||
UnitUpgraded?.Invoke(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct VersionUpgradeResult
|
||||
{
|
||||
private VersionUpgradeResult(string unitId, bool success, VersionUpgradeFailureReason failureReason, int fromVersion, int toVersion, int cost, int mergesRemaining)
|
||||
{
|
||||
UnitId = unitId;
|
||||
Success = success;
|
||||
FailureReason = failureReason;
|
||||
FromVersion = fromVersion;
|
||||
ToVersion = toVersion;
|
||||
Cost = cost;
|
||||
MergesRemaining = mergesRemaining;
|
||||
}
|
||||
|
||||
public string UnitId { get; }
|
||||
public bool Success { get; }
|
||||
public VersionUpgradeFailureReason FailureReason { get; }
|
||||
public int FromVersion { get; }
|
||||
public int ToVersion { get; }
|
||||
public int Cost { get; }
|
||||
public int MergesRemaining { get; }
|
||||
|
||||
public static VersionUpgradeResult Upgraded(string unitId, int fromVersion, int toVersion, int cost, int mergesRemaining)
|
||||
{
|
||||
return new VersionUpgradeResult(unitId, true, VersionUpgradeFailureReason.None, fromVersion, toVersion, cost, mergesRemaining);
|
||||
}
|
||||
|
||||
public static VersionUpgradeResult Failed(string unitId, VersionUpgradeFailureReason reason, int fromVersion = 0, int toVersion = 0, int cost = 0)
|
||||
{
|
||||
return new VersionUpgradeResult(unitId, false, reason, fromVersion, toVersion, cost, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public enum VersionUpgradeFailureReason
|
||||
{
|
||||
None,
|
||||
NotOwned,
|
||||
MaxVersion,
|
||||
InsufficientMerges
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user