C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
#region License (GPL v2)
/*
DESCRIPTION
Copyright (c) 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
as published by the Free Software Foundation; version 2
of the License only.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 (GPL v2)
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("FlyingCarpet", "RFC1920", "1.4.2")]
[Description("Fly a custom object consisting of carpet, chair, lantern, lock, and small sign.")]
internal class FlyingCarpet : RustPlugin
{
#region vars
private ConfigData configData;
private static LayerMask layerMask;
private static LayerMask buildingMask;
private static Dictionary<ulong, PlayerCarpetData> loadplayer = new();
private static List<ulong> pilotslist = new();
public List<string> monNames = new();
public SortedDictionary<string, Vector3> monPos = new();
public SortedDictionary<string, Vector3> monSize = new();
private const string FCGUI = "fcgui.top";
private const string FCGUM = "fcgui.menu";
public static FlyingCarpet Instance;
[PluginReference]
private readonly Plugin SignArtist, GridAPI, Friends, Clans, Teleportication;
public class PlayerCarpetData
{
public BasePlayer player;
public int carpetcount;
}
private void OnServerInitialized()
{
FindMonuments();
}
private void Init()
{
Instance = this;
LoadConfigVariables();
layerMask = (1 << 29);
layerMask |= (1 << 18);
layerMask = ~layerMask;
//layerMask = LayerMask.GetMask("Construction", "Tree", "Rock", "Deployed", "World", "Terrain");
//buildingMask = LayerMask.GetMask("Construction", "Tree", "Rock", "Deployed", "World");
buildingMask = LayerMask.GetMask("Construction", "Prevent Building", "Deployed", "World", "Terrain", "Tree", "Invisible", "Default");
AddCovalenceCommand("fc", "cmdCarpetBuild");
AddCovalenceCommand("fcc", "cmdCarpetCount");
AddCovalenceCommand("fcd", "cmdCarpetDestroy");
AddCovalenceCommand("fcg", "cmdCarpetGiveChat");
AddCovalenceCommand("fchelp", "cmdCarpetHelp");
AddCovalenceCommand("fcnav", "cmdCarpetNav");
AddCovalenceCommand("fcadmin", "cmdCarpetAdmin");
permission.RegisterPermission("flyingcarpet.use", this);
permission.RegisterPermission("flyingcarpet.vip", this);
permission.RegisterPermission("flyingcarpet.admin", this);
permission.RegisterPermission("flyingcarpet.unlimited", this);
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["helptext1"] = "Flying Carpet instructions:",
["helptext2"] = " type /fc to spawn a Flying Carpet",
["helptext3"] = " type /fcd to destroy your flyingcarpet.",
["helptext4"] = " type /fcc to show a count of your carpets",
["notunnel"] = "Access to spawn in tunnels has been blocked !!",
["notauthorized"] = "You don't have permission to do that !!",
["notfound"] = "Could not locate a carpet.You must be within {0} meters for this!!",
["notflyingcarpet"] = "You are not piloting a flying carpet !!",
["maxcarpets"] = "You have reached the maximum allowed carpets",
["landingcarpet"] = "Carpet landing sequence started !!",
["risingcarpet"] = "Carpet takeoff sequence started !!",
["carpetlocked"] = "You must unlock the Carpet first !!",
["carpetspawned"] = "Flying Carpet spawned! Don't forget to lock it !!",
["carpetdestroyed"] = "Flying Carpet destroyed !!",
["carpetfuel"] = "You will need fuel to fly.Do not start without fuel !!",
["carpetnofuel"] = "You have been granted unlimited fly time, no fuel required !!",
["nostartseat"] = "You cannot light this lantern until seated !!",
["nolock"] = "You cannot place a lock on carpet storage.Lock the carpet instead.",
["nofuel"] = "You're out of fuel !!",
["noplayer"] = "Unable to find player {0}!",
["gaveplayer"] = "Gave carpet to player {0}!",
["lowfuel"] = "You're low on fuel !!",
["flyingcarpet"] = "Flying Carpet",
["fcmenu"] = "Flying Carpet Menu",
["menu"] = "Press RELOAD for menu",
["close"] = "Close",
["cancel"] = "Cancel",
["heading"] = "Headed to {0}",
["arrived"] = "Arrived at {0}",
["nocarpets"] = "You have no Carpets",
["currcarpets"] = "Current Carpets : {0}",
["giveusage"] = "You need to supply a valid SteamId."
}, this);
}
private bool isAllowed(BasePlayer player, string perm) => permission.UserHasPermission(player.UserIDString, perm);
private bool HasPermission(ConsoleSystem.Arg arg, string permname)
{
BasePlayer pl = arg.Connection.player as BasePlayer;
if (pl == null)
{
return true;
}
return permission.UserHasPermission(pl.UserIDString, permname);
}
private static HashSet<BasePlayer> FindPlayers(string nameOrIdOrIp)
{
HashSet<BasePlayer> players = new();
if (string.IsNullOrEmpty(nameOrIdOrIp)) return players;
//foreach (BasePlayer activePlayer in BasePlayer.activePlayerList)
foreach (BasePlayer activePlayer in BasePlayer.allPlayerList)
{
if (activePlayer.UserIDString.Equals(nameOrIdOrIp))
{
players.Add(activePlayer);
}
else if (!string.IsNullOrEmpty(activePlayer?.displayName) && activePlayer.displayName.Contains(nameOrIdOrIp, CompareOptions.IgnoreCase))
{
players.Add(activePlayer);
}
else if (activePlayer?.net?.connection != null && activePlayer.net.connection.ipaddress.Equals(nameOrIdOrIp))
{
players.Add(activePlayer);
}
}
return players;
}
#endregion
#region Configuration
protected override void LoadDefaultConfig()
{
Puts("Creating new config file.");
ConfigData config = new()
{
BlockInTunnel = true,
AllowLantern = false,
AllowRepaint = true,
UseMaxCarpetChecks = true,
DoubleFuel = false,
NameOnSign = true,
UseSign = true,
PlayEmptySound = false,
RequireFuel = true,
debug = false,
debugMovement = false,
MaxCarpets = 1,
VIPMaxCarpets = 2,
MinDistance = 10,
MinAltitude = 5,
CruiseAltitude = 35,
NormalSpeed = 12,
SprintSpeed = 25,
BoxSkinID = 1330214613,
RugSkinID = 870448575,
ChairSkinID = 875258235,
Version = Version
};
SaveConfig(config);
}
private void LoadConfigVariables()
{
configData = Config.ReadObject<ConfigData>();
configData.Version = Version;
SaveConfig(configData);
}
private void SaveConfig(ConfigData config)
{
Config.WriteObject(config, true);
}
private class ConfigData
{
//public bool AllowDamage;
public bool BlockInTunnel;
public bool AllowLantern;
public bool AllowRepaint;
public bool UseMaxCarpetChecks;
public bool DoubleFuel;
public bool NameOnSign;
public bool UseSign;
public bool PlayEmptySound;
public bool RequireFuel;
public bool debug;
public bool debugMovement;
public bool HonorRelationships;
public bool useFriends;
public bool useClans;
public bool useTeams;
public int MaxCarpets;
public int VIPMaxCarpets;
//public float InitialHealth;
public float MinDistance;
public float MinAltitude;
public float CruiseAltitude;
public float NormalSpeed;
public float SprintSpeed;
public ulong BoxSkinID;
public ulong RugSkinID;
public ulong ChairSkinID;
public VersionNumber Version;
}
#endregion
#region Message
private string Lang(string key, string id = null, 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));
#endregion
#region Chat Commands
[Command("fc"), Permission("flyingcarpet.use")]
private void cmdCarpetBuild(IPlayer iplayer, string command, string[] args)
{
bool vip = false;
BasePlayer player = iplayer.Object as BasePlayer;
if (!iplayer.HasPermission("flyingcarpet.use")) { Message(iplayer, "notauthorized"); return; }
if (args.Length == 1 && args[0] == "guiclose")
{
CuiHelper.DestroyUi(player, FCGUM);
return;
}
if (iplayer.HasPermission("flyingcarpet.vip"))
{
vip = true;
}
if (CarpetLimitReached(player, vip)) { Message(iplayer, "maxcarpets"); return; }
if (configData.BlockInTunnel && player.transform.position.y < -70)
{
Message(iplayer, "notunnel");
return;
}
AddCarpet(player, player.transform.position);
}
[Command("fcg"), Permission("flyingcarpet.admin")]
private void cmdCarpetGiveChat(IPlayer iplayer, string command, string[] args)
{
BasePlayer player = iplayer.Object as BasePlayer;
if (args.Length == 0)
{
Message(iplayer, "giveusage");
return;
}
bool vip = false;
string pname = args[0];
if (!iplayer.HasPermission("flyingcarpet.admin")) { Message(iplayer, "notauthorized"); return; }
if (pname == null) { Message(iplayer, "noplayer", "NAME_OR_ID"); return; }
BasePlayer Bplayer = BasePlayer.Find(pname);
if (Bplayer == null)
{
Message(iplayer, "noplayer", pname);
return;
}
IPlayer Iplayer = Bplayer.IPlayer;
if (Iplayer.HasPermission("flyingcarpet.vip"))
{
vip = true;
}
if (CarpetLimitReached(Bplayer, vip)) { Message(iplayer, "maxcarpets"); return; }
AddCarpet(Bplayer, Bplayer.transform.position);
Message(iplayer, "gaveplayer", pname);
}
[ConsoleCommand("fcgive")]
private void cmdCarpetGive(ConsoleSystem.Arg arg)
{
if (arg.IsRcon)
{
if (arg.Args == null)
{
Puts("You need to supply a valid SteamId.");
return;
}
}
else if (!HasPermission(arg, "flyingcarpet.admin"))
{
SendReply(arg, Lang("notauthorized", null, arg.Connection.player as BasePlayer));
return;
}
else if (arg.Args == null)
{
SendReply(arg, Lang("giveusage", null, arg.Connection.player as BasePlayer));
return;
}
bool vip = false;
string pname = arg.GetString(0);
if (pname.Length < 1) { Puts("Player name or id cannot be null"); return; }
BasePlayer Bplayer = BasePlayer.Find(pname);
if (Bplayer == null) { Puts($"Unable to find player '{pname}'"); return; }
IPlayer Iplayer = Bplayer.IPlayer;
if (Iplayer.HasPermission("flyingcarpet.vip")) { vip = true; }
if (CarpetLimitReached(Bplayer, vip))
{
Puts($"Player '{pname}' has reached maxcarpets"); return;
}
AddCarpet(Bplayer, Bplayer.transform.position);
Puts($"Gave carpet to '{Bplayer.displayName}'");
}
[Command("fcc"), Permission("flyingcarpet.use")]
private void cmdCarpetCount(IPlayer iplayer, string command, string[] args)
{
BasePlayer player = iplayer.Object as BasePlayer;
if (!iplayer.HasPermission("flyingcarpet.use")) { Message(iplayer, "notauthorized"); return; }
if (!loadplayer.ContainsKey(player.userID))
{
Message(iplayer, "nocarpets");
return;
}
string ccount = loadplayer[player.userID].carpetcount.ToString();
DoLog("CarpetCount: " + ccount);
Message(iplayer, "currcarpets", ccount);
}
[Command("fcd"), Permission("flyingcarpet.use")]
private void cmdCarpetDestroy(IPlayer iplayer, string command, string[] args)
{
BasePlayer player = iplayer.Object as BasePlayer;
if (!iplayer.HasPermission("flyingcarpet.use")) { Message(iplayer, "notauthorized"); return; }
string target = null;
if (args.Length > 0)
{
target = args[0];
}
if (iplayer.HasPermission("flyingcarpet.admin") && target != null)
{
if (target == "all")
{
DestroyAllCarpets(player);
CuiHelper.DestroyUi(player, FCGUI);
CuiHelper.DestroyUi(player, FCGUM);
return;
}
HashSet<BasePlayer> players = FindPlayers(target);
if (players.Count == 0)
{
Message(iplayer, "PlayerNotFound", target);
return;
}
if (players.Count > 1)
{
Message(iplayer, "MultiplePlayers", target, string.Join(", ", players.Select(p => p.displayName).ToArray()));
return;
}
BasePlayer targetPlayer = players.FirstOrDefault();
RemoveCarpet(targetPlayer);
DestroyRemoteCarpet(targetPlayer);
CuiHelper.DestroyUi(targetPlayer, FCGUI);
CuiHelper.DestroyUi(targetPlayer, FCGUM);
}
else
{
RemoveCarpet(player);
DestroyLocalCarpet(player);
CuiHelper.DestroyUi(player, FCGUI);
CuiHelper.DestroyUi(player, FCGUM);
}
}
[Command("fchelp"), Permission("flyingcarpet.use")]
private void cmdCarpetHelp(IPlayer iplayer, string command, string[] args)
{
BasePlayer player = iplayer.Object as BasePlayer;
if (!iplayer.HasPermission("flyingcarpet.use")) { Message(iplayer, "notauthorized"); return; }
Message(iplayer, "helptext1");
Message(iplayer, "helptext2");
Message(iplayer, "helptext3");
Message(iplayer, "helptext4");
}
[Command("fcnav"), Permission("flyingcarpet.use")]
private void cmdCarpetNav(IPlayer iplayer, string command, string[] args)
{
string debug = string.Join(",", args); DoLog($"{debug}");
BasePlayer player = iplayer.Object as BasePlayer;
CarpetEntity iscarpet = player.GetMounted().GetComponentInParent<CarpetEntity>();
if (args.Length > 0 && args[0] == "navclose")
{
CuiHelper.DestroyUi(player, FCGUM);
return;
}
else if (args.Length > 0 && args[0] == "navcancel")
{
CuiHelper.DestroyUi(player, FCGUM);
ShowTopGUI(player);
iscarpet.nav.enabled = false;
iscarpet.nav.paused = false;
iscarpet.autopilot = false;
return;
}
string monname = null;
for (int i = 1; i < args.Length; i++)
{
monname += args[i] + " ";
}
monname = monname.Trim();
Vector3 target = monPos[monname];
DoLog($"Flying from {player.transform.position} to {monname}@{target}");
if (iscarpet != null)
{
iscarpet.nav.currentMonument = monname;
iscarpet.nav.enabled = true;
iscarpet.nav.paused = false;
iscarpet.lantern1.SetFlag(BaseEntity.Flags.On, true);
iscarpet.engineon = true;
CuiHelper.DestroyUi(player, FCGUM);
Interface.GetMod().CallHook("OnCarpetNavChange", player, monname);
player.SendConsoleCommand("ddraw.text", 90, Color.green, monPos[monname], $"<size=20>{monname}</size>");
}
}
[Command("fcadmin"), Permission("flyingcarpet.admin")]
private void cmdCarpetAdmin(IPlayer iplayer, string command, string[] args)
{
if (!iplayer.HasPermission("flyingcarpet.admin")) { Message(iplayer, "notauthorized"); return; }
if (args.Length > 0 && args[0] == "debug")
{
configData.debug = !configData.debug;
Message(iplayer, $"Debug is {configData.debug}");
return;
}
}
#endregion
#region GUI
private void ShowTopGUI(BasePlayer player, string target = "")
{
if (player == null) return;
CuiHelper.DestroyUi(player, FCGUI);
if (target?.Length == 0) target = Lang("menu");
CuiElementContainer container = UI.Container(FCGUI, UI.Color("222222", 0.9f), "0.4 0.95", "0.6 1", false, "Overlay");
UI.Label(ref container, FCGUI, UI.Color("#ffffff", 1f), $"Flying Carpet: {target}", 12, "0.1 0", "0.9 1");
CuiHelper.AddUi(player, container);
}
private void ShowMenuGUI(BasePlayer player, string mode = null, string extra = null)
{
if (player == null) return;
CuiHelper.DestroyUi(player, FCGUM);
CuiElementContainer container = UI.Container(FCGUM, UI.Color("222222", 0.9f), "0.2 0.2", "0.8 0.8", true, "Overlay");
UI.Label(ref container, FCGUM, UI.Color("#ffffff", 1f), Lang("fcmenu"), 16, "0 0.92", "0.9 0.99");
UI.Button(ref container, FCGUM, UI.Color("#d85540", 1f), Lang("close"), 12, "0.92 0.93", "0.99 0.99", "fcnav navclose", UI.Color("#ffffff", 1));
int row = 0;
int col = 0;
float[] posb = new float[4];
foreach (KeyValuePair<string, Vector3> mons in monPos)
{
if (row > 10)
{
row = 0;
col++;
}
posb = GetButtonPositionP(row, col);
string moninfo = mons.Key + " (" + PositionToGrid(mons.Value) + ")";
UI.Button(ref container, FCGUM, UI.Color("#d85540", 1f), moninfo, 10, $"{posb[0]} {posb[1]}", $"{posb[0] + ((posb[2] - posb[0]) / 2)} {posb[3]}", $"fcnav {player.userID} {mons.Key}", UI.Color("#ffffff", 1));
row++;
}
UI.Button(ref container, FCGUM, UI.Color("#ff0000", 1f), Lang("cancel"), 10, $"{posb[0]} {posb[1]}", $"{posb[0] + ((posb[2] - posb[0]) / 2)} {posb[3]}", "fcnav navcancel", UI.Color("#ffffff", 1));
CuiHelper.AddUi(player, container);
}
private int RowNumber(int max, int count) => Mathf.FloorToInt(count / max);
private float[] GetButtonPosition(int rowNumber, int columnNumber)
{
float offsetX = 0.05f + (0.096f * columnNumber);
float offsetY = (0.80f - (rowNumber * 0.064f));
return new float[] { offsetX, offsetY, offsetX + 0.196f, offsetY + 0.03f };
}
private float[] GetButtonPositionP(int rowNumber, int columnNumber)
{
float offsetX = 0.05f + (0.146f * columnNumber);
float offsetY = (0.8f - (rowNumber * 0.074f));
return new float[] { offsetX, offsetY, offsetX + 0.256f, offsetY + 0.03f };
}
#endregion
#region our_hooks
private object OnCarpetNavChange(BasePlayer pl, string newdirection)
{
DoLog("OnCarpetNavChange");
ShowTopGUI(pl, Lang("heading", null, newdirection));
return null;
}
public static Vector3 StringToVector3(string sVector)
{
// Remove the parentheses
if (sVector.StartsWith("(") && sVector.EndsWith(")"))
{
sVector = sVector.Substring(1, sVector.Length - 2);
}
// split the items
string[] sArray = sVector.Split(',');
// return as a Vector3
return new Vector3(
float.Parse(sArray[0]),
float.Parse(sArray[1]),
float.Parse(sArray[2])
);
}
private object OnCarpetNavArrived(BasePlayer pl, string newdirection)
{
ShowTopGUI(pl, Lang("arrived", null, newdirection));
return null;
}
#endregion
#region Hooks
// This is how we take off or land the carpet!
private object OnOvenToggle(BaseOven oven, BasePlayer player)
{
const bool rtrn = false; // Must match other plugins with this call to avoid conflicts.QuickSmelt uses false
CarpetEntity activecarpet;
try
{
activecarpet = player.GetMounted().GetComponentInParent<CarpetEntity>();
if (activecarpet == null) return null;
}
catch
{
if (oven.GetComponentInParent<CarpetEntity>() && !configData.AllowLantern)
{
DoLog("No player mounted on this carpet.");
Message(player.IPlayer, "nostartseat"); return rtrn;
}
DoLog("No carpet or not mounted.");
return null;
}
if (activecarpet.carpetlock?.IsLocked() == true) { Message(player.IPlayer, "carpetlocked"); return rtrn; }
if (!player.isMounted) return rtrn; // player offline, does not mean ismounted on carpet
if (player.GetMounted() != activecarpet.entity)
{
DoLog("OnOvenToggle: Player not mounted on carpet!");
oven.StopCooking();
return rtrn;
}
DoLog("OnOvenToggle: Player cycled lantern!");
if (oven.IsOn())
{
oven.StopCooking();
}
else
{
oven.StartCooking();
}
if (!activecarpet.FuelCheck())
{
if (activecarpet.needfuel)
{
Message(player.IPlayer, "nofuel");
Message(player.IPlayer, "landingcarpet");
activecarpet.engineon = false;
}
}
bool ison = activecarpet.engineon;
if (ison) { activecarpet.islanding = true; Message(player.IPlayer, "landingcarpet"); return null; }
if (!ison)
{
AddPlayerToPilotsList(player);
activecarpet.StartEngine();
return null;
}
return rtrn;
}
// Check for carpet lantern fuel
private void OnFuelConsume(BaseOven oven, Item fuel, ItemModBurnable burnable)
{
// Only work on lanterns
if (oven.ShortPrefabName != "lantern.deployed") return;
int dbl = configData.DoubleFuel ? 4 : 2;
BaseEntity lantern = oven as BaseEntity;
// Only work on lanterns attached to a Carpet
CarpetEntity activecarpet = lantern.GetComponentInParent<CarpetEntity>();
if (activecarpet == null) return;
DoLog("OnConsumeFuel: found a carpet lantern!");
if (activecarpet.needfuel)
{
DoLog("OnConsumeFuel: carpet requires fuel!");
}
else
{
DoLog("OnConsumeFuel: carpet does not require fuel!");
fuel.amount++; // Required to keep it from decrementing
return;
}
BasePlayer player = activecarpet.GetComponent<BaseMountable>().GetMounted() as BasePlayer;
if (!player) return;
DoLog("OnConsumeFuel: checking fuel level...");
// Before it drops to 1 (3 for doublefuel) AFTER this hook call is complete, warn them that the fuel is low (1) - ikr
if (fuel.amount == dbl)
{
DoLog("OnConsumeFuel: sending low fuel warning...");
if (configData.PlayEmptySound)
{
Effect.server.Run("assets/bundled/prefabs/fx/well/pump_down.prefab", player.transform.position);
}
Message(player.IPlayer, "lowfuel");
}
if (configData.DoubleFuel)
{
fuel.amount--;
}
if (fuel.amount == 0)
{
DoLog("OnConsumeFuel: out of fuel.");
Message(player.IPlayer, "lowfuel");
bool ison = activecarpet.engineon;
if (ison)
{
activecarpet.islanding = true;
activecarpet.engineon = false;
Message(player.IPlayer, "landingcarpet");
OnOvenToggle(oven, player);
}
}
}
// To skip cycling our lantern (thanks, k1lly0u)
private object OnNightLanternToggle(BaseEntity entity, bool status)
{
// Only work on lanterns
if (entity.ShortPrefabName != "lantern.deployed") return null;
DoLog("OnNightLanternToggle: Called on a lantern.Checking for carpet...");
// Only work on lanterns attached to a Carpet
CarpetEntity activecarpet = entity.GetComponentInParent<CarpetEntity>();
if (activecarpet != null)
{
DoLog("OnNightLanternToggle: Do not cycle this lantern!");
return true;
}
DoLog("OnNightLanternToggle: Not a carpet lantern.");
return null;
}
#endregion
#region Primary
private void DoLog(string message, bool ismovement = false)
{
if (ismovement && !configData.debugMovement) return;
if (configData.debugMovement || configData.debug) Interface.GetMod().LogInfo(message);
}
private void AddCarpet(BasePlayer player, Vector3 location)
{
if (player == null && location == default) return;
if (location == default && player != null) location = player.transform.position;
Vector3 spawnpos = new();
Quaternion rotation = player.GetNetworkRotation();
Vector3 forward = rotation * Vector3.forward;
Vector3 straight = Vector3.Cross(Vector3.Cross(Vector3.up, forward), Vector3.up).normalized;
// Set initial default for fuel requirement based on config
bool needfuel = configData.RequireFuel;
if (isAllowed(player, "flyingcarpet.unlimited"))
{
// User granted unlimited fly time without fuel
needfuel = false;
}
if (needfuel)
{
// Don't put them on the carpet since they need to fuel up first
spawnpos = location + (straight * 2f);
spawnpos.y = location.y + 0.5f;
}
else
{
// Spawn at point of player
spawnpos = new Vector3(location.x, location.y + 0.5f, location.z);
}
//const string staticprefab = "assets/bundled/prefabs/static/chair.invisible.static.prefab";
const string staticprefab = "assets/bundled/prefabs/static/chair.static.prefab";
BaseEntity newCarpet = GameManager.server.CreateEntity(staticprefab, spawnpos, player.transform.rotation, true);
newCarpet.name = "FlyingCarpet";
BaseMountable chairmount = newCarpet as BaseMountable;
chairmount.isMobile = true;
newCarpet.enableSaving = false;
newCarpet.OwnerID = player.userID;
newCarpet.skinID = Convert.ToUInt64(configData.ChairSkinID);
//foreach (BoxCollider box in newCarpet.gameObject.GetComponentsInChildren<BoxCollider>())
//{
// UnityEngine.Object.DestroyImmediate(box);
//}
foreach (MeshCollider mesh in newCarpet.GetComponentsInChildren<MeshCollider>())
{
UnityEngine.Object.DestroyImmediate(mesh);
}
newCarpet.Spawn();
CarpetEntity carpet = newCarpet.gameObject.AddComponent<CarpetEntity>();
carpet.needfuel = needfuel;
// Unlock the tank if they need fuel.carpet.lantern1.SetFlag(BaseEntity.Flags.Locked, !needfuel);
if (needfuel)
{
// We have to set this after the spawn.carpet.SetFuel(0);
}
// paint sign with username
if (SignArtist && configData.UseSign && configData.NameOnSign)
{
// This only works with version 1.1.7 on
if (SignArtist.Version >= new VersionNumber(1, 1, 7))
{
string message = player.displayName;
int fontsize = Convert.ToInt32(Math.Floor(150f / message.Length));
SignArtist?.Call("API_SignText", player, carpet.sign, message, fontsize, "00FF00", "000000");
}
}
if (configData.UseSign && !configData.AllowRepaint)
{
carpet.sign.SetFlag(BaseEntity.Flags.Busy, true);
}
if (configData.UseSign) carpet.sign.SetFlag(BaseEntity.Flags.Locked, true);
AddPlayerID(player.userID);
if (chairmount != null && player != null)
{
Message(player.IPlayer, "carpetspawned");
if (carpet.needfuel)
{
Message(player.IPlayer, "carpetfuel");
}
else
{
// Put them in the chair.They will still need to unlock it.Message(player.IPlayer, "carpetnofuel");
chairmount.MountPlayer(player);
}
return;
}
}
public bool PilotListContainsPlayer(BasePlayer player)
{
return pilotslist.Contains(player.userID);
}
private void AddPlayerToPilotsList(BasePlayer player)
{
if (PilotListContainsPlayer(player)) return;
pilotslist.Add(player.userID);
}
public void RemovePlayerFromPilotsList(BasePlayer player)
{
if (PilotListContainsPlayer(player))
{
pilotslist.Remove(player.userID);
}
}
private void DestroyLocalCarpet(BasePlayer player)
{
if (player == null) return;
List<BaseEntity> carpetlist = new();
Vis.Entities(player.transform.position, configData.MinDistance, carpetlist);
bool foundcarpet = false;
foreach (BaseEntity p in carpetlist)
{
CarpetEntity foundent = p.GetComponentInParent<CarpetEntity>();
if (foundent != null)
{
foundcarpet = true;
if (foundent.ownerid != player.userID) return;
foundent.entity.Kill(BaseNetworkable.DestroyMode.Gib);
Message(player.IPlayer, "carpetdestroyed");
}
}
if (!foundcarpet)
{
Message(player.IPlayer, "notfound", configData.MinDistance.ToString());
}
}
private void DestroyAllCarpets(BasePlayer player)
{
List<BaseEntity> carpetlist = new();
Vis.Entities(new Vector3(0, 0, 0), 3500f, carpetlist);
bool foundcarpet = false;
foreach (BaseEntity p in carpetlist)
{
CarpetEntity foundent = p.GetComponentInParent<CarpetEntity>();
if (foundent != null)
{
foundcarpet = true;
foundent.entity.Kill(BaseNetworkable.DestroyMode.Gib);
Message(player.IPlayer, "carpetdestroyed");
}
}
if (!foundcarpet)
{
Message(player.IPlayer, "notfound", configData.MinDistance.ToString());
}
}
private void DestroyRemoteCarpet(BasePlayer player)
{
if (player == null) return;
List<BaseEntity> carpetlist = new();
Vis.Entities(new Vector3(0, 0, 0), 3500f, carpetlist);
bool foundcarpet = false;
foreach (BaseEntity p in carpetlist)
{
CarpetEntity foundent = p.GetComponentInParent<CarpetEntity>();
if (foundent != null)
{
foundcarpet = true;
if (foundent.ownerid != player.userID) return;
foundent.entity.Kill(BaseNetworkable.DestroyMode.Gib);
Message(player.IPlayer, "carpetdestroyed");
}
}
if (!foundcarpet)
{
Message(player.IPlayer, "notfound", configData.MinDistance.ToString());
}
}
private void OnPlayerInput(BasePlayer player, InputState input)
{
if (player == null || input == null) return;
if (!player.isMounted) return;
CarpetEntity activecarpet = player.GetMounted().GetComponentInParent<CarpetEntity>();
if (player?.GetMounted() != activecarpet?.entity) return;
activecarpet?.CarpetInput(input, player);
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
{
if (entity == null || hitInfo == null) return;
CarpetEntity iscarpet = entity.GetComponentInParent<CarpetEntity>();
if (iscarpet != null) hitInfo.damageTypes.ScaleAll(0);
}
private object OnEntityGroundMissing(BaseEntity entity)
{
CarpetEntity iscarpet = entity.GetComponentInParent<CarpetEntity>();
if (iscarpet != null) return false;
return null;
}
private bool CarpetLimitReached(BasePlayer player, bool vip = false)
{
if (configData.UseMaxCarpetChecks && loadplayer.ContainsKey(player.userID))
{
int currentcount = loadplayer[player.userID].carpetcount;
int maxallowed = configData.MaxCarpets;
if (vip)
{
maxallowed = configData.VIPMaxCarpets;
}
if (currentcount >= maxallowed) return true;
}
return false;
}
private object CanMountEntity(BasePlayer player, BaseMountable mountable)
{
CarpetEntity activecarpet = mountable.GetComponentInParent<CarpetEntity>();
if (activecarpet != null)
{
if (!IsFriend(player.userID, mountable.OwnerID)) return false;
}
return null;
}
private object CanDismountEntity(BasePlayer player, BaseMountable entity)
{
if (player == null) return null;
if (PilotListContainsPlayer(player))
{
CarpetEntity activecarpet = entity.GetComponentInParent<CarpetEntity>();
if (activecarpet?.engineon == true)
{
return false;
}
}
return null;
}
private void OnEntityMounted(BaseMountable mountable, BasePlayer player)
{
CarpetEntity activecarpet = mountable.GetComponentInParent<CarpetEntity>();
if (activecarpet != null)
{
DoLog("OnEntityMounted: player mounted copter!");
if (mountable.GetComponent<BaseEntity>() != activecarpet.entity) return;
activecarpet.lantern1.SetFlag(BaseEntity.Flags.On, false);
activecarpet.sign?.SetFlag(BaseEntity.Flags.Busy, true);
ShowTopGUI(player);
}
}
private void OnEntityDismounted(BaseMountable mountable, BasePlayer player)
{
CarpetEntity activecarpet = mountable.GetComponentInParent<CarpetEntity>();
if (activecarpet != null)
{
DoLog("OnEntityMounted: player dismounted copter!");
CuiHelper.DestroyUi(player, FCGUI);
if (mountable.GetComponent<BaseEntity>() != activecarpet.entity) return;
activecarpet.sign?.SetFlag(BaseEntity.Flags.Busy, false);
}
}
private object CanLootEntity(BasePlayer player, StorageContainer container)
{
if (container == null || player == null) return null;
CarpetEntity iscarpet = container.GetComponentInParent<CarpetEntity>();
if (iscarpet?.carpetlock?.IsLocked() == true)
{
return false;
}
return null;
}
private object CanPickupEntity(BasePlayer player, BaseCombatEntity entity)
{
if (entity == null || player == null) return null;
string myparent = entity?.GetParentEntity()?.name;
if (myparent == "FlyingCarpet" || entity?.name == "FlyingCarpet")
{
if (configData.debug)
{
if (entity?.name == "FlyingCarpet")
{
DoLog("CanPickupEntity: player trying to pickup the carpet!");
}
else if (myparent == "FlyingCarpet")
{
string entity_name = entity?.LookupPrefab().name;
DoLog($"CanPickupEntity: player trying to remove {entity_name} from a carpet!");
}
}
Message(player.IPlayer, "notauthorized");
return false;
}
return null;
}
private object CanDeployItem(BasePlayer player, Deployer deployer, NetworkableId entityId)
{
if (entityId.Value == 0 || player == null) return null;
string myparent = null;
try
{
BaseNetworkable myent = BaseNetworkable.serverEntities.Find(entityId);
myparent = myent.GetParentEntity().name;
DoLog(myparent);
}
catch { }
if (myparent == "FlyingCarpet")
{
if (configData.debug) DoLog($"Player {player.displayName} trying to place an item(lock?) on carpet storage!");
Message(player.IPlayer, "nolock");
return true;
}
return null;
}
private object CanPickupLock(BasePlayer player, BaseLock baseLock)
{
if (baseLock == null || player == null) return null;
BaseEntity myent = baseLock as BaseEntity;
string myparent = null;
try
{
myparent = myent.GetParentEntity().name;
}
catch { }
if (myparent == "FlyingCarpet")
{
DoLog("CanPickupLock: player trying to remove lock from a carpet!");
Message(player.IPlayer, "notauthorized");
return false;
}
return null;
}
private void AddPlayerID(ulong ownerid)
{
if (!loadplayer.ContainsKey(ownerid))
{
loadplayer.Add(ownerid, new PlayerCarpetData
{
carpetcount = 1,
});
return;
}
loadplayer[ownerid].carpetcount++;
}
private void RemovePlayerID(ulong ownerid)
{
if (loadplayer.ContainsKey(ownerid)) loadplayer[ownerid].carpetcount--;
}
private object OnPlayerDeath(BasePlayer player, HitInfo info)
{
RemovePlayerFromPilotsList(player);
return null;
}
private void RemoveCarpet(BasePlayer player)
{
RemovePlayerFromPilotsList(player);
}
private void OnPlayerDisconnected(BasePlayer player, string reason)
{
RemoveCarpet(player);
}
private void OnPlayerRespawned(BasePlayer player)
{
RemoveCarpet(player);
}
private void DestroyAll<T>()
{
UnityEngine.Object[] objects = UnityEngine.Object.FindObjectsOfType(typeof(T));
if (objects != null)
{
foreach (UnityEngine.Object gameObj in objects)
{
UnityEngine.Object.Destroy(gameObj);
}
}
}
private bool IsFriend(ulong playerid, ulong ownerid)
{
if (!configData.HonorRelationships) return true;
if (playerid == ownerid) return true;
if (configData.useFriends && Friends != null)
{
object fr = Friends?.CallHook("AreFriends", playerid, ownerid);
if (fr != null && (bool)fr)
{
return true;
}
}
if (configData.useClans && Clans != null)
{
string playerclan = (string)Clans?.CallHook("GetClanOf", playerid);
string ownerclan = (string)Clans?.CallHook("GetClanOf", ownerid);
if (playerclan != null && ownerclan != null && playerclan == ownerclan)
{
return true;
}
}
if (configData.useTeams)
{
RelationshipManager.PlayerTeam playerTeam = RelationshipManager.ServerInstance.FindPlayersTeam(playerid);
if (playerTeam?.members.Contains(ownerid) == true)
{
return true;
}
}
return false;
}
private void Unload()
{
DestroyAll<CarpetEntity>();
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, FCGUI);
CuiHelper.DestroyUi(player, FCGUM);
}
}
public string PositionToGrid(Vector3 position)
{
if (GridAPI != null)
{
string[] g = (string[])GridAPI.CallHook("GetGrid", position);
return string.Concat(g);
}
else
{
// From GrTeleport for display only
Vector2 r = new((World.Size / 2) + position.x, (World.Size / 2) + position.z);
float x = Mathf.Floor(r.x / 146.3f) % 26;
float z = Mathf.Floor(World.Size / 146.3f) - Mathf.Floor(r.y / 146.3f);
return $"{(char)('A' + x)}{z - 1}";
}
}
private void FindMonuments()
{
foreach (MonumentInfo monument in UnityEngine.Object.FindObjectsOfType<MonumentInfo>())
{
if (monument.name.Contains("power_sub") || monument.name.Contains("derwater")) continue;
float realWidth = 0f;
string name;
if (monument.name == "OilrigAI")
{
name = "Small Oilrig";
realWidth = 100f;
}
else if (monument.name == "OilrigAI2")
{
name = "Large Oilrig";
realWidth = 200f;
}
else if (monument.name == "assets/bundled/prefabs/autospawn/monument/medium/radtown_small_3.prefab")
{
name = "Sewer Branch";
realWidth = 100;
}
else
{
name = Regex.Match(monument.name, @"\w{6}\/(.+\/)(.+)\.(.+)").Groups[2].Value.Replace("_", " ").Replace(" 1", "").Titleize();// + " 0";
}
if (name.Length == 0) continue;
if (monPos.ContainsKey(name))
{
if (monPos[name] == monument.transform.position) continue;
string newname = name.Remove(name.Length - 1, 1) + "1";
if (monPos.ContainsKey(newname))
{
newname = name.Remove(name.Length - 1, 1) + "2";
}
if (monPos.ContainsKey(newname))
{
continue;
}
name = newname;
}
Vector3 extents = monument.Bounds.extents;
if (realWidth > 0f)
{
extents.z = realWidth;
}
if (extents.z < 1)
{
extents.z = 50f;
}
monPos.Add(name.Trim(), monument.transform.position);
monSize.Add(name.Trim(), extents);
DoLog($"Found monument {name} at {monument.transform.posi
// ... [Code preview truncated due to length. Full source in attached file] ...