C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using ConVar;
using HarmonyLib;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins;
using ProtoBuf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using UnityEngine;
using static BuildingPrivlidge;
using static Carbon.Plugins.BaseBountyPerk;
using static UnityEngine.Rendering.PostProcessing.BloomRenderer;
namespace Carbon.Plugins {
// Extension method to find the driver
public static class VehicleExtensions {
public static BasePlayer GetDriver(this BaseVehicle vehicle) {
return vehicle?.mountPoints[0]?.mountable?.GetMounted() as BasePlayer;
}
}
[Info("Base Bounty", "TTV OdsScott", "1.0.0")]
[Description("Base bounty with raid protection")]
public class BaseBounty : CarbonPlugin {
public override bool AutoPatch => true;
[PluginReference]
public readonly Plugin ImageLibrary, Clans, SimpleStatus, DogTags, RaidHooks, DiscordThreads, NightTime, Kits, CarSpawnSettings;
public static BaseBounty Instance;
public ToolcupboardTracker ToolcupboardTracker;
public BaseBountyData BaseBountyData;
public const float HoursOfProtection = 4f;
private Dictionary<ulong, BaseEntity> PlayerHorses = new Dictionary<ulong, BaseEntity>();
private Dictionary<ulong, BaseEntity> PlayerCars = new Dictionary<ulong, BaseEntity>();
private Dictionary<ulong, BaseEntity> PlayerMinis = new Dictionary<ulong, BaseEntity>();
private bool _debugMode = true;
private void Init() {
Instance = this;
BaseBountyData = BaseBountyData.Load();
LoadImages();
CreateStatus();
}
public static double HoursSinceWipe {
get {
DateTime wipeTime = SaveRestore.SaveCreatedTime;
TimeSpan timeSinceWipe = DateTime.UtcNow - wipeTime;
return timeSinceWipe.TotalHours;
}
}
private void LoadImages() {
if (ImageLibrary == null || !ImageLibrary.IsLoaded) {
Debug("Image Library not found or unloaded.");
return;
}
ImageLibrary.Call<bool>("AddImage", "https://rustwipeday.com/img/raidprotectionindicator.png", "SrpIndicatorIcon", 0UL);
ImageLibrary.Call<bool>("AddImage", "https://rustwipeday.com/img/kits/dogtagneutral.png", "DogTagIcon", 0UL);
}
private void CreateStatus() {
if (SimpleStatus == null || !SimpleStatus.IsLoaded) {
Debug("Simple Status not found or unloaded.");
return;
}
SimpleStatus.CallHook("CreateStatus", this, "raidprotection.status", Colors.COLOR_GREEN_DARK_LESS, "RAID PROTECTION", Colors.COLOR_GREEN, "0%", Colors.COLOR_GREEN, "SrpIndicatorIcon", Colors.COLOR_GREEN);
SimpleStatus.CallHook("CreateStatus", this, "raidprotection.bounty", Colors.COLOR_GREY, "BASE BOUNTY", Colors.COLOR_BLACK, "0 Points", Colors.COLOR_BLACK, "itemid:1223900335", null);
}
private void OnServerInitialized() {
ToolcupboardTracker = new ToolcupboardTracker(BaseBountyData);
GameObject.FindObjectsOfType<BuildingPrivlidge>().ToList().ForEach((buildingPrivlidge) => ToolcupboardTracker.FindOrCreate(buildingPrivlidge));
foreach (BasePlayer basePlayer in BasePlayer.activePlayerList) {
ToolcupboardTracker.TrackBuildingPrivlidgeLoop(basePlayer);
}
}
private void Unload() {
OnServerSave();
Instance = null;
}
// @TODO: Ideally I think we only want to gain base bounty when turning in white dog tags making it rare and hard to level.// void OnConsumedDogTag(BasePlayer player, Item activeItem) => BaseBountyData.AddBaseBountyPoint(player, 1);
void OnAddBountyPoints(ulong playerId, int points, string type) {
Puts(type);
if (points <= 0 || type != "dogtagneutral") {
return;
}
var player = BasePlayer.FindByID(playerId);
if (player == null) {
return;
}
BaseBountyData.AddBaseBountyPoint(player, points);
}
private bool IsNightTime() {
// Utilize TimeOfDay plugin's TOD_Sky instance for accurate time checks.return TOD_Sky.Instance.Cycle.Hour < TOD_Sky.Instance.SunriseTime || TOD_Sky.Instance.Cycle.Hour > TOD_Sky.Instance.SunsetTime;
}
int OnHonorPointsAwarded(BasePlayer player, string victimName, bool isNpc, string victimRank, int points) {
var highestHonorPerk = Instance.ToolcupboardTracker.GetHighestPerkLevel(player, BaseBountyPerk.BaseBountyPerkEnum.Honor);
var newPoints = (int)(points * (1 + CalculatePercentage(highestHonorPerk, 100, 25) / 100));
// Fix for night time double rates.if (IsNightTime()) { newPoints = newPoints * 2; }
return newPoints;
}
/// <summary>
/// I did not like this
/// </summary>
/// <param name="basePlayer"></param>
/// <param name="buildingPrivlidge"></param>
/*void OnTakeBountyPoints(ulong playerId, int points)
{
var player = BasePlayer.FindByID(playerId);
if (player == null)
{
return;
}
BaseBountyData.AddBaseBountyPoint(player, points);
}*/
private void OnLootEntity(BasePlayer basePlayer, BuildingPrivlidge buildingPrivlidge) => ToolcupboardTracker.OnOpenCupboard(basePlayer, buildingPrivlidge);
private void OnLootEntityEnd(BasePlayer basePlayer, BuildingPrivlidge buildingPrivlidge) => ToolcupboardTracker.OnCloseCupboard(basePlayer, buildingPrivlidge);
private void OnPlayerSleepEnded(BasePlayer basePlayer) {
ToolcupboardTracker.TrackBuildingPrivlidgeLoop(basePlayer);
var mainCupboard = ToolcupboardTracker.GetPlayerMainCupboard(basePlayer.userID);
if (mainCupboard == null) {
return;
}
mainCupboard.DepositPlayerHeldBounty(basePlayer);
}
object OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) {
if (entity.ToPlayer() == null) {
return null;
}
var highestLevel = Instance.ToolcupboardTracker.GetHighestPerkLevel(entity.ToPlayer(), BaseBountyPerk.BaseBountyPerkEnum.Gathering);
if (highestLevel == 0) {
return null;
}
item.amount += (int)(item.amount * (0.01 * CalculatePercentage(highestLevel, 200, 30)));
return null;
}
private void OnEntityKill(BuildingPrivlidge buildingPrivlidge, HitInfo hitInfo) => ToolcupboardTracker.FindOrCreate(buildingPrivlidge).Kill(hitInfo);
private void OnEntitySpawned(BuildingPrivlidge buildingPrivlidge) => ToolcupboardTracker.FindOrCreate(buildingPrivlidge);
private object OnCupboardAuthorize(BuildingPrivlidge buildingPrivlidge, BasePlayer player) => ToolcupboardTracker.AuthorizePlayer(buildingPrivlidge, player);
private object OnCupboardDeauthorize(BuildingPrivlidge buildingPrivlidge, BasePlayer player) => ToolcupboardTracker.DeauthorizePlayer(buildingPrivlidge, player);
private void OnServerSave() => BaseBountyData.Save();
private object OnEntityTakeDamage(DecayEntity entity, HitInfo info) => ProtectFromDamage(entity, info);
private object OnEntityTakeDamage(IOEntity entity, HitInfo info) => ProtectFromDamage(entity, info);
private object OnEntityTakeDamage(BaseResourceExtractor entity, HitInfo info) => ProtectFromDamage(entity, info);
private object ProtectFromDamage(BaseCombatEntity entity, HitInfo info) {
if (entity?.net == null || info == null) {
return null;
}
BuildingPrivlidge buildingPrivlidge = entity.GetBuildingPrivilege();
if (buildingPrivlidge == null) {
return null;
}
Rust.DamageType majorityDamage = info.damageTypes.GetMajorityDamageType();
if (majorityDamage == Rust.DamageType.Decay || majorityDamage == Rust.DamageType.ElectricShock) {
return null;
}
;
BaseBountyCupboard baseBountyCupboard = ToolcupboardTracker.FindOrCreate(buildingPrivlidge);
var protectionPercentage = baseBountyCupboard.ProtectionPercentage;
if (protectionPercentage >= 100) {
return true;
}
else {
info.damageTypes.ScaleAll((float)(1 - (protectionPercentage / 100)));
}
return null;
}
object CanBuild(Planner planner, Construction prefab, Construction.Target target) {
var player = target.player;
List<DroppedItemContainer> droppedItemContainers = Facepunch.Pool.GetList<DroppedItemContainer>();
var obb = target.player.WorldSpaceBounds();
Vis.Entities(obb.position, 16f + obb.extents.magnitude, droppedItemContainers);
for (int i = 0; i < droppedItemContainers.Count; i++) {
DroppedItemContainer droppedItemContainer = droppedItemContainers[i];
if (ToolcupboardTracker.BaseBountyClaimContainer.ContainsKey(droppedItemContainer)) {
player.ChatMessage($"<color=#c45508>[RustWipeDay]</color>: You can not build in this area while a base bounty is still unclaimed.");
return false;
}
}
Facepunch.Pool.FreeList(ref droppedItemContainers);
return null;
}
object CanLootEntity(BasePlayer player, DroppedItemContainer droppedItemContainer) => ToolcupboardTracker.OnLootClaimContainer(player, droppedItemContainer);
#region Fuel System Perk Handling
/// <summary>
/// Tracks active engine perks for vehicles by storing fuel storage IDs, driver IDs, and perk levels./// </summary>
private Dictionary<ulong, (ulong driverId, int perkLevel)> FuelStorageEnginePerks = new Dictionary<ulong, (ulong, int)>();
#endregion
#region Engine Event Handlers
/// <summary>
/// Called when a vehicle engine starts.Registers the driver and their highest fuel perk level./// </summary>
/// <param name="vehicle">The vehicle whose engine is starting.</param>
/// <param name="driver">The player driving the vehicle.</param>
/// <returns>Null to allow normal engine start behavior.</returns>
private object OnEngineStart(BaseVehicle vehicle, BasePlayer driver) {
if (vehicle == null || driver == null) {
return null;
// No valid vehicle or driver
}
var fuelSystem = vehicle.GetFuelSystem();
if (fuelSystem == null || !ToolcupboardTracker.GetPlayerBaseBountyToolcupboards(driver.userID).Any()) {
return null;
// No fuel system or no eligible cupboards for the driver
}
// Retrieve the highest fuel perk level for the driver
var highestFuelLevel = ToolcupboardTracker.GetHighestPerkLevel(driver, BaseBountyPerk.BaseBountyPerkEnum.Fuel);
var fuelStorageId = fuelSystem.GetInstanceID().Value;
// Update the perks tracking dictionary
FuelStorageEnginePerks[fuelStorageId] = (driver.userID, highestFuelLevel);
return null;
}
/// <summary>
/// Called when a vehicle engine stops.Cleans up any associated engine perks./// </summary>
/// <param name="vehicle">The vehicle whose engine is stopping.</param>
/// <param name="driver">The player driving the vehicle.</param>
/// <returns>Null to allow normal engine stop behavior.</returns>
private object OnEngineStop(BaseVehicle vehicle, BasePlayer driver) {
if (vehicle == null) {
return null;
// No valid vehicle
}
var fuelSystem = vehicle.GetFuelSystem();
if (fuelSystem == null) {
return null;
// No fuel system
}
var fuelStorageId = fuelSystem.GetInstanceID().Value;
FuelStorageEnginePerks.Remove(fuelStorageId);
// Clean up perks on engine stop
return null;
}
#endregion
#region Fuel Efficiency Hook
/// <summary>
/// Modifies fuel usage based on the player's fuel perk level./// </summary>
/// <param name="fuelSystem">The fuel system of the vehicle.</param>
/// <param name="fuelContainer">The container holding the vehicle's fuel.</param>
/// <param name="seconds">The duration for which fuel is being consumed.</param>
/// <param name="fuelUsedPerSecond">The base amount of fuel consumed per second.</param>
/// <returns>Null to allow normal fuel consumption behavior, or 0 to prevent consumption.</returns>
private object CanUseFuel(EntityFuelSystem fuelSystem, StorageContainer fuelContainer, float seconds, float fuelUsedPerSecond) {
if (fuelSystem == null || fuelContainer == null) {
return null;
// Invalid fuel system or container
}
var fuelStorageId = fuelSystem.fuelStorageInstance.uid.Value;
if (!FuelStorageEnginePerks.TryGetValue(fuelStorageId, out var perkData)) {
return null;
// No perks registered for this fuel storage
}
var (driverId, fuelPerk) = perkData;
// Apply the fuel perk bonus to reduce consumption
var fuelBonus = (seconds * fuelUsedPerSecond) * (fuelPerk * 0.01f);
fuelSystem.pendingFuel -= fuelBonus;
// Reduce pending fuel consumption
return null;
}
#endregion
private (int baseXp, int baseXpRequired, int baseLevel) GetPlayerBaseLevel(ulong userId) {
var cupboard = ToolcupboardTracker.GetPlayerMainCupboard(userId);
try {
return (cupboard.Bounty - cupboard.LevelXp, cupboard.NextLevelXp - cupboard.LevelXp, cupboard.Level);
}
catch (Exception e) {
return (0, 0, 0);
}
}
[ChatCommand("basebounty")]
private void BaseBountyCommand(BasePlayer player, string command, string[] args) {
var amount = BaseBountyData.GetBaseBountyPoints(player.userID);
SendReply(player, $"<color=#c45508>[RustWipeDay]</color>: You currently have {amount} base bounty points." );
}
[ConsoleCommand("basebounty.claim")]
private void Command_BaseBounty_Claim(IPlayer player, string command, string[] args) {
var userId = ulong.Parse(player.Id);
var basePlayer = BasePlayer.FindByID(userId);
if (basePlayer == null || ToolcupboardTracker.GetPlayerMainCupboard(userId) == null) {
return;
}
ulong privId = Convert.ToUInt64(args[0]);
var baseBountyCupboard = ToolcupboardTracker.Find(privId);
if (baseBountyCupboard == null ) {
return;
}
uint perkId = Convert.ToUInt32(args[1]);
var perk = BaseBountyPerk.BaseBountyPerks[(int)perkId];
if (baseBountyCupboard.Data.BaseBountyPerkLevel[perkId] + 1 > perk.MaxLevel) {
return;
}
var cost = perk.Cost;
if (!baseBountyCupboard.SpendBounty(cost)) {
basePlayer.ChatMessage($"<color=#c45508>[RustWipeDay]</color>: Not enough base bounty points to claim the {perk.Title} base perk.");
return;
}
baseBountyCupboard.Data.BaseBountyPerkLevel[perkId]++;
if (perkId == (int)BaseBountyPerk.BaseBountyPerkEnum.TurretLimit) {
baseBountyCupboard.UpdateSentryInterference();
}
baseBountyCupboard.UpdateMapMarker();
basePlayer.ChatMessage($"<color=#c45508>[RustWipeDay]</color>: You have claimed the {perk.Title} base perk for {cost} bounty points.Your base bounty has increased to {baseBountyCupboard.Bounty}.");
Command_BaseBounty_Panel(player, command, new string[] {
args[0]
}
);
}
[ConsoleCommand("basebounty.panel.close")]
private void Command_BaseBounty_Panel_Close(IPlayer player, string command, string[] args) {
var basePlayer = BasePlayer.FindByID(ulong.Parse(player.Id));
if (basePlayer == null) {
return;
}
CuiHelper.DestroyUi(basePlayer, "BaseBountyPanel");
}
[Command("basebounty.panel")]
private void Command_BaseBounty_Panel(IPlayer player, string command, string[] args) {
var basePlayer = BasePlayer.FindByID(ulong.Parse(player.Id));
if (basePlayer == null) {
return;
}
var baseBountyCupboard = ToolcupboardTracker.GetPlayerMainCupboard(basePlayer.userID);
if (baseBountyCupboard == null) {
return;
}
basePlayer.EndLooting();
basePlayer.ClientRPCPlayer(null, basePlayer, "OnDied");
Gui.OpenBaseBountyPanel(basePlayer, baseBountyCupboard);
}
// This command spawns or claims a horse for the player.[ChatCommand("myhorse")]
private void MyHorseCommand(BasePlayer player, string command, string[] args) {
// Check for level requirements first
var mainCupboard = ToolcupboardTracker.GetPlayerMainCupboard(player.userID);
if (mainCupboard == null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You do not have a main cupboard set.Set one using /basebounty.setmain.");
return;
}
int animalPerkLevel = mainCupboard.Level;
if (animalPerkLevel < 5) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You must have a base bounty level of at least 5 to use this command.");
return;
}
var mountedEntity = player.GetMountedVehicle();
// Check if the player is mounted on a horse
if (mountedEntity is BaseMountable mountedHorse && mountedHorse.ShortPrefabName.Contains("horse")) {
if (args.Length > 0 && args[0].ToLower() == "claim") {
// Claim the mounted horse
if (mountedHorse.OwnerID != 0 || PlayerHorses.ContainsValue(mountedHorse)) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: This horse is already owned.");
return;
}
mountedHorse.OwnerID = player.userID;
// Assign ownership
PlayerHorses[player.userID] = mountedHorse;
mountedHorse.gameObject.AddComponent<TeleportCooldown>().Initialize(180f);
// Apply cooldown
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You have claimed this horse.");
return;
}
else {
// Inform the player about /myhorse claim
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You are currently on a horse.Use <color=#00ff00>/myhorse claim</color> to claim it if it's unowned.");
return;
}
}
// Check for existing horse owned by the player
if (PlayerHorses.TryGetValue(player.userID, out BaseEntity existingHorse) && existingHorse.IsValid()) {
// Teleport the horse to the player's location
if (existingHorse.GetComponent<TeleportCooldown>() != null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You must wait before teleporting your horse again.");
return;
}
existingHorse.transform.position = FindSafeSpawnPosition(player.transform.position + player.transform.forward * 2f, 20f);
existingHorse.SendNetworkUpdate();
existingHorse.gameObject.AddComponent<TeleportCooldown>().Initialize(180f);
// 3-minute cooldown
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Your horse has been teleported to a safe location near you.");
return;
}
// Find a safe spawn position for the horse
Vector3 spawnPosition = FindSafeSpawnPosition(player.transform.position + player.transform.forward * 5f, 20f);
if (spawnPosition == Vector3.zero) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Unable to find a safe location to spawn your horse.Please try again.");
return;
}
// Spawn the horse
BaseEntity horse = GameManager.server.CreateEntity("assets/rust.ai/nextai/testridablehorse.prefab", spawnPosition, Quaternion.identity);
if (horse == null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Failed to spawn the horse.Please try again.");
return;
}
horse.Spawn();
horse.OwnerID = player.userID;
PlayerHorses[player.userID] = horse;
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Your horse has been granted.");
}
// This command spawns or claims a modular car for the player.[ChatCommand("mycar")]
private void MyCarCommand(BasePlayer player, string command, string[] args) {
// Check for level requirements first
var mainCupboard = ToolcupboardTracker.GetPlayerMainCupboard(player.userID);
if (mainCupboard == null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You do not have a main cupboard set.Set one using /basebounty.setmain.");
return;
}
int vehiclePerkLevel = mainCupboard.Level;
if (vehiclePerkLevel < 10) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You must have a base bounty level of at least 10 to use this command.");
return;
}
var mountedVehicle = player.GetMountedVehicle();
// Check if the player is mounted in a vehicle
if (mountedVehicle) {
if (args.Length > 0 && args[0].ToLower() == "claim") {
// Claim the mounted modular car
if (mountedVehicle.OwnerID != 0 || PlayerCars.ContainsValue(mountedVehicle)) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: This vehicle is already owned.");
return;
}
if (IsFlyingVehicle(mountedVehicle)) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You can only claim ground vehicles.");
return;
}
mountedVehicle.OwnerID = player.userID;
// Assign ownership
PlayerCars[player.userID] = mountedVehicle;
mountedVehicle.gameObject.AddComponent<TeleportCooldown>().Initialize(180f);
// Apply cooldown
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You have claimed this vehicle.");
return;
}
else {
// Inform the player about /mycar claim
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You are currently in a vehicle.Use <color=#00ff00>/mycar claim</color> to claim it if it's unowned.");
return;
}
}
// Check for existing car owned by the player
if (PlayerCars.TryGetValue(player.userID, out BaseEntity existingCar) && existingCar.IsValid()) {
// Teleport the car to the player's location
if (existingCar.GetComponent<TeleportCooldown>() != null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You must wait before teleporting your vehicle again.");
return;
}
existingCar.transform.position = FindSafeSpawnPosition(player.transform.position + player.transform.forward * 2f, 20f);
existingCar.SendNetworkUpdate();
existingCar.gameObject.AddComponent<TeleportCooldown>().Initialize(180f);
// 3-minute cooldown
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Your vehicle has been teleported to a safe location near you.");
return;
}
// Find a safe spawn position for the car
Vector3 spawnPosition = FindSafeSpawnPosition(player.transform.position + player.transform.forward * 5f, 20f);
if (spawnPosition == Vector3.zero) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Unable to find a safe location to spawn your vehicle.Please try again.");
return;
}
// Spawn the car
ModularCar car = GameManager.server.CreateEntity("assets/content/vehicles/modularcar/4module_car_spawned.entity.prefab", spawnPosition, Quaternion.identity) as ModularCar;
if (car == null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Failed to spawn the vehicle.Please try again.");
return;
}
car.Spawn();
CarSpawnSettings.Call("ProcessCar", car);
// Equip, repair, and fuel the car
car.OwnerID = player.userID;
PlayerCars[player.userID] = car;
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Your vehicle has been granted.");
}
// This command spawns a minicopter for the player.[ChatCommand("mymini")]
private void MyMiniCommand(BasePlayer player, string command, string[] args) {
// Check for level requirements first
var mainCupboard = ToolcupboardTracker.GetPlayerMainCupboard(player.userID);
if (mainCupboard == null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You do not have a main cupboard set.Set one using /basebounty.setmain.");
return;
}
int vehiclePerkLevel = mainCupboard.Level;
if (vehiclePerkLevel < 20) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You must have a base bounty level of at least 20 to use this command.");
return;
}
var mountedVehicle = player.GetMountedVehicle();
// Check if the player is mounted in a vehicle
if (mountedVehicle) {
if (args.Length > 0 && args[0].ToLower() == "claim") {
// Claim the mounted flying vehicle
if (mountedVehicle.OwnerID != 0 || PlayerMinis.ContainsValue(mountedVehicle)) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: This vehicle is already owned.");
return;
}
if (!IsFlyingVehicle(mountedVehicle)) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You can only claim flying vehicles.");
return;
}
mountedVehicle.OwnerID = player.userID;
// Assign ownership
PlayerMinis[player.userID] = mountedVehicle;
mountedVehicle.gameObject.AddComponent<TeleportCooldown>().Initialize(180f);
// Apply cooldown
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You have claimed this vehicle.");
return;
}
else {
// Inform the player about /mymini claim
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You are currently in a vehicle.Use <color=#00ff00>/mymini claim</color> to claim it if it's unowned.");
return;
}
}
// Check for existing minicopter owned by the player
if (PlayerMinis.TryGetValue(player.userID, out BaseEntity existingMini) && existingMini.IsValid()) {
// Teleport the minicopter to the player's location
if (existingMini.GetComponent<TeleportCooldown>() != null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: You must wait before teleporting your vehicle again.");
return;
}
existingMini.transform.position = FindSafeSpawnPosition(player.transform.position + player.transform.forward * 2f, 20f);
existingMini.SendNetworkUpdate();
existingMini.gameObject.AddComponent<TeleportCooldown>().Initialize(180f);
// 3-minute cooldown
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Your minicopter has been teleported to a safe location near you.");
return;
}
// Find a safe spawn position for the minicopter
Vector3 spawnPosition = FindSafeSpawnPosition(player.transform.position + player.transform.forward * 5f, 20f);
if (spawnPosition == Vector3.zero) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Unable to find a safe location to spawn your vehicle.Please try again.");
return;
}
// Spawn the minicopter
BaseEntity mini = GameManager.server.CreateEntity("assets/content/vehicles/minicopter/minicopter.entity.prefab", spawnPosition, Quaternion.identity);
if (mini == null) {
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Failed to spawn the minicopter.Please try again.");
return;
}
mini.Spawn();
mini.OwnerID = player.userID;
PlayerMinis[player.userID] = mini;
SendReply(player, "<color=#c45508>[RustWipeDay]</color>: Your minicopter has been granted.");
}
#region Helper Methods
/// <summary>
/// Finds a safe spawn position within a defined radius./// Ensures the position is free of players, entities, and water./// </summary>
/// <param name="startPosition">The initial position to search around.</param>
/// <param name="maxRadius">The maximum radius to search.</param>
/// <returns>A safe position or Vector3.zero if none is found.</returns>
private Vector3 FindSafeSpawnPosition(Vector3 startPosition, float maxRadius) {
const int maxAttempts = 15;
const float safeDistance = 5f;
for (int i = 0; i < maxAttempts; i++) {
Vector3 potentialPosition = startPosition + UnityEngine.Random.insideUnitSphere * maxRadius;
potentialPosition.y = TerrainMeta.HeightMap.GetHeight(potentialPosition);
if (IsPositionSafe(potentialPosition, safeDistance)) {
return potentialPosition;
}
}
return Vector3.zero;
// No safe position found
}
/// <summary>
/// Checks if a position is safe for spawning a car./// Ensures no nearby players, entities, or water./// </summary>
/// <param name="position">The position to check.</param>
/// <param name="safeDistance">The minimum safe distance from other entities or players.</param>
/// <returns>True if the position is safe; otherwise, false.</returns>
private bool IsPositionSafe(Vector3 position, float safeDistance) {
// Ensure no players are nearby
foreach (BasePlayer player in BasePlayer.activePlayerList) {
if (player.Distance(position) < safeDistance) {
return false;
}
}
// Ensure no significant entities are nearby
var entities = new List<BaseEntity>();
Vis.Entities(position, safeDistance, entities, LayerMask.GetMask("Default", "Deployed", "Construction"));
foreach (BaseEntity entity in entities) {
if (entity != null && entity is BaseCombatEntity) {
return false;
}
}
// Ensure the position is on solid ground and not in water
return UnityEngine.Physics.Raycast(position + Vector3.up * 0.5f, Vector3.down, out RaycastHit hit, safeDistance, LayerMask.GetMask("Terrain")) && hit.collider != null;
}
/// <summary>
/// Determines if a vehicle is a flying vehicle./// </summary>
/// <param name="vehicle">The vehicle to check.</param>
/// <returns>True if the vehicle can fly; otherwise, false.</returns>
private bool IsFlyingVehicle(BaseVehicle vehicle) {
// Check for specific flying vehicle types (e.g., Minicopter, Scrap Transport Helicopter)
return vehicle.PrefabName.Contains("minicopter") || vehicle.PrefabName.Contains("scraptransport");
}
#endregion
private class TeleportCooldown : MonoBehaviour {
private float cooldownTime;
public void Initialize(float cooldown) {
cooldownTime = UnityEngine.Time.time + cooldown;
Invoke(nameof(RemoveCooldown), cooldown);
}
private void RemoveCooldown() {
Destroy(this);
}
}
[Command("basebounty.setmain")]
private void Command_BaseBounty_SetMain(IPlayer player, string command, string[] args) {
var playerId = ulong.Parse(player.Id);
var basePlayer = BasePlayer.FindByID(ulong.Parse(player.Id));
ulong privId = Convert.ToUInt64(args[0]);
var baseBountyCupboard = ToolcupboardTracker.Find(privId);
if (baseBountyCupboard == null) {
return;
}
var hasMainCupboard = ToolcupboardTracker.GetPlayerMainCupboard(basePlayer.userID) != null;
Debug($"Main cupboard: {hasMainCupboard}");
if (hasMainCupboard) {
return;
}
var protectedCupboard = ToolcupboardTracker.Find(privId);
if (protectedCupboard == null || protectedCupboard.Data.IsRemoved) {
return;
}
Debug($"Checking ownership {playerId}");
if (protectedCupboard.Data.AllOwnerIds.Contains(playerId)) {
Debug($"Setting main");
protectedCupboard.Data.IsMainCupboard = true;
protectedCupboard.Data.PlayerRelations.ForEach((relationId) => {
var relationPlayer = BasePlayer.FindByID(relationId);
if (relationPlayer == null) {
return;
}
relationPlayer.ChatMessage($"<color=#c45508>[RustWipeDay]</color>: Your main cupboard has been set and raid protection has been enabled for this wipe.");
protectedCupboard.DepositPlayerHeldBounty(relationPlayer);
}
);
CuiHelper.DestroyUi(basePlayer, "RaidProtectionControl");
Gui.CreateRaidProtectionButton(basePlayer, privId, true);
return;
}
//basePlayer.EndLooting();
//basePlayer.ClientRPCPlayer(null, basePlayer, "OnDied");
//OpenBaseBountyPanel(basePlayer, Convert.ToUInt64(args[0]));
}
#region Harmony Patches
[HarmonyPatch(typeof(VehicleEngineController<GroundVehicle>), "TickFuel")]
public static class TickFuelPatch_GroundVehicle {
// Prefix method to adjust fuel consumption before the original method executes
static void Prefix(ref float fuelPerSecond, VehicleEngineController<GroundVehicle> __instance) {
// Validate that the engine is associated with a vehicle
if (__instance.owner is GroundVehicle vehicle) {
// Attempt to find the driver (BasePlayer) for the vehicle
BasePlayer driver = vehicle.GetDriver() as BasePlayer;
if (driver != null) {
// Get the player's fuel perk level
int fuelPerkLevel = GetPlayerFuelPerkLevel(driver);
if (fuelPerkLevel > 0) {
// Reduce fuel consumption by a percentage based on the perk level
float multiplier = 1f - (fuelPerkLevel * 0.01f);
// Example: Each level reduces fuel by 1%
fuelPerSecond *= Mathf.Clamp(multiplier, 0.1f, 1f);
// Clamp multiplier to prevent zero or negative fuel usage
}
}
}
}
// Example method to retrieve the player's fuel perk level
static int GetPlayerFuelPerkLevel(BasePlayer basePlayer) {
// Replace with your logic to fetch the player's fuel perk level
return Instance.ToolcupboardTracker.GetHighestPerkLevel(basePlayer, BaseBountyPerk.BaseBountyPerkEnum.Fuel);
//return 30; // ToolcupboardTracker.GetPlayerBaseBountyPerkLevel(userID, );
}
}
[HarmonyPatch(typeof(VehicleEngineController<PlayerHelicopter>), "TickFuel")]
public static class TickFuelPatch_PlayerHelicopter {
// Prefix method to adjust fuel consumption before the original method executes
static void Prefix(ref float fuelPerSecond, VehicleEngineController<PlayerHelicopter> __instance) {
// Validate that the engine is associated with a vehicle
if (__instance.owner is PlayerHelicopter vehicle) {
// Attempt to find the driver (BasePlayer) for the vehicle
BasePlayer driver = vehicle.GetDriver();
if (driver != null) {
// Get the player's fuel perk level
int fuelPerkLevel = GetPlayerFuelPerkLevel(driver);
if (fuelPerkLevel > 0) {
// Reduce fuel consumption by a percentage based on the perk level
float multiplier = 1f - (fuelPerkLevel * 0.01f);
// Example: Each level reduces fuel by 1%
fuelPerSecond *= Mathf.Clamp(multiplier, 0.1f, 1f);
// Clamp multiplier to prevent zero or negative fuel usage
}
}
}
}
// Example method to retrieve the player's fuel perk level
static int GetPlayerFuelPerkLevel(BasePlayer basePlayer) {
// Replace with your logic to fetch the player's fuel perk level
return Instance.ToolcupboardTracker.GetHighestPerkLevel(basePlayer, BaseBountyPerk.BaseBountyPerkEnum.Fuel);
}
}
[HarmonyPatch(typeof(BuildingPrivlidge), "CalculateBuildingTaxRate", new Type[] {
}
)] public class BuildingPrivlidge_CalculateBuildingTaxRate {
private static bool Prefix(BuildingPrivlidge __instance, ref float __result) {
BuildingManager.Building building = __instance.GetBuilding();
if (building == null) {
__result = Instance.GetBuildingPriviledgeUpkeepRate(__instance, ConVar.Decay.bracket_0_costfraction);
return false;
}
if (!building.HasBuildingBlocks()) {
__result = Instance.GetBuildingPriviledgeUpkeepRate(__instance, ConVar.Decay.bracket_0_costfraction);
return false;
}
int count = building.buildingBlocks.Count;
int num = count;
for (int i = 0; i < upkeepBrackets.Length; i++) {
UpkeepBracket upkeepBracket = upkeepBrackets[i];
upkeepBracket.blocksTaxPaid = 0f;
if (num > 0) {
int num2 = 0;
num2 = ((i != upkeepBrackets.Length - 1) ? Mathf.Min(num, upkeepBrackets[i].objectsUpTo) : num);
num -= num2;
upkeepBracket.blocksTaxPaid = (float)num2 * upkeepBracket.fraction;
}
}
float num3 = 0f;
for (int j = 0; j < upkeepBrackets.Length; j++) {
UpkeepBracket upkeepBracket2 = upkeepBrackets[j];
if (!(upkeepBracket2.blocksTaxPaid > 0f)) {
break;
}
num3 += upkeepBracket2.blocksTaxPaid;
}
__result = Instance.GetBuildingPriviledgeUpkeepRate(__instance, num3 / (float)count);
return false;
}
}
/* [HarmonyPatch(typeof(BasePlayer), "SendMarkersToClient", new Type[] { })]
public class BasePlayer_SendMarkersToClient
{
private static bool Prefix(BasePlayer __instance)
{
MapNoteList mapNoteList = Facepunch.Pool.Get<MapNoteList>();
mapNoteList.notes = Facepunch.Pool.GetList<MapNote>();
if (__instance.ServerCurrentDeathNote != null)
{
mapNoteList.notes.Add(__instance.ServerCurrentDeathNote);
}
if (__instance.State.pointsOfInterest != null)
{
mapNoteList.notes.AddRange(__instance.State.pointsOfInterest);
}
mapNoteList.notes.AddRange(Instance.ToolcupboardTracker.BaseBounties.Values.Where(tc => tc.Bounty >= 150 && tc.MapNote != null).OrderByDescending(tc => tc.Bounty).Select(tc => tc.MapNote).Take(3));
__instance.ClientRPCPlayer(null, __instance, "Client_ReceiveMarkers", mapNoteList);
mapNoteList.notes.Clear();
return false;
}
} */
[HarmonyPatch(typeof(AutoTurret), "UpdateInterference", new Type[] {
}
)] public class AutoTurret_UpdateInterference {
private static bool Prefix(AutoTurret __instance) {
if (!__instance.IsOn()) {
return false;
}
float num = 0f;
foreach (AutoTurret nearbyTurret in __instance.nearbyTurrets) {
if (!nearbyTurret.isClient && nearbyTurret.IsValid() && nearbyTurret.gameObject.activeSelf && !nearbyTurret.EqualNetID(__instance.net.ID) && nearbyTurret.IsOn() && !nearbyTurret.HasInterference()) {
num += 1f;
}
}
__instance.SetFlag(BaseEntity.Flags.OnFire, num >= Instance.GetSentryInterference(__instance));
return false;
}
}
[HarmonyPatch(typeof(ItemCrafter), "ServerUpdate")]
public class ItemCrafter_ServerUpdate {
private static bool Prefix(ItemCrafter __instance, float delta) {
var player = __instance.owner;
var highestCraftingLevel = Instance.ToolcupboardTracker.GetHighestPerkLevel(player, BaseBountyPerk.BaseBountyPerkEnum.Crafting);
if (highestCraftingLevel == 0) {
return true;
}
if (__instance.queue.Count == 0) {
return false;
}
ItemCraftTask value = __instance.queue.First.Value;
if (value.cancelled) {
__instance.owner.Command("note.craft_done", value.taskUID, 0);
__instance.queue.RemoveFirst();
return false;
}
float currentCraftLevel = __instance.owner.currentCraftLevel;
if (value.endTime > UnityEngine.Time.realtimeSinceStartup) {
return false;
}
if (value.endTime == 0f) {
float scaledDuration = Math.Max(ItemCrafter.GetScaledDuration(value.blueprint, currentCraftLevel, __instance.owner.IsInTutorial) * (float)(1 - (0.02 * highestCraftingLevel)), 0f);
value.endTime = UnityEngine.Time.realtimeSinceStartup + scaledDuration;
value.workbenchEntity = __instance.owner.GetCachedCraftLevelWorkbench();
if (__instance.owner != null) {
__instance.owner.Command("note.craft_start", value.taskUID, scaledDuration, value.amount);
if (__instance.owner.IsAdmin && Craft.instant) {
value.endTime = UnityEngine.Time.realtimeSinceStartup + 1f;
}
}
}
else {
__instance.FinishCrafting(value);
if (value.amount <= 0) {
__instance.queue.RemoveFirst();
}
else {
value.endTime = 0f;
}
}
return false;
}
}
private float GetBuildingPriviledgeUpkeepRate(BuildingPrivlidge buildingPrivlidge, float cost) {
if (buildingPrivlidge == null) {
return cost;
}
BaseBountyCupboard baseBountyCupboard = ToolcupboardTracker.FindOrCreate(buildingPrivlidge);
var level = baseBountyCupboard.Level;
return cost * (float)(1 - level * 0.02);
}
private float GetSentryInterference(AutoTurret autoTurret) {
var buildingPrivlidge = autoTurret.GetNearestBuildingPrivledge();
var sentryInterference = Sentry.maxinterference;
if (buildingPrivlidge == null) {
return sentryInterference;
}
BaseBountyCupboard baseBountyCupboard = ToolcupboardTracker.FindOrCreate(buildingPrivlidge);
var level = baseBountyCupboard.Level;
var limit = CalculateTurretLimit(level);
return limit + sentryInterference;
}
#endregion
public void Debug(string message) {
if (_debugMode) {
Puts(message);
}
}
// ... [Code preview truncated due to length. Full source in attached file] ...