· 5 years ago · Feb 15, 2021, 04:02 AM
1//#define DEBUG
2using System;
3using System.Collections;
4using System.Collections.Generic;
5using System.ComponentModel;
6using System.Globalization;
7using System.Linq;
8using Facepunch;
9using Newtonsoft.Json;
10using Newtonsoft.Json.Converters;
11using Newtonsoft.Json.Linq;
12using Oxide.Core;
13using Oxide.Core.Configuration;
14using Oxide.Core.Plugins;
15using Oxide.Game.Rust;
16using Rust;
17using UnityEngine;
18using static UnityEngine.Vector3;
19using System.Text.RegularExpressions;
20
21namespace Oxide.Plugins
22{
23 [Info("NTeleportation", "RFC1920", "1.0.60", ResourceId = 1832)]
24 class NTeleportation : RustPlugin
25 {
26 private static readonly Vector3 Up = up;
27 private static readonly Vector3 Down = down;
28 private const string NewLine = "\n";
29 private const string ConfigDefaultPermVip = "nteleportation.vip";
30 private const string PermHome = "nteleportation.home";
31 private const string PermTpR = "nteleportation.tpr";
32 private const string PermDeleteHome = "nteleportation.deletehome";
33 private const string PermHomeHomes = "nteleportation.homehomes";
34 private const string PermImportHomes = "nteleportation.importhomes";
35 private const string PermRadiusHome = "nteleportation.radiushome";
36 private const string PermTp = "nteleportation.tp";
37 private const string PermTpB = "nteleportation.tpb";
38 private const string PermTpConsole = "nteleportation.tpconsole";
39 private const string PermTpHome = "nteleportation.tphome";
40 private const string PermTpTown = "nteleportation.tptown";
41 private const string PermTpN = "nteleportation.tpn";
42 private const string PermTpL = "nteleportation.tpl";
43 private const string PermTpRemove = "nteleportation.tpremove";
44 private const string PermTpSave = "nteleportation.tpsave";
45 private const string PermWipeHomes = "nteleportation.wipehomes";
46 private const string PermCraftHome = "nteleportation.crafthome";
47 private const string PermCraftTown = "nteleportation.crafttown";
48 private const string PermCraftTpR = "nteleportation.crafttpr";
49 private DynamicConfigFile dataAdmin;
50 private DynamicConfigFile dataHome;
51 private DynamicConfigFile dataTPR;
52 private DynamicConfigFile dataTown;
53 private Dictionary<ulong, AdminData> Admin;
54 private Dictionary<ulong, HomeData> Home;
55 private Dictionary<ulong, TeleportData> TPR;
56 private Dictionary<ulong, TeleportData> Town;
57 private bool changedAdmin;
58 private bool changedHome;
59 private bool changedTPR;
60 private bool changedTown;
61 private ConfigData configData;
62 private float boundary;
63 private readonly int triggerLayer = LayerMask.GetMask("Trigger");
64 private readonly int groundLayer = LayerMask.GetMask("Terrain", "World");
65 private readonly int buildingLayer = LayerMask.GetMask("Terrain", "World", "Construction", "Deployed");
66 private readonly int blockLayer = LayerMask.GetMask("Construction");
67 private readonly Dictionary<ulong, TeleportTimer> TeleportTimers = new Dictionary<ulong, TeleportTimer>();
68 private readonly Dictionary<ulong, Timer> PendingRequests = new Dictionary<ulong, Timer>();
69 private readonly Dictionary<ulong, BasePlayer> PlayersRequests = new Dictionary<ulong, BasePlayer>();
70 private readonly Dictionary<int, string> ReverseBlockedItems = new Dictionary<int, string>();
71 private readonly HashSet<ulong> teleporting = new HashSet<ulong>();
72 private SortedDictionary<string, Vector3> monPos = new SortedDictionary<string, Vector3>();
73 private SortedDictionary<string, Vector3> monSize = new SortedDictionary<string, Vector3>();
74 private SortedDictionary<string, Vector3> cavePos = new SortedDictionary<string, Vector3>();
75
76 [PluginReference]
77 private Plugin Clans, Economics, ServerRewards, Friends, RustIO;
78
79 class ConfigData
80 {
81 public SettingsData Settings { get; set; }
82 public AdminSettingsData Admin { get; set; }
83 public HomesSettingsData Home { get; set; }
84 public TPRData TPR { get; set; }
85 public TownData Town { get; set; }
86 public VersionNumber Version { get; set; }
87 }
88
89 class SettingsData
90 {
91 public string ChatName { get; set; }
92 public bool HomesEnabled { get; set; }
93 public bool TPREnabled { get; set; }
94 public bool TownEnabled { get; set; }
95 public bool InterruptTPOnHurt { get; set; }
96 public bool InterruptTPOnCold { get; set; }
97 public bool InterruptTPOnHot { get; set; }
98 public bool InterruptTPOnSafe { get; set; }
99 public bool InterruptTPOnBalloon { get; set; }
100 public bool InterruptTPOnCargo { get; set; }
101 public bool InterruptTPOnRig { get; set; }
102 public bool InterruptTPOnLift { get; set; }
103 public bool InterruptTPOnMonument { get; set; }
104 public bool InterruptTPOnMounted { get; set; }
105 public bool InterruptTPOnSwimming { get; set; }
106 public float CaveDistanceSmall { get; set; }
107 public float CaveDistanceMedium { get; set; }
108 public float CaveDistanceLarge { get; set; }
109 public float DefaultMonumentSize { get; set; }
110 public float MinimumTemp { get; set; }
111 public float MaximumTemp { get; set; }
112 public Dictionary<string, string> BlockedItems { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
113 public string BypassCMD { get; set; }
114 public bool UseEconomics { get; set; }
115 public bool UseServerRewards { get; set; }
116 }
117
118 class AdminSettingsData
119 {
120 public bool AnnounceTeleportToTarget { get; set; }
121 public bool UseableByAdmins { get; set; }
122 public bool UseableByModerators { get; set; }
123 public int LocationRadius { get; set; }
124 public int TeleportNearDefaultDistance { get; set; }
125 }
126
127 class HomesSettingsData
128 {
129 public int HomesLimit { get; set; }
130 public Dictionary<string, int> VIPHomesLimits { get; set; }
131 public int Cooldown { get; set; }
132 public int Countdown { get; set; }
133 public int DailyLimit { get; set; }
134 public Dictionary<string, int> VIPDailyLimits { get; set; }
135 public Dictionary<string, int> VIPCooldowns { get; set; }
136 public Dictionary<string, int> VIPCountdowns { get; set; }
137 public int LocationRadius { get; set; }
138 public bool ForceOnTopOfFoundation { get; set; }
139 public bool CheckFoundationForOwner { get; set; }
140 public bool UseFriends { get; set; }
141 public bool UsableOutOfBuildingBlocked { get; set; }
142 public bool UsableIntoBuildingBlocked { get; set; }
143 public bool CupOwnerAllowOnBuildingBlocked { get; set; }
144 public bool AllowIceberg { get; set; }
145 public bool AllowCave { get; set; }
146 public bool AllowCraft { get; set; }
147 public bool AllowAboveFoundation { get; set; }
148 public bool CheckValidOnList { get; set; }
149 public int Pay { get; set; }
150 public int Bypass { get; set; }
151 }
152
153 class TPRData
154 {
155 public int Cooldown { get; set; }
156 public int Countdown { get; set; }
157 public int DailyLimit { get; set; }
158 public Dictionary<string, int> VIPDailyLimits { get; set; }
159 public Dictionary<string, int> VIPCooldowns { get; set; }
160 public Dictionary<string, int> VIPCountdowns { get; set; }
161 public int RequestDuration { get; set; }
162 public bool OffsetTPRTarget { get; set; }
163 public bool BlockTPAOnCeiling { get; set; }
164 public bool UsableOutOfBuildingBlocked { get; set; }
165 public bool UsableIntoBuildingBlocked { get; set; }
166 public bool CupOwnerAllowOnBuildingBlocked { get; set; }
167 public bool AllowCraft { get; set; }
168 public int Pay { get; set; }
169 public int Bypass { get; set; }
170 }
171
172 class TownData
173 {
174 public int Cooldown { get; set; }
175 public int Countdown { get; set; }
176 public int DailyLimit { get; set; }
177 public Dictionary<string, int> VIPDailyLimits { get; set; }
178 public Dictionary<string, int> VIPCooldowns { get; set; }
179 public Dictionary<string, int> VIPCountdowns { get; set; }
180 public Vector3 Location { get; set; }
181 public bool UsableOutOfBuildingBlocked { get; set; }
182 public bool AllowCraft { get; set; }
183 public int Pay { get; set; }
184 public int Bypass { get; set; }
185 }
186
187 class AdminData
188 {
189 [JsonProperty("pl")]
190 public Vector3 PreviousLocation { get; set; }
191
192 [JsonProperty("l")]
193 public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
194 }
195
196 class HomeData
197 {
198 [JsonProperty("l")]
199 public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
200
201 [JsonProperty("t")]
202 public TeleportData Teleports { get; set; } = new TeleportData();
203 }
204
205 class TeleportData
206 {
207 [JsonProperty("a")]
208 public int Amount { get; set; }
209
210 [JsonProperty("d")]
211 public string Date { get; set; }
212
213 [JsonProperty("t")]
214 public int Timestamp { get; set; }
215 }
216
217 class TeleportTimer
218 {
219 public Timer Timer { get; set; }
220 public BasePlayer OriginPlayer { get; set; }
221 public BasePlayer TargetPlayer { get; set; }
222 }
223
224 protected override void LoadDefaultConfig()
225 {
226 Config.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
227 Config.Settings.Converters = new JsonConverter[] { new UnityVector3Converter() };
228 Config.WriteObject(new ConfigData
229 {
230 Settings = new SettingsData
231 {
232 ChatName = "<color=red>Teleportation</color>: ",
233 HomesEnabled = true,
234 TPREnabled = true,
235 TownEnabled = true,
236 InterruptTPOnHurt = true,
237 InterruptTPOnCold = false,
238 InterruptTPOnHot = false,
239 MinimumTemp = 0f,
240 MaximumTemp = 40f,
241 InterruptTPOnSafe = true,
242 InterruptTPOnBalloon = true,
243 InterruptTPOnCargo = true,
244 InterruptTPOnRig = false,
245 InterruptTPOnLift = true,
246 InterruptTPOnMonument = false,
247 InterruptTPOnMounted = true,
248 InterruptTPOnSwimming = true,
249 CaveDistanceSmall = 40f,
250 CaveDistanceMedium = 60f,
251 CaveDistanceLarge = 100f,
252 DefaultMonumentSize = 50f,
253 BypassCMD = "pay",
254 UseEconomics = false,
255 UseServerRewards = false
256 },
257 Admin = new AdminSettingsData
258 {
259 AnnounceTeleportToTarget = false,
260 UseableByAdmins = true,
261 UseableByModerators = true,
262 LocationRadius = 25,
263 TeleportNearDefaultDistance = 30
264 },
265 Home = new HomesSettingsData
266 {
267 HomesLimit = 2,
268 VIPHomesLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
269 Cooldown = 600,
270 Countdown = 15,
271 DailyLimit = 5,
272 VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
273 VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
274 VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
275 LocationRadius = 25,
276 ForceOnTopOfFoundation = true,
277 CheckFoundationForOwner = true,
278 UseFriends = true,
279 AllowAboveFoundation = true,
280 CheckValidOnList = false,
281 CupOwnerAllowOnBuildingBlocked = true
282 },
283 TPR = new TPRData
284 {
285 Cooldown = 600,
286 Countdown = 15,
287 DailyLimit = 5,
288 VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
289 VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
290 VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
291 RequestDuration = 30,
292 BlockTPAOnCeiling = true,
293 OffsetTPRTarget = true,
294 CupOwnerAllowOnBuildingBlocked = true
295 },
296 Town = new TownData
297 {
298 Cooldown = 600,
299 Countdown = 15,
300 DailyLimit = 5,
301 VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
302 VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
303 VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } }
304 },
305 Version = Version
306 }, true);
307 }
308
309 private void Init()
310 {
311 lang.RegisterMessages(new Dictionary<string, string>
312 {
313 {"AdminTP", "You teleported to {0}!"},
314 {"AdminTPTarget", "{0} teleported to you!"},
315 {"AdminTPPlayers", "You teleported {0} to {1}!"},
316 {"AdminTPPlayer", "{0} teleported you to {1}!"},
317 {"AdminTPPlayerTarget", "{0} teleported {1} to you!"},
318 {"AdminTPCoordinates", "You teleported to {0}!"},
319 {"AdminTPTargetCoordinates", "You teleported {0} to {1}!"},
320 {"AdminTPOutOfBounds", "You tried to teleport to a set of coordinates outside the map boundaries!"},
321 {"AdminTPBoundaries", "X and Z values need to be between -{0} and {0} while the Y value needs to be between -100 and 2000!"},
322 {"AdminTPLocation", "You teleported to {0}!"},
323 {"AdminTPLocationSave", "You have saved the current location!"},
324 {"AdminTPLocationRemove", "You have removed the location {0}!"},
325 {"AdminLocationList", "The following locations are available:"},
326 {"AdminLocationListEmpty", "You haven't saved any locations!"},
327 {"AdminTPBack", "You've teleported back to your previous location!"},
328 {"AdminTPBackSave", "Your previous location has been saved, use /tpb to teleport back!"},
329 {"AdminTPTargetCoordinatesTarget", "{0} teleported you to {1}!"},
330 {"AdminTPConsoleTP", "You were teleported to {0}"},
331 {"AdminTPConsoleTPPlayer", "You were teleported to {0}"},
332 {"AdminTPConsoleTPPlayerTarget", "{0} was teleported to you!"},
333 {"HomeTP", "You teleported to your home '{0}'!"},
334 {"HomeAdminTP", "You teleported to {0}'s home '{1}'!"},
335 {"HomeSave", "You have saved the current location as your home!"},
336 {"HomeNoFoundation", "You can only use a home location on a foundation!"},
337 {"HomeFoundationNotOwned", "You can't use home on someone else's house."},
338 {"HomeFoundationUnderneathFoundation", "You can't use home on a foundation that is underneath another foundation."},
339 {"HomeFoundationNotFriendsOwned", "You or a friend need to own the house to use home!"},
340 {"HomeRemovedInvalid", "Your home '{0}' was removed because not on a foundation or not owned!"},
341 {"HomeRemovedInsideBlock", "Your home '{0}' was removed because inside a foundation!"},
342 {"HomeRemove", "You have removed your home {0}!"},
343 {"HomeDelete", "You have removed {0}'s home '{1}'!"},
344 {"HomeList", "The following homes are available:"},
345 {"HomeListEmpty", "You haven't saved any homes!"},
346 {"HomeMaxLocations", "Unable to set your home here, you have reached the maximum of {0} homes!"},
347 {"HomeQuota", "You have set {0} of the maximum {1} homes!"},
348 {"HomeTPStarted", "Teleporting to your home {0} in {1} seconds!"},
349 {"PayToHome", "Standard payment of {0} applies to all home teleports!"},
350 {"PayToTown", "Standard payment of {0} applies to all town teleports!"},
351 {"PayToTPR", "Standard payment of {0} applies to all tprs!"},
352 {"HomeTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
353 {"HomeTPCooldownBypass", "Your teleport was currently on cooldown. You chose to bypass that by paying {0} from your balance."},
354 {"HomeTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
355 {"HomeTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
356 {"HomeTPCooldownBypassP2", "Type /home NAME {0}." },
357 {"HomeTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
358 {"HomeTPAmount", "You have {0} home teleports left today!"},
359 {"HomesListWiped", "You have wiped all the saved home locations!"},
360 {"HomeTPBuildingBlocked", "You can't set your home if you are not allowed to build in this zone!"},
361 {"HomeTPSwimming", "You can't set your home while swimming!"},
362 {"HomeTPCrafting", "You can't set your home while crafting!"},
363 {"Request", "You've requested a teleport to {0}!"},
364 {"RequestTarget", "{0} requested to be teleported to you! Use '/tpa' to accept!"},
365 {"PendingRequest", "You already have a request pending, cancel that request or wait until it gets accepted or times out!"},
366 {"PendingRequestTarget", "The player you wish to teleport to already has a pending request, try again later!"},
367 {"NoPendingRequest", "You have no pending teleport request!"},
368 {"AcceptOnRoof", "You can't accept a teleport while you're on a ceiling, get to ground level!"},
369 {"Accept", "{0} has accepted your teleport request! Teleporting in {1} seconds!"},
370 {"AcceptTarget", "You've accepted the teleport request of {0}!"},
371 {"NotAllowed", "You are not allowed to use this command!"},
372 {"Success", "You teleported to {0}!"},
373 {"SuccessTarget", "{0} teleported to you!"},
374 {"Cancelled", "Your teleport request to {0} was cancelled!"},
375 {"CancelledTarget", "{0} teleport request was cancelled!"},
376 {"TPCancelled", "Your teleport was cancelled!"},
377 {"TPCancelledTarget", "{0} cancelled teleport!"},
378 {"TPYouCancelledTarget", "You cancelled {0} teleport!"},
379 {"TimedOut", "{0} did not answer your request in time!"},
380 {"TimedOutTarget", "You did not answer {0}'s teleport request in time!"},
381 {"TargetDisconnected", "{0} has disconnected, your teleport was cancelled!"},
382 {"TPRCooldown", "Your teleport requests are currently on cooldown. You'll have to wait {0} to send your next teleport request."},
383 {"TPRCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
384 {"TPRCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
385 {"TPRCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
386 {"TPMoney", "{0} deducted from your account!"},
387 {"TPNoMoney", "You do not have {0} in any account!"},
388 {"TPRCooldownBypassP2", "Type /tpr {0}." },
389 {"TPRCooldownBypassP2a", "Type /tpr NAME {0}." },
390 {"TPRLimitReached", "You have reached the daily limit of {0} teleport requests today!"},
391 {"TPRAmount", "You have {0} teleport requests left today!"},
392 {"TPRTarget", "Your target is currently not available!"},
393 {"TPDead", "You can't teleport while being dead!"},
394 {"TPWounded", "You can't teleport while wounded!"},
395 {"TPTooCold", "You're too cold to teleport!"},
396 {"TPTooHot", "You're too hot to teleport!"},
397 {"TPMounted", "You can't teleport while seated!"},
398 {"TPBuildingBlocked", "You can't teleport while in a building blocked zone!"},
399 {"TPTargetBuildingBlocked", "You can't teleport in a building blocked zone!"},
400 {"TPTargetInsideBlock", "You can't teleport into a foundation!"},
401 {"TPSwimming", "You can't teleport while swimming!"},
402 {"TPCargoShip", "You can't teleport from the cargo ship!"},
403 {"TPOilRig", "You can't teleport from the oil rig!"},
404 {"TPHotAirBalloon", "You can't teleport to or from a hot air balloon!"},
405 {"TPLift", "You can't teleport while in an elevator or bucket lift!"},
406 {"TPBucketLift", "You can't teleport while in a bucket lift!"},
407 {"TPRegLift", "You can't teleport while in an elevator!"},
408 {"TPSafeZone", "You can't teleport from a safezone!"},
409 {"TPCrafting", "You can't teleport while crafting!"},
410 {"TPBlockedItem", "You can't teleport while carrying: {0}!"},
411 {"TooCloseToMon", "You can't teleport so close to the {0}!"},
412 {"TooCloseToCave", "You can't teleport so close to a cave!"},
413 {"HomeTooCloseToCave", "You can't set home so close to a cave!"},
414 {"TownTP", "You teleported to town!"},
415 {"TownTPNotSet", "Town is currently not set!"},
416 {"TownTPDisabled", "Town is currently not enabled!"},
417 {"TownTPLocation", "You have set the town location set to {0}!"},
418 {"TownTPStarted", "Teleporting to town in {0} seconds!"},
419 {"TownTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
420 {"TownTPCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
421 {"TownTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
422 {"TownTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
423 {"TownTPCooldownBypassP2", "Type /town {0}." },
424 {"TownTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
425 {"TownTPAmount", "You have {0} town teleports left today!"},
426 {"Interrupted", "Your teleport was interrupted!"},
427 {"InterruptedTarget", "{0}'s teleport was interrupted!"},
428 {"Unlimited", "Unlimited"},
429 {
430 "TPInfoGeneral", string.Join(NewLine, new[]
431 {
432 "Please specify the module you want to view the info of.",
433 "The available modules are: ",
434 })
435 },
436 {
437 "TPHelpGeneral", string.Join(NewLine, new[]
438 {
439 "/tpinfo - Shows limits and cooldowns.",
440 "Please specify the module you want to view the help of.",
441 "The available modules are: ",
442 })
443 },
444 {
445 "TPHelpadmintp", string.Join(NewLine, new[]
446 {
447 "As an admin you have access to the following commands:",
448 "/tp \"targetplayer\" - Teleports yourself to the target player.",
449 "/tp \"player\" \"targetplayer\" - Teleports the player to the target player.",
450 "/tp x y z - Teleports you to the set of coordinates.",
451 "/tpl - Shows a list of saved locations.",
452 "/tpl \"location name\" - Teleports you to a saved location.",
453 "/tpsave \"location name\" - Saves your current position as the location name.",
454 "/tpremove \"location name\" - Removes the location from your saved list.",
455 "/tpb - Teleports you back to the place where you were before teleporting.",
456 "/home radius \"radius\" - Find all homes in radius.",
457 "/home delete \"player name|id\" \"home name\" - Remove a home from a player.",
458 "/home tp \"player name|id\" \"name\" - Teleports you to the home location with the name 'name' from the player.",
459 "/home homes \"player name|id\" - Shows you a list of all homes from the player."
460 })
461 },
462 {
463 "TPHelphome", string.Join(NewLine, new[]
464 {
465 "With the following commands you can set your home location to teleport back to:",
466 "/home add \"name\" - Saves your current position as the location name.",
467 "/home list - Shows you a list of all the locations you have saved.",
468 "/home remove \"name\" - Removes the location of your saved homes.",
469 "/home \"name\" - Teleports you to the home location."
470 })
471 },
472 {
473 "TPHelptpr", string.Join(NewLine, new[]
474 {
475 "With these commands you can request to be teleported to a player or accept someone else's request:",
476 "/tpr \"player name\" - Sends a teleport request to the player.",
477 "/tpa - Accepts an incoming teleport request.",
478 "/tpc - Cancel teleport or request."
479 })
480 },
481 {
482 "TPSettingsGeneral", string.Join(NewLine, new[]
483 {
484 "Please specify the module you want to view the settings of. ",
485 "The available modules are:",
486 })
487 },
488 {
489 "TPSettingshome", string.Join(NewLine, new[]
490 {
491 "Home System has the current settings enabled:",
492 "Time between teleports: {0}",
493 "Daily amount of teleports: {1}",
494 "Amount of saved Home locations: {2}"
495 })
496 },
497 {
498 "TPSettingstpr", string.Join(NewLine, new[]
499 {
500 "TPR System has the current settings enabled:",
501 "Time between teleports: {0}",
502 "Daily amount of teleports: {1}"
503 })
504 },
505 {
506 "TPSettingstown", string.Join(NewLine, new[]
507 {
508 "Town System has the current settings enabled:",
509 "Time between teleports: {0}",
510 "Daily amount of teleports: {1}"
511 })
512 },
513 {"PlayerNotFound", "The specified player couldn't be found please try again!"},
514 {"MultiplePlayers", "Found multiple players: {0}"},
515 {"CantTeleportToSelf", "You can't teleport to yourself!"},
516 {"CantTeleportPlayerToSelf", "You can't teleport a player to himself!"},
517 {"TeleportPending", "You can't initiate another teleport while you have a teleport pending!"},
518 {"TeleportPendingTarget", "You can't request a teleport to someone who's about to teleport!"},
519 {"LocationExists", "A location with this name already exists at {0}!"},
520 {"LocationExistsNearby", "A location with the name {0} already exists near this position!"},
521 {"LocationNotFound", "Couldn't find a location with that name!"},
522 {"NoPreviousLocationSaved", "No previous location saved!"},
523 {"HomeExists", "You have already saved a home location by this name!"},
524 {"HomeExistsNearby", "A home location with the name {0} already exists near this position!"},
525 {"HomeNotFound", "Couldn't find your home with that name!"},
526 {"InvalidCoordinates", "The coordinates you've entered are invalid!"},
527 {"InvalidHelpModule", "Invalid module supplied!"},
528 {"InvalidCharacter", "You have used an invalid character, please limit yourself to the letters a to z and numbers."},
529 {
530 "SyntaxCommandTP", string.Join(NewLine, new[]
531 {
532 "A Syntax Error Occurred!",
533 "You can only use the /tp command as follows:",
534 "/tp \"targetplayer\" - Teleports yourself to the target player.",
535 "/tp \"player\" \"targetplayer\" - Teleports the player to the target player.",
536 "/tp x y z - Teleports you to the set of coordinates.",
537 "/tp \"player\" x y z - Teleports the player to the set of coordinates."
538 })
539 },
540 {
541 "SyntaxCommandTPL", string.Join(NewLine, new[]
542 {
543 "A Syntax Error Occurred!",
544 "You can only use the /tpl command as follows:",
545 "/tpl - Shows a list of saved locations.",
546 "/tpl \"location name\" - Teleports you to a saved location."
547 })
548 },
549 {
550 "SyntaxCommandTPSave", string.Join(NewLine, new[]
551 {
552 "A Syntax Error Occurred!",
553 "You can only use the /tpsave command as follows:",
554 "/tpsave \"location name\" - Saves your current position as 'location name'."
555 })
556 },
557 {
558 "SyntaxCommandTPRemove", string.Join(NewLine, new[]
559 {
560 "A Syntax Error Occurred!",
561 "You can only use the /tpremove command as follows:",
562 "/tpremove \"location name\" - Removes the location with the name 'location name'."
563 })
564 },
565 {
566 "SyntaxCommandTPN", string.Join(NewLine, new[]
567 {
568 "A Syntax Error Occurred!",
569 "You can only use the /tpn command as follows:",
570 "/tpn \"targetplayer\" - Teleports yourself the default distance behind the target player.",
571 "/tpn \"targetplayer\" \"distance\" - Teleports you the specified distance behind the target player."
572 })
573 },
574 {
575 "SyntaxCommandSetHome", string.Join(NewLine, new[]
576 {
577 "A Syntax Error Occurred!",
578 "You can only use the /home add command as follows:",
579 "/home add \"name\" - Saves the current location as your home with the name 'name'."
580 })
581 },
582 {
583 "SyntaxCommandRemoveHome", string.Join(NewLine, new[]
584 {
585 "A Syntax Error Occurred!",
586 "You can only use the /home remove command as follows:",
587 "/home remove \"name\" - Removes the home location with the name 'name'."
588 })
589 },
590 {
591 "SyntaxCommandHome", string.Join(NewLine, new[]
592 {
593 "A Syntax Error Occurred!",
594 "You can only use the /home command as follows:",
595 "/home \"name\" - Teleports yourself to your home with the name 'name'.",
596 "/home \"name\" pay - Teleports yourself to your home with the name 'name', avoiding cooldown by paying for it.",
597 "/home add \"name\" - Saves the current location as your home with the name 'name'.",
598 "/home list - Shows you a list of all your saved home locations.",
599 "/home remove \"name\" - Removes the home location with the name 'name'."
600 })
601 },
602 {
603 "SyntaxCommandHomeAdmin", string.Join(NewLine, new[]
604 {
605 "/home radius \"radius\" - Shows you a list of all homes in radius(10).",
606 "/home delete \"player name|id\" \"name\" - Removes the home location with the name 'name' from the player.",
607 "/home tp \"player name|id\" \"name\" - Teleports you to the home location with the name 'name' from the player.",
608 "/home homes \"player name|id\" - Shows you a list of all homes from the player."
609 })
610 },
611 {
612 "SyntaxCommandTown", string.Join(NewLine, new[]
613 {
614 "A Syntax Error Occurred!",
615 "You can only use the /town command as follows:",
616 "/town - Teleports yourself to town.",
617 "/town pay - Teleports yourself to town, paying the penalty."
618 })
619 },
620 {
621 "SyntaxCommandTownAdmin", string.Join(NewLine, new[]
622 {
623 "/town set - Saves the current location as town.",
624 })
625 },
626 {
627 "SyntaxCommandHomeDelete", string.Join(NewLine, new[]
628 {
629 "A Syntax Error Occurred!",
630 "You can only use the /home delete command as follows:",
631 "/home delete \"player name|id\" \"name\" - Removes the home location with the name 'name' from the player."
632 })
633 },
634 {
635 "SyntaxCommandHomeAdminTP", string.Join(NewLine, new[]
636 {
637 "A Syntax Error Occurred!",
638 "You can only use the /home tp command as follows:",
639 "/home tp \"player name|id\" \"name\" - Teleports you to the home location with the name 'name' from the player."
640 })
641 },
642 {
643 "SyntaxCommandHomeHomes", string.Join(NewLine, new[]
644 {
645 "A Syntax Error Occurred!",
646 "You can only use the /home homes command as follows:",
647 "/home homes \"player name|id\" - Shows you a list of all homes from the player."
648 })
649 },
650 {
651 "SyntaxCommandListHomes", string.Join(NewLine, new[]
652 {
653 "A Syntax Error Occurred!",
654 "You can only use the /home list command as follows:",
655 "/home list - Shows you a list of all your saved home locations."
656 })
657 },
658 {
659 "SyntaxCommandTPR", string.Join(NewLine, new[]
660 {
661 "A Syntax Error Occurred!",
662 "You can only use the /tpr command as follows:",
663 "/tpr \"player name\" - Sends out a teleport request to 'player name'."
664 })
665 },
666 {
667 "SyntaxCommandTPA", string.Join(NewLine, new[]
668 {
669 "A Syntax Error Occurred!",
670 "You can only use the /tpa command as follows:",
671 "/tpa - Accepts an incoming teleport request."
672 })
673 },
674 {
675 "SyntaxCommandTPC", string.Join(NewLine, new[]
676 {
677 "A Syntax Error Occurred!",
678 "You can only use the /tpc command as follows:",
679 "/tpc - Cancels an teleport request."
680 })
681 },
682 {
683 "SyntaxConsoleCommandToPos", string.Join(NewLine, new[]
684 {
685 "A Syntax Error Occurred!",
686 "You can only use the teleport.topos console command as follows:",
687 " > teleport.topos \"player\" x y z"
688 })
689 },
690 {
691 "SyntaxConsoleCommandToPlayer", string.Join(NewLine, new[]
692 {
693 "A Syntax Error Occurred!",
694 "You can only use the teleport.toplayer console command as follows:",
695 " > teleport.toplayer \"player\" \"target player\""
696 })
697 },
698 {"LogTeleport", "{0} teleported to {1}."},
699 {"LogTeleportPlayer", "{0} teleported {1} to {2}."},
700 {"LogTeleportBack", "{0} teleported back to previous location."}
701 }, this);
702
703 lang.RegisterMessages(new Dictionary<string, string>
704 {
705 {"AdminTP", "Ты телепортировался в {0}!"},
706 {"AdminTPTarget", "{0} телепортировался к тебе!"},
707 {"AdminTPPlayers", "Ты телепортировался {0} к {1}!"},
708 {"AdminTPPlayer", "{0} телепортировался вам {1}!"},
709 {"AdminTPPlayerTarget", "{0} телепортированный {1} для вас!"},
710 {"AdminTPCoordinates", "Ты телепортировался в {0}!"},
711 {"AdminTPTargetCoordinates", "Ты телепортировался {0} к {1}!"},
712 {"AdminTPOutOfBounds", "Вы пытались телепортироваться в набор координат за пределами границ карты!"},
713 {"AdminTPBoundaries", "X и Z ценности должны быть между -{0} и {0} а Y значение должно быть между -100 и 2000!"},
714 {"AdminTPLocation", "Ты телепортировался в {0}!"},
715 {"AdminTPLocationSave", "Вы сохранили текущее местоположение!"},
716 {"AdminTPLocationRemove", "Вы удалили местоположение {0}!"},
717 {"AdminLocationList", "Доступны следующие местоположения,"},
718 {"AdminLocationListEmpty", "Вы не сохранили никаких мест!"},
719 {"AdminTPBack", "Ты телепортировался обратно на прежнее место!"},
720 {"AdminTPBackSave", "Ваше предыдущее местоположение было сохранено, используйте /tpb телепортироваться обратно!"},
721 {"AdminTPTargetCoordinatesTarget", "{0} телепортировался вам {1}!"},
722 {"AdminTPConsoleTP", "Тебя телепортировали в {0}"},
723 {"AdminTPConsoleTPPlayer", "Тебя телепортировали в {0}"},
724 {"AdminTPConsoleTPPlayerTarget", "{0} телепортировался к тебе!"},
725 {"HomeTP", "Ты телепортировался в свой дом '{0}'!"},
726 {"HomeAdminTP", "Ты телепортировался в {0}'s дом '{1}'!"},
727 {"HomeSave", "Вы сохранили текущее местоположение в качестве своего дома!"},
728 {"HomeNoFoundation", "Вы можете использовать только расположение дома на фундаменте!"},
729 {"HomeFoundationNotOwned", "Ты не можешь использовать дом на чужом доме."},
730 {"HomeFoundationUnderneathFoundation", "Вы не можете использовать home на фундаменте, который находится под другим фундаментом."},
731 {"HomeFoundationNotFriendsOwned", "Вы или друг должны владеть домом, чтобы использовать дом!"},
732 {"HomeRemovedInvalid", "Твой дом '{0}' убрали, потому что не на фундаменте или не в собственности!"},
733 {"HomeRemovedInsideBlock", "Твой дом '{0}' убрали, потому что внутри фундамент!"},
734 {"HomeRemove", "Вы удалили свой дом {0}!"},
735 {"HomeDelete", "Вы удалили {0}'s дом '{1}'!"},
736 {"HomeList", "Доступны следующие дома,"},
737 {"HomeListEmpty", "Вы не спасли ни одного дома!"},
738 {"HomeMaxLocations", "Не в состоянии установить свой дом здесь, вы достигли максимума {0} дома!"},
739 {"HomeQuota", "Вы установили {0} максимума {1} дома!"},
740 {"HomeTPStarted", "Телепортация в ваш дом {0} в {1} секунды!"},
741 {"PayToHome", "Стандартная оплата {0} применяется ко всем домашним телепортам!"},
742 {"PayToTown", "Стандартная оплата {0} применяется ко всем городским телепортам!"},
743 {"PayToTPR", "Стандартная оплата {0} распространяться на всех tprs!"},
744 {"HomeTPCooldown", "Ваш телепорт в настоящее время находится на перезарядке. Вам придется подождать {0} для следующего телепорта."},
745 {"HomeTPCooldownBypass", "Ваш телепорт в настоящее время был на перезарядке. Вы решили обойти это, заплатив {0} с вашего баланса."},
746 {"HomeTPCooldownBypassF", "Ваш телепорт в настоящее время находится на перезарядке. У вас недостаточно средств - {0} - обходить."},
747 {"HomeTPCooldownBypassP", "Вы можете заплатить {0} чтобы обойти это охлаждение."},
748 {"HomeTPCooldownBypassP2", "Тип /home NAME {0}."},
749 {"HomeTPLimitReached", "Вы достигли дневного предела {0} телепорты сегодня!"},
750 {"HomeTPAmount", "У вас есть {0} домашние телепорты ушли сегодня!"},
751 {"HomesListWiped", "Вы стерли все сохраненные домашние местоположения!"},
752 {"HomeTPBuildingBlocked", "Вы не можете установить свой дом, если вам не разрешено строить в этой зоне!"},
753 {"HomeTPSwimming", "Вы не можете установить свой дом во время плавания!"},
754 {"HomeTPCrafting", "Вы не можете установить свой дом во время крафта!"},
755 {"Request", "Вы запросили телепорт на {0}!"},
756 {"RequestTarget", "{0} просят телепортироваться к вам! Использовать '/tpa' принять!"},
757 {"PendingRequest", "У вас уже есть запрос в ожидании, отменить этот запрос или ждать, пока он не будет принят или тайм-аут!"},
758 {"PendingRequestTarget", "Игрок, которому вы хотите телепортироваться, уже имеет ожидающий запрос, повторите попытку позже!"},
759 {"NoPendingRequest", "У вас нет запроса на телепортацию!"},
760 {"AcceptOnRoof", "Вы не можете принять телепорт, пока вы на потолке, добраться до уровня земли!"},
761 {"Accept", "{0} принял ваш запрос на телепортацию! Телепортироваться в {1} секунды!"},
762 {"AcceptTarget", "Вы приняли запрос на телепортацию {0}!"},
763 {"NotAllowed", "Вы не имеете права использовать эту команду!"},
764 {"Success", "Ты телепортировался в {0}!"},
765 {"SuccessTarget", "{0} телепортировался к тебе!"},
766 {"Cancelled", "Ваш телепорт запрос {0} был отменен!"},
767 {"CancelledTarget", "{0} запрос на телепортацию был отменен!"},
768 {"TPCancelled", "Твой телепорт был отменен!"},
769 {"TPCancelledTarget", "{0} отменен телепорт!"},
770 {"TPYouCancelledTarget", "Вы отменили {0} телепортацию!"},
771 {"TimedOut", "{0} не ответил на ваш запрос!"},
772 {"TimedOutTarget", "Ты не ответил. {0}'s телепортируйте запрос вовремя!"},
773 {"TargetDisconnected", "{0} отключился, телепорт отменен!"},
774 {"TPRCooldown", "Ваши запросы на телепортацию в настоящее время находятся на перезарядке. Вам придется подождать {0} отправить следующий запрос на телепортацию."},
775 {"TPRCooldownBypass", "Ваш запрос на телепортацию был на перезарядке. Вы решили обойти это, заплатив {0} с вашего баланса."},
776 {"TPRCooldownBypassF", "Ваш телепорт в настоящее время находится на перезарядке. У вас недостаточно средств - {0} - обходить."},
777 {"TPRCooldownBypassP", "Вы можете заплатить {0} чтобы обойти это охлаждение."},
778 {"TPMoney", "{0} вычитается из вашего счета!"},
779 {"TPNoMoney", "У вас нет {0} в любом аккаунте!"},
780 {"TPRCooldownBypassP2", "Тип /tpr {0}."},
781 {"TPRCooldownBypassP2a", "Тип /tpr NAME {0}."},
782 {"TPRLimitReached", "Вы достигли дневного предела {0} телепорт просит сегодня!"},
783 {"TPRAmount", "У вас есть {0} телепорт просит оставить сегодня!"},
784 {"TPRTarget", "Ваша цель в настоящее время недоступна!"},
785 {"TPDead", "Ты не можешь телепортироваться, будучи мертвым!"},
786 {"TPWounded", "Ты не можешь телепортироваться, пока ранен!"},
787 {"TPTooCold", "Ты слишком замерз, чтобы телепортироваться!"},
788 {"TPTooHot", "Ты слишком горячий, чтобы телепортироваться!"},
789 {"TPMounted", "Ты не можешь телепортироваться сидя!"},
790 {"TPBuildingBlocked", "Вы не можете телепортироваться в заблокированной зоне здания!"},
791 {"TPTargetBuildingBlocked", "Вы не можете телепортироваться в заблокированной зоне здания!"},
792 {"TPTargetInsideBlock", "Ты не можешь телепортироваться в Фонд!"},
793 {"TPSwimming", "Ты не можешь телепортироваться во время плавания!"},
794 {"TPCargoShip", "Ты не можешь телепортироваться с грузового корабля!"},
795 {"TPOilRig", "Ты не можешь телепортироваться с нефтяная вышка!"},
796 {"TPHotAirBalloon", "Вы не можете телепортироваться на воздушный шар или с него!"},
797 {"TPLift", "Вы не можете телепортироваться в лифте или ковшовом подъемнике!"},
798 {"TPBucketLift", "Вы не можете телепортироваться, находясь в ковшовом подъемнике!"},
799 {"TPRegLift", "Ты не можешь телепортироваться в лифте!"},
800 {"TPSafeZone", "Ты не можешь телепортироваться из безопасной зоны!"},
801 {"TPCrafting", "Вы не можете телепортироваться во время крафта!"},
802 {"TPBlockedItem", "Вы не можете телепортироваться во время переноски, {0}!"},
803 {"TooCloseToMon", "Ты не можешь телепортироваться так близко к {0}!"},
804 {"TooCloseToCave", "Ты не можешь телепортироваться так близко к пещере!"},
805 {"HomeTooCloseToCave", "Нельзя сидеть дома так близко к пещере!"},
806 {"TownTP", "Ты телепортировался в город!"},
807 {"TownTPNotSet", "Город в настоящее время не установлен!"},
808 {"TownTPDisabled", "Город в настоящее время не включен!"},
809 {"TownTPLocation", "Вы установили местоположение города в {0}!"},
810 {"TownTPStarted", "Телепортация в город в {0} секунды!"},
811 {"TownTPCooldown", "Ваш телепорт в настоящее время находится на перезарядке. Вам придется подождать {0} для следующего телепорта."},
812 {"TownTPCooldownBypass", "Ваш запрос на телепортацию был на перезарядке. Вы решили обойти это, заплатив {0} с вашего баланса."},
813 {"TownTPCooldownBypassF", "Ваш телепорт в настоящее время находится на перезарядке. У вас недостаточно средств - {0} - обходить."},
814 {"TownTPCooldownBypassP", "Вы можете заплатить {0} чтобы обойти это охлаждение."},
815 {"TownTPCooldownBypassP2", "Тип /town {0}."},
816 {"TownTPLimitReached", "Вы достигли дневного предела {0} телепорты сегодня!"},
817 {"TownTPAmount", "У вас есть {0} городские телепорты ушли сегодня!"},
818 {"Interrupted", "Ваш телепорт был прерван!"},
819 {"InterruptedTarget", "{0}'s телепорт был прерван!"},
820 {"Unlimited", "Unlimited"},
821 {
822 "TPInfoGeneral", string.Join(NewLine, new[]
823 {
824 "Пожалуйста, укажите модуль, который вы хотите просмотреть.",
825 "Имеющиеся модули, "
826 })
827 },
828 {
829 "TPHelpGeneral", string.Join(NewLine, new[]
830 {
831 "/tpinfo - Показывает ограничения и кулдауны.",
832 "Пожалуйста, укажите модуль, который вы хотите просмотреть в справке.",
833 "Имеющиеся модули, "
834 })
835 },
836 {
837 "TPHelpadmintp", string.Join(NewLine, new[]
838 {
839 "Как администратор Вы имеете доступ к следующим командам,",
840 "/tp \"targetplayer\" - Телепортирует себя к целевому игроку.",
841 "/tp \"player\" \"targetplayer\" - Телепортирует игрока к целевому игроку.",
842 "/tp x y z - Телепортирует вас в набор координат.",
843 "/tpl - Показывает список сохраненных местоположений.",
844 "/tpl \"location name\" - Телепортирует вас в сохраненное место.",
845 "/tpsave \"location name\" - Сохраняет текущую позицию в качестве имени местоположения.",
846 "/tpremove \"location name\" - Удаляет местоположение из сохраненного списка.",
847 "/tpb - Телепортирует вас обратно в то место, где вы были до телепортации.",
848 "/home radius \"radius\" - Найти все дома в радиусе.",
849 "/home delete \"player name|id\" \"home name\" - Удалите дом из плеера.",
850 "/home tp \"player name|id\" \"name\" - Телепортирует вас на главную локацию с именем 'name' от игрока.",
851 "/home homes \"player name|id\" - Показывает список всех домов из плеера."
852 })
853 },
854 {
855 "TPHelphome", string.Join(NewLine, new[]
856 {
857 "С помощью следующих команд вы можете установить свое домашнее местоположение для телепортации обратно в,",
858 "/home add \"name\" - Сохраняет текущую позицию в качестве имени местоположения.",
859 "/home list - Показывает список всех сохраненных местоположений.",
860 "/home remove \"name\" - Удаляет местоположение сохраненных домов.",
861 "/home \"name\" - Телепортирует вас на родное место."
862 })
863 },
864 {
865 "TPHelptpr", string.Join(NewLine, new[]
866 {
867 "С помощью этих команд вы можете запросить телепортацию к игроку или принять чей-либо запрос,",
868 "/tpr \"player name\" - Отправляет запрос на телепортацию игроку.",
869 "/tpa - Принимает входящий запрос телепорта.",
870 "/tpc - Отменить телепортацию или запрос."
871 })
872 },
873 {
874 "TPSettingsGeneral", string.Join(NewLine, new[]
875 {
876 "Пожалуйста, укажите модуль, который вы хотите просмотреть настройки.",
877 "Имеющиеся модули,"
878 })
879 },
880 {
881 "TPSettingshome", string.Join(NewLine, new[]
882 {
883 "В домашней системе включены текущие настройки,",
884 "Время между телепортами, {0}",
885 "Ежедневное количество телепортов, {1}",
886 "Количество сохраненных домашних местоположений, {2}"
887 })
888 },
889 {
890 "TPSettingstpr", string.Join(NewLine, new[]
891 {
892 "TPR В системе включены текущие настройки,",
893 "Время между телепортами, {0}",
894 "Ежедневное количество телепортов, {1}"
895 })
896 },
897 {
898 "TPSettingstown", string.Join(NewLine, new[]
899 {
900 "Town В системе включены текущие настройки,",
901 "Время между телепортами, {0}",
902 "Ежедневное количество телепортов, {1}"
903 })
904 },
905 {"PlayerNotFound", "Не удалось найти указанного игрока, повторите попытку!"},
906 {"MultiplePlayers", "Найдено несколько игроков, {0}"},
907 {"CantTeleportToSelf", "Ты не можешь телепортироваться к себе!"},
908 {"CantTeleportPlayerToSelf", "Вы не можете телепортировать игрока к себе!"},
909 {"TeleportPending", "Вы не можете инициировать другой телепорт, пока у вас есть телепорт в ожидании!"},
910 {"TeleportPendingTarget", "Ты не можешь просить телепортации у того, кто собирается телепортироваться!"},
911 {"LocationExists", "Место с таким именем уже существует в {0}!"},
912 {"LocationExistsNearby", "Место с именем {0} уже существует рядом с этой позицией!"},
913 {"LocationNotFound", "Не мог найти место с таким именем!"},
914 {"NoPreviousLocationSaved", "Предыдущее местоположение не сохранено!"},
915 {"HomeExists", "Вы уже сохранили расположение дома под этим именем!"},
916 {"HomeExistsNearby", "Расположение дома с именем {0} уже существует рядом с этой позицией!"},
917 {"HomeNotFound", "Не мог найти свой дом с таким именем!"},
918 {"InvalidCoordinates", "Введенные вами координаты недействительны!"},
919 {"InvalidHelpModule", "Неверный модуль поставляется!"},
920 {"InvalidCharacter", "Вы использовали недопустимый символ, пожалуйста, ограничьте себя буквами от А до Я и цифрами."},
921 {
922 "SyntaxCommandTP", string.Join(NewLine, new[]
923 {
924 "Произошла Синтаксическая Ошибка!",
925 "Вы можете использовать только /tp команда следующим образом,",
926 "/tp \"targetplayer\" - Телепортирует себя к целевому игроку.",
927 "/tp \"player\" \"targetplayer\" - Телепортирует игрока к целевому игроку.",
928 "/tp x y z - Телепортирует вас в набор координат.",
929 "/tp \"player\" x y z - Телепортирует игрока в набор координат."
930 })
931 },
932 {
933 "SyntaxCommandTPL", string.Join(NewLine, new[]
934 {
935 "Произошла Синтаксическая Ошибка!",
936 "Вы можете использовать только /tpl команда следующим образом,",
937 "/tpl - Показывает список сохраненных местоположений.",
938 "/tpl \"location name\" - Телепортирует вас в сохраненное место."
939 })
940 },
941 {
942 "SyntaxCommandTPSave", string.Join(NewLine, new[]
943 {
944 "Произошла Синтаксическая Ошибка!",
945 "Вы можете использовать только /tpsave команда следующим образом,",
946 "/tpsave \"location name\" - Сохраняет текущую позицию как 'location name'."
947 })
948 },
949 {
950 "SyntaxCommandTPRemove", string.Join(NewLine, new[]
951 {
952 "Произошла Синтаксическая Ошибка!",
953 "Вы можете использовать только /tpremove команда следующим образом,",
954 "/tpremove \"location name\" - Удаляет расположение с именем 'location name'."
955 })
956 },
957 {
958 "SyntaxCommandTPN", string.Join(NewLine, new[]
959 {
960 "Произошла Синтаксическая Ошибка!",
961 "Вы можете использовать только /tpn команда следующим образом,",
962 "/tpn \"targetplayer\" - Телепортирует себя на расстояние по умолчанию позади целевого игрока.",
963 "/tpn \"targetplayer\" \"distance\" - Телепортирует вас на указанное расстояние позади целевого игрока."
964 })
965 },
966 {
967 "SyntaxCommandSetHome", string.Join(NewLine, new[]
968 {
969 "Произошла Синтаксическая Ошибка!",
970 "Вы можете использовать только /home add команда следующим образом,",
971 "/home add \"name\" - Сохранение текущего местоположения в качестве вашего дома с именем 'name'."
972 })
973 },
974 {
975 "SyntaxCommandRemoveHome", string.Join(NewLine, new[]
976 {
977 "Произошла Синтаксическая Ошибка!",
978 "Вы можете использовать только /home remove команда следующим образом,",
979 "/home remove \"name\" - Удаляет расположение дома с именем 'name'."
980 })
981 },
982 {
983 "SyntaxCommandHome", string.Join(NewLine, new[]
984 {
985 "Произошла Синтаксическая Ошибка!",
986 "Вы можете использовать только /home команда следующим образом,",
987 "/home \"name\" - Телепортирует себя в свой дом с именем 'name'.",
988 "/home \"name\" pay - Телепортирует себя в свой дом с именем 'name', избежать перезарядки, заплатив за это.",
989 "/home add \"name\" - Сохранение текущего местоположения в качестве вашего дома с именем 'name'.",
990 "/home list - Показывает список всех сохраненных домашних местоположений.",
991 "/home remove \"name\" - Удаляет расположение дома с именем 'name'."
992 })
993 },
994 {
995 "SyntaxCommandHomeAdmin", string.Join(NewLine, new[]
996 {
997 "/home radius \"radius\" - Показывает список всех домов в radius(10).",
998 "/home delete \"player name|id\" \"name\" - Удаляет расположение дома с именем 'name' от игрока.",
999 "/home tp \"player name|id\" \"name\" - Телепортирует вас на главную локацию с именем 'name' от игрока.",
1000 "/home homes \"player name|id\" - Показывает список всех домов из плеера."
1001 })
1002 },
1003 {
1004 "SyntaxCommandTown", string.Join(NewLine, new[]
1005 {
1006 "Произошла Синтаксическая Ошибка!",
1007 "Вы можете использовать только /town команда следующим образом,",
1008 "/town - Teleports yourself to town.",
1009 "/town pay - Teleports yourself to town, paying the penalty."
1010 })
1011 },
1012 {
1013 "SyntaxCommandTownAdmin", string.Join(NewLine, new[]
1014 {
1015 "/town set - Сохраняет текущее местоположение как town."
1016 })
1017 },
1018 {
1019 "SyntaxCommandHomeDelete", string.Join(NewLine, new[]
1020 {
1021 "Произошла Синтаксическая Ошибка!",
1022 "Вы можете использовать только /home delete команда следующим образом,",
1023 "/home delete \"player name|id\" \"name\" - Удаляет расположение дома с именем 'name' от игрока."
1024 })
1025 },
1026 {
1027 "SyntaxCommandHomeAdminTP", string.Join(NewLine, new[]
1028 {
1029 "Произошла Синтаксическая Ошибка!",
1030 "Вы можете использовать только /home tp команда следующим образом,",
1031 "/home tp \"player name|id\" \"name\" - Телепортирует вас на главную локацию с именем 'name' от игрока."
1032 })
1033 },
1034 {
1035 "SyntaxCommandHomeHomes", string.Join(NewLine, new[]
1036 {
1037 "Произошла Синтаксическая Ошибка!",
1038 "Вы можете использовать только /home homes команда следующим образом,",
1039 "/home homes \"player name|id\" - Показывает список всех домов из плеера."
1040 })
1041 },
1042 {
1043 "SyntaxCommandListHomes", string.Join(NewLine, new[]
1044 {
1045 "Произошла Синтаксическая Ошибка!",
1046 "Вы можете использовать только /home list команда следующим образом,",
1047 "/home list - Показывает список всех сохраненных домашних местоположений."
1048 })
1049 },
1050 {
1051 "SyntaxCommandTPR", string.Join(NewLine, new[]
1052 {
1053 "Произошла Синтаксическая Ошибка!",
1054 "Вы можете использовать только /tpr команда следующим образом,",
1055 "/tpr \"player name\" - Отправляет запрос на телепортацию 'player name'."
1056 })
1057 },
1058 {
1059 "SyntaxCommandTPA", string.Join(NewLine, new[]
1060 {
1061 "Произошла Синтаксическая Ошибка!",
1062 "Вы можете использовать только /tpa команда следующим образом,",
1063 "/tpa - Принимает входящий запрос телепорта."
1064 })
1065 },
1066 {
1067 "SyntaxCommandTPC", string.Join(NewLine, new[]
1068 {
1069 "Произошла Синтаксическая Ошибка!",
1070 "Вы можете использовать только /tpc команда следующим образом,",
1071 "/tpc - Отменяет телепорт запросу."
1072 })
1073 },
1074 {
1075 "SyntaxConsoleCommandToPos", string.Join(NewLine, new[]
1076 {
1077 "Произошла Синтаксическая Ошибка!",
1078 "Вы можете использовать только teleport.topos console команда следующим образом,",
1079 " > teleport.topos \"player\" x y z"
1080 })
1081 },
1082 {
1083 "SyntaxConsoleCommandToPlayer", string.Join(NewLine, new[]
1084 {
1085 "Произошла Синтаксическая Ошибка!",
1086 "Вы можете использовать только teleport.toplayer console команда следующим образом,",
1087 " > teleport.toplayer \"player\" \"target player\""
1088 })
1089 },
1090 {"LogTeleport", "{0} телепортироваться {1}."},
1091 {"LogTeleportPlayer", "{0} телепортированный {1} к {2}."},
1092 {"LogTeleportBack", "{0} телепортировался на прежнее место."}
1093 }, this, "ru");
1094 }
1095
1096 private void Loaded()
1097 {
1098 Config.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
1099 Config.Settings.Converters = new JsonConverter[] { new UnityVector3Converter() };
1100 try
1101 {
1102 configData = Config.ReadObject<ConfigData>();
1103 }
1104 catch (Exception)
1105 {
1106 Puts("Corrupt config, loading default...");
1107 LoadDefaultConfig();
1108 }
1109
1110 if (!(configData.Version == Version))
1111 {
1112 if (configData.Home.VIPHomesLimits == null)
1113 {
1114 configData.Home.VIPHomesLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1115 configData.Home.VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1116 configData.Home.VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1117 configData.TPR.VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1118 configData.TPR.VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1119 configData.Town.VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1120 configData.Town.VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1121 }
1122 if (configData.Home.VIPCountdowns == null)
1123 {
1124 configData.Home.VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1125 configData.TPR.VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1126 configData.Town.VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
1127 }
1128 if (configData.Version <= new VersionNumber(1, 0, 4))
1129 configData.Home.AllowAboveFoundation = true;
1130 if (configData.Version < new VersionNumber(1, 0, 14))
1131 {
1132 configData.Home.UsableIntoBuildingBlocked = true;
1133 configData.TPR.UsableIntoBuildingBlocked = true;
1134 }
1135 if (configData.Version < new VersionNumber(1, 0, 58))
1136 {
1137 configData.Settings.InterruptTPOnMounted = true;
1138 configData.Settings.InterruptTPOnSwimming = true;
1139 }
1140 if (configData.Settings.MaximumTemp < 1)
1141 {
1142 configData.Settings.MaximumTemp = 40f;
1143 }
1144 if (configData.Settings.DefaultMonumentSize < 1)
1145 {
1146 configData.Settings.DefaultMonumentSize = 50f;
1147 }
1148 if (configData.Settings.CaveDistanceSmall < 1)
1149 {
1150 configData.Settings.CaveDistanceSmall = 40f;
1151 }
1152 if (configData.Settings.CaveDistanceMedium < 1)
1153 {
1154 configData.Settings.CaveDistanceMedium = 60f;
1155 }
1156 if (configData.Settings.CaveDistanceLarge < 1)
1157 {
1158 configData.Settings.CaveDistanceLarge = 100f;
1159 }
1160 configData.Version = Version;
1161 Config.WriteObject(configData, true);
1162 }
1163 dataAdmin = GetFile(nameof(NTeleportation) + "Admin");
1164 Admin = dataAdmin.ReadObject<Dictionary<ulong, AdminData>>();
1165 dataHome = GetFile(nameof(NTeleportation) + "Home");
1166 Home = dataHome.ReadObject<Dictionary<ulong, HomeData>>();
1167
1168 dataTPR = GetFile(nameof(NTeleportation) + "TPR");
1169 TPR = dataTPR.ReadObject<Dictionary<ulong, TeleportData>>();
1170 dataTown = GetFile(nameof(NTeleportation) + "Town");
1171 Town = dataTown.ReadObject<Dictionary<ulong, TeleportData>>();
1172 cmd.AddConsoleCommand("teleport.toplayer", this, ccmdTeleport);
1173 cmd.AddConsoleCommand("teleport.topos", this, ccmdTeleport);
1174 permission.RegisterPermission(PermDeleteHome, this);
1175 permission.RegisterPermission(PermHome, this);
1176 permission.RegisterPermission(PermHomeHomes, this);
1177 permission.RegisterPermission(PermImportHomes, this);
1178 permission.RegisterPermission(PermRadiusHome, this);
1179 permission.RegisterPermission(PermTp, this);
1180 permission.RegisterPermission(PermTpB, this);
1181 permission.RegisterPermission(PermTpR, this);
1182 permission.RegisterPermission(PermTpConsole, this);
1183 permission.RegisterPermission(PermTpHome, this);
1184 permission.RegisterPermission(PermTpTown, this);
1185 permission.RegisterPermission(PermTpN, this);
1186 permission.RegisterPermission(PermTpL, this);
1187 permission.RegisterPermission(PermTpRemove, this);
1188 permission.RegisterPermission(PermTpSave, this);
1189 permission.RegisterPermission(PermWipeHomes, this);
1190 permission.RegisterPermission(PermCraftHome, this);
1191 permission.RegisterPermission(PermCraftTown, this);
1192 permission.RegisterPermission(PermCraftTpR, this);
1193 foreach (var key in configData.Home.VIPCooldowns.Keys)
1194 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1195 foreach (var key in configData.Home.VIPCountdowns.Keys)
1196 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1197 foreach (var key in configData.Home.VIPDailyLimits.Keys)
1198 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1199 foreach (var key in configData.Home.VIPHomesLimits.Keys)
1200 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1201 foreach (var key in configData.TPR.VIPCooldowns.Keys)
1202 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1203 foreach (var key in configData.TPR.VIPCountdowns.Keys)
1204 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1205 foreach (var key in configData.TPR.VIPDailyLimits.Keys)
1206 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1207 foreach (var key in configData.Town.VIPCooldowns.Keys)
1208 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1209 foreach (var key in configData.Town.VIPCountdowns.Keys)
1210 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1211 foreach (var key in configData.Town.VIPDailyLimits.Keys)
1212 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1213
1214 FindMonuments();
1215 }
1216
1217 private DynamicConfigFile GetFile(string name)
1218 {
1219 var file = Interface.Oxide.DataFileSystem.GetFile(name);
1220 file.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
1221 file.Settings.Converters = new JsonConverter[] { new UnityVector3Converter(), new CustomComparerDictionaryCreationConverter<string>(StringComparer.OrdinalIgnoreCase) };
1222 return file;
1223 }
1224
1225 void OnServerInitialized()
1226 {
1227 boundary = TerrainMeta.Size.x / 2;
1228 CheckPerms(configData.Home.VIPHomesLimits);
1229 CheckPerms(configData.Home.VIPDailyLimits);
1230 CheckPerms(configData.Home.VIPCooldowns);
1231 CheckPerms(configData.TPR.VIPDailyLimits);
1232 CheckPerms(configData.TPR.VIPCooldowns);
1233 CheckPerms(configData.Town.VIPDailyLimits);
1234 CheckPerms(configData.Town.VIPCooldowns);
1235 foreach (var item in configData.Settings.BlockedItems)
1236 {
1237 var definition = ItemManager.FindItemDefinition(item.Key);
1238 if (definition == null)
1239 {
1240 Puts("Blocked item not found: {0}", item.Key);
1241 continue;
1242 }
1243 ReverseBlockedItems[definition.itemid] = item.Value;
1244 }
1245 }
1246
1247 void OnServerSave()
1248 {
1249 SaveTeleportsAdmin();
1250 SaveTeleportsHome();
1251 SaveTeleportsTPR();
1252 SaveTeleportsTown();
1253 }
1254
1255 void OnServerShutdown() => OnServerSave();
1256
1257 void Unload() => OnServerSave();
1258
1259 void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo)
1260 {
1261 var player = entity.ToPlayer();
1262 if (player == null || hitinfo == null) return;
1263 if (hitinfo.damageTypes.Has(DamageType.Fall) && teleporting.Contains(player.userID))
1264 {
1265 hitinfo.damageTypes = new DamageTypeList();
1266 teleporting.Remove(player.userID);
1267 }
1268 TeleportTimer teleportTimer;
1269 if (!TeleportTimers.TryGetValue(player.userID, out teleportTimer)) return;
1270 NextTick(() =>
1271 {
1272 if (hitinfo.damageTypes.Total() <= 0) return;
1273 if (configData.Settings.InterruptTPOnHurt == false) return;
1274 PrintMsgL(teleportTimer.OriginPlayer, "Interrupted");
1275 if (teleportTimer.TargetPlayer != null)
1276 PrintMsgL(teleportTimer.TargetPlayer, "InterruptedTarget", teleportTimer.OriginPlayer.displayName);
1277 teleportTimer.Timer.Destroy();
1278 TeleportTimers.Remove(player.userID);
1279 });
1280 }
1281
1282 void OnPlayerSleepEnded(BasePlayer player)
1283 {
1284 if (teleporting.Contains(player.userID))
1285 timer.Once(3, () => { teleporting.Remove(player.userID); });
1286 }
1287
1288 void OnPlayerDisconnected(BasePlayer player)
1289 {
1290 Timer reqTimer;
1291 if (PendingRequests.TryGetValue(player.userID, out reqTimer))
1292 {
1293 var originPlayer = PlayersRequests[player.userID];
1294 PrintMsgL(originPlayer, "RequestTargetOff");
1295 reqTimer.Destroy();
1296 PendingRequests.Remove(player.userID);
1297 PlayersRequests.Remove(player.userID);
1298 PlayersRequests.Remove(originPlayer.userID);
1299 }
1300 TeleportTimer teleportTimer;
1301 if (TeleportTimers.TryGetValue(player.userID, out teleportTimer))
1302 {
1303 teleportTimer.Timer.Destroy();
1304 TeleportTimers.Remove(player.userID);
1305 }
1306 teleporting.Remove(player.userID);
1307 }
1308
1309 private void SaveTeleportsAdmin()
1310 {
1311 if (Admin == null || !changedAdmin) return;
1312 dataAdmin.WriteObject(Admin);
1313 changedAdmin = false;
1314 }
1315
1316 private void SaveTeleportsHome()
1317 {
1318 if (Home == null || !changedHome) return;
1319 dataHome.WriteObject(Home);
1320 changedHome = false;
1321 }
1322
1323 private void SaveTeleportsTPR()
1324 {
1325 if (TPR == null || !changedTPR) return;
1326 dataTPR.WriteObject(TPR);
1327 changedTPR = false;
1328 }
1329
1330 private void SaveTeleportsTown()
1331 {
1332 if (Town == null || !changedTown) return;
1333 dataTown.WriteObject(Town);
1334 changedTown = false;
1335 }
1336
1337 private void SaveLocation(BasePlayer player)
1338 {
1339 if (!IsAllowed(player, PermTpB)) return;
1340 AdminData adminData;
1341 if (!Admin.TryGetValue(player.userID, out adminData))
1342 Admin[player.userID] = adminData = new AdminData();
1343 adminData.PreviousLocation = player.transform.position;
1344 changedAdmin = true;
1345 PrintMsgL(player, "AdminTPBackSave");
1346 }
1347
1348 string RandomString()
1349 {
1350 const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1351 List<char> charList = chars.ToList();
1352
1353 string random = "";
1354
1355 for (int i = 0; i <= UnityEngine.Random.Range(5, 10); i++)
1356 random = random + charList[UnityEngine.Random.Range(0, charList.Count - 1)];
1357
1358 return random;
1359 }
1360 // Modified from MonumentFinder.cs by PsychoTea
1361 void FindMonuments()
1362 {
1363 foreach (MonumentInfo monument in UnityEngine.Object.FindObjectsOfType<MonumentInfo>())
1364 {
1365 if(monument.name.Contains("power_sub")) continue;
1366 string name = null;
1367 if(monument.name == "OilrigAI")
1368 {
1369 name = "Small Oilrig";
1370 }
1371 else if(monument.name == "OilrigAI2")
1372 {
1373 name = "Large Oilrig";
1374 }
1375 else
1376 {
1377 name = Regex.Match(monument.name, @"\w{6}\/(.+\/)(.+)\.(.+)").Groups[2].Value.Replace("_", " ").Replace(" 1", "").Titleize();
1378 }
1379 if(monPos.ContainsKey(name)) continue;
1380 if(cavePos.ContainsKey(name)) name = name + RandomString();
1381#if DEBUG
1382 Puts($"Found {name}");
1383#endif
1384 var width = monument.Bounds.extents;
1385 if(monument.name.Contains("cave"))
1386 {
1387#if DEBUG
1388 Puts(" Adding to cave list");
1389#endif
1390 cavePos.Add(name, monument.transform.position);
1391 }
1392 else
1393 {
1394 if(width.z < 1)
1395 {
1396 width.z = configData.Settings.DefaultMonumentSize;
1397 }
1398 monPos.Add(name, monument.transform.position);
1399 monSize.Add(name, width);
1400 }
1401
1402 }
1403 monPos.OrderBy(x => x.Key);
1404 monSize.OrderBy(x => x.Key);
1405 cavePos.OrderBy(x => x.Key);
1406 }
1407
1408 [ChatCommand("tp")]
1409 private void cmdChatTeleport(BasePlayer player, string command, string[] args)
1410 {
1411 if (!IsAllowedMsg(player, PermTp)) return;
1412 BasePlayer target;
1413 float x, y, z;
1414 switch (args.Length)
1415 {
1416 case 1:
1417 target = FindPlayersSingle(args[0], player);
1418 if (target == null) return;
1419 if (target == player)
1420 {
1421#if DEBUG
1422 Puts("Debug mode - allowing self teleport.");
1423#else
1424 PrintMsgL(player, "CantTeleportToSelf");
1425 return;
1426#endif
1427 }
1428 player.SetParent(null, true, true);
1429// if(player.isMounted)
1430// player.DismountObject();
1431 TeleportToPlayer(player, target);
1432 PrintMsgL(player, "AdminTP", target.displayName);
1433 Puts(_("LogTeleport", null, player.displayName, target.displayName));
1434 if (configData.Admin.AnnounceTeleportToTarget)
1435 PrintMsgL(target, "AdminTPTarget", player.displayName);
1436 break;
1437 case 2:
1438 var origin = FindPlayersSingle(args[0], player);
1439 if (origin == null) return;
1440 target = FindPlayersSingle(args[1], player);
1441 if (target == null) return;
1442 if (target == origin)
1443 {
1444 PrintMsgL(player, "CantTeleportPlayerToSelf");
1445 return;
1446 }
1447 origin.SetParent(null, true, true);
1448 TeleportToPlayer(origin, target);
1449 PrintMsgL(player, "AdminTPPlayers", origin.displayName, target.displayName);
1450 PrintMsgL(origin, "AdminTPPlayer", player.displayName, target.displayName);
1451 if (configData.Admin.AnnounceTeleportToTarget)
1452 PrintMsgL(target, "AdminTPPlayerTarget", player.displayName, origin.displayName);
1453 Puts(_("LogTeleportPlayer", null, player.displayName, origin.displayName, target.displayName));
1454 break;
1455 case 3:
1456 if (!float.TryParse(args[0], out x) || !float.TryParse(args[1], out y) || !float.TryParse(args[2], out z))
1457 {
1458 PrintMsgL(player, "InvalidCoordinates");
1459 return;
1460 }
1461 if (!CheckBoundaries(x, y, z))
1462 {
1463 PrintMsgL(player, "AdminTPOutOfBounds");
1464 PrintMsgL(player, "AdminTPBoundaries", boundary);
1465 return;
1466 }
1467 player.SetParent(null, true, true);
1468 TeleportToPosition(player, x, y, z);
1469 PrintMsgL(player, "AdminTPCoordinates", player.transform.position);
1470 Puts(_("LogTeleport", null, player.displayName, player.transform.position));
1471 break;
1472 case 4:
1473 target = FindPlayersSingle(args[0], player);
1474 if (target == null) return;
1475 if (!float.TryParse(args[0], out x) || !float.TryParse(args[1], out y) || !float.TryParse(args[2], out z))
1476 {
1477 PrintMsgL(player, "InvalidCoordinates");
1478 return;
1479 }
1480 if (!CheckBoundaries(x, y, z))
1481 {
1482 PrintMsgL(player, "AdminTPOutOfBounds");
1483 PrintMsgL(player, "AdminTPBoundaries", boundary);
1484 return;
1485 }
1486 player.SetParent(null, true, true);
1487 TeleportToPosition(target, x, y, z);
1488 if (player == target)
1489 {
1490 PrintMsgL(player, "AdminTPCoordinates", player.transform.position);
1491 Puts(_("LogTeleport", null, player.displayName, player.transform.position));
1492 }
1493 else
1494 {
1495 PrintMsgL(player, "AdminTPTargetCoordinates", target.displayName, player.transform.position);
1496 if (configData.Admin.AnnounceTeleportToTarget)
1497 PrintMsgL(target, "AdminTPTargetCoordinatesTarget", player.displayName, player.transform.position);
1498 Puts(_("LogTeleportPlayer", null, player.displayName, target.displayName, player.transform.position));
1499 }
1500 break;
1501 default:
1502 PrintMsgL(player, "SyntaxCommandTP");
1503 break;
1504 }
1505 }
1506
1507 [ChatCommand("tpn")]
1508 private void cmdChatTeleportNear(BasePlayer player, string command, string[] args)
1509 {
1510 if (!IsAllowedMsg(player, PermTpN)) return;
1511 switch (args.Length)
1512 {
1513 case 1:
1514 case 2:
1515 var target = FindPlayersSingle(args[0], player);
1516 if (target == null) return;
1517 if (target == player)
1518 {
1519#if DEBUG
1520 Puts("Debug mode - allowing self teleport.");
1521#else
1522 PrintMsgL(player, "CantTeleportToSelf");
1523 return;
1524#endif
1525 }
1526 int distance;
1527 if (args.Length != 2 || !int.TryParse(args[1], out distance))
1528 distance = configData.Admin.TeleportNearDefaultDistance;
1529 float x = UnityEngine.Random.Range(-distance, distance);
1530 var z = (float)System.Math.Sqrt(System.Math.Pow(distance, 2) - System.Math.Pow(x, 2));
1531 var destination = target.transform.position;
1532 destination.x = destination.x - x;
1533 destination.z = destination.z - z;
1534 Teleport(player, GetGroundBuilding(destination));
1535 PrintMsgL(player, "AdminTP", target.displayName);
1536 Puts(_("LogTeleport", null, player.displayName, target.displayName));
1537 if (configData.Admin.AnnounceTeleportToTarget)
1538 PrintMsgL(target, "AdminTPTarget", player.displayName);
1539 break;
1540 default:
1541 PrintMsgL(player, "SyntaxCommandTPN");
1542 break;
1543 }
1544 }
1545
1546 [ChatCommand("tpl")]
1547 private void cmdChatTeleportLocation(BasePlayer player, string command, string[] args)
1548 {
1549 if (!IsAllowedMsg(player, PermTpL)) return;
1550 AdminData adminData;
1551 if (!Admin.TryGetValue(player.userID, out adminData) || adminData.Locations.Count <= 0)
1552 {
1553 PrintMsgL(player, "AdminLocationListEmpty");
1554 return;
1555 }
1556 switch (args.Length)
1557 {
1558 case 0:
1559 PrintMsgL(player, "AdminLocationList");
1560 foreach (var location in adminData.Locations)
1561 PrintMsgL(player, $"{location.Key} {location.Value}");
1562 break;
1563 case 1:
1564 Vector3 loc;
1565 if (!adminData.Locations.TryGetValue(args[0], out loc))
1566 {
1567 PrintMsgL(player, "LocationNotFound");
1568 return;
1569 }
1570 Teleport(player, loc);
1571 PrintMsgL(player, "AdminTPLocation", args[0]);
1572 break;
1573 default:
1574 PrintMsgL(player, "SyntaxCommandTPL");
1575 break;
1576 }
1577 }
1578
1579 [ChatCommand("tpsave")]
1580 private void cmdChatSaveTeleportLocation(BasePlayer player, string command, string[] args)
1581 {
1582 if (!IsAllowedMsg(player, PermTpSave)) return;
1583 if (args.Length != 1)
1584 {
1585 PrintMsgL(player, "SyntaxCommandTPSave");
1586 return;
1587 }
1588 AdminData adminData;
1589 if (!Admin.TryGetValue(player.userID, out adminData))
1590 Admin[player.userID] = adminData = new AdminData();
1591 Vector3 location;
1592 if (adminData.Locations.TryGetValue(args[0], out location))
1593 {
1594 PrintMsgL(player, "LocationExists", location);
1595 return;
1596 }
1597 var positionCoordinates = player.transform.position;
1598 foreach (var loc in adminData.Locations)
1599 {
1600 if (Vector3.Distance(positionCoordinates, loc.Value) < configData.Admin.LocationRadius)
1601 {
1602 PrintMsgL(player, "LocationExistsNearby", loc.Key);
1603 return;
1604 }
1605 }
1606 adminData.Locations[args[0]] = positionCoordinates;
1607 PrintMsgL(player, "AdminTPLocationSave");
1608 changedAdmin = true;
1609 }
1610
1611 [ChatCommand("tpremove")]
1612 private void cmdChatRemoveTeleportLocation(BasePlayer player, string command, string[] args)
1613 {
1614 if (!IsAllowedMsg(player, PermTpRemove)) return;
1615 if (args.Length != 1)
1616 {
1617 PrintMsgL(player, "SyntaxCommandTPRemove");
1618 return;
1619 }
1620 AdminData adminData;
1621 if (!Admin.TryGetValue(player.userID, out adminData) || adminData.Locations.Count <= 0)
1622 {
1623 PrintMsgL(player, "AdminLocationListEmpty");
1624 return;
1625 }
1626 if (adminData.Locations.Remove(args[0]))
1627 {
1628 PrintMsgL(player, "AdminTPLocationRemove", args[0]);
1629 changedAdmin = true;
1630 return;
1631 }
1632 PrintMsgL(player, "LocationNotFound");
1633 }
1634
1635 [ChatCommand("tpb")]
1636 private void cmdChatTeleportBack(BasePlayer player, string command, string[] args)
1637 {
1638 if (!IsAllowedMsg(player, PermTpB)) return;
1639 if (args.Length != 0)
1640 {
1641 PrintMsgL(player, "SyntaxCommandTPB");
1642 return;
1643 }
1644 AdminData adminData;
1645 if (!Admin.TryGetValue(player.userID, out adminData) || adminData.PreviousLocation == default(Vector3))
1646 {
1647 PrintMsgL(player, "NoPreviousLocationSaved");
1648 return;
1649 }
1650
1651 Teleport(player, adminData.PreviousLocation);
1652 adminData.PreviousLocation = default(Vector3);
1653 changedAdmin = true;
1654 PrintMsgL(player, "AdminTPBack");
1655 Puts(_("LogTeleportBack", null, player.displayName));
1656 }
1657
1658 [ChatCommand("sethome")]
1659 private void cmdChatSetHome(BasePlayer player, string command, string[] args)
1660 {
1661 if (!IsAllowed(player, PermHome)) return;
1662 if (!configData.Settings.HomesEnabled) return;
1663 if (args.Length != 1)
1664 {
1665 PrintMsgL(player, "SyntaxCommandSetHome");
1666 return;
1667 }
1668 var err = CheckPlayer(player, false, CanCraftHome(player), true);
1669 if (err != null)
1670 {
1671 PrintMsgL(player, $"Home{err}");
1672 return;
1673 }
1674 if (!player.CanBuild())
1675 {
1676 PrintMsgL(player, "HomeTPBuildingBlocked");
1677 return;
1678 }
1679 if (!args[0].All(char.IsLetterOrDigit))
1680 {
1681 PrintMsgL(player, "InvalidCharacter");
1682 return;
1683 }
1684 HomeData homeData;
1685 if (!Home.TryGetValue(player.userID, out homeData))
1686 Home[player.userID] = homeData = new HomeData();
1687 var limit = GetHigher(player, configData.Home.VIPHomesLimits, configData.Home.HomesLimit);
1688 if (homeData.Locations.Count >= limit)
1689 {
1690 PrintMsgL(player, "HomeMaxLocations", limit);
1691 return;
1692 }
1693 Vector3 location;
1694 if (homeData.Locations.TryGetValue(args[0], out location))
1695 {
1696 PrintMsgL(player, "HomeExists", location);
1697 return;
1698 }
1699 var positionCoordinates = player.transform.position;
1700 foreach (var loc in homeData.Locations)
1701 {
1702 if (Vector3.Distance(positionCoordinates, loc.Value) < configData.Home.LocationRadius)
1703 {
1704 PrintMsgL(player, "HomeExistsNearby", loc.Key);
1705 return;
1706 }
1707 }
1708 err = CanPlayerTeleport(player);
1709 if (err != null)
1710 {
1711 SendReply(player, err);
1712 return;
1713 }
1714
1715 if (player.IsAdmin)
1716 player.SendConsoleCommand("ddraw.sphere", 60f, Color.blue, GetGround(positionCoordinates), 2.5f);
1717
1718 err = CheckFoundation(player.userID, positionCoordinates);
1719 if (err != null)
1720 {
1721 PrintMsgL(player, err);
1722 return;
1723 }
1724 err = CheckInsideBlock(positionCoordinates);
1725 if (err != null)
1726 {
1727 PrintMsgL(player, err);
1728 return;
1729 }
1730 homeData.Locations[args[0]] = positionCoordinates;
1731 changedHome = true;
1732 PrintMsgL(player, "HomeSave");
1733 PrintMsgL(player, "HomeQuota", homeData.Locations.Count, limit);
1734 }
1735
1736 [ChatCommand("removehome")]
1737 private void cmdChatRemoveHome(BasePlayer player, string command, string[] args)
1738 {
1739 if (!IsAllowed(player, PermHome)) return;
1740 if (!configData.Settings.HomesEnabled) return;
1741 if (args.Length != 1)
1742 {
1743 PrintMsgL(player, "SyntaxCommandRemoveHome");
1744 return;
1745 }
1746 HomeData homeData;
1747 if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
1748 {
1749 PrintMsgL(player, "HomeListEmpty");
1750 return;
1751 }
1752 if (homeData.Locations.Remove(args[0]))
1753 {
1754 changedHome = true;
1755 PrintMsgL(player, "HomeRemove", args[0]);
1756 }
1757 else
1758 PrintMsgL(player, "HomeNotFound");
1759 }
1760
1761 [ChatCommand("home")]
1762 private void cmdChatHome(BasePlayer player, string command, string[] args)
1763 {
1764 if (!IsAllowed(player, PermHome)) return;
1765 if (!configData.Settings.HomesEnabled) return;
1766 if (args.Length == 0)
1767 {
1768 PrintMsgL(player, "SyntaxCommandHome");
1769 if (IsAllowed(player)) PrintMsgL(player, "SyntaxCommandHomeAdmin");
1770 return;
1771 }
1772 switch (args[0].ToLower())
1773 {
1774 case "add":
1775 cmdChatSetHome(player, command, args.Skip(1).ToArray());
1776 break;
1777 case "list":
1778 cmdChatListHome(player, command, args.Skip(1).ToArray());
1779 break;
1780 case "remove":
1781 cmdChatRemoveHome(player, command, args.Skip(1).ToArray());
1782 break;
1783 case "radius":
1784 cmdChatHomeRadius(player, command, args.Skip(1).ToArray());
1785 break;
1786 case "delete":
1787 cmdChatHomeDelete(player, command, args.Skip(1).ToArray());
1788 break;
1789 case "tp":
1790 cmdChatHomeAdminTP(player, command, args.Skip(1).ToArray());
1791 break;
1792 case "homes":
1793 cmdChatHomeHomes(player, command, args.Skip(1).ToArray());
1794 break;
1795 case "wipe":
1796 cmdChatWipeHomes(player, command, args.Skip(1).ToArray());
1797 break;
1798 default:
1799 cmdChatHomeTP(player, command, args);
1800 break;
1801 }
1802 }
1803
1804 [ChatCommand("radiushome")]
1805 private void cmdChatHomeRadius(BasePlayer player, string command, string[] args)
1806 {
1807 if (!IsAllowedMsg(player, PermRadiusHome)) return;
1808 float radius;
1809 if (args.Length != 1 || !float.TryParse(args[0], out radius)) radius = 10;
1810 var found = false;
1811 foreach (var homeData in Home)
1812 {
1813 var toRemove = new List<string>();
1814 var target = RustCore.FindPlayerById(homeData.Key)?.displayName ?? homeData.Key.ToString();
1815 foreach (var location in homeData.Value.Locations)
1816 {
1817 if (Vector3.Distance(player.transform.position, location.Value) <= radius)
1818 {
1819 if (CheckFoundation(homeData.Key, location.Value) != null)
1820 {
1821 toRemove.Add(location.Key);
1822 continue;
1823 }
1824 var entity = GetFoundationOwned(location.Value, homeData.Key);
1825 if (entity == null) continue;
1826 player.SendConsoleCommand("ddraw.text", 30f, Color.blue, entity.CenterPoint() + new Vector3(0, .5f), $"<size=20>{target} - {location.Key} {location.Value}</size>");
1827 DrawBox(player, entity.CenterPoint(), entity.transform.rotation, entity.bounds.size);
1828 PrintMsg(player, $"{target} - {location.Key} {location.Value}");
1829 found = true;
1830 }
1831 }
1832 foreach (var loc in toRemove)
1833 {
1834 homeData.Value.Locations.Remove(loc);
1835 changedHome = true;
1836 }
1837 }
1838 if (!found)
1839 PrintMsgL(player, "HomeNoFound");
1840 }
1841
1842 [ChatCommand("deletehome")]
1843 private void cmdChatHomeDelete(BasePlayer player, string command, string[] args)
1844 {
1845 if (!IsAllowedMsg(player, PermDeleteHome)) return;
1846 if (args.Length != 2)
1847 {
1848 PrintMsgL(player, "SyntaxCommandHomeDelete");
1849 return;
1850 }
1851 var userId = FindPlayersSingleId(args[0], player);
1852 if (userId <= 0) return;
1853 HomeData targetHome;
1854 if (!Home.TryGetValue(userId, out targetHome) || !targetHome.Locations.Remove(args[1]))
1855 {
1856 PrintMsgL(player, "HomeNotFound");
1857 return;
1858 }
1859 changedHome = true;
1860 PrintMsgL(player, "HomeDelete", args[0], args[1]);
1861 }
1862
1863 [ChatCommand("tphome")]
1864 private void cmdChatHomeAdminTP(BasePlayer player, string command, string[] args)
1865 {
1866 if (!IsAllowedMsg(player, PermTpHome)) return;
1867 if (args.Length != 2)
1868 {
1869 PrintMsgL(player, "SyntaxCommandHomeAdminTP");
1870 return;
1871 }
1872 var userId = FindPlayersSingleId(args[0], player);
1873 if (userId <= 0) return;
1874 HomeData targetHome;
1875 Vector3 location;
1876 if (!Home.TryGetValue(userId, out targetHome) || !targetHome.Locations.TryGetValue(args[1], out location))
1877 {
1878 PrintMsgL(player, "HomeNotFound");
1879 return;
1880 }
1881 Teleport(player, location);
1882 PrintMsgL(player, "HomeAdminTP", args[0], args[1]);
1883 }
1884
1885 // Check that plugins are available and enabled for CheckEconomy()
1886 private bool UseEconomy()
1887 {
1888 if((configData.Settings.UseEconomics && Economics) ||
1889 (configData.Settings.UseServerRewards && ServerRewards))
1890 {
1891 return true;
1892 }
1893 return false;
1894 }
1895
1896 // Check balance on multiple plugins and optionally withdraw money from the player
1897 private bool CheckEconomy(BasePlayer player, double bypass, bool withdraw = false, bool deposit = false)
1898 {
1899 double balance = 0;
1900 bool foundmoney = false;
1901
1902 // Check Economics first. If not in use or balance low, check ServerRewards below
1903 if(configData.Settings.UseEconomics && Economics)
1904 {
1905 balance = (double)Economics?.CallHook("Balance", player.UserIDString);
1906 if(balance >= bypass)
1907 {
1908 foundmoney = true;
1909 if(withdraw == true)
1910 {
1911 var w = (bool)Economics?.CallHook("Withdraw", player.userID, bypass);
1912 return w;
1913 }
1914 else if(deposit == true)
1915 {
1916 var w = (bool)Economics?.CallHook("Deposit", player.userID, bypass);
1917 }
1918 }
1919 }
1920
1921 // No money via Economics, or plugin not in use. Try ServerRewards.
1922 if(configData.Settings.UseServerRewards && ServerRewards)
1923 {
1924 object bal = ServerRewards?.Call("CheckPoints", player.userID);
1925 balance = Convert.ToDouble(bal);
1926 if(balance >= bypass && foundmoney == false)
1927 {
1928 foundmoney = true;
1929 if(withdraw == true)
1930 {
1931 var w = (bool)ServerRewards?.Call("TakePoints", player.userID, (int)bypass);
1932 return w;
1933 }
1934 else if(deposit == true)
1935 {
1936 var w = (bool)ServerRewards?.Call("AddPoints", player.userID, (int)bypass);
1937 }
1938 }
1939 }
1940
1941 // Just checking balance without withdrawal - did we find anything?
1942 if(foundmoney == true)
1943 {
1944 return true;
1945 }
1946 return false;
1947 }
1948
1949 private void cmdChatHomeTP(BasePlayer player, string command, string[] args)
1950 {
1951 if (!IsAllowed(player, PermHome)) return;
1952 bool paidmoney = false;
1953 if (!configData.Settings.HomesEnabled) return;
1954 if (args.Length < 1)
1955 {
1956 PrintMsgL(player, "SyntaxCommandHome");
1957 return;
1958 }
1959 var err = CheckPlayer(player, configData.Home.UsableOutOfBuildingBlocked, CanCraftHome(player), true);
1960 if (err != null)
1961 {
1962 PrintMsgL(player, err);
1963 return;
1964 }
1965 HomeData homeData;
1966 if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
1967 {
1968 PrintMsgL(player, "HomeListEmpty");
1969 return;
1970 }
1971 Vector3 location;
1972 if (!homeData.Locations.TryGetValue(args[0], out location))
1973 {
1974 PrintMsgL(player, "HomeNotFound");
1975 return;
1976 }
1977 err = CheckFoundation(player.userID, location) ?? CheckTargetLocation(player, location, configData.Home.UsableIntoBuildingBlocked, configData.Home.CupOwnerAllowOnBuildingBlocked);
1978 if (err != null)
1979 {
1980 PrintMsgL(player, "HomeRemovedInvalid", args[0]);
1981 homeData.Locations.Remove(args[0]);
1982 changedHome = true;
1983 return;
1984 }
1985 err = CheckInsideBlock(location);
1986 if (err != null)
1987 {
1988 PrintMsgL(player, "HomeRemovedInsideBlock", args[0]);
1989 homeData.Locations.Remove(args[0]);
1990 changedHome = true;
1991 return;
1992 }
1993 var timestamp = Facepunch.Math.Epoch.Current;
1994 var currentDate = DateTime.Now.ToString("d");
1995 if (homeData.Teleports.Date != currentDate)
1996 {
1997 homeData.Teleports.Amount = 0;
1998 homeData.Teleports.Date = currentDate;
1999 }
2000 var cooldown = GetLower(player, configData.Home.VIPCooldowns, configData.Home.Cooldown);
2001
2002 if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
2003 {
2004 var cmdSent = "";
2005 bool foundmoney = CheckEconomy(player, configData.Home.Bypass);
2006 try
2007 {
2008 cmdSent = args[1].ToLower();
2009 }
2010 catch {}
2011
2012 bool payalso = false;
2013 if(configData.Home.Pay > 0)
2014 {
2015 payalso = true;
2016 }
2017 if((configData.Settings.BypassCMD != null) && (cmdSent == configData.Settings.BypassCMD.ToLower()))
2018 {
2019 if(foundmoney == true)
2020 {
2021 CheckEconomy(player, configData.Home.Bypass, true);
2022 paidmoney = true;
2023 PrintMsgL(player, "HomeTPCooldownBypass", configData.Home.Bypass);
2024 if(payalso)
2025 {
2026 PrintMsgL(player, "PayToHome", configData.Home.Pay);
2027 }
2028 }
2029 else
2030 {
2031 PrintMsgL(player, "HomeTPCooldownBypassF", configData.Home.Bypass);
2032 return;
2033 }
2034 }
2035 else if(UseEconomy())
2036 {
2037 var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
2038 PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
2039 if(configData.Home.Bypass > 0 && configData.Settings.BypassCMD != null)
2040 {
2041 PrintMsgL(player, "HomeTPCooldownBypassP", configData.Home.Bypass);
2042 PrintMsgL(player, "HomeTPCooldownBypassP2", configData.Settings.BypassCMD);
2043 }
2044 return;
2045 }
2046 else
2047 {
2048 var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
2049 PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
2050 return;
2051 }
2052 }
2053 var limit = GetHigher(player, configData.Home.VIPDailyLimits, configData.Home.DailyLimit);
2054 if (limit > 0 && homeData.Teleports.Amount >= limit)
2055 {
2056 PrintMsgL(player, "HomeTPLimitReached", limit);
2057 return;
2058 }
2059 if (TeleportTimers.ContainsKey(player.userID))
2060 {
2061 PrintMsgL(player, "TeleportPending");
2062 return;
2063 }
2064 err = CanPlayerTeleport(player);
2065 if (err != null)
2066 {
2067 SendReply(player, err);
2068 return;
2069 }
2070 err = CheckItems(player);
2071 if (err != null)
2072 {
2073 PrintMsgL(player, "TPBlockedItem", err);
2074 return;
2075 }
2076
2077 var countdown = GetLower(player, configData.Home.VIPCountdowns, configData.Home.Countdown);
2078 TeleportTimers[player.userID] = new TeleportTimer
2079 {
2080 OriginPlayer = player,
2081 Timer = timer.Once(countdown, () =>
2082 {
2083 err = CheckPlayer(player, configData.Home.UsableOutOfBuildingBlocked, CanCraftHome(player), true);
2084 if (err != null)
2085 {
2086 PrintMsgL(player, "Interrupted");
2087 PrintMsgL(player, err);
2088 if(paidmoney == true)
2089 {
2090 paidmoney = false;
2091 CheckEconomy(player, configData.Home.Bypass, false, true);
2092 }
2093 TeleportTimers.Remove(player.userID);
2094 return;
2095 }
2096 err = CanPlayerTeleport(player);
2097 if (err != null)
2098 {
2099 PrintMsgL(player, "Interrupted");
2100 PrintMsgL(player, err);
2101 if(paidmoney == true)
2102 {
2103 paidmoney = false;
2104 CheckEconomy(player, configData.Home.Bypass, false, true);
2105 }
2106 TeleportTimers.Remove(player.userID);
2107 return;
2108 }
2109 err = CheckItems(player);
2110 if (err != null)
2111 {
2112 PrintMsgL(player, "Interrupted");
2113 PrintMsgL(player, "TPBlockedItem", err);
2114 if(paidmoney == true)
2115 {
2116 paidmoney = false;
2117 CheckEconomy(player, configData.Home.Bypass, false, true);
2118 }
2119 TeleportTimers.Remove(player.userID);
2120 return;
2121 }
2122 err = CheckFoundation(player.userID, location) ?? CheckTargetLocation(player, location, configData.Home.UsableIntoBuildingBlocked, configData.Home.CupOwnerAllowOnBuildingBlocked);
2123 if (err != null)
2124 {
2125 PrintMsgL(player, "HomeRemovedInvalid", args[0]);
2126 homeData.Locations.Remove(args[0]);
2127 changedHome = true;
2128 if(paidmoney == true)
2129 {
2130 paidmoney = false;
2131 CheckEconomy(player, configData.Home.Bypass, false, true);
2132 }
2133 return;
2134 }
2135 err = CheckInsideBlock(location);
2136 if (err != null)
2137 {
2138 PrintMsgL(player, "HomeRemovedInsideBlock", args[0]);
2139 homeData.Locations.Remove(args[0]);
2140 changedHome = true;
2141 if(paidmoney == true)
2142 {
2143 paidmoney = false;
2144 CheckEconomy(player, configData.Home.Bypass, false, true);
2145 }
2146 return;
2147 }
2148 if(UseEconomy())
2149 {
2150 if (configData.Home.Pay > 0 && !CheckEconomy(player, configData.Home.Pay))
2151 {
2152 PrintMsgL(player, "Interrupted");
2153 PrintMsgL(player, "TPNoMoney", configData.Home.Pay);
2154
2155 TeleportTimers.Remove(player.userID);
2156 return;
2157 }
2158 else if(configData.Home.Pay > 0)
2159 {
2160 var w = CheckEconomy(player, (double)configData.Home.Pay, true);
2161 PrintMsgL(player, "TPMoney", (double)configData.Home.Pay);
2162 }
2163 }
2164 player.SetParent(null, true, true);
2165 Teleport(player, location);
2166 homeData.Teleports.Amount++;
2167 homeData.Teleports.Timestamp = timestamp;
2168 changedHome = true;
2169 PrintMsgL(player, "HomeTP", args[0]);
2170 if (limit > 0) PrintMsgL(player, "HomeTPAmount", limit - homeData.Teleports.Amount);
2171 TeleportTimers.Remove(player.userID);
2172 })
2173 };
2174 PrintMsgL(player, "HomeTPStarted", args[0], countdown);
2175 }
2176
2177 [ChatCommand("listhomes")]
2178 private void cmdChatListHome(BasePlayer player, string command, string[] args)
2179 {
2180 if (!configData.Settings.HomesEnabled) return;
2181 if (args.Length != 0)
2182 {
2183 PrintMsgL(player, "SyntaxCommandListHomes");
2184 return;
2185 }
2186 HomeData homeData;
2187 if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
2188 {
2189 PrintMsgL(player, "HomeListEmpty");
2190 return;
2191 }
2192 PrintMsgL(player, "HomeList");
2193 if (configData.Home.CheckValidOnList)
2194 {
2195 var toRemove = new List<string>();
2196 foreach (var location in homeData.Locations)
2197 {
2198 var err = CheckFoundation(player.userID, location.Value);
2199 if (err != null)
2200 {
2201 toRemove.Add(location.Key);
2202 continue;
2203 }
2204 PrintMsgL(player, $"{location.Key} {location.Value}");
2205 }
2206 foreach (var loc in toRemove)
2207 {
2208 PrintMsgL(player, "HomeRemovedInvalid", loc);
2209 homeData.Locations.Remove(loc);
2210 changedHome = true;
2211 }
2212 return;
2213 }
2214 foreach (var location in homeData.Locations)
2215 PrintMsgL(player, $"{location.Key} {location.Value}");
2216 }
2217
2218 [ChatCommand("homehomes")]
2219 private void cmdChatHomeHomes(BasePlayer player, string command, string[] args)
2220 {
2221 if (!IsAllowedMsg(player, PermHomeHomes)) return;
2222 if (args.Length != 1)
2223 {
2224 PrintMsgL(player, "SyntaxCommandHomeHomes");
2225 return;
2226 }
2227 var userId = FindPlayersSingleId(args[0], player);
2228 if (userId <= 0) return;
2229 HomeData homeData;
2230 if (!Home.TryGetValue(userId, out homeData) || homeData.Locations.Count <= 0)
2231 {
2232 PrintMsgL(player, "HomeListEmpty");
2233 return;
2234 }
2235 PrintMsgL(player, "HomeList");
2236 var toRemove = new List<string>();
2237 foreach (var location in homeData.Locations)
2238 {
2239 var err = CheckFoundation(userId, location.Value);
2240 if (err != null)
2241 {
2242 toRemove.Add(location.Key);
2243 continue;
2244 }
2245 PrintMsgL(player, $"{location.Key} {location.Value}");
2246 }
2247 foreach (var loc in toRemove)
2248 {
2249 PrintMsgL(player, "HomeRemovedInvalid", loc);
2250 homeData.Locations.Remove(loc);
2251 changedHome = true;
2252 }
2253 }
2254
2255 [ChatCommand("tpr")]
2256 private void cmdChatTeleportRequest(BasePlayer player, string command, string[] args)
2257 {
2258 if (!IsAllowedMsg(player, PermTpR)) return;
2259 if (!configData.Settings.TPREnabled) return;
2260 //if (args.Length != 1)
2261 if (args.Length == 0)
2262 {
2263 PrintMsgL(player, "SyntaxCommandTPR");
2264 return;
2265 }
2266 var targets = FindPlayersOnline(args[0]);
2267 if (targets.Count <= 0)
2268 {
2269 PrintMsgL(player, "PlayerNotFound");
2270 return;
2271 }
2272 if (targets.Count > 1)
2273 {
2274 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.ConvertAll(p => p.displayName).ToArray()));
2275 return;
2276 }
2277 var target = targets[0];
2278 if (target == player)
2279 {
2280#if DEBUG
2281 Puts("Debug mode - allowing self teleport.");
2282#else
2283 PrintMsgL(player, "CantTeleportToSelf");
2284 return;
2285#endif
2286 }
2287 var err = CheckPlayer(player, configData.TPR.UsableOutOfBuildingBlocked, CanCraftTPR(player), true);
2288 if (err != null)
2289 {
2290 PrintMsgL(player, err);
2291 return;
2292 }
2293 err = CheckTargetLocation(target, target.transform.position, configData.TPR.UsableIntoBuildingBlocked, configData.TPR.CupOwnerAllowOnBuildingBlocked);
2294 if (err != null)
2295 {
2296 PrintMsgL(player, err);
2297 return;
2298 }
2299 var timestamp = Facepunch.Math.Epoch.Current;
2300 var currentDate = DateTime.Now.ToString("d");
2301 TeleportData tprData;
2302 if (!TPR.TryGetValue(player.userID, out tprData))
2303 TPR[player.userID] = tprData = new TeleportData();
2304 if (tprData.Date != currentDate)
2305 {
2306 tprData.Amount = 0;
2307 tprData.Date = currentDate;
2308 }
2309
2310 var cooldown = GetLower(player, configData.TPR.VIPCooldowns, configData.TPR.Cooldown);
2311 if (cooldown > 0 && timestamp - tprData.Timestamp < cooldown)
2312 {
2313 var cmdSent = "";
2314 bool foundmoney = CheckEconomy(player, configData.TPR.Bypass);
2315 try
2316 {
2317 cmdSent = args[1].ToLower();
2318 }
2319 catch {}
2320
2321 bool payalso = false;
2322 if(configData.TPR.Pay > 0)
2323 {
2324 payalso = true;
2325 }
2326 if((configData.Settings.BypassCMD != null) && (cmdSent == configData.Settings.BypassCMD.ToLower()))
2327 {
2328 if(foundmoney == true)
2329 {
2330 CheckEconomy(player, configData.TPR.Bypass, true);
2331 PrintMsgL(player, "TPRCooldownBypass", configData.TPR.Bypass);
2332 if(payalso)
2333 {
2334 PrintMsgL(player, "PayToTPR", configData.TPR.Pay);
2335 }
2336 }
2337 else
2338 {
2339 PrintMsgL(player, "TPRCooldownBypassF", configData.TPR.Bypass);
2340 return;
2341 }
2342 }
2343 else if(UseEconomy())
2344 {
2345 var remain = cooldown - (timestamp - tprData.Timestamp);
2346 PrintMsgL(player, "TPRCooldown", FormatTime(remain));
2347 if(configData.TPR.Bypass > 0 && configData.Settings.BypassCMD != null)
2348 {
2349 PrintMsgL(player, "TPRCooldownBypassP", configData.TPR.Bypass);
2350 if(payalso)
2351 {
2352 PrintMsgL(player, "PayToTPR", configData.TPR.Pay);
2353 }
2354 PrintMsgL(player, "TPRCooldownBypassP2a", configData.Settings.BypassCMD);
2355 }
2356 return;
2357 }
2358 else
2359 {
2360 var remain = cooldown - (timestamp - tprData.Timestamp);
2361 PrintMsgL(player, "TPRCooldown", FormatTime(remain));
2362 return;
2363 }
2364 }
2365 var limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
2366 if (limit > 0 && tprData.Amount >= limit)
2367 {
2368 PrintMsgL(player, "TPRLimitReached", limit);
2369 return;
2370 }
2371 if (TeleportTimers.ContainsKey(player.userID))
2372 {
2373 PrintMsgL(player, "TeleportPending");
2374 return;
2375 }
2376 if (TeleportTimers.ContainsKey(target.userID))
2377 {
2378 PrintMsgL(player, "TeleportPendingTarget");
2379 return;
2380 }
2381 if (PlayersRequests.ContainsKey(player.userID))
2382 {
2383 PrintMsgL(player, "PendingRequest");
2384 return;
2385 }
2386 if (PlayersRequests.ContainsKey(target.userID))
2387 {
2388 PrintMsgL(player, "PendingRequestTarget");
2389 return;
2390 }
2391 err = CanPlayerTeleport(player);
2392 if (err != null)
2393 {
2394 SendReply(player, err);
2395 return;
2396 }
2397 err = CanPlayerTeleport(target);
2398 if (err != null)
2399 {
2400 PrintMsgL(player, "TPRTarget");
2401 return;
2402 }
2403 err = CheckItems(player);
2404 if (err != null)
2405 {
2406 PrintMsgL(player, "TPBlockedItem", err);
2407 return;
2408 }
2409
2410 PlayersRequests[player.userID] = target;
2411 PlayersRequests[target.userID] = player;
2412 PendingRequests[target.userID] = timer.Once(configData.TPR.RequestDuration, () => { RequestTimedOut(player, target); });
2413 PrintMsgL(player, "Request", target.displayName);
2414 PrintMsgL(target, "RequestTarget", player.displayName);
2415 }
2416
2417 [ChatCommand("tpa")]
2418 private void cmdChatTeleportAccept(BasePlayer player, string command, string[] args)
2419 {
2420 if (!configData.Settings.TPREnabled) return;
2421 if (args.Length != 0)
2422 {
2423 PrintMsgL(player, "SyntaxCommandTPA");
2424 return;
2425 }
2426 Timer reqTimer;
2427 if (!PendingRequests.TryGetValue(player.userID, out reqTimer))
2428 {
2429 PrintMsgL(player, "NoPendingRequest");
2430 return;
2431 }
2432 var err = CheckPlayer(player, false, CanCraftTPR(player), false);
2433 if (err != null)
2434 {
2435 PrintMsgL(player, err);
2436 return;
2437 }
2438 err = CanPlayerTeleport(player);
2439 if (err != null)
2440 {
2441 SendReply(player, err);
2442 return;
2443 }
2444 var originPlayer = PlayersRequests[player.userID];
2445 err = CheckTargetLocation(originPlayer, player.transform.position, configData.TPR.UsableIntoBuildingBlocked, configData.TPR.CupOwnerAllowOnBuildingBlocked);
2446 if (err != null)
2447 {
2448 SendReply(player, err);
2449 return;
2450 }
2451 if (configData.TPR.BlockTPAOnCeiling)
2452 {
2453 var position = GetGround(player.transform.position);
2454 if (Vector3.Distance(position, player.transform.position) > 2)
2455 {
2456 RaycastHit hitInfo;
2457 BaseEntity entity = null;
2458 if (Physics.SphereCast(player.transform.position, .5f, Vector3.down, out hitInfo, 5, blockLayer))
2459 entity = hitInfo.GetEntity();
2460 if (entity != null && !entity.PrefabName.Contains("foundation"))
2461 {
2462 PrintMsgL(player, "AcceptOnRoof");
2463 return;
2464 }
2465 }
2466 }
2467 var countdown = GetLower(originPlayer, configData.TPR.VIPCountdowns, configData.TPR.Countdown);
2468 PrintMsgL(originPlayer, "Accept", player.displayName, countdown);
2469 PrintMsgL(player, "AcceptTarget", originPlayer.displayName);
2470 var timestamp = Facepunch.Math.Epoch.Current;
2471 TeleportTimers[originPlayer.userID] = new TeleportTimer
2472 {
2473 OriginPlayer = originPlayer,
2474 TargetPlayer = player,
2475 Timer = timer.Once(countdown, () =>
2476 {
2477 err = CheckPlayer(originPlayer, configData.TPR.UsableOutOfBuildingBlocked, CanCraftTPR(originPlayer)) ?? CheckPlayer(player, false, CanCraftTPR(player), true);
2478 if (err != null)
2479 {
2480 PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
2481 PrintMsgL(originPlayer, "Interrupted");
2482 PrintMsgL(originPlayer, err);
2483 TeleportTimers.Remove(originPlayer.userID);
2484 return;
2485 }
2486 err = CheckTargetLocation(originPlayer, player.transform.position, configData.TPR.UsableIntoBuildingBlocked, configData.TPR.CupOwnerAllowOnBuildingBlocked);
2487 if (err != null)
2488 {
2489 SendReply(player, err);
2490 PrintMsgL(originPlayer, "Interrupted");
2491 SendReply(originPlayer, err);
2492 TeleportTimers.Remove(originPlayer.userID);
2493 return;
2494 }
2495 err = CanPlayerTeleport(originPlayer) ?? CanPlayerTeleport(player);
2496 if (err != null)
2497 {
2498 SendReply(player, err);
2499 PrintMsgL(originPlayer, "Interrupted");
2500 SendReply(originPlayer, err);
2501 TeleportTimers.Remove(originPlayer.userID);
2502 return;
2503 }
2504 err = CheckItems(originPlayer);
2505 if (err != null)
2506 {
2507 PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
2508 PrintMsgL(originPlayer, "Interrupted");
2509 PrintMsgL(originPlayer, "TPBlockedItem", err);
2510 TeleportTimers.Remove(originPlayer.userID);
2511 return;
2512 }
2513 if(UseEconomy())
2514 {
2515 if(configData.TPR.Pay > 0)
2516 {
2517 if(!CheckEconomy(originPlayer, configData.TPR.Pay))
2518 {
2519 PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
2520 PrintMsgL(originPlayer, "TPNoMoney", configData.TPR.Pay);
2521 TeleportTimers.Remove(originPlayer.userID);
2522 return;
2523 }
2524 else
2525 {
2526 CheckEconomy(originPlayer, configData.TPR.Pay, true);
2527 PrintMsgL(originPlayer, "TPMoney", (double)configData.TPR.Bypass);
2528 }
2529 }
2530 }
2531 Teleport(originPlayer, CheckPosition(player.transform.position));
2532 var tprData = TPR[originPlayer.userID];
2533 tprData.Amount++;
2534 tprData.Timestamp = timestamp;
2535 changedTPR = true;
2536 PrintMsgL(player, "SuccessTarget", originPlayer.displayName);
2537 PrintMsgL(originPlayer, "Success", player.displayName);
2538 var limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
2539 if (limit > 0) PrintMsgL(originPlayer, "TPRAmount", limit - tprData.Amount);
2540 TeleportTimers.Remove(originPlayer.userID);
2541 })
2542 };
2543 reqTimer.Destroy();
2544 PendingRequests.Remove(player.userID);
2545 PlayersRequests.Remove(player.userID);
2546 PlayersRequests.Remove(originPlayer.userID);
2547 }
2548
2549 [ChatCommand("wipehomes")]
2550 private void cmdChatWipeHomes(BasePlayer player, string command, string[] args)
2551 {
2552 if (!IsAllowedMsg(player, PermWipeHomes)) return;
2553 Home.Clear();
2554 changedHome = true;
2555 PrintMsgL(player, "HomesListWiped");
2556 }
2557
2558 [ChatCommand("tphelp")]
2559 private void cmdChatTeleportHelp(BasePlayer player, string command, string[] args)
2560 {
2561 if (!configData.Settings.HomesEnabled && !configData.Settings.TPREnabled && !IsAllowedMsg(player)) return;
2562 if (args.Length == 1)
2563 {
2564 var key = $"TPHelp{args[0].ToLower()}";
2565 var msg = _(key, player);
2566 if (key.Equals(msg))
2567 PrintMsgL(player, "InvalidHelpModule");
2568 else
2569 PrintMsg(player, msg);
2570 }
2571 else
2572 {
2573 var msg = _("TPHelpGeneral", player);
2574 if (IsAllowed(player))
2575 msg += NewLine + "/tphelp AdminTP";
2576 if (configData.Settings.HomesEnabled)
2577 msg += NewLine + "/tphelp Home";
2578 if (configData.Settings.TPREnabled)
2579 msg += NewLine + "/tphelp TPR";
2580 PrintMsg(player, msg);
2581 }
2582 }
2583
2584 [ChatCommand("tpinfo")]
2585 private void cmdChatTeleportInfo(BasePlayer player, string command, string[] args)
2586 {
2587 if (!configData.Settings.HomesEnabled && !configData.Settings.TPREnabled && !configData.Settings.TownEnabled) return;
2588 if (args.Length == 1)
2589 {
2590 var module = args[0].ToLower();
2591 var msg = _($"TPSettings{module}", player);
2592 var timestamp = Facepunch.Math.Epoch.Current;
2593 var currentDate = DateTime.Now.ToString("d");
2594 TeleportData teleportData;
2595 int limit;
2596 int cooldown;
2597 switch (module)
2598 {
2599 case "home":
2600 limit = GetHigher(player, configData.Home.VIPDailyLimits, configData.Home.DailyLimit);
2601 cooldown = GetLower(player, configData.Home.VIPCooldowns, configData.Home.Cooldown);
2602 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player), GetHigher(player, configData.Home.VIPHomesLimits, configData.Home.HomesLimit)));
2603 HomeData homeData;
2604 if (!Home.TryGetValue(player.userID, out homeData))
2605 Home[player.userID] = homeData = new HomeData();
2606 if (homeData.Teleports.Date != currentDate)
2607 {
2608 homeData.Teleports.Amount = 0;
2609 homeData.Teleports.Date = currentDate;
2610 }
2611 if (limit > 0) PrintMsgL(player, "HomeTPAmount", limit - homeData.Teleports.Amount);
2612 if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
2613 {
2614 var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
2615 PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
2616 }
2617 break;
2618 case "tpr":
2619 limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
2620 cooldown = GetLower(player, configData.TPR.VIPCooldowns, configData.TPR.Cooldown);
2621 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
2622 if (!TPR.TryGetValue(player.userID, out teleportData))
2623 TPR[player.userID] = teleportData = new TeleportData();
2624 if (teleportData.Date != currentDate)
2625 {
2626 teleportData.Amount = 0;
2627 teleportData.Date = currentDate;
2628 }
2629 if (limit > 0) PrintMsgL(player, "TPRAmount", limit - teleportData.Amount);
2630 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
2631 {
2632 var remain = cooldown - (timestamp - teleportData.Timestamp);
2633 PrintMsgL(player, "TPRCooldown", FormatTime(remain));
2634 }
2635 break;
2636 case "town":
2637 limit = GetHigher(player, configData.Town.VIPDailyLimits, configData.Town.DailyLimit);
2638 cooldown = GetLower(player, configData.Town.VIPCooldowns, configData.Town.Cooldown);
2639 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
2640 if (!Town.TryGetValue(player.userID, out teleportData))
2641 Town[player.userID] = teleportData = new TeleportData();
2642 if (teleportData.Date != currentDate)
2643 {
2644 teleportData.Amount = 0;
2645 teleportData.Date = currentDate;
2646 }
2647 if (limit > 0) PrintMsgL(player, "TownTPAmount", limit - teleportData.Amount);
2648 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
2649 {
2650 var remain = cooldown - (timestamp - teleportData.Timestamp);
2651 PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
2652 PrintMsgL(player, "TownTPCooldownBypassP", configData.Town.Bypass);
2653 PrintMsgL(player, "TownTPCooldownBypassP2", configData.Settings.BypassCMD);
2654 }
2655 break;
2656 default:
2657 PrintMsgL(player, "InvalidHelpModule");
2658 break;
2659 }
2660 }
2661 else
2662 {
2663 var msg = _("TPInfoGeneral", player);
2664 if (configData.Settings.HomesEnabled)
2665 msg += NewLine + "/tpinfo Home";
2666 if (configData.Settings.TPREnabled)
2667 msg += NewLine + "/tpinfo TPR";
2668 if (configData.Settings.TownEnabled)
2669 msg += NewLine + "/tpinfo Town";
2670 PrintMsgL(player, msg);
2671 }
2672 }
2673
2674 [ChatCommand("tpc")]
2675 private void cmdChatTeleportCancel(BasePlayer player, string command, string[] args)
2676 {
2677 if (!configData.Settings.TPREnabled) return;
2678 if (args.Length != 0)
2679 {
2680 PrintMsgL(player, "SyntaxCommandTPC");
2681 return;
2682 }
2683 TeleportTimer teleportTimer;
2684 if (TeleportTimers.TryGetValue(player.userID, out teleportTimer))
2685 {
2686 teleportTimer.Timer?.Destroy();
2687 PrintMsgL(player, "TPCancelled");
2688 PrintMsgL(teleportTimer.TargetPlayer, "TPCancelledTarget", player.displayName);
2689 TeleportTimers.Remove(player.userID);
2690 return;
2691 }
2692 foreach (var keyValuePair in TeleportTimers)
2693 {
2694 if (keyValuePair.Value.TargetPlayer != player) continue;
2695 keyValuePair.Value.Timer?.Destroy();
2696 PrintMsgL(keyValuePair.Value.OriginPlayer, "TPCancelledTarget", player.displayName);
2697 PrintMsgL(player, "TPYouCancelledTarget", keyValuePair.Value.OriginPlayer.displayName);
2698 TeleportTimers.Remove(keyValuePair.Key);
2699 return;
2700 }
2701 BasePlayer target;
2702 if (!PlayersRequests.TryGetValue(player.userID, out target))
2703 {
2704 PrintMsgL(player, "NoPendingRequest");
2705 return;
2706 }
2707 Timer reqTimer;
2708 if (PendingRequests.TryGetValue(player.userID, out reqTimer))
2709 {
2710 reqTimer.Destroy();
2711 PendingRequests.Remove(player.userID);
2712 }
2713 else if (PendingRequests.TryGetValue(target.userID, out reqTimer))
2714 {
2715 reqTimer.Destroy();
2716 PendingRequests.Remove(target.userID);
2717 var temp = player;
2718 player = target;
2719 target = temp;
2720 }
2721 PlayersRequests.Remove(target.userID);
2722 PlayersRequests.Remove(player.userID);
2723 PrintMsgL(player, "Cancelled", target.displayName);
2724 PrintMsgL(target, "CancelledTarget", player.displayName);
2725 }
2726
2727 [ChatCommand("town")]
2728 private void cmdChatTown(BasePlayer player, string command, string[] args)
2729 {
2730 if (!IsAllowedMsg(player, PermTpTown)) return;
2731
2732 bool paidmoney = false;
2733 if (args.Length == 1 && IsAllowed(player) && args[0].ToLower().Equals("set"))
2734 {
2735 configData.Town.Location = player.transform.position;
2736 Config.WriteObject(configData, true);
2737 PrintMsgL(player, "TownTPLocation", configData.Town.Location);
2738 return;
2739 }
2740 if (!configData.Settings.TownEnabled)
2741 {
2742 PrintMsgL(player, "TownTPDisabled");
2743 return;
2744 }
2745 if (args.Length == 1 && (args[0].ToLower() != configData.Settings.BypassCMD.ToLower()))
2746 {
2747 PrintMsgL(player, "SyntaxCommandTown");
2748 if (IsAllowed(player)) PrintMsgL(player, "SyntaxCommandTownAdmin");
2749 return;
2750 }
2751
2752 if (configData.Town.Location == default(Vector3))
2753 {
2754 PrintMsgL(player, "TownTPNotSet");
2755 return;
2756 }
2757 var err = CheckPlayer(player, configData.Town.UsableOutOfBuildingBlocked, CanCraftTown(player), true);
2758 if (err != null)
2759 {
2760 PrintMsgL(player, err);
2761 return;
2762 }
2763 TeleportData teleportData;
2764 if (!Town.TryGetValue(player.userID, out teleportData))
2765 Town[player.userID] = teleportData = new TeleportData();
2766 var timestamp = Facepunch.Math.Epoch.Current;
2767 var currentDate = DateTime.Now.ToString("d");
2768 if (teleportData.Date != currentDate)
2769 {
2770 teleportData.Amount = 0;
2771 teleportData.Date = currentDate;
2772 }
2773 var cooldown = GetLower(player, configData.Town.VIPCooldowns, configData.Town.Cooldown);
2774 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
2775 {
2776 var cmdSent = "";
2777 bool foundmoney = CheckEconomy(player, configData.Town.Bypass);
2778 try
2779 {
2780 cmdSent = args[0].ToLower();
2781 }
2782 catch {}
2783
2784 bool payalso = false;
2785 if(configData.Town.Pay > 0)
2786 {
2787 payalso = true;
2788 }
2789 if((configData.Settings.BypassCMD != null) && (cmdSent == configData.Settings.BypassCMD.ToLower()))
2790 {
2791 if(foundmoney == true)
2792 {
2793 CheckEconomy(player, configData.Town.Bypass, true);
2794 paidmoney = true;
2795 PrintMsgL(player, "TownTPCooldownBypass", configData.Town.Bypass);
2796 if(payalso)
2797 {
2798 PrintMsgL(player, "PayToTown", configData.Town.Pay);
2799 }
2800 }
2801 else
2802 {
2803 PrintMsgL(player, "TownTPCooldownBypassF", configData.Town.Bypass);
2804 return;
2805 }
2806 }
2807 else if(UseEconomy())
2808 {
2809 var remain = cooldown - (timestamp - teleportData.Timestamp);
2810 PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
2811 if(configData.Town.Bypass > 0 && configData.Settings.BypassCMD != null)
2812 {
2813 PrintMsgL(player, "TownTPCooldownBypassP", configData.Town.Bypass);
2814 PrintMsgL(player, "TownTPCooldownBypassP2", configData.Settings.BypassCMD);
2815 }
2816 return;
2817 }
2818 else
2819 {
2820 var remain = cooldown - (timestamp - teleportData.Timestamp);
2821 PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
2822 return;
2823 }
2824 }
2825
2826 var limit = GetHigher(player, configData.Town.VIPDailyLimits, configData.Town.DailyLimit);
2827 if (limit > 0 && teleportData.Amount >= limit)
2828 {
2829 PrintMsgL(player, "TownTPLimitReached", limit);
2830 return;
2831 }
2832 if (TeleportTimers.ContainsKey(player.userID))
2833 {
2834 PrintMsgL(player, "TeleportPending");
2835 return;
2836 }
2837 err = CanPlayerTeleport(player);
2838 if (err != null)
2839 {
2840 SendReply(player, err);
2841 return;
2842 }
2843 err = CheckItems(player);
2844 if (err != null)
2845 {
2846 PrintMsgL(player, "TPBlockedItem", err);
2847 return;
2848 }
2849
2850 var countdown = GetLower(player, configData.Town.VIPCountdowns, configData.Town.Countdown);
2851 TeleportTimers[player.userID] = new TeleportTimer
2852 {
2853 OriginPlayer = player,
2854 Timer = timer.Once(countdown, () =>
2855 {
2856 err = CheckPlayer(player, configData.Town.UsableOutOfBuildingBlocked, CanCraftTown(player), true);
2857 if (err != null)
2858 {
2859 PrintMsgL(player, "Interrupted");
2860 PrintMsgL(player, err);
2861 if(paidmoney == true)
2862 {
2863 paidmoney = false;
2864 CheckEconomy(player, configData.Town.Bypass, false, true);
2865 }
2866 TeleportTimers.Remove(player.userID);
2867 return;
2868 }
2869 err = CanPlayerTeleport(player);
2870 if (err != null)
2871 {
2872 PrintMsgL(player, "Interrupted");
2873 PrintMsgL(player, err);
2874 if(paidmoney == true)
2875 {
2876 paidmoney = false;
2877 CheckEconomy(player, configData.Town.Bypass, false, true);
2878 }
2879 TeleportTimers.Remove(player.userID);
2880 return;
2881 }
2882 err = CheckItems(player);
2883 if (err != null)
2884 {
2885 PrintMsgL(player, "Interrupted");
2886 PrintMsgL(player, "TPBlockedItem", err);
2887 if(paidmoney == true)
2888 {
2889 paidmoney = false;
2890 CheckEconomy(player, configData.Town.Bypass, false, true);
2891 }
2892 TeleportTimers.Remove(player.userID);
2893 return;
2894 }
2895 if(UseEconomy())
2896 {
2897 if(configData.Town.Pay > 0 && ! CheckEconomy(player, configData.Town.Pay))
2898 {
2899 PrintMsgL(player, "Interrupted");
2900 PrintMsgL(player, "TPNoMoney", configData.Town.Pay);
2901 TeleportTimers.Remove(player.userID);
2902 return;
2903 }
2904 else if (configData.Town.Pay > 0)
2905 {
2906 CheckEconomy(player, configData.Town.Pay, true);
2907 PrintMsgL(player, "TPMoney", (double)configData.Town.Pay);
2908 }
2909 }
2910 player.SetParent(null, true, true);
2911 Teleport(player, configData.Town.Location);
2912 teleportData.Amount++;
2913 teleportData.Timestamp = timestamp;
2914
2915 changedTown = true;
2916 PrintMsgL(player, "TownTP");
2917 if (limit > 0) PrintMsgL(player, "TownTPAmount", limit - teleportData.Amount);
2918 TeleportTimers.Remove(player.userID);
2919 })
2920 };
2921 PrintMsgL(player, "TownTPStarted", countdown);
2922 }
2923
2924 private bool ccmdTeleport(ConsoleSystem.Arg arg)
2925 {
2926 if (arg.Player() != null && !IsAllowedMsg(arg.Player(), PermTpConsole)) return false;
2927 HashSet<BasePlayer> players;
2928 switch (arg.cmd.FullName)
2929 {
2930 case "teleport.topos":
2931 if (!arg.HasArgs(4))
2932 {
2933 arg.ReplyWith(_("SyntaxConsoleCommandToPos", arg.Player()));
2934 return false;
2935 }
2936 players = FindPlayers(arg.GetString(0));
2937 if (players.Count <= 0)
2938 {
2939 arg.ReplyWith(_("PlayerNotFound", arg.Player()));
2940 return false;
2941 }
2942 if (players.Count > 1)
2943 {
2944 arg.ReplyWith(_("MultiplePlayers", arg.Player(), string.Join(", ", players.Select(p => p.displayName).ToArray())));
2945 return false;
2946 }
2947 var targetPlayer = players.First();
2948 var x = arg.GetFloat(1, -10000);
2949 var y = arg.GetFloat(2, -10000);
2950 var z = arg.GetFloat(3, -10000);
2951 if (!CheckBoundaries(x, y, z))
2952 {
2953 arg.ReplyWith(_("AdminTPOutOfBounds", arg.Player()) + Environment.NewLine + _("AdminTPBoundaries", arg.Player(), boundary));
2954 return false;
2955 }
2956 targetPlayer.SetParent(null, true, true);
2957 TeleportToPosition(targetPlayer, x, y, z);
2958 if (configData.Admin.AnnounceTeleportToTarget)
2959 PrintMsgL(targetPlayer, "AdminTPConsoleTP", targetPlayer.transform.position);
2960 arg.ReplyWith(_("AdminTPTargetCoordinates", arg.Player(), targetPlayer.displayName, targetPlayer.transform.position));
2961 Puts(_("LogTeleportPlayer", null, arg.Player()?.displayName, targetPlayer.displayName, targetPlayer.transform.position));
2962 break;
2963 case "teleport.toplayer":
2964 if (!arg.HasArgs(2))
2965 {
2966 arg.ReplyWith(_("SyntaxConsoleCommandToPlayer", arg.Player()));
2967 return false;
2968 }
2969 players = FindPlayers(arg.GetString(0));
2970 if (players.Count <= 0)
2971 {
2972 arg.ReplyWith(_("PlayerNotFound", arg.Player()));
2973 return false;
2974 }
2975 if (players.Count > 1)
2976 {
2977 arg.ReplyWith(_("MultiplePlayers", arg.Player(), string.Join(", ", players.Select(p => p.displayName).ToArray())));
2978 return false;
2979 }
2980 var originPlayer = players.First();
2981 players = FindPlayers(arg.GetString(1));
2982 if (players.Count <= 0)
2983 {
2984 arg.ReplyWith(_("PlayerNotFound", arg.Player()));
2985 return false;
2986 }
2987 if (players.Count > 1)
2988 {
2989 arg.ReplyWith(_("MultiplePlayers", arg.Player(), string.Join(", ", players.Select(p => p.displayName).ToArray())));
2990 return false;
2991 }
2992 targetPlayer = players.First();
2993 if (targetPlayer == originPlayer)
2994 {
2995 arg.ReplyWith(_("CantTeleportPlayerToSelf", arg.Player()));
2996 return false;
2997 }
2998 originPlayer.SetParent(null, true, true);
2999 TeleportToPlayer(originPlayer, targetPlayer);
3000 arg.ReplyWith(_("AdminTPPlayers", arg.Player(), originPlayer.displayName, targetPlayer.displayName));
3001 PrintMsgL(originPlayer, "AdminTPConsoleTPPlayer", targetPlayer.displayName);
3002 if (configData.Admin.AnnounceTeleportToTarget)
3003 PrintMsgL(targetPlayer, "AdminTPConsoleTPPlayerTarget", originPlayer.displayName);
3004 Puts(_("LogTeleportPlayer", null, arg.Player()?.displayName, originPlayer.displayName, targetPlayer.displayName));
3005 break;
3006 }
3007 return false;
3008 }
3009
3010 [ConsoleCommand("teleport.importhomes")]
3011 private bool ccmdImportHomes(ConsoleSystem.Arg arg)
3012 {
3013 if (arg.Player() != null && !IsAllowedMsg(arg.Player(), PermImportHomes))
3014 {
3015 arg.ReplyWith("Not allowed.");
3016 return false;
3017 }
3018 var datafile = Interface.Oxide.DataFileSystem.GetFile("m-Teleportation");
3019 if (!datafile.Exists())
3020 {
3021 arg.ReplyWith("No m-Teleportation.json exists.");
3022 return false;
3023 }
3024 datafile.Load();
3025 var allHomeData = datafile["HomeData"] as Dictionary<string, object>;
3026 if (allHomeData == null)
3027 {
3028 arg.ReplyWith("Empty HomeData.");
3029 return false;
3030 }
3031 var count = 0;
3032 foreach (var kvp in allHomeData)
3033 {
3034 var homeDataOld = kvp.Value as Dictionary<string, object>;
3035 if (homeDataOld == null) continue;
3036 if (!homeDataOld.ContainsKey("HomeLocations")) continue;
3037 var homeList = homeDataOld["HomeLocations"] as Dictionary<string, object>;
3038 if (homeList == null) continue;
3039 var userId = Convert.ToUInt64(kvp.Key);
3040 HomeData homeData;
3041 if (!Home.TryGetValue(userId, out homeData))
3042 Home[userId] = homeData = new HomeData();
3043 foreach (var kvp2 in homeList)
3044 {
3045 var positionData = kvp2.Value as Dictionary<string, object>;
3046 if (positionData == null) continue;
3047 if (!positionData.ContainsKey("x") || !positionData.ContainsKey("y") || !positionData.ContainsKey("z")) continue;
3048 var position = new Vector3(Convert.ToSingle(positionData["x"]), Convert.ToSingle(positionData["y"]), Convert.ToSingle(positionData["z"]));
3049 homeData.Locations[kvp2.Key] = position;
3050 changedHome = true;
3051 count++;
3052 }
3053 }
3054 arg.ReplyWith(string.Format("Imported {0} homes.", count));
3055 return false;
3056 }
3057
3058 private void RequestTimedOut(BasePlayer player, BasePlayer target)
3059 {
3060 PlayersRequests.Remove(player.userID);
3061 PlayersRequests.Remove(target.userID);
3062 PendingRequests.Remove(target.userID);
3063 PrintMsgL(player, "TimedOut", target.displayName);
3064 PrintMsgL(target, "TimedOutTarget", player.displayName);
3065 }
3066
3067 #region Util
3068
3069 private string FormatTime(long seconds)
3070 {
3071 var timespan = TimeSpan.FromSeconds(seconds);
3072 return string.Format(timespan.TotalHours >= 1 ? "{2:00}:{0:00}:{1:00}" : "{0:00}:{1:00}", timespan.Minutes, timespan.Seconds, System.Math.Floor(timespan.TotalHours));
3073 }
3074
3075 private double ConvertToRadians(double angle)
3076 {
3077 return System.Math.PI / 180 * angle;
3078 }
3079
3080 #region Teleport
3081
3082 public void TeleportToPlayer(BasePlayer player, BasePlayer target) => Teleport(player, target.transform.position);
3083
3084 public void TeleportToPosition(BasePlayer player, float x, float y, float z) => Teleport(player, new Vector3(x, y, z));
3085
3086 public void Teleport(BasePlayer player, Vector3 position)
3087 {
3088 SaveLocation(player);
3089 teleporting.Add(player.userID);
3090 if (player.net?.connection != null)
3091 player.ClientRPCPlayer(null, player, "StartLoading");
3092 StartSleeping(player);
3093 player.MovePosition(position);
3094 if (player.net?.connection != null)
3095 player.ClientRPCPlayer(null, player, "ForcePositionTo", position);
3096 if (player.net?.connection != null)
3097 player.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, true);
3098 player.UpdateNetworkGroup();
3099 //player.UpdatePlayerCollider(true, false);
3100 player.SendNetworkUpdateImmediate(false);
3101 if (player.net?.connection == null) return;
3102 //TODO temporary for potential rust bug
3103 try
3104 {
3105 player.ClearEntityQueue(null);
3106 }
3107 catch
3108 {
3109 }
3110 player.SendFullSnapshot();
3111 }
3112
3113 private void StartSleeping(BasePlayer player)
3114 {
3115 if (player.IsSleeping())
3116 return;
3117 player.SetPlayerFlag(BasePlayer.PlayerFlags.Sleeping, true);
3118 if (!BasePlayer.sleepingPlayerList.Contains(player))
3119 BasePlayer.sleepingPlayerList.Add(player);
3120 player.CancelInvoke("InventoryUpdate");
3121 //player.inventory.crafting.CancelAll(true);
3122 //player.UpdatePlayerCollider(true, false);
3123 }
3124
3125 #endregion
3126
3127 #region Checks
3128
3129 // Used by tpa only to provide for offset from the target to avoid overlap
3130 private Vector3 CheckPosition(Vector3 position)
3131 {
3132 var hits = Physics.OverlapSphere(position, 2, blockLayer);
3133 var distance = 5f;
3134 BuildingBlock buildingBlock = null;
3135 for (var i = 0; i < hits.Length; i++)
3136 {
3137 var block = hits[i].GetComponentInParent<BuildingBlock>();
3138 if (block == null) continue;
3139 var prefab = block.PrefabName;
3140 if (!prefab.Contains("foundation", CompareOptions.OrdinalIgnoreCase) && !prefab.Contains("floor", CompareOptions.OrdinalIgnoreCase) && !prefab.Contains("pillar", CompareOptions.OrdinalIgnoreCase)) continue;
3141 if (!(Vector3.Distance(block.transform.position, position) < distance)) continue;
3142 buildingBlock = block;
3143 distance = Vector3.Distance(block.transform.position, position);
3144 }
3145 if (buildingBlock == null || configData.TPR.OffsetTPRTarget == false) return position;
3146 var blockRotation = buildingBlock.transform.rotation.eulerAngles.y;
3147 var angles = new[] { 360 - blockRotation, 180 - blockRotation };
3148 var location = default(Vector3);
3149 const double r = 2.9;
3150 var locationDistance = 100f;
3151
3152#if DEBUG
3153 Puts("CheckPosition: Finding suitable target position");
3154 var positions = position.ToString();
3155 Puts($"CheckPosition: Old location {positions}");
3156#endif
3157 for (var i = 0; i < angles.Length; i++)
3158 {
3159 var radians = ConvertToRadians(angles[i]);
3160 var newX = r * System.Math.Cos(radians);
3161 var newZ = r * System.Math.Sin(radians);
3162#if DEBUG
3163 Puts($"CheckPosition: Checking angle {i}");
3164 var newXs = newX.ToString();
3165 var newZs = newZ.ToString();
3166 Puts($"CheckPosition: newX = {newXs}, newZ = {newZs}");
3167#endif
3168 var newLoc = new Vector3((float)(buildingBlock.transform.position.x + newX), buildingBlock.transform.position.y + .2f, (float)(buildingBlock.transform.position.z + newZ));
3169 if (Vector3.Distance(position, newLoc) < locationDistance)
3170 {
3171 location = newLoc;
3172 locationDistance = Vector3.Distance(position, newLoc);
3173#if DEBUG
3174 var locs = newLoc.ToString();
3175 Puts($"CheckPosition: possible new location at {locs}");
3176#endif
3177 }
3178 }
3179#if DEBUG
3180 var locations = location.ToString();
3181 Puts($"CheckPosition: New location {locations}");
3182#endif
3183 return location;
3184 }
3185
3186 private string CanPlayerTeleport(BasePlayer player)
3187 {
3188 return Interface.Oxide.CallHook("CanTeleport", player) as string;
3189 }
3190
3191 private bool CanCraftHome(BasePlayer player)
3192 {
3193 return configData.Home.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftHome);
3194 }
3195
3196 private bool CanCraftTown(BasePlayer player)
3197 {
3198 return configData.Town.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftTown);
3199 }
3200
3201 private bool CanCraftTPR(BasePlayer player)
3202 {
3203 return configData.TPR.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftTpR);
3204 }
3205
3206 private string NearMonument(BasePlayer player)
3207 {
3208 var pos = player.transform.position;
3209 var poss = pos.ToString();
3210
3211 foreach(KeyValuePair<string, Vector3> entry in monPos)
3212 {
3213 var monname = entry.Key;
3214 var monvector = entry.Value;
3215 float realdistance = monSize[monname].z;
3216 monvector.y = pos.y;
3217 float dist = Vector3.Distance(pos, monvector);
3218
3219 if(dist < realdistance)
3220 {
3221 return monname;
3222 }
3223 }
3224 return null;
3225 }
3226
3227 private string NearCave(BasePlayer player)
3228 {
3229 var pos = player.transform.position;
3230 var poss = pos.ToString();
3231
3232 foreach(KeyValuePair<string, Vector3> entry in cavePos)
3233 {
3234 var cavename = entry.Key;
3235 float realdistance = 0f;
3236
3237 if(cavename.Contains("Small"))
3238 {
3239 realdistance = configData.Settings.CaveDistanceSmall;
3240 }
3241 else if(cavename.Contains("Large"))
3242 {
3243 realdistance = configData.Settings.CaveDistanceLarge;
3244 }
3245 else if(cavename.Contains("Medium"))
3246 {
3247 realdistance = configData.Settings.CaveDistanceMedium;
3248 }
3249
3250 var cavevector = entry.Value;
3251 cavevector.y = pos.y;
3252 var cpos = cavevector.ToString();
3253 float dist = Vector3.Distance(pos, cavevector);
3254
3255 if(dist < realdistance)
3256 {
3257#if DEBUG
3258 Puts($"NearCave: {cavename} nearby.");
3259#endif
3260 return cavename;
3261 }
3262 else
3263 {
3264#if DEBUG
3265 Puts("NearCave: Not near this cave.");
3266#endif
3267 }
3268 }
3269 return null;
3270 }
3271
3272 private string CheckPlayer(BasePlayer player, bool build = false, bool craft = false, bool origin = true)
3273 {
3274 var onship = player.GetComponentInParent<CargoShip>();
3275 //var onrig = player.GetComponentInParent<OilrigAI>();
3276 var onballoon = player.GetComponentInParent<HotAirBalloon>();
3277 var inlift = player.GetComponentInParent<Lift>();
3278 var pos = player.transform.position;
3279
3280 string monname = NearMonument(player);
3281 if(configData.Settings.InterruptTPOnMonument == true)
3282 {
3283 if(monname != null)
3284 {
3285 return _("TooCloseToMon", player, monname);
3286 }
3287 }
3288 if(configData.Home.AllowCave == false)
3289 {
3290#if DEBUG
3291 Puts("Checking cave distance...");
3292#endif
3293 string cavename = NearCave(player);
3294 if(cavename != null)
3295 {
3296 return "TooCloseToCave";
3297 }
3298 }
3299 if(player.isMounted && configData.Settings.InterruptTPOnMounted == true)
3300 return "TPMounted";
3301 if(!player.IsAlive())
3302 return "TPDead";
3303 // Block if hurt if the config is enabled. If the player is not the target in a tpa condition, allow.
3304 if((player.IsWounded() && origin) && configData.Settings.InterruptTPOnHurt == true)
3305 return "TPWounded";
3306
3307 if(player.metabolism.temperature.value <= configData.Settings.MinimumTemp && configData.Settings.InterruptTPOnCold == true)
3308 {
3309 return "TPTooCold";
3310 }
3311 if(player.metabolism.temperature.value >= configData.Settings.MaximumTemp && configData.Settings.InterruptTPOnHot == true)
3312 {
3313 return "TPTooHot";
3314 }
3315
3316 if(!build && !player.CanBuild())
3317 return "TPBuildingBlocked";
3318 if(player.IsSwimming() && configData.Settings.InterruptTPOnSwimming == true)
3319 return "TPSwimming";
3320 // This will have to do until we have a proper parent name for this
3321 if(monname != null && monname.Contains("Oilrig") && configData.Settings.InterruptTPOnRig == true)
3322 return "TPOilRig";
3323 if(onship && configData.Settings.InterruptTPOnCargo == true)
3324 return "TPCargoShip";
3325 if(onballoon && configData.Settings.InterruptTPOnBalloon == true)
3326 return "TPHotAirBalloon";
3327 if(inlift && configData.Settings.InterruptTPOnLift == true)
3328 return "TPBucketLift";
3329 if(GetLift(pos) && configData.Settings.InterruptTPOnLift == true)
3330 return "TPRegLift";
3331 if(player.InSafeZone() && configData.Settings.InterruptTPOnSafe == true)
3332 return "TPSafeZone";
3333 if(!craft && player.inventory.crafting.queue.Count > 0)
3334 return "TPCrafting";
3335 return null;
3336 }
3337
3338 private string CheckTargetLocation(BasePlayer player, Vector3 targetLocation, bool build, bool owner)
3339 {
3340 // build == UsableIntoBuildingBlocked
3341 // owner == CupOwnerAllowOnBuildingBlocked (applies to block if no cupboard)
3342 var colliders = Pool.GetList<Collider>();
3343 Vis.Colliders(targetLocation, 0.1f, colliders, buildingLayer);
3344 var cups = false;
3345 foreach(var collider in colliders)
3346 {
3347 var block = collider.GetComponentInParent<BuildingBlock>();
3348 if (block == null)
3349 {
3350 continue;
3351 }
3352 cups = true;
3353
3354 if(CheckCupboardBlock(block, player, owner))
3355 {
3356 cups = false;
3357 continue;
3358 }
3359 if (owner && player.userID == block.OwnerID)
3360 {
3361 cups = false;
3362 continue;
3363 }
3364 }
3365 Pool.FreeList(ref colliders);
3366 return cups && !build ? "TPTargetBuildingBlocked" : null;
3367 }
3368
3369 // Check that a building block is owned by/attached to a cupboard, allow tp if not blocked unless allowed by config
3370 private bool CheckCupboardBlock(BuildingBlock block, BasePlayer player, bool owner)
3371 {
3372 // owner == CupOwnerAllowOnBuildingBlocked
3373 BuildingManager.Building building = block.GetBuilding();
3374 if(building != null)
3375 {
3376 // cupboard overlap. Check privs.
3377 if(building.buildingPrivileges == null)
3378 {
3379 return false;
3380 }
3381
3382 ulong hitEntityOwnerID = block.OwnerID != 0 ? block.OwnerID : 0;
3383 if (owner && player.userID == hitEntityOwnerID)
3384 {
3385 // player set the cupboard and is allowed in by config
3386 return true;
3387 }
3388
3389 foreach(var privs in building.buildingPrivileges)
3390 {
3391 if(CupboardAuthCheck(privs, hitEntityOwnerID))
3392 {
3393 // player is authorized to the cupboard
3394 return true;
3395 }
3396 }
3397 }
3398 return false; // MAY NEED TO BE TRUE I.E. IF NO CUPBOARD AT ALL
3399 }
3400
3401 private bool CupboardAuthCheck(BuildingPrivlidge priv, ulong hitEntityOwnerID)
3402 {
3403 foreach(var auth in priv.authorizedPlayers.Select(x => x.userid).ToArray())
3404 {
3405 if(auth == hitEntityOwnerID)
3406 {
3407 return true;
3408 }
3409 }
3410 return false;
3411 }
3412
3413 private string CheckInsideBlock(Vector3 targetLocation)
3414 {
3415 List<BuildingBlock> blocks = Pool.GetList<BuildingBlock>();
3416 Vis.Entities(targetLocation + new Vector3(0, 0.25f), 0.1f, blocks, blockLayer);
3417 bool inside = blocks.Count > 0;
3418 Pool.FreeList(ref blocks);
3419
3420 return inside ? "TPTargetInsideBlock" : null;
3421 }
3422
3423 private string CheckItems(BasePlayer player)
3424 {
3425 foreach (var blockedItem in ReverseBlockedItems)
3426 {
3427 if (player.inventory.containerMain.GetAmount(blockedItem.Key, true) > 0)
3428 return blockedItem.Value;
3429 if (player.inventory.containerBelt.GetAmount(blockedItem.Key, true) > 0)
3430 return blockedItem.Value;
3431 if (player.inventory.containerWear.GetAmount(blockedItem.Key, true) > 0)
3432 return blockedItem.Value;
3433 }
3434 return null;
3435 }
3436
3437 private string CheckFoundation(ulong userID, Vector3 position)
3438 {
3439 if (UnderneathFoundation(position))
3440 {
3441 return "HomeFoundationUnderneathFoundation";
3442 }
3443
3444 if (!configData.Home.ForceOnTopOfFoundation) return null;
3445
3446 var entities = GetFoundation(position);
3447 if (entities.Count == 0)
3448 return "HomeNoFoundation";
3449
3450 if (!configData.Home.CheckFoundationForOwner) return null;
3451 for (var i = 0; i < entities.Count; i++)
3452 if (entities[i].OwnerID == userID) return null;
3453 if (!configData.Home.UseFriends)
3454 return "HomeFoundationNotOwned";
3455
3456 var moderator = (bool)(Clans?.CallHook("IsModerator", userID) ?? false);
3457 var userIdString = userID.ToString();
3458 for (var i = 0; i < entities.Count; i++)
3459 {
3460 var entity = entities[i];
3461 if ((bool)(Friends?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) || (bool)(Clans?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) && moderator || (bool)(RustIO?.CallHook("HasFriend", entity.OwnerID.ToString(), userIdString) ?? false))
3462 return null;
3463 }
3464
3465 return "HomeFoundationNotFriendsOwned";
3466 }
3467
3468 private BuildingBlock GetFoundationOwned(Vector3 position, ulong userID)
3469 {
3470 var entities = GetFoundation(position);
3471 if (entities.Count == 0)
3472 return null;
3473 if (!configData.Home.CheckFoundationForOwner) return entities[0];
3474 for (var i = 0; i < entities.Count; i++)
3475 if (entities[i].OwnerID == userID) return entities[i];
3476 if (!configData.Home.UseFriends)
3477 return null;
3478 var moderator = (bool)(Clans?.CallHook("IsModerator", userID) ?? false);
3479 var userIdString = userID.ToString();
3480 for (var i = 0; i < entities.Count; i++)
3481 {
3482 var entity = entities[i];
3483 if ((bool)(Friends?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) || (bool)(Clans?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) && moderator || (bool)(RustIO?.CallHook("HasFriend", entity.OwnerID.ToString(), userIdString) ?? false))
3484 return entity;
3485 }
3486 return null;
3487 }
3488
3489 private List<BuildingBlock> GetFoundation(Vector3 positionCoordinates)
3490 {
3491 var position = GetGround(positionCoordinates);
3492 var entities = new List<BuildingBlock>();
3493 var hits = Pool.GetList<BuildingBlock>();
3494 Vis.Entities(position, 2.5f, hits, buildingLayer);
3495 for (var i = 0; i < hits.Count; i++)
3496 {
3497 var entity = hits[i];
3498 if (!entity.PrefabName.Contains("foundation") || positionCoordinates.y < entity.WorldSpaceBounds().ToBounds().max.y) continue;
3499 entities.Add(entity);
3500 }
3501 Pool.FreeList(ref hits);
3502 return entities;
3503 }
3504
3505 private bool CheckBoundaries(float x, float y, float z)
3506 {
3507 return x <= boundary && x >= -boundary && y < 2000 && y >= -100 && z <= boundary && z >= -boundary;
3508 }
3509
3510 private Vector3 GetGround(Vector3 sourcePos)
3511 {
3512 if (!configData.Home.AllowAboveFoundation) return sourcePos;
3513 var newPos = sourcePos;
3514 newPos.y = TerrainMeta.HeightMap.GetHeight(newPos);
3515 sourcePos.y += .5f;
3516 RaycastHit hitinfo;
3517 var done = false;
3518 if (Physics.SphereCast(sourcePos, .1f, Vector3.down, out hitinfo, 250, groundLayer))
3519 {
3520 if ((configData.Home.AllowIceberg && hitinfo.collider.name.Contains("iceberg")) || (configData.Home.AllowCave && hitinfo.collider.name.Contains("cave_")))
3521 {
3522 sourcePos.y = hitinfo.point.y;
3523 done = true;
3524 }
3525 else
3526 {
3527 var mesh = hitinfo.collider.GetComponentInChildren<MeshCollider>();
3528 if (mesh != null && mesh.sharedMesh.name.Contains("rock_"))
3529 {
3530 sourcePos.y = hitinfo.point.y;
3531 done = true;
3532 }
3533 }
3534 }
3535 if (!configData.Home.AllowCave && Physics.SphereCast(sourcePos, .1f, Vector3.up, out hitinfo, 250, groundLayer) && hitinfo.collider.name.Contains("rock_"))
3536 {
3537 sourcePos.y = newPos.y - 10;
3538 done = true;
3539 }
3540 return done ? sourcePos : newPos;
3541 }
3542
3543 private bool GetLift(Vector3 position)
3544 {
3545 List<ProceduralLift> nearObjectsOfType = new List<ProceduralLift>();
3546 Vis.Entities<ProceduralLift>(position, 0.5f, nearObjectsOfType);
3547 if (nearObjectsOfType.Count > 0)
3548 {
3549 return true;
3550 }
3551 return false;
3552 }
3553
3554 private Vector3 GetGroundBuilding(Vector3 sourcePos)
3555 {
3556 sourcePos.y = TerrainMeta.HeightMap.GetHeight(sourcePos);
3557 RaycastHit hitinfo;
3558 if (Physics.Raycast(sourcePos, Vector3.down, out hitinfo, buildingLayer))
3559 {
3560 sourcePos.y = System.Math.Max(hitinfo.point.y, sourcePos.y);
3561 return sourcePos;
3562 }
3563 if (Physics.Raycast(sourcePos, Vector3.up, out hitinfo, buildingLayer))
3564 sourcePos.y = System.Math.Max(hitinfo.point.y, sourcePos.y);
3565 return sourcePos;
3566 }
3567
3568 private bool UnderneathFoundation(Vector3 position)
3569 {
3570 RaycastHit hit;
3571 if (Physics.Raycast(position + new Vector3(0f, 3f, 0f), Vector3.down, out hit, 5f, Layers.Mask.Construction, QueryTriggerInteraction.Ignore))
3572 {
3573 var block = hit.GetEntity() as BuildingBlock;
3574
3575 if (block.IsValid() && (block.prefabID == 72949757 || block.prefabID == 3234260181))
3576 {
3577 return hit.point.y > position.y;
3578 }
3579 }
3580 return false;
3581 }
3582
3583 private bool IsAllowed(BasePlayer player, string perm = null)
3584 {
3585 var playerAuthLevel = player.net?.connection?.authLevel;
3586
3587 var requiredAuthLevel = 3;
3588 if(configData.Admin.UseableByModerators)
3589 {
3590 requiredAuthLevel = 1;
3591 }
3592 else if(configData.Admin.UseableByAdmins)
3593 {
3594 requiredAuthLevel = 2;
3595 }
3596 if (playerAuthLevel >= requiredAuthLevel) return true;
3597
3598 return !string.IsNullOrEmpty(perm) && permission.UserHasPermission(player.UserIDString, perm);
3599 }
3600
3601 private bool IsAllowedMsg(BasePlayer player, string perm = null)
3602 {
3603 if (IsAllowed(player, perm)) return true;
3604 PrintMsg(player, "NotAllowed");
3605 return false;
3606 }
3607
3608 private int GetHigher(BasePlayer player, Dictionary<string, int> limits, int limit)
3609 {
3610 foreach (var l in limits)
3611 {
3612 if (permission.UserHasPermission(player.UserIDString, l.Key) && l.Value > limit)
3613 limit = l.Value;
3614 }
3615 return limit;
3616 }
3617
3618 private int GetLower(BasePlayer player, Dictionary<string, int> times, int time)
3619 {
3620 foreach (var l in times)
3621 {
3622 if (permission.UserHasPermission(player.UserIDString, l.Key) && l.Value < time)
3623 time = l.Value;
3624 }
3625 return time;
3626 }
3627
3628 private void CheckPerms(Dictionary<string, int> limits)
3629 {
3630 foreach (var limit in limits)
3631 {
3632 if (!permission.PermissionExists(limit.Key))
3633 permission.RegisterPermission(limit.Key, this);
3634 }
3635 }
3636
3637 #endregion
3638
3639 #region Message
3640
3641 private string _(string msgId, BasePlayer player, params object[] args)
3642 {
3643 var msg = lang.GetMessage(msgId, this, player?.UserIDString);
3644 return args.Length > 0 ? string.Format(msg, args) : msg;
3645 }
3646
3647 private void PrintMsgL(BasePlayer player, string msgId, params object[] args)
3648 {
3649 if (player == null) return;
3650 PrintMsg(player, _(msgId, player, args));
3651 }
3652
3653 private void PrintMsg(BasePlayer player, string msg)
3654 {
3655 if (player == null) return;
3656 SendReply(player, $"{configData.Settings.ChatName}{msg}");
3657 }
3658
3659 #endregion
3660
3661 #region DrawBox
3662
3663 private static void DrawBox(BasePlayer player, Vector3 center, Quaternion rotation, Vector3 size)
3664 {
3665 size = size / 2;
3666 var point1 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z + size.z), center, rotation);
3667 var point2 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z + size.z), center, rotation);
3668 var point3 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z - size.z), center, rotation);
3669 var point4 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z - size.z), center, rotation);
3670 var point5 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z + size.z), center, rotation);
3671 var point6 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z + size.z), center, rotation);
3672 var point7 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z - size.z), center, rotation);
3673 var point8 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z - size.z), center, rotation);
3674
3675 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point2);
3676 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point3);
3677 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point5);
3678 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point2);
3679 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point3);
3680 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point8);
3681
3682 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point5, point6);
3683 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point5, point7);
3684 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point6, point2);
3685 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point8, point6);
3686 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point8, point7);
3687 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point7, point3);
3688 }
3689
3690 private static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion rotation)
3691 {
3692 return rotation * (point - pivot) + pivot;
3693 }
3694
3695 #endregion
3696
3697 #region FindPlayer
3698
3699 private ulong FindPlayersSingleId(string nameOrIdOrIp, BasePlayer player)
3700 {
3701 var targets = FindPlayers(nameOrIdOrIp);
3702 if (targets.Count > 1)
3703 {
3704 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(p => p.displayName).ToArray()));
3705 return 0;
3706 }
3707 ulong userId;
3708 if (targets.Count <= 0)
3709 {
3710 if (ulong.TryParse(nameOrIdOrIp, out userId)) return userId;
3711 PrintMsgL(player, "PlayerNotFound");
3712 return 0;
3713 }
3714 else
3715 userId = targets.First().userID;
3716 return userId;
3717 }
3718
3719 private BasePlayer FindPlayersSingle(string nameOrIdOrIp, BasePlayer player)
3720 {
3721 var targets = FindPlayers(nameOrIdOrIp);
3722 if (targets.Count <= 0)
3723 {
3724 PrintMsgL(player, "PlayerNotFound");
3725 return null;
3726 }
3727 if (targets.Count > 1)
3728 {
3729 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(p => p.displayName).ToArray()));
3730 return null;
3731 }
3732 return targets.First();
3733 }
3734
3735 private static HashSet<BasePlayer> FindPlayers(string nameOrIdOrIp)
3736 {
3737 var players = new HashSet<BasePlayer>();
3738 if (string.IsNullOrEmpty(nameOrIdOrIp)) return players;
3739 foreach (var activePlayer in BasePlayer.activePlayerList)
3740 {
3741 if (activePlayer.UserIDString.Equals(nameOrIdOrIp))
3742 players.Add(activePlayer);
3743 else if (!string.IsNullOrEmpty(activePlayer.displayName) && activePlayer.displayName.Contains(nameOrIdOrIp, CompareOptions.IgnoreCase))
3744 players.Add(activePlayer);
3745 else if (activePlayer.net?.connection != null && activePlayer.net.connection.ipaddress.Equals(nameOrIdOrIp))
3746 players.Add(activePlayer);
3747 }
3748 foreach (var sleepingPlayer in BasePlayer.sleepingPlayerList)
3749 {
3750 if (sleepingPlayer.UserIDString.Equals(nameOrIdOrIp))
3751 players.Add(sleepingPlayer);
3752 else if (!string.IsNullOrEmpty(sleepingPlayer.displayName) && sleepingPlayer.displayName.Contains(nameOrIdOrIp, CompareOptions.IgnoreCase))
3753 players.Add(sleepingPlayer);
3754 }
3755 return players;
3756 }
3757
3758 private static List<BasePlayer> FindPlayersOnline(string nameOrIdOrIp)
3759 {
3760 var players = new List<BasePlayer>();
3761 if (string.IsNullOrEmpty(nameOrIdOrIp)) return players;
3762 foreach (var activePlayer in BasePlayer.activePlayerList)
3763 {
3764 if (activePlayer.UserIDString.Equals(nameOrIdOrIp))
3765 players.Add(activePlayer);
3766 else if (!string.IsNullOrEmpty(activePlayer.displayName) && activePlayer.displayName.Contains(nameOrIdOrIp, CompareOptions.IgnoreCase))
3767 players.Add(activePlayer);
3768 else if (activePlayer.net?.connection != null && activePlayer.net.connection.ipaddress.Equals(nameOrIdOrIp))
3769 players.Add(activePlayer);
3770 }
3771 return players;
3772 }
3773
3774 #endregion
3775
3776 #endregion
3777
3778 #region API
3779
3780 private Dictionary<string, Vector3> GetHomes(object playerObj)
3781 {
3782 if (playerObj == null) return null;
3783 if (playerObj is string)
3784 playerObj = Convert.ToUInt64(playerObj);
3785 if (!(playerObj is ulong))
3786 throw new ArgumentException("playerObj");
3787 var playerId = (ulong)playerObj;
3788 HomeData homeData;
3789 if (!Home.TryGetValue(playerId, out homeData) || homeData.Locations.Count == 0)
3790 return null;
3791 return homeData.Locations;
3792 }
3793
3794 private int GetLimitRemaining(BasePlayer player, string type)
3795 {
3796 if (player == null || string.IsNullOrEmpty(type)) return 0;
3797 var currentDate = DateTime.Now.ToString("d");
3798 int limit;
3799 var remaining = -1;
3800 switch (type.ToLower())
3801 {
3802 case "home":
3803 limit = GetHigher(player, configData.Home.VIPDailyLimits, configData.Home.DailyLimit);
3804 HomeData homeData;
3805 if (!Home.TryGetValue(player.userID, out homeData))
3806 Home[player.userID] = homeData = new HomeData();
3807 if (homeData.Teleports.Date != currentDate)
3808 {
3809 homeData.Teleports.Amount = 0;
3810 homeData.Teleports.Date = currentDate;
3811 }
3812 if (limit > 0)
3813 remaining = limit - homeData.Teleports.Amount;
3814 break;
3815 case "town":
3816 limit = GetHigher(player, configData.Town.VIPDailyLimits, configData.Town.DailyLimit);
3817 TeleportData townData;
3818 if (!Town.TryGetValue(player.userID, out townData))
3819 Town[player.userID] = townData = new TeleportData();
3820 if (townData.Date != currentDate)
3821 {
3822 townData.Amount = 0;
3823 townData.Date = currentDate;
3824 }
3825 if (limit > 0)
3826 remaining = limit - townData.Amount;
3827 break;
3828 case "tpr":
3829 limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
3830 TeleportData tprData;
3831 if (!TPR.TryGetValue(player.userID, out tprData))
3832 TPR[player.userID] = tprData = new TeleportData();
3833 if (tprData.Date != currentDate)
3834 {
3835 tprData.Amount = 0;
3836 tprData.Date = currentDate;
3837 }
3838 if (limit > 0)
3839 remaining = limit - tprData.Amount;
3840 break;
3841 }
3842 return remaining;
3843 }
3844
3845 private int GetCooldownRemaining(BasePlayer player, string type)
3846 {
3847 if (player == null || string.IsNullOrEmpty(type)) return 0;
3848 var currentDate = DateTime.Now.ToString("d");
3849 var timestamp = Facepunch.Math.Epoch.Current;
3850 int cooldown;
3851 var remaining = -1;
3852 switch (type.ToLower())
3853 {
3854 case "home":
3855 cooldown = GetLower(player, configData.Home.VIPCooldowns, configData.Home.Cooldown);
3856 HomeData homeData;
3857 if (!Home.TryGetValue(player.userID, out homeData))
3858 Home[player.userID] = homeData = new HomeData();
3859 if (homeData.Teleports.Date != currentDate)
3860 {
3861 homeData.Teleports.Amount = 0;
3862 homeData.Teleports.Date = currentDate;
3863 }
3864 if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
3865 remaining = cooldown - (timestamp - homeData.Teleports.Timestamp);
3866 break;
3867 case "town":
3868 cooldown = GetLower(player, configData.Town.VIPCooldowns, configData.Town.Cooldown);
3869 TeleportData townData;
3870 if (!Town.TryGetValue(player.userID, out townData))
3871 Town[player.userID] = townData = new TeleportData();
3872 if (townData.Date != currentDate)
3873 {
3874 townData.Amount = 0;
3875 townData.Date = currentDate;
3876 }
3877 if (cooldown > 0 && timestamp - townData.Timestamp < cooldown)
3878 remaining = cooldown - (timestamp - townData.Timestamp);
3879 break;
3880 case "tpr":
3881 cooldown = GetLower(player, configData.TPR.VIPCooldowns, configData.TPR.Cooldown);
3882 TeleportData tprData;
3883 if (!TPR.TryGetValue(player.userID, out tprData))
3884 TPR[player.userID] = tprData = new TeleportData();
3885 if (tprData.Date != currentDate)
3886 {
3887 tprData.Amount = 0;
3888 tprData.Date = currentDate;
3889 }
3890 if (cooldown > 0 && timestamp - tprData.Timestamp < cooldown)
3891 remaining = cooldown - (timestamp - tprData.Timestamp);
3892 break;
3893 }
3894 return remaining;
3895 }
3896
3897 #endregion
3898
3899 private class UnityVector3Converter : JsonConverter
3900 {
3901 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
3902 {
3903 var vector = (Vector3)value;
3904 writer.WriteValue($"{vector.x} {vector.y} {vector.z}");
3905 }
3906
3907 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
3908 {
3909 if (reader.TokenType == JsonToken.String)
3910 {
3911 var values = reader.Value.ToString().Trim().Split(' ');
3912 return new Vector3(Convert.ToSingle(values[0]), Convert.ToSingle(values[1]), Convert.ToSingle(values[2]));
3913 }
3914 var o = JObject.Load(reader);
3915 return new Vector3(Convert.ToSingle(o["x"]), Convert.ToSingle(o["y"]), Convert.ToSingle(o["z"]));
3916 }
3917
3918 public override bool CanConvert(Type objectType)
3919 {
3920 return objectType == typeof(Vector3);
3921 }
3922 }
3923
3924 private class CustomComparerDictionaryCreationConverter<T> : CustomCreationConverter<IDictionary>
3925 {
3926 private readonly IEqualityComparer<T> comparer;
3927
3928 public CustomComparerDictionaryCreationConverter(IEqualityComparer<T> comparer)
3929 {
3930 if (comparer == null)
3931 throw new ArgumentNullException(nameof(comparer));
3932 this.comparer = comparer;
3933 }
3934
3935 public override bool CanConvert(Type objectType)
3936 {
3937 return HasCompatibleInterface(objectType) && HasCompatibleConstructor(objectType);
3938 }
3939
3940 private static bool HasCompatibleInterface(Type objectType)
3941 {
3942 return objectType.GetInterfaces().Where(i => HasGenericTypeDefinition(i, typeof(IDictionary<,>))).Any(i => typeof(T).IsAssignableFrom(i.GetGenericArguments().First()));
3943 }
3944
3945 private static bool HasGenericTypeDefinition(Type objectType, Type typeDefinition)
3946 {
3947 return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeDefinition;
3948 }
3949
3950 private static bool HasCompatibleConstructor(Type objectType)
3951 {
3952 return objectType.GetConstructor(new[] { typeof(IEqualityComparer<T>) }) != null;
3953 }
3954
3955 public override IDictionary Create(Type objectType)
3956 {
3957 return Activator.CreateInstance(objectType, comparer) as IDictionary;
3958 }
3959 }
3960
3961 // FIXME
3962 [HookMethod("SendHelpText")]
3963 private void SendHelpText(BasePlayer player)
3964 {
3965 PrintMsgL(player, "<size=14>NTeleportation</size> by <color=#ce422b>RFC1920</color>\n<color=#ffd479>/sethome NAME</color> - Set home on current foundation\n<color=#ffd479>/home NAME</color> - Go to one of your homes\n<color=#ffd479>/home list</color> - List your homes\n<color=#ffd479>/town</color> - Go to town, if set\n/tpb - Go back to previous location\n/tpr PLAYER - Request teleport to PLAYER\n/tpa - Accept teleport request");
3966 }
3967 }
3968}
3969