5133p39 14 Posted August 13 I need to lock a vehicle to prevent anybody from: - getting in (not just as a driver, but anywhere) - opening any doors - accessing vehicle's inventory - loading/unloading supplies (i need to get rid of this mechanics in general - my mission is not using it, so it makes no sense for these prompts to even be there) What i need is the equivalent of the good old lock command from Arma 3. Share this post Link to post Share on other sites
5133p39 14 Posted August 14 Never mind. Figured it out with the help of some very patient people on Discord. Will come back here later to describe how to implement it, in case anybody is lost as i was. Share this post Link to post Share on other sites
5133p39 14 Posted August 21 IMPORTANT: I wouldn't expect this to work in MP environment as it is, because i don't know anything about that yet, and i had no time to test in MP at all. I only used the code in SP missions so far. It is rough and needs to be tweaked to work with any vehicle - i only tested it with UAZ469. The code can use some improvements, at the very least to make it more universal, supporting different vehicle types, but most importantly it may need properly implemented replication for MP, and maybe more (post your tips and opinions, especially on how to do things cleanly and properly). First, let's summarize how we want it to work: Each door, trunk, hood, etc., should be (un)lockable individually, and if locked, will prevent all related actions (opening door, entering, exiting, jumping out, inventory access, whatever). NOTE: None of the old cars would have central locking, but we should add such option so it can be used with any modern car mods, i just don't want to bother with that right now, maybe later. The system will work for, and restrict, only players. Bots will behave same way as they did before, unaffected by the system. To lock/unlock anything, players need to have a key (an item), which is paired with the vehicle. Except if players are already inside the vehicle - then they won't need a key to lock/unlock any door withing reach. The key-to-vehicle pairing will be based on 5 letter string codes ("ABCDE"), so if need be, we can show the code to player in a nice readable form, but internally we will rely on hashes. We will make am Inventory Item Component attached on the key item prefab, and a vehicle Script Component attached on the vehicle entities (or base prefabs) - each holding the string code, its hash (we'll need that only on the vehicle), and any minimal methods that we may need on one or the other. To use a key, player will need to place it in some Quick Slot - no actions needed, just have it in a slot. When player closes inventory after placing a key in Quick Slot, we will check all slots and use code from the first key we find. We will store the code, that we found on the first key, in a static variable on a Game Component that we will make for this purpose (and to provide some needed methods) - that way we can have quick and easy access to the "cached" code from anywhere. Starting the vehicle's engine will not need a key. As long as a player is inside a vehicle, they can start the engine freely (or at least unrestricted by this system) without a key. The Game Component: YOUR_PROJECT/scripts/GameCode/Mors/Mors_VehicleLockGameComponent.c To be added as a component to a GameMode. Spoiler class Mors_VehicleLockGameComponentClass: SCR_BaseGameModeComponentClass {} class Mors_VehicleLockGameComponent: SCR_BaseGameModeComponent { protected IEntity _player; protected SCR_CharacterInventoryStorageComponent _inventoryStorage; static int _cachedCodeHash = 0; static string _cachedCode = ""; static const int ENUM_DOOR_LEFT_1 = 1 << 0; static const int ENUM_DOOR_LEFT_2 = 1 << 1; static const int ENUM_DOOR_RIGHT_1 = 1 << 2; static const int ENUM_DOOR_RIGHT_2 = 1 << 3; static const int ENUM_DOOR_REAR = 1 << 4; static const int ENUM_FUEL_CAP_L = 1 << 5; static const int ENUM_FUEL_CAP_R = 1 << 6; static const int ENUM_HOOD_FRONT = 1 << 7; static const int ENUM_HOOD_REPAIR = 1 << 8; ref static const map<string, int> UA_CONTEXT_MAP = new map<string, int>(); void Mors_VehicleLockGameComponent(IEntityComponentSource src, IEntity ent, IEntity parent) { m_pGameMode = SCR_BaseGameMode.Cast(ent); if (!m_pGameMode) { string message = string.Format("%1 is attached to entity '%2' type=%3, required type=%4! This is not allowed!", Type().ToString(), ent.GetName(), ent.ClassName(), "SCR_BaseGameMode"); Debug.Error(message); Print(message, LogLevel.WARNING); } Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Clear(); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l01", ENUM_DOOR_LEFT_1); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l01_int", ENUM_DOOR_LEFT_1); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l01_int_hadle", ENUM_DOOR_LEFT_1); // YES, THAT IS A TYPO, BUT IT IS HOW BI NAMED IT Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l01_int_handle", ENUM_DOOR_LEFT_1); // THE WOULD BE CORRECT NAME, IN CASE BI FIXES IT ONE DAY Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l02", ENUM_DOOR_LEFT_2); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l02_int", ENUM_DOOR_LEFT_2); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_l02_int_handle", ENUM_DOOR_LEFT_2); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_r01", ENUM_DOOR_RIGHT_1); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_r01_int", ENUM_DOOR_RIGHT_1); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_r01_int_handle", ENUM_DOOR_RIGHT_1); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_r02", ENUM_DOOR_RIGHT_2); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_r02_int", ENUM_DOOR_RIGHT_2); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_r02_int_handle", ENUM_DOOR_RIGHT_2); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("door_rear", ENUM_DOOR_REAR); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("fuel_cap_l", ENUM_FUEL_CAP_L); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("fuel_cap_r", ENUM_FUEL_CAP_R); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("hood_front", ENUM_HOOD_FRONT); Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Insert("hood_repair", ENUM_HOOD_REPAIR); } static string GenerateCode() { RandomGenerator rng = new RandomGenerator(); string code = rng.RandInt(65, 91).AsciiToString(); code += rng.RandInt(65, 91).AsciiToString(); code += rng.RandInt(65, 91).AsciiToString(); code += rng.RandInt(65, 91).AsciiToString(); code += rng.RandInt(65, 91).AsciiToString(); return code; } static bool CanUnlockVehicle(Vehicle vehicle) { Mors_VehicleLockingComponent morsVLock = Mors_VehicleLockingComponent.Cast( vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (!morsVLock) return false; return morsVLock.IsLockCodeHash(_cachedCodeHash); } override protected void OnPlayerSpawnFinalize_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity) { // TODO: clean this garbage - most is likely not needed or maybe even wrong if (!entity) entity = EntityUtils.GetPlayer(); if (!entity) entity = SCR_PlayerController.GetLocalControlledEntity(); if (!entity) { PlayerController playerCtrl = GetGame().GetPlayerController(); if (playerCtrl) entity = playerCtrl.GetControlledEntity(); } if (!entity) return; _player = entity; SCR_InventoryStorageManagerComponent inventory = SCR_InventoryStorageManagerComponent.Cast(entity.FindComponent(SCR_InventoryStorageManagerComponent)); if (!inventory) return inventory.m_OnInventoryOpenInvoker.Remove(Mors_OnInventoryOpen); inventory.m_OnInventoryOpenInvoker.Insert(Mors_OnInventoryOpen); } protected void Mors_OnInventoryOpen(bool open) { if (open) return; if (!_player) { _player = SCR_PlayerController.GetLocalControlledEntity(); if (!_player) return; } if (!_inventoryStorage) { _inventoryStorage = SCR_CharacterInventoryStorageComponent.Cast( _player.FindComponent(SCR_CharacterInventoryStorageComponent) ); if (!_inventoryStorage) return; } string code = ""; int codeHash = 0; array<IEntity> entities = _inventoryStorage.GetQuickSlotEntitiesOnly(); foreach (int i, IEntity entity : entities) { if (!entity) continue; Managed mn = entity.FindComponent(Mors_VehicleKeyItem); if (!mn) continue; Mors_VehicleKeyItem key = Mors_VehicleKeyItem.Cast(mn); code = key.Mors_GetLockCode(); if (code != "") { codeHash = code.Hash(); Print(" MORS Quick Slot Key Search: slot #"+ i +", key with code: "+ code +", hash: "+ codeHash); break; } Print(" MORS Quick Slot Key Search: slot #"+ i +", key with no code"); } _cachedCode = code; _cachedCodeHash = codeHash; } } The Vehicle Component: YOUR_PROJECT/scripts/GameCode/Mors/Mors_VehicleLockingComponent.c To be added as a component to a vehicle placed in World Editor directly (not in a "Slot.et", so you can access the component and manually can set the vehicle-to-key pairing code), or some vehicle base class (i added it to an override of Wheeled_base.et). Spoiler class Mors_VehicleLockingComponentClass : ScriptComponentClass {} class Mors_VehicleLockingComponent : ScriptComponent { [ Attribute(defvalue: "", desc: "Set vehicle lock key code, consisting of 5 uppercase letters (A-Z). Leave empty if you use other means to set the code."), RplProp(onRplName: "OnLockCode") ] protected string _lockCode; protected int _lockCodeHash; [Attribute(defvalue: "")] protected bool _doorLeft1InitiallyLocked; [Attribute(defvalue: "")] protected bool _doorLeft2InitiallyLocked; [Attribute(defvalue: "")] protected bool _doorRight1InitiallyLocked; [Attribute(defvalue: "")] protected bool _doorRight2InitiallyLocked; [Attribute(defvalue: "")] protected bool _doorRearInitiallyLocked; [Attribute(defvalue: "")] protected bool _fuelCapLInitiallyLocked; [Attribute(defvalue: "")] protected bool _fuelCapRInitiallyLocked; [Attribute(defvalue: "")] protected bool _hoodFrontInitiallyLocked; [Attribute(defvalue: "")] protected bool _hoodRepairInitiallyLocked; void OnLockCode() { _lockCodeHash = _lockCode.Hash(); //Print("MORS: Mors_VehicleLockingComponent.OnLockCode() - hash set to: "+ _lockCodeHash); } [RplRpc(RplChannel.Reliable, RplRcver.Server)] void RpcAsk_LockCode(string code) { _lockCode = code; //Print("MORS: Mors_VehicleLockingComponent.RpcAsk_LockCode() - code set to: "+ _lockCode); OnLockCode(); Replication.BumpMe(); } void SetLockCode(string code) { //Print("MORS: Mors_VehicleLockingComponent.SetCode() - calling RpcAsk_LockCode, code: "+ code); Rpc(RpcAsk_LockCode, code); } [RplProp(onRplName: "OnLockState")] protected int _lockState; void OnLockState() {} [RplRpc(RplChannel.Reliable, RplRcver.Server)] void RpcAsk_LockState(int state) { _lockState = state; OnLockState(); Replication.BumpMe(); } void SetLockState(int state) { Rpc(RpcAsk_LockState, state); } int GetLockState() return _lockState; void ClearLockBit(int bit) SetLockState(_lockState & ~bit); void FlipLockBit(int bit) SetLockState(_lockState ^ bit); void SetLockBit(int bit) SetLockState(_lockState | bit); bool IsLockBitSet(int bit) return _lockState & bit; string GetLockCode() return _lockCode; int GetLockCodeHash() return _lockCodeHash; bool IsLockCode(string code) return (code == _lockCode); bool IsLockCodeHash(int hash) return (hash == _lockCodeHash); bool CanUlockWithHash(int hash) { if (!hash) return false; // means the player has no key at all, so no lock/unlock should be available return (hash == _lockCodeHash); } bool CanUlockWithCachedHash() { if (!Mors_VehicleLockGameComponent._cachedCodeHash) return false; // means the player has no key at all, so no lock/unlock should be available return (Mors_VehicleLockGameComponent._cachedCodeHash == _lockCodeHash); } //------------------------------------------------------------------------------------------------ override void OnPostInit(IEntity owner) { SetEventMask(owner, EntityEvent.INIT); } //------------------------------------------------------------------------------------------------ override void EOnInit(IEntity owner) { //Print("MORS: Mors_VehicleLockingComponent.EOnInit() - code is: "+ _lockCode); if (_lockCode != "") { _lockCodeHash = _lockCode.Hash(); //Print("MORS: Mors_VehicleLockingComponent.EOnInit() - hash set to: "+ _lockCodeHash); } if (_doorLeft1InitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_DOOR_LEFT_1; if (_doorLeft2InitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_DOOR_LEFT_2; if (_doorRight1InitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_DOOR_RIGHT_1; if (_doorRight2InitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_DOOR_RIGHT_2; if (_doorRearInitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_DOOR_REAR; if (_fuelCapLInitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_FUEL_CAP_L; if (_fuelCapRInitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_FUEL_CAP_R; if (_hoodFrontInitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_HOOD_FRONT; if (_hoodRepairInitiallyLocked) _lockState = _lockState | Mors_VehicleLockGameComponent.ENUM_HOOD_REPAIR; //Print("MORS: Mors_VehicleLockingComponent.EOnInit() - lock state set to: "+ _lockState); /* string bits = ""; bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_DOOR_LEFT_1)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_DOOR_LEFT_2)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_DOOR_RIGHT_1)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_DOOR_RIGHT_2)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_DOOR_REAR)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_FUEL_CAP_L)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_FUEL_CAP_R)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_HOOD_FRONT)); bits = bits + (int)((bool)(_lockState & Mors_VehicleLockGameComponent.ENUM_HOOD_REPAIR)); Print("MORS: Mors_VehicleLockingComponent.EOnInit() - lock state bits: "+ bits); */ ClearEventMask(owner, EntityEvent.INIT); } } The Inventory Item Component: YOUR_PROJECT/scripts/GameCode/Mors/Mors_VehicleKeyItem.cTo be added as a component to the vehicle key prefab. For now, just make a duplicate of some bandage item (anything that you know is already configured as inventory item that can be placed in Quick Slot), and then attach this component to that prefab, and use that as the "vehicle key". Also, you need to place this item in the World Editor directly, so you can have access to the component on it and manually set the vehicle code that is to be used for pairing with a vehicle (otherwise you'd need to do that using some script, which is not covered here). Spoiler class Mors_VehicleKeyItemClass: InventoryItemComponentClass {} class Mors_VehicleKeyItem: InventoryItemComponent { [ Attribute(defvalue: "", desc: "Set desired vehicle lock key code, consisting of 5 uppercase letters (A-Z). Leave empty if you use other means to set the code.", category: "Lockable Vehicles"), RplProp(onRplName: "Mors_OnLockCode") ] protected string _morsLockCode; void Mors_OnLockCode() {} [RplRpc(RplChannel.Reliable, RplRcver.Server)] void RpcAsk_MorsLockCode(string code) { _morsLockCode = code; Mors_OnLockCode(); Replication.BumpMe(); } string Mors_GetLockCode() return _morsLockCode; void Mors_SetLockCode(string code) { Rpc(RpcAsk_MorsLockCode, code); } } The next things you need, are overrides of various User Action classes/methods. That is where you add conditions to check for locked door before allowing to proceed with opening it, etc.To set these scripts up, use filter to find these files, create duplicates (keep same filename) into your project, move the created duplicates into a "modded" subfolder that you create in the location where each duplicate was created, then edit them accordingly: SCR_GetInUserAction.cFound in "ArmaReforger/scripts/Game/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/UserActions/modded/". Spoiler modded class SCR_GetInUserAction : SCR_CompartmentUserAction { //------------------------------------------------------------------------------------------------ override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity) { if (!pOwnerEntity || !pUserEntity) return; ChimeraCharacter character = ChimeraCharacter.Cast(pUserEntity); if (!character) return; BaseCompartmentSlot targetCompartment = GetCompartmentSlot(); if (!targetCompartment) return; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( pOwnerEntity.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { // if already in the vehicle, user is switching seats which we should allow even if locked IEntity entVehicleIn = compartmentAccess.GetVehicleIn(pUserEntity); if (entVehicleIn && GetOwner().GetRootParent() != entVehicleIn) return; } } } } if (!compartmentAccess.GetInVehicle(pOwnerEntity, targetCompartment, false, GetRelevantDoorIndex(pUserEntity), ECloseDoorAfterActions.RETURN_TO_PREVIOUS_STATE, false)) return; super.PerformAction(pOwnerEntity, pUserEntity); } //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { if (m_DamageManager && m_DamageManager.GetState() == EDamageState.DESTROYED) return false; BaseCompartmentSlot compartment = GetCompartmentSlot(); if (!compartment) return false; SCR_ChimeraCharacter character = SCR_ChimeraCharacter.Cast(user); if (!character) return false; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return false; //~ TODO: Hotfix until proper solution, only blocks player does not block AI or Editor actions float storedResources; if (m_ResourceComp && m_CompartmentManager && m_CompartmentManager.BlockSuppliesIfOccupied()) { if (SCR_ResourceSystemHelper.GetStoredResources(m_ResourceComp, storedResources) && storedResources > 0) { SetCannotPerformReason(OCCUPIED_BY_SUPPLIES); return false; } } IEntity owner = compartment.GetOwner(); Vehicle vehicle = Vehicle.Cast(SCR_EntityHelper.GetMainParent(owner, true)); Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } if (vehicle) { Faction characterFaction = character.GetFaction(); Faction vehicleFaction = vehicle.GetFaction(); if (characterFaction && vehicleFaction && characterFaction.IsFactionEnemy(vehicleFaction)) { SetCannotPerformReason("#AR-UserAction_SeatHostile"); return false; } } if (compartment.GetOccupant()) { SetCannotPerformReason("#AR-UserAction_SeatOccupied"); return false; } // Check if the position isn't lock. if (m_pLockComp && m_pLockComp.IsLocked(user, compartment)) { SetCannotPerformReason(m_pLockComp.GetCannotPerformReason(user)); return false; } // Make sure vehicle can be enter via provided door, if not, set reason. if (!compartmentAccess.CanGetInVehicleViaDoor(owner, m_CompartmentManager, GetRelevantDoorIndex(user))) { SetCannotPerformReason("#AR-UserAction_SeatObstructed"); return false; } return true; } }; SCR_GetOutUserAction.cFound in "ArmaReforger/scripts/Game/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/UserActions/modded/". Spoiler modded class SCR_GetOutAction : SCR_CompartmentUserAction { //------------------------------------------------------------------------------------------------ override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity) { if (!pOwnerEntity) return; ChimeraCharacter character = ChimeraCharacter.Cast(pUserEntity); if (!character) return; CharacterControllerComponent controller = character.GetCharacterController(); if (controller && controller.IsUnconscious()) return; BaseCompartmentSlot targetCompartment = GetCompartmentSlot(); if (!targetCompartment) return; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( pOwnerEntity.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return; } } } } if (!compartmentAccess.GetOutVehicle(EGetOutType.ANIMATED, GetRelevantDoorIndex(pUserEntity), ECloseDoorAfterActions.RETURN_TO_PREVIOUS_STATE, false)) return; super.PerformAction(pOwnerEntity, pUserEntity); } //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { ChimeraCharacter character = ChimeraCharacter.Cast(user); if (!character) return false; CharacterControllerComponent controller = character.GetCharacterController(); if (controller && controller.IsUnconscious()) return false; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return false; if (!compartmentAccess.IsInCompartment()) return false; if (compartmentAccess.IsGettingIn() || compartmentAccess.IsGettingOut()) return false; BaseCompartmentSlot compartment = compartmentAccess.GetCompartment(); if (!compartment) return false; Vehicle vehicle = Vehicle.Cast(GetOwner().GetRootParent()); Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } // Do not allow plain GetOut with speeds higher than 15 km/phys if (vehicle) { Physics phys = vehicle.GetPhysics(); if (phys) { vector velocity = phys.GetVelocity(); if ((velocity.LengthSq()) > MAX_GETOUT_SPEED_METER_PER_SEC_SQ) return false; } // Disallow GetOut when flying is above 3 meters HelicopterControllerComponent helicopterController = HelicopterControllerComponent.Cast(vehicle.GetVehicleController()); if (helicopterController) { VehicleHelicopterSimulation simulation = VehicleHelicopterSimulation.Cast(helicopterController.GetBaseSimulation()); if (simulation && simulation.GetAltitudeAGL() > MAX_GETOUT_ALTITUDE_AGL_METERS) return false; } } BaseCompartmentSlot thisCompartment = GetCompartmentSlot(); return thisCompartment == compartment; } //------------------------------------------------------------------------------------------------ override bool CanBeShownScript(IEntity user) { return CanBePerformed(user); } //------------------------------------------------------------------------------------------------ override bool GetActionNameScript(out string outName) { BaseCompartmentSlot compartment = GetCompartmentSlot(); if (!compartment) return false; UIInfo actionInfo = GetUIInfo(); if (!actionInfo) return false; outName = actionInfo.GetName(); return true; } }; SCR_JumpOutUserAction.cFound in "ArmaReforger/scripts/Game/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/UserActions/modded/". Spoiler modded class SCR_JumpOutAction : SCR_CompartmentUserAction { //------------------------------------------------------------------------------------------------ override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity) { if (!pOwnerEntity) return; ChimeraCharacter character = ChimeraCharacter.Cast(pUserEntity); if (!character) return; CharacterControllerComponent controller = character.GetCharacterController(); if (controller && controller.IsUnconscious()) return; BaseCompartmentSlot targetCompartment = GetCompartmentSlot(); if (!targetCompartment) return; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( pOwnerEntity.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return; } } } } if (!compartmentAccess.GetOutVehicle(EGetOutType.ANIMATED, GetRelevantDoorIndex(pUserEntity), ECloseDoorAfterActions.LEAVE_OPEN, false)) return; super.PerformAction(pOwnerEntity, pUserEntity); } //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { ChimeraCharacter character = ChimeraCharacter.Cast(user); if (!character) return false; CharacterControllerComponent controller = character.GetCharacterController(); if (controller && controller.IsUnconscious()) return false; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return false; if (!compartmentAccess.IsInCompartment()) return false; if (compartmentAccess.IsGettingIn() || compartmentAccess.IsGettingOut()) return false; BaseCompartmentSlot compartment = compartmentAccess.GetCompartment(); if (!compartment) return false; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( GetOwner().GetRootParent().FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } BaseCompartmentSlot thisCompartment = GetCompartmentSlot(); return thisCompartment == compartment; } }; SCR_OpenVehicleDoorUserAction.c Found in "ArmaReforger/scripts/Game/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/UserActions/modded/". Spoiler modded class SCR_OpenVehicleDoorUserAction : VehicleDoorUserAction { //------------------------------------------------------------------------------------------------ override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity) { if (!pOwnerEntity || !pUserEntity) return; ChimeraCharacter character = ChimeraCharacter.Cast(pUserEntity); if (!character) return; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return; ECharacterDoorAnimType animType = ECharacterDoorAnimType.INVALID; if (compartmentAccess.IsInCompartment()) animType = ECharacterDoorAnimType.FROM_INSIDE; else animType = ECharacterDoorAnimType.FROM_OUTSIDE; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( pOwnerEntity.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return; } } } } if (!compartmentAccess.OpenDoor(pOwnerEntity, animType, GetDoorIndex())) return; super.PerformAction(pOwnerEntity, pUserEntity); } //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { SCR_VehicleDamageManagerComponent damageManager = SCR_VehicleDamageManagerComponent.Cast(GetOwner().FindComponent(SCR_VehicleDamageManagerComponent)); if (damageManager && damageManager.GetState() == EDamageState.DESTROYED) return false; SCR_ChimeraCharacter character = SCR_ChimeraCharacter.Cast(user); if (!character) return false; CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (!compartmentAccess) return false; BaseCompartmentManagerComponent managerComponent = BaseCompartmentManagerComponent.Cast(GetOwner().FindComponent(BaseCompartmentManagerComponent)); if (!managerComponent) return false; Vehicle vehicle = Vehicle.Cast(SCR_EntityHelper.GetMainParent(GetOwner(), true)); if (vehicle) { Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } Faction characterFaction = character.GetFaction(); Faction vehicleFaction = vehicle.GetFaction(); if (characterFaction && vehicleFaction && characterFaction.IsFactionEnemy(vehicleFaction)) { SetCannotPerformReason("#AR-UserAction_SeatHostile"); return false; } } if (managerComponent.GetDoorUser(GetDoorIndex()) && managerComponent.GetDoorUser(GetDoorIndex()) != user || managerComponent.AreDoorOpen(GetDoorIndex())) { SetCannotPerformReason("#AR-UserAction_SeatOccupied"); return false; } if (!compartmentAccess.CanAccessDoor(vehicle, managerComponent, GetDoorIndex())) { SetCannotPerformReason("#AR-UserAction_SeatObstructed"); return false; } return true; } }; SCR_OpenVehicleStorageAction.c Found in "ArmaReforger/scripts/Game/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/UserActions/modded/". Spoiler modded class SCR_OpenVehicleStorageAction : SCR_InventoryAction { #ifndef DISABLE_INVENTORY //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { if (!user) return false; Managed genericInventoryManager = user.FindComponent(SCR_InventoryStorageManagerComponent); if (!genericInventoryManager) return false; RplComponent genericRpl = RplComponent.Cast(user.FindComponent( RplComponent )); if (!genericRpl) return false; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( m_Vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } return genericRpl.IsOwner(); } //------------------------------------------------------------------------------------------------ override protected void PerformActionInternal(SCR_InventoryStorageManagerComponent manager, IEntity pOwnerEntity, IEntity pUserEntity) { Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( m_Vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return; } } } } manager.SetStorageToOpen(m_InventoryOwner); manager.OpenInventory(); } #endif }; SCR_RefuelAtSupportStationAction.c Found in "ArmaReforger/scripts/Game/UserActions/SupportStations/", create duplicate into "YOUR_PROJECT/scripts/Game/UserActions/SupportStations/modded/". Spoiler modded class SCR_RefuelAtSupportStationAction : SCR_BaseUseSupportStationAction { //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { bool canRefuel = true; if (!m_FuelManager) { canRefuel = false; return false; } Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( GetOwner().GetRootParent().FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } if (m_aFuelTankIDs.IsEmpty()) { if (!m_FuelManager.CanBeRefueledScripted(m_aFuelTankIDs)) canRefuel = false; } else { canRefuel = false; array<BaseFuelNode> nodes = {}; m_FuelManager.GetFuelNodesList(nodes); SCR_FuelNode scrNode; foreach (BaseFuelNode node : nodes) { scrNode = SCR_FuelNode.Cast(node); if (!CheckIfFuelNodeIsValid(scrNode)) continue; if (node.GetFuel() < node.GetMaxFuel()) { canRefuel = true; break; } } } if (!canRefuel) { SetCanPerform(false, ESupportStationReasonInvalid.FUEL_TANK_FULL); return false; } canRefuel = super.CanBePerformedScript(user); return canRefuel; } }; SCR_ResourceContainerVehicleLoadAction.c Found in "ArmaReforger/scripts/Game/Sandbox/Resources/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/Sandbox/Resources/UserActions/modded/". Spoiler modded class SCR_ResourceContainerVehicleLoadAction : SCR_ScriptedUserAction { //------------------------------------------------------------------------------------------------ override event bool CanBePerformedScript(IEntity user) { m_bCanPerform = false; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( GetOwner().GetRootParent().FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } //~ TODO: Hotfix until proper solution, only blocks player does not block AI or Editor actions if (m_CompartmentManager && m_CompartmentManager.BlockSuppliesIfOccupied() && m_CompartmentManager.GetOccupantCount() > 0) { SetCannotPerformReason(OCCUPIED_BY_CHARACTER); return false; } if (!m_ResourceInventoryPlayerComponentRplId || !m_ResourceInventoryPlayerComponentRplId.IsValid()) m_ResourceInventoryPlayerComponentRplId = Replication.FindId(SCR_ResourcePlayerControllerInventoryComponent.Cast(GetGame().GetPlayerController().FindComponent(SCR_ResourcePlayerControllerInventoryComponent))); if (!m_ResourceComponent || !m_ResourceInventoryPlayerComponentRplId.IsValid() || !m_ResourceGenerator && !m_ResourceComponent.GetGenerator(EResourceGeneratorID.VEHICLE_LOAD, m_eResourceType, m_ResourceGenerator) || !m_ResourceConsumer && !m_ResourceComponent.GetConsumer(EResourceGeneratorID.VEHICLE_LOAD, m_eResourceType, m_ResourceConsumer)) { SetCannotPerformReason("#AR-Supplies_CannotPerform_Generic"); return false; } if (m_ResourceSubscriptionHandleConsumer) m_ResourceSubscriptionHandleConsumer.Poke(); else m_ResourceSubscriptionHandleConsumer = GetGame().GetResourceSystemSubscriptionManager().RequestSubscriptionListenerHandleGraceful(m_ResourceConsumer, m_ResourceInventoryPlayerComponentRplId); if (m_ResourceSubscriptionHandleGenerator) m_ResourceSubscriptionHandleGenerator.Poke(); else m_ResourceSubscriptionHandleGenerator = GetGame().GetResourceSystemSubscriptionManager().RequestSubscriptionListenerHandleGraceful(m_ResourceGenerator, m_ResourceInventoryPlayerComponentRplId); GetResourceValues(m_fCurrentResource, m_fMaxStoredResource, m_fCurrentTransferValue); if (m_ResourceConsumer.GetAggregatedMaxResourceValue() == 0.0) SetCannotPerformReason("#AR-Supplies_CannotPerform_Vehicle_NoStorage"); else if (m_fCurrentResource == 0.0) SetCannotPerformReason("#AR-Supplies_CannotPerform_Generic"); else if (m_fMaxStoredResource == 0.0) SetCannotPerformReason("#AR-Supplies_CannotPerform_Vehicle_StorageFull"); m_bCanPerform = m_fMaxStoredResource > 0.0 && m_fMaxStoredResource <= m_fCurrentResource; return m_bCanPerform; } } SCR_ResourceContainerVehicleUnloadAction.c Found in "ArmaReforger/scripts/Game/Sandbox/Resources/UserActions/", create duplicate into "YOUR_PROJECT/scripts/Game/Sandbox/Resources/UserActions/modded/". Spoiler modded class SCR_ResourceContainerVehicleUnloadAction : SCR_ScriptedUserAction { //------------------------------------------------------------------------------------------------ override event bool CanBePerformedScript(IEntity user) { m_bCanPerform = false; Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( GetOwner().GetRootParent().FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC) { if (morsVLC.GetLockState() > 0) { UserActionContext ctx = GetActiveContext(); if (ctx) { string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) { SetCannotPerformReason("Locked"); return false; } } } } if (!m_ResourceInventoryPlayerComponentRplId || !m_ResourceInventoryPlayerComponentRplId.IsValid()) m_ResourceInventoryPlayerComponentRplId = Replication.FindId(SCR_ResourcePlayerControllerInventoryComponent.Cast(GetGame().GetPlayerController().FindComponent(SCR_ResourcePlayerControllerInventoryComponent))); if (!m_ResourceComponent || !m_ResourceInventoryPlayerComponentRplId.IsValid() || !m_ResourceGenerator && !m_ResourceComponent.GetGenerator(EResourceGeneratorID.VEHICLE_UNLOAD, m_eResourceType, m_ResourceGenerator) || !m_ResourceConsumer && !m_ResourceComponent.GetConsumer(EResourceGeneratorID.VEHICLE_UNLOAD, m_eResourceType, m_ResourceConsumer)) { SetCannotPerformReason("#AR-Supplies_CannotPerform_Generic"); return false; } if (m_ResourceSubscriptionHandleConsumer) m_ResourceSubscriptionHandleConsumer.Poke(); else m_ResourceSubscriptionHandleConsumer = GetGame().GetResourceSystemSubscriptionManager().RequestSubscriptionListenerHandleGraceful(m_ResourceConsumer, m_ResourceInventoryPlayerComponentRplId); if (m_ResourceSubscriptionHandleGenerator) m_ResourceSubscriptionHandleGenerator.Poke(); else m_ResourceSubscriptionHandleGenerator = GetGame().GetResourceSystemSubscriptionManager().RequestSubscriptionListenerHandleGraceful(m_ResourceGenerator, m_ResourceInventoryPlayerComponentRplId); GetResourceValues(m_fCurrentResource, m_fMaxStoredResource, m_fCurrentTransferValue); if (m_ResourceGenerator.GetAggregatedMaxResourceValue() == 0.0) SetCannotPerformReason("#AR-Supplies_CannotPerform_Vehicle_NoStorage"); else if (m_fCurrentResource == 0.0) SetCannotPerformReason("#AR-Supplies_CannotPerform_Vehicle_StorageEmpty"); else if (m_fCurrentTransferValue == 0.0) SetCannotPerformReason("#AR-Supplies_CannotPerform_Container_StorageFull"); m_bCanPerform = m_fMaxStoredResource > 0.0 && m_fMaxStoredResource <= m_fCurrentResource; return m_bCanPerform; } } Mors_LockUnlockVehicleAction.c You need to CREATE this script. This is a new script for a custom Lock/Unlock user action.Place into "YOUR_PROJECT/scripts/Game/UserActions/". Spoiler class Mors_LockUnlockVehicleAction : SCR_ScriptedUserAction { //------------------------------------------------------------------------------------------------ override bool CanBePerformedScript(IEntity user) { Vehicle vehicle = Vehicle.Cast(GetOwner().GetRootParent()); if (!vehicle) return false; SCR_ChimeraCharacter character = SCR_ChimeraCharacter.Cast(user); if (!character) return false; // A user inside a vehicle can unlock anything CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent(); if (compartmentAccess) { BaseCompartmentSlot slot = compartmentAccess.GetCompartment(); if (slot) { IEntity slotRootParent = slot.GetOwner().GetRootParent(); if (slotRootParent == vehicle) return true; } } // A user outside of vehicle must have a matching key Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( vehicle.FindComponent(Mors_VehicleLockingComponent) ); if (morsVLC && !morsVLC.CanUlockWithCachedHash()) { SetCannotPerformReason("You don't have the key"); return false; } return true; } //------------------------------------------------------------------------------------------------ //! Called when someone tries to perform the action, user entity is typically character override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity) { Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( GetOwner().GetRootParent().FindComponent(Mors_VehicleLockingComponent) ); //Print("MORS: morsVLC: "+ morsVLC); if (!morsVLC) return; UserActionContext ctx = GetActiveContext(); //Print("MORS: UserActionContext: "+ ctx); if (!ctx) return; string ctxName = ctx.GetContextName(); //Print("MORS: ContextName: "+ ctxName); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); //Print("MORS: lockBit: "+ lockBit); //Print("MORS: lockState before flip: "+ morsVLC.GetLockState()); morsVLC.FlipLockBit(lockBit); //Print("MORS: lockState after flip: "+ morsVLC.GetLockState()); } //------------------------------------------------------------------------------------------------ //! If overridden and true is returned, outName is returned when BaseUserAction.GetActionName is called. //! If not overridden or false is returned the default value from UIInfo is taken (or empty string if no UI info exists) override bool GetActionNameScript(out string outName) { Mors_VehicleLockingComponent morsVLC = Mors_VehicleLockingComponent.Cast( GetOwner().GetRootParent().FindComponent(Mors_VehicleLockingComponent) ); if (!morsVLC) return false; if (morsVLC.GetLockState() < 1) { outName = "Lock"; return true; } UserActionContext ctx = GetActiveContext(); if (!ctx) return false; string ctxName = ctx.GetContextName(); int lockBit = Mors_VehicleLockGameComponent.UA_CONTEXT_MAP.Get(ctxName); if (morsVLC.IsLockBitSet(lockBit)) outName = "Unlock"; else outName = "Lock"; return true; } } That would be the scripts. Now we need to... smash it all together. And because describing all this would be too much work 🙂 i'll just use screenshots. This post is already WAY to long (god! i hope i won't need to edit it), so i will make a break here, and continue in another post below. 1 Share this post Link to post Share on other sites
5133p39 14 Posted August 21 This is the continuation of the process started in my previous post. I had to upload the images on IMGUR, and i don't know if they show up embedded in here - if not, use this link: https://imgur.com/a/MIcAFSh 1. Find this prefab called Wheeled_Base.et and create an override in your project, and add the VehicleLocking Component to it. https://imgur.com/Qp2LKHK 2. Add 2 custom user actions to ActionsManagerComponent on the Wheel_Base override, configure them as they are on this image. https://imgur.com/k57Lzvq 3. To create the Vehicle Key Inventory Item, find this prefab and duplicate into your project. https://imgur.com/TNo1OHK 4. Vehicle Key Inventory Item - Rename it, and add the highlighted VehicleKeyItem Component to it. https://imgur.com/GMwekmx 5. Add the VehicleLockGameComponent to your GameMode. https://imgur.com/Oc2iUKa That should be all, unless i forgot something. Now you can test it. Place some vehicle into the world (preferably the UAZ 469, on which i tested this), and the the "vehicle key" inventory item. You need to manually set the pairing codes on both, and placing them in the world directly ensures that you will be able to access those attributes on the entities. Of course you can use scripting to set the codes, but this will be quicker, just tor test the thing. Make sure you set the code on the vehicle and on the key to the same value (example: ABCDE). Stick to simple ASCII characters, and i recommend sticking to the length of 5 characters. Then hit Play (assuming you have your test "mission" prepared - which i do not cover here at all), pickup the key you placed in the World Editor, put it in some Quick Slot, and mess with the vehicle to see how it works. Then quit, rewrite everything properly into a proper mod that works universally with any vehicle, and drop me a link so i can save some time doing that myself 😉 1 Share this post Link to post Share on other sites