C#:
#region License (GPL v2)
/*
DESCRIPTION
Copyright (c) 2023 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 UnityEngine;
using System.Collections.Generic;
using Oxide.Core;
using System;
using System.Linq;
using Oxide.Core.Plugins;
using System.Text;
using Oxide.Core.Libraries.Covalence;
using System.Collections;
namespace Oxide.Plugins
{
[Info("My Mini Copter", "RFC1920", "0.5.4")]
// 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 Dictionary<ulong, ulong>();
private Dictionary<int, Hovering> hovers = new Dictionary<int, Hovering>();
private Dictionary<ulong, DateTime> hoverDelayTimers = new Dictionary<ulong, DateTime>();
private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0);
private class StoredData
{
public Dictionary<ulong, uint> playerminiID = new Dictionary<ulong, uint>();
public Dictionary<ulong, double> playercounter = new Dictionary<ulong, double>();
}
private StoredData storedData;
private bool HasPermission(ConsoleSystem.Arg arg, string permname)
{
return !(arg.Connection.player is 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, uint> playerMini in storedData.playerminiID)
{
MiniCopter miniCopter = BaseNetworkable.serverEntities.Find(new NetworkableId(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>());
}
StorageContainer fuelCan = miniCopter?.GetFuelSystem().fuelStorageInstance.Get(true);
if (permission.UserHasPermission(playerMini.Key.ToString(), MinicopterUnlimited) || (vip && vipsettings.unlimited))
{
miniCopter.fuelPerSec = 0f;
if (fuelCan?.IsValid() == true)
{
if (fuelCan?.inventory.IsEmpty() != true)
{
DoLog($"Setting fuel for MiniCopter {playerMini.Value} owned by {playerMini.Key}.");
ItemManager.CreateByItemID(-946369541, 1)?.MoveToContainer(fuelCan?.inventory);
fuelCan?.inventory.MarkDirty();
}
// Default for unlimited fuel
fuelCan?.SetFlag(BaseEntity.Flags.Locked, true);
if (configData.Global.allowFuelIfUnlimited || (vip && vipsettings.canloot))
{
fuelCan?.SetFlag(BaseEntity.Flags.Locked, false);
}
}
continue;
}
else
{
// Done here in case player's unlimited permission was revoked.
fuelCan?.SetFlag(BaseEntity.Flags.Locked, false);
}
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 void OnPlayerInput(BasePlayer player, InputState input)
{
if (player == null || input == null) return;
if (!player.userID.IsSteamId()) return;
if (!configData.Global.UseKeystrokeForHover) 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;
BaseHelicopterVehicle mini = player.GetMountedVehicle() as BaseHelicopterVehicle;
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 stablize
if (storedData.playerminiID.ContainsKey(player.userID) && mini.net.ID.Value == storedData.playerminiID[player.userID])
{
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}");
hovers[iid]?.ToggleHover();
}
}
}
}
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))
{
uint findme;
storedData.playerminiID.TryGetValue(bplayer.userID, out findme);
BaseNetworkable foundent = BaseNetworkable.serverEntities.Find(new NetworkableId(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(false);
mounted.ClientRPCPlayer(null, bplayer, "ForcePositionTo", player_pos);
mountPointInfo.mountable._mounted = null;
}
}
}
Vector3 newLoc = new Vector3(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))
{
uint findme;
storedData.playerminiID.TryGetValue(bplayer.userID, out findme);
BaseNetworkable foundit = BaseNetworkable.serverEntities.Find(new NetworkableId(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);
BaseHelicopterVehicle mini = (player.Object as BasePlayer)?.GetMountedVehicle() as BaseHelicopterVehicle;
if (mini == null) return;
if (storedData.playerminiID.ContainsKey(playerId) && mini.net.ID.Value == storedData.playerminiID[playerId])
{
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(Vector3)) return;
BaseVehicle vehicleMini = (BaseVehicle)GameManager.server.CreateEntity(prefab, position, new Quaternion());
if (vehicleMini == null) return;
vehicleMini.OwnerID = player.userID;
MiniCopter miniCopter = vehicleMini as MiniCopter;
vehicleMini.Spawn();
if (permission.UserHasPermission(player.UserIDString, MinicopterCanHover))
{
hovers.Add(miniCopter.GetInstanceID(), miniCopter.gameObject.AddComponent<Hovering>());
}
if (permission.UserHasPermission(player.UserIDString, MinicopterUnlimited) || (vip && vipsettings.unlimited))
{
// Set fuel requirements to 0
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
StorageContainer fuelCan = miniCopter?.GetFuelSystem().fuelStorageInstance.Get(true);
if (fuelCan?.IsValid() == true)
{
ItemManager.CreateByItemID(-946369541, 1)?.MoveToContainer(fuelCan.inventory);
fuelCan.inventory.MarkDirty();
fuelCan.SetFlag(BaseEntity.Flags.Locked, true);
}
}
}
else if (configData.Global.startingFuel > 0 || (vip && vipsettings.startingFuel > 0))
{
StorageContainer fuelCan = miniCopter?.GetFuelSystem().fuelStorageInstance.Get(true);
if (fuelCan?.IsValid() == true)
{
float sf = vip ? vipsettings.startingFuel : configData.Global.startingFuel;
ItemManager.CreateByItemID(-946369541, Convert.ToInt32(sf))?.MoveToContainer(fuelCan.inventory);
fuelCan.inventory.MarkDirty();
}
}
else
{
miniCopter.fuelPerSec = vip ? vipsettings.stdFuelConsumption : configData.Global.stdFuelConsumption;
}
SendReply(player, Lang("SpawnedMsg"));
uint minicopteruint = (uint)vehicleMini.net.ID.Value;
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 List<BaseEntity>();
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)
{
uint findPlayerId;
storedData.playerminiID.TryGetValue(player.userID, out findPlayerId);
BaseNetworkable tokill = BaseNetworkable.serverEntities.Find(new NetworkableId(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 == null) return null;
if (mountable == null) return null;
MiniCopter mini = mountable?.GetComponentInParent<MiniCopter>();
if (mini == null) return null;
DoLog($"Player {player?.userID} wants to mount seat id {mountable?.net.ID}");
uint id = (uint)mountable.net.ID.Value - 2; // max seat == copter.net.ID + 2, e.g. passenger seat id - 2 == copter id
for (int i = 0; i < 3; i++)
{
// Find copter and seats in storedData
DoLog($" Is this our copter with ID {id}?");
if (storedData.playerminiID.ContainsValue(id))
{
DoLog(" yes, it is...");
if (!IsFriend(player.userID, mountable.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;
}
}
id++;
}
return null;
}
private void OnEntityMounted(BaseMountable mountable, BasePlayer player)
{
MiniCopter mini = mountable.GetComponentInParent<MiniCopter>();
if (mini != null)
{
DoLog($"Player {player.userID} mounted seat id {mountable.net.ID}");
// Check this seat's ID to see if the copter is one of ours
uint id = (uint)mountable.net.ID.Value - 2; // max seat == copter.net.ID + 2, e.g. passenger seat id - 2 == copter id
for (int i = 0; i < 3; i++)
{
// Find copter in storedData
DoLog($"Is this our copter with ID {id}?");
if (storedData.playerminiID.ContainsValue(id))
{
DoLog($"Removing {player.displayName}'s ID {player.userID} from currentMounts for seat {mountable.net.ID} on {id}");
currentMounts.Remove(mountable.net.ID.Value);
DoLog($"Adding {player.displayName}'s ID {player.userID} to currentMounts for seat {mountable.net.ID} on {id}");
currentMounts.Add(mountable.net.ID.Value, player.userID);
break;
}
id++;
}
}
}
private object CanDismountEntity(BasePlayer player, BaseMountable mountable)
{
if (player == null) return null;
MiniCopter mini = mountable.GetComponentInParent<MiniCopter>();
if (mini != null)
{
if (!Physics.Raycast(new Ray(mountable.transform.position, Vector3.down), configData.Global.minDismountHeight, layerMask))
{
// Is this one of ours?
if (storedData.playerminiID.ContainsValue((uint)mountable.net.ID.Value - 1))
{
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((uint)mountable.net.ID.Value - 2))
{
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($"Player {player.userID} dismounted seat id {mountable.net.ID}");
uint id = (uint)mountable.net.ID.Value - 2;
for (int i = 0; i < 3; i++)
{
// Find copter and seats in storedData
DoLog($"Is this our copter with ID {id}?");
if (storedData.playerminiID.ContainsValue(id))
{
DoLog($"Removing {player.displayName}'s ID {player.userID} from currentMounts for seat {mountable.net.ID} on {id}");
currentMounts.Remove(mountable.net.ID.Value);
break;
}
id++;
}
}
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 ulong();
if (!storedData.playerminiID.ContainsValue((uint)entity.net.ID.Value))
{
DoLog("KILLED non-plugin minicopter");
return;
}
foreach (KeyValuePair<ulong, uint> item in storedData.playerminiID)
{
if (item.Value == entity.net.ID.Value)
{
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((uint)entity.net.ID.Value) == 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 == null) return;
if (storedData.playerminiID.ContainsKey(player.userID))
{
uint findPlayerId;
storedData.playerminiID.TryGetValue(player.userID, out findPlayerId);
BaseNetworkable tokill = BaseNetworkable.serverEntities.Find(new NetworkableId(findPlayerId));
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 = 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();
}
}
#endregion
[HookMethod("SendHelpText")]
private void SendHelpText(BasePlayer player)
{
StringBuilder sb = new StringBuilder();
sb.Append("<color=#05eb59>").Append(Name).Append(' ').Append(Version).Append("</color> · Spawn a Mini Helicopter\n");
sb.Append(" · ").Append("/mymini: ").AppendLine(Lang("MyMiniHelp", null, configData.Global.mindistance));
sb.Append(" · ").Append("/nomini: ").AppendLine(Lang("NoMiniHelp", null, configData.Global.mindistance));
sb.Append(" · ").Append("/wmini: ").AppendLine(Lang("WMiniHelp"));
if (permission.UserHasPermission(player.UserIDString, MinicopterFetch))
{
sb.Append(" · ").Append("/gmini: ").AppendLine(Lang("GetMiniHelp"));
}
player.ChatMessage(sb.ToString());
}
private bool IsFriend(ulong playerid, ulong ownerid)
{
if (!configData.Global.useFriends && !configData.Global.useClans && !configData.Global.useTeams) return true;
if (playerid == ownerid) return true;
if (configData.Global.useFriends && Friends != null)
{
object fr = Friends?.CallHook("AreFriends", playerid, ownerid);
if (fr != null && (bool)fr)
{
return true;
}
}
if (configData.Global.useClans && Clans != null)
{
string playerclan = (string)Clans?.CallHook("GetClanOf", playerid);
string ownerclan = (string)Clans?.CallHook("GetClanOf", ownerid);
if (playerclan == ownerclan && playerclan != null && ownerclan != null)
{
return true;
}
}
if (configData.Global.useTeams)
// ... [Code preview truncated due to length. Full source in attached file] ...