using System; using System.Collections.Generic; using Oxide.Core; using Oxide.Game.Rust.Cui; using UnityEngine; using Facepunch; using Network; // ____ _ _ _ // / ___(_) __ _(_) | ___ // \___ \| |/ _` | | |/ _ \ // ___) | | (_| | | | (_) | // |____/|_|\__, |_|_|\___/ // |___/ // ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾ // ✦ RUST PLUGINS ✦ // ✦ https://sigilo.dev ✦ // ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾ namespace Oxide.Plugins { [Info("MotorcycleCombat", "Sigilo", "1.1.0")] [Description("Adds boost, jump, and minigun to motorcycles with crash explosion.")] public class MotorcycleCombat : RustPlugin { private class PluginConfig { public bool RequirePermission = true; public float BoostThrustMultiplier = 0.5f; public float BoostFuelDuration = 1.0f; public float BoostRechargeTime = 10.0f; public string BoostActivateSound = "assets/prefabs/npc/patrol helicopter/effects/rocket_fire.prefab"; public string BoostLoopEffect = "assets/prefabs/ammo/rocket/rocket_basic.prefab"; public float JumpForce = 15.0f; public float JumpCooldown = 3.0f; public string JumpSound = "assets/prefabs/locks/keypad/effects/lock.code.lock.prefab"; public float MinigunDamage = 15.0f; public float MinigunRange = 300.0f; public float MinigunBulletVelocity = 400.0f; public float MinigunBulletGravity = 0.6f; public float MinigunAimconeDegrees = 0.6f; public float MinigunRoundsPerSecond = 8.0f; public int MinigunMagazineSize = 80; public float MinigunReloadTime = 25.0f; public bool MinigunEnableMuzzleFlash = true; public bool MinigunEnableSound = true; public string MinigunSoundEffect = "assets/prefabs/weapons/ak47u/effects/attack.prefab"; public string MinigunMuzzleFlashEffect = "assets/prefabs/weapons/ak47u/effects/attack.prefab"; public string MinigunImpactEffect = "assets/bundled/prefabs/fx/impacts/additive/explosion.prefab"; public float CrashSpeedThreshold = 20.0f; public string CrashExplosionEffect = "assets/bundled/prefabs/fx/explosions/explosion_01.prefab"; public string CrashFireEffect = "assets/bundled/prefabs/fx/fire/fire_v3_2x2.prefab"; public bool EnableHUD = true; public bool UnlimitedFuel = true; } private PluginConfig config; private const string HUD_PANEL = "MotorcycleCombat_HUD"; private const string PermissionUse = "motorcyclecombat.use"; private static readonly int BulletLayerMask = ~((1 << 2) | (1 << 29)); private readonly Dictionary boostByVehicle = new Dictionary(); private readonly Dictionary lastJumpTime = new Dictionary(); private readonly Dictionary minigunTimersByPlayer = new Dictionary(); private readonly Dictionary minigunAmmoByVehicle = new Dictionary(); private readonly Dictionary isMinigunReloading = new Dictionary(); private readonly Dictionary minigunReloadTimers = new Dictionary(); private readonly Dictionary hudUpdateTimers = new Dictionary(); private readonly List pendingBullets = new List(); private readonly HashSet activeRiders = new HashSet(); private readonly HashSet crashVulnerableVehicles = new HashSet(); private readonly Dictionary spawnedBikesByPlayer = new Dictionary(); private class BoostState { public BaseVehicle Vehicle; public bool IsActive; public float FuelRemaining; public float RechargeTimer; public float LastEffectTime; } private class PendingBullet { public BasePlayer Owner; public BaseVehicle Vehicle; public Vector3 Origin; public Vector3 Velocity; public Vector3 CurrentPosition; public float SpawnTime; public float BaseDamage; public float MaxLifetime; public Vector3 LastPosition; } private void Init() { permission.RegisterPermission(PermissionUse, this); } private readonly Dictionary lastMotoSpawn = new Dictionary(); private const float MOTO_SPAWN_COOLDOWN = 5f; [ChatCommand("moto")] private void CmdSpawnMoto(BasePlayer player, string command, string[] args) { if (!HasPermission(player)) { player.ChatMessage("You don't have permission to use this command."); return; } if (player.IsDead()) { player.ChatMessage("You cannot spawn a motorcycle while dead."); return; } if (player.isMounted) { player.ChatMessage("You cannot spawn a motorcycle while mounted."); return; } if (player.IsSwimming()) { player.ChatMessage("You cannot spawn a motorcycle in water."); return; } if (player.IsBuildingBlocked()) { player.ChatMessage("You cannot spawn a motorcycle in a building blocked area."); return; } float now = Time.realtimeSinceStartup; if (lastMotoSpawn.TryGetValue(player.userID, out float lastSpawn)) { float remaining = MOTO_SPAWN_COOLDOWN - (now - lastSpawn); if (remaining > 0) { player.ChatMessage($"You must wait {remaining:F0} seconds before spawning another motorcycle."); return; } } if (spawnedBikesByPlayer.TryGetValue(player.userID, out BaseVehicle existingBike) && existingBike != null && !existingBike.IsDestroyed) { bool hasMountedPlayers = false; foreach (var mount in existingBike.allMountPoints) { if (mount?.mountable != null && mount.mountable.GetMounted() != null) { hasMountedPlayers = true; break; } } if (hasMountedPlayers) { player.ChatMessage("Your previous motorcycle has players mounted. Dismount first."); return; } existingBike.Kill(BaseNetworkable.DestroyMode.None); } Vector3 spawnPos = player.transform.position + player.eyes.HeadForward() * 3f; spawnPos.y = player.transform.position.y; if (!Physics.Raycast(spawnPos + Vector3.up * 2f, Vector3.down, out RaycastHit hit, 5f, BulletLayerMask)) { player.ChatMessage("Invalid spawn location."); return; } spawnPos = hit.point + Vector3.up * 0.5f; var moto = GameManager.server.CreateEntity("assets/content/vehicles/bikes/motorbike.prefab", spawnPos, Quaternion.LookRotation(player.eyes.HeadForward())) as BaseVehicle; if (moto == null) { player.ChatMessage("Failed to spawn motorcycle."); return; } moto.Spawn(); spawnedBikesByPlayer[player.userID] = moto; lastMotoSpawn[player.userID] = now; player.ChatMessage("Motorcycle spawned!"); } protected override void LoadDefaultConfig() { config = new PluginConfig(); SaveConfig(); } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) throw new Exception("Config is null"); } catch { PrintWarning("Invalid config, creating new..."); LoadDefaultConfig(); } } protected override void SaveConfig() => Config.WriteObject(config, true); private void OnServerInitialized() { config = Config.ReadObject(); SaveConfig(); Puts($"MotorcycleCombat v1.1.0 loaded"); } private void OnTick() { ProcessPendingBullets(); if (boostByVehicle.Count > 0) ProcessBoostPhysics(); } private void Unload() { foreach (var player in BasePlayer.activePlayerList) DestroyHUD(player); foreach (var t in minigunTimersByPlayer.Values) t?.Destroy(); minigunTimersByPlayer.Clear(); foreach (var t in minigunReloadTimers.Values) t?.Destroy(); minigunReloadTimers.Clear(); foreach (var t in hudUpdateTimers.Values) t?.Destroy(); hudUpdateTimers.Clear(); pendingBullets.Clear(); } private void OnPlayerInput(BasePlayer player, InputState input) { if (player == null || input == null || !player.IsConnected) return; var mounted = player.GetMountedVehicle(); if (mounted == null) return; bool isMotorbike = mounted.ShortPrefabName.Contains("motorbike"); if (!isMotorbike) return; if (!HasPermission(player)) return; bool isSidecar = mounted.ShortPrefabName.Contains("sidecar"); bool isDriver = !isSidecar || IsDriver(player, mounted); bool isPassenger = isSidecar && !IsDriver(player, mounted); ulong vehicleId = mounted.net.ID.Value; if (config.EnableHUD) StartHUDUpdates(player, mounted, isDriver, isPassenger); if (isDriver) { if (input.IsDown(BUTTON.SPRINT)) HandleBoostInput(player, mounted, true); else HandleBoostInput(player, mounted, false); if (input.WasJustPressed(BUTTON.RELOAD)) TryJump(player, mounted); var heldItem = player.GetActiveItem(); bool hasWeapon = heldItem != null && (heldItem.info.category == ItemCategory.Weapon || heldItem.info.category == ItemCategory.Tool); if (!hasWeapon && input.IsDown(BUTTON.FIRE_PRIMARY)) { if (!IsMinigunReloading(vehicleId)) StartMinigunTimer(player, mounted); } else { StopMinigunTimer(player.userID); } } } private void HandleBoostInput(BasePlayer player, BaseVehicle vehicle, bool isPressed) { ulong vehicleId = vehicle.net.ID.Value; if (!boostByVehicle.TryGetValue(vehicleId, out BoostState state)) { state = new BoostState { Vehicle = vehicle, IsActive = false, FuelRemaining = config.BoostFuelDuration, RechargeTimer = 0f, LastEffectTime = 0f }; boostByVehicle[vehicleId] = state; } if (isPressed && state.FuelRemaining > 0f && state.RechargeTimer <= 0f && IsGrounded(vehicle)) { if (!state.IsActive) { state.IsActive = true; crashVulnerableVehicles.Add(vehicleId); Effect.server.Run(config.BoostActivateSound, vehicle.transform.position); } } else { if (state.IsActive) { state.IsActive = false; if (state.FuelRemaining <= 0f) { state.RechargeTimer = config.BoostRechargeTime; } } } } private void ProcessBoostPhysics() { float dt = Time.deltaTime; float currentTime = Time.realtimeSinceStartup; List toRemove = null; foreach (var kvp in boostByVehicle) { ulong vehicleId = kvp.Key; BoostState state = kvp.Value; var vehicle = state.Vehicle; if (vehicle == null || vehicle.IsDestroyed) { if (toRemove == null) toRemove = new List(); toRemove.Add(vehicleId); continue; } if (state.RechargeTimer > 0f) { state.RechargeTimer -= dt; if (state.RechargeTimer <= 0f) { state.FuelRemaining = config.BoostFuelDuration; state.RechargeTimer = 0f; var driver = GetDriver(vehicle); if (driver != null) Effect.server.Run("assets/bundled/prefabs/fx/invite_notice.prefab", vehicle.transform.position); } } if (state.IsActive && state.FuelRemaining > 0f) { if (!IsGrounded(vehicle)) { state.IsActive = false; continue; } state.FuelRemaining -= dt; var rb = vehicle.GetComponent(); if (rb != null) { Vector3 boostForce = vehicle.transform.forward * config.BoostThrustMultiplier * 50f; rb.AddForce(boostForce, ForceMode.Acceleration); } if (currentTime - state.LastEffectTime > 0.2f) { Vector3 exhaustPos = vehicle.transform.position - vehicle.transform.forward * 1.0f; Effect.server.Run(config.BoostLoopEffect, exhaustPos, -vehicle.transform.forward); state.LastEffectTime = currentTime; } if (state.FuelRemaining <= 0f) { state.IsActive = false; state.FuelRemaining = 0f; state.RechargeTimer = config.BoostRechargeTime; } } else if (!state.IsActive && state.RechargeTimer <= 0f && state.FuelRemaining >= config.BoostFuelDuration) { crashVulnerableVehicles.Remove(vehicleId); } } if (toRemove != null) foreach (var id in toRemove) { boostByVehicle.Remove(id); crashVulnerableVehicles.Remove(id); } } private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) { if (entity == null || info == null) return; var vehicle = entity as BaseVehicle; if (vehicle == null || !vehicle.ShortPrefabName.Contains("motorbike")) return; ulong vehicleId = vehicle.net.ID.Value; if (!crashVulnerableVehicles.Contains(vehicleId)) return; if (!boostByVehicle.TryGetValue(vehicleId, out BoostState state)) return; if (!state.IsActive) return; var rb = vehicle.GetComponent(); float speed = rb != null ? rb.velocity.magnitude : 0f; if (speed < config.CrashSpeedThreshold) return; bool isCollision = info.damageTypes.Has(Rust.DamageType.Collision) || info.damageTypes.Has(Rust.DamageType.Blunt) || (info.Initiator == null && info.damageTypes.Total() > 0); if (!isCollision) return; TriggerCrashExplosion(vehicle, info.HitPositionWorld); } private void TriggerCrashExplosion(BaseVehicle vehicle, Vector3 crashPoint) { if (vehicle == null || vehicle.IsDestroyed) return; ulong vehicleId = vehicle.net.ID.Value; boostByVehicle.Remove(vehicleId); crashVulnerableVehicles.Remove(vehicleId); Vector3 pos = crashPoint != Vector3.zero ? crashPoint : vehicle.transform.position; for (int i = 0; i < 3; i++) { Vector3 offset = UnityEngine.Random.insideUnitSphere * 2f; Effect.server.Run(config.CrashExplosionEffect, pos + offset, Vector3.up); } Effect.server.Run(config.CrashFireEffect, pos, Vector3.up); Effect.server.Run(config.CrashFireEffect, pos + Vector3.up * 1f, Vector3.up); Effect.server.Run("assets/bundled/prefabs/fx/explosions/explosion_03.prefab", pos, Vector3.up); var driver = GetDriver(vehicle); if (driver != null) { activeRiders.Remove(driver); StopHUDUpdates(driver); DestroyHUD(driver); } timer.Once(0.1f, () => { if (vehicle != null && !vehicle.IsDestroyed) { vehicle.Kill(BaseNetworkable.DestroyMode.Gib); } }); } private void TryJump(BasePlayer player, BaseVehicle vehicle) { ulong vehicleId = vehicle.net.ID.Value; float now = Time.realtimeSinceStartup; if (lastJumpTime.TryGetValue(vehicleId, out float lastTime)) { float remaining = config.JumpCooldown - (now - lastTime); if (remaining > 0) return; } var rb = vehicle.GetComponent(); if (rb == null) return; if (!IsGrounded(vehicle)) return; lastJumpTime[vehicleId] = now; rb.AddForce(Vector3.up * config.JumpForce, ForceMode.VelocityChange); Effect.server.Run(config.JumpSound, vehicle.transform.position); } private bool IsGrounded(BaseVehicle vehicle) { Vector3 pos = vehicle.transform.position; if (!Physics.Raycast(pos + Vector3.up * 0.3f, Vector3.down, 1.5f, BulletLayerMask)) return false; var rb = vehicle.GetComponent(); if (rb != null && Mathf.Abs(rb.velocity.y) > 5.0f) return false; return true; } private void StartMinigunTimer(BasePlayer player, BaseVehicle vehicle) { if (minigunTimersByPlayer.ContainsKey(player.userID)) return; float fireInterval = 1f / config.MinigunRoundsPerSecond; FireMinigun(player, vehicle); minigunTimersByPlayer[player.userID] = timer.Every(fireInterval, () => { if (player == null || !player.IsConnected || player.IsDead()) { StopMinigunTimer(player.userID); return; } var mounted = player.GetMountedVehicle(); if (mounted == null || mounted != vehicle) { StopMinigunTimer(player.userID); return; } FireMinigun(player, vehicle); }); } private void StopMinigunTimer(ulong playerId) { if (minigunTimersByPlayer.TryGetValue(playerId, out var t)) { t?.Destroy(); minigunTimersByPlayer.Remove(playerId); } } private void FireMinigun(BasePlayer player, BaseVehicle vehicle) { ulong vehicleId = vehicle.net.ID.Value; int ammo = GetMinigunAmmo(vehicleId); if (ammo <= 0) { StartMinigunReload(player, vehicle, vehicleId); StopMinigunTimer(player.userID); return; } minigunAmmoByVehicle[vehicleId] = ammo - 1; Vector3 muzzlePos = player.eyes.position + player.eyes.HeadForward() * 1.0f; Vector3 direction = player.eyes.HeadForward(); float aimcone = config.MinigunAimconeDegrees * Mathf.Deg2Rad; direction = ApplyAimcone(direction, aimcone); Vector3 velocity = direction * config.MinigunBulletVelocity; if (config.MinigunEnableSound) Effect.server.Run(config.MinigunSoundEffect, muzzlePos); if (config.MinigunEnableMuzzleFlash) Effect.server.Run(config.MinigunMuzzleFlashEffect, muzzlePos); float maxLifetime = config.MinigunRange / config.MinigunBulletVelocity; pendingBullets.Add(new PendingBullet { Owner = player, Vehicle = vehicle, Origin = muzzlePos, Velocity = velocity, CurrentPosition = muzzlePos, SpawnTime = Time.realtimeSinceStartup, BaseDamage = config.MinigunDamage, MaxLifetime = maxLifetime, LastPosition = muzzlePos }); } private Vector3 ApplyAimcone(Vector3 direction, float aimcone) { float x = UnityEngine.Random.Range(-aimcone, aimcone); float y = UnityEngine.Random.Range(-aimcone, aimcone); return Quaternion.Euler(x * Mathf.Rad2Deg, y * Mathf.Rad2Deg, 0) * direction; } private void ProcessPendingBullets() { if (pendingBullets.Count == 0) return; float dt = Time.deltaTime; float currentTime = Time.realtimeSinceStartup; for (int i = pendingBullets.Count - 1; i >= 0; i--) { var bullet = pendingBullets[i]; float age = currentTime - bullet.SpawnTime; if (age > bullet.MaxLifetime) { pendingBullets.RemoveAt(i); continue; } bullet.Velocity.y -= config.MinigunBulletGravity * dt * 9.81f; bullet.LastPosition = bullet.CurrentPosition; bullet.CurrentPosition += bullet.Velocity * dt; Vector3 direction = bullet.CurrentPosition - bullet.LastPosition; float distance = direction.magnitude; if (distance > 0.01f && Physics.Raycast(bullet.LastPosition, direction.normalized, out RaycastHit hit, distance, BulletLayerMask)) { var entity = hit.GetEntity(); if (entity != null && entity != bullet.Vehicle) { var combatEntity = entity as BaseCombatEntity; if (combatEntity != null) { var hitInfo = new HitInfo(bullet.Owner, combatEntity, Rust.DamageType.Bullet, bullet.BaseDamage, hit.point); combatEntity.OnAttacked(hitInfo); } Effect.server.Run(config.MinigunImpactEffect, hit.point, hit.normal); } else if (entity == null) { Effect.server.Run(config.MinigunImpactEffect, hit.point, hit.normal); } pendingBullets.RemoveAt(i); } } } private int GetMinigunAmmo(ulong vehicleId) { if (!minigunAmmoByVehicle.TryGetValue(vehicleId, out int ammo)) { ammo = config.MinigunMagazineSize; minigunAmmoByVehicle[vehicleId] = ammo; } return ammo; } private bool IsMinigunReloading(ulong vehicleId) { return isMinigunReloading.TryGetValue(vehicleId, out bool reloading) && reloading; } private void StartMinigunReload(BasePlayer player, BaseVehicle vehicle, ulong vehicleId) { if (IsMinigunReloading(vehicleId)) return; isMinigunReloading[vehicleId] = true; player.ChatMessage($"Reloading... {config.MinigunReloadTime}s"); if (minigunReloadTimers.TryGetValue(vehicleId, out var existingTimer)) existingTimer?.Destroy(); minigunReloadTimers[vehicleId] = timer.Once(config.MinigunReloadTime, () => { minigunAmmoByVehicle[vehicleId] = config.MinigunMagazineSize; isMinigunReloading[vehicleId] = false; var v = BaseNetworkable.serverEntities.Find(new NetworkableId(vehicleId)) as BaseVehicle; if (v != null) { Effect.server.Run("assets/bundled/prefabs/fx/invite_notice.prefab", v.transform.position); var driver = GetDriver(v); if (driver != null) driver.ChatMessage("Minigun reloaded!"); } minigunReloadTimers.Remove(vehicleId); }); } private void StartHUDUpdates(BasePlayer player, BaseVehicle vehicle, bool isDriver, bool isPassenger) { if (hudUpdateTimers.ContainsKey(player.userID)) return; UpdateHUD(player, vehicle, isDriver, isPassenger); hudUpdateTimers[player.userID] = timer.Every(0.5f, () => { if (player == null || !player.IsConnected) { StopHUDUpdates(player); return; } var mounted = player.GetMountedVehicle(); if (mounted == null || !mounted.ShortPrefabName.Contains("motorbike")) { StopHUDUpdates(player); DestroyHUD(player); return; } UpdateHUD(player, mounted, IsDriver(player, mounted), !IsDriver(player, mounted) && mounted.ShortPrefabName.Contains("sidecar")); }); } private void StopHUDUpdates(BasePlayer player) { if (player == null) return; if (hudUpdateTimers.TryGetValue(player.userID, out var t)) { t?.Destroy(); hudUpdateTimers.Remove(player.userID); } } private void UpdateHUD(BasePlayer player, BaseVehicle vehicle, bool isDriver, bool isPassenger) { if (player == null || vehicle == null) return; if (!isDriver) return; ulong vehicleId = vehicle.net.ID.Value; CuiHelper.DestroyUi(player, HUD_PANEL); var elements = new CuiElementContainer(); elements.Add(new CuiPanel { Image = { Color = "0 0 0 0" }, RectTransform = { AnchorMin = "0.30 0.095", AnchorMax = "0.70 0.125" }, CursorEnabled = false }, "Hud", HUD_PANEL); float boostFuel = config.BoostFuelDuration; bool boostRecharging = false; if (boostByVehicle.TryGetValue(vehicleId, out BoostState boostState)) { boostFuel = boostState.FuelRemaining; boostRecharging = boostState.RechargeTimer > 0f; } float jumpCooldown = 0f; if (lastJumpTime.TryGetValue(vehicleId, out float lastTime)) jumpCooldown = Mathf.Max(0, config.JumpCooldown - (Time.realtimeSinceStartup - lastTime)); int ammo = GetMinigunAmmo(vehicleId); bool reloading = IsMinigunReloading(vehicleId); elements.Add(new CuiPanel { Image = { Color = "0.1 0.1 0.1 0.7" }, RectTransform = { AnchorMin = "0.02 0.1", AnchorMax = "0.34 0.9" } }, HUD_PANEL, HUD_PANEL + "_BoostBG"); string boostValue; if (boostRecharging) { boostValue = "RECHARGING"; } else { float boostPercent = (boostFuel / config.BoostFuelDuration) * 100f; string boostColor = boostPercent > 50 ? "#44aaff" : (boostPercent > 25 ? "#ffcc00" : "#ff4444"); boostValue = $"{boostPercent:F0}%"; } elements.Add(new CuiLabel { Text = { Text = $"Boost: {boostValue}", FontSize = 11, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1", Font = "robotocondensed-bold.ttf" }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, HUD_PANEL + "_BoostBG"); elements.Add(new CuiPanel { Image = { Color = "0.1 0.1 0.1 0.7" }, RectTransform = { AnchorMin = "0.35 0.1", AnchorMax = "0.65 0.9" } }, HUD_PANEL, HUD_PANEL + "_JumpBG"); string jumpValue; if (jumpCooldown > 0) { jumpValue = $"{jumpCooldown:F1}s"; } else { jumpValue = "READY"; } elements.Add(new CuiLabel { Text = { Text = $"Jump: {jumpValue}", FontSize = 11, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1", Font = "robotocondensed-bold.ttf" }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, HUD_PANEL + "_JumpBG"); elements.Add(new CuiPanel { Image = { Color = "0.1 0.1 0.1 0.7" }, RectTransform = { AnchorMin = "0.66 0.1", AnchorMax = "0.98 0.9" } }, HUD_PANEL, HUD_PANEL + "_AmmoBG"); string ammoValue; if (reloading) { ammoValue = "RELOADING"; } else { string ammoColor = ammo > 30 ? "#88ff88" : (ammo > 10 ? "#ffcc00" : "#ff4444"); ammoValue = $"{ammo}"; } elements.Add(new CuiLabel { Text = { Text = $"Ammo: {ammoValue}", FontSize = 11, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1", Font = "robotocondensed-bold.ttf" }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, HUD_PANEL + "_AmmoBG"); CuiHelper.AddUi(player, elements); } private void DestroyHUD(BasePlayer player) { if (player == null) return; CuiHelper.DestroyUi(player, HUD_PANEL); } private void OnEntityDismounted(BaseMountable mountable, BasePlayer player) { if (player == null) return; activeRiders.Remove(player); StopMinigunTimer(player.userID); StopHUDUpdates(player); DestroyHUD(player); } private void OnPlayerDisconnected(BasePlayer player, string reason) { if (player == null) return; activeRiders.Remove(player); StopMinigunTimer(player.userID); StopHUDUpdates(player); DestroyHUD(player); } private void OnEntityKill(BaseNetworkable entity) { if (entity is BaseVehicle vehicle) { ulong vehicleId = vehicle.net.ID.Value; boostByVehicle.Remove(vehicleId); crashVulnerableVehicles.Remove(vehicleId); lastJumpTime.Remove(vehicleId); minigunAmmoByVehicle.Remove(vehicleId); isMinigunReloading.Remove(vehicleId); if (minigunReloadTimers.TryGetValue(vehicleId, out var t)) { t?.Destroy(); minigunReloadTimers.Remove(vehicleId); } ulong ownerToRemove = 0; foreach (var kvp in spawnedBikesByPlayer) { if (kvp.Value == vehicle) { ownerToRemove = kvp.Key; break; } } if (ownerToRemove != 0) spawnedBikesByPlayer.Remove(ownerToRemove); } } private object OnFuelCheck(EntityFuelSystem fuelSystem) { if (!config.UnlimitedFuel) return null; var vehicle = fuelSystem.GetFuelContainer()?.GetParentEntity() as BaseVehicle; if (vehicle != null && vehicle.ShortPrefabName.Contains("motorbike")) return true; return null; } private bool HasPermission(BasePlayer player) { if (!config.RequirePermission) return true; return permission.UserHasPermission(player.UserIDString, PermissionUse); } private bool IsDriver(BasePlayer player, BaseVehicle vehicle) { if (vehicle == null) return false; var driver = GetDriver(vehicle); return driver != null && driver == player; } private BasePlayer GetDriver(BaseVehicle vehicle) { if (vehicle == null) return null; foreach (var mount in vehicle.allMountPoints) { if (mount?.mountable != null && mount.isDriver) { return mount.mountable.GetMounted() as BasePlayer; } } return null; } } }