Skip to content

Instantly share code, notes, and snippets.

@acidumirae
Last active December 5, 2024 20:56
Show Gist options
  • Save acidumirae/a36a823685771e2fb11532c19f47b2fe to your computer and use it in GitHub Desktop.
Save acidumirae/a36a823685771e2fb11532c19f47b2fe to your computer and use it in GitHub Desktop.
Rust TeleportGUI quick & dirty patch for 'PhoneController' does not contain a definition for 'PositionToGridCoord' | Line: 198, Pos: 64
using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Ext.Chaos;
using Oxide.Ext.Chaos.UIFramework;
using Oxide.Core.Plugins;
using UnityEngine;
using System.Linq;
using Oxide.Core.Libraries;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
using Facepunch;
using Newtonsoft.Json.Converters;
using Oxide.Ext.Chaos.Data;
using Oxide.Ext.Chaos.Json;
using Oxide.Game.Rust.Cui;
using Bounds = UnityEngine.Bounds;
using Chaos = Oxide.Ext.Chaos;
using Color = Oxide.Ext.Chaos.UIFramework.Color;
using Font = Oxide.Ext.Chaos.UIFramework.Font;
using GridLayoutGroup = Oxide.Ext.Chaos.UIFramework.GridLayoutGroup;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("TeleportGUI", "k1lly0u", "2.0.26")]
class TeleportGUI : ChaosPlugin
{
#region Fields
#region Permissions
[Chaos.Permission] private const string PERMISSION_TP_USE = "teleportgui.tp.use";
[Chaos.Permission] private const string PERMISSION_TP_CANCEL = "teleportgui.tp.tpcancel";
[Chaos.Permission] private const string PERMISSION_TP_BACK = "teleportgui.tp.tpback";
[Chaos.Permission] private const string PERMISSION_TP_BACK_ADMIN = "teleportgui.tp.tpback.admin";
[Chaos.Permission] private const string PERMISSION_TP_HERE = "teleportgui.tp.tphere";
[Chaos.Permission] private const string PERMISSION_TP_SLEEPERS = "teleportgui.tp.sleepers";
[Chaos.Permission] private const string PERMISSION_TP_AUTOACCEPT = "teleportgui.tp.autoaccept";
[Chaos.Permission] private const string PERMISSION_TP_LOCATION = "teleportgui.tp.location";
[Chaos.Permission] private const string PERMISSION_HOME_USE = "teleportgui.homes.use";
[Chaos.Permission] private const string PERMISSION_HOME_BACK = "teleportgui.homes.back";
[Chaos.Permission] private const string PERMISSION_HOME_BACK_ADMIN = "teleportgui.homes.back.admin";
[Chaos.Permission] private const string PERMISSION_HOME_BYPASS = "teleportgui.homes.back.bypass";
[Chaos.Permission] private const string PERMISSION_HOME_VIEW_OTHER_HOMES = "teleportgui.homes.viewothershomes";
[Chaos.Permission] private const string PERMISSION_HOME_DELETE_OTHER_HOMES = "teleportgui.homes.deleteothershomes";
[Chaos.Permission] private const string PERMISSION_WARP_USE = "teleportgui.warps.use";
[Chaos.Permission] private const string PERMISSION_WARP_BACK = "teleportgui.warps.back";
[Chaos.Permission] private const string PERMISSION_WARP_BACK_ADMIN = "teleportgui.warps.back.admin";
[Chaos.Permission] private const string PERMISSION_WARP_BYPASS = "teleportgui.warps.back.bypass";
[Chaos.Permission] private const string PERMISSION_WARP_ADMIN = "teleportgui.warps.admin";
[Chaos.Permission] private const string PERMISSION_HIDE_UI = "teleportgui.hideingui";
[Chaos.Permission] private const string PERMISSION_SEE_ALL = "teleportgui.seeall";
[Chaos.Permission] private const string PERMISSION_COMMAND_ADMIN = "teleportgui.admin";
#endregion
private static Datafile<TeleportData> m_TeleportData;
private static Datafile<Hash<string, WarpPoint>> m_WarpData;
private static Hash<string, MonumentWarpPoint> m_MonumentWarps = new Hash<string, MonumentWarpPoint>();
private static Hash<ulong, Vector3> m_LastTeleport = new Hash<ulong, Vector3>();
private static Hash<ulong, Vector3> m_LastHome = new Hash<ulong, Vector3>();
private static Hash<ulong, Vector3> m_LastWarp = new Hash<ulong, Vector3>();
private readonly Hash<ulong, Timer> m_PopupTimers = new Hash<ulong, Timer>();
private static Func<float, Action, Timer> m_CreateTimer;
private static ItemDefinition m_ScrapItemDefinition;
private static RaycastHit[] m_RayBuffer = new RaycastHit[64];
private static readonly List<Monument> m_Monuments = new List<Monument>();
private static readonly List<Monument> m_OilRigs = new List<Monument>();
private readonly Hash<string, Bounds> m_BoundsOverrides = new Hash<string, Bounds>()
{
["fishing_village_a"] = new Bounds(Vector3.up * 5f, Vector3.one * 85),
["fishing_village_b"] = new Bounds(Vector3.up * 5f, Vector3.one * 75),
["fishing_village_c"] = new Bounds(Vector3.up * 5f, Vector3.one * 50),
["lighthouse"] = new Bounds(Vector3.up * 20f, Vector3.one * 80),
["swamp_a"] = new Bounds(),
["swamp_b"] = new Bounds(),
["swamp_c"] = new Bounds(),
["supermarket_1"] = new Bounds(Vector3.zero, Vector3.one * 75),
["powerplant_1"] = new Bounds(Vector3.zero, new Vector3(250, 200, 300)),
["launch_site_1"] = new Bounds(Vector3.forward * -25, new Vector3(600, 200, 350)),
["trainyard_1"] = new Bounds(Vector3.zero, Vector3.one * 250),
["water_treatment_plant_1"] = new Bounds(Vector3.forward * -50, new Vector3(300, 200, 300)),
["radtown_small_3"] = new Bounds(Vector3.forward * -25, Vector3.one * 175),
["harbor_2"] = new Bounds(Vector3.zero, new Vector3(250, 200, 300)),
};
private const string FOUNDATION_SHORTNAME = "foundation";
private const string FOUNDATION_TRIANGLE_SHORTNAME = "foundation.triangle";
private const string FLOOR_SHORTNAME = "floor";
private const string FLOOR_TRIANGLE_SHORTNAME = "floor.triangle";
private bool m_IsNewSave;
private enum Mode { Teleport, Home, Warp }
#endregion
#region Oxide Hooks
private void Loaded()
{
m_TeleportData = new Datafile<TeleportData>($"{Title}/userdata", new VectorConverter());
m_WarpData = new Datafile<Hash<string, WarpPoint>>($"{Title}/warpdata", new VectorConverter());
Configuration.RegisterCustomPermissions(permission, this);
foreach (WarpPoint warpData in m_WarpData.Data.Values)
{
if (!string.IsNullOrEmpty(warpData.Permission))
{
string perm = warpData.Permission.StartsWith("teleportgui.") ? warpData.Permission : $"teleportgui.{warpData.Permission}";
if (!permission.PermissionExists(perm))
permission.RegisterPermission(perm, this);
}
}
m_GetString = GetString;
m_SendMessage = BroadcastToPlayer;
m_CreateTimer = timer.Every;
TeleportRequest.m_PopupAction = PlayerTeleporter.m_PopupAction = CreateTeleportRequestPopup;
PositionTeleporter.m_PopupAction = CreateTeleportRequestPopup;
foreach (string cmdAlias in Configuration.TP.CommandAliases)
cmd.AddChatCommand(cmdAlias, this, TPCommand);
foreach (string cmdAlias in Configuration.Home.CommandAliases)
cmd.AddChatCommand(cmdAlias, this, HomeCommand);
foreach (string cmdAlias in Configuration.Warp.CommandAliases)
cmd.AddChatCommand(cmdAlias, this, WarpCommand);
if (m_TeleportData.Data.ShouldResetUses)
ResetDailyUses();
else timer.Once(TimeUntilMidnight(), ResetDailyUses);
if (!Configuration.Home.SleepingBags.CreateHomeOnBagPlacement &&
!Configuration.Home.SleepingBags.CreateHomeOnBedPlacement &&
!Configuration.Home.SleepingBags.CreateHomeOnBeachTowelPlacement)
{
Unsubscribe(nameof(CanRenameBed));
Unsubscribe(nameof(OnEntitySpawned));
}
}
private void OnServerInitialized()
{
PrepareLocalization();
PurgeOldUsers();
if (m_IsNewSave && Configuration.Home.WipeHomesOnNewServerSave)
m_TeleportData.Data.WipeHomesData();
SetupUIComponents();
m_ScrapItemDefinition = ItemManager.FindItemDefinition("scrap");
bool saveConfig = false;
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
foreach (MonumentInfo monument in TerrainMeta.Path.Monuments)
{
string shortname = System.IO.Path.GetFileNameWithoutExtension(monument.name);
Bounds bounds = monument.Bounds.extents == Vector3.zero && m_BoundsOverrides.TryGetValue(shortname, out Bounds @override) ? @override : monument.Bounds;
if (bounds.extents != Vector3.zero)
{
if (shortname.Contains("oilrig", CompareOptions.IgnoreCase))
m_OilRigs.Add(new Monument(shortname, monument.transform, bounds));
else if (shortname.Contains("underwater_lab", CompareOptions.IgnoreCase))
continue;
else
{
if (!Configuration.Warp.MonumentWarps.TryGetValue(shortname, out ConfigData.WarpOptions.MonumentWarp monumentWarp))
{
Configuration.Warp.MonumentWarps[shortname] = monumentWarp = new ConfigData.WarpOptions.MonumentWarp();
saveConfig = true;
}
m_Monuments.Add(new Monument(shortname, monument.transform, bounds));
if (monumentWarp.Enabled)
{
string uniqueName = textInfo.ToTitleCase(Regex.Replace(shortname.Replace("_", " "), @"[\d-]", string.Empty).Trim());
//string gridCoord = PhoneController.PositionToGridCoord(monument.transform.position);
string gridCoord = MapHelper.PositionToString(monument.transform.position);
m_Messages[shortname] = uniqueName;
uniqueName += $" ({gridCoord})";
MonumentWarpPoint monumentWarpPoint = new MonumentWarpPoint(shortname, gridCoord, uniqueName, monument.transform, bounds);
if (monumentWarpPoint.Count > 0)
{
m_MonumentWarps[uniqueName] = monumentWarpPoint;
if (!string.IsNullOrEmpty(monumentWarp.Permission))
{
string perm = monumentWarp.Permission.StartsWith("teleportgui.") ? monumentWarp.Permission : $"teleportgui.{monumentWarp.Permission}";
if (!permission.PermissionExists(perm))
permission.RegisterPermission(perm, this);
monumentWarpPoint.Permission = perm;
}
if (!string.IsNullOrEmpty(monumentWarp.Command))
cmd.AddChatCommand(monumentWarp.Command, this, (player, command, args) =>
{
if (!player.HasPermission(PERMISSION_WARP_USE) || !monumentWarpPoint.HasPermission(player))
{
SendReply(player, "General.NoPermission");
return;
}
WarpTo(player, uniqueName);
});
}
}
}
}
}
lang.RegisterMessages(m_Messages, this);
if (saveConfig)
SaveConfiguration();
}
private void OnPlayerConnected(BasePlayer player)
{
if (m_TeleportData.Data.Users.TryGetValue(player.userID, out TeleportData.User userData))
userData.LastOnlineTime = CurrentTime();
}
private void OnPlayerDisconnected(BasePlayer player)
{
if (PlayerTeleporter.HasIncomingPending(player, out PlayerTeleporter teleporter) || PlayerTeleporter.HasOutgoingPending(player, out teleporter))
teleporter.CancelTeleport(true, "Reason.Disconnected");
if (TeleportRequest.HasIncomingRequest(player, out TeleportRequest teleporterRequest) || TeleportRequest.HasOutgoingRequest(player, out teleporterRequest))
teleporterRequest.RequestCancelled();
if (PositionTeleporter.IsWaiting(player, out PositionTeleporter positionTeleporter))
positionTeleporter.CancelTeleport(true, "Reason.Disconnected");
}
private void OnEntitySpawned(SleepingBag sleepingBag)
{
if (!sleepingBag)
return;
BasePlayer player = BasePlayer.FindByID(sleepingBag.OwnerID);
if (!player)
return;
if ((sleepingBag.ShortPrefabName == "sleepingbag_leather_deployed" && Configuration.Home.SleepingBags.CreateHomeOnBagPlacement) ||
(sleepingBag.ShortPrefabName == "bed_deployed" && Configuration.Home.SleepingBags.CreateHomeOnBedPlacement) ||
(sleepingBag.ShortPrefabName == "beachtowel.deployed" && Configuration.Home.SleepingBags.CreateHomeOnBeachTowelPlacement))
{
if (Configuration.Home.SleepingBags.OnlyCreateInBuilding && sleepingBag.IsOutside())
return;
if (ZoneManager.IsLoaded && ZoneManager.PlayerHasFlag(player, "notp"))
{
SendReply(player, "Homes can not be set in NoTP zones");
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
if (!HasMaximumHomes(player, userData))
{
SendReply(player, "Home.Error.LimitReached");
return;
}
string newName = userData.Homes.ContainsKey(sleepingBag.niceName) ? GetUniqueBagName(userData, sleepingBag.niceName) : sleepingBag.niceName;
userData.Homes[newName] = new TeleportData.User.HomePoint
{
Position = sleepingBag.transform.position,
EntityID = sleepingBag.net.ID.Value
};
int maxHomes = GetMaxHomesForPlayer(player);
if (maxHomes == 0)
SendReply(player, "Home.Success.Created.Bed", newName);
else SendReply(player, "Home.Success.Created.Bed.Remaining", newName, maxHomes - userData.Homes.Count);
}
}
private void OnEntityTakeDamage(BasePlayer player, HitInfo hitInfo)
{
if (!player)
return;
if (Configuration.TP.CancelOnDamage)
{
if (PlayerTeleporter.HasIncomingPending(player, out PlayerTeleporter teleporter) || PlayerTeleporter.HasOutgoingPending(player, out teleporter))
teleporter.CancelTeleport(true, "Reason.TookDamage");
}
if (PositionTeleporter.IsWaiting(player, out PositionTeleporter positionTeleporter))
{
if ((positionTeleporter.IsHomeTeleport && Configuration.Home.CancelOnDamage) || (!positionTeleporter.IsHomeTeleport && Configuration.Warp.CancelOnDamage))
positionTeleporter.CancelTeleport(true, "Reason.TookDamage");
}
}
private void OnEntityDeath(BasePlayer player, HitInfo hitInfo)
{
if (!player)
return;
if (Configuration.TP.CancelOnDeath)
{
if (PlayerTeleporter.HasIncomingPending(player, out PlayerTeleporter teleporter) || PlayerTeleporter.HasOutgoingPending(player, out teleporter))
teleporter.CancelTeleport(true, "Reason.Death");
}
if (PositionTeleporter.IsWaiting(player, out PositionTeleporter positionTeleporter))
{
if ((positionTeleporter.IsHomeTeleport && Configuration.Home.CancelOnDeath) || (!positionTeleporter.IsHomeTeleport && Configuration.Warp.CancelOnDeath))
positionTeleporter.CancelTeleport(true, "Reason.Death");
}
}
private void OnEntityDeath(SleepingBag sleepingBag, HitInfo hitInfo) => HandleSleepingBagDestroyed(sleepingBag);
private void OnEntityKill(SleepingBag sleepingBag) => HandleSleepingBagDestroyed(sleepingBag);
private void CanRenameBed(BasePlayer player, SleepingBag sleepingBag, string bedName)
{
if (m_TeleportData.Data.Users.TryGetValue(sleepingBag.OwnerID, out TeleportData.User userData))
{
string homeName = null;
TeleportData.User.HomePoint homePoint = null;
foreach (KeyValuePair<string, TeleportData.User.HomePoint> kvp in userData.Homes)
{
if (kvp.Value.EntityID == sleepingBag.net.ID.Value)
{
homeName = kvp.Key;
homePoint = kvp.Value;
break;
}
}
if (string.IsNullOrEmpty(homeName) || homePoint == null)
return;
NextTick(() =>
{
if (!sleepingBag || sleepingBag.IsDestroyed)
return;
string newName = userData.Homes.ContainsKey(sleepingBag.niceName) ? GetUniqueBagName(userData, sleepingBag.niceName) : sleepingBag.niceName;
userData.Homes.Remove(homeName);
userData.Homes.Add(newName, homePoint);
});
}
}
private string GetUniqueBagName(TeleportData.User userData, string bedName)
{
int random = Random.Range(1000, 9999);
if (userData.Homes.ContainsKey(bedName + " " + random))
return GetUniqueBagName(userData, bedName);
return bedName + " " + random;
}
private void OnServerSave() => m_TeleportData.Save();
private void OnNewSave(string str) => m_IsNewSave = true;
private void Unload()
{
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
ChaosUI.Destroy(player, TPUI);
ChaosUI.Destroy(player, TPR_POPUP);
ChaosUI.Destroy(player, TPP_POPUP);
}
TeleportRequest.Clear();
PlayerTeleporter.Clear();
PositionTeleporter.Clear();
if (!Interface.Oxide.IsShuttingDown)
m_TeleportData.Save();
m_Monuments.Clear();
m_OilRigs.Clear();
m_MonumentWarps.Clear();
m_TeleportData = null;
m_WarpData = null;
m_SendMessage = null;
m_CreateTimer = null;
Configuration = null;
}
#endregion
#region Functions
private void PurgeOldUsers()
{
List<ulong> purgeUsers = Pool.Get<List<ulong>>();
double currentTime = CurrentTime();
foreach (KeyValuePair<ulong, TeleportData.User> kvp in m_TeleportData.Data.Users)
{
if (kvp.Value.LastOnlineTime + (Configuration.PurgeDays * 86400) < currentTime)
purgeUsers.Add(kvp.Key);
}
foreach (ulong playerId in purgeUsers)
m_TeleportData.Data.Users.Remove(playerId);
Pool.FreeUnmanaged(ref purgeUsers);
}
private void HandleSleepingBagDestroyed(SleepingBag sleepingBag)
{
if (!sleepingBag || sleepingBag.net == null ||
(!Configuration.Home.SleepingBags.CreateHomeOnBagPlacement && !Configuration.Home.SleepingBags.CreateHomeOnBedPlacement))
return;
NetworkableId sleepingBagId = sleepingBag.net.ID;
BasePlayer player = BasePlayer.FindByID(sleepingBag.OwnerID);
if (m_TeleportData.Data.Users.TryGetValue(sleepingBag.OwnerID, out TeleportData.User userData))
{
foreach (KeyValuePair<string, TeleportData.User.HomePoint> kvp in userData.Homes)
{
if (kvp.Value.EntityID == sleepingBagId.Value)
{
if (player && player.IsConnected)
SendReply(player, "Notification.BedHomeDestroyed", kvp.Key);
userData.Homes.Remove(kvp);
break;
}
}
}
}
#endregion
#region Messaging
private static Action<BasePlayer, string, object[]> m_SendMessage;
private static Func<string, BasePlayer, string> m_GetString;
private static string GetTranslatedString(string key, BasePlayer player) => m_GetString(key, player);
private static void SendReply(BasePlayer target, string key, params object[] args) => m_SendMessage(target, key, args);
private void BroadcastToPlayer(BasePlayer player, string key, params object[] args)
{
string message = lang.GetMessage(key, this, player.UserIDString);
if (args?.Length > 0)
message = string.Format(message, args);
if (Configuration.Chat.UsePrefix)
message = Configuration.Chat.Prefix + message;
if (Configuration.Chat.Icon != 0UL)
player.SendConsoleCommand("chat.add", 2, Configuration.Chat.Icon, message);
else player.ChatMessage(message);
}
#endregion
#region Helpers
private static TeleportData.User GetOrCreateUserSettings(BasePlayer player)
{
if (!m_TeleportData.Data.Users.TryGetValue(player.userID, out TeleportData.User userData))
{
userData = m_TeleportData.Data.Users[player.userID] = new TeleportData.User{ LastOnlineTime = CurrentTime() };
}
return userData;
}
private static bool IsNearOilRig(Vector3 position)
{
foreach (Monument monument in m_OilRigs)
{
if (monument.IsInMonument(position))
return true;
}
return false;
}
private static bool IsInMonument(BasePlayer player, bool ignoreSafeZone = false) => IsInMonument(player.transform.position, ignoreSafeZone);
private static bool IsInMonument(Vector3 position, bool ignoreSafeZone = false)
{
foreach (Monument monument in m_Monuments)
{
if (ignoreSafeZone && monument.IsSafeZone)
continue;
if (monument.IsInMonument(position))
return true;
}
return false;
}
private static bool IsInUnderwaterLab(Vector3 position)
{
int hits = Physics.OverlapSphereNonAlloc(position, 1f, Vis.colBuffer, 1 << 18);
for (int i = 0; i < hits; i++)
{
Collider col = Vis.colBuffer[i];
EnvironmentVolume environmentVolume = col.gameObject.GetComponent<EnvironmentVolume>();
if (environmentVolume != null && (environmentVolume.Type & EnvironmentType.UnderwaterLab) == EnvironmentType.UnderwaterLab)
return true;
}
return false;
}
private static bool IsInFoundation(Vector3 position) => IsUnderFoundation(position) || IsInsideFoundation(position);
private static bool IsInsideFoundation(Vector3 pos)
{
RaycastHit[] hits = Physics.RaycastAll(new Ray(pos + (Vector3.up * 2f), Vector3.down), 3f, 1 << 21);
for (int i = 0; i < hits.Length; i++)
{
BuildingBlock block = hits[i].GetEntity() as BuildingBlock;
if (block != null && block.ShortPrefabName is FOUNDATION_SHORTNAME or FOUNDATION_TRIANGLE_SHORTNAME)
{
if (block.transform.position.y > pos.y)
return true;
}
}
return false;
}
private static bool IsUnderFoundation(Vector3 pos)
{
bool hitBackFaces = Physics.queriesHitBackfaces;
try
{
Physics.queriesHitBackfaces = true;
if (Physics.Raycast(pos + (Vector3.up * 0.25f), Vector3.up, out RaycastHit raycastHit, 50f, 1 << 21, QueryTriggerInteraction.Collide))
{
BuildingBlock buildingBlock = raycastHit.GetEntity() as BuildingBlock;
if (buildingBlock && buildingBlock.ShortPrefabName is FOUNDATION_SHORTNAME or FOUNDATION_TRIANGLE_SHORTNAME)
return true;
}
return false;
}
finally
{
Physics.queriesHitBackfaces = hitBackFaces;
}
}
private static bool CheckFoundation(Vector3 position) =>
FindBuildingBlock(position, FOUNDATION_SHORTNAME) || FindBuildingBlock(position, FOUNDATION_TRIANGLE_SHORTNAME);
private static bool CheckFloor(Vector3 position) =>
FindBuildingBlock(position, FLOOR_SHORTNAME) || FindBuildingBlock(position, FLOOR_TRIANGLE_SHORTNAME);
private static bool FindBuildingBlock(Vector3 position, string shortname)
{
int num = Physics.RaycastNonAlloc(new Ray(position + (Vector3.up * 0.1f), Vector3.down), m_RayBuffer, 0.2f);
if (num == 0)
return false;
for (int i = 0; i < num; i++)
{
RaycastHit raycastHit = m_RayBuffer[i];
BuildingBlock buildingBlock = raycastHit.GetEntity() as BuildingBlock;
if (buildingBlock && buildingBlock.ShortPrefabName == shortname)
return true;
}
return false;
}
private static bool IsInWater(BasePlayer player)
{
ModelState modelState = player.modelState;
return modelState != null && modelState.waterLevel > 0f;
}
private static bool HasReachedDailyLimit(BasePlayer player, TeleportData.User userData, Mode mode)
{
switch (mode)
{
case Mode.Teleport:
{
if (Configuration.TP.Limits.Default == 0)
return false;
int limit = Configuration.TP.Limits.GetHighestOption(player);
return userData.TPUsage.UsesToday >= limit;
}
case Mode.Home:
{
if (Configuration.Home.Limits.Default == 0)
return false;
int limit = Configuration.Home.Limits.GetHighestOption(player);
return userData.HomeUsage.UsesToday >= limit;
}
case Mode.Warp:
{
if (Configuration.Warp.Limits.Default == 0)
return false;
int limit = Configuration.Warp.Limits.GetHighestOption(player);
return userData.WarpUsage.UsesToday >= limit;
}
}
return false;
}
private static int TimeUntilMidnight() => ((59 - DateTime.Now.Second) + ((59 - DateTime.Now.Minute) * 60) + ((23 - DateTime.Now.Hour) * 3600));
private void ResetDailyUses()
{
foreach (TeleportData.User userSettings in m_TeleportData.Data.Users.Values)
{
userSettings.TPUsage.UsesToday = 0;
userSettings.HomeUsage.UsesToday = 0;
userSettings.WarpUsage.UsesToday = 0;
}
m_TeleportData.Data.LastResetTime = CurrentTime();
m_TeleportData.Save();
timer.Once(TimeUntilMidnight(), ResetDailyUses);
}
private static bool IsAdmin(BasePlayer player) => ServerUsers.Is(player.userID, ServerUsers.UserGroup.Moderator) || ServerUsers.Is(player.userID, ServerUsers.UserGroup.Owner);
#endregion
#region Teleport
private static void Teleport(BasePlayer player, BasePlayer target) => Teleport(player, target.transform.position);
private static void Teleport(BasePlayer player, Vector3 position)
{
m_LastTeleport[player.userID] = player.transform.position;
try
{
if (player.isMounted)
player.GetMounted().DismountPlayer(player, true);
player.SetParent(null, true, true);
player.StartSleeping();
player.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, true);
player.EnablePlayerCollider();
player.SetServerFall(true);
player.MovePosition(position);
player.ClientRPCPlayer(null, player, "ForcePositionTo", position);
if (player.IsConnected)
{
player.UpdateNetworkGroup();
player.SendNetworkUpdateImmediate(false);
player.ClearEntityQueue(null);
player.SendFullSnapshot();
}
}
finally
{
player.EnablePlayerCollider();
player.SetServerFall(false);
}
}
#endregion
#region Player Search
private static Action<List<BasePlayer>, string, ListHashSet<BasePlayer>> m_FindPlayerAction = ((results, search, input) =>
{
foreach (BasePlayer player in input)
{
if (player.UserIDString == search)
{
results.Clear();
results.Add(player);
return;
}
if (player.displayName.Contains(search, CompareOptions.OrdinalIgnoreCase))
results.Add(player);
}
});
private static List<BasePlayer> m_PlayerSearchList = new List<BasePlayer>();
private static List<BasePlayer> FindPlayer(string nameOrUserId, bool includeSleepers = false)
{
m_PlayerSearchList.Clear();
m_FindPlayerAction(m_PlayerSearchList, nameOrUserId, BasePlayer.activePlayerList);
if (includeSleepers)
m_FindPlayerAction(m_PlayerSearchList, nameOrUserId, BasePlayer.sleepingPlayerList);
return m_PlayerSearchList;
}
#endregion
#region Payment
private static bool PayForTeleport(BasePlayer player, Mode mode)
{
ConfigData.PurchaseOptions purchaseOptions = mode == Mode.Warp ? Configuration.Warp.Purchase :
mode == Mode.Home ? Configuration.Home.Purchase :
Configuration.TP.Purchase;
int cost = purchaseOptions.GetLowestOption(player);
switch (purchaseOptions.Mode)
{
case PurchaseMode.ServerRewards:
if (!ServerRewards.IsLoaded || (int) ServerRewards.CheckPoints(player.userID) < cost)
{
SendReply(player, "Purchase.Error.SR", cost);
return false;
}
break;
case PurchaseMode.Economics:
if (!Economics.IsLoaded || Convert.ToInt32(Economics.Balance(player.userID)) < cost)
{
SendReply(player, "Purchase.Error.Economics", cost);
return false;
}
break;
case PurchaseMode.Scrap:
if (player.inventory.GetAmount(m_ScrapItemDefinition.itemid) < cost)
{
SendReply(player, "Purchase.Error.Scrap", cost);
return false;
}
break;
default:
Debug.Log($"[TeleportGUI] Invalid currency type set in config!");
return false;
}
switch (purchaseOptions.Mode)
{
case PurchaseMode.ServerRewards:
ServerRewards.TakePoints(player.userID, cost);
SendReply(player, "Purchase.Success.SR", cost);
break;
case PurchaseMode.Economics:
Economics.Withdraw(player.userID, cost);
SendReply(player, "Purchase.Success.Economics", cost);
break;
case PurchaseMode.Scrap:
player.inventory.Take(null, m_ScrapItemDefinition.itemid, cost);
SendReply(player, "Purchase.Success.Scrap", cost);
break;
}
return true;
}
private static void RefundPayment(BasePlayer player, Mode mode)
{
ConfigData.PurchaseOptions purchaseOptions = mode == Mode.Warp ? Configuration.Warp.Purchase :
mode == Mode.Home ? Configuration.Home.Purchase :
Configuration.TP.Purchase;
int cost = purchaseOptions.GetLowestOption(player);
switch (purchaseOptions.Mode)
{
case PurchaseMode.ServerRewards:
ServerRewards.AddPoints(player.userID, cost);
SendReply(player, "Purchase.Refund.SR", cost);
break;
case PurchaseMode.Economics:
Economics.Deposit(player.userID, (double)cost);
SendReply(player, "Purchase.Refund.Economics", cost);
break;
case PurchaseMode.Scrap:
player.GiveItem(ItemManager.Create(m_ScrapItemDefinition, cost), BaseEntity.GiveItemReason.PickedUp);
SendReply(player, "Purchase.Refund.Scrap", cost);
break;
}
}
#endregion
#region Classes
private interface ITeleport
{
bool IsValid { get; set; }
int TimeRemaining { get; }
bool CanAccept { get; }
void Accept();
void Decline();
void Cancel();
bool CanCancel(BasePlayer player);
}
private class TeleportRequest : Pool.IPooled, ITeleport
{
private BasePlayer m_From;
private BasePlayer m_To;
private ulong m_FromID;
private ulong m_ToID;
private bool m_TpHere;
private int m_Time;
private bool m_IsPaying;
private bool m_IsInstant;
private Timer m_Timer;
public int TimeRemaining => m_Time;
public bool CanAccept => true;
public bool IsValid { get; set; }
private static Hash<ulong, TeleportRequest> m_OutgoingRequests = new Hash<ulong, TeleportRequest>();
private static Hash<ulong, TeleportRequest> m_IncomingRequests = new Hash<ulong, TeleportRequest>();
public static Action<BasePlayer, string, ITeleport, string, string, bool> m_PopupAction;
public static void Create(BasePlayer from, BasePlayer to, int timeoutTime, bool tpHere, bool playerIsPaying)
{
TeleportRequest teleportRequest = Pool.Get<TeleportRequest>();
teleportRequest.m_From = from;
teleportRequest.m_FromID = from.userID;
teleportRequest.m_To = to;
teleportRequest.m_ToID = to.userID;
teleportRequest.m_TpHere = tpHere;
teleportRequest.m_Time = timeoutTime;
teleportRequest.m_IsPaying = playerIsPaying;
teleportRequest.IsValid = true;
if (tpHere)
{
SendReply(from, "TPRequest.Here.Sent", to.displayName);
SendReply(to, "TPRequest.Here.Received", from.displayName);
}
else
{
SendReply(from, "TPRequest.Sent", to.displayName);
SendReply(to, "TPRequest.Received", from.displayName);
}
if (m_TeleportData.Data.Users.TryGetValue(to.userID, out TeleportData.User userData))
{
if (((userData.AutoAccept & TeleportData.User.AutoAcceptEnum.All) != 0) ||
((userData.AutoAccept & TeleportData.User.AutoAcceptEnum.Teams) != 0 && from.currentTeam != 0UL && from.currentTeam == to.currentTeam) ||
((userData.AutoAccept & TeleportData.User.AutoAcceptEnum.Clans) != 0 && Clans.IsLoaded && Clans.IsClanMember(from.userID, to.userID)) ||
((userData.AutoAccept & TeleportData.User.AutoAcceptEnum.Friends) != 0 && Friends.IsLoaded && Friends.AreFriends(from.userID, to.userID)))
{
teleportRequest.m_IsInstant = true;
teleportRequest.RequestAccepted();
return;
}
}
m_OutgoingRequests[from.userID] = teleportRequest;
m_IncomingRequests[to.userID] = teleportRequest;
teleportRequest.m_Timer = m_CreateTimer(1f, teleportRequest.TimerTick);
m_PopupAction(to, TPR_POPUP, teleportRequest, tpHere ? "Popup.Incoming.TPHere" : "Popup.Incoming.TPR", from.displayName.StripTags(), true);
m_PopupAction(from, TPR_POPUP, teleportRequest, tpHere ? "Popup.Outgoing.TPHere" : "Popup.Outgoing.TPR", to.displayName.StripTags(), false);
}
public static void Clear()
{
List<TeleportRequest> list = Pool.Get<List<TeleportRequest>>();
list.AddRange(m_OutgoingRequests.Values);
foreach (TeleportRequest teleportRequest in list)
teleportRequest.RequestCancelled();
Pool.FreeUnmanaged(ref list);
m_OutgoingRequests.Clear();
m_IncomingRequests.Clear();
m_PopupAction = null;
}
public static bool HasIncomingRequest(BasePlayer target, out TeleportRequest teleportRequest) => m_IncomingRequests.TryGetValue(target.userID, out teleportRequest);
public static bool HasOutgoingRequest(BasePlayer player, out TeleportRequest teleportRequest) => m_OutgoingRequests.TryGetValue(player.userID, out teleportRequest);
public static bool HasPendingRequest(BasePlayer player) => m_IncomingRequests.ContainsKey(player.userID) || m_OutgoingRequests.ContainsKey(player.userID);
private void TimerTick()
{
if (m_Time == 0)
{
RequestTimeOut();
return;
}
if (!m_To || !m_To.IsConnected || !m_From || !m_From.IsConnected)
{
RequestCancelled();
return;
}
m_Time--;
}
public void Accept() => RequestAccepted();
public void Decline() => RequestDeclined();
public void Cancel() => RequestCancelled();
public bool CanCancel(BasePlayer player) => true;
public void RequestAccepted()
{
int delay = Configuration.TP.Delay.GetLowestOption(m_From);
if (m_IsInstant)
{
SendReply(m_From, "TPRequest.Instant.Sent", m_To.displayName, delay);
SendReply(m_To, "TPRequest.Instant.Received", m_From.displayName, delay);
}
else
{
SendReply(m_From, "TPRequest.To.Accepted", m_To.displayName, delay);
SendReply(m_To, "TPRequest.From.Accepted", m_From.displayName, delay);
}
PlayerTeleporter.Create(m_From, m_To, delay, m_IsPaying, m_TpHere);
Destroy();
}
public void RequestDeclined()
{
if (m_From)
{
if (m_TpHere)
SendReply(m_From, "TPRequest.Here.To.Denied", m_To.displayName);
else SendReply(m_From, "TPRequest.To.Denied", m_To.displayName);
if (m_IsPaying)
RefundPayment(m_From, Mode.Teleport);
}
if (m_To)
{
if (m_TpHere)
SendReply(m_To, "TPRequest.Here.From.Denied", m_From.displayName);
else SendReply(m_To, "TPRequest.From.Denied", m_From.displayName);
}
Destroy();
}
public void RequestCancelled()
{
if (m_From != null)
{
SendReply(m_From, "TPRequest.To.Cancelled", m_To.displayName);
if (m_IsPaying)
RefundPayment(m_From, Mode.Teleport);
}
if (m_To != null)
{
SendReply(m_To, "TPRequest.From.Cancelled", m_From.displayName);
}
Destroy();
}
private void RequestTimeOut()
{
if (m_From)
{
if (m_To)
{
if (m_TpHere)
{
SendReply(m_From, "TPRequest.Here.To.TimeOut", m_To.displayName);
SendReply(m_To, "TPRequest.Here.From.TimeOut", m_From.displayName);
}
else
{
SendReply(m_From, "TPRequest.To.TimeOut", m_To.displayName);
SendReply(m_To, "TPRequest.From.TimeOut", m_From.displayName);
}
}
if (m_IsPaying)
RefundPayment(m_From, Mode.Teleport);
}
Destroy();
}
private void Destroy()
{
m_Timer?.Destroy();
IsValid = false;
m_OutgoingRequests.Remove(m_FromID);
m_IncomingRequests.Remove(m_ToID);
if (m_From)
ChaosUI.Destroy(m_From, TPR_POPUP);
if (m_To)
ChaosUI.Destroy(m_To, TPR_POPUP);
TeleportRequest teleportRequest = this;
Pool.Free(ref teleportRequest);
}
public void EnterPool()
{
m_From = null;
m_To = null;
m_FromID = 0UL;
m_ToID = 0UL;
m_IsInstant = false;
}
public void LeavePool(){}
}
private class PlayerTeleporter : Pool.IPooled, ITeleport
{
private BasePlayer m_From;
private BasePlayer m_To;
private ulong m_FromID;
private ulong m_ToID;
private int m_TimeUntilTP;
private bool m_IsPaying;
private bool m_TPHere;
private Timer m_Timer;
public int TimeRemaining => m_TimeUntilTP;
public bool CanAccept => false;
public bool IsValid { get; set; }
private static Hash<ulong, PlayerTeleporter> m_Outgoing = new Hash<ulong, PlayerTeleporter>();
private static Hash<ulong, PlayerTeleporter> m_Incoming = new Hash<ulong, PlayerTeleporter>();
public static Action<BasePlayer, string, ITeleport, string, string, bool> m_PopupAction;
public static void Create(BasePlayer from, BasePlayer to, int delay, bool isPaying, bool tpHere)
{
PlayerTeleporter teleporter = Pool.Get<PlayerTeleporter>();
teleporter.m_From = from;
teleporter.m_FromID = from.userID;
teleporter.m_To = to;
teleporter.m_ToID = to.userID;
teleporter.m_TimeUntilTP = delay;
teleporter.m_IsPaying = isPaying;
teleporter.m_TPHere = tpHere;
teleporter.IsValid = true;
m_Outgoing[from.userID] = teleporter;
m_Incoming[to.userID] = teleporter;
teleporter.m_Timer = m_CreateTimer(1f, teleporter.TimerTick);
if (delay > 0)
{
m_PopupAction(to, TPP_POPUP, teleporter, "Popup.Incoming.TP", from.displayName.StripTags(), true);
m_PopupAction(from, TPP_POPUP, teleporter, "Popup.Outgoing.TP", to.displayName.StripTags(), false);
}
}
public static void Clear()
{
List<PlayerTeleporter> list = Pool.Get<List<PlayerTeleporter>>();
list.AddRange(m_Outgoing.Values);
foreach (PlayerTeleporter teleporter in list)
teleporter.CancelTeleport(true);
Pool.FreeUnmanaged(ref list);
m_Outgoing.Clear();
m_Incoming.Clear();
m_PopupAction = null;
}
public static bool HasIncomingPending(BasePlayer target, out PlayerTeleporter teleporter) => m_Incoming.TryGetValue(target.userID, out teleporter);
public static bool HasOutgoingPending(BasePlayer player, out PlayerTeleporter teleporter) => m_Outgoing.TryGetValue(player.userID, out teleporter);
public static bool IsWaiting(BasePlayer player) => m_Incoming.ContainsKey(player.userID) || m_Outgoing.ContainsKey(player.userID);
private void TimerTick()
{
if (!m_To || !m_To.IsConnected || !m_From || !m_From.IsConnected)
{
CancelTeleport(true, "Reason.Disconnected");
return;
}
if (m_TimeUntilTP == 0)
{
Teleport();
return;
}
m_TimeUntilTP--;
}
private void Teleport()
{
if (m_To.IsDead())
{
SendReply(m_From, "TP.Error.TargetDead");
CancelTeleport(false);
return;
}
if (!Configuration.Conditions.MeetsConditions(m_From, m_To))
{
CancelTeleport(false);
return;
}
if (m_TPHere)
TeleportGUI.Teleport(m_To, m_From);
else TeleportGUI.Teleport(m_From, m_To);
TeleportData.User userData = GetOrCreateUserSettings(m_From);
int cooldown = Configuration.TP.Cooldown.GetLowestOption(m_From);
userData.TPUsage.Cooldown = CurrentTime() + cooldown;
if (Configuration.TP.Limits.Default > 0)
{
int dailyLimit = Configuration.TP.Limits.GetHighestOption(m_From);
if (dailyLimit > 0)
{
if (userData.TPUsage.UsesToday < dailyLimit)
SendReply(m_From, "Notification.TP.Remaining", dailyLimit - userData.TPUsage.UsesToday - 1);
userData.TPUsage.UsesToday++;
}
}
Destroy();
}
public void Accept(){}
public void Decline() => CancelTeleport(true, "Reason.Declined");
public void Cancel() => CancelTeleport(true);
public bool CanCancel(BasePlayer player) => player == m_From && player.HasPermission(PERMISSION_TP_CANCEL);
public void CancelTeleport(bool notify, string reason = "")
{
if (notify)
{
if (!string.IsNullOrEmpty(reason))
{
SendReply(m_From, "TP.To.Cancelled.Reason", m_To.displayName, GetTranslatedString(reason, m_From));
SendReply(m_To, "TP.From.Cancelled.Reason", m_From.displayName, GetTranslatedString(reason, m_To));
}
else
{
SendReply(m_From, "TP.To.Cancelled", m_To.displayName);
SendReply(m_To, "TP.From.Cancelled", m_From.displayName);
}
}
if (m_IsPaying)
RefundPayment(m_From, Mode.Teleport);
Destroy();
}
private void Destroy()
{
if (m_From)
ChaosUI.Destroy(m_From, TPP_POPUP);
if (m_To)
ChaosUI.Destroy(m_To, TPP_POPUP);
m_Timer?.Destroy();
IsValid = false;
m_Outgoing.Remove(m_FromID);
m_Incoming.Remove(m_ToID);
PlayerTeleporter teleporter = this;
Pool.Free(ref teleporter);
}
public void EnterPool()
{
m_From = null;
m_To = null;
m_FromID = 0UL;
m_ToID = 0UL;
}
public void LeavePool(){}
}
private class PositionTeleporter : Pool.IPooled, ITeleport
{
private BasePlayer m_From;
private ulong m_FromID;
private Vector3 m_To;
private string m_ToName;
private int m_TimeUntilTP;
private bool m_IsPaying;
private Timer m_Timer;
public int TimeRemaining => m_TimeUntilTP;
public bool CanAccept => false;
public bool IsValid { get; set; }
public bool IsHomeTeleport { get; private set; }
public bool IsTPBack;
private static Hash<ulong, PositionTeleporter> m_Pending = new Hash<ulong, PositionTeleporter>();
public static Action<BasePlayer, string, ITeleport, string, string, bool> m_PopupAction;
public static void Create(BasePlayer from, Vector3 to, string homeName, int delay, bool isPaying, bool isHome)
{
PositionTeleporter teleporter = Pool.Get<PositionTeleporter>();
teleporter.m_From = from;
teleporter.m_FromID = from.userID;
teleporter.m_To = to;
teleporter.m_ToName = homeName;
teleporter.m_TimeUntilTP = delay;
teleporter.m_IsPaying = isPaying;
teleporter.IsValid = true;
teleporter.IsHomeTeleport = isHome;
m_Pending[from.userID] = teleporter;
teleporter.m_Timer = m_CreateTimer(1f, teleporter.TimerTick);
if (delay > 0)
m_PopupAction(from, TPP_POPUP, teleporter, isHome ? "Popup.Outgoing.TP.Home" : "Popup.Outgoing.TP.Warp", homeName, false);
}
public static void Clear()
{
List<PositionTeleporter> list = Pool.Get<List<PositionTeleporter>>();
list.AddRange(m_Pending.Values);
foreach (PositionTeleporter teleporter in list)
teleporter.CancelTeleport(true);
Pool.FreeUnmanaged(ref list);
m_Pending.Clear();
m_PopupAction = null;
}
public static bool IsWaiting(BasePlayer player, out PositionTeleporter teleporter) => m_Pending.TryGetValue(player.userID, out teleporter);
public static bool IsWaiting(BasePlayer player, out bool isHomeTeleport)
{
if (m_Pending.TryGetValue(player.userID, out PositionTeleporter positionTeleporter))
{
isHomeTeleport = positionTeleporter.IsHomeTeleport;
return true;
}
isHomeTeleport = false;
return false;
}
private void TimerTick()
{
if (!m_From || !m_From.IsConnected)
{
CancelTeleport(true, "Reason.Disconnected");
return;
}
if (m_TimeUntilTP == 0)
{
Teleport();
return;
}
m_TimeUntilTP--;
}
private void Teleport()
{
if ((IsHomeTeleport && !Configuration.Conditions.MeetsConditions(m_From, m_To)) || (!IsHomeTeleport && !Configuration.Conditions.MeetsWarpConditions(m_From, m_To)))
{
CancelTeleport(false);
return;
}
if (IsHomeTeleport)
m_LastHome[m_From.userID] = m_From.transform.position;
else m_LastWarp[m_From.userID] = m_From.transform.position;
TeleportGUI.Teleport(m_From, m_To);
TeleportData.User userData = GetOrCreateUserSettings(m_From);
int cooldown = IsHomeTeleport ? Configuration.Home.Cooldown.GetLowestOption(m_From) :
Configuration.Warp.Cooldown.GetLowestOption(m_From);
TeleportData.User.Usage usage = IsHomeTeleport ? userData.HomeUsage : userData.WarpUsage;
ConfigData.LimitOptions limits = IsHomeTeleport ? Configuration.Home.Limits : Configuration.Warp.Limits;
usage.Cooldown = CurrentTime() + cooldown;
if (limits.Default > 0)
{
int dailyLimit = limits.GetHighestOption(m_From);
if (dailyLimit > 0)
{
if (usage.UsesToday < dailyLimit)
SendReply(m_From, IsHomeTeleport ? "Notification.Home.Remaining" : "Notification.Warp.Remaining", dailyLimit - usage.UsesToday - 1);
usage.UsesToday++;
}
}
Destroy();
}
public void Accept(){}
public void Decline(){}
public void Cancel() => CancelTeleport(true);
public bool CanCancel(BasePlayer player) => player == m_From;
public void CancelTeleport(bool notify, string reason = "")
{
if (notify)
{
if (!string.IsNullOrEmpty(reason))
SendReply(m_From, "TP.To.Cancelled", m_ToName, GetTranslatedString(reason, m_From));
else SendReply(m_From, "TP.To.Cancelled", m_ToName);
}
if (m_IsPaying)
RefundPayment(m_From, IsHomeTeleport ? Mode.Home : Mode.Warp);
Destroy();
}
private void Destroy()
{
if (m_From)
ChaosUI.Destroy(m_From, TPP_POPUP);
m_Timer?.Destroy();
IsValid = false;
m_Pending.Remove(m_FromID);
PositionTeleporter teleporter = this;
Pool.Free(ref teleporter);
}
public void EnterPool()
{
m_From = null;
m_To = Vector3.zero;
m_FromID = 0UL;
m_ToName = string.Empty;
}
public void LeavePool(){}
}
#endregion
#region TP Functions
private void TPR(BasePlayer player, BasePlayer targetPlayer, bool tpHere)
{
if (IsAdmin(player) && Configuration.Admin.Instant)
{
if (!Configuration.Admin.Silent)
SendReply(targetPlayer, tpHere ? "TPR.Admin.YouWereTPTo" : "TPR.Admin.TPTo", player.displayName);
SendReply(player, tpHere ? "TPR.Admin.YouTPToYou" : "TPR.YouTPTo", targetPlayer.displayName);
if (tpHere)
Teleport(targetPlayer, player);
else Teleport(player, targetPlayer);
return;
}
if (TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player))
{
SendReply(player, "TP.SelfHasPendingRequest");
return;
}
if (TeleportRequest.HasPendingRequest(targetPlayer) || PlayerTeleporter.IsWaiting(targetPlayer))
{
SendReply(player, "TP.TargetPendingRequest", targetPlayer.displayName);
return;
}
if (PositionTeleporter.IsWaiting(player, out bool isHomeTeleport))
{
SendReply(player, isHomeTeleport ? "Home.SelfHasPendingRequest" : "Warp.SelfHasPendingRequest");
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
if (userData.TPUsage.IsOnCooldown())
{
SendReply(player, "TP.CooldownFormat", FormatTime(userData.TPUsage.Cooldown - CurrentTime()));
return;
}
if (!Configuration.Conditions.MeetsConditions(player, targetPlayer))
return;
bool playerIsPaying = false;
if (HasReachedDailyLimit(player, userData, Mode.Teleport))
{
if (!Configuration.TP.Purchase.PayAfterUsingDailyLimits)
{
SendReply(player, "TP.MaxTeleportsReached");
return;
}
if (!PayForTeleport(player, Mode.Teleport))
return;
playerIsPaying = true;
}
TeleportRequest.Create(player, targetPlayer, Configuration.TP.RequestTimeout, tpHere, playerIsPaying);
}
private void TPC(BasePlayer player)
{
if (PlayerTeleporter.HasOutgoingPending(player, out PlayerTeleporter teleporter))
{
teleporter.CancelTeleport(true);
return;
}
object call = Interface.Oxide.CallHook("CancelAllTeleports", player);
if (call is string)
{
SendReply(player, call as string);
return;
}
SendReply(player, "TP.Error.NoPending");
}
private void TPB(BasePlayer player)
{
if (!m_LastTeleport.TryGetValue(player.userID, out Vector3 position))
{
SendReply(player, "TPB.NoBackLocation");
return;
}
if (!IsAdmin(player))
{
if (TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player))
{
SendReply(player, "TP.SelfHasPendingRequest");
return;
}
if (PositionTeleporter.IsWaiting(player, out bool isHomeTeleport))
{
SendReply(player, isHomeTeleport ? "Home.SelfHasPendingRequest" : "Warp.SelfHasPendingRequest");
return;
}
if (IsInFoundation(position))
{
SendReply(player, "General.InsideFoundation");
return;
}
if (!Configuration.Conditions.MeetsConditions(player, position))
return;
}
Teleport(player, position);
SendReply(player, "TPB.Success");
}
private void TPHere(BasePlayer player, BasePlayer target)
{
if (!player.HasPermission(PERMISSION_TP_HERE))
{
SendReply(player, "General.NoPermission");
return;
}
if (player == target)
{
SendReply(player, "TPH.CantTeleportSelf");
return;
}
if (IsAdmin(player) && Configuration.Admin.Instant)
{
if (!Configuration.Admin.Silent)
SendReply(player, "TPR.Admin.YouWereTPTo", player.displayName);
SendReply(player, "TPR.Admin.TPTo", target.displayName);
Teleport(target, player);
return;
}
TPR(player, target, true);
}
#endregion
#region Home Functions
private int GetMaxHomesForPlayer(BasePlayer player)
{
if (Configuration.Home.MaxHomes.Default > 0)
{
int maxHomes = Configuration.Home.MaxHomes.GetHighestOption(player);
if (maxHomes > 0)
return maxHomes;
}
return Configuration.Home.MaxHomes.Default;
}
private bool HasMaximumHomes(BasePlayer player, TeleportData.User userData)
{
int maxHomes = GetMaxHomesForPlayer(player);
if (maxHomes == 0)
return true;
int homesCount = userData != null ? userData.Homes.Count : 0;
return maxHomes > homesCount;
}
private bool CanSetHome(BasePlayer player)
{
if (!Configuration.Home.AllowSetHomeInBuildBlocked && !player.CanBuild())
{
SendReply(player, "Home.Error.IsBuildingBlocked");
return false;
}
m_TeleportData.Data.Users.TryGetValue(player.userID, out TeleportData.User userData);
if (!HasMaximumHomes(player, userData))
{
SendReply(player, "Home.Error.LimitReached");
return false;
}
if (Configuration.Home.MinimumHomeRadiusDistance > 0)
{
if (userData != null)
{
foreach (TeleportData.User.HomePoint home in userData.Homes.Values)
{
float distance = Vector3.Distance(player.transform.position, home.Position);
if (distance <= Configuration.Home.MinimumHomeRadiusDistance)
{
SendReply(player, "Home.Error.NearbyRadius",
distance.ToString("N1"),
Configuration.Home.MinimumHomeRadiusDistance.ToString("N1"));
return false;
}
}
}
}
if (Configuration.Home.MustSetHomeOnBuilding)
{
if (!Configuration.Home.CanSetHomeOnFloor)
{
if (!CheckFoundation(player.transform.position))
{
SendReply(player, "Home.Error.NotOnFoundation");
return false;
}
}
else
{
if (!CheckFoundation(player.transform.position) && !CheckFloor(player.transform.position))
{
SendReply(player, "Home.Error.NotOnFoundationFloor");
return false;
}
}
}
if (ZoneManager.IsLoaded)
{
if (ZoneManager.PlayerHasFlag(player, "notp"))
{
SendReply(player, "Home.Error.NoTPZone");
return false;
}
}
return true;
}
private bool IsHomePointValid(TeleportData.User.HomePoint homePoint)
{
if (homePoint.EntityID != 0U)
return true;
if (Configuration.Home.MustSetHomeOnBuilding)
{
if (!CheckFoundation(homePoint.Position) && !CheckFloor(homePoint.Position))
return false;
}
return true;
}
private bool CanTeleportHome(BasePlayer player, Vector3 position, out bool playerIsPaying)
{
playerIsPaying = false;
if (TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player))
{
SendReply(player, "TP.SelfHasPendingRequest");
return false;
}
if (PositionTeleporter.IsWaiting(player, out bool isHomeTeleport))
{
SendReply(player, isHomeTeleport ? "Home.SelfHasPendingRequest" : "Warp.SelfHasPendingRequest");
return false;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
if (userData.HomeUsage.IsOnCooldown())
{
SendReply(player, "Home.CooldownFormat", FormatTime(userData.HomeUsage.Cooldown - CurrentTime()));
return false;
}
if (!Configuration.Conditions.MeetsConditions(player, position))
return false;
if (HasReachedDailyLimit(player, userData, Mode.Home))
{
if (!Configuration.Home.Purchase.PayAfterUsingDailyLimits)
{
SendReply(player, "Home.MaxTeleportsReached");
return false;
}
if (!PayForTeleport(player, Mode.Home))
return false;
playerIsPaying = true;
}
return true;
}
private void HomeBack(BasePlayer player)
{
if (!m_LastHome.TryGetValue(player.userID, out Vector3 position))
{
SendReply(player, "Home.Error.NoBackLocation");
return;
}
Teleport(player, position);
SendReply(player, "TPB.Success");
}
private void HomeBackLimited(BasePlayer player)
{
if (!m_LastHome.TryGetValue(player.userID, out Vector3 position))
{
SendReply(player, "Home.Error.NoBackLocation");
return;
}
if (!CanTeleportHome(player, position, out bool playerIsPaying))
return;
PositionTeleporter.Create(player, position, "Previous location", Configuration.Home.Delay.GetLowestOption(player), playerIsPaying, true);
}
#endregion
#region Warp Functions
private void WarpTo(BasePlayer player, string warpName)
{
MonumentWarpPoint monumentWarpPoint = null;
if (!m_WarpData.Data.TryGetValue(warpName, out WarpPoint warpPoint) && !m_MonumentWarps.TryGetValue(warpName, out monumentWarpPoint))
{
SendReply(player, "Warp.Error.DoesntExist", warpName);
return;
}
IWarpPoint iWarpPoint = (IWarpPoint)warpPoint ?? (IWarpPoint)monumentWarpPoint;
if (!iWarpPoint.HasPermission(player))
{
SendReply(player, "Warp.Error.NoPermission");
return;
}
Vector3 position = iWarpPoint.GetPosition();
if (Configuration.Admin.Instant && player.IsAdmin)
{
Teleport(player, position);
ChaosUI.Destroy(player, TPUI);
return;
}
if (!CanWarpTo(player, position, out bool playerIsPaying))
return;
ChaosUI.Destroy(player, TPUI);
PositionTeleporter.Create(player, position, warpName, Configuration.Warp.Delay.GetLowestOption(player), playerIsPaying, false);
}
private bool CanWarpTo(BasePlayer player, Vector3 position, out bool playerIsPaying)
{
playerIsPaying = false;
if (TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player))
{
SendReply(player, "TP.SelfHasPendingRequest");
return false;
}
if (PositionTeleporter.IsWaiting(player, out bool isHomeTeleport))
{
SendReply(player, isHomeTeleport ? "Home.SelfHasPendingRequest" : "Warp.SelfHasPendingRequest");
return false;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
if (userData.WarpUsage.IsOnCooldown())
{
SendReply(player, "Warp.CooldownFormat", FormatTime(userData.WarpUsage.Cooldown - CurrentTime()));
return false;
}
if (!Configuration.Conditions.MeetsWarpConditions(player, position))
return false;
if (HasReachedDailyLimit(player, userData, Mode.Warp))
{
if (!Configuration.Warp.Purchase.PayAfterUsingDailyLimits)
{
SendReply(player, "Warp.MaxTeleportsReached");
return false;
}
if (!PayForTeleport(player, Mode.Warp))
return false;
playerIsPaying = true;
}
return true;
}
#endregion
#region Chat Commands
#region Locations
[ChatCommand("tpsave")]
void TPSaveCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_LOCATION))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length == 0)
{
SendReply(player, "TPL.NoNameSpecified");
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
userData.Locations[args[0]] = player.transform.position;
SendReply(player, "TPL.Success", args[0]);
}
[ChatCommand("tpl")]
void TPLCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_LOCATION))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length == 0)
{
SendReply(player, "TPL.NoNameSpecified");
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
if (userData.Locations.Count == 0)
{
SendReply(player, "TPL.Error.NoLocations");
return;
}
if (!userData.Locations.TryGetValue(args[0], out Vector3 position))
{
SendReply(player, "TPL.Error.NoLocation.Name", args[0]);
return;
}
Teleport(player, position);
}
[ChatCommand("tpllist")]
void TPLListCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_LOCATION))
{
SendReply(player, "General.NoPermission");
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
if (userData.Locations.Count == 0)
{
SendReply(player, "TPL.Error.NoLocations");
return;
}
SendReply(player, "TPL.List", userData.Locations.Keys.ToSentence());
}
#endregion
#region TP
[ChatCommand("tp")]
private void TPCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (IsAdmin(player) && args.Length > 0)
{
if (args.Length == 1)
{
List<BasePlayer> matches = FindPlayer(args[0], true);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", args[0]);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", args[0], matches.Select(p => p.displayName).ToSentence());
return;
}
BasePlayer targetPlayer = matches[0];
Teleport(player, targetPlayer.transform.position);
SendReply(player, "TPR.YouTPTo", targetPlayer.displayName);
return;
}
if (args.Length == 2)
{
List<BasePlayer> matches = FindPlayer(args[0], true);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", args[0]);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", args[0], matches.Select(p => p.displayName).ToSentence());
return;
}
BasePlayer sender = matches[0];
matches = FindPlayer(args[1], true);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", args[1]);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", args[1], matches.Select(p => p.displayName).ToSentence());
return;
}
BasePlayer targetPlayer = matches[0];
Teleport(sender, targetPlayer.transform.position);
SendReply(sender, "TPR.YouTPTo", targetPlayer.displayName);
SendReply(targetPlayer, "TPR.Admin.TPTo", sender.displayName);
return;
}
if (args.Length < 3)
{
SendReply(player, "TP.Error.PositionSyntax");
return;
}
if (!float.TryParse(args[0], out float x) || !float.TryParse(args[1], out float y) || !float.TryParse(args[2], out float z))
{
SendReply(player, "TP.Error.PositionSyntax");
return;
}
Teleport(player, new Vector3(x, y, z));
SendReply(player, "TP.Success.Position", x.ToString("N1"), y.ToString("N1"), z.ToString("N1"));
return;
}
if (!Configuration.UI.DisableUI)
ShowTeleportUI(player, Mode.Teleport);
else
{
if (!player.HasPermission(PERMISSION_TP_LOCATION))
{
SendReply(player, "Help.TP.1"); // /tpsave <name> - Save your current position as a TP location
SendReply(player, "Help.TP.2"); // /tpl <name> - Teleport to the specified TP location
SendReply(player, "Help.TP.3"); // /tplist - List all of your TP locations
SendReply(player, "Help.TP.4"); // /tpr <playername> - Request a teleport to the specified player
SendReply(player, "Help.TP.5"); // /tpa - Accept an incoming TP request
SendReply(player, "Help.TP.6"); // /tpd - Decline an incoming TP request
SendReply(player, "Help.TP.7"); // /tpc - Cancel a pending TP request
if (player.HasPermission(PERMISSION_TP_BACK))
SendReply(player, "Help.TP.8"); // /tpb - Teleport back to your previous location
if (player.HasPermission(PERMISSION_TP_HERE))
SendReply(player, "Help.TP.9"); // /tprhere <playername> - Request a teleport that brings the specified player to you
}
}
}
[ChatCommand("tpr")]
private void TPRCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (args == null || args.Length < 1)
{
SendReply(player, "TPR.InvalidSynax");
return;
}
string name = string.Join(" ", args);
List<BasePlayer> matches = FindPlayer(name);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", name);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", name, matches.Select(x => x.displayName).ToSentence());
return;
}
BasePlayer targetPlayer = matches[0];
if (targetPlayer == player)
{
SendReply(player, "TP.CantTeleportSelf");
return;
}
TPR(player, targetPlayer, false);
}
[ChatCommand("tpa")]
private void TPACommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (!TeleportRequest.HasIncomingRequest(player, out TeleportRequest teleportRequest))
{
SendReply(player, "TPA.NonePending");
return;
}
teleportRequest.RequestAccepted();
}
[ChatCommand("tpd")]
private void TPDCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (!TeleportRequest.HasIncomingRequest(player, out TeleportRequest teleportRequest))
{
SendReply(player, "TPA.NonePending");
return;
}
teleportRequest.RequestDeclined();
}
[ChatCommand("tpc")]
private void TPCCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_CANCEL))
{
SendReply(player, "General.NoPermission");
return;
}
TPC(player);
}
[ChatCommand("tpb")]
private void TPBCommand(BasePlayer player, string command, string[] args)
{
if (player.HasPermission(PERMISSION_TP_BACK_ADMIN) && args != null && args.Length > 0)
{
string name = args[0];
List<BasePlayer> matches = FindPlayer(name);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", name);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", name, matches.Select(x => x.displayName).ToSentence());
return;
}
BasePlayer targetPlayer = matches[0];
if (!m_LastTeleport.TryGetValue(targetPlayer.userID, out Vector3 position))
{
SendReply(player, "TPB.Admin.NoBackLocation", targetPlayer.displayName);
return;
}
Teleport(targetPlayer, position);
SendReply(targetPlayer, "TPB.Admin.Success.Target");
SendReply(player, "TPB.Admin.Success", targetPlayer.displayName);
return;
}
if (!player.HasPermission(PERMISSION_TP_BACK))
{
SendReply(player, "General.NoPermission");
return;
}
TPB(player);
}
[ChatCommand("tprhere")]
private void TPRHereCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_TP_HERE))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length < 1)
{
SendReply(player, "TPR.Here.InvalidSynax");
return;
}
string targetName = string.Join(" ", args);
List<BasePlayer> matches = FindPlayer(targetName);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", targetName);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", targetName, matches.Select(x => x.displayName).ToSentence());
return;
}
TPHere(player, matches[0]);
}
#endregion
#region Homes
[ChatCommand("home")]
private void HomeCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_HOME_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length > 0)
{
string option = args[0].ToLower();
/* Support for NTeleportation format: /home add/remove name */
if (option == "add")
{
SetHomeCommand(player, "sethome", args.Skip(1).ToArray());
return;
}
if (option == "remove")
{
DeleteHomeCommand(player, "delhome", args.Skip(1).ToArray());
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
string homeName = string.Join(" ", args);
if (!userData.Homes.TryGetValue(homeName, out TeleportData.User.HomePoint homePoint))
{
SendReply(player, "Home.Error.DoesntExist", homeName);
return;
}
if (!IsHomePointValid(homePoint))
{
userData.Homes.Remove(homeName);
SendReply(player, "Home.Error.Invalid", homeName);
return;
}
if (!CanTeleportHome(player, homePoint.Position, out bool playerIsPaying))
return;
PositionTeleporter.Create(player, homePoint.Position, homeName, Configuration.Home.Delay.GetLowestOption(player), playerIsPaying, true);
return;
}
if (!Configuration.UI.DisableUI)
ShowTeleportUI(player, Mode.Home);
else
{
SendReply(player, "Help.Homes.1"); // /home <homename> - Teleport to the specified home
SendReply(player, "Help.Homes.2"); // /sethome <homename> - Create a home on your current position
SendReply(player, "Help.Homes.3"); // /delhome <homename> - Delete the home with the specified name
SendReply(player, "Help.Homes.4"); // /listhomes - List all of your home names
if (player.HasPermission(PERMISSION_HOME_BYPASS) || player.HasPermission(PERMISSION_HOME_BACK))
SendReply(player, "Help.Homes.5"); // /homec - Cancel a pending home teleport
}
}
[ChatCommand("sethome")]
private void SetHomeCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_HOME_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (Configuration.Home.SleepingBags.DisableSetHomeCommand)
{
SendReply(player, "Home.Error.BagDisableCommand");
return;
}
if (args.Length < 1)
{
SendReply(player, "Home.Error.SetHomeSyntax");
return;
}
TeleportData.User userData = GetOrCreateUserSettings(player);
string homeName = string.Join(" ", args);
if (userData.Homes.ContainsKey(homeName))
{
SendReply(player, "Home.Error.AlreadyExists", homeName);
return;
}
if (!CanSetHome(player))
return;
userData.Homes.Add(homeName, new TeleportData.User.HomePoint{ Position = player.transform.position });
int maxHomes = GetMaxHomesForPlayer(player);
if (maxHomes == 0)
SendReply(player, "Home.Success.Created", homeName);
else SendReply(player, "Home.Success.Created.Remaining", homeName, maxHomes - userData.Homes.Count);
}
[ChatCommand("delhome")]
private void DeleteHomeCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_HOME_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length < 1)
{
SendReply(player, "Home.Error.DelHomeSyntax");
return;
}
TeleportData.User userData;
if (args.Length > 1 && player.HasPermission(PERMISSION_HOME_DELETE_OTHER_HOMES))
{
List<BasePlayer> targets = FindPlayer(args[0], true);
if (targets.Count == 0)
{
SendReply(player, "General.PlayerNotFound", args[0]);
return;
}
if (targets.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", args[0], targets.Select(x => x.displayName).ToSentence());
return;
}
string targetHomeName = string.Join(" ", args.Skip(1));
BasePlayer target = targets[0];
if (!m_TeleportData.Data.Users.TryGetValue(target.userID, out userData) || userData.Homes.Count == 0)
{
SendReply(player, "Home.Error.NoHomes.Target", target.displayName);
return;
}
if (!userData.Homes.ContainsKey(targetHomeName))
{
SendReply(player, "Home.Error.DoesntExist.Target", target.displayName, targetHomeName);
return;
}
userData.Homes.Remove(targetHomeName);
SendReply(player, "Home.Success.Deleted", targetHomeName);
return;
}
if (!m_TeleportData.Data.Users.TryGetValue(player.userID, out userData) || userData.Homes.Count == 0)
{
SendReply(player, "Home.Error.NoHomes");
return;
}
string homeName = string.Join(" ", args);
if (!userData.Homes.ContainsKey(homeName))
{
SendReply(player, "Home.Error.DoesntExist", homeName);
return;
}
userData.Homes.Remove(homeName);
SendReply(player, "Home.Success.Deleted", homeName);
}
[ChatCommand("listhomes")]
private void ListHomesCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_HOME_USE))
{
SendReply(player, "General.NoPermission");
return;
}
TeleportData.User userData;
if (args.Length > 0)
{
if (!player.HasPermission(PERMISSION_HOME_VIEW_OTHER_HOMES))
{
SendReply(player, "General.NoPermission");
return;
}
string playerName = string.Join(" ", args);
List<BasePlayer> targets = FindPlayer(playerName, true);
if (targets.Count == 0)
{
SendReply(player, "General.PlayerNotFound", playerName);
return;
}
if (targets.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", playerName, targets.Select(x => x.displayName).ToSentence());
return;
}
BasePlayer target = targets[0];
if (!m_TeleportData.Data.Users.TryGetValue(target.userID, out userData) || userData.Homes.Count == 0)
{
SendReply(player, "Home.Error.NoHomes.Target", target.displayName);
return;
}
SendReply(player, "Home.List.Target", target.displayName, string.Join(", ", userData.Homes.Keys));
return;
}
if (!m_TeleportData.Data.Users.TryGetValue(player.userID, out userData) || userData.Homes.Count == 0)
{
SendReply(player, "Home.Error.NoHomes");
return;
}
SendReply(player, "Home.List", string.Join(", ", userData.Homes.Keys));
}
[ChatCommand("homec")]
private void HomeCancelCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_HOME_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (!PositionTeleporter.IsWaiting(player, out PositionTeleporter positionTeleporter))
{
SendReply(player, "Home.Error.NoPending");
return;
}
positionTeleporter.CancelTeleport(true);
}
[ChatCommand("homeback")]
private void HomeBackCommand(BasePlayer player, string command, string[] args)
{
if (player.HasPermission(PERMISSION_HOME_BACK_ADMIN) && args != null && args.Length > 0)
{
string name = args[0];
List<BasePlayer> matches = FindPlayer(name);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", name);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", name, matches.Select(x => x.displayName).ToSentence());
return;
}
BasePlayer targetPlayer = matches[0];
if (!m_LastHome.TryGetValue(targetPlayer.userID, out Vector3 position))
{
SendReply(player, "TPB.Admin.NoBackLocation", targetPlayer.displayName);
return;
}
Teleport(targetPlayer, position);
SendReply(targetPlayer, "TPB.Admin.Success.Target");
SendReply(player, "TPB.Admin.Success", targetPlayer.displayName);
return;
}
if (player.HasPermission(PERMISSION_HOME_BYPASS))
{
HomeBack(player);
return;
}
if (player.HasPermission(PERMISSION_HOME_BACK))
{
HomeBackLimited(player);
return;
}
SendReply(player, "General.NoPermission");
}
#endregion
#region Warp
[ChatCommand("warp")]
private void WarpCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_WARP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length == 0)
{
if (!Configuration.UI.DisableUI)
ShowTeleportUI(player, Mode.Warp);
else
{
SendReply(player, "Help.Warp.1"); // /warp to <warpname> - Teleport to the specified warp point
SendReply(player, "Help.Warp.2"); // /warp list - Show available warp points
SendReply(player, "Help.Warp.3"); // /warpadd <warpname> - Add a new warp point on your current position
SendReply(player, "Help.Warp.4"); // /warpremove <warpname> - Delete the specified warp point
}
return;
}
if (args[0] == "to")
{
string warpName = string.Join(" ", args.Skip(1).ToArray());
WarpTo(player, warpName);
return;
}
if (args[0] == "list")
{
List<string> allowedWarps = Pool.Get<List<string>>();
foreach (KeyValuePair<string, WarpPoint> kvp in m_WarpData.Data)
{
if (!string.IsNullOrEmpty(kvp.Value.Permission) && !player.HasPermission(kvp.Value.Permission))
continue;
allowedWarps.Add(kvp.Key);
}
SendReply(player, "WarpTo.List", allowedWarps.ToSentence());
Pool.FreeUnmanaged(ref allowedWarps);
return;
}
SendReply(player, "WarpTo.Error.Syntax");
}
[ChatCommand("warpback")]
private void WarpBackCommand(BasePlayer player, string command, string[] args)
{
if (player.HasPermission(PERMISSION_WARP_BACK_ADMIN) && args != null && args.Length > 0)
{
string name = args[0];
List<BasePlayer> matches = FindPlayer(name);
if (matches.Count == 0)
{
SendReply(player, "General.PlayerNotFound", name);
return;
}
if (matches.Count > 1)
{
SendReply(player, "General.MultiplePlayersFound", name, matches.Select(x => x.displayName).ToSentence());
return;
}
BasePlayer targetPlayer = matches[0];
if (!m_LastWarp.TryGetValue(targetPlayer.userID, out Vector3 position))
{
SendReply(player, "TPB.Admin.NoBackLocation", targetPlayer.displayName);
return;
}
Teleport(targetPlayer, position);
SendReply(targetPlayer, "TPB.Admin.Success.Target");
SendReply(player, "TPB.Admin.Success", targetPlayer.displayName);
return;
}
if (player.HasPermission(PERMISSION_WARP_BYPASS))
{
WarpBack(player);
return;
}
if (player.HasPermission(PERMISSION_WARP_BACK))
{
WarpBackLimited(player);
return;
}
SendReply(player, "General.NoPermission");
}
private void WarpBack(BasePlayer player)
{
if (!m_LastWarp.TryGetValue(player.userID, out Vector3 position))
{
SendReply(player, "Warp.Error.NoBackLocation");
return;
}
Teleport(player, position);
SendReply(player, "TPB.Success");
}
private void WarpBackLimited(BasePlayer player)
{
if (!m_LastWarp.TryGetValue(player.userID, out Vector3 position))
{
SendReply(player, "Warp.Error.NoBackLocation");
return;
}
if (!CanWarpTo(player, position, out bool playerIsPaying))
return;
PositionTeleporter.Create(player, position, "Previous location", Configuration.Warp.Delay.GetLowestOption(player), playerIsPaying, true);
}
[ChatCommand("warpadd")]
private void WarpAddCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_WARP_ADMIN))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length < 1)
{
SendReply(player, "WarpAdd.Error.Syntax");
return;
}
string warpName = args[0];
if (m_WarpData.Data.ContainsKey(warpName))
{
SendReply(player, "WarpAdd.Error.Exists", args[0]);
return;
}
string permissionString = args.Length > 1 ? args[1].ToLower() : string.Empty;
if (!string.IsNullOrEmpty(permissionString))
{
if (!permissionString.StartsWith("teleportgui."))
permissionString = $"teleportgui.{permissionString}";
permission.RegisterPermission(permissionString, this);
}
m_WarpData.Data.Add(warpName, new WarpPoint
{
Position = player.transform.position,
Permission = permissionString
});
m_WarpData.Save();
if (string.IsNullOrEmpty(permissionString))
SendReply(player, "WarpAdd.Success", warpName);
else SendReply(player, "WarpAdd.Success.Permission", warpName, permissionString);
}
[ChatCommand("warpremove")]
private void WarpRemoveCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_WARP_ADMIN))
{
SendReply(player, "General.NoPermission");
return;
}
if (args.Length < 1)
{
SendReply(player, "WarpRemove.Error.Syntax");
return;
}
if (!m_WarpData.Data.ContainsKey(args[0]))
{
SendReply(player, "Warp.Error.DoesntExist", args[0]);
return;
}
m_WarpData.Data.Remove(args[0]);
m_WarpData.Save();
SendReply(player, "WarpRemove.Success", args[0]);
}
#endregion
#endregion
#region Console Commands
[ConsoleCommand("tpgui")]
private void TPGuiCommand(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Connection.player as BasePlayer;
if (!player)
return;
if (!player.HasPermission(PERMISSION_TP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (!Configuration.UI.DisableUI)
ShowTeleportUI(player, Mode.Teleport);
}
[ConsoleCommand("homegui")]
private void HomeGuiCommand(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Connection.player as BasePlayer;
if (!player)
return;
if (!player.HasPermission(PERMISSION_HOME_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (!Configuration.UI.DisableUI)
ShowTeleportUI(player, Mode.Home);
}
[ConsoleCommand("warpgui")]
private void WarpGuiCommand(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Connection.player as BasePlayer;
if (!player)
return;
if (!player.HasPermission(PERMISSION_WARP_USE))
{
SendReply(player, "General.NoPermission");
return;
}
if (!Configuration.UI.DisableUI)
ShowTeleportUI(player, Mode.Warp);
}
[ConsoleCommand("tpadmin")]
private void AdminTPCommand(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Connection?.player as BasePlayer;
if (player && !player.HasPermission(PERMISSION_COMMAND_ADMIN))
{
SendReply(arg, "You do not have permission to use this command");
return;
}
if (arg.Args == null || arg.Args.Length != 2)
{
SendReply(arg, "tpadmin wipehomes <user_id> - Wipe the homes for the specified player");
SendReply(arg, "tpadmin wipelocations <user_id> - Wipe the locations for the specified player");
SendReply(arg, "tpadmin wipetpusage <user_id> - Wipe the TP usage record for the specified player");
SendReply(arg, "tpadmin wipehomeusage <user_id> - Wipe the home usage record for the specified player");
SendReply(arg, "tpadmin wipewarpusage <user_id> - Wipe the warp usage record for the specified player");
SendReply(arg, "All commands accept '*' as an argument in place of the user ID to indicate all players.\nex. 'tpadmin wipe homes *' will wipe all player homes");
return;
}
string targetPlayer = arg.Args[1];
bool applyToEveryone = targetPlayer.Equals("*", StringComparison.OrdinalIgnoreCase);
TeleportData.User specifiedUser = null;
if (!applyToEveryone)
{
if (!ulong.TryParse(arg.Args[1], out ulong userId))
{
SendReply(arg, "Invalid user ID entered");
return;
}
if (!m_TeleportData.Data.Users.TryGetValue(userId, out specifiedUser))
{
SendReply(arg, "No user data found with the specified user ID");
return;
}
}
switch (arg.Args[0].ToLower())
{
case "wipehomes":
if (applyToEveryone)
{
foreach (TeleportData.User user in m_TeleportData.Data.Users.Values)
{
user.Homes.Clear();
user.HomeUsage.Reset();
}
SendReply(arg, "You have wiped all home data");
}
else
{
specifiedUser.Homes.Clear();
specifiedUser.HomeUsage.Reset();
SendReply(arg, $"You have wiped the home data for {arg.Args[1]}");
}
return;
case "wipelocations":
if (applyToEveryone)
{
foreach (TeleportData.User user in m_TeleportData.Data.Users.Values)
{
user.Locations.Clear();
}
SendReply(arg, "You have wiped all location data");
}
else
{
specifiedUser.Locations.Clear();
SendReply(arg, $"You have wiped the location data for {arg.Args[1]}");
}
return;
case "wipetpusage":
if (applyToEveryone)
{
foreach (TeleportData.User user in m_TeleportData.Data.Users.Values)
{
user.TPUsage.Reset();
}
SendReply(arg, "You have wiped all TP usage data");
}
else
{
specifiedUser.TPUsage.Reset();
SendReply(arg, $"You have wiped the TP usage data for {arg.Args[1]}");
}
return;
case "wipehomeusage":
if (applyToEveryone)
{
foreach (TeleportData.User user in m_TeleportData.Data.Users.Values)
{
user.HomeUsage.Reset();
}
SendReply(arg, "You have wiped all home usage data");
}
else
{
specifiedUser.HomeUsage.Reset();
SendReply(arg, $"You have wiped the home usage data for {arg.Args[1]}");
}
return;
case "wipewarpusage":
if (applyToEveryone)
{
foreach (TeleportData.User user in m_TeleportData.Data.Users.Values)
{
user.WarpUsage.Reset();
}
SendReply(arg, "You have wiped all warp usage data");
}
else
{
specifiedUser.WarpUsage.Reset();
SendReply(arg, $"You have wiped the warp usage data for {arg.Args[1]}");
}
return;
default:
SendReply(arg, "Incorrect syntax");
return;
}
}
#endregion
#region UI
private const string TPUI = "teleport.ui";
private const string TPR_POPUP = "teleportrequest.ui.popup";
private const string TPP_POPUP = "teleportpending.ui.popup";
private string m_MagnifyImage;
/*private Style m_BackgroundStyle;
private Style m_PanelStyle;
private Style m_HeaderStyle;
private Style m_ButtonStyle;
private Style m_ButtonDisabledStyle;
private Style m_CloseStyle;
private Style m_ToggleStyle;*/
private StylePreset m_StylePreset;
private OutlineComponent m_OutlineClose;
private OutlineComponent m_OutlineHighlight;
private OutlineComponent m_OutlineDark = new OutlineComponent(new Color(0.1647059f, 0.1803922f, 0.1921569f, 1f));
private readonly GridLayoutGroup m_GridLayout = new GridLayoutGroup(2, 16, Axis.Horizontal)
{
Area = new Area(-220f, -215f, 220f, 215f),
Spacing = new Spacing(5f, 5f),
Padding = new Padding(5f, 5f, 5f, 5f),
Corner = Corner.TopLeft,
};
private CommandCallbackHandler m_CallbackHandler;
private void SetupUIComponents()
{
m_CallbackHandler = new CommandCallbackHandler(this);
m_StylePreset = new StylePreset
{
Background = new Style(ChaosStyle.Background)
{
ImageColor = new Color(Configuration.UI.Colors.Background.Hex, Configuration.UI.Colors.Background.Alpha)
},
Panel = new Style(ChaosStyle.Panel)
{
ImageColor = new Color(Configuration.UI.Colors.Panel.Hex, Configuration.UI.Colors.Panel.Alpha)
},
Header = new Style(ChaosStyle.Header)
{
ImageColor = new Color(Configuration.UI.Colors.Header.Hex, Configuration.UI.Colors.Header.Alpha),
Sprite = Sprites.Background_Rounded_top,
FontSize = 14,
Font = Font.PermanentMarker,
Alignment = TextAnchor.MiddleLeft
},
Button = new Style(ChaosStyle.Button)
{
ImageColor = new Color(Configuration.UI.Colors.Button.Hex, Configuration.UI.Colors.Button.Alpha)
},
DisabledButton = new Style(ChaosStyle.DisabledButton)
{
ImageColor = new Color(Configuration.UI.Colors.Button.Hex, Mathf.Min(Configuration.UI.Colors.Button.Alpha, 0.8f)),
FontColor = new Color(1f, 1f, 1f, 0.2f),
},
Close = new Style(ChaosStyle.Close)
{
FontSize = 16
},
Toggle = new Style(ChaosStyle.Toggle)
{
ImageColor = new Color(Configuration.UI.Colors.Highlight.Hex, Configuration.UI.Colors.Highlight.Alpha)
}
};
m_OutlineClose = new OutlineComponent(new Color(Configuration.UI.Colors.Close.Hex, Configuration.UI.Colors.Close.Alpha));
m_OutlineHighlight = new OutlineComponent(new Color(Configuration.UI.Colors.Highlight.Hex, Configuration.UI.Colors.Highlight.Alpha));
if (ImageLibrary.IsLoaded)
{
ImageLibrary.AddImage("https://chaoscode.io/oxide/Images/magnifyingglass.png", "teleportgui.search", 0UL, () =>
{
m_MagnifyImage = ImageLibrary.GetImage("teleportgui.search", 0UL);
});
}
}
#region Teleport
private void ShowTeleportUI(BasePlayer player, Mode mode)
{
TeleportData.User userData = GetOrCreateUserSettings(player);
BaseContainer root = ChaosPrefab.Background(TPUI, Layer.Hud, Anchor.Center, new Offset(-225f, -265f, 225f, 265f), m_StylePreset)
.WithChildren(parent =>
{
CreateModeSelector(player, parent, mode);
CreateTitleBar(player, userData, parent);
if (mode == Mode.Teleport)
{
List<BasePlayer> list = BuildPlayerList(player, userData);
CreateHeaderBar(player, userData, parent, m_GridLayout.HasNextPage(userData.Page, list.Count), mode);
CreateGridLayout(player, userData, parent, list, mode,
(layout, anchor, offset, t) => CreatePlayerEntry(player, userData, layout, anchor, offset, t));
Pool.FreeUnmanaged(ref list);
}
if (mode == Mode.Home)
{
List<KeyValuePair<string, TeleportData.User.HomePoint>> list = BuildHomeList(userData);
CreateHeaderBar(player, userData, parent, m_GridLayout.HasNextPage(userData.Page, list.Count), mode);
CreateGridLayout(player, userData, parent, list, mode,
(layout, anchor, offset, t) => CreateHomeEntry(player, userData, layout, anchor, offset, t));
Pool.FreeUnmanaged(ref list);
}
if (mode == Mode.Warp)
{
List<KeyValuePair<string, IWarpPoint>> list = BuildWarpList(player, userData);
CreateHeaderBar(player, userData, parent, m_GridLayout.HasNextPage(userData.Page, list.Count), mode);
CreateGridLayout(player, userData, parent, list, mode,
(layout, anchor, offset, t) => CreateWarpEntry(player, userData, layout, anchor, offset, t));
Pool.FreeUnmanaged(ref list);
}
})
.NeedsCursor()
.NeedsKeyboard()
.DestroyExisting();
ChaosUI.Show(player, root);
}
private void CreateTitleBar(BasePlayer player, TeleportData.User userData, BaseContainer parent)
{
ChaosPrefab.Panel(parent, Anchor.TopStretch, new Offset(5f, -35f, -5f, -5f))
.WithChildren(titleBar =>
{
ChaosPrefab.Title(titleBar, Anchor.CenterLeft, new Offset(5f, -15f, 205f, 15f), GetString("UI.Title", player))
.WithOutline(ChaosStyle.BlackOutline);
ChaosPrefab.CloseButton(titleBar, Anchor.CenterRight, new Offset(-25f, -10f, -5f, 10f), m_OutlineClose)
.WithCallback(m_CallbackHandler, arg =>
{
userData.OnClose();
ChaosUI.Destroy(player, TPUI);
}, $"{player.UserIDString}.menu.exit");
});
}
private void CreateModeSelector(BasePlayer player, BaseContainer parent, Mode mode)
{
ImageContainer.Create(parent, Anchor.TopCenter, new Offset(-150f, 0f, 150f, 25f))
.WithStyle(m_StylePreset.Background)
.WithSprite(Sprites.Background_Rounded_top)
.WithChildren(modeSelector =>
{
bool canTeleport = player.HasPermission(PERMISSION_TP_USE);
bool canHome = player.HasPermission(PERMISSION_HOME_USE);
bool canWarp = player.HasPermission(PERMISSION_WARP_USE);
ImageContainer.Create(modeSelector, Anchor.Center, new Offset(-145f, -12.5f, -51.66666f, 7.5f))
.WithStyle(mode == Mode.Teleport ? m_StylePreset.Header : (canTeleport ? m_StylePreset.Button : m_StylePreset.DisabledButton))
.WithSprite(Sprites.Background_Rounded)
.WithChildren(button =>
{
TextContainer.Create(button, Anchor.FullStretch, Offset.zero)
.WithText(GetString("UI.Teleport", player))
.WithStyle(canTeleport ? m_StylePreset.Button : m_StylePreset.DisabledButton);
if (canTeleport)
{
ButtonContainer.Create(button, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => ShowTeleportUI(player, Mode.Teleport), $"{player.UserIDString}.mode.teleport");
}
});
ImageContainer.Create(modeSelector, Anchor.Center, new Offset(-46.66666f, -12.5f, 46.66667f, 7.5f))
.WithStyle(mode == Mode.Home ? m_StylePreset.Header : (canHome ? m_StylePreset.Button : m_StylePreset.DisabledButton))
.WithSprite(Sprites.Background_Rounded)
.WithChildren(button =>
{
TextContainer.Create(button, Anchor.FullStretch, Offset.zero)
.WithText(GetString("UI.Homes", player))
.WithStyle(canHome ? m_StylePreset.Button : m_StylePreset.DisabledButton);
if (canHome)
{
ButtonContainer.Create(button, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => ShowTeleportUI(player, Mode.Home), $"{player.UserIDString}.mode.home");
}
});
ImageContainer.Create(modeSelector, Anchor.Center, new Offset(51.66667f, -12.5f, 145f, 7.5f))
.WithStyle(mode == Mode.Warp ? m_StylePreset.Header : (canWarp ? m_StylePreset.Button : m_StylePreset.DisabledButton))
.WithSprite(Sprites.Background_Rounded)
.WithChildren(button =>
{
TextContainer.Create(button, Anchor.FullStretch, Offset.zero)
.WithText(GetString("UI.Warps", player))
.WithStyle(canWarp ? m_StylePreset.Button : m_StylePreset.DisabledButton);
if (canWarp)
{
ButtonContainer.Create(button, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => ShowTeleportUI(player, Mode.Warp), $"{player.UserIDString}.mode.warp");
}
});
});
}
private void CreateHeaderBar(BasePlayer player, TeleportData.User userData, BaseContainer parent, bool hasNextPage, Mode mode)
{
ChaosPrefab.Panel(parent, Anchor.TopStretch, new Offset(5f, -70f, -5f, -40f))
.WithChildren(header =>
{
// Pagination
ChaosPrefab.PreviousPage(header, Anchor.CenterLeft, new Offset(5f, -10f, 35f, 10f), userData.Page > 0)?
.WithCallback(m_CallbackHandler, arg =>
{
userData.Page -= 1;
ShowTeleportUI(player, mode);
}, $"{player.UserIDString}.back");
ChaosPrefab.NextPage(header, Anchor.CenterRight, new Offset(-35f, -10f, -5f, 10f), hasNextPage)?
.WithCallback(m_CallbackHandler, arg =>
{
userData.Page += 1;
ShowTeleportUI(player, mode);
}, $"{player.UserIDString}.next");
// Search Input
ChaosPrefab.Input(header, Anchor.CenterRight, new Offset(-240f, -10f, -40f, 10f), userData.SearchString)
.WithCallback(m_CallbackHandler, arg =>
{
userData.SearchString = arg.Args.Length > 1 ? string.Join(" ", arg.Args.Skip(1)) : string.Empty;
ShowTeleportUI(player, mode);
}, $"{player.UserIDString}.search");
if (!string.IsNullOrEmpty(m_MagnifyImage))
{
RawImageContainer.Create(header, Anchor.CenterRight, new Offset(-265f, -10f, -245f, 10f))
.WithPNG(m_MagnifyImage);
}
if (mode == Mode.Teleport)
{
if (player.HasPermission(PERMISSION_TP_AUTOACCEPT) || player.HasPermission(PERMISSION_TP_SLEEPERS))
{
ChaosPrefab.SpriteButton(header, Anchor.CenterLeft, new Offset(40f, -10f, 60f, 10f),
Icon.Gear, Anchor.FullStretch, new Offset(2f, 2f, -2f, -2f))
.WithCallback(m_CallbackHandler, arg => ShowTeleportSettingsUI(player, userData, mode), $"{player.UserIDString}.settings");
/*ImageContainer.Create(header, Anchor.CenterLeft, new Offset(40f, -10f, 60f, 10f))
.WithStyle(m_ButtonStyle)
.WithChildren(settings =>
{
ImageContainer.Create(settings, Anchor.FullStretch, new Offset(2f, 2f, -2f, -2f))
.WithSprite(Icon.Gear);
ButtonContainer.Create(settings, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => ShowTeleportSettingsUI(player, userData, mode), $"{player.UserIDString}.settings");
});*/
}
}
if (mode == Mode.Home)
{
bool canSetHome = player.HasPermission(PERMISSION_HOME_USE) && userData.Homes.Count < GetMaxHomesForPlayer(player);
ImageContainer.Create(header, Anchor.CenterLeft, new Offset(40f, -10f, 60f, 10f))
.WithStyle(canSetHome ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(settings =>
{
ImageContainer.Create(settings, Anchor.FullStretch, new Offset(2f, 2f, -2f, -2f))
.WithSprite(Icon.Add)
.WithColor(canSetHome ? Color.White : m_StylePreset.DisabledButton.FontColor);
if (canSetHome)
{
ButtonContainer.Create(settings, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => SaveHomeUI(player), $"{player.UserIDString}.addhome");
}
});
}
if (mode == Mode.Warp && player.HasPermission(PERMISSION_WARP_ADMIN))
{
ImageContainer.Create(header, Anchor.CenterLeft, new Offset(40f, -10f, 60f, 10f))
.WithStyle(m_StylePreset.Button)
.WithChildren(settings =>
{
ImageContainer.Create(settings, Anchor.FullStretch, new Offset(2f, 2f, -2f, -2f))
.WithSprite(Icon.Add);
ButtonContainer.Create(settings, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => SaveWarpUI(player), $"{player.UserIDString}.addwarp");
});
}
});
}
private void CreateGridLayout<T>(BasePlayer player, TeleportData.User userData, BaseContainer parent, List<T> list, Mode mode, Action<BaseContainer, Anchor, Offset, T> createAction)
{
ChaosPrefab.Panel(parent, Anchor.FullStretch, new Offset(5f, 5f, -5f, -75f))
.WithChildren(grid =>
{
ConfigData.LimitOptions limits = mode == Mode.Warp ? Configuration.Warp.Limits :
mode == Mode.Home ? Configuration.Home.Limits :
Configuration.TP.Limits;
ConfigData.PurchaseOptions purchase = mode == Mode.Warp ? Configuration.Warp.Purchase :
mode == Mode.Home ? Configuration.Home.Purchase :
Configuration.TP.Purchase;
TeleportData.User.Usage usage = mode == Mode.Warp ? userData.WarpUsage :
mode == Mode.Home ? userData.HomeUsage :
userData.TPUsage;
bool isOnCooldown = usage.IsOnCooldown();
bool notRestrictedByLimit = true;
bool _;
bool hasPendingRequest = TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player) || PositionTeleporter.IsWaiting(player, out _);
bool canTeleport = !isOnCooldown && !hasPendingRequest && notRestrictedByLimit;
ImageContainer.Create(grid, Anchor.TopStretch, new Offset(0f, -20f, 0f, 0f))
.WithStyle(m_StylePreset.Header)
.WithChildren(header =>
{
if (limits.Default > 0)
{
if (HasReachedDailyLimit(player, userData, mode))
{
if (purchase.PayAfterUsingDailyLimits)
{
TextContainer.Create(header, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(FormatString("UI.CostToTP", player, purchase.GetLowestOption(player), GetString($"PurchaseMode.{purchase.Mode}", player)))
.WithAlignment(TextAnchor.MiddleLeft);
}
else
{
notRestrictedByLimit = false;
TextContainer.Create(header, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(GetString("UI.TPLimit", player))
.WithAlignment(TextAnchor.MiddleLeft);
}
}
else
{
int limit = limits.GetHighestOption(player);
TextContainer.Create(header, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(FormatString("UI.DailyLimitRemain", player, limit - usage.UsesToday))
.WithAlignment(TextAnchor.MiddleLeft);
}
}
if (isOnCooldown)
{
TextContainer.Create(header, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(GetString("UI.CooldownRemain", player))
.WithAlignment(TextAnchor.MiddleRight)
.WithCountdown(new CountdownComponent((int)(usage.Cooldown - CurrentTime())));
// Temp solution for broken countdown component
/*string guid = CuiHelper.GetGuid();
int time = (int)(usage.Cooldown - CurrentTime());
TextContainer.Create(header, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(GetString("UI.CooldownRemain", player).Replace("%TIME_LEFT%", time.ToString()))
.WithAlignment(TextAnchor.MiddleRight)
.WithName(guid);
ServerCountdown.Add(player, guid, "UI.CooldownRemain", time);*/
}
});
if (list.Count == 0)
{
TextContainer.Create(grid, Anchor.FullStretch, Offset.zero)
.WithText(GetString(mode == Mode.Home ? "UI.NoHomes" : mode == Mode.Warp ? "UI.NoWarps" : "UI.NoPlayers", player))
.WithAlignment(TextAnchor.MiddleCenter);
}
BaseContainer.Create(grid, Anchor.FullStretch, new Offset(0f, 0f, 0f, -20f))
.WithLayoutGroup(m_GridLayout, list, userData.Page, (int i, T t, BaseContainer layout, Anchor anchor, Offset offset) =>
{
createAction(layout, anchor, offset, t);
});
});
}
private void CreatePlayerEntry(BasePlayer player, TeleportData.User userData, BaseContainer layout, Anchor anchor, Offset offset, BasePlayer t)
{
bool isOnCooldown = userData.TPUsage.IsOnCooldown();
bool hasPendingRequest = TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player) || PositionTeleporter.IsWaiting(player, out bool _);
bool canTeleport = !isOnCooldown && !hasPendingRequest;
bool canTeleportToPlayer = canTeleport && (t.IsConnected || (IsAdmin(player) && Configuration.Admin.Instant));
ChaosPrefab.Panel(layout, anchor, offset)
.WithChildren(template =>
{
TextContainer.Create(template, Anchor.FullStretch, new Offset(5f, 0f, -85f, 0f))
.WithText(t.displayName.StripTags())
.WithAlignment(TextAnchor.MiddleLeft);
ImageContainer.Create(template, Anchor.RightStretch, new Offset(-41f, 1f, -1f, -1f))
.WithStyle(canTeleportToPlayer ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(tpto =>
{
TextContainer.Create(tpto, Anchor.FullStretch, Offset.zero)
.WithStyle(canTeleportToPlayer ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithSize(12)
.WithText(GetString("UI.TPR", player))
.WithAlignment(TextAnchor.MiddleCenter);
if (canTeleportToPlayer)
{
ButtonContainer.Create(tpto, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
userData.OnClose();
ChaosUI.Destroy(player, TPUI);
TPR(player, t, false);
}, $"{player.UserIDString}.tpr.{t.UserIDString}");
}
});
if (player.HasPermission(PERMISSION_TP_HERE))
{
ImageContainer.Create(template, Anchor.RightStretch, new Offset(-86f, 1f, -46f, -1f))
.WithStyle(canTeleportToPlayer ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(tphere =>
{
TextContainer.Create(tphere, Anchor.FullStretch, Offset.zero)
.WithStyle(canTeleportToPlayer ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithSize(12)
.WithText(GetString("UI.TPHere", player))
.WithAlignment(TextAnchor.MiddleCenter);
if (canTeleportToPlayer)
{
ButtonContainer.Create(tphere, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
userData.OnClose();
ChaosUI.Destroy(player, TPUI);
TPR(player, t, true);
}, $"{player.UserIDString}.tphere.{t.UserIDString}");
}
});
}
});
}
private void CreateHomeEntry(BasePlayer player, TeleportData.User userData, BaseContainer layout, Anchor anchor, Offset offset, KeyValuePair<string, TeleportData.User.HomePoint> t)
{
ButtonContainer.Create(layout, anchor, offset)
.WithCallback(m_CallbackHandler, arg =>
{
bool isOnCooldown = userData.HomeUsage.IsOnCooldown();
bool hasPendingRequest = TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player) || PositionTeleporter.IsWaiting(player, out bool _);
if (!isOnCooldown && !hasPendingRequest)
{
if (!IsHomePointValid(t.Value))
{
SendReply(player, "Home.Error.Invalid", t.Key);
userData.Homes.Remove(t.Key);
ShowTeleportUI(player, Mode.Home);
return;
}
if (CanTeleportHome(player, t.Value.Position, out bool playerIsPaying))
{
PositionTeleporter.Create(player, t.Value.Position, t.Key, Configuration.Home.Delay.GetLowestOption(player), playerIsPaying, true);
ChaosUI.Destroy(player, TPUI);
}
}
}, $"{player.UserIDString}.home.{t.Key}")
.WithStyle(m_StylePreset.Panel)
.WithChildren(template =>
{
TextContainer.Create(template, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(t.Key)
.WithAlignment(TextAnchor.MiddleCenter);
ImageContainer.Create(template, Anchor.RightStretch, new Offset(-20f, 2f, -2f, -2f))
.WithStyle(m_StylePreset.Button)
.WithOutline(m_OutlineClose)
.WithChildren(delete =>
{
ImageContainer.Create(delete, Anchor.FullStretch, new Offset(2f, 2f, -2f, -2f))
.WithSprite(Icon.Clear);
ButtonContainer.Create(delete, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
userData.Homes.Remove(t.Key);
SendReply(player, "Home.Success.Deleted", t.Key);
ShowTeleportUI(player, Mode.Home);
}, $"{player.UserIDString}.deletehome.{t.Key}");
});
});
}
private void CreateWarpEntry(BasePlayer player, TeleportData.User userData, BaseContainer layout, Anchor anchor, Offset offset, KeyValuePair<string, IWarpPoint> t)
{
bool canWarp = t.Value.HasPermission(player);
ButtonContainer.Create(layout, anchor, offset)
.WithCallback(m_CallbackHandler, arg =>
{
if (!canWarp)
{
SendReply(player, "Warp.Error.NoPermission");
return;
}
bool isOnCooldown = userData.WarpUsage.IsOnCooldown();
bool hasPendingRequest = TeleportRequest.HasPendingRequest(player) || PlayerTeleporter.IsWaiting(player) || PositionTeleporter.IsWaiting(player, out bool _);
bool canTeleport = !isOnCooldown && !hasPendingRequest;
if (canTeleport)
{
Vector3 position = t.Value.GetPosition();
if (CanWarpTo(player, position, out bool playerIsPaying))
{
PositionTeleporter.Create(player, position, t.Key, Configuration.Warp.Delay.GetLowestOption(player), playerIsPaying, false);
ChaosUI.Destroy(player, TPUI);
}
}
}, $"{player.UserIDString}.warp.{t.Key}")
.WithStyle(m_StylePreset.Panel)
.WithChildren(template =>
{
TextContainer.Create(template, Anchor.FullStretch, new Offset(5f, 0f, -5f, 0f))
.WithText(t.Key)
.WithAlignment(TextAnchor.MiddleCenter)
.WithColor(canWarp ? Color.White : m_StylePreset.DisabledButton.FontColor);
if (player.HasPermission(PERMISSION_WARP_ADMIN) && t.Value is WarpPoint)
{
ImageContainer.Create(template, Anchor.RightStretch, new Offset(-20f, 2f, -2f, -2f))
.WithStyle(m_StylePreset.Button)
.WithOutline(m_OutlineClose)
.WithChildren(delete =>
{
ImageContainer.Create(delete, Anchor.FullStretch, new Offset(2f, 2f, -2f, -2f))
.WithSprite(Icon.Clear);
ButtonContainer.Create(delete, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
m_WarpData.Data.Remove(t.Key);
m_WarpData.Save();
SendReply(player, "WarpRemove.Success", t.Key);
ShowTeleportUI(player, Mode.Warp);
}, $"{player.UserIDString}.deletewarp.{t.Key}");
});
}
});
}
private List<BasePlayer> BuildPlayerList(BasePlayer player, TeleportData.User userData)
{
List<BasePlayer> list = Pool.Get<List<BasePlayer>>();
list.AddRange(BasePlayer.activePlayerList);
if (userData.ShowSleepers)
list.AddRange(BasePlayer.sleepingPlayerList);
if (Configuration.UI.HideAdminsInUI && !IsAdmin(player))
list.RemoveAll(IsAdmin);
list.RemoveAll(x => x.HasPermission(PERMISSION_HIDE_UI));
list.Remove(player);
if (!player.HasPermission(PERMISSION_SEE_ALL) && Configuration.TP.FriendliesOnly)
{
List<ulong> friendlies = Pool.Get<List<ulong>>();
if (Clans.IsLoaded)
{
List<string> clanMembers = Clans.GetClanMembers(player.userID);
foreach (string s in clanMembers)
{
if (ulong.TryParse(s, out ulong id))
friendlies.Add(id);
}
}
if (Friends.IsLoaded)
{
if (Configuration.TP.MutualFriendsOnly)
{
ulong[] friends = Friends.GetFriends(player.userID);
foreach (ulong id in friends)
{
if (Friends.HasFriend(id, player.userID))
friendlies.Add(id);
}
}
else friendlies.AddRange(Friends.GetFriends(player.userID));
}
if (player.Team != null)
friendlies.AddRange(player.Team.members);
list.RemoveAll(x => !friendlies.Contains(x.userID));
}
if (!string.IsNullOrEmpty(userData.SearchString))
{
for (int i = list.Count - 1; i >= 0; i--)
{
BasePlayer p = list[i];
if (!p.displayName.Contains(userData.SearchString, CompareOptions.OrdinalIgnoreCase))
list.RemoveAt(i);
}
}
list.Sort((a,b) => a.displayName.CompareTo(b.displayName));
return list;
}
private List<KeyValuePair<string, TeleportData.User.HomePoint>> BuildHomeList(TeleportData.User userData)
{
List<KeyValuePair<string, TeleportData.User.HomePoint>> list = Pool.Get<List<KeyValuePair<string, TeleportData.User.HomePoint>>>();
list.AddRange(userData.Homes);
if (!string.IsNullOrEmpty(userData.SearchString))
{
for (int i = list.Count - 1; i >= 0; i--)
{
KeyValuePair<string, TeleportData.User.HomePoint> p = list[i];
if (!p.Key.Contains(userData.SearchString, CompareOptions.OrdinalIgnoreCase))
list.RemoveAt(i);
}
}
list.Sort((a,b) => a.Key.CompareTo(b.Key));
return list;
}
private List<KeyValuePair<string, IWarpPoint>> BuildWarpList(BasePlayer player, TeleportData.User userData)
{
List<KeyValuePair<string, IWarpPoint>> list = Pool.Get<List<KeyValuePair<string, IWarpPoint>>>();
foreach (KeyValuePair<string, MonumentWarpPoint> kvp in m_MonumentWarps)
list.Add(new KeyValuePair<string, IWarpPoint>(m_GetString(kvp.Value.Shortname, player) + $" ({kvp.Value.MapGrid})", kvp.Value));
foreach (KeyValuePair<string, WarpPoint> kvp in m_WarpData.Data)
list.Add(new KeyValuePair<string, IWarpPoint>(kvp.Key, kvp.Value));
if (Configuration.UI.HideWarpsNoPermission)
list.RemoveAll(x => !x.Value.HasPermission(player));
if (!string.IsNullOrEmpty(userData.SearchString))
{
for (int i = list.Count - 1; i >= 0; i--)
{
KeyValuePair<string, IWarpPoint> p = list[i];
if (!p.Key.Contains(userData.SearchString, CompareOptions.OrdinalIgnoreCase))
list.RemoveAt(i);
}
}
list.Sort((a, b) => a.Key.CompareTo(b.Key));
return list;
}
private void SaveHomeUI(BasePlayer player, string homeName = "")
{
BaseContainer root = ChaosPrefab.Background(TPUI, Layer.Overall, Anchor.FullStretch, Offset.zero, m_StylePreset)
.WithChildren(parent =>
{
ChaosPrefab.Panel(parent, Anchor.Center, new Offset(-150f, -27.5f, 150f, 27.5f))
.WithChildren(titleBar =>
{
TextContainer.Create(titleBar, Anchor.TopStretch, new Offset(5f, -25f, -5f, -5f))
.WithText(GetString("UI.HomeName", player))
.WithAlignment(TextAnchor.MiddleLeft);
ChaosPrefab.Input(titleBar, Anchor.TopStretch, new Offset(80f, -25f, -5.000015f, -5f), homeName)
.WithCallback(m_CallbackHandler, arg =>
{
SaveHomeUI(player, arg.Args.Length > 1 ? string.Join(" ", arg.Args.Skip(1)) : string.Empty);
}, $"{player.UserIDString}.homenameinput");
ChaosPrefab.TextButton(titleBar, Anchor.BottomStretch, new Offset(5f, 5f, -155f, 25f), GetString("UI.Save", player), null, m_OutlineHighlight)
.WithCallback(m_CallbackHandler, arg =>
{
SetHomeCommand(player, "sethome", new string[]{ homeName });
ShowTeleportUI(player, Mode.Home);
}, $"{player.UserIDString}.addhome.save");
ChaosPrefab.TextButton(titleBar, Anchor.BottomStretch, new Offset(155f, 5f, -5f, 25f), GetString("UI.Cancel", player), null, m_OutlineClose)
.WithCallback(m_CallbackHandler, arg => { ShowTeleportUI(player, Mode.Home); }, $"{player.UserIDString}.addhome.cancel");
});
ChaosPrefab.Panel(parent, Anchor.Center, new Offset(-150f, 32.5f, 150f, 52.5f))
.WithChildren(infoBar =>
{
TextContainer.Create(infoBar, Anchor.FullStretch, Offset.zero)
.WithText(GetString("UI.CreateNewHome", player))
.WithAlignment(TextAnchor.MiddleCenter);
});
})
.DestroyExisting()
.NeedsCursor()
.NeedsKeyboard();
ChaosUI.Show(player, root);
}
private void SaveWarpUI(BasePlayer player, string warpName = "", string perm = "")
{
BaseContainer root = ChaosPrefab.Background(TPUI, Layer.Overall, Anchor.FullStretch, Offset.zero, m_StylePreset)
.WithChildren(parent =>
{
ChaosPrefab.Panel(parent, Anchor.Center, new Offset(-150f, -40f, 150f, 40f))
.WithChildren(titleBar =>
{
TextContainer.Create(titleBar, Anchor.TopStretch, new Offset(5f, -25f, -5f, -5f))
.WithText(GetString("UI.WarpName", player))
.WithAlignment(TextAnchor.MiddleLeft);
ChaosPrefab.Input(titleBar, Anchor.TopStretch, new Offset(80f, -25f, -5.000015f, -5f), warpName)
.WithCallback(m_CallbackHandler, arg =>
{
SaveWarpUI(player, arg.Args.Length > 1 ? string.Join(" ", arg.Args.Skip(1)) : string.Empty, perm);
}, $"{player.UserIDString}.warpnameinput");
TextContainer.Create(titleBar, Anchor.TopStretch, new Offset(5f, -50f, -5f, -30f))
.WithText(GetString("UI.Permission", player))
.WithAlignment(TextAnchor.MiddleLeft);
ImageContainer.Create(titleBar, Anchor.TopStretch, new Offset(80f, -50f, -5.000015f, -30f))
.WithStyle(m_StylePreset.Button)
.WithChildren(permissionInput =>
{
TextContainer.Create(permissionInput, Anchor.FullStretch, new Offset(5f, 0f, 0f, 0f))
.WithText("teleportgui.")
.WithAlignment(TextAnchor.MiddleLeft);
InputFieldContainer.Create(permissionInput, Anchor.FullStretch, new Offset(73f, 0f, 0f, 0f))
.WithText(perm)
.WithAlignment(TextAnchor.MiddleLeft)
.WithCallback(m_CallbackHandler, arg =>
{
SaveWarpUI(player, warpName, arg.Args.Length > 1 ? string.Join(" ", arg.Args.Skip(1)) : string.Empty);
}, $"{player.UserIDString}.warppermissioninput");
});
ChaosPrefab.TextButton(titleBar, Anchor.BottomStretch, new Offset(5f, 5f, -155f, 25f), GetString("UI.Save", player), null, m_OutlineHighlight)
.WithCallback(m_CallbackHandler, arg =>
{
WarpAddCommand(player, "warpadd", new string[] {warpName, perm});
ShowTeleportUI(player, Mode.Warp);
}, $"{player.UserIDString}.addwarp.save");
ChaosPrefab.TextButton(titleBar, Anchor.BottomStretch, new Offset(155f, 5f, -5f, 25f), GetString("UI.Cancel", player), null, m_OutlineClose)
.WithCallback(m_CallbackHandler, arg => ShowTeleportUI(player, Mode.Warp), $"{player.UserIDString}.addwarp.cancel");
});
ChaosPrefab.Panel(parent, Anchor.Center, new Offset(-150f, 45f, 150f, 65f))
.WithChildren(infoBar =>
{
TextContainer.Create(infoBar, Anchor.FullStretch, Offset.zero)
.WithText(GetString("UI.CreateNewWarp", player))
.WithAlignment(TextAnchor.MiddleCenter);
});
})
.DestroyExisting()
.NeedsCursor()
.NeedsKeyboard();
ChaosUI.Show(player, root);
}
private void ShowTeleportSettingsUI(BasePlayer player, TeleportData.User userData, Mode mode)
{
BaseContainer root = ChaosPrefab.Background(TPUI, Layer.Overall, Anchor.FullStretch, Offset.zero, m_StylePreset)
.WithChildren(parent =>
{
ChaosPrefab.Panel(parent, Anchor.Center, new Offset(-100f, -77.5f, 100f, 77.5f))
.WithChildren(titleBar =>
{
ChaosPrefab.TextButton(titleBar, Anchor.BottomStretch, new Offset(5f, 5f, -5f, 25f), GetString("UI.Close", player), null, m_OutlineClose)
.WithCallback(m_CallbackHandler, arg => ShowTeleportUI(player, mode), $"{player.UserIDString}.settings.close");
bool canToggleAutoAccept = player.HasPermission(PERMISSION_TP_AUTOACCEPT);
ImageContainer.Create(titleBar, Anchor.TopLeft, new Offset(5f, -25f, 25f, -5f))
.WithStyle(m_StylePreset.Button)
.WithChildren(autoaccept =>
{
bool isActive = (userData.AutoAccept & TeleportData.User.AutoAcceptEnum.Clans) != 0;
if (isActive)
{
ImageContainer.Create(autoaccept, Anchor.FullStretch, new Offset(5f, 5f, -5f, -5f))
.WithStyle(m_StylePreset.Toggle);
}
if (canToggleAutoAccept)
{
ButtonContainer.Create(autoaccept, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
if (isActive)
userData.AutoAccept &= ~TeleportData.User.AutoAcceptEnum.Clans;
else userData.AutoAccept |= TeleportData.User.AutoAcceptEnum.Clans;
ShowTeleportSettingsUI(player, userData, mode);
}, $"{player.UserIDString}.settings.aaclan");
}
TextContainer.Create(autoaccept, Anchor.HoriztonalCenterStretch, new Offset(25f, -10f, 170f, 10f))
.WithText(GetString("UI.AA.Clan", player))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithAlignment(TextAnchor.MiddleLeft);
});
ImageContainer.Create(titleBar, Anchor.TopLeft, new Offset(5f, -50f, 25f, -30f))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(autoaccept =>
{
bool isActive = (userData.AutoAccept & TeleportData.User.AutoAcceptEnum.Friends) != 0;
if (isActive)
{
ImageContainer.Create(autoaccept, Anchor.FullStretch, new Offset(5f, 5f, -5f, -5f))
.WithStyle(m_StylePreset.Toggle);
}
if (canToggleAutoAccept)
{
ButtonContainer.Create(autoaccept, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
if (isActive)
userData.AutoAccept &= ~TeleportData.User.AutoAcceptEnum.Friends;
else userData.AutoAccept |= TeleportData.User.AutoAcceptEnum.Friends;
ShowTeleportSettingsUI(player, userData, mode);
}, $"{player.UserIDString}.settings.aafriend");
}
TextContainer.Create(autoaccept, Anchor.HoriztonalCenterStretch, new Offset(25f, -10f, 170f, 10f))
.WithText(GetString("UI.AA.Friend", player))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithAlignment(TextAnchor.MiddleLeft);
});
ImageContainer.Create(titleBar, Anchor.TopLeft, new Offset(5f, -75f, 25f, -55f))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(autoaccept =>
{
bool isActive = (userData.AutoAccept & TeleportData.User.AutoAcceptEnum.Teams) != 0;
if (isActive)
{
ImageContainer.Create(autoaccept, Anchor.FullStretch, new Offset(5f, 5f, -5f, -5f))
.WithStyle(m_StylePreset.Toggle);
}
if (canToggleAutoAccept)
{
ButtonContainer.Create(autoaccept, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
if (isActive)
userData.AutoAccept &= ~TeleportData.User.AutoAcceptEnum.Teams;
else userData.AutoAccept |= TeleportData.User.AutoAcceptEnum.Teams;
ShowTeleportSettingsUI(player, userData, mode);
}, $"{player.UserIDString}.settings.aateam");
}
TextContainer.Create(autoaccept, Anchor.HoriztonalCenterStretch, new Offset(25f, -10f, 170f, 10f))
.WithText(GetString("UI.AA.Team", player))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithAlignment(TextAnchor.MiddleLeft);
});
ImageContainer.Create(titleBar, Anchor.TopLeft, new Offset(5f, -100f, 25f, -80f))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(autoaccept =>
{
bool isActive = (userData.AutoAccept & TeleportData.User.AutoAcceptEnum.All) != 0;
if (isActive)
{
ImageContainer.Create(autoaccept, Anchor.FullStretch, new Offset(5f, 5f, -5f, -5f))
.WithStyle(m_StylePreset.Toggle);
}
if (canToggleAutoAccept)
{
ButtonContainer.Create(autoaccept, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
if (isActive)
userData.AutoAccept &= ~TeleportData.User.AutoAcceptEnum.All;
else userData.AutoAccept |= TeleportData.User.AutoAcceptEnum.All;
ShowTeleportSettingsUI(player, userData, mode);
}, $"{player.UserIDString}.settings.aaall");
}
TextContainer.Create(autoaccept, Anchor.HoriztonalCenterStretch, new Offset(25f, -10f, 170f, 10f))
.WithText(GetString("UI.AA.All", player))
.WithStyle(canToggleAutoAccept ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithAlignment(TextAnchor.MiddleLeft);
});
bool canToggleSleepers = player.HasPermission(PERMISSION_TP_SLEEPERS);
ImageContainer.Create(titleBar, Anchor.TopLeft, new Offset(5f, -125f, 25f, -105f))
.WithStyle(canToggleSleepers ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithChildren(showSleepers =>
{
if (userData.ShowSleepers)
{
ImageContainer.Create(showSleepers, Anchor.FullStretch, new Offset(5f, 5f, -5f, -5f))
.WithStyle(m_StylePreset.Toggle);
}
if (canToggleSleepers)
{
ButtonContainer.Create(showSleepers, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
userData.ShowSleepers = !userData.ShowSleepers;
ShowTeleportSettingsUI(player, userData, mode);
}, $"{player.UserIDString}.settings.sleepers");
}
TextContainer.Create(showSleepers, Anchor.HoriztonalCenterStretch, new Offset(25f, -10f, 170f, 10f))
.WithText(GetString("UI.ShowSleepers", player))
.WithStyle(canToggleSleepers ? m_StylePreset.Button : m_StylePreset.DisabledButton)
.WithAlignment(TextAnchor.MiddleLeft);
});
});
ChaosPrefab.Panel(parent, Anchor.Center, new Offset(-100f, 82.5f, 100f, 102.5f))
.WithChildren(infoBar =>
{
TextContainer.Create(infoBar, Anchor.FullStretch, Offset.zero)
.WithText(GetString("UI.TeleportSettings", player))
.WithAlignment(TextAnchor.MiddleCenter);
});
})
.DestroyExisting()
.NeedsCursor()
.NeedsKeyboard();
ChaosUI.Show(player, root);
}
#endregion
#region Popups
private void CreateTeleportRequestPopup(BasePlayer player, string panel, ITeleport teleport, string key, string displayName, bool isReceiver)
{
BaseContainer root = ImageContainer.Create(panel, Layer.Hud, Anchor.CenterRight, new Offset(-140f, -22.5f, 10f, 22.5f))
.WithStyle(m_StylePreset.Background)
.WithFadeIn(0.25f)
.WithFadeOut(0.25f)
.WithChildren(parent =>
{
ImageContainer.Create(parent, Anchor.FullStretch, new Offset(5f, 5f, 0f, -5f))
.WithStyle(m_StylePreset.Panel)
.WithChildren(contents =>
{
ImageContainer.Create(contents, Anchor.TopStretch, new Offset(0f, -15f, 0f, 0f))
.WithStyle(m_StylePreset.Header)
.WithChildren(header =>
{
TextContainer.Create(header, Anchor.FullStretch, new Offset(0f, 0f, -10f, 0f))
.WithSize(12)
.WithText(GetString(key, player))
.WithAlignment(TextAnchor.MiddleCenter)
.WithCountdown(new CountdownComponent(teleport.TimeRemaining));
// Temp solution for broken countdown component
/*string guid = CuiHelper.GetGuid();
TextContainer.Create(header, Anchor.FullStretch, new Offset(0f, 0f, -10f, 0f))
.WithSize(12)
.WithText(GetString(key, player).Replace("%TIME_LEFT%", teleport.TimeRemaining.ToString()))
.WithAlignment(TextAnchor.MiddleCenter)
.WithName(guid);
ServerCountdown.Add(player, guid, key, teleport.TimeRemaining);*/
});
TextContainer.Create(contents, Anchor.FullStretch, new Offset(5f, 0f, -55f, -15f))
.WithText(displayName)
.WithSize(12)
.WithAlignment(TextAnchor.MiddleLeft);
if (isReceiver && teleport.CanAccept)
{
ImageContainer.Create(contents, Anchor.CenterRight, new Offset(-50f, -15f, -35f, 0f))
.WithStyle(m_StylePreset.Button)
.WithOutline(m_OutlineHighlight)
.WithChildren(accept =>
{
TextContainer.Create(accept, Anchor.FullStretch, Offset.zero)
.WithSize(10)
.WithText("✔")
.WithAlignment(TextAnchor.MiddleCenter)
.WithWrapMode(VerticalWrapMode.Overflow);
ButtonContainer.Create(accept, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
if (m_PopupTimers.TryGetValue(player.userID, out Timer t))
t?.Destroy();
ChaosUI.Destroy(player, panel);
if (teleport.IsValid)
teleport.Accept();
}, $"{player.UserIDString}.tprpopup.accept");
});
}
if (teleport.CanCancel(player))
{
ImageContainer.Create(contents, Anchor.CenterRight, new Offset(-30f, -15f, -15f, 0f))
.WithStyle(m_StylePreset.Button)
.WithOutline(m_OutlineClose)
.WithChildren(decline =>
{
TextContainer.Create(decline, Anchor.FullStretch, Offset.zero)
.WithSize(12)
.WithText("✘")
.WithAlignment(TextAnchor.MiddleCenter)
.WithWrapMode(VerticalWrapMode.Overflow);
ButtonContainer.Create(decline, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg =>
{
if (m_PopupTimers.TryGetValue(player.userID, out Timer t))
t?.Destroy();
ChaosUI.Destroy(player, panel);
if (teleport.IsValid)
{
if (isReceiver)
teleport.Decline();
else teleport.Cancel();
}
}, $"{player.UserIDString}.tprpopup.decline");
});
}
});
})
.DestroyExisting();
if (m_PopupTimers.TryGetValue(player.userID, out Timer _t))
_t?.Destroy();
m_PopupTimers[player.userID] = timer.Once(teleport.TimeRemaining, () => ChaosUI.Destroy(player, panel));
ChaosUI.Show(player, root);
}
#endregion
#endregion
#region Config
private static ConfigData Configuration;
[JsonConverter(typeof(StringEnumConverter))]
public enum PurchaseMode {Economics, ServerRewards, Scrap}
protected class ConfigData : BaseConfigData
{
[JsonProperty("Chat options")]
public ChatOptions Chat { get; set; }
[JsonProperty("Teleport options")]
public TeleportOptions TP { get; set; }
[JsonProperty("Home options")]
public HomeOptions Home { get; set; }
[JsonProperty("Warp options")]
public WarpOptions Warp { get; set; }
[JsonProperty("Teleport conditions")]
public TeleportConditions Conditions { get; set; }
[JsonProperty("Purge user data after x amount of days of no activity")]
public int PurgeDays { get; set; }
[JsonProperty("Admin options")]
public AdminOptions Admin { get; set; }
[JsonProperty("UI options")]
public UIOptions UI { get; set; }
public class BaseOptions
{
[JsonProperty("Cancel pending teleport if hurt")]
public bool CancelOnDamage { get; set; }
[JsonProperty("Cancel pending teleport if either player dies")]
public bool CancelOnDeath { get; set; }
[JsonProperty("Teleport delay options")]
public TeleportDelayOptions Delay { get; set; }
[JsonProperty("Teleport cooldown options")]
public CooldownOptions Cooldown { get; set; }
[JsonProperty("Teleport daily limit options")]
public LimitOptions Limits { get; set; }
[JsonProperty("Purchase options")]
public PurchaseOptions Purchase { get; set; }
[JsonProperty("Command aliases")]
public List<string> CommandAliases { get; set; }
}
public class TeleportOptions : BaseOptions
{
[JsonProperty("Teleport request timeout (seconds)")]
public int RequestTimeout { get; set; }
[JsonProperty("Only shows friends, clan members and team mates in player list")]
public bool FriendliesOnly { get; set; }
[JsonProperty("[Friends Plugin] Only shows friends that are mutual friends in player list (slow)")]
public bool MutualFriendsOnly { get; set; }
}
public class HomeOptions : BaseOptions
{
[JsonProperty("Max home options")]
public HomeLimits MaxHomes { get; set; }
[JsonProperty("Sleeping bag homes")]
public SleepingBagOptions SleepingBags { get; set; }
[JsonProperty("Allow creating home in building blocked area")]
public bool AllowSetHomeInBuildBlocked { get; set; }
[JsonProperty("Homes can only be set on building blocks")]
public bool MustSetHomeOnBuilding { get; set; }
[JsonProperty("Allow homes to be set on floors")]
public bool CanSetHomeOnFloor { get; set; }
[JsonProperty("Don't allow homes to be set within X distance of another home")]
public float MinimumHomeRadiusDistance { get; set; }
[JsonProperty("Wipe home data when the server is wiped")]
public bool WipeHomesOnNewServerSave { get; set; }
public class SleepingBagOptions
{
[JsonProperty("Create home on bag placement")]
public bool CreateHomeOnBagPlacement = false;
[JsonProperty("Create home on bed placement")]
public bool CreateHomeOnBedPlacement = false;
[JsonProperty("Create home on beach towel placement")]
public bool CreateHomeOnBeachTowelPlacement = false;
[JsonProperty("Only create a home on placement if it is inside a building")]
public bool OnlyCreateInBuilding = true;
//[JsonProperty("Remove home on bed/bag removal")]
//public bool RemoveHomeOnRemoval = false;
[JsonProperty("Disable set home command")]
public bool DisableSetHomeCommand = false;
}
public class HomeLimits : VipOption
{
[JsonProperty("Default home limit (0 disables limits entirely)")]
public override int Default { get; set; }
[JsonProperty("VIP home limit (permission | limit)")]
public override Dictionary<string, int> VIP { get; set; }
public void RegisterPermissions(Permission permission, Plugin plugin)
{
foreach (string perm in VIP.Keys)
{
if (!permission.PermissionExists(perm))
permission.RegisterPermission(perm, plugin);
}
}
}
}
public class WarpOptions : BaseOptions
{
[JsonProperty("Teleport to random point in X vicinity (0 to disable)")]
public float VicinityTeleportRadius { get; set; }
[JsonProperty("Radius to check for NPC's when teleporting to a monument warp point")]
public float MonumentWarpNPCRadius { get; set; }
[JsonProperty("Monument warp points")]
public Hash<string, MonumentWarp> MonumentWarps { get; set; }
public class MonumentWarp
{
[JsonProperty("Generate warp for this monument")]
public bool Enabled { get; set; }
[JsonProperty("Custom chat command")]
public string Command { get; set; } = string.Empty;
[JsonProperty("Required permission (prefix with teleportgui.)")]
public string Permission { get; set; } = string.Empty;
}
}
public class AdminOptions
{
[JsonProperty("Don't notify user's when a admin teleports to them")]
public bool Silent { get; set; }
[JsonProperty("Allow instant teleportation for admins")]
public bool Instant { get; set; }
}
public class UIOptions
{
[JsonProperty("Disable UI")]
public bool DisableUI { get; set; }
[JsonProperty("Hide admins from player search list")]
public bool HideAdminsInUI { get; set; }
[JsonProperty("Hide warp points if the player doesn't have permission")]
public bool HideWarpsNoPermission { get; set; }
[JsonProperty("UI Colors")]
public UIColors Colors { get; set; }
}
public class ChatOptions
{
[JsonProperty("Use chat prefix")]
public bool UsePrefix { get; set; }
[JsonProperty("Chat prefix")]
public string Prefix { get; set; }
[JsonProperty("Chat icon (steam ID)")]
public ulong Icon { get; set; }
}
public class TeleportDelayOptions : VipOption
{
[JsonProperty("Default time until teleport (seconds)")]
public override int Default { get; set; }
[JsonProperty("VIP time until teleport (permission | seconds)")]
public override Dictionary<string, int> VIP { get; set; }
public void RegisterPermissions(Permission permission, Plugin plugin)
{
foreach (string perm in VIP.Keys)
{
if (!permission.PermissionExists(perm))
permission.RegisterPermission(perm, plugin);
}
}
}
public class CooldownOptions : VipOption
{
[JsonProperty("Default cooldown time (seconds)")]
public override int Default { get; set; }
[JsonProperty("VIP cooldown times (permission | seconds)")]
public override Dictionary<string, int> VIP { get; set; }
public void RegisterPermissions(Permission permission, Plugin plugin)
{
foreach (string perm in VIP.Keys)
{
if (!permission.PermissionExists(perm))
permission.RegisterPermission(perm, plugin);
}
}
}
public class LimitOptions : VipOption
{
[JsonProperty("Default daily limit (0 disables limits entirely)")]
public override int Default { get; set; }
[JsonProperty("VIP daily limit (permission | limit)")]
public override Dictionary<string, int> VIP { get; set; }
public void RegisterPermissions(Permission permission, Plugin plugin)
{
foreach (string perm in VIP.Keys)
{
if (!permission.PermissionExists(perm))
permission.RegisterPermission(perm, plugin);
}
}
}
public class PurchaseOptions : VipOption
{
[JsonProperty("Require payment to teleport after daily limit has been reached")]
public bool PayAfterUsingDailyLimits { get; set; }
[JsonProperty("Payment mode (ServerRewards, Economics, Scrap)")]
public PurchaseMode Mode { get; set; }
[JsonProperty("Default payment cost")]
public override int Default { get; set; }
[JsonProperty("VIP payment cost (permission | cost)")]
public override Dictionary<string, int> VIP { get; set; }
}
public abstract class VipOption
{
public abstract int Default { get; set; }
public abstract Dictionary<string, int> VIP { get; set; }
public int GetLowestOption(BasePlayer player)
{
int t = int.MaxValue;
foreach (KeyValuePair<string, int> kvp in VIP)
{
if (player.HasPermission(kvp.Key) && kvp.Value < t)
t = kvp.Value;
}
if (t == int.MaxValue)
t = Default;
return t;
}
public int GetHighestOption(BasePlayer player)
{
int t = 0;
foreach (KeyValuePair<string, int> kvp in VIP)
{
if (player.HasPermission(kvp.Key) && kvp.Value > t)
t = kvp.Value;
}
if (t == 0)
t = Default;
return t;
}
}
public class UIColors
{
public Color Background { get; set; }
public Color Panel { get; set; }
public Color Header { get; set; }
public Color Button { get; set; }
public Color Close { get; set; }
public Color Highlight { get; set; }
public class Color
{
public string Hex { get; set; }
public float Alpha { get; set; }
}
}
#region Teleport Conditions
public class TeleportConditions
{
[JsonProperty("Can teleport whilst bleeding")]
public WhilstBleedingCondition WhilstBleeding { get; set; }
[JsonProperty("Can teleport whilst crafting")]
public WhenCraftingCondition WhenCrafting { get; set; }
[JsonProperty("Can teleport if mounted")]
public MountedCondition Mounted { get; set; }
[JsonProperty("Can teleport if building blocked")]
public BuildingBlockedCondition BuildingBlocked { get; set; }
[JsonProperty("Can teleport if raid blocked")]
public RaidBlockedCondition RaidBlocked { get; set; }
[JsonProperty("Can teleport if on cargo ship")]
public CargoShipCondition CargoShip { get; set; }
[JsonProperty("Can teleport if on hot air balloon")]
public HotAirBalloonCondition HotAirBalloon { get; set; }
[JsonProperty("Can teleport if near oil rig")]
public OilRigCondition OilRig { get; set; }
[JsonProperty("Can teleport if in underwater labs")]
public UnderwaterLabsCondition UnderwaterLabs { get; set; }
[JsonProperty("Can teleport if in water")]
public InWaterCondition InWater { get; set; }
[JsonProperty("Can teleport if in notp zone")]
public NoTPZoneCondition NoTpZone { get; set; }
[JsonProperty("Can teleport if in safe zone")]
public SafeZoneCondition SafeZone { get; set; }
[JsonProperty("Can teleport if hostile")]
public HostileCondition Hostile { get; set; }
[JsonProperty("Can teleport if in monument")]
public InMonumentCondition InMonument { get; set; }
[JsonProperty("Can teleport if in any topologies (advanced)")]
public CustomTopologyCondition Topology { get; set; }
public bool MeetsConditions(BasePlayer player, BasePlayer target)
{
if (player.IsWounded())
{
SendReply(player, "Condition.Wounded");
return false;
}
string canTeleport = Interface.Oxide.CallHook("CanTeleport", player) as string;
if (canTeleport != null)
{
SendReply(player, canTeleport);
return false;
}
if (!WhilstBleeding.CanTeleportTo(player, target))
return false;
if (!WhenCrafting.CanTeleportTo(player, target))
return false;
if (!Mounted.CanTeleportTo(player, target))
return false;
if (!BuildingBlocked.CanTeleportTo(player, target))
return false;
if (!RaidBlocked.CanTeleportTo(player, target))
return false;
if (!CargoShip.CanTeleportTo(player, target))
return false;
if (!HotAirBalloon.CanTeleportTo(player, target))
return false;
if (!OilRig.CanTeleportTo(player, target))
return false;
if (!UnderwaterLabs.CanTeleportTo(player, target))
return false;
if (!InWater.CanTeleportTo(player, target))
return false;
if (!NoTpZone.CanTeleportTo(player, target))
return false;
if (!SafeZone.CanTeleportTo(player, target))
return false;
if (!Hostile.OnlyWarps && !Hostile.CanTeleportTo(player, target))
return false;
if (!InMonument.CanTeleportTo(player, target))
return false;
if (!Topology.CanTeleportTo(player, target))
return false;
return true;
}
public bool MeetsConditions(BasePlayer player, Vector3 target)
{
if (player.IsWounded())
{
SendReply(player, "Condition.Wounded");
return false;
}
string canTeleport = Interface.Oxide.CallHook("CanTeleport", player) as string;
if (canTeleport != null)
{
SendReply(player, canTeleport);
return false;
}
if (!WhilstBleeding.CanTeleportTo(player))
return false;
if (!WhenCrafting.CanTeleportTo(player))
return false;
if (!Mounted.CanTeleportTo(player))
return false;
if (!BuildingBlocked.CanTeleportTo(player, target))
return false;
if (!RaidBlocked.CanTeleportTo(player))
return false;
if (!CargoShip.CanTeleportTo(player))
return false;
if (!HotAirBalloon.CanTeleportTo(player))
return false;
if (!OilRig.CanTeleportTo(player, target))
return false;
if (!UnderwaterLabs.CanTeleportTo(player, target))
return false;
if (!InWater.CanTeleportTo(player))
return false;
if (!NoTpZone.CanTeleportTo(player))
return false;
if (!SafeZone.CanTeleportTo(player))
return false;
if (!Hostile.OnlyWarps && !Hostile.CanTeleportTo(player))
return false;
if (!InMonument.CanTeleportTo(player, target))
return false;
if (!Topology.CanTeleportTo(player, target))
return false;
return true;
}
public bool MeetsWarpConditions(BasePlayer player, Vector3 target)
{
if (player.IsWounded())
{
SendReply(player, "Condition.Wounded");
return false;
}
string canTeleport = Interface.Oxide.CallHook("CanTeleport", player) as string;
if (canTeleport != null)
{
SendReply(player, canTeleport);
return false;
}
if (!WhilstBleeding.CanTeleportTo(player))
return false;
if (!WhenCrafting.CanTeleportTo(player))
return false;
if (!Mounted.CanTeleportTo(player))
return false;
if (!BuildingBlocked.CanTeleportTo(player))
return false;
if (!RaidBlocked.CanTeleportTo(player))
return false;
if (!CargoShip.CanTeleportTo(player))
return false;
if (!HotAirBalloon.CanTeleportTo(player))
return false;
if (!OilRig.CanTeleportTo(player))
return false;
if (!UnderwaterLabs.CanTeleportTo(player))
return false;
if (!InWater.CanTeleportTo(player))
return false;
if (!SafeZone.CanTeleportTo(player))
return false;
if (!NoTpZone.CanTeleportTo(player))
return false;
if (!Topology.CanTeleportTo(player))
return false;
if (!Hostile.CanTeleportTo(player))
return false;
if (!InMonument.CanTeleportTo(player))
return false;
if (!Topology.CanTeleportTo(player))
return false;
return true;
}
}
public class WhilstBleedingCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.metabolism.bleeding.value > 0f)
{
SendReply(player, "Condition.Bleeding.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.metabolism.bleeding.value > 0f)
{
SendReply(player, "Condition.Bleeding.Self");
return false;
}
return true;
}
}
public class WhenCraftingCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.metabolism.bleeding.value > 0f)
{
SendReply(player, "Condition.Crafting.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.inventory.crafting.queue.Count > 0)
{
SendReply(player, "Condition.Crafting.Self");
return false;
}
return true;
}
}
public class BuildingBlockedCondition : AllTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && !target.CanBuild())
{
SendReply(player, "Condition.BuildingBlocked.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && !player.CanBuild())
{
SendReply(player, "Condition.BuildingBlocked.Self");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player, Vector3 position)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPosition && player.IsBuildingBlocked(position, player.transform.rotation, player.bounds))
{
SendReply(player, "Condition.BuildingBlocked.Position");
return false;
}
return true;
}
}
public class RaidBlockedCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer)
{
if (NoEscape.IsLoaded && (NoEscape.IsCombatBlocked(target) || NoEscape.IsRaidBlocked(target)))
{
SendReply(player, "Condition.RaidBlocked.Target");
return false;
}
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport)
{
if (NoEscape.IsLoaded && (NoEscape.IsCombatBlocked(player) || NoEscape.IsRaidBlocked(player)))
{
SendReply(player, "Condition.RaidBlocked.Self");
return false;
}
}
return true;
}
}
public class CargoShipCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.GetParentEntity() is CargoShip)
{
SendReply(player, "Condition.CargoShip.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.GetParentEntity() is CargoShip)
{
SendReply(player, "Condition.CargoShip.Self");
return false;
}
return true;
}
}
public class HotAirBalloonCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.GetParentEntity() is HotAirBalloon)
{
SendReply(player, "Condition.HAB.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.GetParentEntity() is HotAirBalloon)
{
SendReply(player, "Condition.HAB.Self");
return false;
}
return true;
}
}
public class OilRigCondition : AllTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && IsNearOilRig(target.transform.position))
{
SendReply(player, "Condition.OilRig.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && IsNearOilRig(player.transform.position))
{
SendReply(player, "Condition.OilRig.Self");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player, Vector3 position)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPosition && IsNearOilRig(position))
{
SendReply(player, "Condition.OilRig.Position");
return false;
}
return true;
}
}
public class UnderwaterLabsCondition : AllTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && IsInUnderwaterLab(target.transform.position))
{
SendReply(player, "Condition.UnderwaterLabs.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && IsInUnderwaterLab(player.transform.position))
{
SendReply(player, "Condition.UnderwaterLabs.Self");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player, Vector3 position)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPosition && IsInUnderwaterLab(position))
{
SendReply(player, "Condition.UnderwaterLabs.Position");
return false;
}
return true;
}
}
public class MountedCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.isMounted)
{
SendReply(player, "Condition.Mounted.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.isMounted)
{
SendReply(player, "Condition.Mounted.Self");
return false;
}
return true;
}
}
public class InWaterCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && IsInWater(target))
{
SendReply(player, "Condition.InWater.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && IsInWater(player))
{
SendReply(player, "Condition.InWater.Self");
return false;
}
return true;
}
}
public class NoTPZoneCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer)
{
if (ZoneManager.IsLoaded && ZoneManager.PlayerHasFlag(target, "notp"))
{
SendReply(player, "Condition.NoTPZone.Target");
return false;
}
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport)
{
if (ZoneManager.IsLoaded && ZoneManager.PlayerHasFlag(player, "notp"))
{
SendReply(player, "Condition.NoTPZone.Self");
return false;
}
}
return true;
}
}
public class SafeZoneCondition : TargetTeleportCondition
{
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.InSafeZone())
{
SendReply(player, "Condition.SafeZone.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.InSafeZone())
{
SendReply(player, "Condition.SafeZone.Self");
return false;
}
return true;
}
}
public class HostileCondition : TargetTeleportCondition
{
[JsonProperty("Only check when warping")]
public bool OnlyWarps { get; set; }
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && target.IsHostile())
{
SendReply(player, "Condition.Hostile.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && player.IsHostile())
{
SendReply(player, "Condition.Hostile.Self");
return false;
}
return true;
}
}
public class InMonumentCondition : AllTeleportCondition
{
[JsonProperty("Ignore safe zones when checking condition")]
public bool IgnoreSafeZones { get; set; }
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && IsInMonument(target.transform.position, IgnoreSafeZones))
{
SendReply(player, "Condition.Monument.Target");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (!CanTeleport && IsInMonument(player.transform.position, IgnoreSafeZones))
{
SendReply(player, "Condition.Monument.Self");
return false;
}
return true;
}
public override bool CanTeleportTo(BasePlayer player, Vector3 position)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPosition && IsInMonument(position, IgnoreSafeZones))
{
SendReply(player, "Condition.Monument.Position");
return false;
}
return true;
}
}
public class CustomTopologyCondition : AllTeleportCondition
{
[JsonProperty("Topology names (ex. ['Road', 'Roadside', 'Cliff'], replacing single quotation marks with double quotation marks)")]
public string[] Topologies { get; set; }
private int m_Topology = int.MaxValue;
[JsonIgnore]
public int Topology
{
get
{
if (m_Topology == int.MaxValue)
{
if (Topologies?.Length == 0)
m_Topology = 0;
else
{
m_Topology = 0;
string[] topologyNames = Enum.GetNames(typeof(TerrainTopology.Enum));
for (int i = 0; i < topologyNames.Length; i++)
{
if (Topologies.Contains(topologyNames[i], StringComparer.OrdinalIgnoreCase))
m_Topology |= 1 << i;
}
}
}
return m_Topology;
}
}
public override bool CanTeleportTo(BasePlayer player, BasePlayer target)
{
if (Topology != 0)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPlayer && ContainsTopologyAtPoint(target.transform.position))
{
SendReply(player, "Condition.Topology.Target");
return false;
}
}
return true;
}
public override bool CanTeleportTo(BasePlayer player)
{
if (Topology != 0)
{
if (!CanTeleport && ContainsTopologyAtPoint(player.transform.position))
{
SendReply(player, "Condition.Topology.Self");
return false;
}
}
return true;
}
public override bool CanTeleportTo(BasePlayer player, Vector3 position)
{
if (Topology != 0)
{
if (!CanTeleportTo(player))
return false;
if (!CanTeleportTargetPosition && ContainsTopologyAtPoint(position))
{
SendReply(player, "Condition.Topology.Position");
return false;
}
}
return true;
}
private bool ContainsTopologyAtPoint(Vector3 position) => (TerrainMeta.TopologyMap.GetTopology(position) & Topology) != 0;
}
public abstract class AllTeleportCondition : TargetTeleportCondition
{
[JsonProperty("Can teleport if target position has condition")]
public bool CanTeleportTargetPosition { get; set; }
public abstract bool CanTeleportTo(BasePlayer player, Vector3 position);
}
public abstract class TargetTeleportCondition : BasicTeleportCondition
{
[JsonProperty("Can teleport if target player has condition")]
public bool CanTeleportTargetPlayer { get; set; }
public abstract bool CanTeleportTo(BasePlayer player, BasePlayer target);
}
public abstract class BasicTeleportCondition
{
[JsonProperty("Can teleport if player has condition")]
public bool CanTeleport { get; set; }
public abstract bool CanTeleportTo(BasePlayer player);
}
#endregion
public void RegisterCustomPermissions(Permission permission, Plugin plugin)
{
TP.Delay.RegisterPermissions(permission, plugin);
TP.Cooldown.RegisterPermissions(permission, plugin);
TP.Limits.RegisterPermissions(permission, plugin);
Home.Delay.RegisterPermissions(permission, plugin);
Home.Cooldown.RegisterPermissions(permission, plugin);
Home.Limits.RegisterPermissions(permission, plugin);
Home.MaxHomes.RegisterPermissions(permission, plugin);
Warp.Delay.RegisterPermissions(permission, plugin);
Warp.Cooldown.RegisterPermissions(permission, plugin);
Warp.Limits.RegisterPermissions(permission, plugin);
}
}
protected override void LoadConfig()
{
base.LoadConfig();
Configuration = ConfigurationData as ConfigData;
}
protected override ConfigurationFile OnLoadConfig(ref ConfigurationFile configurationFile) => configurationFile = new ConfigurationFile<ConfigData>(Config);
protected override void OnConfigurationUpdated(VersionNumber oldVersion)
{
ConfigData baseConfigData = GenerateDefaultConfiguration<ConfigData>();
if (oldVersion < new VersionNumber(2, 0, 0))
ConfigurationData = baseConfigData;
if (oldVersion < new VersionNumber(2, 0, 3))
(ConfigurationData as ConfigData).Warp.MonumentWarps = new Hash<string, ConfigData.WarpOptions.MonumentWarp>();
if (oldVersion < new VersionNumber(2, 0, 4))
(ConfigurationData as ConfigData).Conditions.Topology = baseConfigData.Conditions.Topology;
if (oldVersion < new VersionNumber(2, 0, 8))
(ConfigurationData as ConfigData).Warp.MonumentWarpNPCRadius = 25f;
if (oldVersion < new VersionNumber(2, 0, 13))
(ConfigurationData as ConfigData).Conditions.Hostile = baseConfigData.Conditions.Hostile;
Configuration = ConfigurationData as ConfigData;
}
protected override T GenerateDefaultConfiguration<T>()
{
return new ConfigData
{
Chat = new ConfigData.ChatOptions
{
UsePrefix = true,
Prefix = "<color=#C4FF00>TP: </color>",
Icon = 0UL
},
TP = new ConfigData.TeleportOptions
{
RequestTimeout = 30,
CancelOnDamage = true,
CancelOnDeath = false,
Delay = new ConfigData.TeleportDelayOptions
{
Default = 15,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 10,
["teleportgui.elite"] = 5,
["teleportgui.god"] = 3,
}
},
Cooldown = new ConfigData.CooldownOptions
{
Default = 10,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 60,
["teleportgui.elite"] = 30,
["teleportgui.god"] = 15,
}
},
Limits = new ConfigData.LimitOptions
{
Default = 3,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 5,
["teleportgui.elite"] = 8,
["teleportgui.god"] = 15,
}
},
Purchase = new ConfigData.PurchaseOptions
{
Mode = PurchaseMode.Scrap,
PayAfterUsingDailyLimits = false,
Default = 10,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 8,
["teleportgui.elite"] = 6,
["teleportgui.god"] = 5,
}
},
CommandAliases = new List<string>(),
},
Home = new ConfigData.HomeOptions
{
CancelOnDamage = true,
CancelOnDeath = false,
AllowSetHomeInBuildBlocked = false,
MustSetHomeOnBuilding = true,
CanSetHomeOnFloor = false,
MinimumHomeRadiusDistance = 20f,
WipeHomesOnNewServerSave = true,
MaxHomes = new ConfigData.HomeOptions.HomeLimits
{
Default = 3,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 5,
["teleportgui.elite"] = 8,
["teleportgui.god"] = 10,
}
},
Delay = new ConfigData.TeleportDelayOptions
{
Default = 15,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 10,
["teleportgui.elite"] = 5,
["teleportgui.god"] = 3,
}
},
Cooldown = new ConfigData.CooldownOptions
{
Default = 10,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 60,
["teleportgui.elite"] = 30,
["teleportgui.god"] = 15,
}
},
Limits = new ConfigData.LimitOptions
{
Default = 3,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 5,
["teleportgui.elite"] = 8,
["teleportgui.god"] = 15,
}
},
Purchase = new ConfigData.PurchaseOptions
{
Mode = PurchaseMode.Scrap,
PayAfterUsingDailyLimits = false,
Default = 10,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 8,
["teleportgui.elite"] = 6,
["teleportgui.god"] = 5,
}
},
CommandAliases = new List<string>(),
SleepingBags = new ConfigData.HomeOptions.SleepingBagOptions
{
CreateHomeOnBagPlacement = false,
CreateHomeOnBedPlacement = false,
CreateHomeOnBeachTowelPlacement = false,
OnlyCreateInBuilding = true,
// RemoveHomeOnRemoval = false,
DisableSetHomeCommand = false
},
},
Warp = new ConfigData.WarpOptions
{
CancelOnDamage = true,
CancelOnDeath = false,
VicinityTeleportRadius = 0,
MonumentWarpNPCRadius = 25f,
Delay = new ConfigData.TeleportDelayOptions
{
Default = 15,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 10,
["teleportgui.elite"] = 5,
["teleportgui.god"] = 3,
}
},
Cooldown = new ConfigData.CooldownOptions
{
Default = 10,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 60,
["teleportgui.elite"] = 30,
["teleportgui.god"] = 15,
}
},
Limits = new ConfigData.LimitOptions
{
Default = 3,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 5,
["teleportgui.elite"] = 8,
["teleportgui.god"] = 15,
}
},
Purchase = new ConfigData.PurchaseOptions
{
Mode = PurchaseMode.Scrap,
PayAfterUsingDailyLimits = false,
Default = 10,
VIP = new Dictionary<string, int>
{
["teleportgui.vip"] = 8,
["teleportgui.elite"] = 6,
["teleportgui.god"] = 5,
}
},
CommandAliases = new List<string>(),
},
Conditions = new ConfigData.TeleportConditions
{
WhilstBleeding = new ConfigData.WhilstBleedingCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = true
},
WhenCrafting = new ConfigData.WhenCraftingCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = true
},
Mounted = new ConfigData.MountedCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false
},
BuildingBlocked = new ConfigData.BuildingBlockedCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
CanTeleportTargetPosition = false
},
RaidBlocked = new ConfigData.RaidBlockedCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
CargoShip = new ConfigData.CargoShipCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
HotAirBalloon = new ConfigData.HotAirBalloonCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
OilRig = new ConfigData.OilRigCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
CanTeleportTargetPosition = false
},
UnderwaterLabs = new ConfigData.UnderwaterLabsCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
CanTeleportTargetPosition = false
},
InWater = new ConfigData.InWaterCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
NoTpZone = new ConfigData.NoTPZoneCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
SafeZone = new ConfigData.SafeZoneCondition
{
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
Hostile = new ConfigData.HostileCondition
{
OnlyWarps = true,
CanTeleport = false,
CanTeleportTargetPlayer = false,
},
InMonument = new ConfigData.InMonumentCondition
{
IgnoreSafeZones = false,
CanTeleport = false,
CanTeleportTargetPlayer = false,
CanTeleportTargetPosition = false
},
Topology = new ConfigData.CustomTopologyCondition
{
CanTeleport = true,
CanTeleportTargetPlayer = true,
CanTeleportTargetPosition = true,
Topologies = Array.Empty<string>()
}
},
PurgeDays = 7,
Admin = new ConfigData.AdminOptions
{
Instant = false,
Silent = false,
},
UI = new ConfigData.UIOptions
{
HideAdminsInUI = false,
Colors = new ConfigData.UIColors
{
Background = new ConfigData.UIColors.Color
{
Hex = "151515",
Alpha = 0.94f
},
Panel = new ConfigData.UIColors.Color
{
Hex = "FFFFFF",
Alpha = 0.165f
},
Header = new ConfigData.UIColors.Color
{
Hex = "C4FF00",
Alpha = 0.314f
},
Button = new ConfigData.UIColors.Color
{
Hex = "2A2E32",
Alpha = 1f
},
Close = new ConfigData.UIColors.Color
{
Hex = "CE422B",
Alpha = 1f
},
Highlight = new ConfigData.UIColors.Color
{
Hex = "C4FF00",
Alpha = 1f
},
}
}
} as T;
}
#endregion
#region Data
private interface IWarpPoint
{
bool IsEnabled();
bool HasPermission(BasePlayer player);
Vector3 GetPosition();
}
private class MonumentWarpPoint : IWarpPoint
{
internal LocalSpawnGenerator m_Spawns;
public string Permission;
public string Shortname { get; }
public string MapGrid { get; }
public int Count => m_Spawns?.Count ?? 0;
public MonumentWarpPoint(string shortname, string grid, string uniqueName, Transform transform, Bounds bounds)
{
Shortname = shortname;
MapGrid = grid;
m_Spawns = new LocalSpawnGenerator(transform, bounds);
if (m_Spawns.Count == 0)
{
Debug.Log($"[TeleportGUI] Failed to generate spawn points in monument {uniqueName}");
m_Spawns = null;
}
}
public bool IsEnabled() => m_Spawns?.Count > 0;
public bool HasPermission(BasePlayer player) => string.IsNullOrEmpty(Permission) || player.HasPermission(Permission);
public Vector3 GetPosition() => m_Spawns.GetRandom();
}
private class WarpPoint : IWarpPoint
{
public Vector3 Position;
public string Permission;
public bool HasPermission(BasePlayer player) => string.IsNullOrEmpty(Permission) || player.HasPermission(Permission);
public bool IsEnabled() => true;
public Vector3 GetPosition()
{
Vector3 warpPosition = Position;
if (Configuration.Warp.VicinityTeleportRadius > 0f)
{
Vector3 random = Random.insideUnitCircle * Configuration.Warp.VicinityTeleportRadius;
warpPosition.x += random.x;
warpPosition.z += random.y;
warpPosition.y = TerrainMeta.HeightMap.GetHeight(warpPosition);
}
return warpPosition;
}
}
private class TeleportData
{
public Hash<ulong, User> Users = new Hash<ulong, User>();
public double LastResetTime;
[JsonIgnore]
public bool ShouldResetUses
{
get
{
DateTime now = DateTime.UtcNow;
DateTime lastTime = DateTimeOffset.FromUnixTimeSeconds((long)LastResetTime).DateTime;
return now.Day != lastTime.Day || now.Month != lastTime.Month || now.Year != lastTime.Year;
}
}
public void WipeHomesData()
{
foreach (User user in Users.Values)
{
user.Homes.Clear();
user.HomeUsage.Reset();
}
m_TeleportData.Save();
}
public class User
{
public Hash<string, Vector3> Locations = new Hash<string, Vector3>();
public Hash<string, HomePoint> Homes = new Hash<string, HomePoint>();
public Usage TPUsage = new Usage();
public Usage HomeUsage = new Usage();
public Usage WarpUsage = new Usage();
public double LastOnlineTime;
public bool ShowSleepers;
public AutoAcceptEnum AutoAccept;
[JsonIgnore] public string SearchString = string.Empty;
[JsonIgnore] public int Page = 0;
public void OnClose()
{
SearchString = string.Empty;
Page = 0;
}
[Flags]
public enum AutoAcceptEnum { None = 0, Clans = 1 << 0, Teams = 1 << 1, Friends = 1 << 2, All = 1 << 3 }
public class Usage
{
public int UsesToday;
public double Cooldown;
public bool IsOnCooldown() => Cooldown > CurrentTime();
public void Reset()
{
UsesToday = 0;
Cooldown = 0;
}
}
public class HomePoint
{
public Vector3 Position;
public ulong EntityID;
}
}
}
#endregion
#region Localization
protected override void PopulatePhrases(){}
private void PrepareLocalization()
{
m_Messages = new Dictionary<string, string>()
{
["Purchase.Error.SR"] = "You do not have enough RP to purchase this teleport ({0} RP)",
["Purchase.Error.Economics"] = "You do not have enough coins to purchase this teleport ({0} coins)",
["Purchase.Error.Scrap"] = "You do not have enough scrap to purchase this teleport ({0} scrap)",
["Purchase.Success.SR"] = "You have purchased this teleport for {0} RP",
["Purchase.Success.Economics"] = "You have purchased this teleport for {0} coinds",
["Purchase.Success.Scrap"] = "You have purchased this teleport for {0} scrap",
["Purchase.Refund.SR"] = "You were refunded {0} RP",
["Purchase.Refund.Economics"] = "You were refunded {0} coinds",
["Purchase.Refund.Scrap"] = "You were refunded {0} scrap",
["TPRequest.Here.Sent"] = "You sent a teleport here request to {0}",
["TPRequest.Here.Received"] = "You have received a teleport here request from {0}",
["TPRequest.Sent"] = "You sent a teleport request to {0}",
["TPRequest.Received"] = "You have received a teleport request from {0}",
["TPRequest.Instant.Sent"] = "Your teleport request to {0} was accepted automatically. Teleporting in {1} seconds",
["TPRequest.Instant.Received"] = "The teleport request from {0} was accepted as you have auto-accept enabled. Teleporting in {1} seconds.",
["TPRequest.To.Accepted"] = "Your teleport request to {0} was accepted. Teleporting in {1} seconds.",
["TPRequest.From.Accepted"] = "Teleport request from {0} accepted. Telporting in {1} seconds.",
["TPRequest.Here.To.Denied"] = "Your teleport here request to {0} was denied",
["TPRequest.Here.From.Denied"] = "Teleport here request from {0} denied",
["TPRequest.To.Denied"] = "Your teleport request to {0} was denied",
["TPRequest.From.Denied"] = "Teleport request from {0} denied",
["TPRequest.To.Cancelled"] = "Teleport request to {0} cancelled",
["TPRequest.From.Cancelled"] = "Teleport request from {0} cancelled",
["TPRequest.Here.To.TimeOut"] = "Your teleport here request to {0} timed out",
["TPRequest.Here.From.TimeOut"] = "The teleport here request from {0} timed out",
["TPRequest.To.TimeOut"] = "Your teleport request to {0} timed out",
["TPRequest.From.TimeOut"] = "The teleport request from {0} timed out",
["TPR.Admin.YouWereTPTo"] = "You were teleported to {0}",
["TPR.Admin.TPTo"] = "{0} teleported to you",
["TPR.Admin.YouTPToYou"] = "You teleported {0} to your position",
["TPR.YouTPTo"] = "You teleported to {0}",
["TPR.InvalidSynax"] = "Invalid TPR syntax! /tpr {player name}",
["TPR.Here.InvalidSynax"] = "Invalid TPR syntax! /tprhere {player name}",
["TP.TargetPendingRequest"] = "{0} has a pending teleport request",
["TP.SelfHasPendingRequest"] = "You have a pending teleport request",
["TP.To.Cancelled"] = "Teleport to {0} was cancelled",
["TP.From.Cancelled"] = "Teleport from {0} was cancelled",
["TP.To.Cancelled.Reason"] = "Teleport to {0} was cancelled : {1}",
["TP.From.Cancelled.Reason"] = "Teleport from {0} was cancelled : {1}",
["TP.Error.TargetDead"] = "Teleport cancelled. The target player has died",
["TP.Error.NoPending"] = "You have no teleports to cancel",
["TP.Error.PositionSyntax"] = "Invalid position sytanx! /tp {x} {y} {z}",
["TP.Success.Position"] = "You teleported to {0} {1} {2}",
["TPB.NoBackLocation"] = "You do not have a location to TP back to",
["TPB.Success"] = "You teleported back to your previous location",
["TPB.Admin.NoBackLocation"] = "{0} does not have a location to TP back to",
["TPB.Admin.Success"] = "You teleported {0} back to their previous location",
["TPB.Admin.Success.Target"] = "You were teleported back to your previous location by an admin",
["TPA.NonePending"] = "You do not have any pending teleport requests",
["TP.CantTeleportSelf"] = "You can not teleport to yourself",
["TPL.NoNameSpecified"] = "You must specify a location name",
["TPL.Success"] = "You have saved your position as teleport location {0}",
["TPL.Error.NoLocations"] = "You do not have any saved locations",
["TPL.Error.NoLocation.Name"] = "You do not have a saved location with the name {0}",
["TPL.List"] = "Your saved locations: {0}",
["Home.SelfHasPendingRequest"] = "You have a pending home teleport",
["Home.Error.IsBuildingBlocked"] = "You can not set home in a building blocked area",
["Home.Error.LimitReached"] = "You already have the maximum number of homes allowed",
["Home.Error.NearbyRadius"] = "You already have a home set {0}m away. The minimum distance for homes is {1}m",
["Home.Error.NotOnFoundation"] = "Homes can only be set on foundations",
["Home.Error.NotOnFoundationFloor"] = "Homes can only be set on foundations and floors",
["Home.Error.NoTPZone"] = "Homes can not be set in NoTP zones",
["Home.Error.NoBackLocation"] = "You do not have a location to TP back to",
["Home.Error.NoPending"] = "You have no pending home teleports to cancel",
["Home.Error.Invalid"] = "The home {0} has been destroyed as the block it was placed on has been destroyed",
["Home.Error.DoesntExist"] = "The home {0} doesnt exist",
["Home.Error.NoHomes"] = "You have not created any homes",
["Home.Error.AlreadyExists"] = "A home with the name {0} already exists",
["Home.Error.BagDisableCommand"] = "The /sethome is disabled on this server. Homes are created on bags/beds",
["Home.Error.SetHomeSyntax"] = "Invalid sethome syntax! /sethome {homename}",
["Home.Error.DelHomeSyntax"] = "Invalid delhome syntax! /delhome {homename}",
["Home.Error.NoHomes.Target"] = "{0} has not created any homes",
["Home.Error.DoesntExist.Target"] = "{0} does not have a home with the name {1}",
["Home.Cancelled"] = "You have cancelled your pending home teleport",
["Home.List"] = "Your homes: {0}",
["Home.List.Target"] = "Homes for {0}: {1}",
["Home.Success.Created"] = "You have created a new home with the name {0}",
["Home.Success.Created.Bed"] = "A home point has been created on this bag/bed with the name {0}",
["Home.Success.Created.Remaining"] = "You have created a new home with the name {0} ({1} homes remaining)",
["Home.Success.Created.Bed.Remaining"] = "A home point has been created on this bag/bed with the name {0} ({1} homes remaining)",
["Home.Success.Deleted"] = "You have deleted the home {0}",
["Notification.BedHomeDestroyed"] = "Your home {0} was destroyed",
["Warp.SelfHasPendingRequest"] = "You have a pending warp teleport",
["Warp.Error.DoesntExist"] = "The warp {0} doesnt exist",
["Warp.Error.NoPermission"] = "You do not have permission to use this warp point",
["Warp.Error.NoBackLocation"] = "You do not have a location to TP back to",
["WarpTo.Error.Syntax"] = "Invalid warp syntax! /warp {to/list} {warpname}",
["WarpTo.List"] = "Available warps: {0}",
["WarpAdd.Error.Syntax"] = "Invalid warp add syntax! /warpadd {warpname} {opt:permission}",
["WarpAdd.Error.Exists"] = "A warp with the name {0} already exists",
["WarpAdd.Success"] = "You have created a warp point on your position with the name {0}",
["WarpAdd.Success.Permission"] = "You have created a warp point on your position with the name {0} and permission {1}",
["WarpRemove.Error.Syntax"] = "Invalid warp remove syntax! /warpremove {warpname}",
["WarpRemove.Success"] = "You have removed the warp point {0}",
["TP.CooldownFormat"] = "Your teleport is on cooldown for another {0}",
["Home.CooldownFormat"] = "Your home teleport is on cooldown for another {0}",
["Warp.CooldownFormat"] = "Your warp teleport is on cooldown for another {0}",
["TP.MaxTeleportsReached"] = "You have reached your max teleports for today.",
["Home.MaxTeleportsReached"] = "You have reached your max home teleports for today.",
["Warp.MaxTeleportsReached"] = "You have reached your max warp teleports for today.",
["General.InsideFoundation"] = "The target position is inside a foundation",
["General.NoPermission"] = "You do not have permission to use this command",
["General.PlayerNotFound"] = "Unable to find a player with the name {0}",
["General.MultiplePlayersFound"] = "Multiple players found with the name {0}\n{1}",
["Notification.TP.Remaining"] = "{0} teleports remaining today.",
["Notification.Home.Remaining"] = "{0} home teleports remaining today.",
["Notification.Warp.Remaining"] = "{0} warp teleports remaining today.",
["UI.Title"] = "Teleport GUI",
["UI.Teleport"] = "Teleport",
["UI.Homes"] = "Homes",
["UI.Warps"] = "Warps",
["UI.CostToTP"] = "Cost to TP: {0} {1}",
["UI.TPLimit"] = "You have used all your teleports for today",
["UI.DailyLimitRemain"] = "Daily Limit Remaining: {0}",
["UI.CooldownRemain"] = "Cooldown Remaining : %TIME_LEFT%s",
["UI.NoPlayers"] = "No players available at the moment",
["UI.NoHomes"] = "You have not created any homes",
["UI.NoWarps"] = "No warp points have been created",
["UI.TPR"] = "TPR",
["UI.TPHere"] = "HERE",
["UI.HomeName"] = "Home Name",
["UI.WarpName"] = "Warp Name",
["UI.Permission"] = "Permission",
["UI.CreateNewWarp"] = "Create Warp Point",
["UI.CreateNewHome"] = "Create New Home",
["UI.Save"] = "Save",
["UI.Cancel"] = "Cancel",
["UI.Close"] = "Close",
["UI.AA.Clan"] = "Auto accept clan",
["UI.AA.Team"] = "Auto accept team",
["UI.AA.Friend"] = "Auto accept friends",
["UI.AA.All"] = "Auto accept all",
["UI.ShowSleepers"] = "Show sleepers",
["UI.TeleportSettings"] = "Teleport Settings",
["PurchaseMode.Scrap"] = "Scrap",
["PurchaseMode.ServerRewards"] = "RP",
["PurchaseMode.Economics"] = "coins",
["Popup.Incoming.TPHere"] = "INCOMING TPHERE (%TIME_LEFT%s)",
["Popup.Incoming.TPR"] = "INCOMING TPR (%TIME_LEFT%s)",
["Popup.Outgoing.TPHere"] = "OUTGOING TPHERE (%TIME_LEFT%s)",
["Popup.Outgoing.TPR"] = "OUTGOING TPR (%TIME_LEFT%s)",
["Popup.Incoming.TP"] = "INCOMING TP IN %TIME_LEFT%s",
["Popup.Outgoing.TP"] = "TELEPORTING IN %TIME_LEFT%s",
["Popup.Outgoing.TP.Home"] = "TELEPORTING IN %TIME_LEFT%s",
["Popup.Outgoing.TP.Warp"] = "TELEPORTING IN %TIME_LEFT%s",
["Condition.Wounded"] = "You can not teleport whilst wounded",
["Condition.Bleeding.Self"] = "You can not teleport whilst bleeding",
["Condition.Bleeding.Target"] = "You can not teleport when the target is bleeding",
["Condition.Mounted.Self"] = "You can not teleport whilst mounted",
["Condition.Mounted.Target"] = "You can not teleport when the target is mounted",
["Condition.InWater.Self"] = "You can not teleport whilst in water",
["Condition.InWater.Target"] = "You can not teleport when the target is in water",
["Condition.NoTPZone.Self"] = "You can not teleport whilst in a NoTP zone",
["Condition.NoTPZone.Target"] = "You can not teleport when the target is in a NoTP zone",
["Condition.SafeZone.Self"] = "You can not teleport whilst in a safe zone",
["Condition.SafeZone.Target"] = "You can not teleport when the target is in a safe zone",
["Condition.Hostile.Self"] = "You can not teleport whilst deemed hostile",
["Condition.Hostile.Target"] = "You can not teleport when the target is deemed hostile",
["Condition.Crafting.Self"] = "You can not teleport whilst crafting",
["Condition.Crafting.Target"] = "You can not teleport when the target is crafting",
["Condition.BuildingBlocked.Self"] = "You can not teleport whilst building blocked",
["Condition.BuildingBlocked.Target"] = "You can not teleport when the target is building blocked",
["Condition.BuildingBlocked.Position"] = "You can not teleport as you are building blocked in the desired location",
["Condition.RaidBlocked.Self"] = "You can not teleport whilst raid/combat blocked",
["Condition.RaidBlocked.Target"] = "You can not teleport when the target is raid/combat blocked",
["Condition.CargoShip.Self"] = "You can not teleport whilst on the cargo ship",
["Condition.CargoShip.Target"] = "You can not teleport when the target is on the cargo ship",
["Condition.HAB.Self"] = "You can not teleport whilst in a hot air balloon",
["Condition.HAB.Target"] = "You can not teleport when the target is in a hot air balloon",
["Condition.OilRig.Self"] = "You can not teleport whilst you are near the oil rig",
["Condition.OilRig.Target"] = "You can not teleport when the target is near the oil rig",
["Condition.OilRig.Position"] = "You can not teleport as the desired location is too close to the oil rig",
["Condition.UnderwaterLabs.Self"] = "You can not teleport whilst you are in underwater labs",
["Condition.UnderwaterLabs.Target"] = "You can not teleport when the target is in underwater labs",
["Condition.UnderwaterLabs.Position"] = "You can not teleport as the desired location is too close to underwater labs",
["Condition.Monument.Self"] = "You can not teleport whilst you are in a monument",
["Condition.Monument.Target"] = "You can not teleport when the target is in a monument",
["Condition.Monument.Position"] = "You can not teleport as the desired location is too close to a monument",
["Condition.Topology.Self"] = "You can not teleport from your current position",
["Condition.Topology.Target"] = "You can not teleport to the targets current position",
["Condition.Topology.Position"] = "You can not teleport to the target position",
["Reason.Disconnected"] = "Disconnected",
["Reason.TookDamage"] = "Hurt",
["Reason.Death"] = "Died",
["Reason.Declined"] = "Declined",
["Help.Warp.1"] = "/warp to <warpname> - Teleport to the specified warp point",
["Help.Warp.2"] = "/warp list - Show available warp points",
["Help.Warp.3"] = "/warpadd <warpname> - Add a new warp point on your current position",
["Help.Warp.4"] = "/warpremove <warpname> - Delete the specified warp point",
["Help.Homes.1"] = "/home <homename> - Teleport to the specified home",
["Help.Homes.2"] = "/sethome <homename> - Create a home on your current position",
["Help.Homes.3"] = "/delhome <homename> - Delete the home with the specified name",
["Help.Homes.4"] = "/listhomes - List all of your home names",
["Help.Homes.5"] = "/homec - Cancel a pending home teleport",
["Help.TP.1"] = "/tpsave <name> - Save your current position as a TP location",
["Help.TP.2"] = "/tpl <name> - Teleport to the specified TP location",
["Help.TP.3"] = "/tplist - List all of your TP locations",
["Help.TP.4"] = "/tpr <playername> - Request a teleport to the specified player",
["Help.TP.5"] = "/tpa - Accept an incoming TP request",
["Help.TP.6"] = "/tpd - Decline an incoming TP request",
["Help.TP.7"] = "/tpc - Cancel a pending TP request",
["Help.TP.8"] = "/tpb - Teleport back to your previous location",
["Help.TP.9"] = "/tprhere <playername> - Request a teleport that brings the specified player to you",
/*
["Monument.trainyard_1"] = "Trainyard",
["Monument.harbor_2"] = "Harbor",
["Monument.harbor_1"] = "Large Harbor",
["Monument.fishing_village_c"] = "Fishing Village",
["Monument.fishing_village_a"] = "Fishing Village",
["Monument.fishing_village_b"] = "Fishing Village",
["Monument.desert_military_base_a"] = "Military Base",
["Monument.desert_military_base_b"] = "Military Base",
["Monument.desert_military_base_c"] = "Military Base",
["Monument.desert_military_base_d"] = "Military Base",
["Monument.arctic_research_base_a"] = "Arctic Research Base",
["Monument.arctic_research_base_b"] = "Arctic Research Base",
["Monument.arctic_research_base_c"] = "Arctic Research Base",
["Monument.launch_site_1"] = "Launch Site",
["Monument.compound"] = "Compound",
["Monument.bandit_town"] = "Bandit Town",
["Monument.excavator_1"] = "Excavator",
["Monument.junkyard_1"] = "Junkyard",
["Monument.stables_a"] = "Stables",
["Monument.stables_b"] = "Stables",
["Monument.stables_c"] = "Stables",
["Monument.powerplant_1"] = "Powerplant",
["Monument.military_tunnel_1"] = "Military Tunnel",
["Monument.water_treatment_plant_1"] = "Water Treatment",
["Monument.airfield_1"] = "Airfield",
["Monument.radtown_small_3"] = "Radtown",
["Monument.mining_quarry_a"] = "Sulfur Quarry",
["Monument.mining_quarry_b"] = "Stone Quarry",
["Monument.mining_quarry_c"] = "HQM Quarry",
["Monument.sphere_tank"] = "Dome",
["Monument.satellite_dish"] = "Satellite Dish",
["Monument.entrance_bunker_a"] = "Entrance Bunker",
["Monument.entrance_bunker_b"] = "Entrance Bunker",
["Monument.entrance_bunker_c"] = "Entrance Bunker",
["Monument.entrance_bunker_d"] = "Entrance Bunker",
["Monument.warehouse"] = "Warehouse",
["Monument.supermarket_1"] = "Supermarket",
["Monument.gas_station_1"] = "Gas Station",
["Monument.lighthouse"] = "Lighthouse",
["Monument.nuclear_missile_silo"] = "Missle Silo",
["Monument.water_well_a"] = "Water Well",
["Monument.water_well_b"] = "Water Well",
["Monument.water_well_c"] = "Water Well",*/
};
}
#endregion
#region API
private Dictionary<string, Vector3> GetPlayerHomes(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return null;
return user.Homes.ToDictionary(k => k.Key, v => v.Value.Position);
}
private Dictionary<string, Vector3> GetPlayerLocations(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return null;
return user.Locations.ToDictionary(k => k.Key, v => v.Value);
}
private Dictionary<string, Vector3> GetWarpPoints()
{
return m_WarpData.Data.ToDictionary(k => k.Key, v => v.Value.Position);
}
private double GetPlayerHomeCooldown(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return 0;
return user.HomeUsage.Cooldown;
}
private int GetPlayerHomeUses(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return 0;
return user.HomeUsage.UsesToday;
}
private double GetPlayerTPCooldown(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return 0;
return user.TPUsage.Cooldown;
}
private int GetPlayerTPUses(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return 0;
return user.TPUsage.UsesToday;
}
private double GetPlayerWarpCooldown(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return 0;
return user.WarpUsage.Cooldown;
}
private int GetPlayerWarpUses(ulong userID)
{
if (!m_TeleportData.Data.Users.TryGetValue(userID, out TeleportData.User user))
return 0;
return user.WarpUsage.UsesToday;
}
#endregion
private class Monument
{
public string Shortname { get; private set; }
private Transform m_Transform;
private Bounds m_Bounds;
public bool IsSafeZone { get; }
public Monument(string shortname, Transform transform, Bounds bounds)
{
Shortname = shortname;
m_Transform = transform;
m_Bounds = bounds;
IsSafeZone = shortname.Equals("compound") || shortname.Equals("bandit_town");
}
public bool IsInMonument(Vector3 position)
{
Vector3 local = m_Transform.InverseTransformPoint(position);
return m_Bounds.Contains(local);
}
}
#region Monument Warps
[ChatCommand("showgeneratedwarps")]
private void CommandShowWarps(BasePlayer player, string command, string[] args)
{
if (!player.IsAdmin)
return;
foreach (KeyValuePair<string, MonumentWarpPoint> kvp in m_MonumentWarps)
{
foreach (Vector3 v in kvp.Value.m_Spawns.m_SpawnPoints)
{
if (Vector3.Distance(player.transform.position, v) < 300)
player.SendConsoleCommand("ddraw.sphere", 30f, UnityEngine.Color.blue, v, 0.5f);
}
}
}
public class LocalSpawnGenerator
{
internal List<Vector3> m_SpawnPoints;
private List<Vector3> m_AvailablePoints;
public int Count => m_SpawnPoints.Count;
private const int TARGET_LAYERS = ~(1 << 10 | 1 << 18 | 1 << 28 | 1 << 29);
private static readonly string[] m_TargetNames = new string[] { "road", "carpark", "pavement", "walkway", "cliff", "Cliff", "a_lighthouse_ext" };
private static RaycastHit m_RaycastHit;
public LocalSpawnGenerator(Transform transform, Bounds bounds)
{
m_SpawnPoints = new List<Vector3>();
for (int i = 0; i < 150; i++)
{
Vector3 p = transform.TransformPoint(GetRandomPoint(bounds));
if (Physics.SphereCast(new Ray(p, Vector3.down), 0.5f, out m_RaycastHit, bounds.size.y, TARGET_LAYERS, QueryTriggerInteraction.Ignore))
{
if (m_RaycastHit.collider.GetComponent<ProceduralObject>())
continue;
if (Mathf.Abs(Vector3.Dot(m_RaycastHit.normal, Vector3.up)) < 0.9f)
continue;
if (TerrainMeta.WaterMap.GetHeight(m_RaycastHit.point) > m_RaycastHit.point.y)
continue;
Vector3 localPoint = transform.InverseTransformPoint(m_RaycastHit.point);
if (m_RaycastHit.collider is TerrainCollider)
{
m_SpawnPoints.Add(m_RaycastHit.point);
continue;
}
if (bounds.center.y > 0 && localPoint.y > bounds.center.y)
continue;
if (m_TargetNames.Any(m_RaycastHit.collider.name.Contains))
m_SpawnPoints.Add(m_RaycastHit.point);
}
if (Count >= 30)
break;
}
m_AvailablePoints = new List<Vector3>(m_SpawnPoints);
}
private Vector3 GetRandomPoint(Bounds bounds)
{
Vector3 target = new Vector3(Random.Range(bounds.min.x, bounds.max.x), bounds.center.y + bounds.max.y, Random.Range(bounds.min.z, bounds.max.z));
return bounds.ClosestPoint(target);
}
public Vector3 GetRandom(int attempt = 0)
{
Vector3 point = m_AvailablePoints.GetRandom();
m_AvailablePoints.Remove(point);
if (attempt < 5)
{
List<BasePlayer> list = Pool.Get<List<BasePlayer>>();
Vis.Entities(point, Configuration.Warp.MonumentWarpNPCRadius, list, 1 << (int) Rust.Layer.Player_Server);
bool hasNpcsNear = list.Any(x => x.IsNpc);
Pool.FreeUnmanaged(ref list);
if (m_AvailablePoints.Count == 0)
m_AvailablePoints.AddRange(m_SpawnPoints);
if (hasNpcsNear)
return GetRandom(attempt + 1);
}
return point;
}
}
#endregion
#region NTeleportation Data Converter
[ConsoleCommand("teleportgui.convertntp")]
private void CommandConvertNTeleportation(ConsoleSystem.Arg arg)
{
if (arg.Connection != null)
{
SendReply(arg, "This command can only be run via rcon console");
return;
}
if (arg.Args == null || arg.Args.Length == 0)
{
SendReply(arg, "The command usage is 'teleportgui.convertntp <Data File Directory>'\n" +
"If you do not have \"Data File Directory (Blank = Default)\" set in your NTeleportation config then enter the word 'null' as the argument");
return;
}
string folder = arg.GetString(0);
if (folder.Equals("null", StringComparison.OrdinalIgnoreCase))
folder = string.Empty;
NTeleportationLoader.LoadNTeleportationData(folder, out Dictionary<ulong, NTeleportationLoader.AdminData> adminData, out Dictionary<ulong, NTeleportationLoader.HomeData> homeData);
int adminLocations = 0;
int homeLocations = 0;
int adminUsers = 0;
int homeUsers = 0;
if (adminData?.Count > 0)
{
foreach (KeyValuePair<ulong, NTeleportationLoader.AdminData> kvp in adminData)
{
if (!m_TeleportData.Data.Users.TryGetValue(kvp.Key, out TeleportData.User userData))
userData = m_TeleportData.Data.Users[kvp.Key] = new TeleportData.User { LastOnlineTime = CurrentTime() };
userData.Locations.Clear();
foreach (KeyValuePair<string, Vector3> location in kvp.Value.Locations)
{
userData.Locations[location.Key] = location.Value;
adminLocations++;
}
adminUsers++;
}
SendReply(arg, $"[TeleportGUI] Converted {adminLocations} admin locations for {adminUsers} admin users");
}
if (homeData?.Count > 0)
{
foreach (KeyValuePair<ulong, NTeleportationLoader.HomeData> kvp in homeData)
{
if (!m_TeleportData.Data.Users.TryGetValue(kvp.Key, out TeleportData.User userData))
userData = m_TeleportData.Data.Users[kvp.Key] = new TeleportData.User { LastOnlineTime = CurrentTime() };
userData.Homes.Clear();
foreach (KeyValuePair<string, Vector3> home in kvp.Value.Locations)
{
userData.Homes[home.Key] = new TeleportData.User.HomePoint { Position = home.Value };
homeLocations++;
}
homeUsers++;
}
SendReply(arg, $"[TeleportGUI] Converted {homeLocations} home locations for {homeUsers} home users");
}
if (adminUsers > 0 || adminLocations > 0 || homeUsers > 0 || homeLocations > 0)
m_TeleportData.Save();
}
public static class NTeleportationLoader
{
private static Core.Configuration.DynamicConfigFile GetFile(string name, string folder = "")
{
string fileName = string.IsNullOrEmpty(folder) ? $"NTeleportation{name}" : $"{folder}{System.IO.Path.DirectorySeparatorChar}{name}";
if (!Interface.Oxide.DataFileSystem.ExistsDatafile(fileName))
{
Debug.LogError($"[TeleportGUI] Datafile {name} does not exist in folder {System.IO.Path.Combine(Interface.Oxide.DataDirectory, folder)}");
return null;
}
Core.Configuration.DynamicConfigFile file = Interface.Oxide.DataFileSystem.GetFile(fileName);
file.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
file.Settings.Converters = new JsonConverter[] { new Vector3Converter(), new CustomComparerDictionaryCreationConverter<string>(StringComparer.OrdinalIgnoreCase) };
return file;
}
public static void LoadNTeleportationData(string folder, out Dictionary<ulong, AdminData> admin, out Dictionary<ulong, HomeData> home)
{
Core.Configuration.DynamicConfigFile dataAdmin = GetFile("Admin", folder);
admin = dataAdmin?.ReadObject<Dictionary<ulong, AdminData>>();
Core.Configuration.DynamicConfigFile dataHome = GetFile("Home", folder);
home = dataHome?.ReadObject<Dictionary<ulong, HomeData>>();
}
public class AdminData
{
[JsonProperty("pl")]
public Vector3 PreviousLocation { get; set; }
[JsonProperty("l")]
public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
}
public class HomeData
{
[JsonProperty("l")]
public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
[JsonProperty("t")]
public TeleportData Teleports { get; set; } = new TeleportData();
}
public class TeleportData
{
[JsonProperty("a")]
public int Amount { get; set; }
[JsonProperty("d")]
public string Date { get; set; }
[JsonProperty("t")]
public int Timestamp { get; set; }
}
public class CustomComparerDictionaryCreationConverter<T> : CustomCreationConverter<IDictionary>
{
private readonly IEqualityComparer<T> comparer;
public CustomComparerDictionaryCreationConverter(IEqualityComparer<T> comparer)
{
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));
this.comparer = comparer;
}
public override bool CanConvert(Type objectType)
{
return HasCompatibleInterface(objectType) && HasCompatibleConstructor(objectType);
}
private static bool HasCompatibleInterface(Type objectType)
{
return objectType.GetInterfaces().Where(i => HasGenericTypeDefinition(i, typeof(IDictionary<,>))).Any(i => typeof(T).IsAssignableFrom(i.GetGenericArguments().First()));
}
private static bool HasGenericTypeDefinition(Type objectType, Type typeDefinition)
{
return objectType.GetTypeInfo().IsGenericType && objectType.GetGenericTypeDefinition() == typeDefinition;
}
private static bool HasCompatibleConstructor(Type objectType)
{
return objectType.GetConstructor(new[] { typeof(IEqualityComparer<T>) }) != null;
}
public override IDictionary Create(Type objectType)
{
return Activator.CreateInstance(objectType, comparer) as IDictionary;
}
}
}
#endregion
/*#region Server Countdown
// Temp solution for broken countdown component
private class ServerCountdown
{
private static List<Entry> m_Countdowns = new List<Entry>();
public static void Add(BasePlayer player, string name, string phrase, int time)
{
m_Countdowns.Add(new Entry { Player = player, Name = name, Phrase = phrase, Time = time });
if (!ServerMgr.Instance.IsInvoking(Update))
ServerMgr.Instance.InvokeRepeating(Update, 1, 1);
}
private static void Update()
{
for (int i = m_Countdowns.Count - 1; i >= 0; i--)
{
Entry entry = m_Countdowns[i];
if (!entry.Player || entry.Player.IsDead() || !entry.Player.IsConnected)
{
m_Countdowns.RemoveAt(i);
continue;
}
entry.Time -= 1;
UpdateComponent<TextComponent> component = ChaosUI.PrepareUpdate<TextComponent>(entry.Name);
string text = GetTranslatedString(entry.Phrase, entry.Player);
text = text.Replace("%TIME_LEFT%", entry.Time.ToString());
component.Component.Text = text;
component.Send(entry.Player);
if (entry.Time <= 0)
{
m_Countdowns.RemoveAt(i);
}
}
if (m_Countdowns.Count == 0)
{
ServerMgr.Instance.CancelInvoke(Update);
}
}
private class Entry
{
public BasePlayer Player;
public string Name;
public string Phrase;
public int Time;
}
}
#endregion*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment