C#:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust.Modular;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Car Turrets", "WhiteThunder", "1.6.3")]
[Description("Allows players to deploy auto turrets onto modular cars.")]
internal class CarTurrets : CovalencePlugin
{
#region Fields
[PluginReference]
private readonly Plugin VehicleDeployedLocks;
private Configuration _config;
private const string PermissionDeployCommand = "carturrets.deploy.command";
private const string PermissionDeployInventory = "carturrets.deploy.inventory";
private const string PermissionFree = "carturrets.free";
private const string PermissionControl = "carturrets.control";
private const string PermissionRemoveAll = "carturrets.removeall";
private const string PermissionLimit2 = "carturrets.limit.2";
private const string PermissionLimit3 = "carturrets.limit.3";
private const string PermissionLimit4 = "carturrets.limit.4";
private const string PermissionSpawnWithCar = "carturrets.spawnwithcar";
private const string PermissionAllModules = "carturrets.allmodules";
private const string PermissionModuleFormat = "carturrets.{0}";
private const string PrefabEntityAutoTurret = "assets/prefabs/npc/autoturret/autoturret_deployed.prefab";
private const string PrefabEntityElectricSwitch = "assets/prefabs/deployable/playerioents/simpleswitch/switch.prefab";
private const string PrefabEffectDeployAutoTurret = "assets/prefabs/npc/autoturret/effects/autoturret-deploy.prefab";
private const string PrefabEffectCodeLockDenied = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab";
private const int ItemIdAutoTurret = -2139580305;
private static readonly Vector3 TurretSwitchPosition = new Vector3(0, 0.36f, -0.32f);
private static readonly Quaternion TurretBackwardRotation = Quaternion.Euler(0, 180, 0);
private static readonly Quaternion TurretSwitchRotation = Quaternion.Euler(0, 180, 0);
private readonly object False = false;
private DynamicHookSubscriber<NetworkableId> _carTurretTracker;
private ProtectionProperties ImmortalProtection;
#endregion
#region Hooks
private void Init()
{
permission.RegisterPermission(PermissionDeployCommand, this);
permission.RegisterPermission(PermissionDeployInventory, this);
permission.RegisterPermission(PermissionFree, this);
permission.RegisterPermission(PermissionControl, this);
permission.RegisterPermission(PermissionRemoveAll, this);
permission.RegisterPermission(PermissionLimit2, this);
permission.RegisterPermission(PermissionLimit3, this);
permission.RegisterPermission(PermissionLimit4, this);
permission.RegisterPermission(PermissionSpawnWithCar, this);
permission.RegisterPermission(PermissionAllModules, this);
foreach (var moduleItemShortName in _config.ModulePositions.Keys)
{
permission.RegisterPermission(GetAutoTurretPermission(moduleItemShortName), this);
}
Unsubscribe(nameof(OnEntitySpawned));
var dynamicHookNames = new List<string>()
{
nameof(OnItemDropped),
nameof(OnEntityKill),
nameof(OnSwitchToggle),
nameof(OnSwitchToggled),
nameof(OnTurretTarget),
};
if (_config.EnableTurretPickup)
{
Unsubscribe(nameof(CanPickupEntity));
Unsubscribe(nameof(canRemove));
}
else
{
dynamicHookNames.Add(nameof(CanPickupEntity));
dynamicHookNames.Add(nameof(canRemove));
}
if (!_config.OnlyPowerTurretsWhileEngineIsOn)
{
Unsubscribe(nameof(OnEngineStartFinished));
Unsubscribe(nameof(OnEngineStopped));
Unsubscribe(nameof(OnTurretStartup));
}
else
{
dynamicHookNames.Add(nameof(OnEngineStartFinished));
dynamicHookNames.Add(nameof(OnEngineStopped));
dynamicHookNames.Add(nameof(OnTurretStartup));
}
if (!_config.RequirePermissionToControl)
{
Unsubscribe(nameof(OnBookmarkControlStarted));
}
else
{
dynamicHookNames.Add(nameof(OnBookmarkControlStarted));
}
_carTurretTracker = new DynamicHookSubscriber<NetworkableId>(this, dynamicHookNames.ToArray());
_carTurretTracker.UnsubscribeAll();
}
private void Unload()
{
if (ImmortalProtection != null)
{
UnityEngine.Object.Destroy(ImmortalProtection);
ImmortalProtection = null;
}
}
private void OnServerInitialized()
{
ImmortalProtection = ScriptableObject.CreateInstance<ProtectionProperties>();
ImmortalProtection.name = "CarTurretsSwitchProtection";
ImmortalProtection.Add(1);
foreach (var car in BaseNetworkable.serverEntities.OfType<ModularCar>().ToArray())
{
foreach (var module in car.AttachedModuleEntities)
{
var turret = GetModuleAutoTurret(module);
if (turret == null)
continue;
try { RefreshCarTurret(turret); } catch {}
}
if (_config.OnlyPowerTurretsWhileEngineIsOn)
{
if (car.IsOn())
{
OnEngineStartFinished(car);
}
else
{
OnEngineStopped(car);
}
}
}
if (_config.SpawnWithCarConfig.Enabled)
{
Subscribe(nameof(OnEntitySpawned));
}
}
private void OnEntitySpawned(ModularCar car)
{
if (!ShouldSpawnTurretsWithCar(car))
return;
// Intentionally using both NextTick and Invoke.// Using NextTick to delay until the items have been added to the module inventory.// Using Invoke since that's what the game uses to delay spawning module entities.NextTick(() =>
{
if (car == null)
return;
car.Invoke(() =>
{
var ownerIdString = car.OwnerID != 0 ? car.OwnerID.ToString() : string.Empty;
var ownerPlayer = FindEntityOwner(car);
var allowedTurretsRemaining = GetCarAutoTurretLimit(car);
for (var i = 0; i < car.AttachedModuleEntities.Count && allowedTurretsRemaining > 0; i++)
{
var vehicleModule = car.AttachedModuleEntities[i];
Vector3 position;
if (!TryGetAutoTurretPositionForModule(vehicleModule, out position) ||
GetModuleAutoTurret(vehicleModule) != null ||
ownerIdString != string.Empty && !HasPermissionToVehicleModule(ownerIdString, vehicleModule) ||
UnityEngine.Random.Range(0, 100) >= GetAutoTurretChanceForModule(vehicleModule) ||
DeployWasBlocked(vehicleModule, ownerPlayer, automatedDeployment: true))
continue;
if (ownerPlayer == null)
DeployAutoTurret(car, vehicleModule, position);
else
DeployAutoTurretForPlayer(car, vehicleModule, position, ownerPlayer);
allowedTurretsRemaining--;
}
}, 0);
});
}
private object CanMoveItem(Item item, PlayerInventory playerInventory, ItemContainerId targetContainerId, int targetSlot, int amount)
{
if (item == null || playerInventory == null)
return null;
var basePlayer = playerInventory.baseEntity;
if (basePlayer == null)
return null;
if (item.parent == null || item.parent.uid == targetContainerId)
return null;
if (playerInventory.loot.containers.Contains(item.parent))
{
// Player is moving an item from the loot panel.var fromCar = item.parent.entityOwner as ModularCar;
if (fromCar == null)
return null;
return HandleRemoveTurret(basePlayer, item, fromCar);
}
// Player is moving an item to the loot panel (module inventory is at position 1).var targetContainer = targetContainerId.Value != 0
? playerInventory.loot.FindContainer(targetContainerId)
: (playerInventory.loot.containers != null && playerInventory.loot.containers.Count > 1 ? playerInventory.loot.containers[1] : null);
var toCar = targetContainer?.entityOwner as ModularCar;
if ((object)toCar == null)
return null;
return HandleAddTurret(basePlayer, item, toCar, targetContainer, targetSlot);
}
private object HandleAddTurret(BasePlayer basePlayer, Item item, ModularCar car, ItemContainer targetContainer, int targetSlot)
{
var player = basePlayer.IPlayer;
var itemid = item.info.itemid;
if (itemid != ItemIdAutoTurret)
return null;
// In case a future update or a plugin adds another storage container to the car.if (car.Inventory.ModuleContainer != targetContainer)
return null;
if (!player.HasPermission(PermissionDeployInventory))
{
ChatMessage(basePlayer, Lang.GenericErrorNoPermission);
return null;
}
if (!VerifyCarHasAutoTurretCapacity(player, car, replyInChat: true))
return null;
if (targetSlot == -1)
{
targetSlot = FindFirstSuitableSocketIndex(car, basePlayer);
}
if (targetSlot == -1)
{
ChatMessage(basePlayer, Lang.DeployErrorNoSuitableModule);
return null;
}
var moduleItem = targetContainer.GetSlot(targetSlot);
if (moduleItem == null)
return null;
var vehicleModule = car.GetModuleForItem(moduleItem);
if (vehicleModule == null)
return null;
if (!HasPermissionToVehicleModule(player.Id, vehicleModule))
{
ChatMessage(basePlayer, Lang.DeployErrorNoPermissionToModule);
return null;
}
if (GetModuleAutoTurret(vehicleModule) != null)
{
ChatMessage(basePlayer, Lang.DeployErrorModuleAlreadyHasTurret);
return null;
}
Vector3 position;
if (!TryGetAutoTurretPositionForModule(vehicleModule, out position)
|| DeployWasBlocked(vehicleModule, basePlayer))
return null;
if (DeployAutoTurretForPlayer(car, vehicleModule, position, basePlayer, GetItemConditionFraction(item)) == null)
return null;
if (!player.HasPermission(PermissionFree))
{
UseItem(basePlayer, item);
}
return False;
}
private object HandleRemoveTurret(BasePlayer basePlayer, Item moduleItem, ModularCar car, ItemContainer targetContainer = null)
{
if (car.Inventory.ModuleContainer != moduleItem.parent)
return null;
var vehicleModule = car.GetModuleForItem(moduleItem);
if (vehicleModule == null)
return null;
var autoTurret = GetModuleAutoTurret(vehicleModule);
if (autoTurret == null)
return null;
if (_config.EnableTurretPickup && autoTurret.pickup.enabled)
{
if (autoTurret.pickup.requireEmptyInv && !autoTurret.inventory.IsEmpty() && !autoTurret.inventory.IsLocked())
{
ChatMessage(basePlayer, Lang.RemoveErrorTurretHasItems);
return False;
}
var turretItem = ItemManager.CreateByItemID(ItemIdAutoTurret);
if (turretItem == null)
return null;
if (turretItem.info.condition.enabled)
{
turretItem.condition = autoTurret.healthFraction * 100;
}
if (targetContainer == null)
{
if (!basePlayer.inventory.GiveItem(turretItem))
{
turretItem.Remove();
return False;
}
}
else if (!turretItem.MoveToContainer(targetContainer))
{
turretItem.Remove();
return False;
}
basePlayer.Command("note.inv", ItemIdAutoTurret, 1);
}
autoTurret.Kill();
return null;
}
private void OnItemDropped(Item item, BaseEntity itemEntity)
{
if (item?.parent == null)
return;
var car = item.parent.entityOwner as ModularCar;
if (car == null)
return;
if (item.info.GetComponent<ItemModVehicleModule>() == null)
return;
var vehicleModule = car.GetModuleForItem(item);
if (vehicleModule == null)
return;
var autoTurret = GetModuleAutoTurret(vehicleModule);
if (autoTurret == null)
return;
if (_config.EnableTurretPickup && autoTurret.pickup.enabled)
{
var turretItem = CreateItemFromAutoTurret(autoTurret);
if (turretItem == null)
return;
var rigidBody = itemEntity.GetComponent<Rigidbody>();
turretItem.Drop(itemEntity.transform.position, rigidBody?.velocity ?? Vector3.zero, itemEntity.transform.rotation);
}
}
// Automatically move a deployed turret when a module moves.// This is not done in the CanMoveItem hook since we don't know if it's being moved yet.private void OnEntityKill(BaseVehicleModule vehicleModule)
{
var moduleItem = vehicleModule.AssociatedItemInstance;
if (moduleItem == null)
return;
var car = vehicleModule.Vehicle as ModularCar;
if (car == null)
return;
var autoTurret = GetModuleAutoTurret(vehicleModule);
if (autoTurret == null)
return;
autoTurret.SetParent(null);
var moduleItem2 = moduleItem;
var car2 = car;
var autoTurret2 = autoTurret;
NextTick(() =>
{
if (car2 == null)
{
autoTurret2.Kill();
}
else
{
var newModule = car2.GetModuleForItem(moduleItem2);
if (newModule == null)
{
autoTurret2.Kill();
}
else
{
autoTurret2.SetParent(newModule);
}
}
});
}
private void OnEntityKill(AutoTurret turret)
{
_carTurretTracker.Remove(turret.net.ID);
}
private object OnSwitchToggle(ElectricSwitch electricSwitch, BasePlayer player)
{
var turret = GetParentTurret(electricSwitch);
if (turret == null)
return null;
var vehicleModule = GetParentVehicleModule(turret);
if (vehicleModule == null)
return null;
var car = vehicleModule.Vehicle as ModularCar;
if (car == null)
return null;
if (!player.CanBuild())
{
// Disallow switching the turret on and off while building blocked.Effect.server.Run(PrefabEffectCodeLockDenied, electricSwitch, 0, Vector3.zero, Vector3.forward);
return False;
}
return null;
}
private void OnSwitchToggled(ElectricSwitch electricSwitch, BasePlayer player)
{
var turret = GetParentTurret(electricSwitch);
if (turret == null)
return;
var vehicleModule = GetParentVehicleModule(turret);
if (vehicleModule == null)
return;
var car = vehicleModule.Vehicle as ModularCar;
if (car == null)
return;
if (electricSwitch.IsOn())
{
if (_config.OnlyPowerTurretsWhileEngineIsOn && !car.IsOn())
{
ChatMessage(player, Lang.InfoPowerRequiresEngine);
}
else
{
try { turret.InitiateStartup(); } catch {}
}
}
else
{
try { turret.InitiateShutdown(); } catch {}
}
}
private object OnTurretTarget(AutoTurret turret, BaseCombatEntity target)
{
if (turret == null || target == null || GetParentVehicleModule(turret) == null)
return null;
if (!_config.TargetAnimals && target is BaseAnimalNPC)
return False;
var basePlayer = target as BasePlayer;
if (basePlayer != null)
{
if (!_config.TargetNPCs && basePlayer.IsNpc)
return False;
if (!_config.TargetPlayers && basePlayer.userID.IsSteamId())
return False;
// Don't target human or NPC players in safe zones, unless they are hostile.if (basePlayer.InSafeZone() && (basePlayer.IsNpc || !basePlayer.IsHostile()))
return False;
return null;
}
return null;
}
private void OnBookmarkControlStarted(ComputerStation station, BasePlayer player, string bookmarkName, AutoTurret turret)
{
var vehicleModule = GetParentVehicleModule(turret);
if (vehicleModule == null)
return;
if (!RCUtils.HasController(turret, player))
return;
if (!HasPermissionToControl(player))
{
RCUtils.RemoveController(turret);
RCUtils.AddFakeViewer(turret);
RCUtils.AddViewer(turret, player);
RCUtils.RemoveController(turret);
station.SetFlag(ComputerStation.Flag_HasFullControl, false);
}
}
// This is only subscribed while config option EnableTurretPickup is false.private object CanPickupEntity(BasePlayer player, AutoTurret turret)
{
if (GetParentVehicleModule(turret) != null)
return False;
return null;
}
// This hook is exposed by plugin: Remover Tool (RemoverTool).// Only subscribed while config option EnableTurretPickup is false.private object canRemove(BasePlayer player, AutoTurret turret)
{
if (GetParentVehicleModule(turret) != null)
return False;
return null;
}
// This is only subscribed while OnlyPowerTurretsWhileEngineIsOn is true.private void OnEngineStartFinished(ModularCar car)
{
foreach (var module in car.AttachedModuleEntities)
{
var turret = GetModuleAutoTurret(module);
if (turret == null || turret.booting || turret.IsOn())
continue;
var electricSwitch = GetTurretSwitch(turret);
if (electricSwitch == null || !electricSwitch.IsOn())
continue;
try { turret.InitiateStartup(); } catch {}
}
}
// This is only subscribed while OnlyPowerTurretsWhileEngineIsOn is true.private void OnEngineStopped(ModularCar car)
{
foreach (var module in car.AttachedModuleEntities)
{
var turret = GetModuleAutoTurret(module);
if (turret == null || !turret.booting && !turret.IsOn())
continue;
var electricSwitch = GetTurretSwitch(turret);
if (electricSwitch == null)
continue;
try { turret.InitiateShutdown(); } catch {}
}
}
// This is only subscribed while OnlyPowerTurretsWhileEngineIsOn is true.private object OnTurretStartup(AutoTurret turret)
{
var module = GetParentVehicleModule(turret);
if (module == null)
return null;
var car = module.Vehicle as ModularCar;
if (car == null)
return null;
if (!car.IsOn())
return False;
return null;
}
#endregion
#region API
[HookMethod(nameof(API_DeployAutoTurret))]
public AutoTurret API_DeployAutoTurret(BaseVehicleModule vehicleModule, BasePlayer basePlayer)
{
var car = vehicleModule.Vehicle as ModularCar;
if (car == null)
return null;
Vector3 position;
if (!TryGetAutoTurretPositionForModule(vehicleModule, out position) ||
GetModuleAutoTurret(vehicleModule) != null ||
DeployWasBlocked(vehicleModule, basePlayer))
return null;
return basePlayer == null
? DeployAutoTurret(car, vehicleModule, position)
: DeployAutoTurretForPlayer(car, vehicleModule, position, basePlayer);
}
#endregion
private object OnEntityUse(BaseEntity entity, BasePlayer player)
{
if (player == null || entity == null)
return null;
// If a seat was directly used, try mounting it first to prevent engine UI from opening.var directSeat = entity as BaseMountable;
if (directSeat != null)
{
if (!directSeat.IsMounted())
{
try
{
directSeat.MountPlayer(player);
if (player.isMounted)
return False;
}
catch {}
}
}
ModularCar car = entity as ModularCar;
if (car == null)
{
var module = entity as BaseVehicleModule;
car = module != null ? module.Vehicle as ModularCar : null;
}
if (car == null)
return null;
// Try to mount the closest available seat to the player's aim to prioritize entering over opening engine UI
try
{
// First, raycast to detect a seat or car surface under the crosshair (e.g., when clicking the door area)
var headRay = player.eyes.HeadRay();
RaycastHit hitInfo;
if (Physics.Raycast(headRay, out hitInfo, 5f, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Collide))
{
var hitEntity = hitInfo.GetEntity();
var hitSeat = hitEntity as BaseMountable ?? hitInfo.collider?.GetComponentInParent<BaseMountable>();
if (hitSeat != null && !hitSeat.IsMounted())
{
hitSeat.MountPlayer(player);
if (player.isMounted)
return False;
}
// If we clicked the car/module (e.g., door), choose the nearest seat to the hit point
if (hitEntity is ModularCar || hitEntity is BaseVehicleModule)
{
BaseMountable nearestToDoor = null;
float nearestDist = float.MaxValue;
var seats = car.GetComponentsInChildren<BaseMountable>();
foreach (var seat in seats)
{
if (seat == null || !seat.isActiveAndEnabled || seat.IsMounted())
continue;
var dist = Vector3.Distance(seat.CenterPoint(), hitInfo.point);
if (dist < nearestDist)
{
nearestDist = dist;
nearestToDoor = seat;
}
}
if (nearestToDoor != null)
{
nearestToDoor.MountPlayer(player);
if (player.isMounted)
return False;
}
}
}
headRay = player.eyes.HeadRay();
BaseMountable bestSeat = null;
float bestDistance = float.MaxValue;
var allSeats = car.GetComponentsInChildren<BaseMountable>();
foreach (var seat in allSeats)
{
if (seat == null || !seat.isActiveAndEnabled || seat.IsMounted())
continue;
var distance = Vector3.Cross(headRay.direction, seat.CenterPoint() - headRay.origin).magnitude;
if (distance < bestDistance)
{
bestDistance = distance;
bestSeat = seat;
}
}
if (bestSeat != null)
{
bestSeat.MountPlayer(player);
if (player.isMounted)
return False;
}
}
catch { }
return null;
}
#region Commands
[Command("carturret")]
private void CommandDeploy(IPlayer player, string cmd, string[] args)
{
if (player.IsServer || !VerifyPermissionAny(player, PermissionDeployCommand))
return;
var basePlayer = player.Object as BasePlayer;
ModularCar car;
BaseVehicleModule vehicleModule;
if (!VerifyCanBuild(player) ||
!VerifyVehicleModuleFound(player, out car, out vehicleModule) ||
!CanAccessVehicle(car, basePlayer) ||
!VerifyCarHasAutoTurretCapacity(player, car) ||
!VerifyPermissionToModule(player, vehicleModule))
return;
if (GetModuleAutoTurret(vehicleModule) != null)
{
ReplyToPlayer(player, Lang.DeployErrorModuleAlreadyHasTurret);
return;
}
Vector3 position;
if (!TryGetAutoTurretPositionForModule(vehicleModule, out position))
{
ReplyToPlayer(player, Lang.DeployErrorUnsupportedModule);
return;
}
Item autoTurretItem = null;
var conditionFraction = 1.0f;
var isFree = player.HasPermission(PermissionFree);
if (!isFree)
{
autoTurretItem = FindPlayerAutoTurretItem(basePlayer);
if (autoTurretItem == null)
{
ReplyToPlayer(player, Lang.DeployErrorNoTurret);
return;
}
conditionFraction = GetItemConditionFraction(autoTurretItem);
}
if (DeployWasBlocked(vehicleModule, basePlayer))
return;
if (DeployAutoTurretForPlayer(car, vehicleModule, position, basePlayer, conditionFraction) != null && !isFree && autoTurretItem != null)
{
UseItem(basePlayer, autoTurretItem);
}
}
[Command("carturrets.removeall")]
private void CommandRemoveAllCarTurrets(IPlayer player, string cmd, string[] args)
{
if (!player.IsServer && !VerifyPermissionAny(player, PermissionRemoveAll))
return;
var turretsRemoved = 0;
foreach (var turret in BaseNetworkable.serverEntities.OfType<AutoTurret>().ToArray())
{
if (turret.GetParentEntity() is BaseVehicleModule)
{
turret.Kill();
turretsRemoved++;
}
}
ReplyToPlayer(player, Lang.RemoveAllSuccess, turretsRemoved);
}
#endregion
#region Helper Methods - Command Checks
private bool VerifyPermissionAny(IPlayer player, params string[] permissionNames)
{
foreach (var perm in permissionNames)
{
if (player.HasPermission(perm))
return true;
}
ReplyToPlayer(player, Lang.GenericErrorNoPermission);
return false;
}
private bool VerifyCanBuild(IPlayer player)
{
if ((player.Object as BasePlayer).CanBuild())
return true;
ReplyToPlayer(player, Lang.GenericErrorBuildingBlocked);
return false;
}
private bool VerifyVehicleModuleFound(IPlayer player, out ModularCar car, out BaseVehicleModule vehicleModule)
{
var basePlayer = player.Object as BasePlayer;
var entity = GetLookEntity(basePlayer);
vehicleModule = entity as BaseVehicleModule;
if (vehicleModule != null)
{
car = vehicleModule.Vehicle as ModularCar;
if (car != null)
return true;
ReplyToPlayer(player, Lang.DeployErrorNoCarFound);
return false;
}
car = entity as ModularCar;
if (car == null)
{
var lift = entity as ModularCarGarage;
car = lift?.carOccupant;
if (car == null)
{
ReplyToPlayer(player, Lang.DeployErrorNoCarFound);
return false;
}
}
var closestModule = FindClosestModuleToAim(car, basePlayer);
if (closestModule != null)
{
vehicleModule = closestModule;
return true;
}
ReplyToPlayer(player, Lang.DeployErrorNoModules);
return false;
}
private bool VerifyCarHasAutoTurretCapacity(IPlayer player, ModularCar car, bool replyInChat = false)
{
var limit = GetCarAutoTurretLimit(car);
if (GetCarTurretCount(car) < limit)
return true;
if (replyInChat)
{
ChatMessage(player.Object as BasePlayer, Lang.DeployErrorTurretLimit, limit);
}
else
{
ReplyToPlayer(player, Lang.DeployErrorTurretLimit, limit);
}
return false;
}
private bool VerifyPermissionToModule(IPlayer player, BaseVehicleModule vehicleModule)
{
if (HasPermissionToVehicleModule(player.Id, vehicleModule))
return true;
ReplyToPlayer(player, Lang.DeployErrorNoPermissionToModule);
return false;
}
#endregion
#region Helpers
private static class RCUtils
{
public static bool HasController(IRemoteControllable controllable, BasePlayer player)
{
return controllable.ControllingViewerId?.SteamId == player.userID;
}
public static void RemoveController(IRemoteControllable controllable)
{
var controllerId = controllable.ControllingViewerId;
if (controllerId.HasValue)
{
controllable.StopControl(controllerId.Value);
}
}
public static bool AddViewer(IRemoteControllable controllable, BasePlayer player)
{
return controllable.InitializeControl(new CameraViewerId(player.userID, 0));
}
public static bool AddFakeViewer(IRemoteControllable controllable)
{
return controllable.InitializeControl(new CameraViewerId());
}
}
private static bool DeployWasBlocked(BaseVehicleModule vehicleModule, BasePlayer basePlayer, bool automatedDeployment = false)
{
var hookResult = Interface.CallHook("OnCarAutoTurretDeploy", vehicleModule, basePlayer, automatedDeployment);
return hookResult is bool && (bool)hookResult == false;
}
private static BaseVehicleModule FindClosestModuleToAim(ModularCar car, BasePlayer basePlayer)
{
var headRay = basePlayer.eyes.HeadRay();
BaseVehicleModule closestModule = null;
float closestDistance = 0;
for (var socketIndex = 0; socketIndex < car.TotalSockets; socketIndex++)
{
BaseVehicleModule currentModule;
if (car.TryGetModuleAt(socketIndex, out currentModule) && currentModule.FirstSocketIndex == socketIndex)
{
var currentDistance = Vector3.Cross(headRay.direction, currentModule.CenterPoint() - headRay.origin).magnitude;
if (ReferenceEquals(closestModule, null))
{
closestModule = currentModule;
closestDistance = currentDistance;
}
else if (currentDistance < closestDistance)
{
closestModule = currentModule;
closestDistance = currentDistance;
}
}
}
return closestModule;
}
private static void UseItem(BasePlayer basePlayer, Item item, int amountToConsume = 1)
{
item.amount -= amountToConsume;
if (item.amount <= 0)
{
item.RemoveFromContainer();
item.Remove();
}
else
{
item.MarkDirty();
}
basePlayer.Command("note.inv", item.info.itemid, -amountToConsume);
}
private static float GetItemConditionFraction(Item item)
{
return item.hasCondition ? item.condition / item.info.condition.max : 1.0f;
}
private static Item FindPlayerAutoTurretItem(BasePlayer basePlayer)
{
return basePlayer.inventory.FindItemByItemID(ItemIdAutoTurret);
}
private static Item CreateItemFromAutoTurret(AutoTurret autoTurret)
{
var turretItem = ItemManager.CreateByItemID(ItemIdAutoTurret);
if (turretItem == null)
return null;
if (turretItem.info.condition.enabled)
{
turretItem.condition = autoTurret.healthFraction * 100;
}
return turretItem;
}
private static string GetAutoTurretPermissionForModule(BaseVehicleModule vehicleModule)
{
return GetAutoTurretPermission(vehicleModule.AssociatedItemDef.shortname);
}
private static string GetAutoTurretPermission(string moduleItemShortName)
{
return string.Format(PermissionModuleFormat, moduleItemShortName);
}
private static int GetCarTurretCount(ModularCar car)
{
var numTurrets = 0;
foreach (var module in car.AttachedModuleEntities)
{
var turret = GetModuleAutoTurret(module);
if (turret != null)
{
numTurrets++;
}
}
return numTurrets;
}
private static T GetChildOfType<T>(BaseEntity entity) where T : BaseEntity
{
foreach (var child in entity.children)
{
var childOfType = child as T;
if (childOfType != null)
return childOfType;
}
return null;
}
private static AutoTurret GetModuleAutoTurret(BaseVehicleModule vehicleModule)
{
return GetChildOfType<AutoTurret>(vehicleModule);
}
private static ElectricSwitch GetTurretSwitch(AutoTurret turret)
{
return GetChildOfType<ElectricSwitch>(turret);
}
private static bool IsNaturalCarSpawn(ModularCar car)
{
var spawnable = car.GetComponent<Spawnable>();
return spawnable != null && spawnable.Population != null;
}
private static BaseVehicleModule GetParentVehicleModule(BaseEntity entity)
{
return entity.GetParentEntity() as BaseVehicleModule;
}
private static AutoTurret GetParentTurret(BaseEntity entity)
{
return entity.GetParentEntity() as AutoTurret;
}
private static void RunOnEntityBuilt(Item turretItem, AutoTurret autoTurret)
{
Interface.CallHook("OnEntityBuilt", turretItem.GetHeldEntity(), autoTurret.gameObject);
}
private static void HideInputsAndOutputs(IOEntity ioEntity)
{
// Hide the inputs and outputs on the client.foreach (var input in ioEntity.inputs)
{
input.type = IOEntity.IOType.Generic;
}
foreach (var output in ioEntity.outputs)
{
output.type = IOEntity.IOType.Generic;
}
}
private static Quaternion GetIdealTurretRotation(ModularCar car, BaseVehicleModule vehicleModule)
{
var lastSocketIndex = vehicleModule.FirstSocketIndex + vehicleModule.GetNumSocketsTaken() - 1;
var faceForward = car.TotalSockets == 2
? vehicleModule.FirstSocketIndex == 0
: car.TotalSockets == 3
? lastSocketIndex <= 1
: vehicleModule.FirstSocketIndex <= 1;
return faceForward ? Quaternion.identity : TurretBackwardRotation;
}
private static void RemoveColliders<T>(BaseEntity entity) where T : Collider
{
foreach (var collider in entity.GetComponentsInChildren<T>())
{
UnityEngine.Object.DestroyImmediate(collider);
}
}
private static void RemoveGroundWatch(BaseEntity entity)
{
UnityEngine.Object.DestroyImmediate(entity.GetComponent<DestroyOnGroundMissing>());
UnityEngine.Object.DestroyImmediate(entity.GetComponent<GroundWatch>());
}
private static BaseEntity GetLookEntity(BasePlayer basePlayer, float maxDistance = 3)
{
RaycastHit hit;
return Physics.Raycast(basePlayer.eyes.HeadRay(), out hit, maxDistance, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Ignore)
? hit.GetEntity()
: null;
}
private static BasePlayer FindEntityOwner(BaseEntity entity)
{
return entity.OwnerID != 0 ? BasePlayer.FindByID(entity.OwnerID) : null;
}
private bool HasPermissionToControl(BasePlayer player)
{
if (!_config.RequirePermissionToControl)
return true;
return permission.UserHasPermission(player.UserIDString, PermissionControl);
}
private void SetupCarTurret(AutoTurret turret)
{
turret.gameObject.layer = (int)Rust.Layer.Vehicle_Detailed;
RemoveColliders<MeshCollider>(turret);
RemoveGroundWatch(turret);
_carTurretTracker.Add(turret.net.ID);
}
private AutoTurret DeployAutoTurret(ModularCar car, BaseVehicleModule vehicleModule, Vector3 position, float conditionFraction = 1, ulong ownerId = 0)
{
var autoTurret = GameManager.server.CreateEntity(PrefabEntityAutoTurret, position, GetIdealTurretRotation(car, vehicleModule)) as AutoTurret;
if (autoTurret == null)
return null;
try
{
autoTurret.SetFlag(IOEntity.Flag_HasPower, true);
autoTurret.SetParent(vehicleModule);
autoTurret.OwnerID = ownerId;
autoTurret.Spawn();
autoTurret.SetHealth(autoTurret.MaxHealth() * conditionFraction);
}
catch
{
try { autoTurret.Kill(); } catch {}
return null;
}
SetupCarTurret(autoTurret);
try { AttachTurretSwitch(autoTurret); } catch {}
try { Effect.server.Run(PrefabEffectDeployAutoTurret, autoTurret.transform.position); } catch {}
return autoTurret;
}
private void RefreshCarTurret(AutoTurret turret)
{
SetupCarTurret(turret);
var turretSwitch = GetTurretSwitch(turret);
if (turretSwitch != null)
{
SetupTurretSwitch(turretSwitch);
}
}
private ElectricSwitch AttachTurretSwitch(AutoTurret autoTurret)
{
var turretSwitch = GameManager.server.CreateEntity(PrefabEntityElectricSwitch, autoTurret.transform.TransformPoint(TurretSwitchPosition), autoTurret.transform.rotation * TurretSwitchRotation) as ElectricSwitch;
if (turretSwitch == null)
return null;
try
{
SetupTurretSwitch(turretSwitch);
turretSwitch.Spawn();
turretSwitch.SetParent(autoTurret, true);
}
catch
{
try { turretSwitch.Kill(); } catch {}
return null;
}
return turretSwitch;
}
private void SetupTurretSwitch(ElectricSwitch electricSwitch)
{
electricSwitch.pickup.enabled = false;
electricSwitch.SetFlag(IOEntity.Flag_HasPower, true);
electricSwitch.baseProtection = ImmortalProtection;
RemoveColliders<Collider>(electricSwitch);
RemoveGroundWatch(electricSwitch);
HideInputsAndOutputs(electricSwitch);
if (electricSwitch != null && electricSwitch.HasParent())
{
var transform = electricSwitch.transform;
if (transform.localPosition != TurretSwitchPosition)
{
transform.localPosition = TurretSwitchPosition;
try { electricSwitch.InvalidateNetworkCache(); } catch {}
try { electricSwitch.SendNetworkUpdate_Position(); } catch {}
}
}
}
private bool CanAccessVehicle(BaseVehicle vehicle, BasePlayer basePlayer, bool provideFeedback = true)
{
if (VehicleDeployedLocks == null)
return true;
var canAccess = VehicleDeployedLocks.Call("API_CanAccessVehicle", basePlayer, vehicle, provideFeedback);
return !(canAccess is bool) || (bool)canAccess;
}
private int FindFirstSuitableSocketIndex(ModularCar car, BasePlayer basePlayer)
{
for (var socketIndex = 0; socketIndex < car.TotalSockets; socketIndex++)
{
BaseVehicleModule currentModule;
if (car.TryGetModuleAt(socketIndex, out currentModule)
&& currentModule.FirstSocketIndex == socketIndex
&& HasPermissionToVehicleModule(basePlayer.UserIDString, currentModule)
&& GetModuleAutoTurret(currentModule) == null)
{
return socketIndex;
}
}
return -1;
}
private int GetCarAutoTurretLimit(ModularCar car)
{
var defaultLimit = _config.DefaultLimitPerCar;
if (car.OwnerID == 0)
return defaultLimit;
var ownerIdString = car.OwnerID.ToString();
if (defaultLimit < 4 && permission.UserHasPermission(ownerIdString, PermissionLimit4))
return 4;
if (defaultLimit < 3 && permission.UserHasPermission(ownerIdString, PermissionLimit3))
return 3;
if (defaultLimit < 2 && permission.UserHasPermission(ownerIdString, PermissionLimit2))
return 2;
return defaultLimit;
}
private bool HasPermissionToVehicleModule(string userId, BaseVehicleModule vehicleModule)
{
return permission.UserHasPermission(userId, PermissionAllModules)
|| permission.UserHasPermission(userId, GetAutoTurretPermissionForModule(vehicleModule));
}
private bool ShouldSpawnTurretsWithCar(ModularCar car)
{
var spawnWithCarConfig = _config.SpawnWithCarConfig;
if (!spawnWithCarConfig.Enabled)
return false;
if (IsNaturalCarSpawn(car))
return spawnWithCarConfig.NaturalCarSpawns.Enabled;
if (!spawnWithCarConfig.OtherCarSpawns.Enabled)
return false;
if (!spawnWithCarConfig.OtherCarSpawns.RequirePermission)
return true;
return car.OwnerID != 0 && permission.UserHasPermission(car.OwnerID.ToString(), PermissionSpawnWithCar);
}
private bool TryGetAutoTurretPositionForModule(BaseVehicleModule vehicleModule, out Vector3 position)
{
return _config.ModulePositions.TryGetValue(vehicleModule.AssociatedItemDef.shortname, out position);
}
private int GetAutoTurretChanceForModule(BaseVehicleModule vehicleModule)
{
int chance;
return _config.SpawnWithCarConfig.SpawnChanceByModule.TryGetValue(vehicleModule.AssociatedItemDef.shortname, out chance)
? chance
: 0;
}
private AutoTurret DeployAutoTurretForPlayer(ModularCar car, BaseVehicleModule vehicleModule, Vector3 position, BasePlayer basePlayer, float conditionFraction = 1)
{
var autoTurret = DeployAutoTurret(car, vehicleModule, position, conditionFraction, basePlayer.userID);
if (autoTurret == null)
return null;
// Other plugins may have already automatically authorized the player.if (!autoTurret.IsAuthed(basePlayer))
{
autoTurret.authorizedPlayers.Add(new ProtoBuf.PlayerNameID
{
userid = basePlayer
// ... [Code preview truncated due to length. Full source in attached file] ...