acdream/src/AcDream.Core/Items/StackMergePlanner.cs
Erik dc1649c493 feat(ui): complete retail item drop branches
Give retained buttons a reusable item-drop seam, wire the toolbar backpack target, and port retail stack-merge legality, capacity clamping, wire dispatch, destination selection, and immediate shortcut rekey notice. Record the live ACE merge gate and keep split quantity under AP-101.

Co-Authored-By: Codex <codex@openai.com>
2026-07-11 09:46:32 +02:00

49 lines
1.6 KiB
C#

using System;
namespace AcDream.Core.Items;
public readonly record struct StackMergeItem(
uint ObjectId,
uint WeenieClassId,
int StackSize,
int MaxStackSize,
int TradeState);
public readonly record struct StackMergePlan(uint SourceObjectId, uint TargetObjectId, uint Amount);
/// <summary>
/// Pure port of <c>ItemHolder::IsMergeAttemptLegal @ 0x00586F30</c> and the
/// transfer-size branch in <c>ItemHolder::AttemptMerge @ 0x005878F0</c>.
/// </summary>
public static class StackMergePlanner
{
public static StackMergePlan? Plan(
in StackMergeItem source,
in StackMergeItem target,
bool readyForInventoryRequest,
int requestedAmount)
{
if (!readyForInventoryRequest
|| source.ObjectId == 0
|| target.ObjectId == 0
|| source.ObjectId == target.ObjectId
|| source.MaxStackSize <= 1
|| target.MaxStackSize <= 1
|| source.TradeState == 1
|| target.TradeState == 1
|| source.WeenieClassId != target.WeenieClassId)
return null;
int targetSize = Math.Max(1, target.StackSize);
int available = target.MaxStackSize - targetSize;
if (available <= 0)
return null;
int sourceSize = Math.Max(1, source.StackSize);
int desired = requestedAmount > 0 ? Math.Min(requestedAmount, sourceSize) : sourceSize;
uint transfer = (uint)Math.Min(desired, available);
return transfer == 0
? null
: new StackMergePlan(source.ObjectId, target.ObjectId, transfer);
}
}