Added Standard Banner with a 5-star selector
This commit is contained in:
@@ -15,6 +15,9 @@ namespace KernelPanic.Meta
|
||||
public const int BeginnerMaxPulls = 50;
|
||||
public const int BeginnerSinglePullCost = 1;
|
||||
public const int BeginnerTenPullCost = 8;
|
||||
public const int StandardSinglePullCost = 1;
|
||||
public const int StandardTenPullCost = 10;
|
||||
public const int StandardFiveStarSelectorInterval = 200;
|
||||
public const int FourStarHardPity = 10;
|
||||
public const int FiveStarSoftPityStart = 66;
|
||||
public const int FiveStarHardPity = 80;
|
||||
@@ -32,6 +35,7 @@ namespace KernelPanic.Meta
|
||||
private readonly Dictionary<GachaCurrencyType, int> _currencyBalances = new();
|
||||
private readonly Random _random;
|
||||
private GachaBannerState _beginnerState = new(BeginnerBannerId);
|
||||
private GachaBannerState _standardState = new(StandardBannerId);
|
||||
|
||||
public GachaService()
|
||||
: this(new Random())
|
||||
@@ -49,7 +53,9 @@ namespace KernelPanic.Meta
|
||||
public int RootCredits { get; private set; }
|
||||
public IReadOnlyList<DistroDefinition> BannerPool => _bannerPool;
|
||||
public GachaBannerState BeginnerState => _beginnerState;
|
||||
public GachaBannerState StandardState => _standardState;
|
||||
public bool IsBeginnerBannerAvailable => !_beginnerState.exhausted && _beginnerState.totalPulls < BeginnerMaxPulls && _bannerPool.Count > 0;
|
||||
public int PendingStandardFiveStarSelectors => Math.Max(0, _standardState.totalPulls / StandardFiveStarSelectorInterval - _standardState.fiveStarSelectorsClaimed);
|
||||
|
||||
public event Action BannerPoolChanged;
|
||||
public event Action Changed;
|
||||
@@ -74,6 +80,12 @@ namespace KernelPanic.Meta
|
||||
|
||||
_beginnerState.fiveStarPityCounter = Math.Max(0, _beginnerState.fiveStarPityCounter);
|
||||
|
||||
_standardState = data.standardBannerState ?? new GachaBannerState(StandardBannerId);
|
||||
_standardState.bannerId = StandardBannerId;
|
||||
_standardState.EnsureLists();
|
||||
_standardState.fiveStarPityCounter = Math.Max(0, _standardState.fiveStarPityCounter);
|
||||
_standardState.fiveStarSelectorsClaimed = Math.Max(0, _standardState.fiveStarSelectorsClaimed);
|
||||
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
@@ -88,6 +100,7 @@ namespace KernelPanic.Meta
|
||||
data.rootCredits = RootCredits;
|
||||
data.commitsBalance = GetCurrencyBalance(GachaCurrencyType.StandardPull);
|
||||
data.beginnerBannerState = CloneState(_beginnerState);
|
||||
data.standardBannerState = CloneState(_standardState);
|
||||
}
|
||||
|
||||
public int GetCurrencyBalance(GachaCurrencyType currencyType)
|
||||
@@ -195,6 +208,11 @@ namespace KernelPanic.Meta
|
||||
}
|
||||
|
||||
public bool CanAffordPull(int pullCount, EntropyWallet wallet, PullPaymentSource source, out string failureReason)
|
||||
{
|
||||
return CanAffordPull(pullCount, wallet, source, BeginnerBannerId, out failureReason);
|
||||
}
|
||||
|
||||
public bool CanAffordPull(int pullCount, EntropyWallet wallet, PullPaymentSource source, string bannerId, out string failureReason)
|
||||
{
|
||||
if (pullCount != 1 && pullCount != 10)
|
||||
{
|
||||
@@ -202,20 +220,28 @@ namespace KernelPanic.Meta
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsBeginnerBannerAvailable)
|
||||
if (bannerId == BeginnerBannerId)
|
||||
{
|
||||
failureReason = "beginner banner unavailable";
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (bannerId != StandardBannerId)
|
||||
{
|
||||
failureReason = $"banner '{bannerId}' not implemented yet";
|
||||
return false;
|
||||
}
|
||||
|
||||
int remainingPulls = BeginnerMaxPulls - _beginnerState.totalPulls;
|
||||
if (pullCount > remainingPulls)
|
||||
{
|
||||
failureReason = $"only {remainingPulls} beginner pull(s) remain";
|
||||
return false;
|
||||
}
|
||||
|
||||
int cost = GetBeginnerPullCost(pullCount);
|
||||
int cost = GetPullCost(bannerId, pullCount);
|
||||
if (source == PullPaymentSource.Commits && GetCurrencyBalance(GachaCurrencyType.StandardPull) < cost)
|
||||
{
|
||||
failureReason = $"need {cost} {FormatCurrencyName(GachaCurrencyType.StandardPull)}";
|
||||
@@ -233,16 +259,36 @@ namespace KernelPanic.Meta
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetPullCost(string bannerId, int pullCount)
|
||||
{
|
||||
if (bannerId == BeginnerBannerId)
|
||||
{
|
||||
return GetBeginnerPullCost(pullCount);
|
||||
}
|
||||
|
||||
if (bannerId == StandardBannerId)
|
||||
{
|
||||
return pullCount == 10 ? StandardTenPullCost : StandardSinglePullCost;
|
||||
}
|
||||
|
||||
return pullCount;
|
||||
}
|
||||
|
||||
public bool PayForPull(int pullCount, EntropyWallet wallet, PullPaymentSource source, out int commitsSpent, out int entropySpent, out string failureReason)
|
||||
{
|
||||
return PayForPull(pullCount, wallet, source, BeginnerBannerId, out commitsSpent, out entropySpent, out failureReason);
|
||||
}
|
||||
|
||||
public bool PayForPull(int pullCount, EntropyWallet wallet, PullPaymentSource source, string bannerId, out int commitsSpent, out int entropySpent, out string failureReason)
|
||||
{
|
||||
commitsSpent = 0;
|
||||
entropySpent = 0;
|
||||
if (!CanAffordPull(pullCount, wallet, source, out failureReason))
|
||||
if (!CanAffordPull(pullCount, wallet, source, bannerId, out failureReason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int cost = GetBeginnerPullCost(pullCount);
|
||||
int cost = GetPullCost(bannerId, pullCount);
|
||||
if (source == PullPaymentSource.Commits)
|
||||
{
|
||||
commitsSpent = cost;
|
||||
@@ -301,6 +347,82 @@ namespace KernelPanic.Meta
|
||||
return new GachaPullResult(true, BeginnerBannerId, GachaCurrencyType.StandardPull, commitSpend, entropySpent, rewards, null);
|
||||
}
|
||||
|
||||
public GachaPullResult PerformStandardPull(int pullCount, EntropyWallet wallet, PullPaymentSource source)
|
||||
{
|
||||
if (!CanAffordPull(pullCount, wallet, source, StandardBannerId, out string failureReason))
|
||||
{
|
||||
return GachaPullResult.Failed(StandardBannerId, failureReason);
|
||||
}
|
||||
|
||||
if (!PayForPull(pullCount, wallet, source, StandardBannerId, out int commitSpend, out int entropySpent, out failureReason))
|
||||
{
|
||||
return GachaPullResult.Failed(StandardBannerId, failureReason);
|
||||
}
|
||||
|
||||
List<GachaReward> rewards = new();
|
||||
for (int i = 0; i < pullCount; i++)
|
||||
{
|
||||
_standardState.totalPulls++;
|
||||
rewards.Add(RollGenericReward(_standardState));
|
||||
}
|
||||
|
||||
Changed?.Invoke();
|
||||
return new GachaPullResult(true, StandardBannerId, GachaCurrencyType.StandardPull, commitSpend, entropySpent, rewards, null);
|
||||
}
|
||||
|
||||
public int GetStandardFiveStarSelectorProgress()
|
||||
{
|
||||
return _standardState.totalPulls % StandardFiveStarSelectorInterval;
|
||||
}
|
||||
|
||||
public bool TryClaimStandardFiveStarSelector(out string failureReason)
|
||||
{
|
||||
if (PendingStandardFiveStarSelectors <= 0)
|
||||
{
|
||||
failureReason = "no 5-star selector available";
|
||||
return false;
|
||||
}
|
||||
|
||||
_standardState.fiveStarSelectorsClaimed++;
|
||||
failureReason = null;
|
||||
Changed?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
public IReadOnlyList<DistroDefinition> GetStandardFiveStarCharacterChoices()
|
||||
{
|
||||
List<DistroDefinition> choices = new();
|
||||
for (int i = 0; i < _bannerPool.Count; i++)
|
||||
{
|
||||
DistroDefinition distro = _bannerPool[i];
|
||||
if (distro != null && GetDistroStars(distro) == 5)
|
||||
{
|
||||
choices.Add(distro);
|
||||
}
|
||||
}
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
public IReadOnlyList<PackageDefinition> GetStandardFiveStarEquipmentChoices(PackageDatabase packageDatabase)
|
||||
{
|
||||
List<PackageDefinition> choices = new();
|
||||
if (packageDatabase != null)
|
||||
{
|
||||
IReadOnlyList<PackageDefinition> packages = packageDatabase.AllPackages;
|
||||
for (int i = 0; i < packages.Count; i++)
|
||||
{
|
||||
PackageDefinition package = packages[i];
|
||||
if (package != null && package.Rarity == 5)
|
||||
{
|
||||
choices.Add(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
public static string FormatCurrencyName(GachaCurrencyType currencyType)
|
||||
{
|
||||
return currencyType switch
|
||||
@@ -372,20 +494,25 @@ namespace KernelPanic.Meta
|
||||
return RewardGuaranteedBeginnerFiveStar("50-pull beginner guarantee");
|
||||
}
|
||||
|
||||
if (TryRollFiveStar(_beginnerState, out bool fiveStarPityTriggered))
|
||||
return RollGenericReward(_beginnerState);
|
||||
}
|
||||
|
||||
private GachaReward RollGenericReward(GachaBannerState state)
|
||||
{
|
||||
if (TryRollFiveStar(state, out bool fiveStarPityTriggered))
|
||||
{
|
||||
return RewardRandomFiveStar(fiveStarPityTriggered);
|
||||
}
|
||||
|
||||
bool pityTriggered = _beginnerState.pityCounter >= FourStarHardPity - 1;
|
||||
bool pityTriggered = state.pityCounter >= FourStarHardPity - 1;
|
||||
bool hitFourStarOrCharacter = pityTriggered || _random.NextDouble() < FourStarBaseChance;
|
||||
if (!hitFourStarOrCharacter)
|
||||
{
|
||||
_beginnerState.pityCounter++;
|
||||
state.pityCounter++;
|
||||
return GachaReward.Package(3, "3-star package", false, false);
|
||||
}
|
||||
|
||||
_beginnerState.pityCounter = 0;
|
||||
state.pityCounter = 0;
|
||||
if (_bannerPool.Count > 0 && _random.NextDouble() < DistroChanceOnFeaturedTier)
|
||||
{
|
||||
DistroDefinition distro = FindRandomBannerPoolDistroByStars(4);
|
||||
@@ -560,7 +687,8 @@ namespace KernelPanic.Meta
|
||||
pityCounter = state.pityCounter,
|
||||
fiveStarPityCounter = state.fiveStarPityCounter,
|
||||
exhausted = state.exhausted,
|
||||
featuredFiveStarGuaranteed = state.featuredFiveStarGuaranteed
|
||||
featuredFiveStarGuaranteed = state.featuredFiveStarGuaranteed,
|
||||
fiveStarSelectorsClaimed = state.fiveStarSelectorsClaimed
|
||||
};
|
||||
clone.guaranteedDistroIds.AddRange(state.guaranteedDistroIds);
|
||||
return clone;
|
||||
|
||||
Reference in New Issue
Block a user