C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
#region License (GPL v2)
/*
DESCRIPTION
Copyright (c) 2024 RFC1920 <desolationoutpostpve@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License v2.0
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.Optionally you can also view the license at <http://www.gnu.org/licenses/>.*/
#endregion License Information (GPL v2)
#region License Notice
/*
* Hovering class modified from code at https://umod.org/plugins/helicopter-hover,
originally licensed under the following license:
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
#endregion
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Oxide.Plugins {
[Info("My Mini Copter", "RFC1920", "0.6.9")]
// Thanks to BuzZ[PHOQUE], the original author of this plugin
[Description("Spawn a Mini Helicopter")]
internal class MyMiniCopter : RustPlugin {
[PluginReference]
private readonly Plugin NoEscape, Friends, Clans;
public static MyMiniCopter Instance;
private const string prefab = "assets/content/vehicles/minicopter/minicopter.entity.prefab";
private ConfigData configData;
private const string MinicopterSpawn = "myminicopter.spawn";
private const string MinicopterFetch = "myminicopter.fetch";
private const string MinicopterWhere = "myminicopter.where";
private const string MinicopterAdmin = "myminicopter.admin";
private const string MinicopterCooldown = "myminicopter.cooldown";
private const string MinicopterUnlimited = "myminicopter.unlimited";
private const string MinicopterCanHover = "myminicopter.canhover";
private static LayerMask layerMask = LayerMask.GetMask("Terrain", "World", "Construction");
private Dictionary<ulong, ulong> currentMounts = new();
private Dictionary<int, Hovering> hovers = new();
private Dictionary<ulong, DateTime> hoverDelayTimers = new();
private static readonly DateTime epoch = new(1970, 1, 1, 0, 0, 0);
private bool do056upgrade;
private class StoredData {
public Dictionary<ulong, NetworkableId> playerminiID = new();
public Dictionary<ulong, double> playercounter = new();
}
private StoredData storedData;
// For pre-0.5.6 upgrade
private class OldStoredData {
public Dictionary<ulong, uint> playerminiID = new();
public Dictionary<ulong, double> playercounter = new();
}
private bool HasPermission(ConsoleSystem.Arg arg, string permname) {
return arg.Connection.player is not BasePlayer || permission.UserHasPermission((arg.Connection.player as BasePlayer)?.UserIDString, permname);
}
#region loadunload
private void OnServerInitialized() {
LoadConfigVariables();
if (configData?.VIPSettings?.Count > 0) {
foreach (string vipperm in configData.VIPSettings.Keys) {
string perm = vipperm.StartsWith($"{Name.ToLower()}.") ? vipperm : $"{Name.ToLower()}.{vipperm}";
DoLog($"Registering vip perm {perm}");
permission.RegisterPermission(perm, this);
}
}
if (((configData.Global.cooldownmin * 60) <= 120) && configData.Global.useCooldown) {
PrintError("Please set a longer cooldown time.Minimum is 2 min.");
configData.Global.cooldownmin = 2;
SaveConfig(configData);
}
LoadData();
foreach (KeyValuePair<ulong, NetworkableId> playerMini in storedData.playerminiID) {
Minicopter miniCopter = BaseNetworkable.serverEntities.Find(playerMini.Value) as Minicopter;
if (miniCopter == null) continue;
BasePlayer pl = FindPlayerById(playerMini.Key);
if (pl == null) continue;
VIPSettings vipsettings;
GetVIPSettings(pl, out vipsettings);
bool vip = vipsettings != null;
if (permission.UserHasPermission(playerMini.Key.ToString(), MinicopterCanHover)) {
hovers.Add(miniCopter.GetInstanceID(), miniCopter.gameObject.AddComponent<Hovering>());
}
IFuelSystem fuelCan = miniCopter?.GetFuelSystem();
if (permission.UserHasPermission(playerMini.Key.ToString(), MinicopterUnlimited) || (vip && vipsettings.unlimited && !vipsettings.canloot)) {
miniCopter.fuelPerSec = 0f;
if (fuelCan != null) {
if (!fuelCan.HasFuel()) {
DoLog($"Setting fuel for MiniCopter {playerMini.Value} owned by {playerMini.Key}.");
fuelCan?.AddFuel(1);
}
}
continue;
}
miniCopter.fuelPerSec = vip ? vipsettings.stdFuelConsumption : configData.Global.stdFuelConsumption;
}
}
private void OnNewSave() {
storedData = new StoredData();
SaveData();
}
private void Init() {
Instance = this;
AddCovalenceCommand("mymini", "SpawnMyMinicopterCommand");
AddCovalenceCommand("nomini", "KillMyMinicopterCommand");
AddCovalenceCommand("gmini", "GetMyMiniMyCopterCommand");
AddCovalenceCommand("wmini", "WhereisMyMiniMyCopterCommand");
AddCovalenceCommand("remini", "ReSpawnMyMinicopterCommand");
AddCovalenceCommand("hmini", "HoverMyMinicopterCommand");
permission.RegisterPermission(MinicopterSpawn, this);
permission.RegisterPermission(MinicopterFetch, this);
permission.RegisterPermission(MinicopterWhere, this);
permission.RegisterPermission(MinicopterAdmin, this);
permission.RegisterPermission(MinicopterCooldown, this);
permission.RegisterPermission(MinicopterUnlimited, this);
permission.RegisterPermission(MinicopterCanHover, this);
}
private void Unload() {
SaveData();
foreach (KeyValuePair<int, Hovering> hover in hovers) UnityEngine.Object.Destroy(hover.Value);
}
#endregion
#region Messages
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
{
"MyMiniHelp", "Spawn minicopter in front of you."
}
, {
"NoMiniHelp", "Destroy your minicopter if in range ({0} meters)."
}
, {
"WMiniHelp", "Find your minicopter."
}
, {
"GetMiniHelp", "Retrieve your minicopter."
}
, {
"AlreadyMsg", "You already have a mini helicopter.\nUse command '/nomini' to remove it."
}
, {
"SpawnedMsg", "Your mini copter has spawned !\nUse command '/nomini' to remove it."
}
, {
"KilledMsg", "Your mini copter has been removed/killed."
}
, {
"NoPermMsg", "You are not allowed to do this."
}
, {
"RaidBlockMsg", "You are not allowed to do this while raid blocked!"
}
, {
"SpawnUsage", "You need to supply a valid SteamId."
}
, {
"NoFoundMsg", "You do not have an active copter."
}
, {
"FoundMsg", "Your copter is located at {0}."
}
, {
"CooldownMsg", "You must wait {0} seconds before spawning a new mini copter."
}
, {
"DistanceMsg", "You must be within {0} meters of your mini copter."
}
, {
"FlyingMsg", "Your copter is currently flying and cannot be fetched."
}
, {
"RunningMsg2", "Your copter is currently running and cannot be fetched."
}
, {
"BlockedMsg", "You cannot spawn or fetch your copter while building blocked."
}
, {
"NotFlying", "The copter is not flying"
}
, {
"NoAccess", "You do not have permission to access this minicopter"
}
, {
"NoPermission", "You do not have permission to hover"
}
, {
"HoverEnabled", "MiniCopter hover: enabled"
}
, {
"HoverDisabled", "MiniCopter hover: disabled"
}
, {
"NotInHelicopter", "You are not in a minicopter"
}
, {
"NoPassengerToggle", "Passengers cannot toggle hover"
}
}
, this, "en");
lang.RegisterMessages(new Dictionary<string, string> {
{
"MyMiniHelp", "Créez un mini hélicoptère devant vous."
}
, {
"NoMiniHelp", "Détruisez votre mini hélicoptère si il est à portée.({0} mètres)."
}
, {
"GetMiniHelp", "Récupérez votre mini hélicoptère."
}
, {
"AlreadyMsg", "Vous avez déjà un mini hélicoptère\nUtilisez la commande '/nomini' pour le supprimer."
}
, {
"SpawnedMsg", "Votre mini hélico est arrivé !\nUtilisez la commande '/nomini' pour le supprimer."
}
, {
"KilledMsg", "Votre mini hélico a disparu du monde."
}
, {
"NoPermMsg", "Vous n'êtes pas autorisé."
}
, {
"RaidBlockMsg", "Vous n'êtes pas autorisé à faire cela pendant que le raid est bloqué!"
}
, {
"SpawnUsage", "Vous devez fournir un SteamId valide."
}
, {
"NoFoundMsg", "Vous n'avez pas de mini hélico actif"
}
, {
"FoundMsg", "Votre mini hélico est situé à {0}."
}
, {
"CooldownMsg", "Vous devez attendre {0} secondes avant de créer un nouveau mini hélico."
}
, {
"DistanceMsg", "Vous devez être à moins de {0} mètres de votre mini-hélico."
}
, {
"BlockedMsg", "Vous ne pouvez pas faire apparaître ou aller chercher votre hélico lorsque la construction est bloquée."
}
, {
"NotFlying", "L'hélicoptère ne vole pas"
}
, {
"NoPermission", "Vous n'êtes pas autorisé à survoler"
}
, {
"HoverEnabled", "Vol stationnaire mini hélicoptère: activé"
}
, {
"HoverDisabled", "Vol stationnaire mini hélicoptère: désactivé"
}
, {
"NotInHelicopter", "Vous n'êtes pas dans un mini hélicoptère"
}
, {
"NoPassengerToggle", "Les passagers ne peuvent pas basculer en vol stationnaire"
}
}
, this, "fr");
}
private object CanLootEntity(BasePlayer player, StorageContainer container) {
if (player?.userID == 0) return null;
Minicopter mini = container.GetParentEntity() as Minicopter;
if (mini != null) {
if (storedData.playerminiID.ContainsKey(player.userID) && mini?.net.ID.Value == storedData.playerminiID[player.userID].Value) {
GetVIPSettings(player, out VIPSettings vipsettings);
bool vip = vipsettings != null;
bool unlimited = permission.UserHasPermission(player.UserIDString, MinicopterUnlimited) || (vip && vipsettings.unlimited);
if (!unlimited) return null;
if (!(unlimited && configData.Global.allowFuelIfUnlimited)) {
Message(player.IPlayer, "NoPermMsg");
return true;
}
}
return null;
}
return null;
}
private void OnPlayerInput(BasePlayer player, InputState input) {
if (!configData.Global.UseKeystrokeForHover) return;
//if (!input.IsValidEntityReference()) return; // This was added at some point to fix some NRE, but now seems to prevent operation.if (player?.userID.IsSteamId() != true || input == null) return;
if (!permission.UserHasPermission(player?.UserIDString, MinicopterCanHover)) return;
//if (input.current.buttons > 0) Puts($"OnPlayerInput: {input.current.buttons}");
if (!player.isMounted) {
return;
}
int hoverkey = configData.Global.HoverKey > 0 ? configData.Global.HoverKey : (int)BUTTON.FIRE_THIRD;
// MMB
bool dohover = input.current.buttons == hoverkey;
bool stabilize = input.current.buttons == (int)BUTTON.BACKWARD;
if (!(dohover || stabilize)) return;
BaseHelicopter mini = player.GetMountedVehicle() as BaseHelicopter;
if (mini == null) return;
DoLog($"Stabilize: {stabilize}, hover toggle: {dohover}");
DoLog($"HoverDelay: {hoverDelayTimers.ContainsKey(player.userID)}");
// Process hoverDelayTimers for user regardless of hover or stabilize.// If trying to hover and timer not expired, return.// If timer expired, remove timer and continue.if (hoverDelayTimers.ContainsKey(player.userID))
{
if (DateTime.Now - hoverDelayTimers[player.userID] < TimeSpan.FromMilliseconds(1000) && dohover) {
DoLog("Hover delay not elapsed, returning");
return;
}
hoverDelayTimers.Remove(player.userID);
}
// Now, if trying to hover, setup a new delay timer for the next keystroke.if (dohover)
{
DoLog("Resetting hover delay timer");
hoverDelayTimers.Remove(player.userID);
hoverDelayTimers.Add(player.userID, DateTime.Now);
}
// Process hover or stabilize
if (storedData.playerminiID.ContainsKey(player.userID) && mini?.net.ID.Value == storedData.playerminiID[player.userID].Value) {
if (dohover && player != mini?.GetDriver() && !configData.Global.PassengerCanToggleHover) {
Message(player.IPlayer, "NoPassengerToggle");
return;
}
if (mini.IsEngineOn() && mini.GetDriver()) {
int iid = mini.GetInstanceID();
DoLog($"Hovers contains {iid}: {hovers.ContainsKey(iid)}");
if (stabilize && hovers.ContainsKey(iid) && hovers[iid].isHovering) {
DoLog($"Stabilizing {mini.net.ID}");
hovers[iid]?.Stabilize();
}
else if (dohover && hovers.ContainsKey(iid)) {
DoLog($"Toggling hover for {mini.net.ID}");
NextTick(() => hovers[iid]?.ToggleHover());
}
}
}
}
private object OnEngineStart(Minicopter mini) {
if (storedData.playerminiID.ContainsValue(mini.net.ID)) {
BasePlayer player = BasePlayer.Find(mini.OwnerID.ToString());
if (player != null) {
VIPSettings vipsettings;
GetVIPSettings(player, out vipsettings);
bool fast = vipsettings != null ? vipsettings.FastStart : configData.Global.FastStart;
if (fast) {
mini?.engineController?.FinishStartingEngine();
}
}
}
return null;
}
private string Lang(string key, string id = default(string), params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
private void Message(IPlayer player, string key, params object[] args) => player.Reply(Lang(key, player.Id, args));
// Chat message to online player with ulong
private void ChatPlayerOnline(ulong userid, string message) {
BasePlayer player = BasePlayer.FindByID(userid);
if (player != null) {
SendReply(player, Lang("KilledMsg"));
}
}
#endregion
#region Commands
// Chat spawn
[Command("mymini")]
private void SpawnMyMinicopterCommand(IPlayer player, string command, string[] args) {
double secondsSinceEpoch = DateTime.UtcNow.Subtract(epoch).TotalSeconds;
BasePlayer bplayer = player.Object as BasePlayer;
if (!player.HasPermission(MinicopterSpawn)) {
Message(player, "NoPermMsg");
return;
}
if (IsRaidBlocked(bplayer)) {
Message(player, "RaidBlockMsg");
return;
}
if (storedData.playerminiID.ContainsKey(bplayer.userID)) {
if (!configData.Global.allowRespawnWhenActive) {
Message(player, "AlreadyMsg");
return;
}
KillMyMinicopterPlease(bplayer, true);
}
if (bplayer.IsBuildingBlocked() && !configData.Global.allowWhenBlocked) {
Message(player, "BlockedMsg");
return;
}
bool hascooldown = player.HasPermission(MinicopterCooldown);
if (!configData.Global.useCooldown) hascooldown = false;
int secsleft;
if (hascooldown) {
VIPSettings vipsettings;
GetVIPSettings(player.Object as BasePlayer, out vipsettings);
float cooldownMin = vipsettings != null ? vipsettings.cooldownmin : configData.Global.cooldownmin;
if (!storedData.playercounter.ContainsKey(bplayer.userID)) {
storedData.playercounter.Add(bplayer.userID, secondsSinceEpoch);
SaveData();
}
else {
double count;
storedData.playercounter.TryGetValue(bplayer.userID, out count);
if ((secondsSinceEpoch - count) > (cooldownMin * 60)) {
DoLog("Player reached cooldown.Clearing data.");
storedData.playercounter.Remove(bplayer.userID);
SaveData();
}
else {
secsleft = Math.Abs((int)((cooldownMin * 60) - (secondsSinceEpoch - count)));
if (secsleft > 0) {
DoLog($"Player DID NOT reach cooldown.Still {secsleft} secs left.");
Message(player, "CooldownMsg", secsleft.ToString());
return;
}
}
}
}
else {
if (storedData.playercounter.ContainsKey(bplayer.userID)) {
storedData.playercounter.Remove(bplayer.userID);
SaveData();
}
}
SpawnMyMinicopter(bplayer);
}
// Fetch copter
[Command("gmini")]
private void GetMyMiniMyCopterCommand(IPlayer player, string command, string[] args) {
BasePlayer bplayer = player.Object as BasePlayer;
if (bplayer.IsBuildingBlocked() && !configData.Global.allowWhenBlocked) {
Message(player, "BlockedMsg");
return;
}
bool canspawn = player.HasPermission(MinicopterSpawn);
bool canfetch = player.HasPermission(MinicopterFetch);
if (!(canspawn && canfetch)) {
Message(player, "NoPermMsg");
return;
}
if (IsRaidBlocked(bplayer)) {
Message(player, "RaidBlockMsg");
return;
}
VIPSettings vipsettings;
GetVIPSettings(player.Object as BasePlayer, out vipsettings);
bool vip = vipsettings != null;
if (storedData.playerminiID.ContainsKey(bplayer.userID)) {
NetworkableId findme;
storedData.playerminiID.TryGetValue(bplayer.userID, out findme);
BaseNetworkable foundent = BaseNetworkable.serverEntities.Find(findme);
if (foundent != null) {
// Distance check - need a Y check as well...maybe.float gminiDistance = vip ? vipsettings.gminidistance : configData.Global.gminidistance;
if (gminiDistance > 0f && Vector3.Distance(bplayer.transform.position, foundent.transform.position) > gminiDistance) {
Message(player, "DistanceMsg", gminiDistance);
return;
}
Minicopter copter = foundent as Minicopter;
float terrainHeight = TerrainMeta.HeightMap.GetHeight(foundent.transform.position);
if (copter.engineController.IsOn) {
if (!configData.Global.StopEngineOnGMini) {
Message(player, "RunningMsg2");
return;
}
copter.engineController.StopEngine();
}
// Check for and dismount all players before moving the copter
foreach (BaseVehicle.MountPointInfo mountPointInfo in copter.mountPoints) {
if (mountPointInfo.mountable != null) {
BasePlayer mounted = mountPointInfo.mountable.GetMounted();
if (mounted) {
if (mounted.transform.position.y - terrainHeight > 10f) {
Message(player, "FlyingMsg");
return;
}
Vector3 player_pos = mounted.transform.position + new Vector3(1, 0, 1);
mounted.DismountObject();
mounted.MovePosition(player_pos);
mounted.SendNetworkUpdateImmediate();
mounted.ClientRPC(RpcTarget.Player("ForcePositionTo", bplayer), player_pos);
mountPointInfo.mountable._mounted = null;
}
}
}
Vector3 newLoc = new(bplayer.transform.position.x + 2f, bplayer.transform.position.y + 2f, bplayer.transform.position.z + 2f);
foundent.transform.position = newLoc;
Message(player, "FoundMsg", newLoc);
}
}
else {
Message(player, "NoFoundMsg");
}
}
// Find copter
[Command("wmini")]
private void WhereisMyMiniMyCopterCommand(IPlayer player, string command, string[] args) {
if (!player.HasPermission(MinicopterWhere)) {
Message(player, "NoPermMsg");
return;
}
BasePlayer bplayer = player.Object as BasePlayer;
if (storedData.playerminiID.ContainsKey(bplayer.userID)) {
NetworkableId findme;
storedData.playerminiID.TryGetValue(bplayer.userID, out findme);
BaseNetworkable foundit = BaseNetworkable.serverEntities.Find(findme);
if (foundit != null) {
string loc = foundit.transform.position.ToString();
Message(player, "FoundMsg", loc);
}
}
else {
Message(player, "NoFoundMsg");
}
}
[Command("hmini")]
private void HoverMyMinicopterCommand(IPlayer player, string command, string[] args) {
if (!player.HasPermission(MinicopterCanHover)) {
Message(player, "NoPermMsg");
return;
}
ulong playerId = ulong.Parse(player.Id);
if (hoverDelayTimers.ContainsKey(playerId)) {
if (DateTime.Now - hoverDelayTimers[playerId] < TimeSpan.FromMilliseconds(1000)) {
return;
}
hoverDelayTimers.Remove(playerId);
}
hoverDelayTimers.Add(playerId, DateTime.Now);
BaseHelicopter mini = (player.Object as BasePlayer)?.GetMountedVehicle() as BaseHelicopter;
if (mini == null) return;
if (storedData.playerminiID.ContainsKey(playerId) && mini.net.ID.Value == storedData.playerminiID[playerId].Value) {
if ((player.Object as BasePlayer) != mini.GetDriver() && !configData.Global.PassengerCanToggleHover) {
Message(player, "NoPassengerToggle");
return;
}
if (mini.IsEngineOn() && mini.GetDriver()) {
DoLog($"Finding hover object for {mini.net.ID}");
hovers[mini.GetInstanceID()]?.ToggleHover();
}
}
}
// Chat despawn
[Command("remini")]
private void ReSpawnMyMinicopterCommand(IPlayer player, string command, string[] args) {
if (IsRaidBlocked(player.Object as BasePlayer)) {
Message(player, "RaidBlockMsg");
return;
}
KillMyMinicopterCommand(player, "nomini", new string[0]);
SpawnMyMinicopterCommand(player, "mymini", new string[0]);
}
[Command("nomini")]
private void KillMyMinicopterCommand(IPlayer player, string command, string[] args) {
if (!player.HasPermission(MinicopterSpawn)) {
Message(player, "NoPermMsg");
return;
}
if (IsRaidBlocked(player.Object as BasePlayer)) {
Message(player, "RaidBlockMsg");
return;
}
KillMyMinicopterPlease(player.Object as BasePlayer);
}
#endregion
#region consolecommands
// Console spawn
[ConsoleCommand("spawnminicopter")]
private void SpawnMyMinicopterConsoleCommand(ConsoleSystem.Arg arg) {
if (arg.IsRcon) {
if (arg.Args == null) {
DoLog("You need to supply a valid SteamId.");
return;
}
}
else if (!HasPermission(arg, MinicopterAdmin)) {
SendReply(arg.Connection.player as BasePlayer, Lang("NoPermMsg"));
return;
}
else if (arg.Args == null) {
SendReply(arg.Connection.player as BasePlayer, Lang("SpawnUsage"));
return;
}
if (arg.Args.Length == 1) {
ulong steamid = Convert.ToUInt64(arg.Args[0]);
if (steamid == 0) return;
if (!steamid.IsSteamId()) return;
BasePlayer player = BasePlayer.FindByID(steamid);
if (player != null) {
SpawnMyMinicopter(player);
}
}
}
// Console despawn
[ConsoleCommand("killminicopter")]
private void KillMyMinicopterConsoleCommand(ConsoleSystem.Arg arg) {
if (arg.IsRcon) {
if (arg.Args == null) {
DoLog("You need to supply a valid SteamId.");
return;
}
}
else if (!HasPermission(arg, MinicopterAdmin)) {
SendReply(arg.Connection.player as BasePlayer, Lang("NoPermMsg"));
return;
}
else if (arg.Args == null) {
SendReply(arg.Connection.player as BasePlayer, Lang("SpawnUsage"));
return;
}
if (arg.Args.Length == 1) {
ulong steamid = Convert.ToUInt64(arg.Args[0]);
if (steamid == 0) return;
if (!steamid.IsSteamId()) return;
BasePlayer player = BasePlayer.FindByID(steamid);
if (player != null) {
KillMyMinicopterPlease(player);
}
}
}
#endregion
#region ourhooks
// Spawn hook
private void SpawnMyMinicopter(BasePlayer player) {
if (player.IsBuildingBlocked() && !configData.Global.allowWhenBlocked) {
SendReply(player, Lang("BlockedMsg"));
return;
}
VIPSettings vipsettings;
GetVIPSettings(player, out vipsettings);
bool vip = vipsettings != null;
Quaternion rotation = player.GetNetworkRotation();
Vector3 forward = rotation * Vector3.forward;
// Make straight perpendicular to up axis so we don't spawn into ground or above player's head.Vector3 straight = Vector3.Cross(Vector3.Cross(Vector3.up, forward), Vector3.up).normalized;
Vector3 position = player.transform.position + (straight * 5f);
position.y = player.transform.position.y + 2.5f;
if (position == default) return;
BaseVehicle vehicleMini = (BaseVehicle)GameManager.server.CreateEntity(prefab, position, new Quaternion());
if (vehicleMini == null) return;
vehicleMini.OwnerID = player.userID;
vehicleMini.Spawn();
Minicopter miniCopter = vehicleMini as Minicopter;
if (permission.UserHasPermission(player.UserIDString, MinicopterCanHover)) {
hovers.Add(miniCopter.GetInstanceID(), miniCopter.gameObject.AddComponent<Hovering>());
}
if (permission.UserHasPermission(player.UserIDString, MinicopterUnlimited) || (vip && vipsettings.unlimited)) {
DoLog("Setting fuel requirements to zero");
miniCopter.fuelPerSec = 0f;
if (!configData.Global.allowFuelIfUnlimited && !(vip && !vipsettings.canloot)) {
// If the player is not allowed to use the fuel container, add 1 fuel so the copter will start.// Also lock fuel container since there is no point in adding/removing fuel
IFuelSystem fuelCan = miniCopter.GetFuelSystem();
DoLog("Adding 1 fuel");
fuelCan?.AddFuel(1);
}
}
else if (configData.Global.startingFuel > 0 || (vip && vipsettings.startingFuel > 0)) {
IFuelSystem fuelCan = miniCopter?.GetFuelSystem();
if (fuelCan != null) {
float sf = vip ? vipsettings.startingFuel : configData.Global.startingFuel;
fuelCan.AddFuel((int)sf);
}
}
else {
miniCopter.fuelPerSec = vip ? vipsettings.stdFuelConsumption : configData.Global.stdFuelConsumption;
}
SendReply(player, Lang("SpawnedMsg"));
NetworkableId minicopteruint = vehicleMini.net.ID;
DoLog($"SPAWNED MINICOPTER {minicopteruint} for player {player?.displayName} OWNER {vehicleMini?.OwnerID}");
storedData.playerminiID.Remove(player.userID);
ulong myKey = currentMounts.FirstOrDefault(x => x.Value == player.userID).Key;
currentMounts.Remove(myKey);
storedData.playerminiID.Add(player.userID, minicopteruint);
SaveData();
miniCopter = null;
}
// Kill minicopter hook
private void KillMyMinicopterPlease(BasePlayer player, bool killalways = false) {
bool foundcopter = false;
VIPSettings vipsettings;
GetVIPSettings(player, out vipsettings);
float minDistance = vipsettings != null ? vipsettings.mindistance : configData.Global.mindistance;
if (minDistance == 0f || killalways) {
foundcopter = true;
}
else {
List<BaseEntity> copterlist = new();
Vis.Entities(player.transform.position, minDistance, copterlist);
foreach (BaseEntity p in copterlist) {
Minicopter foundent = p.GetComponentInParent<Minicopter>();
if (foundent != null) {
foundcopter = true;
}
}
}
if (storedData.playerminiID.ContainsKey(player.userID) && foundcopter) {
NetworkableId findPlayerId;
storedData.playerminiID.TryGetValue(player.userID, out findPlayerId);
BaseNetworkable tokill = BaseNetworkable.serverEntities.Find(findPlayerId);
tokill?.Kill(BaseNetworkable.DestroyMode.Gib);
storedData.playerminiID.Remove(player.userID);
ulong myKey = currentMounts.FirstOrDefault(x => x.Value == player.userID).Key;
currentMounts.Remove(myKey);
if (storedData.playercounter.ContainsKey(player.userID) && !configData.Global.useCooldown) {
storedData.playercounter.Remove(player.userID);
}
SaveData();
LoadData();
}
else if (!foundcopter) {
DoLog("Player too far from copter to destroy.");
SendReply(player, Lang("DistanceMsg", null, minDistance));
}
}
private bool IsRaidBlocked(BasePlayer player) {
if (configData.Global.useNoEscape && NoEscape) {
return (bool)NoEscape?.CallHook("IsRaidBlocked", player);
}
return false;
}
#endregion
#region hooks
private object CanMountEntity(BasePlayer player, BaseMountable mountable) {
if (player?.userID.IsSteamId() != true) return null;
if (mountable == null) return null;
Minicopter mini = mountable?.GetComponentInParent<Minicopter>();
if (mini == null) return null;
DoLog($"CanMountEntity: Player {player?.userID} wants to mount seat id {mountable?.net.ID}");
NetworkableId currentseat = new(mini.net.ID.Value);
currentseat.Value += 3;
// Start with driver seat
for (int i = 0; i < 2; i++) {
// Find copter and seats in storedData
DoLog($" Is this our copter with ID {mini.net.ID.Value}?");
if (storedData.playerminiID.ContainsValue(mini.net.ID)) {
DoLog(" yes, it is...");
if (player?.userID.IsSteamId() != true) return true;
// Block mounting by NPCs
BaseVehicle minimount = BaseNetworkable.serverEntities.Find(mini.net.ID) as BaseVehicle;
DoLog($"Does {player.userID} match {minimount?.OwnerID}, or are they a friend?");
if (!IsFriend(player.userID, minimount.OwnerID)) {
DoLog("Player does not own minicopter, and is not a friend of the owner.");
Message(player.IPlayer, "NoAccess");
return false;
}
if (currentMounts.ContainsValue(player.userID)) {
if (!player.GetMounted()) {
ulong myKey = currentMounts.FirstOrDefault(x => x.Value == player.userID).Key;
currentMounts.Remove(myKey);
}
return false;
}
break;
}
currentseat.Value++;
}
return null;
}
private void OnEntityMounted(BaseMountable mountable, BasePlayer player) {
Minicopter mini = mountable.GetComponentInParent<Minicopter>();
if (mini != null) {
DoLog($"OnEntityMounted: Player {player.userID} mounted seat id {mountable.net.ID}");
// Check this seat's ID to see if the copter is one of ours
NetworkableId currentseat = new(mini.net.ID.Value);
currentseat.Value += 3;
// Start with driver seat
for (int i = 0; i < 2; i++) {
// Find copter in storedData
DoLog($"Is this our copter with ID {mini.net.ID.Value}?");
if (storedData.playerminiID.ContainsValue(mini.net.ID)) {
DoLog(" yes, it is...");
DoLog($"Removing {player.displayName}'s ID {player.userID} from currentMounts for seat {mountable.net.ID} on {currentseat.Value}");
currentMounts.Remove(mountable.net.ID.Value);
DoLog($"Adding {player.displayName}'s ID {player.userID} to currentMounts for seat {mountable.net.ID} on {currentseat.Value}");
currentMounts.Add(mountable.net.ID.Value, player.userID);
break;
}
currentseat.Value++;
}
}
}
private object CanDismountEntity(BasePlayer player, BaseMountable mountable) {
if (player?.userID.IsSteamId() != true) return null;
if (mountable == null) return null;
Minicopter mini = mountable?.GetComponentInParent<Minicopter>();
if (mini == null) return null;
DoLog($"CanDismountEntity: Player {player?.userID} wants to dismount seat id {mountable?.net.ID}");
// Only operates if mini is not null and if we are flying above minimum height
if (mini != null && !Physics.Raycast(new Ray(mountable.transform.position, Vector3.down), configData.Global.minDismountHeight, layerMask)) {
DoLog($"Is this our copter with ID {mini.net.ID.Value}?");
NetworkableId passenger = new(mini.net.ID.Value);
passenger.Value += 4;
NetworkableId driver = new(mini.net.ID.Value);
driver.Value += 3;
if (storedData.playerminiID.ContainsValue(mini.net.ID)) {
DoLog(" yes, it is...");
if (!configData.Global.allowDriverDismountWhileFlying) {
DoLog("DENY PILOT DISMOUNT");
return false;
}
ulong myKey = currentMounts.FirstOrDefault(x => x.Value == player.userID).Key;
currentMounts.Remove(myKey);
}
else if (storedData.playerminiID.ContainsValue(passenger)) {
if (!configData.Global.allowPassengerDismountWhileFlying) {
DoLog("DENY PASSENGER DISMOUNT");
return false;
}
ulong myKey = currentMounts.FirstOrDefault(x => x.Value == player.userID).Key;
currentMounts.Remove(myKey);
}
}
return null;
}
private void OnEntityDismounted(BaseMountable mountable, BasePlayer player) {
Minicopter mini = mountable.GetComponentInParent<Minicopter>();
if (mini != null) {
DoLog($"OnEntityDismounted: Player {player.userID} dismounted seat id {mountable.net.ID}");
NetworkableId currentseat = new(mini.net.ID.Value);
currentseat.Value += 3;
// Start with driver seat
for (int i = 0; i < 2; i++) {
// Find copter and seats in storedData
DoLog($"Is this our copter with ID {mini.net.ID.Value}?");
if (storedData.playerminiID.ContainsValue(mini.net.ID)) {
DoLog(" yes, it is...");
DoLog($"Removing {player.displayName}'s ID {player.userID} from currentMounts for seat {mountable.net.ID} on {currentseat.Value}");
currentMounts.Remove(mountable.net.ID.Value);
break;
}
currentseat.Value++;
}
}
ulong myKey = currentMounts.FirstOrDefault(x => x.Value == player.userID).Key;
currentMounts.Remove(myKey);
}
// On kill - tell owner
private void OnEntityKill(Minicopter entity) {
if (entity == null) return;
if (entity.net.ID.Value == 0) return;
if (storedData == null) return;
if (storedData.playerminiID == null) return;
ulong todelete = new();
if (!storedData.playerminiID.ContainsValue(entity.net.ID)) {
DoLog("KILLED non-plugin minicopter");
return;
}
foreach (KeyValuePair<ulong, NetworkableId> item in storedData.playerminiID) {
if (item.Value == entity.net.ID) {
ChatPlayerOnline(item.Key, "killed");
BasePlayer player = BasePlayer.FindByID(item.Key);
todelete = item.Key;
}
}
if (todelete != 0) {
storedData.playerminiID.Remove(todelete);
currentMounts.Remove(entity.net.ID.Value);
currentMounts.Remove(entity.net.ID.Value + 1);
currentMounts.Remove(entity.net.ID.Value + 2);
hovers.Remove(entity.GetInstanceID());
SaveData();
}
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) {
if (entity?.net?.ID == null) return null;
if (hitInfo?.damageTypes == null) return null;
if (storedData?.playerminiID?.ContainsValue(entity.net.ID) == true) {
if (hitInfo?.damageTypes?.GetMajorityDamageType().ToString() == "Decay") {
if (configData.Global.copterDecay) {
DoLog($"Enabling standard decay for spawned minicopter {entity.net.ID}.");
}
else {
DoLog($"Disabling decay for spawned minicopter {entity.net.ID}.");
hitInfo.damageTypes.Scale(Rust.DamageType.Decay, 0);
}
return null;
}
else {
if (!configData.Global.allowDamage) return true;
foreach (KeyValuePair<string, VIPSettings> vip in configData.VIPSettings) {
string perm = vip.Key.StartsWith($"{Name.ToLower()}.") ? vip.Key : $"{Name.ToLower()}.{vip.Key}";
if (permission.UserHasPermission(entity.OwnerID.ToString(), perm) && vip.Value is VIPSettings && !vip.Value.allowDamage) {
return true;
}
}
}
}
return null;
}
private void OnPlayerDisconnected(BasePlayer player, string reason) {
if (!configData.Global.killOnSleep) return;
if (player?.userID.IsSteamId() != true) return;
if (storedData.playerminiID.ContainsKey(player.userID)) {
NetworkableId findMiniId;
storedData.playerminiID.TryGetValue(player.userID, out findMiniId);
BaseNetworkable tokill = BaseNetworkable.serverEntities.Find(findMiniId);
if (tokill == null) return;
// Didn't find it
// Check for mounted players
BaseVehicle copter = tokill as BaseVehicle;
for (int i = 0; i < copter?.mountPoints.Count; i++) {
BaseVehicle.MountPointInfo mountPointInfo = copter.mountPoints[i];
if (mountPointInfo.mountable != null) {
BasePlayer mounted = mountPointInfo.mountable.GetMounted();
if (mounted) {
DoLog("Copter owner sleeping but another one is mounted - cannot destroy copter");
return;
}
}
}
DoLog("Copter owner sleeping - destroying copter");
tokill.Kill();
storedData.playerminiID.Remove(player.userID);
ulong myKey = curren
// ... [Code preview truncated due to length. Full source in attached file] ...