C#:
using HarmonyLib;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Oxide.Plugins
{
[Info("GhostRider", "0xF", "1.2.0")]
partial class GhostRider : RustPlugin
{
private const string MOTORBIKE_DRIVER_SEAT_PREFAB = "assets/prefabs/vehicle/seats/motorbikedriverseat.prefab";
private const string BOOST_EFFECT_PREFAB = "assets/prefabs/npc/patrol helicopter/effects/gun_fire_small.prefab";
private const string FLAME_PREFAB = "assets/prefabs/weapons/flamethrower/flamethrower_fireball.prefab";
private readonly string[] HORSE_BONE_NAMES = new string[] { "head", "L_Fore_Foot_END", "R_Fore_Foot_END", "L_Rear_Foot_END", "R_Rear_Foot_END" };
private const uint FIREBALL_PREFABID = 3369311876;
private static uint HEADBONE = StringPool.Get("head");
private static GhostRider Instance;
private Dictionary<BaseVehicle, List<BaseEntity>> subEntities = new Dictionary<BaseVehicle, List<BaseEntity>>();
void Init()
{
Instance = this;
}
void OnServerInitialized()
{
Bike.doPlayerDamage = config.CanCrashDamage;
foreach (BasePlayer player in BasePlayer.allPlayerList)
{
DisposeRiderStaff(player);
}
foreach (BaseNetworkable baseNetworkable in BaseNetworkable.serverEntities)
{
if (baseNetworkable is BaseVehicle)
{
BaseVehicle vehicle = baseNetworkable as BaseVehicle;
if (vehicle.HasDriver())
{
if (baseNetworkable is Bike)
SetupRiderStaff(vehicle.GetDriver(), vehicle as Bike);
else if (baseNetworkable is RidableHorse)
SetupRiderStaff(vehicle.GetDriver(), vehicle as RidableHorse);
}
}
}
}
void OnEntityKill(BaseVehicle vehicle)
{
if (vehicle is Bike || vehicle is RidableHorse)
DisposeRiderStaff(vehicle);
}
void Unload()
{
foreach (BasePlayer player in BasePlayer.allPlayerList)
DisposeRiderStaff(player);
foreach (BaseNetworkable baseNetworkable in BaseNetworkable.serverEntities)
{
if (baseNetworkable is Bike || baseNetworkable is RidableHorse)
DisposeRiderStaff(baseNetworkable as BaseVehicle);
}
}
private BaseEntity CreateFireball()
{
GameObject gameObject = new GameObject("ghostrider-fireball");
gameObject.layer = (int)Rust.Layer.Reserved1;
SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
BaseEntity fireball = gameObject.AddComponent<BaseEntity>();
fireball.prefabID = 3369311876;
fireball.enableSaving = false;
gameObject.SetActive(true);
return fireball;
}
private static bool IsValidVehicle(BaseVehicle vehicle)
{
if (!vehicle)
return false;
if (!vehicle.PrefabName.Contains("motor") && !vehicle.PrefabName.Contains("horse"))
return false;
if (vehicle is Bike)
{
if (config.Only2Wheels && (vehicle as Bike).GetWheels().Length != 2)
return false;
}
return true;
}
private static bool IsMotorbikeSeat(BikeDriverSeat seat)
{
return seat.PrefabName == MOTORBIKE_DRIVER_SEAT_PREFAB;
}
private void BaseSetupRiderStaff(BasePlayer player, BaseVehicle baseVehicle)
{
if (!IsValidVehicle(baseVehicle))
return;
if (subEntities.TryGetValue(baseVehicle, out List<BaseEntity> ents))
{
foreach (var ent in ents)
if (!ent.IsDestroyed)
ent.Kill();
ents.Clear();
}
else
{
ents = subEntities[baseVehicle] = new List<BaseEntity>();
}
BaseEntity headFireball = CreateFireball();
headFireball.SetParent(player, HEADBONE);
headFireball.Spawn();
ents.Add(headFireball);
baseVehicle.gameObject.AddComponent<GroundFlame>();
}
private void SetupRiderStaff(BasePlayer player, Bike bike)
{
if (!IsValidVehicle(bike))
return;
BaseSetupRiderStaff(player, bike);
foreach (CarWheel wheel in bike.GetWheels())
{
BaseEntity fireball = CreateFireball();
fireball.SetParent(bike);
fireball.transform.localPosition = wheel.wheelCollider.transform.localPosition;
fireball.Spawn();
subEntities[bike].Add(fireball);
}
}
private void SetupRiderStaff(BasePlayer player, RidableHorse horse)
{
if (!IsValidVehicle(horse))
return;
BaseSetupRiderStaff(player, horse);
foreach (string boneName in HORSE_BONE_NAMES)
{
BaseEntity fireball = CreateFireball();
fireball.SetParent(horse, boneName);
fireball.transform.localPosition = Vector3.zero;
fireball.Spawn();
subEntities[horse].Add(fireball);
}
}
private void DisposeRiderStaff(BaseVehicle vehicle)
{
if (subEntities.TryGetValue(vehicle, out List<BaseEntity> ents))
{
foreach (var ent in ents)
if (!ent.IsDestroyed)
ent.Kill();
ents.Clear();
UnityEngine.Object.Destroy(vehicle.gameObject.GetComponent<GroundFlame>());
}
}
private class GroundFlame : FacepunchBehaviour
{
private BaseVehicle vehicle;
private float tickRate = 0.15f;
private float nextFlameTime;
private float range = 2f;
void Start()
{
vehicle = GetComponent<BaseVehicle>();
InvokeRepeating(new Action(this.FlameTick), tickRate, tickRate);
}
public void FlameTick()
{
if (vehicle.GetSpeed() > 5 && UnityEngine.Time.realtimeSinceStartup >= this.nextFlameTime)
{
this.nextFlameTime = UnityEngine.Time.realtimeSinceStartup + 0.4f;
FireBall fireball = global::GameManager.server.CreateEntity(FLAME_PREFAB, vehicle.transform.position - vehicle.transform.rotation * -Vector3.forward * range, default(Quaternion), true) as FireBall;
if (fireball)
{
fireball.creatorEntity = vehicle.GetDriver();
fireball.lifeTimeMin = 3;
fireball.lifeTimeMax = 3;
fireball.Spawn();
fireball.CancelInvoke(new Action(fireball.Think));
}
}
}
}
private void DisposeRiderStaff(BasePlayer player)
{
foreach (BaseEntity fireball in player.children.Where(e => e.prefabID == FIREBALL_PREFABID && e.parentBone == HEADBONE).ToArray())
{
if (!fireball.IsDestroyed)
fireball.Kill();
}
}
void Loaded()
{
foreach (Type type in this.GetType().GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
{
object[] attribute = type.GetCustomAttributes(typeof(HarmonyPatch), false);
if (attribute.Length >= 1)
{
PatchClassProcessor patchClassProcessor = this.HarmonyInstance.CreateClassProcessor(type);
patchClassProcessor.Patch();
}
}
}
[HarmonyPatch(typeof(BaseVehicle), "PlayerMounted")]
private static class BaseVehicle_PlayerMounted_Patch
{
private static bool Prefix(BaseVehicle __instance, BasePlayer __0, BaseMountable __1)
{
if (__instance is Bike && __1 is BikeDriverSeat && IsMotorbikeSeat(__1 as BikeDriverSeat))
{
Instance.SetupRiderStaff(__0, __instance as Bike);
return false;
}
else if (__instance is RidableHorse)
{
Instance.SetupRiderStaff(__0, __instance as RidableHorse);
return false;
}
return true;
}
}
[HarmonyPatch(typeof(BaseVehicle), "PrePlayerDismount")]
private static class BaseVehicle_PrePlayerDismount_Patch
{
private static bool Prefix(BaseVehicle __instance, BaseMountable __1)
{
if (__instance is Bike && __1 is BikeDriverSeat && IsMotorbikeSeat(__1 as BikeDriverSeat) || __instance is RidableHorse)
{
Instance.DisposeRiderStaff(__instance);
return false;
}
return true;
}
}
[HarmonyPatch(typeof(BasePlayer), "DismountObject")]
private static class BasePlayer_DismountObject_Patch
{
private static void Postfix(BasePlayer __instance)
{
Instance.DisposeRiderStaff(__instance);
}
}
[HarmonyPatch(typeof(Bike), "PlayerServerInput")]
private static class Bike_PlayerServerInput_Patch
{
private static void Postfix(Bike __instance, InputState inputState, BasePlayer player)
{
if (!__instance.IsDriver(player) || !IsValidVehicle(__instance))
return;
if (inputState.WasJustPressed(BUTTON.SPRINT))
{
Effect.server.Run(BOOST_EFFECT_PREFAB, __instance.transform.position + __instance.transform.rotation * new Vector3(0.2f, 1f, -0.7f));
}
}
}
[HarmonyPatch(typeof(Bike), "GetMaxDriveForce")]
private static class Bike_GetMaxDriveForce_Patch
{
private static void Postfix(Bike __instance, ref float __result)
{
if (Instance.subEntities.ContainsKey(__instance) && __instance.SprintInput && IsValidVehicle(__instance))
__result = __result * config.EngineMul;
}
}
[HarmonyPatch(typeof(RidableHorse), "GetRunSpeed")]
private static class RidableHorse_GetRunSpeed_Patch
{
private static void Postfix(RidableHorse __instance, ref float __result)
{
if (Instance.subEntities.ContainsKey(__instance) && __instance.currentRunState == BaseRidableAnimal.RunState.sprint && IsValidVehicle(__instance))
__result = __result * config.HorseMul;
}
}
#region Config
static Configuration config;
public class Configuration
{
[JsonProperty(PropertyName = "Can bike crashes cause damage or death to the rider?")]
public bool CanCrashDamage { get; set; } = false;
[JsonProperty(PropertyName = "Only 2-wheels motorbike?")]
public bool Only2Wheels { get; set; } = false;
[JsonProperty(PropertyName = "Multiplier Engine Power")]
public float EngineMul { get; set; } = 2.5f;
[JsonProperty(PropertyName = "Multiplier Horse Power")]
public float HorseMul { get; set; } = 2f;
public static Configuration DefaultConfig()
{
return new Configuration();
}
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<Configuration>();
if (config == null) LoadDefaultConfig();
SaveConfig();
}
catch (Exception ex)
{
Debug.LogException(ex);
PrintWarning("Creating new configuration file.");
LoadDefaultConfig();
}
}
protected override void LoadDefaultConfig() => config = Configuration.DefaultConfig();
protected override void SaveConfig() => Config.WriteObject(config);
#endregion
}
}