147 lines
5.6 KiB
C#
147 lines
5.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
using Terraria.GameContent.ItemDropRules;
|
|
using Terraria.Utilities;
|
|
using Terraria;
|
|
|
|
// Repurposed from https://github.com/CalamityTeam/CalamityModPublic/blob/1.4.4/Utilities/DropHelper.cs
|
|
namespace continuity.Utilities
|
|
{
|
|
internal class CalamDropHelper
|
|
{
|
|
public struct Fraction
|
|
{
|
|
internal readonly int numerator;
|
|
internal readonly int denominator;
|
|
|
|
public Fraction(int n, int d)
|
|
{
|
|
numerator = n < 0 ? 0 : n;
|
|
denominator = d <= 0 ? 1 : d;
|
|
}
|
|
|
|
public static implicit operator float(Fraction f) => f.numerator / (float)f.denominator;
|
|
}
|
|
|
|
|
|
public struct WeightedItemStack
|
|
{
|
|
public const float DefaultWeight = 1f;
|
|
public const float MinisiculeWeight = 1E-6f;
|
|
|
|
internal int itemID;
|
|
internal float weight;
|
|
internal int minQuantity;
|
|
internal int maxQuantity;
|
|
|
|
internal WeightedItemStack(int id, float w)
|
|
{
|
|
itemID = id;
|
|
weight = w;
|
|
minQuantity = 1;
|
|
maxQuantity = 1;
|
|
}
|
|
|
|
internal WeightedItemStack(int id, float w, int quantity)
|
|
{
|
|
itemID = id;
|
|
weight = w;
|
|
minQuantity = quantity;
|
|
maxQuantity = quantity;
|
|
}
|
|
|
|
internal WeightedItemStack(int id, float w, int min, int max)
|
|
{
|
|
itemID = id;
|
|
weight = w;
|
|
minQuantity = min;
|
|
maxQuantity = max;
|
|
}
|
|
|
|
internal int ChooseQuantity(UnifiedRandom rng) => rng.Next(minQuantity, maxQuantity + 1);
|
|
|
|
// Allow for implicitly casting integer item IDs into weighted item stacks.
|
|
// Stack size is assumed to be 1. Weight is assumed to be default.
|
|
public static implicit operator WeightedItemStack(int id)
|
|
{
|
|
return new WeightedItemStack(id, DefaultWeight, 1);
|
|
}
|
|
}
|
|
|
|
public class AllOptionsAtOnceWithPityDropRule : IItemDropRule
|
|
{
|
|
public WeightedItemStack[] stacks;
|
|
public Fraction dropRate;
|
|
public bool usesLuck;
|
|
public List<IItemDropRuleChainAttempt> ChainedRules { get; set; }
|
|
|
|
public AllOptionsAtOnceWithPityDropRule(Fraction dropRate, bool luck, params WeightedItemStack[] stacks)
|
|
{
|
|
this.dropRate = dropRate;
|
|
this.stacks = stacks;
|
|
usesLuck = luck;
|
|
ChainedRules = new List<IItemDropRuleChainAttempt>();
|
|
}
|
|
|
|
public AllOptionsAtOnceWithPityDropRule(Fraction dropRate, bool luck, params int[] itemIDs)
|
|
{
|
|
this.dropRate = dropRate;
|
|
stacks = new WeightedItemStack[itemIDs.Length];
|
|
for (int i = 0; i < stacks.Length; ++i)
|
|
stacks[i] = itemIDs[i]; // implicit conversion operator
|
|
usesLuck = luck;
|
|
ChainedRules = new List<IItemDropRuleChainAttempt>();
|
|
}
|
|
|
|
public bool CanDrop(DropAttemptInfo info) => true;
|
|
|
|
public ItemDropAttemptResult TryDroppingItem(DropAttemptInfo info)
|
|
{
|
|
bool droppedAnything = false;
|
|
|
|
// Roll for each drop individually.
|
|
foreach (WeightedItemStack stack in stacks)
|
|
{
|
|
bool rngRoll = usesLuck ? info.player.RollLuck(dropRate.denominator) < dropRate.numerator : info.rng.NextFloat() < dropRate;
|
|
droppedAnything |= rngRoll;
|
|
if (rngRoll)
|
|
CommonCode.DropItem(info, stack.itemID, stack.ChooseQuantity(info.rng));
|
|
}
|
|
|
|
// If everything fails to drop, force drop one item from the set.
|
|
if (!droppedAnything)
|
|
{
|
|
WeightedItemStack stack = info.rng.NextFromList(stacks);
|
|
CommonCode.DropItem(info, stack.itemID, stack.ChooseQuantity(info.rng));
|
|
}
|
|
|
|
// Calamity style drops cannot fail. You will always get at least one item.
|
|
ItemDropAttemptResult result = default;
|
|
result.State = ItemDropAttemptResultState.Success;
|
|
return result;
|
|
}
|
|
|
|
public void ReportDroprates(List<DropRateInfo> drops, DropRateInfoChainFeed ratesInfo)
|
|
{
|
|
int numDrops = stacks.Length;
|
|
float rawDropRate = dropRate;
|
|
// Combinatorics:
|
|
// OPTION 1: [The item drops = Raw Drop Rate]
|
|
// +
|
|
// OPTION 2: [ALL items fail to drop = (1-x)^n] * [This item is chosen as pity = 1/n]
|
|
float dropRateWithPityRoll = rawDropRate + (float)(Math.Pow(1f - rawDropRate, numDrops) * (1f / numDrops));
|
|
float dropRateAdjustedForParent = dropRateWithPityRoll * ratesInfo.parentDroprateChance;
|
|
|
|
// Report the drop rate of each individual item. This calculation includes the fact that each individual item can be guaranteed as pity.
|
|
foreach (WeightedItemStack stack in stacks)
|
|
drops.Add(new DropRateInfo(stack.itemID, stack.minQuantity, stack.maxQuantity, dropRateAdjustedForParent, ratesInfo.conditions));
|
|
|
|
Chains.ReportDroprates(ChainedRules, rawDropRate, drops, ratesInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|