#ifdef WORKBENCH class ERR_NativeMaintenanceMove : ScriptedInventoryOperationCallback { int Result; protected void Finish(int result) { if (!Result) Result = result; } protected override void OnComplete() { Finish(1); } protected override void OnAllComplete() { Finish(1); } protected override void OnFailed() { Finish(-1); } protected override void OnAllFailed() { Finish(-1); } } // Opt-in fixture only. One existing Southern can, original UAZ, native action. // Direct action execution proves transfer/custody, not held input or animation. // The outer fixture owns its explicitly recorded player approach; no movement here. class ERR_NativeMaintenanceProbe { protected ERR_GameMode m_Mode; protected IEntity m_Player; protected ERR_SiteRuntime m_Site; protected IEntity m_Container; protected Vehicle m_Vehicle; protected IEntity m_Can; protected IEntity m_Spare; protected InventoryStorageManagerComponent m_Source; protected InventoryStorageManagerComponent m_Inventory; protected BaseInventoryStorageComponent m_Destination; protected SCR_FuelManagerComponent m_Fuel; protected SCR_FuelManagerComponent m_CanFuel; protected SCR_FuelManagerComponent m_SpareFuel; protected SCR_FuelNode m_Tank; protected ref array m_Nodes = {}; protected ref array m_InitialNodes = {}; protected SCR_RefuelAtSupportStationAction m_Action; protected SCR_FuelSupportStationComponent m_Station; protected SCR_GadgetManagerComponent m_Gadgets; protected ref ERR_NativeMaintenanceMove m_Move; protected int m_State; protected int m_Calls; protected float m_Deadline = -1; protected float m_Next; protected float m_Interval; protected float m_InitialCan; protected float m_InitialSpare; protected float m_BaseTank; protected float m_LastCan; protected float m_LastTank; protected bool m_ActionStarted; protected bool m_Interrupted; protected bool m_Stopped; void ERR_NativeMaintenanceProbe(ERR_GameMode mode, IEntity player) { m_Mode = mode; m_Player = player; } static bool Enabled() { string enabled, smoke, outcome; return System.GetCLIParam("errMaintenanceRefuel", enabled) && enabled == "1" && System.GetCLIParam("errSmoke", smoke) && smoke == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "normal-escape-route"; } static bool WithinDeadline(float started, float now) { return started >= 0 && now >= started && now - started <= 120000; } static bool FuelConserved(float initialCan, float can, float baseTank, float tank, float capacity) { return initialCan > 0 && initialCan <= 5.001 && can >= 0 && can <= initialCan && baseTank >= 0 && tank >= baseTank && tank <= capacity && Math.AbsFloat((initialCan - can) - (tank - baseTank)) <= 0.01; } bool NeedsApproach() { return !m_Stopped && m_State == 3; } vector ApproachPosition() { if (!m_Action || !m_Vehicle) return vector.Zero; vector point = m_Action.GetWorldPositionAction(); vector direction = point - m_Vehicle.GetOrigin(); direction[1] = 0; if (direction.LengthSq() < 0.01) return vector.Zero; direction.Normalize(); return ERR_Actors.Ground(point + direction); } protected bool Check(bool condition, string detail) { if (condition) { Print("ERR SMOKE PASS: maintenance " + detail); return true; } Print("ERR SMOKE FAIL: maintenance " + detail, LogLevel.ERROR); m_State = -1; Stop(); return false; } protected bool Contains(InventoryStorageManagerComponent inventory, IEntity exact) { if (!inventory || !exact) return false; array items = {}; ERR_Inventory.Items(inventory, items); return items.Count() <= 128 && items.Contains(exact); } protected bool Context() { if (!Replication.IsServer() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active()) return false; if (!m_Player || !m_Mode.Players() || !m_Mode.Players().CurrentActor(m_Player) || !ERR_Actors.CanFight(m_Player)) return false; CompartmentAccessComponent access = CompartmentAccessComponent.Cast(m_Player.FindComponent(CompartmentAccessComponent)); if (!access || access.IsInCompartment()) return false; if (!m_Mode.Sites() || !m_Site || m_Mode.Sites().Find("everon-supply-1") != m_Site) return false; if (!m_Container || m_Site.Container != m_Container || !m_Mode.Sites().Owns(m_Container)) return false; if (m_Site.Transport != m_Vehicle || m_Site.TransportEntity != m_Vehicle || !ERR_Actors.Alive(m_Vehicle)) return false; if (!m_Mode.Sites().Owns(m_Vehicle) || !m_Source || m_Source.GetOwner() != m_Container) return false; if (!m_Inventory || m_Inventory.GetOwner() != m_Player || !m_Can || !m_Spare) return false; if (!m_Mode.Sites().Owns(m_Can) || !m_Mode.Sites().Owns(m_Spare) || !m_CanFuel || !m_SpareFuel) return false; if (!m_Fuel || m_Fuel.GetOwner() != m_Vehicle || !m_Tank || !m_Action || m_Action.GetOwner() != m_Vehicle) return false; if (m_CanFuel.GetOwner() != m_Can || m_SpareFuel.GetOwner() != m_Spare || !m_Station || m_Station.GetOwner() != m_Can) return false; if (!Contains(m_Source, m_Spare) || !(Math.AbsFloat(m_SpareFuel.GetTotalFuel() - m_InitialSpare) <= 0.001)) return false; CarControllerComponent controller = CarControllerComponent.Cast(m_Vehicle.FindComponent(CarControllerComponent)); Physics physics = m_Vehicle.GetPhysics(); if (!controller || controller.IsEngineOn() || !controller.GetPersistentHandBrake()) return false; if (!physics || physics.GetVelocity().LengthSq() > 0.01) return false; BaseCompartmentManagerComponent manager = BaseCompartmentManagerComponent.Cast(m_Vehicle.FindComponent(BaseCompartmentManagerComponent)); array seats = {}; if (manager) manager.GetCompartments(seats); if (!manager || seats.IsEmpty() || seats.Count() > 64) return false; foreach (BaseCompartmentSlot seat : seats) if (!seat || seat.GetOccupant()) return false; return true; } protected bool Carried() { if (!Contains(m_Inventory, m_Can) || Contains(m_Source, m_Can)) return false; return ERR_InfantryAmmoTransfer.StoredUnder(m_Can, m_Player); } protected bool Held() { return Carried() && m_Gadgets && m_Gadgets.GetHeldGadget() == m_Can && SCR_SupportStationGadgetComponent.GetHeldSupportStation(ESupportStationType.FUEL, m_Player) == m_Station; } protected bool AtAction() { return m_Action && m_Action.GetOwner() == m_Vehicle && vector.Distance(m_Player.GetOrigin(), m_Action.GetWorldPositionAction()) <= 3; } protected bool ActionAllowed() { bool shown = m_Action.CanBeShownScript(m_Player); bool allowed = shown && m_Action.CanBePerformedScript(m_Player); if (!allowed) { ESupportStationReasonInvalid reason; m_Action.CanPerform(reason); PrintFormat("ERR MAINTENANCE REFUSAL: shown=%1 allowed=%2 nativeReason=%3", shown, allowed, reason); } return allowed; } protected bool Conservation() { array nodes = {}; m_Fuel.GetFuelNodesList(nodes); if (nodes.Count() != m_Nodes.Count()) return false; for (int i = 0; i < nodes.Count(); i++) { if (!nodes[i] || nodes[i] != m_Nodes[i]) return false; if (nodes[i] != m_Tank && !(Math.AbsFloat(nodes[i].GetFuel() - m_InitialNodes[i]) <= 0.001)) return false; } float can = m_CanFuel.GetTotalFuel(), tank = m_Tank.GetFuel(); return FuelConserved(m_InitialCan, can, m_BaseTank, tank, m_Tank.GetMaxFuel()); } protected void CancelAction() { if (!m_ActionStarted) return; m_ActionStarted = false; // Do not cancel another holder's gadget or a replacement round's action. if (m_Mode && m_Mode == ERR_GameMode.Get() && m_Action && m_Action.GetOwner() == m_Vehicle && m_Player && m_Mode.Players() && m_Mode.Players().CurrentActor(m_Player) && m_Gadgets && m_Gadgets.GetHeldGadget() == m_Can) m_Action.OnActionCanceled(m_Vehicle, m_Player); } void Stop() { if (m_Stopped) return; m_Stopped = true; CancelAction(); if (m_Mode && m_Mode == ERR_GameMode.Get() && m_Mode.Players() && m_Mode.Players().CurrentActor(m_Player) && m_Gadgets && m_Gadgets.GetHeldGadget() == m_Can && m_Can) m_Gadgets.SetGadgetMode(m_Can, EGadgetMode.IN_STORAGE); } protected bool Select() { if (!m_Mode || !m_Mode.Sites() || !m_Player) return false; m_Site = m_Mode.Sites().Find("everon-supply-1"); if (!m_Site || !m_Site.Container || !m_Site.Transport || m_Site.TransportOmitted) return false; m_Container = m_Site.Container; m_Vehicle = m_Site.Transport; m_Source = InventoryStorageManagerComponent.Cast(m_Container.FindComponent(InventoryStorageManagerComponent)); m_Inventory = InventoryStorageManagerComponent.Cast(m_Player.FindComponent(InventoryStorageManagerComponent)); m_Fuel = SCR_FuelManagerComponent.Cast(m_Vehicle.FindComponent(SCR_FuelManagerComponent)); m_Gadgets = SCR_GadgetManagerComponent.GetGadgetManager(m_Player); if (!m_Source || !m_Inventory || !m_Fuel || !m_Gadgets) return false; array items = {}; ERR_Inventory.Items(m_Source, items); if (items.Count() > 128) return false; int cans; foreach (IEntity item : items) { if (!item || !item.GetPrefabData() || item.GetPrefabData().GetPrefabName() != "{12D5AD21E383B768}Prefabs/Items/Fuel/Jerrycan_01/Jerrycan_01_item.et") continue; cans++; if (!m_Can) m_Can = item; else m_Spare = item; } if (cans != 2 || m_Can == m_Spare) return false; m_CanFuel = SCR_FuelManagerComponent.Cast(m_Can.FindComponent(SCR_FuelManagerComponent)); m_SpareFuel = SCR_FuelManagerComponent.Cast(m_Spare.FindComponent(SCR_FuelManagerComponent)); m_Station = SCR_FuelSupportStationComponent.Cast(m_Can.FindComponent(SCR_FuelSupportStationComponent)); if (!m_CanFuel || !m_SpareFuel || !m_Station) return false; m_InitialCan = m_CanFuel.GetTotalFuel(); m_InitialSpare = m_SpareFuel.GetTotalFuel(); if (!(Math.AbsFloat(m_InitialCan - 5) <= 0.001) || !(Math.AbsFloat(m_InitialSpare - 5) <= 0.001)) return false; m_Fuel.GetFuelNodesList(m_Nodes); if (m_Nodes.IsEmpty() || m_Nodes.Count() > 8) return false; foreach (BaseFuelNode node : m_Nodes) { if (!SCR_FuelNode.Cast(node)) return false; m_InitialNodes.Insert(node.GetFuel()); } BaseActionsManagerComponent manager = BaseActionsManagerComponent.Cast(m_Vehicle.FindComponent(BaseActionsManagerComponent)); array actions = {}; if (manager) manager.GetActionsList(actions); if (actions.Count() > 64) return false; foreach (BaseUserAction candidate : actions) { SCR_RefuelAtSupportStationAction action = SCR_RefuelAtSupportStationAction.Cast(candidate); if (!action || action.GetOwner() != m_Vehicle) continue; foreach (BaseFuelNode baseNode : m_Nodes) { SCR_FuelNode tank = SCR_FuelNode.Cast(baseNode); if (!tank || !tank.CanReceiveFuel() || tank.GetFuel() < 5 || !action.CheckIfFuelNodeIsValid(tank)) continue; // Refuse an action addressing several non-full tanks: prove one exact target. int addressed; foreach (BaseFuelNode other : m_Nodes) { SCR_FuelNode otherTank = SCR_FuelNode.Cast(other); if (!otherTank) return false; if (otherTank == tank || (action.CheckIfFuelNodeIsValid(otherTank) && otherTank.GetFuel() < otherTank.GetMaxFuel())) addressed++; } if (addressed != 1) continue; m_Action = action; m_Tank = tank; break; } if (m_Action) break; } if (!m_Action || !m_Tank) return false; m_Interval = Math.AbsFloat(m_Action.GetActionDuration()) * 1000; return m_Interval >= 100 && m_Interval <= 5000 && Context(); } int Tick(float now) { if (m_State == 8) return 1; if (m_Stopped || m_State < 0) return -1; if (!Enabled() || !Replication.IsServer() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active()) { Stop(); return -1; } if (m_Deadline < 0) m_Deadline = now + 120000; if (!WithinDeadline(m_Deadline - 120000, now)) { Check(false, "fixed refuel proof deadline"); return -1; } if (m_State == 0) { if (!Check(Select(), "read-only selection pins two original five-litre cans and exact original parked UAZ action/tank")) return -1; if (!Check(vector.Distance(m_Player.GetOrigin(), m_Container.GetOrigin()) <= 4 && Contains(m_Source, m_Can) && !Contains(m_Inventory, m_Can) && ERR_InfantryAmmoTransfer.StoredUnder(m_Can, m_Container), "original can is exclusively at the nearby cache")) return -1; m_Destination = m_Inventory.FindStorageForItem(m_Can, EStoragePurpose.PURPOSE_DEPOSIT); if (!Check(m_Destination && ERR_InfantryAmmoTransfer.StoredUnder(m_Destination.GetOwner(), m_Player) && m_Source.CanMoveItemToStorage(m_Can, m_Destination), "native destination has capacity for original can")) return -1; m_Move = new ERR_NativeMaintenanceMove(); m_State = 1; bool accepted = m_Source.TryMoveItemToStorage(m_Can, m_Destination, -1, m_Move); PrintFormat("ERR MAINTENANCE MOVE: accepted=%1 callback=%2; one submission only", accepted, m_Move.Result); return 0; } if (!Context()) { Check(false, "original mission, player, empty stationary UAZ or untouched spare can lost"); return -1; } if (m_State == 1) { if (!m_Move.Result) return 0; if (!Check(m_Move.Result == 1, "original can inventory callback succeeded")) return -1; if (!Carried()) return 0; InventoryItemComponent item = InventoryItemComponent.Cast(m_Can.FindComponent(InventoryItemComponent)); if (!Check(item && item.GetParentSlot() && item.GetParentSlot().GetStorage() == m_Destination && item.GetParentSlot().GetAttachedEntity() == m_Can, "callback and exact destination slot agree")) return -1; m_State = 2; m_Gadgets.SetGadgetMode(m_Can, EGadgetMode.IN_HAND); return 0; } if (m_State == 2) { if (!Held()) return 0; if (!Check(Math.AbsFloat(m_CanFuel.GetTotalFuel() - 5) <= 0.001, "equipping original can did not refill or consume it")) return -1; m_State = 3; return 0; } if (!Held()) { Check(false, "exact original can is no longer the held native fuel support station"); return -1; } if (m_State == 3) { if (!AtAction()) return 0; m_BaseTank = m_Tank.GetFuel() - 5; if (!Check(m_BaseTank >= 0 && m_Action.CheckIfFuelNodeIsValid(m_Tank), "one original tank permits five-litre fixture depletion")) return -1; PrintFormat("ERR MAINTENANCE ARRANGE: original tank before=%1 after=%2; can unchanged=%3", m_Tank.GetFuel(), m_BaseTank, m_CanFuel.GetTotalFuel()); m_Tank.SetFuel(m_BaseTank); if (!Check(Conservation(), "one-time tank depletion recorded; both original cans remain unchanged")) return -1; if (!Check(ActionAllowed(), "existing native refuel action accepts actual held can and parked UAZ")) return -1; m_Action.OnActionStart(m_Player); m_ActionStarted = true; m_State = 4; m_Next = now + m_Interval; return 0; } if (!AtAction() || !Conservation()) { Check(false, "physical action reach or finite fuel conservation lost"); return -1; } if (now < m_Next) return 0; if (m_State == 5) { if (!Check(Math.AbsFloat(m_CanFuel.GetTotalFuel() - m_LastCan) <= 0.001 && Math.AbsFloat(m_Tank.GetFuel() - m_LastTank) <= 0.001, "cancelled action transfers no fuel while interrupted")) return -1; m_Action.OnActionStart(m_Player); m_ActionStarted = true; m_State = 4; m_Next = now + m_Interval; return 0; } if (m_State == 6) { if (!Check(Math.AbsFloat(m_CanFuel.GetTotalFuel() - m_LastCan) <= 0.001 && Math.AbsFloat(m_Tank.GetFuel() - m_LastTank) <= 0.001 && Contains(m_Inventory, m_Can), "empty original can persists and refused replay adds no fuel")) return -1; m_State = 8; Stop(); return 1; } float beforeCan = m_CanFuel.GetTotalFuel(), beforeTank = m_Tank.GetFuel(); if (beforeCan <= 0.001) { if (!Check(m_Interrupted, "at least one partial native transfer was interrupted and resumed")) return -1; CancelAction(); if (!Check(!m_Station.IsValid(m_Vehicle, m_Player, m_Action, m_Action.GetWorldPositionAction(), m_EmptyReason, m_EmptySupplies), "empty original can is natively ineligible")) return -1; if (!Check(!m_Action.CanBePerformedScript(m_Player), "native action refuses empty held can")) return -1; m_Action.PerformAction(m_Vehicle, m_Player); m_LastCan = beforeCan; m_LastTank = beforeTank; m_State = 6; m_Next = now + 1500; return 0; } if (!Check(++m_Calls <= 128 && ActionAllowed(), "bounded native action remains eligible before transfer")) return -1; m_Action.PerformAction(m_Vehicle, m_Player); float afterCan = m_CanFuel.GetTotalFuel(), afterTank = m_Tank.GetFuel(); if (!Check(afterCan < beforeCan && afterTank > beforeTank && Conservation(), "native transfer consumes original can fuel and adds the same amount to the original tank")) return -1; PrintFormat("ERR MAINTENANCE TRANSFER: call=%1 can=%2 tank=%3 consumed=%4 gained=%5", m_Calls, afterCan, afterTank, m_InitialCan - afterCan, afterTank - m_BaseTank); m_Next = now + m_Interval; if (!m_Interrupted && afterCan > 0.001) { m_Interrupted = true; CancelAction(); m_LastCan = afterCan; m_LastTank = afterTank; m_State = 5; m_Next = now + 1500; } return 0; } protected ESupportStationReasonInvalid m_EmptyReason; protected int m_EmptySupplies; } #endif