C#:
using Oxide.Core;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using ConVar;
using Newtonsoft.Json;
using Oxide.Game.Rust.Cui;
using Rust;
using UnityEngine;
using VLB;
using Color = UnityEngine.Color;
using Physics = UnityEngine.Physics;
using Time = UnityEngine.Time;
namespace Oxide.Plugins {
[Info("Teleportation", "TopPlugin.ru", "2.0.0")]
[Description("Teleportation")]
class Teleportation : RustPlugin {
#region Classes
private class ParentPosition {
public static Vector3 GetOffsetPosition(Transform parent, Vector3 child) {
return parent.InverseTransformPoint(child);
}
public static Vector3 GetFinalPosition(Transform parent, Vector3 offset) {
return parent.TransformPoint(offset);
}
}
private static class TimeHelper {
public static string FormatTime(TimeSpan time, int maxSubstr = 5, string language = "ru") {
string result = string.Empty;
switch (language) {
case "ru" : int i = 0;
if (time.Days != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Days, "д", "д", "д")}";
i++;
}
if (time.Hours != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Hours, "ч", "ч", "ч")}";
i++;
}
if (time.Minutes != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Minutes, "м", "м", "м")}";
i++;
}
if (time.Days == 0) {
if (time.Seconds != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Seconds, "с", "с", "с")}";
i++;
}
}
break;
case "en" : int b = 0;
if (time.Days != 0 && b < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Days, "d", "d", "d")}";
b++;
}
if (time.Hours != 0 && b < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Hours, "h", "h", "h")}";
b++;
}
if (time.Minutes != 0 && b < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Minutes, "m", "m", "m")}";
b++;
}
if (time.Days == 0) {
if (time.Seconds != 0 && b < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Seconds, "s", "s", "s")}";
b++;
}
}
break;
}
return result;
}
private static string Format(int units, string form1, string form2, string form3) {
var tmp = units % 10;
if (units >= 5 && units <= 20 || tmp >= 5 && tmp <= 9) return $"{units}{form1}";
if (tmp >= 2 && tmp <= 4) return $"{units}{form2}";
return $"{units}{form3}";
}
private static DateTime Epoch = new DateTime(1970, 1, 1);
public static double GetTimeStamp() {
return DateTime.Now.Subtract(Epoch).TotalSeconds;
}
}
private static class PlayerHelper {
private static bool FindPlayerPredicate(BasePlayer player, string nameOrUserId) {
return player.displayName.IndexOf(nameOrUserId, StringComparison.OrdinalIgnoreCase) != -1 || player.UserIDString == nameOrUserId;
}
public static bool Find(string nameOrUserId, out BasePlayer target) {
nameOrUserId = nameOrUserId.ToLower();
foreach (BasePlayer activePlayer in BasePlayer.activePlayerList) {
if (FindPlayerPredicate(activePlayer, nameOrUserId)) {
target = activePlayer;
return true;
}
}
foreach (BasePlayer sleepingPlayer in BasePlayer.sleepingPlayerList) {
if (FindPlayerPredicate(sleepingPlayer, nameOrUserId)) {
target = sleepingPlayer;
return true;
}
}
target = (BasePlayer) null;
return false;
}
public static bool FindOnlineMultiple(string nameOrUserId, BasePlayer player, out BasePlayer target) {
if (nameOrUserId == string.Empty) {
_plugin.SendMessage(player, "PLAYER.NOT.FOUND");
target = (BasePlayer) null;
return false;
}
List<BasePlayer> activePlayers = new List<BasePlayer>();
nameOrUserId = nameOrUserId.ToLower();
foreach (BasePlayer activePlayer in BasePlayer.activePlayerList) {
if (FindPlayerPredicate(activePlayer, nameOrUserId)) {
activePlayers.Add(activePlayer);
}
}
switch (activePlayers.Count) {
case 0 : _plugin.SendMessage(player, $"PLAYER.NOT.FOUND.PARAM", nameOrUserId);
target = null;
return false;
case 1 : target = activePlayers[0];
return true;
default : var text = "";
foreach (var activePlayer in activePlayers) {
text += $" {activePlayer.displayName},";
}
_plugin.SendMessage(player, "PLAYER.FIND.MULTIPLE");
target = null;
return false;
}
}
public static bool FindOnline(string nameOrUserId, out BasePlayer target) {
if (nameOrUserId == string.Empty) {
target = (BasePlayer) null;
return false;
}
nameOrUserId = nameOrUserId.ToLower();
foreach (BasePlayer activePlayer in BasePlayer.activePlayerList) {
if (FindPlayerPredicate(activePlayer, nameOrUserId)) {
target = activePlayer;
return true;
}
}
target = (BasePlayer) null;
return false;
}
}
public class Request {
public bool IsHome = false;
public SettingMonument.AvailableMonument Monument;
public BasePlayer Target = null;
public BasePlayer Player = null;
public Vector3 Position = Vector3.zero;
public RealTimeSince RequestTimeout = 0;
public bool RequestConfirmed = false;
public Request(BasePlayer player, bool isHome, Vector3 position = default(Vector3), BasePlayer target = null, SettingMonument.AvailableMonument monument = null) {
this.IsHome = isHome;
this.Monument = monument;
this.Target = target;
this.Player = player;
this.Position = position;
}
}
#endregion
#region Variables
[PluginReference]
private Plugin IQChat, ImageLibrary, Friends, Clans;
private const string TeleportImage = "https://i.imgur.com/kTyCOMy.png";
private static Teleportation _plugin;
private const string Layer = "UI.Teleportation";
private const string LayerBlur = "UI.Teleportation.Layer";
private const string LayerNotification = "UI.Teleportation.Notification";
private const string EffectPrefab = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab";
private readonly int triggerLayer = LayerMask.GetMask("Trigger");
private readonly int groundLayer = LayerMask.GetMask("Terrain", "World");
private readonly int buildingLayer = LayerMask.GetMask("Terrain", "World", "Construction", "Deployed");
private readonly int blockLayer = LayerMask.GetMask("Construction");
private readonly int deployedLayer = LayerMask.GetMask("Deployed");
#endregion
#region OxideHooks
void OnServerInitialized() {
_plugin = this;
StartPluginLoad();
}
void OnNewSave(string fileName) {
if (_config.AutoWipe) {
PrintWarning("Wipe detected! wiping data file...");
_data.PlayersData = new Dictionary<ulong, PlayerData>();
PluginData.SaveData();
}
}
void OnServerSave() {
PluginData.SaveData();
}
void Unload() {
PluginData.SaveData();
for (var i = 0; i < MarkerBag.MarkerBags.Count; i++) UnityEngine.Object.Destroy(MarkerBag.MarkerBags[i]);
for (var i = 0; i < TeleportMgr.ManagersList.Count; i++) UnityEngine.Object.Destroy(TeleportMgr.ManagersList[i]);
for (var i = 0; i < _config.SettingsMonument.AvailableMonuments.Count; i++) {
var monument = _config.SettingsMonument.AvailableMonuments[i];
if(monument.CreatedBag != null) monument.CreatedBag.Kill();
}
}
void OnPlayerConnected(BasePlayer player) {
if (player == null) return;
if (player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot)) {
timer.Once(2f, () => OnPlayerConnected(player));
return;
}
if (!player.IsConnected) return;
player.GetOrAddComponent<TeleportMgr>();
}
object CanRenameBed(BasePlayer player, SleepingBag bed, string bedName) {
if (_config.SettingsMonument.AvailableMonuments.Exists(x => x.CreatedBag != null && x.CreatedBag == bed)) return false;
return null;
}
object OnSleepingBagValidCheck(SleepingBag bag, ulong targetPlayerID, bool ignoreTimers) {
if (_config.SettingsMonument.AvailableMonuments.Exists(x => x.CreatedBag != null && x.CreatedBag == bag)) return true;
return null;
}
#endregion
#region Commands
[ChatCommand("tpc")]
private void ChatCommandTpc(BasePlayer player, string command, string[] args) {
var teleportMgr = TeleportMgr.GetManager(player);
if (teleportMgr == null) return;
if (teleportMgr.IncomingRequest != null && teleportMgr.IncomingRequest.Player != null) {
var objTarget = TeleportMgr.GetManager(teleportMgr.IncomingRequest.Player);
SendMessage(player, "TPR.PLAYER.CANCEL", teleportMgr.IncomingRequest.Player.displayName);
SendMessage(teleportMgr.IncomingRequest.Player, "TPR.TARGET.CANCEL", player.displayName);
objTarget.CancelTeleport();
teleportMgr.CancelTeleport();
return;
}
if (teleportMgr.Request != null && teleportMgr.Request.IsHome) {
teleportMgr.CancelTeleport();
SendMessage(player, "HOME.PLAYER.CANCEL");
}
if (teleportMgr.Request != null && teleportMgr.Request.Monument != null) {
teleportMgr.CancelTeleport();
SendMessage(player, "TPM.PLAYER.CANCEL");
}
}
[ConsoleCommand("home")]
void ConsoleCmdHome(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (arg.HasArgs() == false) return;
ChatCommandHome(player, "", arg.Args);
}
[ChatCommand("tp")]
private void ChatCommandTp(BasePlayer player, string command, string[] args) {
switch (args.Length) {
case 0 : break;
case 1 : if (!player.IsAdmin) return;
string name = args[0];
BasePlayer target = null;
if(!PlayerHelper.FindOnlineMultiple(name, player, out target)) {
return;
}
if (target == null) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "PLAYER.NOT.FOUND");
return;
}
Teleport(player, target);
break;
case 2 : if (!player.IsAdmin) return;
string name1 = args[0];
string name2 = args[1];
BasePlayer target1 = null;
if(!PlayerHelper.FindOnlineMultiple(name1, player, out target1)) {
return;
}
BasePlayer target2 = null;
if(!PlayerHelper.FindOnlineMultiple(name2, player, out target2)) {
return;
}
if (target1 == null || target2 == null) {
SendMessage(player, "PLAYER.NOT.FOUND");
return;
}
Teleport(target1, target2);
break;
case 3 : if (!player.IsAdmin) return;
float x = float.Parse(args[0]);
float y = float.Parse(args[1]);
float z = float.Parse(args[2]);
Teleport(player, x, y, z);
break;
}
}
[ConsoleCommand("UI_Teleportation")]
private void ConsoleCommandTeleportation(ConsoleSystem.Arg arg) {
var player = arg?.Connection?.player as BasePlayer;
if (player == null) return;
switch (arg.GetString(0)) {
case "close" : CuiHelper.DestroyUi(player, Layer);
CuiHelper.DestroyUi(player, LayerBlur);
break;
}
}
[ChatCommand("tpa")]
private void ChatCommandTpa(BasePlayer player, string command, string[] args) {
var teleportMgr = TeleportMgr.GetManager(player);
if (teleportMgr == null) return;
if (teleportMgr.IncomingRequest == null) {
SendMessage(player, "TPA.ZERO.REQUEST");
return;
}
var pendingPlayer = teleportMgr.IncomingRequest.Player;
var pendingTeleportMgr = TeleportMgr.GetManager(pendingPlayer);
if (pendingTeleportMgr == null) return;
if (player.IsWounded() && _config.SettingsResolution.CantTeleportWounded) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "TP.CANCEL.BLOOD.OR.WOUNDED");
return;
}
if (player.metabolism.bleeding.value > _config.SettingsResolution.SettingsBleeding.NeedBleedingAmount && _config.SettingsResolution.SettingsBleeding.CantTeleportBleeding) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "TP.CANCEL.BLOOD.OR.WOUNDED");
return;
}
if (player.IsBuildingBlocked() && _config.SettingsResolution.CantTeleportInBuild) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "TP.CANCEL.BUILD.DONT.AUTH");
return;
}
var ret = Interface.Call("CanTeleport", player) as string;
if (ret != null) {
SendMessageWithoutLang(player, ret);
return;
}
SendMessage(player, "TPA.PLAYER.ACCEPT", pendingPlayer.displayName);
SendMessage(pendingPlayer, "TPA.TARGET.ACCEPT");
CuiHelper.DestroyUi(player, LayerNotification);
teleportMgr.IncomingRequest = null;
pendingTeleportMgr.AcceptRequest();
}
[ChatCommand("tpr")]
private void ChatCommandTpr(BasePlayer player, string command, string[] args) {
if (GetTeleportTimeTpr(player.userID) <= 0) return;
if (args.Length < 1) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "TPR.HELP");
return;
}
var ret = Interface.Call("CanTeleport", player) as string;
if (ret != null) {
SendMessageWithoutLang(player, ret);
return;
}
var message = CanTeleportPlayer(player);
if (string.IsNullOrEmpty(message) == false) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessageWithoutLang(player, message);
return;
}
var data = PluginData.GetForPlayer(player.userID);
if (data == null) return;
if (data.CooldownTeleport >= GetCurrentTime()) {
SendMessage(player, "TPR.COOLDOWN", TimeHelper.FormatTime(TimeSpan.FromSeconds(data.CooldownTeleport - GetCurrentTime()), language : lang.GetLanguage(player.UserIDString)));
return;
}
BasePlayer target = null;
if(!PlayerHelper.FindOnlineMultiple(string.Join(" ", args), player, out target)) {
return;
}
if (target == null) return;
if (target == player) return;
var teleportMgr = TeleportMgr.GetManager(player);
if (teleportMgr == null) return;
if (teleportMgr.Request != null) {
SendMessage(player, "TPR.PLAYER.CANCEL.REQUEST");
return;
}
var targetTeleportMgr = TeleportMgr.GetManager(target);
if (targetTeleportMgr == null) return;
if (targetTeleportMgr.IncomingRequest != null) {
SendMessage(player, "TPR.TARGET.CANCEL.REQUEST");
return;
}
teleportMgr.InitRequest(target.transform.position, false, target);
targetTeleportMgr.IncomingRequest = teleportMgr.Request;
SendMessage(player, "TPR.PLAYER.SEND", target.displayName);
SendMessage(target, "TPR.TARGET.SEND", player.displayName);
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel {
CursorEnabled = false, RectTransform = {
AnchorMin = "0.5 0", AnchorMax = "0.5 0", OffsetMax = "0 0"
}
, Image = {
Color = GetColor("#FFFFFF", 0.4f)
}
}
, "Hud", LayerNotification);
container.Add(new CuiElement() {
Parent = LayerNotification, Name = LayerNotification + ".Main", Components = {
new CuiRawImageComponent() {
Png = GetImage(TeleportImage)
}
, new CuiRectTransformComponent() {
AnchorMin = $"0 0", AnchorMax = $"1 1", OffsetMin = "-200 81", OffsetMax = "180 117.6"
}
,
}
}
);
container.Add(new CuiPanel() {
RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-151 81", OffsetMax = "132 117.6"
}
, Image = {
Color = "0.9686275 0.9215686 0.8823529 0.13529412"
}
}
, LayerNotification);
container.Add(new CuiLabel() {
RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-151 81", OffsetMax = "132 117.6"
}
, Text = {
Text = GetMessage(player, "TPR.UI.REQUEST" , player.displayName), Font = "RobotoCondensed-Bold.ttf", Align = TextAnchor.MiddleCenter, FontSize = 16, Color = GetColor("#FFFFFFB2")
}
,
}
, LayerNotification);
container.Add(new CuiButton() {
RectTransform = {
AnchorMin = "0.02 0", AnchorMax = "0 1", OffsetMax = "49 0"
}
, Button = {
Color = "0 0 0 0", Command = "chat.say /tpc"
}
, Text = {
Text = GetMessage(player, "TPR.UI.REQUEST.NO"), Align = TextAnchor.MiddleCenter, Color = GetColor("#D12C2C"), FontSize = 16
}
}
, LayerNotification + ".Main");
container.Add(new CuiButton() {
RectTransform = {
AnchorMin = "0.98 0", AnchorMax = "1 1", OffsetMin = "-49 0"
}
, Button = {
Color = "0 0 0 0", Command = "chat.say /tpa"
}
, Text = {
Text = GetMessage(player, "TPR.UI.REQUEST.YES"), Align = TextAnchor.MiddleCenter, Color = GetColor("#C1FF9B"), FontSize = 16
}
}
, LayerNotification + ".Main");
CuiHelper.AddUi(target, container);
}
[ChatCommand("removehome")]
private void СhatCommandRemoveHome(BasePlayer player, string command, string[] args) {
if (args.Length < 1) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "REMOVE.HOME.HELP");
return;
}
string homeName = string.Join(" ", args);
var data = PluginData.GetForPlayer(player.userID);
var home = data.GetHome(homeName);
if (home == null) {
SendMessage(player, "REMOVE.HOME.NONE");
return;
}
if (home.SleepingBagId != 0) {
var bag = BaseNetworkable.serverEntities.Find(home.SleepingBagId);
if (bag != null) bag.Kill();
}
data.HomesList.Remove(home);
SendMessage(player, "REMOVE.HOME.SUCCESS", homeName);
}
[ChatCommand("homelist")]
private void СhatCommandHomeList(BasePlayer player, string command, string[] args) {
var data = PluginData.GetForPlayer(player.userID);
if (data == null) return;
if (data.HomesList.Count < 1) {
SendMessage(player, "HOMELIST.ZERO");
return;
}
string homes = "";
for (var i = data.HomesList.Count - 1; i >= 0; i--) {
var home = data.HomesList[i];
homes += $"{home.HomeName}";
if (i != 0) homes += ", ";
}
SendMessage(player, "HOMELIST.FOUND", homes);
}
[ChatCommand("sethome")]
private void ChatCommandSetHome(BasePlayer player, string command, string[] args) {
if (GetHomeLimit(player.userID) <= 0) return;
var foundationList = GetFoundation(player.ServerPosition);
if (foundationList == null || foundationList.Count == 0) {
SendMessage(player, "FOUNDATION.NOT.FOUND");
return;
}
var foundation = foundationList?.First();
if (foundation == null) {
SendMessage(player, "FOUNDATION.NOT.FOUND");
return;
}
string err = CheckFoundation(player.userID, player.ServerPosition);
if (err != null) {
SendMessage(player, err);
return;
}
err = CheckInsideBlock(player.ServerPosition);
if (err != null) {
SendMessage(player, err);
return;
}
if (player.GetBuildingPrivilege(player.WorldSpaceBounds()) != null && !player.GetBuildingPrivilege(player.WorldSpaceBounds()).authorizedPlayers.Select(p => p.userid).Contains(player.userID)) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "SETHOME.CANCEL.BUILD");
return;
}
if (args.Length != 1) {
SendMessage(player, "SETHOME.HELP");
return;
}
if (player.IsWounded() && _config.SettingsResolution.CantTeleportWounded || player.metabolism.bleeding.value > _config.SettingsResolution.SettingsBleeding.NeedBleedingAmount && _config.SettingsResolution.SettingsBleeding.CantTeleportBleeding) {
SendMessage(player, "BLOOD.OR.WOUNDED");
return;
}
SetHome(player, args[0]);
}
[ChatCommand("home")]
private void ChatCommandHome(BasePlayer player, string command, string[] args) {
if (args.Length != 1) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "HOME.HELP");
return;
}
if (GetTeleportTimeHome(player.userID) <= 0) return;
var data = PluginData.GetForPlayer(player.userID);
var homeName = string.Join(" ", args);
var home = data.GetHome(homeName);
if (home == null) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "HOME.NOT.FOUND.OR.REMOVED");
return;
}
var ret = Interface.Call("CanTeleport", player) as string;
if (ret != null) {
SendMessageWithoutLang(player, ret);
return;
}
if (_config.SettingsHome.EnableSleepingBags) {
if (home.SleepingBagId == 0) {
data.HomesList.Remove(home);
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "HOME.NOT.FOUND.BAG");
return;
}
var bag = BaseNetworkable.serverEntities.Find(home.SleepingBagId);
if (bag == null) {
data.HomesList.Remove(home);
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "HOME.NOT.FOUND.BAG");
return;
}
}
if (data.CooldownHome > GetCurrentTime()) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessage(player, "HOME.COOLDOWN", TimeHelper.FormatTime(TimeSpan.FromSeconds(data.CooldownHome - GetCurrentTime())));
return;
}
var message = CanTeleportPlayer(player);
if (!string.IsNullOrEmpty(message)) {
Effect.server.Run(EffectPrefab, player, 0, Vector3.zero, Vector3.forward);
SendMessageWithoutLang(player, message);
return;
}
string err = CheckInsideBlock(home.HomePos);
if (err != null) {
SendMessage(player, "HOME.REMOVED.INSIDE.BLOCK", home.HomeName);
data.HomesList.Remove(home);
return;
}
var teleportMgr = TeleportMgr.GetManager(player);
if (teleportMgr == null) return;
if (teleportMgr.Request != null) {
SendMessage(player, "TPR.PLAYER.CANCEL.REQUEST");
return;
}
teleportMgr.InitRequest(home.HomePos, true);
SendMessage(player, "HOME.TELEPORTATION", TimeHelper.FormatTime(TimeSpan.FromSeconds(GetTeleportTimeHome(player.userID)), language : lang.GetLanguage(player.UserIDString)));
}
[ChatCommand("tpm")]
private void ChatCommandTpMonument(BasePlayer player, string command, string[] args) {
if (args.Length < 1) {
SendMessage(player, "TPM.HELP");
return;
}
switch (args[0]) {
case "add" : {
if (player.IsAdmin == false) break;
var closestMonument = FindClosestMonument(player.transform.position);
if (closestMonument == null) {
SendMessage(player, "TPM.ADMIN.CLOSEST.NOT.FOUND");
break;
}
var monumentInfo = _config.SettingsMonument.AvailableMonuments.Find(x => closestMonument.name.Contains(x.ShortPrefabName));
if (monumentInfo != null) {
SendMessage(player, "TPM.ADMIN.EXISTS");
break;
}
Vector3 positionOffset = ParentPosition.GetOffsetPosition(closestMonument.transform, player.transform.position);
string name = GetShortMonumentName(closestMonument.name);
SleepingBag bag = null;
if (_config.SettingsMonument.EnableSleepingBags) {
bag = CreateSleepingBag(null, ParentPosition.GetFinalPosition(closestMonument.transform, positionOffset) - new Vector3(0, 100f, 0), name);
bag.SetPublic(false);
bag.OwnerID = 0;
bag.deployerUserID = 0;
bag.spawnOffset += new Vector3(0, 100f, 0);
bag.unlockTime = Time.realtimeSinceStartup + 30f;
bag.secondsBetweenReuses = 30f;
}
monumentInfo = new SettingMonument.AvailableMonument() {
ShortPrefabName = name, CoolDown = 300f, CoolDownPublic = 30f, TeleportTime = 30f, Name = name, PositionOffset = positionOffset, ShortCutCommand = "", CreatedBag = bag, MonumentInfo = closestMonument,
}
;
_config.SettingsMonument.AvailableMonuments.Add(monumentInfo);
SaveConfig();
SendMessage(player, "TPM.ADMIN.SUCCESS");
break;
}
case "remove" : {
if (player.IsAdmin == false) break;
if (args.Length < 2) break;
string name = string.Join(" ", args.Skip(1));
var monumentInfo = _config.SettingsMonument.AvailableMonuments.Find(x => x.Name == name);
if (monumentInfo == null) {
SendMessage(player, "TPM.ADMIN.REMOVE.NOT.FOUND");
break;
}
_config.SettingsMonument.AvailableMonuments.Remove(monumentInfo);
SaveConfig();
SendMessage(player, "TPM.ADMIN.REMOVE.SUCCESS");
break;
}
case "list" : {
if (_config.SettingsMonument.AvailableMonuments.Count < 1) {
SendMessage(player, "TPM.NO.AVAILABLE.MONUMENTS");
break;
}
string monuments = "";
for (var i = _config.SettingsMonument.AvailableMonuments.Count - 1; i >= 0; i--) {
var availableMonument = _config.SettingsMonument.AvailableMonuments[i];
monuments += $"\n/tpm {availableMonument.Name}" + (string.IsNullOrEmpty(availableMonument.ShortCutCommand) ? "" : $" (/{availableMonument.ShortCutCommand})");
}
SendMessage(player, "TPM.LIST.FOUND", monuments);
break;
}
default : {
if (args.Length < 1) {
SendMessage(player, "TPM.HELP");
break;
}
string name = string.Join(" ", args);
var monumentInfo = _config.SettingsMonument.AvailableMonuments.Find(x => x.Name == name);
if (monumentInfo == null) {
SendMessage(player, "TPM.NOT.FOUND");
break;
}
if (monumentInfo.MonumentInfo == null) {
SendMessage(player, "TPM.NOT.FOUND");
break;
}
var data = PluginData.GetForPlayer(player.userID);
if (data == null) break;
double cooldown = data.GetMonumentCooldown(monumentInfo.Name);
if (cooldown > GetCurrentTime()) {
SendMessage(player, "TPM.COOLDOWN", TimeHelper.FormatTime(TimeSpan.FromSeconds(cooldown - GetCurrentTime())));
break;
}
var teleportMgr = TeleportMgr.GetManager(player);
if (teleportMgr == null) break;
teleportMgr.InitRequest(ParentPosition.GetFinalPosition(monumentInfo.MonumentInfo.transform, monumentInfo.PositionOffset), false, null, monumentInfo);
SendMessage(player, "TPM.TP.SUCCESS", TimeHelper.FormatTime(TimeSpan.FromSeconds(monumentInfo.TeleportTime)));
break;
}
}
}
private MonumentInfo FindClosestMonument(Vector3 position) {
MonumentInfo info = null;
float minDistance = float.MaxValue;
var monuments = TerrainMeta.Path.Monuments;
foreach (var monument in monuments) {
float distance = Vector3.Distance(position, monument.transform.position);
if (distance > minDistance) continue;
minDistance = distance;
info = monument;
}
return info;
}
private MonumentInfo FindMonument(string shortPrefabName) {
return TerrainMeta.Path.Monuments.Find(x => x.name.Contains(shortPrefabName));
}
private string GetShortMonumentName(string fullName) {
var split = fullName.Split('/');
return split[split.Length - 1].Replace(".prefab", string.Empty);
}
#endregion
#region Lang
private void SendMessage(BasePlayer player, string message, string arg1 = "", string arg2 = "", Chat.ChatChannel channel = Chat.ChatChannel.Global) {
if (IQChat) IQChat?.Call("API_ALERT_PLAYER", player, GetMessage(player, message, arg1, arg2), GetMessage(player, "TELEPORTATION.TITLE"));
else player.SendConsoleCommand("chat.add", channel, 0, GetMessage(player, message, arg1, arg2));
}
private void SendMessageWithoutLang(BasePlayer player, string message, Chat.ChatChannel channel = Chat.ChatChannel.Global) {
if (IQChat) IQChat?.Call("API_ALERT_PLAYER", player, message, GetMessage(player, "TELEPORTATION.TITLE"));
else player.SendConsoleCommand("chat.add", channel, 0, message);
}
private string GetMessage(BasePlayer player, string message, string arg1 = "", string arg2 = "") {
return string.Format(lang.GetMessage(message, this, player.UserIDString), arg1, arg2);
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string>() {
["TPM.ADMIN.REMOVE.NOT.FOUND"]
= "Monument not found.", ["TPM.ADMIN.REMOVE.SUCCESS"] = "You successfuly removed the monument from config.", ["TPM.PLAYER.CANCEL"] = "Teleport to monument cancelled.", ["TPM.NO.AVAILABLE.MONUMENTS"] = "There is no available monuments.", ["TPM.LIST.FOUND"] = "Available monuments: {0}", ["TPM.HELP"] = "Usage: /tpm 'monument name'.\nUse: /tpm list - to see all available monuments.", ["TPM.NOT.FOUND"] = "Monument not found!", ["TPM.COOLDOWN"] = "Teleportation has been canceled! Reason: cooldown {0}", ["TPM.TP.SUCCESS"] = "You successfully teleport to monument.Time before teleporting {0}", ["TPM.SUCCESS"] = "You have successfully teleported to monument", ["TPM.ADMIN.SUCCESS"] = "Monument was successfully added into your config.", ["TPM.ADMIN.CLOSEST.NOT.FOUND"] = "The system could not find the nearest monument to you!", ["TPM.ADMIN.EXISTS"] = "Such a monument already exists!", ["TPR.PLAYER.CANCEL"] = "You have successfully canceled teleportation to player {0}", ["TPR.TARGET.CANCEL"] = "Player {0} has canceled teleportation to you!", ["HOME.PLAYER.CANCEL"] = "You canceled teleportation to your home!", ["PLAYER.NOT.FOUND"] = "Player not found", ["TPA.ZERO.REQUEST"] = "You have no pending teleport request!", ["TP.CANCEL.BLOOD.OR.WOUNDED"] = "Teleport canceled! You can't teleport while wounded!", ["TP.CANCEL.BUILD.DONT.AUTH"] = "You can't teleport while in a building blocked zone!", ["TPA.PLAYER.ACCEPT"] = "You've accepted the teleport request of {0}!", ["TPA.TARGET.ACCEPT"] = "Player has accepted your teleport request!! Teleportation has started!", ["TPR.HELP"] = "Use: /tpr nickname", ["TPR.COOLDOWN"] = "Your teleport requests are currently on cooldown.You'll have to wait {0} to send your next teleport request.", ["TELEPORTATION.TITLE"] = "[TELEPORTATION]", ["TPR.PLAYER.CANCEL.REQUEST"] = "You can't initiate another teleport while you have a teleport pending!", ["TPR.TARGET.CANCEL.REQUEST"] = "You can't request a teleport to someone who's about to teleport!", ["TPR.PLAYER.SEND"] = "Teleportation request to {0} was sent successfully", ["TPR.TARGET.SEND"] = "{0} sent you teleportation request\nTo accept type: /tpa\nTo refuse type: /tpc", ["REMOVE.HOME.HELP"] = "Use: /removehome name", ["REMOVE.HOME.NONE"] = "Couldn't find your home with that name!", ["REMOVE.HOME.SUCCESS"] = "You have removed your home {0}!", ["FOUNDATION.NOT.FOUND"] = "Foundation not found!", ["FOUNDATION.NOT.OWNED"] = "Foundation is not owned by you or your friends!", ["FOUNDATION.HOME.INSIDE.BLOCK"] = "You can't create home inside other blocks!", ["SETHOME.HELP"] = "Use: /sethome name", ["BLOOD.OR.WOUNDED"] = "You are bleeding or you wounded!", ["HOME.HELP"] = "Use: /home name", ["HOME.NOT.FOUND.OR.REMOVED"] = "Home location not found or has been deleted!", ["HOME.NOT.FOUND.BAG"] = "Teleportation is not possible! Sleeping bag not found!", ["HOME.COOLDOWN"] = "Teleportation has been canceled! Reason: cooldown {0}", ["HOME.REMOVED.INSIDE.BLOCK"] = "Your home '{0}' was removed because it was inside a foundation!", ["CAN.TP.SWIMMING"] = "Teleportation in water is prohibited!", ["CAN.TP.ALIVE"] = "You can't teleport while being dead!", ["CAN.TP.RADIATION"] = "You are irradiated! Teleportation has been canceled!", ["CAN.TP.BLEEDING"] = "You are bleeding! Teleportation canceled!", ["CAN.TP.TEMPERATURE"] = "You're too cold to teleport!", ["CAN.TP.WOUNDED"] = "You can't teleport while wounded!", ["CAN.TP.MOUNTED"] = "You can't teleport while mounting vehicle!", ["CAN.TP.BUILDINGBLOCK"] = "You can't teleport while in a building blocked zone!", ["SETHOME.ALREADY.EXISTS"] = "A location with this name already exists!", ["SETHOME.CANCEL.RADIUS"] = "You already have home location nearby!", ["SETHOME.CANCEL.MAXHOUSE"] = "Unable to set your home here, you have reached the maximum amount of homes!", ["SETHOME.SUCCESS"] = "You have saved the current location {0} as your home!", ["HOMELIST.ZERO"] = "You have no houses!", ["HOMELIST.FOUND"] = "Houses list: {0}", ["HOME.TELEPORTATION"] = "You successfully teleport to your home.Time before teleporting {0}", ["REQUEST.CHECK.DISCONNECT"] = "Teleport request has been canceled! Reason: player left server", ["REQUEST.CHECK.DEAD"] = "Teleport request has been canceled! Reason: player died!", ["REQUEST.CHECK.WOUNDED"] = "Teleport request has been canceled! Reason: player wounded!", ["REQUEST.CHECK.TIME.ENDED"] = "Teleport request has been canceled! Reason: time out!", ["TP.CHECK.BUILD"] = "Teleport request has been canceled! Reason: building blocked zone", ["TP.HOME.SUCCESS"] = "You teleported to your home!", ["TP.PLAYER.SUCCESS"] = "You have successfully teleported to {0}", ["TP.TARGET.SUCCESS"] = "Player {0} successfully teleported to you!", ["MARKER.BAG"] = "<size=24>{0}</size>\n<size=16>{1}</size>", ["MARKER.BAG.NAMEBAG"] = "{0}", ["MARKER.BAG.COOLDOWN"] = "COUNTDOWN: {0}", ["MARKER.BAG.NOTCOOLDOWN"] = "AVAILABLE FOR TELEPORTATION", ["TELEPORTATION.UI"] = "TIME LEFT BEFORE TELEPORT: {0}", ["PLAYER.NOT.FOUND.PARAM"] = "Player {0} not found", ["PLAYER.FIND.MULTIPLE"] = "Several players were found: {0}", ["TPR.UI.REQUEST"] = "REQUEST FROM PLAYER {0}", ["TPR.UI.REQUEST.YES"] = "YES", ["TPR.UI.REQUEST.NO"] = "NO", ["SETHOME.CANCEL.BUILD"] = "You cannot set your home location while in building blocked zone!"
}
, this);
lang.RegisterMessages(new Dictionary<string, string>() {
["TPM.ADMIN.REMOVE.NOT.FOUND"]
= "Монумент не найден.", ["TPM.ADMIN.REMOVE.SUCCESS"] = "Вы успешно удалили монумент из конфига.", ["TPM.PLAYER.CANCEL"] = "Телепорт на монумент был отменён.", ["TPM.NO.AVAILABLE.MONUMENTS"] = "Нет доступных монументов.", ["TPM.LIST.FOUND"] = "Доступные монументы: {0}", ["TPM.HELP"] = "Использование: /tpm 'название монумента'.\nИспользуйте: /tpm list - чтобы увидеть список доступных монументов.", ["TPM.NOT.FOUND"] = "Монумент не найден!", ["TPM.COOLDOWN"] = "Телепортация отменена! Причина: откат {0}", ["TPM.TP.SUCCESS"] = "Вы успешно телепортируетесь на монумент.Время перед телепортом {0}", ["TPM.SUCCESS"] = "Вы успешно телепортированы на монумент.", ["TPM.ADMIN.SUCCESS"] = "Вы успешно добавили новый монумент в конфиг.", ["TPM.ADMIN.CLOSEST.NOT.FOUND"] = "Система не смогла определить ближайший монумент к вам!", ["TPM.ADMIN.EXISTS"] = "Такой монумент уже есть в конфиге!", ["TPR.PLAYER.CANCEL"] = "Вы успешно отменили телепортацию к игроку {0}", ["TPR.TARGET.CANCEL"] = "Игрок {0} отменил телепортацию к Вам!", ["HOME.PLAYER.CANCEL"] = "Вы отменили телепортацию домой!", ["PLAYER.NOT.FOUND"] = "Игрок не найден", ["TPA.ZERO.REQUEST"] = "У вас нет входящих запросов на телепортацию!", ["TP.CANCEL.BLOOD.OR.WOUNDED"] = "Телепортация была отменена! Причина кровоток или вы упали!", ["TP.CANCEL.BUILD.DONT.AUTH"] = "В билдинг зоне телепортация запрещена!", ["TPA.PLAYER.ACCEPT"] = "Вы приняли запрос игрока {0}", ["TPA.TARGET.ACCEPT"] = "Игрок принял ваш запрос! Телепортация началась!", ["TPR.HELP"] = "Используйте: /tpr ник", ["TPR.COOLDOWN"] = "Телепортация невозможна! У вас кулдаун: {0}", ["TELEPORTATION.TITLE"] = "[TELEPORTATION]", ["TPR.PLAYER.CANCEL.REQUEST"] = "Телепортация невозможна! У Вас уже есть запрос!", ["TPR.TARGET.CANCEL.REQUEST"] = "Телепортация невозможна! У игрока уже есть запрос!", ["TPR.PLAYER.SEND"] = "Запрос к {0} был успешно отправлен", ["TPR.TARGET.SEND"] = "{0} отправил вам запрос на телепортацию\nЧтобы принять используйте /tpa\nЧтобы отказаться используйте /tpc", ["REMOVE.HOME.HELP"] = "Используйте: /removehome название", ["REMOVE.HOME.NONE"] = "Данного дома не существует", ["REMOVE.HOME.SUCCESS"] = "Вы удалили дом под названием {0}", ["FOUNDATION.NOT.FOUND"] = "Фундамент не найден!", ["FOUNDATION.NOT.OWNED"] = "Фундамент не пренадлежит вам или вашим друзьям!", ["FOUNDATION.HOME.INSIDE.BLOCK"] = "Вы не можете создавать дом внутри другого блока!", ["SETHOME.HELP"] = "Используйте: /sethome название", ["BLOOD.OR.WOUNDED"] = "У вас кровотечение или вы упали!", ["HOME.HELP"] = "Используйте: /home название", ["HOME.NOT.FOUND.OR.REMOVED"] = "Точка телепортации ненайдена или была удалена!", ["HOME.NOT.FOUND.BAG"] = "Телепортация невозможна! Спальник не обнаружен!", ["HOME.COOLDOWN"] = "Телепортация была отменена! Причина: кулдаун {0}", ["HOME.REMOVED.INSIDE.BLOCK"] = "Ваш дом '{0}' был удалён потому, что он был внутри фундамента!", ["CAN.TP.SWIMMING"] = "Телепортация в воде запрещена", ["CAN.TP.ALIVE"] = "Телепортация мертвым запрещена", ["CAN.TP.RADIATION"] = "Вы облучены! Телепортация была отменена!", ["CAN.TP.BLEEDING"] = "Вы истекаете кровью! Телепортация отменена!", ["CAN.TP.TEMPERATURE"] = "Вы замерзли! Телепортация отменена!", ["CAN.TP.WOUNDED"] = "Вы получили ранение! Телепортация отменена!", ["CAN.TP.MOUNTED"] = "Вы сидите! Телепортация отменена!", ["CAN.TP.CRAWLING"] = "Вы получили ранение! Телепортация отменена!", ["CAN.TP.BUILDINGBLOCK"] = "Вы находитесь в зоне действия шкафа! Телепортация отменена!", ["SETHOME.ALREADY.EXISTS"] = "Данный дом уже существует", ["SETHOME.CANCEL.RADIUS"] = "У вас уже установлен дом в этом месте!", ["SETHOME.CANCEL.MAXHOUSE"] = "Вы превысили максимальное количество сетхомов!", ["SETHOME.SUCCESS"] = "Вы успешно установили дом {0}", ["HOMELIST.ZERO"] = "У вас нет домов!", ["HOMELIST.FOUND"] = "Список ваших домов: {0}", ["HOME.TELEPORTATION"] = "Вы успешно телепортируетесь на дом.До телепортации {0}", ["REQUEST.CHECK.DISCONNECT"] = "Запрос на телепортацию был отменён! Причина: цель покинула сервер", ["REQUEST.CHECK.DEAD"] = "Запрос на телепортацию был отменён! Причина: цель погибла!", ["REQUEST.CHECK.WOUNDED"] = "Запрос на телепортацию был отменён! Причина: цель упала!", ["REQUEST.CHECK.CRAWLING"] = "Запрос на телепортацию был отменён! Причина: цель упала!", ["REQUEST.CHECK.TIME.ENDED"] = "Запрос на телепортацию был отменён! Причина: окончилось время ожидания!", ["TP.CHECK.BUILD"] = "Телепортация была отменена! Причина: зона шкафа", ["TP.HOME.SUCCESS"] = "Вы успешно были телепортированы домой!", ["TP.PLAYER.SUCCESS"] = "Вы успешно телепортировались к {0}", ["TP.TARGET.SUCCESS"] = "Игрок {0} успешно телепортировался к Вам!", ["MARKER.BAG"] = "<size=24>{0}</size>\n<size=16>{1}</size>", ["MARKER.BAG.NAMEBAG"] = "{0}", ["MARKER.BAG.COOLDOWN"] = "ОТКАТ: {0}", ["MARKER.BAG.NOTCOOLDOWN"] = "ДОСТУПЕН ДЛЯ ТЕЛЕПОРТАЦИИ", ["TELEPORTATION.UI"] = "ДО КОНЦА ТЕЛЕПОРТАЦИИ ОСТАЛОСЬ: {0}", ["PLAYER.NOT.FOUND.PARAM"] = "Игрок {0} был не найден", ["PLAYER.FIND.MULTIPLE"] = "Было найдено несколько игроков: {0}", ["TPR.UI.REQUEST"] = "ЗАПРОС ОТ ИГРОКА {0}", ["TPR.UI.REQUEST.YES"] = "ДА", ["TPR.UI.REQUEST.NO"] = "НЕТ", ["SETHOME.CANCEL.BUILD"] = "Вы не можете установить сетхом в билдинг зоне!"
}
, this, "ru");
}
#endregion
#region Utils
private string CanTeleportPlayer(BasePlayer player) {
if (player.IsSwimming() && _config.SettingsResolution.CantTeleportSwimming) return GetMessage(player, "CAN.TP.SWIMMING");
if (!player.IsAlive()) return GetMessage(player, "CAN.TP.ALIVE");
if (player.isMounted) return
// ... [Code preview truncated due to length. Full source in attached file] ...