109 lines
5.0 KiB
C
109 lines
5.0 KiB
C
class ERR_MortarSupplyCallback : ScriptedInventoryOperationCallback
|
|
{
|
|
ERR_MortarSupplyTransfer Transfer;
|
|
protected override void OnComplete() { if (Transfer) Transfer.Acknowledge(true); }
|
|
protected override void OnFailed() { if (Transfer) Transfer.Acknowledge(false); }
|
|
}
|
|
|
|
// Exact-item native transaction. Callbacks only record outcomes; the HQ tick
|
|
// authorizes progress after checking actual inventory ownership and the order.
|
|
class ERR_MortarSupplyTransfer
|
|
{
|
|
protected ERR_MortarBattery m_Battery;
|
|
protected IEntity m_Gun;
|
|
protected SCR_AIGroup m_Group;
|
|
protected IEntity m_Donor;
|
|
protected IEntity m_Gunner;
|
|
protected IEntity m_Shell;
|
|
protected InventoryStorageManagerComponent m_Source;
|
|
protected InventoryStorageManagerComponent m_Destination;
|
|
protected BaseInventoryStorageComponent m_Storage;
|
|
protected ref ERR_MortarSupplyCallback m_Callback;
|
|
protected int m_Order;
|
|
protected int m_Acknowledgement;
|
|
protected float m_Deadline;
|
|
protected bool m_Accepted;
|
|
protected bool m_Invalidated;
|
|
protected bool m_Resolved;
|
|
protected bool m_Ready;
|
|
void ERR_MortarSupplyTransfer(ERR_MortarBattery battery, IEntity donor, IEntity gunner, IEntity shell, BaseInventoryStorageComponent storage, float now)
|
|
{
|
|
m_Battery = battery; m_Gun = battery.Gun; m_Group = battery.Group;
|
|
m_Donor = donor; m_Gunner = gunner; m_Shell = shell; m_Storage = storage;
|
|
m_Order = battery.OrderId; m_Deadline = now + 8000;
|
|
m_Source = InventoryStorageManagerComponent.Cast(donor.FindComponent(InventoryStorageManagerComponent));
|
|
m_Destination = InventoryStorageManagerComponent.Cast(gunner.FindComponent(InventoryStorageManagerComponent));
|
|
}
|
|
static bool Authority(bool valid, bool invalidated, int capturedOrder, int currentOrder)
|
|
{
|
|
return valid && !invalidated && capturedOrder == currentOrder;
|
|
}
|
|
static bool Proven(bool callbackComplete, bool sourceContains, bool destinationContains, bool exactSlot)
|
|
{
|
|
return callbackComplete && !sourceContains && destinationContains && exactSlot;
|
|
}
|
|
protected bool Contains(InventoryStorageManagerComponent inventory)
|
|
{
|
|
if (!inventory || !m_Shell) return false;
|
|
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
|
|
return items.Contains(m_Shell);
|
|
}
|
|
protected bool Valid(float now)
|
|
{
|
|
if (!m_Battery || !Authority(m_Battery.SupplyAuthorized(now), m_Invalidated, m_Order, m_Battery.OrderId)) return false;
|
|
if (m_Battery.Gun != m_Gun || m_Battery.Group != m_Group || !m_Donor || !m_Gunner || m_Donor == m_Gunner || !m_Shell || !m_Source || !m_Destination || !m_Storage) return false;
|
|
if (!ERR_Actors.CanFight(m_Donor) || !ERR_Actors.CanFight(m_Gunner) || ERR_Actors.Group(m_Donor) != m_Group || ERR_Actors.Group(m_Gunner) != m_Group) return false;
|
|
Faction faction = m_Group.GetFaction();
|
|
if (SCR_FactionManager.SGetFaction(m_Donor) != faction || SCR_FactionManager.SGetFaction(m_Gunner) != faction || vector.Distance(m_Donor.GetOrigin(), m_Gunner.GetOrigin()) > 12) return false;
|
|
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Gun.FindComponent(SCR_AIVehicleUsageComponent));
|
|
if (!usage || !usage.GetTurretCompartmentSlot()) return false;
|
|
IEntity occupant = usage.GetTurretCompartmentSlot().GetOccupant();
|
|
return !occupant || occupant == m_Gunner;
|
|
}
|
|
void Acknowledge(bool success)
|
|
{
|
|
if (m_Acknowledgement != 0) return;
|
|
if (success) m_Acknowledgement = 1;
|
|
else m_Acknowledgement = -1;
|
|
}
|
|
bool Begin(float now)
|
|
{
|
|
if (m_Callback || !Replication.IsServer() || !Valid(now) || !Contains(m_Source) || Contains(m_Destination)) return false;
|
|
m_Callback = new ERR_MortarSupplyCallback(); m_Callback.Transfer = this;
|
|
// Do not use callback.GetItem(): the native contract excludes Move operations.
|
|
m_Accepted = m_Source.TryMoveItemToStorage(m_Shell, m_Storage, -1, m_Callback);
|
|
if (!m_Accepted) { Acknowledge(false); m_Resolved = true; }
|
|
return m_Accepted;
|
|
}
|
|
void Invalidate() { m_Invalidated = true; m_Ready = false; }
|
|
bool Unresolved() { return m_Accepted && !m_Resolved; }
|
|
int CapturedOrder() { return m_Order; }
|
|
ERR_MortarSupplyState Poll(float now)
|
|
{
|
|
if (!Replication.IsServer()) return ERR_MortarSupplyState.FAILED;
|
|
bool valid = Valid(now);
|
|
bool sourceContains = Contains(m_Source);
|
|
bool destinationContains = Contains(m_Destination);
|
|
bool exactSlot;
|
|
InventoryItemComponent item;
|
|
if (m_Shell) item = InventoryItemComponent.Cast(m_Shell.FindComponent(InventoryItemComponent));
|
|
if (item && item.GetParentSlot()) exactSlot = item.GetParentSlot().GetStorage() == m_Storage;
|
|
bool proven = Proven(m_Acknowledgement == 1, sourceContains, destinationContains, exactSlot);
|
|
if (proven || m_Acknowledgement < 0) m_Resolved = true;
|
|
// A successful callback without physical destination proof is ambiguous.
|
|
// Keep it reserved through timeout/cancellation; never start a second move.
|
|
if (!valid || now >= m_Deadline)
|
|
{
|
|
m_Invalidated = true; m_Ready = false;
|
|
return ERR_MortarSupplyState.FAILED;
|
|
}
|
|
if (proven)
|
|
{
|
|
m_Ready = true;
|
|
return ERR_MortarSupplyState.READY;
|
|
}
|
|
if (m_Acknowledgement < 0 || !m_Accepted) return ERR_MortarSupplyState.FAILED;
|
|
return ERR_MortarSupplyState.PENDING;
|
|
}
|
|
}
|