fix: Add CalamDropHelper Utilities to replace strong reference to CalamityMod

This commit is contained in:
Abdulmujeeb Raji 2024-11-10 21:36:50 +00:00
parent 1a77650e1a
commit c9b5ec17e6
Signed by untrusted user: midnadimple
GPG key ID: EB02C582F8C3962B
3 changed files with 153 additions and 1 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
# Auto-generated directories
bin/
obj/
.vs/

View file

@ -5,6 +5,7 @@ using Terraria.ID;
using Terraria.ModLoader;
using Terraria.GameContent.ItemDropRules;
using Continuity.Content.Items;
using continuity.Utilities;
namespace Continuity.Content
{
@ -47,7 +48,10 @@ namespace Continuity.Content
itemLoot.RemoveWhere(rule => rule is OneFromRulesRule);
if (ModLoader.HasMod("CalamityMod"))
{
itemLoot.RemoveWhere(rule => rule is CalamityMod.DropHelper.AllOptionsAtOnceWithPityDropRule);
// NOTE(midnadimple): This line causes build issue in tModloader v2024.9.3.0, i dont think
// this is how you're meant to access other mods' drop rules.
itemLoot.RemoveWhere(rule => rule is CalamDropHelper.AllOptionsAtOnceWithPityDropRule);
if (ModLoader.TryGetMod("CalamityMod", out Mod calamityMod) && calamityMod.TryFind<ModItem>("BlossomFlux", out ModItem BlossomFlux)) {
itemLoot.RemoveWhere(rule => rule is CommonDrop drop
&& drop.itemId == BlossomFlux.Type);

View file

@ -0,0 +1,147 @@
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);
}
}
}
}