C#:
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Globalization;
using Rust;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
namespace Oxide.Plugins {
[Info("Tank Commander", "k1lly0u", "0.2.2")]
[Description("Drive tanks, shoot stuff")]
class TankCommander : RustPlugin {
#region Fields
[PluginReference]
private Plugin Friends, Clans, Godmode;
private static TankCommander ins;
private List<APCController> controllers = new List<APCController>();
private Dictionary<CommandType, BUTTON> controlButtons;
private int rocketId;
private int mgId;
private const string APC_PREFAB = "assets/prefabs/npc/m2bradley/bradleyapc.prefab";
private const string CHAIR_PREFAB = "assets/prefabs/vehicle/seats/miniheliseat.prefab";
private const string UI_HEALTH = "TCUI_Health";
private const string UI_AMMO_MG = "TCUI_Ammo_MG";
private const string UI_AMMO_ROCKET = "TCUI_Ammo_Rocket";
private const int TARGET_LAYERS = ~(1 << 10 | 1 << 13 | 1 << 17 | 1 << 18 | 1 << 20 | 1 << 28 | 1 << 29);
#endregion
#region Oxide Hooks
private void Loaded() {
lang.RegisterMessages(Messages, this);
permission.RegisterPermission("tankcommander.admin", this);
permission.RegisterPermission("tankcommander.use", this);
}
private void OnServerInitialized() {
ins = this;
ConvertControlButtons();
rocketId = ItemManager.itemList.Find(x => x.shortname == configData.Weapons.Cannon.Type)?.itemid ?? 0;
mgId = ItemManager.itemList.Find(x => x.shortname == configData.Weapons.MG.Type)?.itemid ?? 0;
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
if (entity is BradleyAPC) {
APCController controller = entity.GetComponent<APCController>();
if (controller != null) {
controller.ManageDamage(info);
return null;
}
}
if (entity is BasePlayer) {
if (IsOnboardAPC(entity as BasePlayer)) return true;
if (info.Initiator is BradleyAPC) {
APCController controller = (info.Initiator as BradleyAPC).GetComponent<APCController>();
if (controller != null) {
if (!controller.HasCommander) return true;
}
}
}
else if (entity is BaseMountable) {
if (entity.GetComponentInParent<APCController>()) return true;
}
return null;
}
private void OnPlayerInput(BasePlayer player, InputState input) {
if (player == null || IsOnboardAPC(player) || !HasPermission(player, "tankcommander.use")) return;
if (configData.Inventory.Enabled && input.WasJustPressed(controlButtons[CommandType.Inventory])) {
RaycastHit hit;
if (Physics.SphereCast(player.eyes.position, 0.5f, Quaternion.Euler(player.serverInput.current.aimAngles) * Vector3.forward, out hit, 3f)) {
APCController controller = hit.GetEntity()?.GetComponent<APCController>();
if (controller != null && !controller.HasCommander) OpenTankInventory(player, controller);
}
return;
}
if (input.WasJustPressed(controlButtons[CommandType.EnterExit])) {
RaycastHit hit;
if (Physics.SphereCast(player.eyes.position, 0.5f, Quaternion.Euler(player.serverInput.current.aimAngles) * Vector3.forward, out hit, 3f)) {
APCController controller = hit.GetEntity()?.GetComponent<APCController>();
if (controller != null) {
if (!controller.HasCommander) {
controller.MountPlayer(player);
}
else {
if (configData.Passengers.Enabled && controller.CanMountPlayer()) {
BasePlayer commander = controller.Commander;
if (!configData.Passengers.UseFriends && !configData.Passengers.UseClans) {
controller.MountPlayer(player);
return;
}
if (configData.Passengers.UseFriends && AreFriends(commander.userID, player.userID)) {
controller.MountPlayer(player);
return;
}
if (configData.Passengers.UseClans && IsClanmate(commander.userID, player.userID)) {
controller.MountPlayer(player);
return;
}
player.ChatMessage(msg("not_friend", player.UserIDString));
}
else player.ChatMessage(msg("in_use", player.UserIDString));
}
}
}
}
}
private object CanDismountEntity(BasePlayer player, BaseMountable mountable) {
APCController controller = mountable?.GetComponentInParent<APCController>();
if (controller != null) {
controller.DismountPlayer(player);
return false;
}
return null;
}
private void OnPlayerDisconnected(BasePlayer player) {
APCController controller;
if (IsOnboardAPC(player, out controller)) {
controller.DismountPlayer(player);
}
}
private object OnRunPlayerMetabolism(PlayerMetabolism metabolism, BaseCombatEntity entity) {
BasePlayer player = entity.ToPlayer();
if (player == null || !IsOnboardAPC(player)) return null;
if (Godmode && (bool)Godmode.Call("IsGod", player.UserIDString)) return null;
return true;
}
private void Unload() {
foreach (APCController controller in controllers) UnityEngine.Object.Destroy(controller);
APCController[] objects = UnityEngine.Object.FindObjectsOfType<APCController>();
if (objects != null) {
foreach(APCController obj in objects) UnityEngine.Object.Destroy(obj);
}
controllers.Clear();
configData = null;
ins = null;
}
#endregion
#region Functions
private bool IsOnboardAPC(BasePlayer player) => player?.GetMounted()?.GetComponentInParent<APCController>() != null;
private bool IsOnboardAPC(BasePlayer player, out APCController controller) {
controller = player?.GetMounted()?.GetComponentInParent<APCController>();
return controller != null;
}
private T ParseType<T>(string type) => (T)Enum.Parse(typeof(T), type, true);
private bool HasPermission(BasePlayer player, string perm) => permission.UserHasPermission(player.UserIDString, perm) || permission.UserHasPermission(player.UserIDString, "tankcommander.admin");
private void ConvertControlButtons() {
controlButtons = new Dictionary<CommandType, BUTTON> {
[CommandType.EnterExit]
= ParseType<BUTTON>(configData.Buttons.Enter), [CommandType.Lights] = ParseType<BUTTON>(configData.Buttons.Lights), [CommandType.Inventory] = ParseType<BUTTON>(configData.Buttons.Inventory), [CommandType.Boost] = ParseType<BUTTON>(configData.Buttons.Boost), [CommandType.Cannon] = ParseType<BUTTON>(configData.Buttons.Cannon), [CommandType.Coax] = ParseType<BUTTON>(configData.Buttons.Coax), [CommandType.MG] = ParseType<BUTTON>(configData.Buttons.MG)
}
;
}
private void OpenTankInventory(BasePlayer player, APCController controller) {
player.inventory.loot.Clear();
player.inventory.loot.entitySource = controller.entity;
player.inventory.loot.itemSource = null;
player.inventory.loot.AddContainer(controller.inventory);
player.inventory.loot.SendImmediate();
player.ClientRPCPlayer(null, player, "RPC_OpenLootPanel", "generic");
player.SendNetworkUpdate();
}
#endregion
#region Component
private enum CommandType {
EnterExit, Lights, Inventory, Boost, Cannon, Coax, MG
}
private class APCController : MonoBehaviour {
public BradleyAPC entity;
private Rigidbody rigidBody;
private BaseMountable[] mountables;
private WheelCollider[] leftWheels;
private WheelCollider[] rightWheels;
public ItemContainer inventory;
private float accelTimeTaken;
private float accelTimeToTake = 3f;
private float forwardTorque = 2000f;
private float maxBrakeTorque = 50f;
private float turnTorque = 2000f;
private float lastFireCannon;
private float lastFireMG;
private float lastFireCoax;
private bool isDying = false;
private RaycastHit eyeRay;
private Vector3 mouseInput = Vector3.zero;
private Vector3 aimVector = Vector3.forward;
private Vector3 aimVectorTop = Vector3.forward;
private Dictionary<CommandType, BUTTON> controlButtons;
private ConfigData.WeaponOptions.WeaponSystem cannon;
private ConfigData.WeaponOptions.WeaponSystem mg;
private ConfigData.WeaponOptions.WeaponSystem coax;
private ConfigData.CrushableTypes crushables;
public bool HasCommander {
get {
return mountables[0].GetMounted() != null;
}
}
public BasePlayer Commander {
get;
private set;
}
private void Awake() {
entity = GetComponent<BradleyAPC>();
entity.enabled = false;
entity.CancelInvoke(entity.UpdateTargetList);
entity.CancelInvoke(entity.UpdateTargetVisibilities);
rigidBody = entity.myRigidBody;
leftWheels = entity.leftWheels;
rightWheels = entity.rightWheels;
forwardTorque = configData.Movement.ForwardTorque;
turnTorque = configData.Movement.TurnTorque;
maxBrakeTorque = configData.Movement.BrakeTorque;
accelTimeToTake = configData.Movement.Acceleration;
controlButtons = ins.controlButtons;
cannon = configData.Weapons.Cannon;
mg = configData.Weapons.MG;
coax = configData.Weapons.Coax;
crushables = configData.Crushables;
if (configData.Inventory.Enabled) {
inventory = new ItemContainer();
inventory.ServerInitialize(null, configData.Inventory.Size);
if ((int)inventory.uid == 0) inventory.GiveUID();
}
CreateMountPoints();
SetInitialAimDirection();
}
private void OnDestroy() {
DismountAll();
for (int i = 0; i < mountables.Length; i++) {
BaseMountable mountable = mountables[i];
if (mountable != null && !mountable.IsDestroyed) mountable.Kill();
}
if (entity != null && !entity.IsDestroyed) entity.Kill();
}
private void Update() {
if (Commander != null) {
DoMovementControls();
AdjustAiming();
DoWeaponControls();
entity.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
}
private void LateUpdate() {
mouseInput = Commander?.serverInput?.current?.mouseDelta ?? Vector2.zero;
}
private void OnCollisionEnter(Collision collision) {
if (!HasCommander) return;
GameObject gObject = collision.gameObject;
if (gObject == null) return;
if (crushables.Players) {
BasePlayer triggerPlayer = gObject.GetComponentInParent<BasePlayer>();
if (triggerPlayer != null && !IsPassenger(triggerPlayer)) {
triggerPlayer.Die(new HitInfo(Commander, triggerPlayer, DamageType.Blunt, 200f));
return;
}
}
if (crushables.Animals) {
BaseNpc npc = gObject.GetComponentInParent<BaseNpc>();
if (npc != null) {
npc.Die(new HitInfo(Commander, npc, DamageType.Blunt, 200f));
return;
}
}
float impactForce = CalculateImpactForce(collision);
if (crushables.Buildings) {
BuildingBlock buildingBlock = gObject.GetComponentInParent<BuildingBlock>();
if (buildingBlock != null && impactForce >= crushables.GradeForce[buildingBlock.grade.ToString()]) {
buildingBlock.Die(new HitInfo(Commander, buildingBlock, DamageType.Blunt, 1000f));
return;
}
SimpleBuildingBlock simpleBlock = gObject.GetComponentInParent<SimpleBuildingBlock>();
if (simpleBlock != null && impactForce >= crushables.WallForce) {
simpleBlock.Die(new HitInfo(Commander, simpleBlock, DamageType.Blunt, 1500));
return;
}
}
if (crushables.Loot) {
LootContainer loot = gObject.GetComponentInParent<LootContainer>();
if (loot != null) {
loot.Die(new HitInfo(Commander, loot, DamageType.Blunt, 200f));
return;
}
}
if (crushables.Resources) {
ResourceEntity resource = gObject.GetComponentInParent<ResourceEntity>();
if (resource != null && impactForce >= crushables.ResourceForce) {
resource.Kill(BaseNetworkable.DestroyMode.None);
return;
}
}
}
private float CalculateImpactForce(Collision col) {
float impactVelocityX = rigidBody.velocity.x;
impactVelocityX *= Mathf.Sign(impactVelocityX);
float impactVelocityY = rigidBody.velocity.y;
impactVelocityY *= Mathf.Sign(impactVelocityY);
float impactVelocity = impactVelocityX + impactVelocityY;
float impactForce = impactVelocity * rigidBody.mass;
impactForce *= Mathf.Sign(impactForce);
return impactForce;
}
private bool IsPassenger(BasePlayer player) {
BaseMountable mountable = player.GetMounted();
if (mountable == null) return false;
return mountables.Contains(mountable);
}
#region Mounting
private List<KeyValuePair<Vector3, Vector3>> mountOffsets = new List<KeyValuePair<Vector3, Vector3>>() {
new KeyValuePair<Vector3, Vector3>(new Vector3(0.6f, 1.9f, -0.6f), Vector3.zero), new KeyValuePair<Vector3, Vector3>(new Vector3(-0.6f, 0.4f, -1.3f), new Vector3(0f, 90f, 0f)), new KeyValuePair<Vector3, Vector3>(new Vector3(-0.6f, 0.4f, -2f), new Vector3(0f, 90f, 0f)), new KeyValuePair<Vector3, Vector3>(new Vector3(0.5f, 0.4f, -1.3f), new Vector3(0f, -90f, 0f)), new KeyValuePair<Vector3, Vector3>(new Vector3(0.5f, 0.4f, -2f), new Vector3(0f, -90f, 0f)),
}
;
private void CreateMountPoints() {
int passengers = Mathf.Clamp(configData.Passengers.Max, 0, 4) + 1;
mountables = new BaseMountable[passengers];
for (int i = 0; i < passengers; i++) {
CreateMountPoint(i);
}
}
private void CreateMountPoint(int index) {
KeyValuePair<Vector3, Vector3> offset = mountOffsets[index];
BaseMountable mountable = GameManager.server.CreateEntity(CHAIR_PREFAB, entity.transform.position) as BaseMountable;
mountable.enableSaving = false;
mountable.Spawn();
Destroy(mountable.GetComponent<DestroyOnGroundMissing>());
Destroy(mountable.GetComponent<GroundWatch>());
mountable.SetParent(entity, index == 0 ? (uint)4239370974 : (uint)0, false, true);
mountable.transform.localPosition = offset.Key;
mountable.transform.localRotation = Quaternion.Euler(offset.Value);
GameObject tr = new GameObject("Seat Transform");
tr.transform.SetParent(mountable.transform);
tr.transform.localPosition = index > 0 ? new Vector3(0, 0, -3f) : new Vector3(3f, 0, 0);
mountable.dismountPositions = new Transform[] {
tr.transform
}
;
mountables[index] = mountable;
}
public bool CanMountPlayer() {
for (int i = 0; i < mountables.Length; i++) {
BaseMountable mountable = mountables[i];
if (!mountable.IsMounted()) return true;
}
return false;
}
public void MountPlayer(BasePlayer player) {
for (int i = 0; i < mountables.Length; i++) {
BaseMountable mountable = mountables[i];
if (mountable.IsMounted()) continue;
player.EnsureDismounted();
mountable._mounted = player;
player.MountObject(mountable, 0);
player.MovePosition(mountable.mountAnchor.transform.position);
player.transform.rotation = mountable.mountAnchor.transform.rotation;
player.ServerRotation = mountable.mountAnchor.transform.rotation;
player.OverrideViewAngles(mountable.mountAnchor.transform.rotation.eulerAngles);
player.eyes.NetworkUpdate(mountable.mountAnchor.transform.rotation);
player.ClientRPCPlayer(null, player, "ForcePositionTo", player.transform.position);
mountable.SetFlag(BaseEntity.Flags.Busy, true, false);
if (i > 0) player.SetPlayerFlag(BasePlayer.PlayerFlags.ThirdPersonViewmode, true);
OnEntityMounted(player, i == 0);
return;
}
}
public void DismountPlayer(BasePlayer player) {
if (player == null) return;
BaseMountable mountable = player.GetMounted();
if (mountable == null) return;
player.PauseFlyHackDetection(1f);
Vector3 dismountPosition = mountable.dismountPositions[0].position;
if (TerrainMeta.HeightMap.GetHeight(dismountPosition) > dismountPosition.y) dismountPosition.y = TerrainMeta.HeightMap.GetHeight(dismountPosition) + 0.5f;
player.DismountObject();
player.transform.rotation = Quaternion.identity;
player.MovePosition(dismountPosition);
player.eyes.NetworkUpdate(Quaternion.identity);
player.SetPlayerFlag(BasePlayer.PlayerFlags.ThirdPersonViewmode, false);
player.SendNetworkUpdateImmediate(false);
player.ClientRPCPlayer(null, player, "ForcePositionTo", dismountPosition);
mountable._mounted = null;
if (player == Commander) {
entity.CancelInvoke(DrawTargeting);
Commander = null;
}
DestroyAllUI(player);
}
private void DismountAll() {
for (int i = 0; i < mountables.Length; i++) {
BasePlayer player = mountables[i].GetMounted();
if (player != null) DismountPlayer(player);
}
Commander = null;
}
private void OnEntityMounted(BasePlayer player, bool isOperator) {
if (isOperator) {
string ctrlStr = msg("controls", player.UserIDString);
if (configData.Weapons.Cannon.Enabled) ctrlStr += $"\n{string.Format(msg("fire_cannon", player.UserIDString), configData.Buttons.Cannon)}";
if (configData.Weapons.Coax.Enabled) ctrlStr += $"\n{string.Format(msg("fire_coax", player.UserIDString), configData.Buttons.Coax)}";
if (configData.Weapons.MG.Enabled) ctrlStr += $"\n{string.Format(msg("fire_mg", player.UserIDString), configData.Buttons.MG)}";
ctrlStr += $"\n{string.Format(msg("speed_boost", player.UserIDString), configData.Buttons.Boost)}";
ctrlStr += $"\n{string.Format(msg("enter_exit", player.UserIDString), configData.Buttons.Enter)}";
ctrlStr += $"\n{string.Format(msg("toggle_lights", player.UserIDString), configData.Buttons.Lights)}";
if (configData.Inventory.Enabled) ctrlStr += $"\n{string.Format(msg("access_inventory", player.UserIDString), configData.Buttons.Inventory)}";
player.ChatMessage(ctrlStr);
CreateHealthUI(player, this);
CreateMGAmmoUI(player, this);
CreateRocketAmmoUI(player, this);
Commander = player;
entity.InvokeRepeating(DrawTargeting, DDRAW_UPDATE_TIME, DDRAW_UPDATE_TIME);
}
else {
player.ChatMessage(string.Format(msg("enter_exit", player.UserIDString), configData.Buttons.Enter));
if (configData.Inventory.Enabled) player.ChatMessage(string.Format(msg("access_inventory", player.UserIDString), configData.Buttons.Inventory));
CreateHealthUI(player, this);
}
}
#endregion
#region Weapons
private void SetInitialAimDirection() {
aimVector = aimVectorTop = entity.transform.forward.normalized;
entity.AimWeaponAt(entity.mainTurret, entity.coaxPitch, aimVector, -90f, 7f, 360f, null);
entity.AimWeaponAt(entity.mainTurret, entity.CannonPitch, aimVector, -90f, 7f, 360f, null);
entity.AimWeaponAt(entity.topTurretYaw, entity.topTurretPitch, aimVectorTop, -360f, 360f, 360f, entity.mainTurret);
entity.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
private void DoWeaponControls() {
if (Commander.serverInput.WasJustPressed(controlButtons[CommandType.Lights])) ToggleLights();
if (Commander.serverInput.WasJustPressed(controlButtons[CommandType.Cannon])) FireCannon();
if (Commander.serverInput.IsDown(controlButtons[CommandType.MG]) || Commander.serverInput.WasJustPressed(controlButtons[CommandType.MG])) FireMG();
if (Commander.serverInput.IsDown(controlButtons[CommandType.Coax]) || Commander.serverInput.WasJustPressed(controlButtons[CommandType.Coax])) FireCoax();
}
private void AdjustAiming() {
if (Mathf.Abs(mouseInput.x) < 0.5f) mouseInput.x = 0;
if (Mathf.Abs(mouseInput.y) < 0.5f) mouseInput.y = 0;
if (mouseInput == Vector3.zero) return;
Vector3 direction = Quaternion.Euler(entity.mainTurret.transform.eulerAngles + (new Vector3(-mouseInput.y, mouseInput.x, 0f) * 5f)) * Vector3.forward;
Ray ray = new Ray(Commander.eyes.transform.position, direction);
Vector3 hitPoint;
if (Physics.Raycast(ray, out eyeRay, 5000f, TARGET_LAYERS)) hitPoint = eyeRay.point;
else hitPoint = ray.GetPoint(100);
Vector3 desiredAim = (hitPoint - Commander.eyes.transform.position).normalized;
Vector3 desiredAimTop = (hitPoint - entity.topTurretEyePos.transform.position).normalized;
aimVector = entity.turretAimVector = Vector3.Lerp(aimVector, desiredAim, Time.deltaTime * 3f);
aimVectorTop = entity.topTurretAimVector = Vector3.Lerp(aimVectorTop, desiredAimTop, Time.deltaTime * 3f);
entity.AimWeaponAt(entity.mainTurret, entity.coaxPitch, aimVector, -90f, 7f, 360f, null);
entity.AimWeaponAt(entity.mainTurret, entity.CannonPitch, aimVector, -90f, 7f, 360f, null);
entity.AimWeaponAt(entity.topTurretYaw, entity.topTurretPitch, aimVectorTop, -360f, 360f, 360f, entity.mainTurret);
}
private const float DDRAW_UPDATE_TIME = 0.02f;
private void DrawTargeting() {
if (!configData.Weapons.EnableCrosshair || Commander == null) return;
Vector3 start = entity.CannonMuzzle.transform.position;
Vector3 end = Vector3.zero;
RaycastHit rayHit;
if (Physics.Raycast(start, entity.CannonMuzzle.forward, out rayHit, 300, 1219701521, QueryTriggerInteraction.Ignore)) end = rayHit.point;
else end = start + (entity.CannonMuzzle.forward * 300);
if (Commander.IsAdmin) {
Commander.SendConsoleCommand("ddraw.text", DDRAW_UPDATE_TIME + 0.01f, configData.Weapons.CrosshairColor.Color, end, $"<size={configData.Weapons.CrosshairSize}>⊕</size>");
}
else {
Commander.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
Commander.SendNetworkUpdateImmediate();
Commander.SendConsoleCommand("ddraw.text", DDRAW_UPDATE_TIME + 0.01f, configData.Weapons.CrosshairColor.Color, end, $"<size={configData.Weapons.CrosshairSize}>⊕</size>");
Commander.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
}
}
private void FireCannon() {
if (cannon.RequireAmmo) {
if (inventory.itemList.Find(x => x.info.shortname == cannon.Type) == null) {
if (ItemManager.itemDictionaryByName.ContainsKey(cannon.Type)) Commander.ChatMessage(string.Format(msg("no_ammo_cannon", Commander.UserIDString), ItemManager.itemDictionaryByName[cannon.Type].displayName.english));
else print($"Invalid ammo type for the cannon set in config: {cannon.Type}");
return;
}
}
if (Time.realtimeSinceStartup >= lastFireCannon) {
Vector3 modifiedAimConeDirection = AimConeUtil.GetModifiedAimConeDirection(cannon.Accuracy, entity.CannonMuzzle.rotation * Vector3.forward, true);
Vector3 cannonPitch = (entity.CannonPitch.transform.rotation * Vector3.back) + (entity.transform.up * -1f);
entity.myRigidBody.AddForceAtPosition(cannonPitch.normalized * entity.recoilScale, entity.CannonPitch.transform.position, ForceMode.Impulse);
Effect.server.Run(entity.mainCannonMuzzleFlash.resourcePath, entity, StringPool.Get(entity.CannonMuzzle.gameObject.name), Vector3.zero, Vector3.zero, null, false);
BaseEntity rocket = GameManager.server.CreateEntity(entity.mainCannonProjectile.resourcePath, entity.CannonMuzzle.transform.position, Quaternion.LookRotation(modifiedAimConeDirection), true);
ServerProjectile projectile = rocket.GetComponent<ServerProjectile>();
projectile.InitializeVelocity(modifiedAimConeDirection.normalized * projectile.speed);
rocket.Spawn();
TimedExplosive explosive = rocket.GetComponent<TimedExplosive>();
if (explosive != null) explosive.damageTypes.Add(new DamageTypeEntry {
amount = cannon.Damage, type = DamageType.Explosion
}
);
lastFireCannon = Time.realtimeSinceStartup + cannon.Interval;
if (cannon.RequireAmmo) inventory.itemList.Find(x => x.info.shortname == cannon.Type)?.UseItem(1);
CreateRocketAmmoUI(Commander, this);
}
}
private void FireCoax() {
if (coax.RequireAmmo) {
if (inventory.itemList.Find(x => x.info.shortname == coax.Type) == null) {
if (ItemManager.itemDictionaryByName.ContainsKey(coax.Type)) Commander.ChatMessage(string.Format(msg("no_ammo_coax", Commander.UserIDString), ItemManager.itemDictionaryByName[coax.Type].displayName.english));
else print($"Invalid ammo type for the coaxial gun set in config: {coax.Type}");
return;
}
}
if (Time.realtimeSinceStartup >= lastFireCoax) {
Vector3 vector3 = entity.coaxMuzzle.transform.position - (entity.coaxMuzzle.forward * 0.25f);
Vector3 modifiedAimConeDirection = AimConeUtil.GetModifiedAimConeDirection(coax.Accuracy, entity.coaxMuzzle.transform.forward, true);
Vector3 targetPos = vector3 + (modifiedAimConeDirection * 300f);
List<RaycastHit> list = Pool.GetList<RaycastHit>();
GamePhysics.TraceAll(new Ray(vector3, modifiedAimConeDirection), 0f, list, 300f, 1219701521, QueryTriggerInteraction.UseGlobal);
for (int i = 0; i < list.Count; i++) {
RaycastHit hit = list[i];
BaseEntity hitEntity = hit.GetEntity();
if (hitEntity != null && hitEntity != this.entity) {
BaseCombatEntity baseCombatEntity = hitEntity as BaseCombatEntity;
if (baseCombatEntity != null) ApplyDamage(baseCombatEntity, coax.Damage, hit.point, modifiedAimConeDirection);
if (hitEntity.ShouldBlockProjectiles()) {
targetPos = hit.point;
break;
}
}
}
entity.ClientRPC(null, "CLIENT_FireGun", true, targetPos);
Pool.FreeList<RaycastHit>(ref list);
lastFireCoax = Time.realtimeSinceStartup + coax.Interval;
if (coax.RequireAmmo) inventory.itemList.Find(x => x.info.shortname == coax.Type)?.UseItem(1);
CreateMGAmmoUI(Commander, this);
}
}
private void FireMG() {
if (mg.RequireAmmo) {
if (inventory.itemList.Find(x => x.info.shortname == mg.Type) == null) {
if (ItemManager.itemDictionaryByName.ContainsKey(mg.Type)) Commander.ChatMessage(string.Format(msg("no_ammo_mg", Commander.UserIDString), ItemManager.itemDictionaryByName[mg.Type].displayName.english));
else print($"Invalid ammo type for the machine gun set in config: {mg.Type}");
return;
}
}
if (Time.realtimeSinceStartup >= lastFireMG) {
Vector3 firePosition = entity.topTurretMuzzle.transform.position - (entity.topTurretMuzzle.forward * 0.25f);
Vector3 modifiedAimConeDirection = AimConeUtil.GetModifiedAimConeDirection(mg.Accuracy, entity.topTurretMuzzle.transform.forward, true);
Vector3 targetPos = firePosition + modifiedAimConeDirection;
List<RaycastHit> list = Pool.GetList<RaycastHit>();
GamePhysics.TraceAll(new Ray(firePosition, modifiedAimConeDirection), 0f, list, 300f, 1219701521, QueryTriggerInteraction.UseGlobal);
for (int i = 0; i < list.Count; i++) {
RaycastHit hit = list[i];
BaseEntity hitEntity = hit.GetEntity();
if (hitEntity != null && !hitEntity == this.entity) {
BaseCombatEntity baseCombatEntity = hitEntity as BaseCombatEntity;
if (baseCombatEntity != null) ApplyDamage(baseCombatEntity, mg.Damage, hit.point, modifiedAimConeDirection);
if (hitEntity.ShouldBlockProjectiles()) {
targetPos = hit.point;
break;
}
}
}
entity.ClientRPC(null, "CLIENT_FireGun", false, targetPos);
Pool.FreeList<RaycastHit>(ref list);
lastFireMG = Time.realtimeSinceStartup + mg.Interval;
if (mg.RequireAmmo) inventory.itemList.Find(x => x.info.shortname == mg.Type)?.UseItem(1);
CreateMGAmmoUI(Commander, this);
}
}
private void FireSideGuns() {
}
private void ApplyDamage(BaseCombatEntity hitEntity, float damage, Vector3 point, Vector3 normal) {
float single = damage * UnityEngine.Random.Range(0.9f, 1.1f);
hitEntity.OnAttacked(new HitInfo(this.entity, hitEntity, DamageType.Bullet, single, point));
if (hitEntity is BasePlayer || hitEntity is BaseNpc) {
HitInfo hitInfo = new HitInfo() {
HitPositionWorld = point, HitNormalWorld = -normal, HitMaterial = StringPool.Get("Flesh")
}
;
Effect.server.ImpactEffect(hitInfo);
}
}
#endregion
#region Movement
private void DoMovementControls() {
float accelerate = Commander.serverInput.IsDown(BUTTON.FORWARD) ? 1f : Commander.serverInput.IsDown(BUTTON.BACKWARD) ? -1f : 0f;
float steer = Commander.serverInput.IsDown(BUTTON.RIGHT) ? 1f : Commander.serverInput.IsDown(BUTTON.LEFT) ? -1f : 0f;
bool boost = Commander.serverInput.IsDown(controlButtons[CommandType.Boost]);
SetThrottleSpeed(accelerate, steer, boost);
}
private void SetThrottleSpeed(float acceleration, float steering, bool boost) {
if (acceleration == 0 && steering == 0) {
ApplyBrakes(0.5f);
if (accelTimeTaken > 0) accelTimeTaken = Mathf.Clamp(accelTimeTaken -= (Time.deltaTime * 2), 0, accelTimeToTake);
}
else {
ApplyBrakes(0f);
accelTimeTaken += Time.deltaTime;
float engineRpm = Mathf.InverseLerp(0f, accelTimeToTake, accelTimeTaken);
float throttle = Mathf.InverseLerp(0f, 1f, engineRpm);
float leftTrack = 0;
float rightTrack = 0;
float torque = 0;
if (acceleration > 0) {
torque = forwardTorque;
leftTrack = 1f;
rightTrack = 1f;
}
else if (acceleration < 0) {
torque = forwardTorque;
leftTrack = -1f;
rightTrack = -1f;
}
if (steering > 0) {
if (acceleration == 0) {
torque = turnTorque;
leftTrack = 1f;
rightTrack = -1f;
}
else {
torque = (forwardTorque + turnTorque) * 0.75f;
rightTrack *= 0.5f;
}
}
else if (steering < 0) {
if (acceleration == 0) {
torque = turnTorque;
leftTrack = -1f;
rightTrack = 1f;
}
else {
torque = (forwardTorque + turnTorque) * 0.75f;
leftTrack *= 0.5f;
}
}
if (boost) {
if (torque > 0) torque += configData.Movement.BoostTorque;
if (torque < 0) torque -= configData.Movement.BoostTorque;
}
float sidewaysVelocity = Mathf.InverseLerp(5f, 1.5f, rigidBody.velocity.magnitude * Mathf.Abs(Vector3.Dot(rigidBody.velocity.normalized, entity.transform.forward)));
entity.ScaleSidewaysFriction(1f - sidewaysVelocity);
ApplyMotorTorque(Mathf.Clamp(leftTrack * throttle, -1f, 1f) * torque, false);
ApplyMotorTorque(Mathf.Clamp(rightTrack * throttle, -1f, 1f) * torque, true);
}
}
private void ApplyBrakes(float amount) {
amount = Mathf.Clamp(maxBrakeTorque * amount, 0, maxBrakeTorque);
ApplyBrakeTorque(amount, true);
ApplyBrakeTorque(amount, false);
}
private void ApplyBrakeTorque(float amount, bool rightSide) {
WheelCollider[] wheelColliderArray = (!rightSide ? leftWheels : rightWheels);
for (int i = 0; i < wheelColliderArray.Length; i++) wheelColliderArray[i].brakeTorque = maxBrakeTorque * amount;
}
private void ApplyMotorTorque(float torque, bool rightSide) {
WheelCollider[] wheelColliderArray = (!rightSide ? leftWheels : rightWheels);
for (int i = 0; i < wheelColliderArray.Length; i++) wheelColliderArray[i].motorTorque = torque;
}
#endregion
private void ToggleLights() => entity.SetFlag(BaseEntity.Flags.Reserved5, !entity.HasFlag(BaseEntity.Flags.Reserved5), false);
public void ManageDamage(HitInfo info) {
if (isDying) return;
if (info.damageTypes.Total() >= entity.health) {
info.damageTypes = new DamageTypeList();
info.HitEntity = null;
info.HitMaterial = 0;
info.PointStart = Vector3.zero;
OnDeath();
}
else {
ins.NextTick(() => {
if (Commander != null) CreateHealthUI(Commander, this);
for (int i = 1; i < mountables.Length; i++) {
BasePlayer player = mountables[i]?.GetMounted();
if (player != null) CreateHealthUI(player, this);
}
}
);
}
}
private void OnDeath() {
isDying = true;
DismountAll();
Effect.server.Run(entity.explosionEffect.resourcePath, entity.transform.position, Vector3.up, null, true);
List<ServerGib> serverGibs = ServerGib.CreateGibs(entity.servergibs.resourcePath, entity.gameObject, entity.servergibs.Get().GetComponent<ServerGib>()._gibSource, Vector3.zero, 3f);
for (int i = 0; i < 12 - entity.maxCratesToSpawn; i++) {
BaseEntity fireBall = GameManager.server.CreateEntity(entity.fireBall.resourcePath, entity.transform.position, entity.transform.rotation, true);
if (fireBall) {
Vector3 onSphere = UnityEngine.Random.onUnitSphere;
fireBall.transform.position = (entity.transform.position + new Vector3(0f, 1.5f, 0f)) + (onSphere * UnityEngine.Random.Range(-4f, 4f));
Collider collider = fireBall.GetComponent<Collider>();
fireBall.Spawn();
fireBall.SetVelocity(Vector3.zero + (onSphere * UnityEngine.Random.Range(3, 10)));
foreach (ServerGib serverGib in serverGibs) Physics.IgnoreCollision(collider, serverGib.GetCollider(), true);
}
}
if (configData.Inventory.DropInv) {
inventory.Drop("assets/prefabs/misc/item drop/item_drop.prefab", (entity.transform.position + new Vector3(0f, 1.5f, 0f)) + (UnityEngine.Random.onUnitSphere * UnityEngine.Random.Range(2f, 3f)), new Quaternion());
}
if (configData.Inventory.DropLoot) {
for (int j = 0; j < entity.maxCratesToSpawn; j++) {
Vector3 onSphere = UnityEngine.Random.onUnitSphere;
BaseEntity lootCrate = GameManager.server.CreateEntity(entity.crateToDrop.resourcePath, (entity.transform.position + new Vector3(0f, 1.5f, 0f)) + (onSphere * UnityEngine.Random.Range(2f, 3f)), Quaternion.LookRotation(onSphere), true);
lootCrate.Spawn();
LootContainer lootContainer = lootCrate as LootContainer;
if (lootContainer) lootContainer.Invoke(new Action(lootContainer.RemoveMe), 1800f);
Collider collider = lootCrate.GetComponent<Collider>();
Rigidbody rigidbody = lootCrate.gameObject.AddComponent<Rigidbody>();
rigidbody.useGravity = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rigidbody.mass = 2f;
rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
rigidbody.velocity = Vector3.zero + (onSphere * UnityEngine.Random.Range(1f, 3f));
rigidbody.angularVelocity = Vector3Ex.Range(-1.75f, 1.75f);
rigidbody.drag = 0.5f * (rigidbody.mass / 5f);
rigidbody.angularDrag = 0.2f * (rigidbody.mass / 5f);
FireBall fireBall = GameManager.server.CreateEntity(entity.fireBall.resourcePath, lootCrate.transform.position, new Quaternion(), true) as FireBall;
if (fireBall) {
fireBall.transform.position = lootCrate.transform.position;
fireBall.Spawn();
fireBall.GetComponent<Rigidbody>().isKinematic = true;
fireBall.GetComponent<Collider>().enabled = false;
fireBall.transform.parent = lootCrate.transform;
}
lootCrate.SendMessage("SetLockingEnt", fireBall.gameObject, SendMessageOptions.DontRequireReceiver);
foreach (ServerGib serverGib1 in serverGibs) Physics.IgnoreCollision(collider, serverGib1.GetCollider(), true);
}
}
if (entity != null && !entity.IsDestroyed) entity.Kill(BaseNetworkable.DestroyMode.Gib);
}
}
#endregion
#region Commands
[ChatCommand("spawntank")]
void cmdTank(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, "tankcommander.admin")) return;
Vector3 position = player.eyes.position + (player.eyes.MovementForward() * 5f);
BaseEntity entity = GameManager.server.CreateEntity(APC_PREFAB, position, Quaternion.Euler(0, player.eyes.rotation.eulerAngles.y - 90f, 0));
entity.enableSaving = false;
entity.Spawn();
controllers.Add(entity.gameObject.AddComponent<APCController>());
}
[ConsoleCommand("spawntank")]
void ccmdSpawnTank(ConsoleSystem.Arg arg) {
if (arg.Connection != null || arg.Args == null) return;
if (arg.Args.Length == 1) {
BasePlayer player = covalence.Players.Connected.FirstOrDefault(x => x.Id == arg.GetString(0))?.Object as BasePlayer;
if (player != null) {
Vector3 position = player.eyes.position + (player.eyes.MovementForward() * 5f);
BaseEntity entity = GameManager.server.CreateEntity(APC_PREFAB, position, Quaternion.Euler(0, player.eyes.rotation.eulerAngles.y - 90f, 0));
entity.enableSaving = false;
entity.Spawn();
controllers.Add(entity.gameObject.AddComponent<APCController>());
}
return;
}
if (arg.Args.Length == 3) {
float x;
float y;
float z;
if (float.TryParse(arg.GetString(0), out x)) {
if (float.TryParse(arg.GetString(1), out y)) {
if (float.TryParse(arg.GetString(2), out z)) {
BaseEntity entity = GameManager.server.CreateEntity(APC_PREFAB, new Vector3(x, y, z));
entity.enableSaving = false;
entity.Spawn();
controllers.Add(entity.gameObject.AddComponent<APCController>());
return;
}
}
}
PrintError($"Invalid arguments supplied to spawn a tank at position : (x = {arg.GetString(0)}, y = {arg.GetString(1)}, z = {arg.GetString(2)})");
}
}
#endregion
#region Friends
private bool AreFriends(ulong playerId, ulong friendId) {
if (Friends && configData.Passengers.UseFriends) return (bool)Friends?.Call("AreFriendsS", playerId.ToString(), friendId.ToString());
return true;
}
private bool IsClanmate(ulong playerId, ulong friendId) {
if (Clans && configData.Passengers.UseClans) {
object playerTag = Clans?.Call("GetClanOf", playerId);
object friendTag = Clans?.Call("GetClanOf", friendId);
if (playerTag is string && friendTag is string) {
if (!string.IsNullOrEmpty((string)playerTag) && !string.IsNullOrEmpty((string)friendTag) && (playerTag == friendTag)) return true;
}
return false;
}
return true;
}
#endregion
#region UI
#region UI Elements
public static class UI {
static public CuiElementContainer ElementContainer(string pan
// ... [Code preview truncated due to length. Full source in attached file] ...