HollowKnightAltEntrancesAPMod/Mod/Archipelago.HollowKnight/IC/Modules/GiftingModule.cs
2025-10-12 19:15:58 -04:00

160 lines
5.1 KiB
C#

using Archipelago.Gifting.Net.Service;
using Archipelago.Gifting.Net.Traits;
using Archipelago.Gifting.Net.Versioning.Gifts.Current;
using Archipelago.MultiClient.Net;
using ItemChanger;
using ItemChanger.Modules;
using ItemChanger.Tags;
using MenuChanger;
using System.Collections.Generic;
using System.Linq;
namespace Archipelago.HollowKnight.IC.Modules;
public class GiftingModule : Module
{
private static readonly string[] AcceptedTraits = [GiftFlag.Mana, GiftFlag.Life, "Artifact"];
private ArchipelagoSession session => ArchipelagoMod.Instance.session;
private GiftingService giftingService;
public override void Initialize()
{
if (ArchipelagoMod.Instance.GS.EnableGifting)
{
giftingService = new GiftingService(session);
On.GameManager.FinishedEnteringScene += BeginGifting;
}
}
public override void Unload()
{
if (giftingService != null)
{
giftingService.CloseGiftBox();
giftingService.OnNewGift -= ReceiveOrRefundGift;
giftingService = null;
}
}
private async void BeginGifting(On.GameManager.orig_FinishedEnteringScene orig, GameManager self)
{
orig(self);
On.GameManager.FinishedEnteringScene -= BeginGifting;
giftingService.OnNewGift += ReceiveOrRefundGift;
Dictionary<string, Gift> pendingGifts = await giftingService.CheckGiftBoxAsync();
foreach (Gift gift in pendingGifts.Values)
{
ReceiveOrRefundGift(gift);
}
giftingService.OpenGiftBox(false, AcceptedTraits);
}
private void ReceiveOrRefundGift(Gift gift)
{
giftingService.RemoveGiftFromGiftBox(gift.ID);
GiftTrait bestTrait = PickBestMatchingTrait(gift);
if (bestTrait != null)
{
string itemName;
if (bestTrait.Trait == GiftFlag.Mana)
{
// soul refill based on quality. Average is 90 (large totems), below is 54, above is 200, scaling linearly
if (bestTrait.Quality >= 2.2)
{
itemName = ItemNames.Soul_Totem_Path_of_Pain;
}
else if (bestTrait.Quality <= 0.6)
{
itemName = ItemNames.Soul_Totem_B;
}
else
{
itemName = ItemNames.Soul_Totem_A;
}
}
else if (bestTrait.Trait == GiftFlag.Life)
{
// blue hearts based on quality. Average is 2 blue masks, scaling linearly from that
// todo - do we want XL/XS lifeblood for more interesting variance?
if (bestTrait.Quality >= 1.5)
{
itemName = ItemNames.Lifeblood_Cocoon_Large;
}
else
{
itemName = ItemNames.Lifeblood_Cocoon_Small;
}
}
else if (bestTrait.Trait == "Artifact")
{
// relic based on quality (average case is Hallownest seal, scales roughly linearly from that)
if (bestTrait.Quality <= 0.4)
{
itemName = ItemNames.Wanderers_Journal;
}
else if (bestTrait.Quality >= 2.7)
{
itemName = ItemNames.Arcane_Egg;
}
else if (bestTrait.Quality >= 1.8)
{
itemName = ItemNames.Kings_Idol;
}
else
{
itemName = ItemNames.Hallownest_Seal;
}
}
else
{
// safety net in case we update acceptedtraits
ArchipelagoMod.Instance.LogWarn($"Got an unexpected trait {bestTrait} for gift {gift}");
giftingService.RefundGift(gift);
return;
}
string sender = session.Players.GetPlayerName(gift.SenderSlot);
DispatchItem(itemName, gift.Amount, sender);
}
else
{
giftingService.RefundGift(gift);
}
}
private GiftTrait PickBestMatchingTrait(Gift gift)
{
GiftTrait best = null;
foreach (GiftTrait trait in gift.Traits)
{
if (AcceptedTraits.Contains(trait.Trait))
{
if (best == null || trait.Quality > best.Quality)
{
best = trait;
}
}
}
return best;
}
private void DispatchItem(string itemName, int amount, string sender)
{
ThreadSupport.BeginInvoke(() =>
{
for (int i = 0; i < amount; i++)
{
AbstractItem item = Finder.GetItem(itemName);
InteropTag recentItemsTag = item.AddTag<InteropTag>();
recentItemsTag.Message = "RecentItems";
recentItemsTag.Properties["DisplaySource"] = sender;
item.Load();
item.Give(null, ItemNetworkingModule.RemoteGiveInfo);
item.Unload();
}
});
}
}