· 5 years ago · Jul 15, 2020, 03:40 PM
1//#define DEBUG
2using System;
3using System.Collections;
4using System.Collections.Generic;
5using System.Globalization;
6using System.Linq;
7using Facepunch;
8using Newtonsoft.Json;
9using Newtonsoft.Json.Converters;
10using Newtonsoft.Json.Linq;
11using Oxide.Core;
12using Oxide.Core.Configuration;
13using Oxide.Core.Plugins;
14using Oxide.Game.Rust;
15using Rust;
16using UnityEngine;
17using System.Reflection;
18using Oxide.Core.Libraries.Covalence;
19using Network;
20
21namespace Oxide.Plugins
22{
23 [Info("NTeleportation", "Author Nogrod, Maintainer nivex", "1.3.7")]
24 class NTeleportation : RustPlugin
25 {
26 private bool newSave;
27 private string banditPrefab;
28 private string outpostPrefab;
29 private const bool True = true;
30 private const bool False = false;
31 private Vector3 Zero = default(Vector3);
32 private readonly Vector3 Up = Vector3.up;
33 private readonly Vector3 Down = Vector3.down;
34 private const string NewLine = "\n";
35 private const string ConfigDefaultPermVip = "nteleportation.vip";
36 private const string PermHome = "nteleportation.home";
37 private const string PermTpR = "nteleportation.tpr";
38 private const string PermTpT = "nteleportation.tpt";
39 private const string PermDeleteHome = "nteleportation.deletehome";
40 private const string PermHomeHomes = "nteleportation.homehomes";
41 private const string PermImportHomes = "nteleportation.importhomes";
42 private const string PermRadiusHome = "nteleportation.radiushome";
43 private const string PermTp = "nteleportation.tp";
44 private const string PermTpB = "nteleportation.tpb";
45 private const string PermTpConsole = "nteleportation.tpconsole";
46 private const string PermTpHome = "nteleportation.tphome";
47 private const string PermTpTown = "nteleportation.tptown";
48 private const string PermTpOutpost = "nteleportation.tpoutpost";
49 private const string PermTpBandit = "nteleportation.tpbandit";
50 private const string PermTpN = "nteleportation.tpn";
51 private const string PermTpL = "nteleportation.tpl";
52 private const string PermTpRemove = "nteleportation.tpremove";
53 private const string PermTpSave = "nteleportation.tpsave";
54 private const string PermWipeHomes = "nteleportation.wipehomes";
55 private const string PermCraftHome = "nteleportation.crafthome";
56 private const string PermCraftTown = "nteleportation.crafttown";
57 private const string PermCraftOutpost = "nteleportation.craftoutpost";
58 private const string PermCraftBandit = "nteleportation.craftbandit";
59 private const string PermCraftTpR = "nteleportation.crafttpr";
60 private DynamicConfigFile dataConvert;
61 private DynamicConfigFile dataDisabled;
62 private DynamicConfigFile dataAdmin;
63 private DynamicConfigFile dataHome;
64 private DynamicConfigFile dataTPR;
65 private DynamicConfigFile dataTPT;
66 private DynamicConfigFile dataTown;
67 private DynamicConfigFile dataOutpost;
68 private DynamicConfigFile dataBandit;
69 private Dictionary<ulong, AdminData> Admin;
70 private Dictionary<ulong, HomeData> Home;
71 private Dictionary<ulong, TeleportData> TPR;
72 private Dictionary<string, List<string>> TPT;
73 private Dictionary<ulong, TeleportData> Town;
74 private Dictionary<ulong, TeleportData> Outpost;
75 private Dictionary<ulong, TeleportData> Bandit;
76 private bool changedAdmin;
77 private bool changedHome;
78 private bool changedTPR;
79 private bool changedTPT;
80 private bool changedTown;
81 private bool changedOutpost;
82 private bool changedBandit;
83 private float boundary;
84 private readonly int triggerLayer = LayerMask.GetMask("Trigger");
85 private readonly int groundLayer = LayerMask.GetMask("Terrain", "World");
86 private int buildingLayer { get; set; } = LayerMask.GetMask("Terrain", "World", "Construction", "Deployed");
87 private readonly int blockLayer = LayerMask.GetMask("Construction");
88 private readonly int deployedLayer = LayerMask.GetMask("Deployed");
89 private readonly Dictionary<ulong, TeleportTimer> TeleportTimers = new Dictionary<ulong, TeleportTimer>();
90 private readonly Dictionary<ulong, Timer> PendingRequests = new Dictionary<ulong, Timer>();
91 private readonly Dictionary<ulong, BasePlayer> PlayersRequests = new Dictionary<ulong, BasePlayer>();
92 private readonly Dictionary<int, string> ReverseBlockedItems = new Dictionary<int, string>();
93 private readonly Dictionary<ulong, Vector3> teleporting = new Dictionary<ulong, Vector3>();
94 private SortedDictionary<string, Vector3> caves = new SortedDictionary<string, Vector3>();
95 private SortedDictionary<string, MonInfo> monuments = new SortedDictionary<string, MonInfo>();
96 private bool outpostEnabled;
97 private string OutpostTPDisabledMessage = "OutpostTPDisabled";
98 private bool banditEnabled;
99 private string BanditTPDisabledMessage = "BanditTPDisabled";
100
101 [PluginReference]
102 private Plugin Clans, Economics, ServerRewards, Friends, CompoundTeleport, ZoneManager, NoEscape, Vanish;
103
104 class MonInfo
105 {
106 public Vector3 Position;
107 public float Radius;
108 }
109
110 #region Configuration
111
112 private static Configuration config;
113
114 public class InterruptSettings
115 {
116 [JsonProperty(PropertyName = "Above Water")]
117 public bool AboveWater { get; set; } = True;
118
119 [JsonProperty(PropertyName = "Balloon")]
120 public bool Balloon { get; set; } = True;
121
122 [JsonProperty(PropertyName = "Cargo Ship")]
123 public bool Cargo { get; set; } = True;
124
125 [JsonProperty(PropertyName = "Cold")]
126 public bool Cold { get; set; } = False;
127
128 [JsonProperty(PropertyName = "Excavator")]
129 public bool Excavator { get; set; } = False;
130
131 [JsonProperty(PropertyName = "Hot")]
132 public bool Hot { get; set; } = False;
133
134 [JsonProperty(PropertyName = "Hostile")]
135 public bool Hostile { get; set; } = False;
136
137 [JsonProperty(PropertyName = "Hurt")]
138 public bool Hurt { get; set; } = True;
139
140 [JsonProperty(PropertyName = "Lift")]
141 public bool Lift { get; set; } = True;
142
143 [JsonProperty(PropertyName = "Monument")]
144 public bool Monument { get; set; } = False;
145
146 [JsonProperty(PropertyName = "Mounted")]
147 public bool Mounted { get; set; } = True;
148
149 [JsonProperty(PropertyName = "Oil Rig")]
150 public bool Oilrig { get; set; } = False;
151
152 [JsonProperty(PropertyName = "Safe Zone")]
153 public bool Safe { get; set; } = True;
154
155 [JsonProperty(PropertyName = "Swimming")]
156 public bool Swimming { get; set; } = False;
157 }
158
159 public class PluginSettings
160 {
161 [JsonProperty(PropertyName = "Interrupt TP")]
162 public InterruptSettings Interrupt { get; set; } = new InterruptSettings();
163
164 [JsonProperty(PropertyName = "Block Teleport (NoEscape)")]
165 public bool BlockNoEscape { get; set; } = False;
166
167 [JsonProperty(PropertyName = "Block Teleport (ZoneManager)")]
168 public bool BlockZoneFlag { get; set; } = False;
169
170 [JsonProperty(PropertyName = "Chat Name")]
171 public string ChatName { get; set; } = "<color=red>Teleportation</color>: ";
172
173 [JsonProperty(PropertyName = "Chat Steam64ID")]
174 public ulong ChatID { get; set; } = 76561199056025689;
175
176 [JsonProperty(PropertyName = "Check Boundaries On Teleport X Y Z")]
177 public bool CheckBoundaries { get; set; } = True;
178
179 [JsonProperty(PropertyName = "Draw Sphere On Set Home")]
180 public bool DrawHomeSphere { get; set; } = True;
181
182 [JsonProperty(PropertyName = "Homes Enabled")]
183 public bool HomesEnabled { get; set; } = True;
184
185 [JsonProperty(PropertyName = "TPR Enabled")]
186 public bool TPREnabled { get; set; } = True;
187
188 [JsonProperty(PropertyName = "Town Enabled")]
189 public bool TownEnabled { get; set; } = True;
190
191 [JsonProperty(PropertyName = "Outpost Enabled")]
192 public bool OutpostEnabled { get; set; } = True;
193
194 [JsonProperty(PropertyName = "Bandit Enabled")]
195 public bool BanditEnabled { get; set; } = True;
196
197 [JsonProperty(PropertyName = "Strict Foundation Check")]
198 public bool StrictFoundationCheck { get; set; } = False;
199
200 [JsonProperty(PropertyName = "Cave Distance Small")]
201 public float CaveDistanceSmall { get; set; } = 50f;
202
203 [JsonProperty(PropertyName = "Cave Distance Medium")]
204 public float CaveDistanceMedium { get; set; } = 70f;
205
206 [JsonProperty(PropertyName = "Cave Distance Large")]
207 public float CaveDistanceLarge { get; set; } = 110f;
208
209 [JsonProperty(PropertyName = "Default Monument Size")]
210 public float DefaultMonumentSize { get; set; } = 50f;
211
212 [JsonProperty(PropertyName = "Minimum Temp")]
213 public float MinimumTemp { get; set; } = 0f;
214
215 [JsonProperty(PropertyName = "Maximum Temp")]
216 public float MaximumTemp { get; set; } = 40f;
217
218 [JsonProperty(PropertyName = "Blocked Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
219 public Dictionary<string, string> BlockedItems { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
220
221 [JsonProperty(PropertyName = "Bypass CMD")]
222 public string BypassCMD { get; set; } = "pay";
223
224 [JsonProperty(PropertyName = "Use Economics")]
225 public bool UseEconomics { get; set; } = False;
226
227 [JsonProperty(PropertyName = "Use Server Rewards")]
228 public bool UseServerRewards { get; set; } = False;
229
230 [JsonProperty(PropertyName = "Wipe On Upgrade Or Change")]
231 public bool WipeOnUpgradeOrChange { get; set; } = False;
232
233 [JsonProperty(PropertyName = "Auto Generate Outpost Location")]
234 public bool AutoGenOutpost { get; set; } = False;
235
236 [JsonProperty(PropertyName = "Auto Generate Bandit Location")]
237 public bool AutoGenBandit { get; set; } = False;
238 }
239
240 public class AdminSettings
241 {
242 [JsonProperty(PropertyName = "Announce Teleport To Target")]
243 public bool AnnounceTeleportToTarget { get; set; } = False;
244
245 [JsonProperty(PropertyName = "Usable By Admins")]
246 public bool UseableByAdmins { get; set; } = True;
247
248 [JsonProperty(PropertyName = "Usable By Moderators")]
249 public bool UseableByModerators { get; set; } = True;
250
251 [JsonProperty(PropertyName = "Location Radius")]
252 public int LocationRadius { get; set; } = 25;
253
254 [JsonProperty(PropertyName = "Teleport Near Default Distance")]
255 public int TeleportNearDefaultDistance { get; set; } = 30;
256 }
257
258 public class HomesSettings
259 {
260 [JsonProperty(PropertyName = "Homes Limit")]
261 public int HomesLimit { get; set; } = 2;
262
263 [JsonProperty(PropertyName = "VIP Homes Limits", ObjectCreationHandling = ObjectCreationHandling.Replace)]
264 public Dictionary<string, int> VIPHomesLimits { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
265
266 [JsonProperty(PropertyName = "Cooldown")]
267 public int Cooldown { get; set; } = 600;
268
269 [JsonProperty(PropertyName = "Countdown")]
270 public int Countdown { get; set; } = 15;
271
272 [JsonProperty(PropertyName = "Daily Limit")]
273 public int DailyLimit { get; set; } = 5;
274
275 [JsonProperty(PropertyName = "VIP Daily Limits", ObjectCreationHandling = ObjectCreationHandling.Replace)]
276 public Dictionary<string, int> VIPDailyLimits { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
277
278 [JsonProperty(PropertyName = "VIP Cooldowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
279 public Dictionary<string, int> VIPCooldowns { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
280
281 [JsonProperty(PropertyName = "VIP Countdowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
282 public Dictionary<string, int> VIPCountdowns { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
283
284 [JsonProperty(PropertyName = "Location Radius")]
285 public int LocationRadius { get; set; } = 25;
286
287 [JsonProperty(PropertyName = "Force On Top Of Foundation")]
288 public bool ForceOnTopOfFoundation { get; set; } = True;
289
290 [JsonProperty(PropertyName = "Check Foundation For Owner")]
291 public bool CheckFoundationForOwner { get; set; } = True;
292
293 [JsonProperty(PropertyName = "Use Friends")]
294 public bool UseFriends { get; set; } = True;
295
296 [JsonProperty(PropertyName = "Use Clans")]
297 public bool UseClans { get; set; } = True;
298
299 [JsonProperty(PropertyName = "Use Teams")]
300 public bool UseTeams { get; set; } = True;
301
302 [JsonProperty(PropertyName = "Usable Out Of Building Blocked")]
303 public bool UsableOutOfBuildingBlocked { get; set; } = False;
304
305 [JsonProperty(PropertyName = "Usable Into Building Blocked")]
306 public bool UsableIntoBuildingBlocked { get; set; } = False;
307
308 [JsonProperty(PropertyName = "Allow Cupboard Owner When Building Blocked")]
309 public bool CupOwnerAllowOnBuildingBlocked { get; set; } = True;
310
311 [JsonProperty(PropertyName = "Allow Iceberg")]
312 public bool AllowIceberg { get; set; } = False;
313
314 [JsonProperty(PropertyName = "Allow Cave")]
315 public bool AllowCave { get; set; } = False;
316
317 [JsonProperty(PropertyName = "Allow Crafting")]
318 public bool AllowCraft { get; set; } = False;
319
320 [JsonProperty(PropertyName = "Allow Above Foundation")]
321 public bool AllowAboveFoundation { get; set; } = True;
322
323 [JsonProperty(PropertyName = "Check If Home Is Valid On Listhomes")]
324 public bool CheckValidOnList { get; set; } = False;
325
326 [JsonProperty(PropertyName = "Pay")]
327 public int Pay { get; set; } = 0;
328
329 [JsonProperty(PropertyName = "Bypass")]
330 public int Bypass { get; set; } = 0;
331 }
332
333 public class TPTSettings
334 {
335 [JsonProperty(PropertyName = "Use Friends")]
336 public bool UseFriends { get; set; }
337
338 [JsonProperty(PropertyName = "Use Clans")]
339 public bool UseClans { get; set; }
340
341 [JsonProperty(PropertyName = "Use Teams")]
342 public bool UseTeams { get; set; }
343
344 [JsonProperty(PropertyName = "Allow Cave")]
345 public bool AllowCave { get; set; }
346 }
347
348 public class TPRSettings
349 {
350 [JsonProperty(PropertyName = "Allow Cave")]
351 public bool AllowCave { get; set; } = False;
352
353 [JsonProperty(PropertyName = "Allow TPB")]
354 public bool AllowTPB { get; set; } = True;
355
356 [JsonProperty(PropertyName = "Cooldown")]
357 public int Cooldown { get; set; } = 600;
358
359 [JsonProperty(PropertyName = "Countdown")]
360 public int Countdown { get; set; } = 15;
361
362 [JsonProperty(PropertyName = "Daily Limit")]
363 public int DailyLimit { get; set; } = 5;
364
365 [JsonProperty(PropertyName = "VIP Daily Limits", ObjectCreationHandling = ObjectCreationHandling.Replace)]
366 public Dictionary<string, int> VIPDailyLimits { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
367
368 [JsonProperty(PropertyName = "VIP Cooldowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
369 public Dictionary<string, int> VIPCooldowns { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
370
371 [JsonProperty(PropertyName = "VIP Countdowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
372 public Dictionary<string, int> VIPCountdowns { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
373
374 [JsonProperty(PropertyName = "Request Duration")]
375 public int RequestDuration { get; set; } = 30;
376
377 [JsonProperty(PropertyName = "Block TPA On Ceiling")]
378 public bool BlockTPAOnCeiling { get; set; } = True;
379
380 [JsonProperty(PropertyName = "Usable Out Of Building Blocked")]
381 public bool UsableOutOfBuildingBlocked { get; set; } = False;
382
383 [JsonProperty(PropertyName = "Usable Into Building Blocked")]
384 public bool UsableIntoBuildingBlocked { get; set; } = False;
385
386 [JsonProperty(PropertyName = "Allow Cupboard Owner When Building Blocked")]
387 public bool CupOwnerAllowOnBuildingBlocked { get; set; } = True;
388
389 [JsonProperty(PropertyName = "Allow Crafting")]
390 public bool AllowCraft { get; set; } = False;
391
392 [JsonProperty(PropertyName = "Pay")]
393 public int Pay { get; set; } = 0;
394
395 [JsonProperty(PropertyName = "Bypass")]
396 public int Bypass { get; set; } = 0;
397 }
398
399 public class TownSettings
400 {
401 [JsonProperty(PropertyName = "Allow Cave")]
402 public bool AllowCave { get; set; } = False;
403
404 [JsonProperty(PropertyName = "Cooldown")]
405 public int Cooldown { get; set; } = 600;
406
407 [JsonProperty(PropertyName = "Countdown")]
408 public int Countdown { get; set; } = 15;
409
410 [JsonProperty(PropertyName = "Daily Limit")]
411 public int DailyLimit { get; set; } = 5;
412
413 [JsonProperty(PropertyName = "VIP Daily Limits", ObjectCreationHandling = ObjectCreationHandling.Replace)]
414 public Dictionary<string, int> VIPDailyLimits { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
415
416 [JsonProperty(PropertyName = "VIP Cooldowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
417 public Dictionary<string, int> VIPCooldowns { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
418
419 [JsonProperty(PropertyName = "VIP Countdowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
420 public Dictionary<string, int> VIPCountdowns { get; set; } = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
421
422 [JsonProperty(PropertyName = "Location")]
423 public Vector3 Location { get; set; } = Vector3.zero;
424
425 [JsonProperty(PropertyName = "Usable Out Of Building Blocked")]
426 public bool UsableOutOfBuildingBlocked { get; set; } = False;
427
428 [JsonProperty(PropertyName = "Allow Crafting")]
429 public bool AllowCraft { get; set; } = False;
430
431 [JsonProperty(PropertyName = "Pay")]
432 public int Pay { get; set; } = 0;
433
434 [JsonProperty(PropertyName = "Bypass")]
435 public int Bypass { get; set; } = 0;
436 }
437
438 private class Configuration
439 {
440 [JsonProperty(PropertyName = "Settings")]
441 public PluginSettings Settings = new PluginSettings();
442
443 [JsonProperty(PropertyName = "Admin")]
444 public AdminSettings Admin = new AdminSettings();
445
446 [JsonProperty(PropertyName = "Home")]
447 public HomesSettings Home = new HomesSettings();
448
449 [JsonProperty(PropertyName = "TPT")]
450 public TPTSettings TPT = new TPTSettings();
451
452 [JsonProperty(PropertyName = "TPR")]
453 public TPRSettings TPR = new TPRSettings();
454
455 [JsonProperty(PropertyName = "Town")]
456 public TownSettings Town = new TownSettings();
457
458 [JsonProperty(PropertyName = "Outpost")]
459 public TownSettings Outpost = new TownSettings();
460
461 [JsonProperty(PropertyName = "Bandit")]
462 public TownSettings Bandit = new TownSettings();
463 }
464
465 protected override void LoadConfig()
466 {
467 base.LoadConfig();
468
469 try
470 {
471 Config.Settings.Converters = new JsonConverter[] { new UnityVector3Converter() };
472 config = Config.ReadObject<Configuration>();
473 if (config == null) throw new Exception();
474 }
475 catch
476 {
477 PrintError("Your configuration file contains an error. Using default configuration values.");
478 LoadDefaultConfig();
479 }
480
481 SaveConfig();
482 }
483
484 protected override void SaveConfig() => Config.WriteObject(config);
485
486 protected override void LoadDefaultConfig()
487 {
488 config = new Configuration();
489 Puts("Loaded default configuration.");
490 }
491
492 #endregion
493
494 class DisabledData
495 {
496 [JsonProperty("List of disabled commands")]
497 public List<string> DisabledCommands = new List<string>();
498
499 public DisabledData() { }
500 }
501
502 DisabledData DisabledTPT = new DisabledData();
503
504 class AdminData
505 {
506 [JsonProperty("pl")]
507 public Vector3 PreviousLocation { get; set; }
508
509 [JsonProperty("l")]
510 public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
511 }
512
513 class HomeData
514 {
515 [JsonProperty("l")]
516 public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
517
518 [JsonProperty("t")]
519 public TeleportData Teleports { get; set; } = new TeleportData();
520 }
521
522 class TeleportData
523 {
524 [JsonProperty("a")]
525 public int Amount { get; set; }
526
527 [JsonProperty("d")]
528 public string Date { get; set; }
529
530 [JsonProperty("t")]
531 public int Timestamp { get; set; }
532 }
533
534 class TeleportTimer
535 {
536 public Timer Timer { get; set; }
537 public BasePlayer OriginPlayer { get; set; }
538 public BasePlayer TargetPlayer { get; set; }
539 }
540
541 private enum checkmode
542 {
543 home, tpr, tpa, town
544 };
545
546 protected override void LoadDefaultMessages()
547 {
548 lang.RegisterMessages(new Dictionary<string, string>
549 {
550 {"AdminTP", "You teleported to {0}!"},
551 {"AdminTPTarget", "{0} teleported to you!"},
552 {"AdminTPPlayers", "You teleported {0} to {1}!"},
553 {"AdminTPPlayer", "{0} teleported you to {1}!"},
554 {"AdminTPPlayerTarget", "{0} teleported {1} to you!"},
555 {"AdminTPCoordinates", "You teleported to {0}!"},
556 {"AdminTPTargetCoordinates", "You teleported {0} to {1}!"},
557 {"AdminTPOutOfBounds", "You tried to teleport to a set of coordinates outside the map boundaries!"},
558 {"AdminTPBoundaries", "X and Z values need to be between -{0} and {0} while the Y value needs to be between -100 and 2000!"},
559 {"AdminTPLocation", "You teleported to {0}!"},
560 {"AdminTPLocationSave", "You have saved the current location!"},
561 {"AdminTPLocationRemove", "You have removed the location {0}!"},
562 {"AdminLocationList", "The following locations are available:"},
563 {"AdminLocationListEmpty", "You haven't saved any locations!"},
564 {"AdminTPBack", "You've teleported back to your previous location!"},
565 {"AdminTPBackSave", "Your previous location has been saved, use <color=yellow>/tpb</color> to teleport back!"},
566 {"AdminTPTargetCoordinatesTarget", "{0} teleported you to {1}!"},
567 {"AdminTPConsoleTP", "You were teleported to {0}"},
568 {"AdminTPConsoleTPPlayer", "You were teleported to {0}"},
569 {"AdminTPConsoleTPPlayerTarget", "{0} was teleported to you!"},
570 {"HomeTP", "You teleported to your home '{0}'!"},
571 {"HomeAdminTP", "You teleported to {0}'s home '{1}'!"},
572 {"HomeSave", "You have saved the current location as your home!"},
573 {"HomeNoFoundation", "You can only use a home location on a foundation!"},
574 {"HomeFoundationNotOwned", "You can't use home on someone else's house."},
575 {"HomeFoundationUnderneathFoundation", "You can't use home on a foundation that is underneath another foundation."},
576 {"HomeFoundationNotFriendsOwned", "You or a friend need to own the house to use home!"},
577 {"HomeRemovedInvalid", "Your home '{0}' was removed because not on a foundation or not owned!"},
578 {"HighWallCollision", "High Wall Collision!"},
579 {"HomeRemovedInsideBlock", "Your home '{0}' was removed because inside a foundation!"},
580 {"HomeRemove", "You have removed your home {0}!"},
581 {"HomeDelete", "You have removed {0}'s home '{1}'!"},
582 {"HomeList", "The following homes are available:"},
583 {"HomeListEmpty", "You haven't saved any homes!"},
584 {"HomeMaxLocations", "Unable to set your home here, you have reached the maximum of {0} homes!"},
585 {"HomeQuota", "You have set {0} of the maximum {1} homes!"},
586 {"HomeTPStarted", "Teleporting to your home {0} in {1} seconds!"},
587 {"PayToHome", "Standard payment of {0} applies to all home teleports!"},
588 {"PayToTown", "Standard payment of {0} applies to all town teleports!"},
589 {"PayToTPR", "Standard payment of {0} applies to all tprs!"},
590 {"HomeTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
591 {"HomeTPCooldownBypass", "Your teleport was currently on cooldown. You chose to bypass that by paying {0} from your balance."},
592 {"HomeTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
593 {"HomeTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
594 {"HomeTPCooldownBypassP2", "Type <color=yellow>/home NAME {0}</color>." },
595 {"HomeTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
596 {"HomeTPAmount", "You have {0} home teleports left today!"},
597 {"HomesListWiped", "You have wiped all the saved home locations!"},
598 {"HomeTPBuildingBlocked", "You can't set your home if you are not allowed to build in this zone!"},
599 {"HomeTPSwimming", "You can't set your home while swimming!"},
600 {"HomeTPCrafting", "You can't set your home while crafting!"},
601 {"Request", "You've requested a teleport to {0}!"},
602 {"RequestTarget", "{0} requested to be teleported to you! Use '<color=yellow>/tpa</color>' to accept!"},
603 {"PendingRequest", "You already have a request pending, cancel that request or wait until it gets accepted or times out!"},
604 {"PendingRequestTarget", "The player you wish to teleport to already has a pending request, try again later!"},
605 {"NoPendingRequest", "You have no pending teleport request!"},
606 {"AcceptOnRoof", "You can't accept a teleport while you're on a ceiling, get to ground level!"},
607 {"Accept", "{0} has accepted your teleport request! Teleporting in {1} seconds!"},
608 {"AcceptTarget", "You've accepted the teleport request of {0}!"},
609 {"NotAllowed", "You are not allowed to use this command!"},
610 {"Success", "You teleported to {0}!"},
611 {"SuccessTarget", "{0} teleported to you!"},
612 {"Cancelled", "Your teleport request to {0} was cancelled!"},
613 {"CancelledTarget", "{0} teleport request was cancelled!"},
614 {"TPCancelled", "Your teleport was cancelled!"},
615 {"TPCancelledTarget", "{0} cancelled teleport!"},
616 {"TPYouCancelledTarget", "You cancelled {0} teleport!"},
617 {"TimedOut", "{0} did not answer your request in time!"},
618 {"TimedOutTarget", "You did not answer {0}'s teleport request in time!"},
619 {"TargetDisconnected", "{0} has disconnected, your teleport was cancelled!"},
620 {"TPRCooldown", "Your teleport requests are currently on cooldown. You'll have to wait {0} to send your next teleport request."},
621 {"TPRCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
622 {"TPRCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
623 {"TPRCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
624 {"TPMoney", "{0} deducted from your account!"},
625 {"TPNoMoney", "You do not have {0} in any account!"},
626 {"TPRCooldownBypassP2", "Type <color=yellow>/tpr {0}</color>." },
627 {"TPRCooldownBypassP2a", "Type <color=yellow>/tpr NAME {0}</color>." },
628 {"TPRLimitReached", "You have reached the daily limit of {0} teleport requests today!"},
629 {"TPRAmount", "You have {0} teleport requests left today!"},
630 {"TPRTarget", "Your target is currently not available!"},
631 {"TPDead", "You can't teleport while being dead!"},
632 {"TPWounded", "You can't teleport while wounded!"},
633 {"TPTooCold", "You're too cold to teleport!"},
634 {"TPTooHot", "You're too hot to teleport!"},
635 {"TPHostile", "Can't teleport to outpost or bandit when hostile!"},
636 {"HostileTimer", "Teleport available in {0} minutes."},
637 {"TPMounted", "You can't teleport while seated!"},
638 {"TPBuildingBlocked", "You can't teleport while in a building blocked zone!"},
639 {"TPAboveWater", "You can't teleport while above water!"},
640 {"TPTargetBuildingBlocked", "You can't teleport in a building blocked zone!"},
641 {"TPTargetInsideBlock", "You can't teleport into a foundation!"},
642 {"TPSwimming", "You can't teleport while swimming!"},
643 {"TPCargoShip", "You can't teleport from the cargo ship!"},
644 {"TPOilRig", "You can't teleport from the oil rig!"},
645 {"TPExcavator", "You can't teleport from the excavator!"},
646 {"TPHotAirBalloon", "You can't teleport to or from a hot air balloon!"},
647 {"TPLift", "You can't teleport while in an elevator or bucket lift!"},
648 {"TPBucketLift", "You can't teleport while in a bucket lift!"},
649 {"TPRegLift", "You can't teleport while in an elevator!"},
650 {"TPSafeZone", "You can't teleport from a safezone!"},
651 {"TPFlagZone", "You can't teleport from this zone!"},
652 {"TPNoEscapeBlocked", "You can't teleport while blocked!"},
653 {"TPCrafting", "You can't teleport while crafting!"},
654 {"TPBlockedItem", "You can't teleport while carrying: {0}!"},
655 {"TooCloseToMon", "You can't teleport so close to the {0}!"},
656 {"TooCloseToCave", "You can't teleport so close to a cave!"},
657 {"HomeTooCloseToCave", "You can't set home so close to a cave!"},
658 {"TownTP", "You teleported to town!"},
659 {"TownTPNotSet", "Town is currently not set!"},
660 {"TownTPDisabled", "Town is currently not enabled!"},
661 {"TownTPLocation", "You have set the town location to {0}!"},
662 {"TownTPStarted", "Teleporting to town in {0} seconds!"},
663 {"TownTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
664 {"TownTPCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
665 {"TownTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
666 {"TownTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
667 {"TownTPCooldownBypassP2", "Type <color=yellow>/town {0}</color>." },
668 {"TownTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
669 {"TownTPAmount", "You have {0} town teleports left today!"},
670
671 {"OutpostTP", "You teleported to the outpost!"},
672 {"OutpostTPNotSet", "Outpost is currently not set!"},
673 {"OutpostTPDisabled", "Outpost is currently not enabled!"},
674 {"OutpostTPDisabledConfig", "Outpost is currently not enabled because it isn't enabled in the config"},
675 {"OutpostTPDisabledNoLocation", "Outpost is currently not enabled, location is not set and auto generation is disabled!"},
676 {"OutpostTPDisabledNoLocationAutoGen", "Outpost is currently not enabled because auto generation failed!"},
677 {"OutpostTPLocation", "You have set the outpost location to {0}!"},
678 {"OutpostTPStarted", "Teleporting to the outpost in {0} seconds!"},
679 {"OutpostTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
680 {"OutpostTPCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
681 {"OutpostTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
682 {"OutpostTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
683 {"OutpostTPCooldownBypassP2", "Type <color=yellow>/outpost {0}</color>." },
684 {"OutpostTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
685 {"OutpostTPAmount", "You have {0} outpost teleports left today!"},
686
687 {"BanditTP", "You teleported to bandit town!"},
688 {"BanditTPNotSet", "Bandit is currently not set!"},
689 {"BanditTPDisabled", "Bandit is currently not enabled!"},
690 {"BanditTPDisabledConfig", "Bandit is currently not enabled because it isn't enabled in the config!"},
691 {"BanditTPDisabledNoLocation", "Bandit is currently not enabled, location is not set and auto generation is disabled!"},
692 {"BanditTPDisabledNoLocationAutoGen", "Bandit is currently not enabled because auto generation failed!"},
693 {"BanditTPLocation", "You have set the bandit town location to {0}!"},
694 {"BanditTPStarted", "Teleporting to bandit town in {0} seconds!"},
695 {"BanditTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
696 {"BanditTPCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
697 {"BanditTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
698 {"BanditTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
699 {"BanditTPCooldownBypassP2", "Type <color=yellow>/bandit {0}</color>." },
700 {"BanditTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
701 {"BanditTPAmount", "You have {0} bandit town teleports left today!"},
702
703 {"Interrupted", "Your teleport was interrupted!"},
704 {"InterruptedTarget", "{0}'s teleport was interrupted!"},
705 {"Unlimited", "Unlimited"},
706 {
707 "TPInfoGeneral", string.Join(NewLine, new[]
708 {
709 "Please specify the module you want to view the info of.",
710 "The available modules are: ",
711 })
712 },
713 {
714 "TPHelpGeneral", string.Join(NewLine, new[]
715 {
716 "<color=yellow>/tpinfo</color> - Shows limits and cooldowns.",
717 "Please specify the module you want to view the help of.",
718 "The available modules are: ",
719 })
720 },
721 {
722 "TPHelpadmintp", string.Join(NewLine, new[]
723 {
724 "As an admin you have access to the following commands:",
725 "<color=yellow>/tp \"targetplayer\"</color> - Teleports yourself to the target player.",
726 "<color=yellow>/tp \"player\" \"targetplayer\"</color> - Teleports the player to the target player.",
727 "<color=yellow>/tp x y z</color> - Teleports you to the set of coordinates.",
728 "<color=yellow>/tpl</color> - Shows a list of saved locations.",
729 "<color=yellow>/tpl \"location name\"</color> - Teleports you to a saved location.",
730 "<color=yellow>/tpsave \"location name\"</color> - Saves your current position as the location name.",
731 "<color=yellow>/tpremove \"location name\"</color> - Removes the location from your saved list.",
732 "<color=yellow>/tpb</color> - Teleports you back to the place where you were before teleporting.",
733 "<color=yellow>/home radius \"radius\"</color> - Find all homes in radius.",
734 "<color=yellow>/home delete \"player name|id\" \"home name\"</color> - Remove a home from a player.",
735 "<color=yellow>/home tp \"player name|id\" \"name\"</color> - Teleports you to the home location with the name 'name' from the player.",
736 "<color=yellow>/home homes \"player name|id\"</color> - Shows you a list of all homes from the player."
737 })
738 },
739 {
740 "TPHelphome", string.Join(NewLine, new[]
741 {
742 "With the following commands you can set your home location to teleport back to:",
743 "<color=yellow>/home add \"name\"</color> - Saves your current position as the location name.",
744 "<color=yellow>/home list</color> - Shows you a list of all the locations you have saved.",
745 "<color=yellow>/home remove \"name\"</color> - Removes the location of your saved homes.",
746 "<color=yellow>/home \"name\"</color> - Teleports you to the home location."
747 })
748 },
749 {
750 "TPHelptpr", string.Join(NewLine, new[]
751 {
752 "With these commands you can request to be teleported to a player or accept someone else's request:",
753 "<color=yellow>/tpr \"player name\"</color> - Sends a teleport request to the player.",
754 "<color=yellow>/tpa</color> - Accepts an incoming teleport request.",
755 "<color=yellow>/tpc</color> - Cancel teleport or request."
756 })
757 },
758 {
759 "TPSettingsGeneral", string.Join(NewLine, new[]
760 {
761 "Please specify the module you want to view the settings of. ",
762 "The available modules are:",
763 })
764 },
765 {
766 "TPSettingshome", string.Join(NewLine, new[]
767 {
768 "Home System has the current settings enabled:",
769 "Time between teleports: {0}",
770 "Daily amount of teleports: {1}",
771 "Amount of saved Home locations: {2}"
772 })
773 },
774 {
775 "TPSettingsbandit", string.Join(NewLine, new[]
776 {
777 "Bandit System has the current settings enabled:",
778 "Time between teleports: {0}",
779 "Daily amount of teleports: {1}"
780 })
781 },
782 {
783 "TPSettingsoutpost", string.Join(NewLine, new[]
784 {
785 "Outpost System has the current settings enabled:",
786 "Time between teleports: {0}",
787 "Daily amount of teleports: {1}"
788 })
789 },
790 {
791 "TPSettingstpr", string.Join(NewLine, new[]
792 {
793 "TPR System has the current settings enabled:",
794 "Time between teleports: {0}",
795 "Daily amount of teleports: {1}"
796 })
797 },
798 {
799 "TPSettingstown", string.Join(NewLine, new[]
800 {
801 "Town System has the current settings enabled:",
802 "Time between teleports: {0}",
803 "Daily amount of teleports: {1}"
804 })
805 },
806 {"TPT_True", "enabled"},
807 {"TPT_False", "disabled"},
808 {"TPT_clan", "TPT clan has been {0}."},
809 {"TPT_friend", "TPT friend has been {0}."},
810 {"TPT_team", "TPT team has been {0}."},
811 {"NotValidTPT", "Not valid, player is not"},
812 {"NotValidTPTFriend", " a friend!"},
813 {"NotValidTPTTeam", " on your team!"},
814 {"NotValidTPTClan", " in your clan!"},
815 {"TPTInfo", "`<color=yellow>/tpt clan|team|friend</color>` - toggle allowing/blocking of players trying to TPT to you via one of these options."},
816 {"PlayerNotFound", "The specified player couldn't be found please try again!"},
817 {"MultiplePlayers", "Found multiple players: {0}"},
818 {"CantTeleportToSelf", "You can't teleport to yourself!"},
819 {"CantTeleportPlayerToSelf", "You can't teleport a player to himself!"},
820 {"TeleportPending", "You can't initiate another teleport while you have a teleport pending!"},
821 {"TeleportPendingTarget", "You can't request a teleport to someone who's about to teleport!"},
822 {"LocationExists", "A location with this name already exists at {0}!"},
823 {"LocationExistsNearby", "A location with the name {0} already exists near this position!"},
824 {"LocationNotFound", "Couldn't find a location with that name!"},
825 {"NoPreviousLocationSaved", "No previous location saved!"},
826 {"HomeExists", "You have already saved a home location by this name!"},
827 {"HomeExistsNearby", "A home location with the name {0} already exists near this position!"},
828 {"HomeNotFound", "Couldn't find your home with that name!"},
829 {"InvalidCoordinates", "The coordinates you've entered are invalid!"},
830 {"InvalidHelpModule", "Invalid module supplied!"},
831 {"InvalidCharacter", "You have used an invalid character, please limit yourself to the letters a to z and numbers."},
832 {
833 "SyntaxCommandTP", string.Join(NewLine, new[]
834 {
835 "A Syntax Error Occurred!",
836 "You can only use the <color=yellow>/tp</color> command as follows:",
837 "<color=yellow>/tp \"targetplayer\"</color> - Teleports yourself to the target player.",
838 "<color=yellow>/tp \"player\" \"targetplayer\"</color> - Teleports the player to the target player.",
839 "<color=yellow>/tp x y z</color> - Teleports you to the set of coordinates.",
840 "<color=yellow>/tp \"player\" x y z</color> - Teleports the player to the set of coordinates."
841 })
842 },
843 {
844 "SyntaxCommandTPL", string.Join(NewLine, new[]
845 {
846 "A Syntax Error Occurred!",
847 "You can only use the <color=yellow>/tpl</color> command as follows:",
848 "<color=yellow>/tpl</color> - Shows a list of saved locations.",
849 "<color=yellow>/tpl \"location name\"</color> - Teleports you to a saved location."
850 })
851 },
852 {
853 "SyntaxCommandTPSave", string.Join(NewLine, new[]
854 {
855 "A Syntax Error Occurred!",
856 "You can only use the <color=yellow>/tpsave</color> command as follows:",
857 "<color=yellow>/tpsave \"location name\"</color> - Saves your current position as 'location name'."
858 })
859 },
860 {
861 "SyntaxCommandTPRemove", string.Join(NewLine, new[]
862 {
863 "A Syntax Error Occurred!",
864 "You can only use the <color=yellow>/tpremove</color> command as follows:",
865 "<color=yellow>/tpremove \"location name\"</color> - Removes the location with the name 'location name'."
866 })
867 },
868 {
869 "SyntaxCommandTPN", string.Join(NewLine, new[]
870 {
871 "A Syntax Error Occurred!",
872 "You can only use the <color=yellow>/tpn</color> command as follows:",
873 "<color=yellow>/tpn \"targetplayer\"</color> - Teleports yourself the default distance behind the target player.",
874 "<color=yellow>/tpn \"targetplayer\" \"distance\"</color> - Teleports you the specified distance behind the target player."
875 })
876 },
877 {
878 "SyntaxCommandSetHome", string.Join(NewLine, new[]
879 {
880 "A Syntax Error Occurred!",
881 "You can only use the <color=yellow>/home add</color> command as follows:",
882 "<color=yellow>/home add \"name\"</color> - Saves the current location as your home with the name 'name'."
883 })
884 },
885 {
886 "SyntaxCommandRemoveHome", string.Join(NewLine, new[]
887 {
888 "A Syntax Error Occurred!",
889 "You can only use the <color=yellow>/home remove</color> command as follows:",
890 "<color=yellow>/home remove \"name\"</color> - Removes the home location with the name 'name'."
891 })
892 },
893 {
894 "SyntaxCommandHome", string.Join(NewLine, new[]
895 {
896 "A Syntax Error Occurred!",
897 "You can only use the <color=yellow>/home</color> command as follows:",
898 "<color=yellow>/home \"name\"</color> - Teleports yourself to your home with the name 'name'.",
899 "<color=yellow>/home \"name\" pay</color> - Teleports yourself to your home with the name 'name', avoiding cooldown by paying for it.",
900 "<color=yellow>/home add \"name\"</color> - Saves the current location as your home with the name 'name'.",
901 "<color=yellow>/home list</color> - Shows you a list of all your saved home locations.",
902 "<color=yellow>/home remove \"name\"</color> - Removes the home location with the name 'name'."
903 })
904 },
905 {
906 "SyntaxCommandHomeAdmin", string.Join(NewLine, new[]
907 {
908 "<color=yellow>/home radius \"radius\"</color> - Shows you a list of all homes in radius(10).",
909 "<color=yellow>/home delete \"player name|id\" \"name\"</color> - Removes the home location with the name 'name' from the player.",
910 "<color=yellow>/home tp \"player name|id\" \"name\"</color> - Teleports you to the home location with the name 'name' from the player.",
911 "<color=yellow>/home homes \"player name|id\"</color> - Shows you a list of all homes from the player."
912 })
913 },
914 {
915 "SyntaxCommandTown", string.Join(NewLine, new[]
916 {
917 "A Syntax Error Occurred!",
918 "You can only use the <color=yellow>/town</color> command as follows:",
919 "<color=yellow>/town</color> - Teleports yourself to town.",
920 "<color=yellow>/town pay</color> - Teleports yourself to town, paying the penalty."
921 })
922 },
923 {
924 "SyntaxCommandTownAdmin", string.Join(NewLine, new[]
925 {
926 "<color=yellow>/town set</color> - Saves the current location as town.",
927 })
928 },
929 {
930 "SyntaxCommandOutpost", string.Join(NewLine, new[]
931 {
932 "A Syntax Error Occurred!",
933 "You can only use the <color=yellow>/outpost</color> command as follows:",
934 "<color=yellow>/outpost</color> - Teleports yourself to the Outpost.",
935 "<color=yellow>/outpost pay</color> - Teleports yourself to the Outpost, paying the penalty."
936 })
937 },
938 {
939 "SyntaxCommandOutpostAdmin", string.Join(NewLine, new[]
940 {
941 "<color=yellow>/outpost set</color> - Saves the current location as Outpost.",
942 })
943 },
944 {
945 "SyntaxCommandBandit", string.Join(NewLine, new[]
946 {
947 "A Syntax Error Occurred!",
948 "You can only use the <color=yellow>/bandit</color> command as follows:",
949 "<color=yellow>/bandit</color> - Teleports yourself to the Bandit Town.",
950 "<color=yellow>/bandit pay</color> - Teleports yourself to the Bandit Town, paying the penalty."
951 })
952 },
953 {
954 "SyntaxCommandBanditAdmin", string.Join(NewLine, new[]
955 {
956 "<color=yellow>/bandit set</color> - Saves the current location as Bandit Town.",
957 })
958 },
959 {
960 "SyntaxCommandHomeDelete", string.Join(NewLine, new[]
961 {
962 "A Syntax Error Occurred!",
963 "You can only use the <color=yellow>/home delete</color> command as follows:",
964 "<color=yellow>/home delete \"player name|id\" \"name\"</color> - Removes the home location with the name 'name' from the player."
965 })
966 },
967 {
968 "SyntaxCommandHomeAdminTP", string.Join(NewLine, new[]
969 {
970 "A Syntax Error Occurred!",
971 "You can only use the <color=yellow>/home tp</color> command as follows:",
972 "<color=yellow>/home tp \"player name|id\" \"name\"</color> - Teleports you to the home location with the name 'name' from the player."
973 })
974 },
975 {
976 "SyntaxCommandHomeHomes", string.Join(NewLine, new[]
977 {
978 "A Syntax Error Occurred!",
979 "You can only use the <color=yellow>/home homes</color> command as follows:",
980 "<color=yellow>/home homes \"player name|id\"</color> - Shows you a list of all homes from the player."
981 })
982 },
983 {
984 "SyntaxCommandListHomes", string.Join(NewLine, new[]
985 {
986 "A Syntax Error Occurred!",
987 "You can only use the <color=yellow>/home list</color> command as follows:",
988 "<color=yellow>/home list</color> - Shows you a list of all your saved home locations."
989 })
990 },
991 {
992 "SyntaxCommandTPT", string.Join(NewLine, new[]
993 {
994 "A Syntax Error Occurred!",
995 "You can only use the <color=yellow>/tpt</color> command as follows:",
996 "<color=yellow>/tpt \"player name\"</color> - Teleports you to a team or clan member."
997 })
998 },
999 {
1000 "SyntaxCommandTPR", string.Join(NewLine, new[]
1001 {
1002 "A Syntax Error Occurred!",
1003 "You can only use the <color=yellow>/tpr</color> command as follows:",
1004 "<color=yellow>/tpr \"player name\"</color> - Sends out a teleport request to 'player name'."
1005 })
1006 },
1007 {
1008 "SyntaxCommandTPA", string.Join(NewLine, new[]
1009 {
1010 "A Syntax Error Occurred!",
1011 "You can only use the <color=yellow>/tpa</color> command as follows:",
1012 "<color=yellow>/tpa</color> - Accepts an incoming teleport request."
1013 })
1014 },
1015 {
1016 "SyntaxCommandTPC", string.Join(NewLine, new[]
1017 {
1018 "A Syntax Error Occurred!",
1019 "You can only use the <color=yellow>/tpc</color> command as follows:",
1020 "<color=yellow>/tpc</color> - Cancels an teleport request."
1021 })
1022 },
1023 {
1024 "SyntaxConsoleCommandToPos", string.Join(NewLine, new[]
1025 {
1026 "A Syntax Error Occurred!",
1027 "You can only use the <color=orange>teleport.topos</color> console command as follows:",
1028 " > <color=orange>teleport.topos \"player\" x y z</color>"
1029 })
1030 },
1031 {
1032 "SyntaxConsoleCommandToPlayer", string.Join(NewLine, new[]
1033 {
1034 "A Syntax Error Occurred!",
1035 "You can only use the <color=orange>teleport.toplayer</color> console command as follows:",
1036 " > <color=orange>teleport.toplayer \"player\" \"target player\"</color>"
1037 })
1038 },
1039 {"LogTeleport", "{0} teleported to {1}."},
1040 {"LogTeleportPlayer", "{0} teleported {1} to {2}."},
1041 {"LogTeleportBack", "{0} teleported back to previous location."}
1042 }, this);
1043
1044 lang.RegisterMessages(new Dictionary<string, string>
1045 {
1046 {"AdminTP", "Вы телепортированы к {0}!"},
1047 {"AdminTPTarget", "{0} телепортирован к вам!"},
1048 {"AdminTPPlayers", "Вы телепортировали {0} к {1}!"},
1049 {"AdminTPPlayer", "{0} телепортировал вас к {1}!"},
1050 {"AdminTPPlayerTarget", "{0} телепортировал {1} к вам!"},
1051 {"AdminTPCoordinates", "Вы телепортированы к {0}!"},
1052 {"AdminTPTargetCoordinates", "Вы телепортировали {0} к {1}!"},
1053 {"AdminTPOutOfBounds", "Вы попытались телепортироваться на координаты, вне границ карты!"},
1054 {"AdminTPBoundaries", "Значения X и Z должны быть между -{0} и {0}, а значение Y между -100 и 2000!"},
1055 {"AdminTPLocation", "Вы телепортированы к {0}!"},
1056 {"AdminTPLocationSave", "Вы сохранили текущее местоположение!"},
1057 {"AdminTPLocationRemove", "Вы удалили местоположение {0}!"},
1058 {"AdminLocationList", "Доступны следующие местоположения:"},
1059 {"AdminLocationListEmpty", "Вы не сохранили никаких местоположений!"},
1060 {"AdminTPBack", "Вы телепортированы назад в ваше предыдущее местоположение!"},
1061 {"AdminTPBackSave", "Ваше предыдущее местоположение сохранено, используйте <color=yellow>/tpb</color>, чтобы телепортироваться назад!"},
1062 {"AdminTPTargetCoordinatesTarget", "{0} телепортировал вас к {1}!"},
1063 {"AdminTPConsoleTP", "Вы были телепортированы к {0}"},
1064 {"AdminTPConsoleTPPlayer", "Вы были телепортированы к {0}"},
1065 {"AdminTPConsoleTPPlayerTarget", "{0} был телепортирован к вам!"},
1066 {"HomeTP", "Вы телепортированы в ваш дом '{0}'!"},
1067 {"HomeAdminTP", "Вы телепортированы к дому '{1}' принадлежащему {0}!"},
1068 {"HomeSave", "Вы сохранили текущее местоположение как ваш дом!"},
1069 {"HomeNoFoundation", "Использовать местоположение в качестве дома разрешено только на фундаменте!"},
1070 {"HomeFoundationNotOwned", "Вы не можете использовать команду home в чужом доме."},
1071 {"HomeFoundationUnderneathFoundation", "Вы не можете использовать команду home на фундаменте, который находится под другим фундаментом."},
1072 {"HomeFoundationNotFriendsOwned", "Вы, или ваш друг должны быть владельцем дома, чтобы использовать команду home!"},
1073 {"HomeRemovedInvalid", "Ваш дом '{0}' был удалён потому, что не на фундаменте, или у фундамента новый владелец!"},
1074 {"HighWallCollision", "Столкновение Высоких Стен!"},
1075 {"HomeRemovedInsideBlock", "Ваш дом '{0}' был удалён потому, что внутри фундамента!"},
1076 {"HomeRemove", "Вы удалили свой дом {0}!"},
1077 {"HomeDelete", "Вы удалили дом '{1}' принадлежащий {0}!"},
1078 {"HomeList", "Доступны следующие дома:"},
1079 {"HomeListEmpty", "Вы не сохранили ни одного дома!"},
1080 {"HomeMaxLocations", "Невозможно установить здесь ваш дом, вы достигли лимита в {0} домов!"},
1081 {"HomeQuota", "Вы установили {0} из {1} максимально возможных домов!"},
1082 {"HomeTPStarted", "Телепортация в ваш дом {0} через {1} секунд!"},
1083 {"PayToHome", "Стандартный платеж {0} распространяется на все телепорты домой!"},
1084 {"PayToTown", "Стандартный платеж {0} распространяется на все телепорты в город!"},
1085 {"PayToTPR", "Стандартный платеж {0} распространяется на все tpr'ы!"},
1086 {"HomeTPCooldown", "Ваш телепорт перезаряжается. Вам необходимо подождать {0} до следующей телепортации."},
1087 {"HomeTPCooldownBypass", "Ваш телепорт был на перезарядке. Вы выбрали обойти это оплатив {0} с вашего баланса."},
1088 {"HomeTPCooldownBypassF", "Ваш телепорт перезаряжается. У вас недостаточно средств - {0} - чтобы обойти."},
1089 {"HomeTPCooldownBypassP", "Вы можете выбрать оплатить {0} чтобы обойти эту перезарядку." },
1090 {"HomeTPCooldownBypassP2", "Напишите <color=yellow>/home \"название дома\" {0}</color>." },
1091 {"HomeTPLimitReached", "Вы исчерпали ежедневный лимит {0} телепортаций сегодня!"},
1092 {"HomeTPAmount", "У вас осталось {0} телепортаций домой сегодня!"},
1093 {"HomesListWiped", "Вы очистили все местоположения, сохранённые как дом!"},
1094 {"HomeTPBuildingBlocked", "Вы не можете сохранить местоположение в качестве дома, если у вас нет прав на строительство в этой зоне!"},
1095 {"HomeTPSwimming", "Вы не можете устанавливать местоположение а качестве дома пока плывёте!"},
1096 {"HomeTPCrafting", "Вы не можете устанавливать местоположение а качестве дома в процессе крафта!"},
1097 {"Request", "Вы запросили телепортацию к {0}!"},
1098 {"RequestTarget", "{0} запросил телепортацию к вам! Используйте <color=yellow>/tpa</color>, чтобы принять!"},
1099 {"PendingRequest", "У вас уже есть активный запрос, отмените его, ожидайте подтверждения, либо отмены по таймауту!"},
1100 {"PendingRequestTarget", "У игрока, к которому вы хотите телепортироваться уже есть активный запрос, попробуйте позже!"},
1101 {"NoPendingRequest", "У вас нет активных запросов на телепортацию!"},
1102 {"AcceptOnRoof", "Вы не можете принять запрос на телепортацию стоя на потолке, спуститесь на уровень фундамента!"},
1103 {"Accept", "{0} принял ваш запрос! Телепортация через {1} секунд!"},
1104 {"AcceptTarget", "Вы приняли запрос на телепортацию {0}!"},
1105 {"NotAllowed", "Вам не разрешено использовать эту команду!"},
1106 {"Success", "Вы телепортированы к {0}!"},
1107 {"SuccessTarget", "{0} телепортировал вас!"},
1108 {"Cancelled", "Ваш запрос на телепортацию к {0} был отменён!"},
1109 {"CancelledTarget", "Запрос на телепортацию {0} был отменён!"},
1110 {"TPCancelled", "Ваша телепортация отменена!"},
1111 {"TPCancelledTarget", "{0} отменил телепортацию!"},
1112 {"TPYouCancelledTarget", "Вы отменили телепортацию {0}!"},
1113 {"TimedOut", "{0} не ответил на ваш запрос во время!"},
1114 {"TimedOutTarget", "Вы не ответили вовремя на запрос телепортации от {0}!"},
1115 {"TargetDisconnected", "{0} отключился, ваша телепортация отменена!"},
1116 {"TPRCooldown", "Ваши запросы на телепортацию в данный момент на перезарядке. Вам необходимо подождать {0} прежде чем отправить следующий запрос."},
1117 {"TPRCooldownBypass", "Ваши запросы на телепортацию были на перезарядке. Вы выбрали обойти это оплатив {0} с вашего баланса."},
1118 {"TPRCooldownBypassF", "Ваши запросы на телепортацию в данный момент на перезарядке. У вас недостаточно средств - {0} - чтобы обойти это."},
1119 {"TPRCooldownBypassP", "Вы можете выбрать оплатить {0} чтобы обойти эту перезарядку." },
1120 {"TPMoney", "{0} списано с вашего аккаунта!"},
1121 {"TPNoMoney", "У вас нет {0} ни на одном аккаунте!"},
1122 {"TPRCooldownBypassP2", "Напишите <color=yellow>/tpr {0}</color>." },
1123 {"TPRCooldownBypassP2a", "Напишите <color=yellow>/tpr \"имя игрока\" {0}</color>." },
1124 {"TPRLimitReached", "Вы исчерпали ежедневный лимит {0} запросов на телепортацию сегодня!"},
1125 {"TPRAmount", "У вас осталось {0} запросов на телепортацию на сегодня!"},
1126 {"TPRTarget", "Ваша цель в данный момент не доступна!"},
1127 {"TPDead", "Вы не можете телепортироваться, пока мертвы!"},
1128 {"TPWounded", "Вы не можете телепортироваться, будучи раненым!"},
1129 {"TPTooCold", "Вам слишком холодно для телепортации!"},
1130 {"TPTooHot", "Вам слишком жарко для телепортации!"},
1131 {"TPHostile", "Невозможно телепортироваться в город NPC или лагерь бандитов пока враждебен!"},
1132 {"HostileTimer", "Телепорт станет доступен через {0} минут."},
1133 {"TPMounted", "Вы не можете телепортироваться, когда сидите!"},
1134 {"TPBuildingBlocked", "Вы не можете телепортироваться, находясь в зоне блокировки строительства!"},
1135 {"TPAboveWater", "Вы не можете телепортироваться находясь над водой!"},
1136 {"TPTargetBuildingBlocked", "Вы не можете телепортироваться в зону, где блокировано строительство!"},
1137 {"TPTargetInsideBlock", "Вы не можете телепортироваться в фундамент!"},
1138 {"TPSwimming", "Вы не можете телепортироваться, пока плывёте!"},
1139 {"TPCargoShip", "Вы не можете телепортироваться с грузового корабля!"},
1140 {"TPOilRig", "Вы не можете телепортироваться с нефтяной вышки!"},
1141 {"TPExcavator", "Вы не можете телепортироваться с экскаватора!"},
1142 {"TPHotAirBalloon", "Вы не можете телепортироваться с или на воздушный шар!"},
1143 {"TPLift", "Вы не можете телепортироваться в лифте или подъемнике!"},
1144 {"TPBucketLift", "Вы не можете телепортироваться в подъемнике!"},
1145 {"TPRegLift", "Вы не можете телепортироваться в лифте!"},
1146 {"TPSafeZone", "Вы не можете телепортироваться из безопасной зоны!"},
1147 {"TPFlagZone", "Вы не можете телепортироваться из этой зоны!"},
1148 {"TPNoEscapeBlocked", "Вы не можете телепортироваться пока активна блокировка!"},
1149 {"TPCrafting", "Вы не можете телепортироваться в процессе крафта!"},
1150 {"TPBlockedItem", "Вы не можете телепортироваться пока несёте: {0}!"},
1151 {"TooCloseToMon", "Вы не можете телепортироваться так близко к {0}!"},
1152 {"TooCloseToCave", "Вы не можете телепортироваться так близко к пещере!"},
1153 {"HomeTooCloseToCave", "Вы не можете сохранить местоположение в качестве дома так близко к пещере!"},
1154 {"TownTP", "Вы телепортрованы в город!"},
1155 {"TownTPNotSet", "Город не задан!"},
1156 {"TownTPDisabled", "Город в данный момент не активирован!"},
1157 {"TownTPLocation", "Вы задали местоположение города {0}!"},
1158 {"TownTPStarted", "Телепортация в город через {0} секунд!"},
1159 {"TownTPCooldown", "Ваш телепорт в данный момент на перезарядке. Вам необходимо подождать {0} до следующей телепортации."},
1160 {"TownTPCooldownBypass", "Ваш запрос на телепортацию был на перезарядке. Вы выбрали обойти это оплатив {0} с вашего баланса."},
1161 {"TownTPCooldownBypassF", "Ваш телепорт в данный момент на перезарядке. У вас недостаточно средств - {0} - чтобы обойти это."},
1162 {"TownTPCooldownBypassP", "Вы можете выбрать оплатить {0} чтобы обойти эту перезарядку." },
1163 {"TownTPCooldownBypassP2", "Напишите <color=yellow>/town {0}</color>." },
1164 {"TownTPLimitReached", "Вы исчерпали ежедневный лимит {0} телепортаций на сегодня!"},
1165 {"TownTPAmount", "У вас осталось {0} телепортаций в город на сегодня!"},
1166
1167 {"OutpostTP", "Вы телепортированы в город NPC!"},
1168 {"OutpostTPNotSet", "Город NPC в данный момент не установлен!"},
1169 {"OutpostTPDisabled", "Город NPC в данный момент не активирован!"},
1170 {"OutpostTPDisabledConfig", "Город NPC не включен в конфиг-файле"},
1171 {"OutpostTPDisabledNoLocation", "Город NPC в данный момент не активирован, местоположение не задано и автоматическое генерирование местоположения отключено!"},
1172 {"OutpostTPDisabledNoLocationAutoGen", "Город NPC отключен потому, что автоматическое генерирование местоположения не удалось!"},
1173 {"OutpostTPLocation", "Вы установили местоположение города NPC {0}!"},
1174 {"OutpostTPStarted", "Телепортация в город NPC через {0} секунд!"},
1175 {"OutpostTPCooldown", "Ваш телепорт в данный момент на перезарядке. Вам необходимо подождать {0} до следующей телепортации."},
1176 {"OutpostTPCooldownBypass", "Ваш запрос на телепортацию был на перезарядке. Вы выбрали обойти это оплатив {0} с вашего баланса."},
1177 {"OutpostTPCooldownBypassF", "Ваш телепорт в данный момент на перезарядке. У вас недостаточно средств - {0} - чтобы обойти это."},
1178 {"OutpostTPCooldownBypassP", "Вы можете выбрать оплатить {0} чтобы обойти эту перезарядку." },
1179 {"OutpostTPCooldownBypassP2", "Напишите <color=yellow>/outpost {0}</color>." },
1180 {"OutpostTPLimitReached", "Вы исчерпали ежедневный лимит {0} телепортаций на сегодня!"},
1181 {"OutpostTPAmount", "У вас осталось {0} телепортаций в город NPC на сегодня!"},
1182
1183 {"BanditTP", "Вы телепортированы в лагерь бандитов!"},
1184 {"BanditTPNotSet", "Лагерь бандитов в данный момент не установлен!"},
1185 {"BanditTPDisabled", "Лагерь бандитов в данный момент не активирован!"},
1186 {"BanditTPDisabledConfig", "Лагерь бандитов не включен в конфиг-файле!"},
1187 {"BanditTPDisabledNoLocation", "Лагерь бандитов в данный момент не активирован, местоположение не задано и автоматическое генерирование местоположения отключено!"},
1188 {"BanditTPDisabledNoLocationAutoGen", "Лагерь бандитов отключен потому, что автоматическое генерирование местоположения не удалось!"},
1189 {"BanditTPLocation", "Вы установили местоположение лагеря бандитов {0}!"},
1190 {"BanditTPStarted", "Телепортация в лагерь бандитов через {0} секунд!"},
1191 {"BanditTPCooldown", "Ваш телепорт в данный момент на перезарядке. Вам необходимо подождать {0} до следующей телепортации."},
1192 {"BanditTPCooldownBypass", "Ваш запрос на телепортацию был на перезарядке. Вы выбрали обойти это оплатив {0} с вашего баланса."},
1193 {"BanditTPCooldownBypassF", "Ваш телепорт в данный момент на перезарядке. У вас недостаточно средств - {0} - чтобы обойти это."},
1194 {"BanditTPCooldownBypassP", "Вы можете выбрать оплатить {0} чтобы обойти эту перезарядку." },
1195 {"BanditTPCooldownBypassP2", "Напишите <color=yellow>/bandit {0}</color>." },
1196 {"BanditTPLimitReached", "Вы исчерпали ежедневный лимит {0} телепортаций на сегодня!"},
1197 {"BanditTPAmount", "У вас осталось {0} телепортаций в лагерь бандитов на сегодня!"},
1198
1199 {"Interrupted", "Ваша телепортация была прервана!"},
1200 {"InterruptedTarget", "Телепортация {0} была прервана!"},
1201 {"Unlimited", "Не ограничено"},
1202 {
1203 "TPInfoGeneral", string.Join(NewLine, new[]
1204 {
1205 "Пожалуйста, укажите модуль, о котором вы хотите просмотреть информацию.",
1206 "Доступные модули: ",
1207 })
1208 },
1209 {
1210 "TPHelpGeneral", string.Join(NewLine, new[]
1211 {
1212 "<color=yellow>/tpinfo</color> - Отображает лимиты и перезарядки.",
1213 "Пожалуйста, укажите модуль, о котором вы хотите просмотреть помощь.",
1214 "Доступные модули: ",
1215 })
1216 },
1217 {
1218 "TPHelpadmintp", string.Join(NewLine, new[]
1219 {
1220 "Как админ, вы имеете доступ к следующим командам:",
1221 "<color=yellow>/tp \"имя игрока\"</color> - Телепортирует вас к указанному игроку.",
1222 "<color=yellow>/tp \"имя игрока\" \"имя игрока 2\"</color> - Телепортирует игрока с именем 'имя игрока' к игроку 'имя игрока 2'.",
1223 "<color=yellow>/tp x y z</color> - Телепортирует вас на указанные координаты.",
1224 "<color=yellow>/tpl</color> - Отображает список сохранённых местоположений.",
1225 "<color=yellow>/tpl \"название местоположения\"</color> - Телепортирует вас в сохранённое местоположение.",
1226 "<color=yellow>/tpsave \"название местоположения\"</color> - Сохраняет ваше текущее местоположение с указанным названием.",
1227 "<color=yellow>/tpremove \"название местоположения\"</color> - Удаляет местоположение из списка сохранённых.",
1228 "<color=yellow>/tpb</color> - Телепортирует вас назад на место, где вы были перед телепортацией.",
1229 "<color=yellow>/home radius \"радиус\"</color> - Найти все дома в радиусе.",
1230 "<color=yellow>/home delete \"имя игрока или ID\" \"название дома\"</color> - Удаляет дом с указанным именем принадлежащий указанному игроку.",
1231 "<color=yellow>/home tp \"имя игрока или ID\" \"название дома\"</color> - Телепортирует вас в дом игрока с указанным названием принадлежащий указанному игроку.",
1232 "<color=yellow>/home homes \"имя игрока или ID\"</color> - Отображает вам список всех домов, принадлежащих указанному игроку."
1233 })
1234 },
1235 {
1236 "TPHelphome", string.Join(NewLine, new[]
1237 {
1238 "Используя следующие команды, вы можете установить местоположение вашего дома, чтобы затем в него телепортироваться:",
1239 "<color=yellow>/home add \"название дома\"</color> - Сохраняет ваше текущее местоположение как ваш дом с указанным названием.",
1240 "<color=yellow>/home list</color> - Отображает список всех местоположений, сохранённых вами как дом.",
1241 "<color=yellow>/home remove \"название дома\"</color> - Удаляет расположение сохранённого дома с указанным названием.",
1242 "<color=yellow>/home \"название дома\"</color> - Телепортирует вас в местоположение дома с указанным названием."
1243 })
1244 },
1245 {
1246 "TPHelptpr", string.Join(NewLine, new[]
1247 {
1248 "Используя эти команды, вы можете отправить запрос на телепортацию к игроку, или принять чей-то запрос:",
1249 "<color=yellow>/tpr \"имя игрока\"</color> - Отправляет запрос на телепортацию игроку с указанным именем.",
1250 "<color=yellow>/tpa</color> - Принять входящий запрос на телепортацию.",
1251 "<color=yellow>/tpc</color> - Отменить запрос на телепортацию."
1252 })
1253 },
1254 {
1255 "TPSettingsGeneral", string.Join(NewLine, new[]
1256 {
1257 "Пожалуйста, укажите модуль, настройки которого вы хотите просмотреть. ",
1258 "Доступные модули:",
1259 })
1260 },
1261 {
1262 "TPSettingshome", string.Join(NewLine, new[]
1263 {
1264 "Система домов в данный момент имеет следующие включённые параметры:",
1265 "Время между телепортами: {0}",
1266 "Ежедневный лимит телепортаций: {1}",
1267 "Количество сохранённых домов: {2}"
1268 })
1269 },
1270 {
1271 "TPSettingsbandit", string.Join(NewLine, new[]
1272 {
1273 "Система лагеря бандитов в данный момент имеет следующие включённые параметры:",
1274 "Время между телепортами: {0}",
1275 "Ежедневный лимит телепортаций: {1}"
1276 })
1277 },
1278 {
1279 "TPSettingsoutpost", string.Join(NewLine, new[]
1280 {
1281 "Система города NPC в данный момент имеет следующие включённые параметры:",
1282 "Время между телепортами: {0}",
1283 "Ежедневный лимит телепортаций: {1}"
1284 })
1285 },
1286 {
1287 "TPSettingstpr", string.Join(NewLine, new[]
1288 {
1289 "Система TPR в данный момент имеет следующие включённые параметры:",
1290 "Время между телепортами: {0}",
1291 "Ежедневный лимит телепортаций: {1}"
1292 })
1293 },
1294 {
1295 "TPSettingstown", string.Join(NewLine, new[]
1296 {
1297 "Система городов в данный момент имеет следующие включённые параметры:",
1298 "Время между телепортами: {0}",
1299 "Ежедневный лимит телепортаций: {1}"
1300 })
1301 },
1302 {"TPT_True", "включено"},
1303 {"TPT_False", "выключено"},
1304 {"TPT_clan", "TPT clan has been {0}."},
1305 {"TPT_friend", "TPT friend has been {0}."},
1306 {"TPT_team", "TPT team has been {0}."},
1307 {"NotValidTPT", "Неверно, игрок не"},
1308 {"NotValidTPTFriend", " друг!"},
1309 {"NotValidTPTTeam", " в вашей команде!"},
1310 {"NotValidTPTClan", " в вашем клане!"},
1311 {"TPTInfo", "`<color=yellow>/tpt clan|team|friend</color>` - чтобы разрешить/запретить игрокам пытающимся TPT к вам через одну из этих опций."},
1312 {"PlayerNotFound", "Указанный игрок не обнаружен, пожалуйста попробуйте ещё раз!"},
1313 {"MultiplePlayers", "Найдено несколько игроков: {0}"},
1314 {"CantTeleportToSelf", "Вы не можете телепортироваться к себе!"},
1315 {"CantTeleportPlayerToSelf", "Вы не можете телепортровать игрока к самому себе!"},
1316 {"TeleportPending", "Вы не можете инициировать телепортацию, пока у вас есть активный запрос!"},
1317 {"TeleportPendingTarget", "Вы не можете отправить запрос к тому, кто в процессе телепортации!"},
1318 {"LocationExists", "Местоположение с таким названием уже существует в {0}!"},
1319 {"LocationExistsNearby", "Местоположение с названием {0} уже существует рядом с текущей позицией!"},
1320 {"LocationNotFound", "Не найдено местоположение с таким названием!"},
1321 {"NoPreviousLocationSaved", "Предыдущее местоположение не сохранено!"},
1322 {"HomeExists", "Вы уже сохранили дом с таким названием!"},
1323 {"HomeExistsNearby", "Дом с названием {0} уже существует рядом с текущей позицией!"},
1324 {"HomeNotFound", "Дом с таким названием не найден!"},
1325 {"InvalidCoordinates", "Вы указали неверные координаты!"},
1326 {"InvalidHelpModule", "Введен неверный модуль!"},
1327 {"InvalidCharacter", "Вы использовали недопустимый символ, пожалуйста, ограничьте себя буквами от a до z и цифрами."},
1328 {
1329 "SyntaxCommandTP", string.Join(NewLine, new[]
1330 {
1331 "Произошла синтаксическая ошибка!",
1332 "Использование команды <color=yellow>/tp</color> возможно только следующим образом:",
1333 "<color=yellow>/tp \"имя игрока\"</color> - Телепортирует вас к указанному игроку.",
1334 "<color=yellow>/tp \"имя игрока\" \"имя игрока 2\"</color> - Телепортирует игрока с именем 'имя игрока' к игроку 'имя игрока 2'.",
1335 "<color=yellow>/tp x y z</color> - Телепортирует вас на указанные координаты.",
1336 "<color=yellow>/tp \"имя игрока\" x y z</color> - Телепортирует игрока с именем 'имя игрока' на указанные координаты."
1337 })
1338 },
1339 {
1340 "SyntaxCommandTPL", string.Join(NewLine, new[]
1341 {
1342 "Произошла синтаксическая ошибка!",
1343 "Использование команды <color=yellow>/tpl</color> возможно только следующим образом:",
1344 "<color=yellow>/tpl</color> - Отображает список сохранённых местоположений.",
1345 "<color=yellow>/tpl \"название местоположения\"</color> - Телепортирует вас в место с указанным названием."
1346 })
1347 },
1348 {
1349 "SyntaxCommandTPSave", string.Join(NewLine, new[]
1350 {
1351 "Произошла синтаксическая ошибка!",
1352 "Использование команды <color=yellow>/tpsave</color> возможно только следующим образом:",
1353 "<color=yellow>/tpsave \"название местоположения\"</color> - Сохраняет ваше текущее местоположение с указанным названием."
1354 })
1355 },
1356 {
1357 "SyntaxCommandTPRemove", string.Join(NewLine, new[]
1358 {
1359 "Произошла синтаксическая ошибка!",
1360 "Использование команды <color=yellow>/tpremove</color> возможно только следующим образом:",
1361 "<color=yellow>/tpremove \"название местоположения\"</color> - Удаляет местоположение с указанным названием."
1362 })
1363 },
1364 {
1365 "SyntaxCommandTPN", string.Join(NewLine, new[]
1366 {
1367 "Произошла синтаксическая ошибка!",
1368 "Использование команды <color=yellow>/tpn</color> возможно только следующим образом:",
1369 "<color=yellow>/tpn \"имя игрока\"</color> - Телепортирует вас на расстояние по умолчанию позади игрока с указанным именем.",
1370 "<color=yellow>/tpn \"имя игрока\" \"расстояние\"</color> - Телепортирует вас на указанное расстояние позади игрока с указанным именем."
1371 })
1372 },
1373 {
1374 "SyntaxCommandSetHome", string.Join(NewLine, new[]
1375 {
1376 "Произошла синтаксическая ошибка!",
1377 "Использование команды <color=yellow>/home add</color> возможно только следующим образом:",
1378 "<color=yellow>/home add \"название\"</color> - Сохраняет ваше текущее местоположение как ваш дом с указанным названием."
1379 })
1380 },
1381 {
1382 "SyntaxCommandRemoveHome", string.Join(NewLine, new[]
1383 {
1384 "Произошла синтаксическая ошибка!",
1385 "Использование команды <color=yellow>/home remove</color> возможно только следующим образом:",
1386 "<color=yellow>/home remove \"название\"</color> - Удаляет местоположение дома с указанным названием."
1387 })
1388 },
1389 {
1390 "SyntaxCommandHome", string.Join(NewLine, new[]
1391 {
1392 "Произошла синтаксическая ошибка!",
1393 "Использование команды <color=yellow>/home</color> возможно только следующим образом:",
1394 "<color=yellow>/home \"название\"</color> - Телепортирует вас в ваш дом с указанным названием.",
1395 "<color=yellow>/home \"название\" pay</color> - Телепортирует вас в ваш дом с указанным названием, избегая перезарядки, заплатив за это.",
1396 "<color=yellow>/home add \"название\"</color> - Сохраняет ваше текущее местоположение как ваш дом с указанным названием.",
1397 "<color=yellow>/home list</color> - Отображает список всех местоположений, сохранённых вами как дом.",
1398 "<color=yellow>/home remove \"название\"</color> - Удаляет местоположение дома с указанным названием."
1399 })
1400 },
1401 {
1402 "SyntaxCommandHomeAdmin", string.Join(NewLine, new[]
1403 {
1404 "<color=yellow>/home radius \"радиус\"</color> - Отображает список всех домов в радиусе(10).",
1405 "<color=yellow>/home delete \"имя игрока или ID\" \"название\"</color> - Удаляет дом с указанным названием, принадлежащий указанному игроку.",
1406 "<color=yellow>/home tp \"имя игрока или ID\" \"название\"</color> - Телепортирует вас в дом с указанным названием, принадлежащий указанному игроку.",
1407 "<color=yellow>/home homes \"имя игрока или ID\"</color> - Отображает вам список всех домов, принадлежащих указанному игроку."
1408 })
1409 },
1410 {
1411 "SyntaxCommandTown", string.Join(NewLine, new[]
1412 {
1413 "Произошла синтаксическая ошибка!",
1414 "Использование команды <color=yellow>/town</color> возможно только следующим образом:",
1415 "<color=yellow>/town</color> - Телепортирует вас в город.",
1416 "<color=yellow>/town pay</color> - Телепортирует вас в город с оплатой штрафа."
1417 })
1418 },
1419 {
1420 "SyntaxCommandTownAdmin", string.Join(NewLine, new[]
1421 {
1422 "<color=yellow>/town set</color> - Сохраняет текущее местоположение как город.",
1423 })
1424 },
1425 {
1426 "SyntaxCommandOutpost", string.Join(NewLine, new[]
1427 {
1428 "Произошла синтаксическая ошибка!",
1429 "Использование команды <color=yellow>/outpost</color> возможно только следующим образом:",
1430 "<color=yellow>/outpost</color> - Телепортирует вас в город NPC.",
1431 "<color=yellow>/outpost pay</color> - Телепортирует вас в город NPC с оплатой штрафа."
1432 })
1433 },
1434 {
1435 "SyntaxCommandOutpostAdmin", string.Join(NewLine, new[]
1436 {
1437 "<color=yellow>/outpost set</color> - Сохраняет текущее местоположение как город NPC.",
1438 })
1439 },
1440 {
1441 "SyntaxCommandBandit", string.Join(NewLine, new[]
1442 {
1443 "Произошла синтаксическая ошибка!",
1444 "Использование команды <color=yellow>/bandit</color> возможно только следующим образом:",
1445 "<color=yellow>/bandit</color> - Телепортирует вас в лагерь бандитов.",
1446 "<color=yellow>/bandit pay</color> - Телепортирует вас в лагерь бандитов с оплатой штрафа."
1447 })
1448 },
1449 {
1450 "SyntaxCommandBanditAdmin", string.Join(NewLine, new[]
1451 {
1452 "<color=yellow>/bandit set</color> - Сохраняет текущее местоположение как лагерь бандитов.",
1453 })
1454 },
1455 {
1456 "SyntaxCommandHomeDelete", string.Join(NewLine, new[]
1457 {
1458 "Произошла синтаксическая ошибка!",
1459 "Использование команды <color=yellow>/home delete</color> возможно только следующим образом:",
1460 "<color=yellow>/home delete \"имя игрока или ID\" \"название\"</color> - Удаляет дом с указанным названием, принадлежащий указанному игроку."
1461 })
1462 },
1463 {
1464 "SyntaxCommandHomeAdminTP", string.Join(NewLine, new[]
1465 {
1466 "Произошла синтаксическая ошибка!",
1467 "Использование команды <color=yellow>/home tp</color> возможно только следующим образом:",
1468 "<color=yellow>/home tp \"имя игрока или ID\" \"название\"</color> - Телепортирует вас в дом игрока с указанным названием, принадлежащий указанному игроку."
1469 })
1470 },
1471 {
1472 "SyntaxCommandHomeHomes", string.Join(NewLine, new[]
1473 {
1474 "Произошла синтаксическая ошибка!",
1475 "Использование команды <color=yellow>/home homes</color> возможно только следующим образом:",
1476 "<color=yellow>/home homes \"имя игрока или ID\"</color> - Отображает вам список всех домов, принадлежащих указанному игроку."
1477 })
1478 },
1479 {
1480 "SyntaxCommandListHomes", string.Join(NewLine, new[]
1481 {
1482 "Произошла синтаксическая ошибка!",
1483 "Использование команды <color=yellow>/home list</color> возможно только следующим образом:",
1484 "<color=yellow>/home list</color> - Отображает список всех местоположений, сохранённых вами как дом."
1485 })
1486 },
1487 {
1488 "SyntaxCommandTPT", string.Join(NewLine, new[]
1489 {
1490 "Произошла синтаксическая ошибка!",
1491 "Использование команды <color=yellow>/tpt</color> возможно только следующим образом:",
1492 "<color=yellow>/tpt \"имя игрока или ID\"</color> - Телепортирует вас к участнику команды, или клана."
1493 })
1494 },
1495 {
1496 "SyntaxCommandTPR", string.Join(NewLine, new[]
1497 {
1498 "Произошла синтаксическая ошибка!",
1499 "Использование команды <color=yellow>/tpr</color> возможно только следующим образом:",
1500 "<color=yellow>/tpr \"имя игрока или ID\"</color> - Отправляет указанному игроку запрос на телепортацию."
1501 })
1502 },
1503 {
1504 "SyntaxCommandTPA", string.Join(NewLine, new[]
1505 {
1506 "Произошла синтаксическая ошибка!",
1507 "Использование команды <color=yellow>/tpa</color> возможно только следующим образом:",
1508 "<color=yellow>/tpa</color> - Принять входящий запрос на телепортацию."
1509 })
1510 },
1511 {
1512 "SyntaxCommandTPC", string.Join(NewLine, new[]
1513 {
1514 "Произошла синтаксическая ошибка!",
1515 "Использование команды <color=yellow>/tpc</color> возможно только следующим образом:",
1516 "<color=yellow>/tpc</color> - Отменить запрос на телепортацию."
1517 })
1518 },
1519 {
1520 "SyntaxConsoleCommandToPos", string.Join(NewLine, new[]
1521 {
1522 "Произошла синтаксическая ошибка!",
1523 "Использование консольной команды <color=orange>teleport.topos</color> возможно только следующим образом:",
1524 " > <color=orange>teleport.topos \"имя игрока\" x y z</color>"
1525 })
1526 },
1527 {
1528 "SyntaxConsoleCommandToPlayer", string.Join(NewLine, new[]
1529 {
1530 "Произошла синтаксическая ошибка!",
1531 "Использование консольной команды <color=orange>teleport.toplayer</color> возможно только следующим образом:",
1532 " > <color=orange>teleport.toplayer \"имя игрока или ID\" \"имя игрока 2|id 2\"</color>"
1533 })
1534 },
1535 {"LogTeleport", "{0} телепортирован к {1}."},
1536 {"LogTeleportPlayer", "{0} телепортировал {1} к {2}."},
1537 {"LogTeleportBack", "{0} телепортрован назад к предыдущему местоположению."}
1538 }, this, "ru");
1539 }
1540
1541 private void Init()
1542 {
1543 Unsubscribe(nameof(OnEntityTakeDamage));
1544 Unsubscribe(nameof(OnPlayerSleepEnded));
1545 Unsubscribe(nameof(OnPlayerDisconnected));
1546 }
1547
1548 private void Loaded()
1549 {
1550 dataAdmin = GetFile(nameof(NTeleportation) + "Admin");
1551 Admin = dataAdmin.ReadObject<Dictionary<ulong, AdminData>>();
1552 dataHome = GetFile(nameof(NTeleportation) + "Home");
1553 Home = dataHome.ReadObject<Dictionary<ulong, HomeData>>();
1554 dataTPT = GetFile(nameof(NTeleportation) + "TPT");
1555 TPT = dataTPT.ReadObject<Dictionary<string, List<string>>>();
1556 dataTPR = GetFile(nameof(NTeleportation) + "TPR");
1557 TPR = dataTPR.ReadObject<Dictionary<ulong, TeleportData>>();
1558 dataTown = GetFile(nameof(NTeleportation) + "Town");
1559 Town = dataTown.ReadObject<Dictionary<ulong, TeleportData>>();
1560 dataOutpost = GetFile(nameof(NTeleportation) + "Outpost");
1561 Outpost = dataOutpost.ReadObject<Dictionary<ulong, TeleportData>>();
1562 dataBandit = GetFile(nameof(NTeleportation) + "Bandit");
1563 Bandit = dataBandit.ReadObject<Dictionary<ulong, TeleportData>>();
1564 dataDisabled = GetFile(nameof(NTeleportation) + "DisabledCommands");
1565 DisabledTPT = dataDisabled.ReadObject<DisabledData>();
1566 permission.RegisterPermission(PermDeleteHome, this);
1567 permission.RegisterPermission(PermHome, this);
1568 permission.RegisterPermission(PermHomeHomes, this);
1569 permission.RegisterPermission(PermImportHomes, this);
1570 permission.RegisterPermission(PermRadiusHome, this);
1571 permission.RegisterPermission(PermTp, this);
1572 permission.RegisterPermission(PermTpB, this);
1573 permission.RegisterPermission(PermTpR, this);
1574 permission.RegisterPermission(PermTpConsole, this);
1575 permission.RegisterPermission(PermTpHome, this);
1576 permission.RegisterPermission(PermTpTown, this);
1577 permission.RegisterPermission(PermTpT, this);
1578 permission.RegisterPermission(PermTpOutpost, this);
1579 permission.RegisterPermission(PermTpBandit, this);
1580 permission.RegisterPermission(PermTpN, this);
1581 permission.RegisterPermission(PermTpL, this);
1582 permission.RegisterPermission(PermTpRemove, this);
1583 permission.RegisterPermission(PermTpSave, this);
1584 permission.RegisterPermission(PermWipeHomes, this);
1585 permission.RegisterPermission(PermCraftHome, this);
1586 permission.RegisterPermission(PermCraftTown, this);
1587 permission.RegisterPermission(PermCraftOutpost, this);
1588 permission.RegisterPermission(PermCraftBandit, this);
1589 permission.RegisterPermission(PermCraftTpR, this);
1590 foreach (var key in config.Home.VIPCooldowns.Keys)
1591 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1592 foreach (var key in config.Home.VIPCountdowns.Keys)
1593 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1594 foreach (var key in config.Home.VIPDailyLimits.Keys)
1595 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1596 foreach (var key in config.Home.VIPHomesLimits.Keys)
1597 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1598 foreach (var key in config.TPR.VIPCooldowns.Keys)
1599 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1600 foreach (var key in config.TPR.VIPCountdowns.Keys)
1601 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1602 foreach (var key in config.TPR.VIPDailyLimits.Keys)
1603 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1604 foreach (var key in config.Town.VIPCooldowns.Keys)
1605 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1606 foreach (var key in config.Town.VIPCountdowns.Keys)
1607 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1608 foreach (var key in config.Town.VIPDailyLimits.Keys)
1609 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
1610 }
1611
1612 private DynamicConfigFile GetFile(string name)
1613 {
1614 var file = Interface.Oxide.DataFileSystem.GetFile(name);
1615 file.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
1616 file.Settings.Converters = new JsonConverter[] { new UnityVector3Converter(), new CustomComparerDictionaryCreationConverter<string>(StringComparer.OrdinalIgnoreCase) };
1617 return file;
1618 }
1619
1620 private void CheckNewSave()
1621 {
1622 if (!newSave && BuildingManager.server.buildingDictionary.Count == 0)
1623 {
1624 newSave = true;
1625 }
1626
1627 if (!newSave)
1628 {
1629 return;
1630 }
1631
1632 if (config.Settings.WipeOnUpgradeOrChange)
1633 {
1634 Puts("Rust was upgraded or map changed - clearing homes, town, outpost and bandit!");
1635 Home.Clear();
1636 changedHome = True;
1637 config.Town.Location = Zero;
1638 config.Outpost.Location = Zero;
1639 config.Bandit.Location = Zero;
1640 SaveConfig();
1641 }
1642 else
1643 {
1644 Puts("Rust was upgraded or map changed - homes, town, outpost and bandit may be invalid!");
1645 }
1646 }
1647
1648 void OnServerInitialized()
1649 {
1650 CheckNewSave();
1651 banditPrefab = StringPool.Get(2074025910);
1652 banditEnabled = config.Settings.BanditEnabled;
1653 outpostPrefab = StringPool.Get(1879405026);
1654 outpostEnabled = config.Settings.OutpostEnabled;
1655
1656 Subscribe(nameof(OnPlayerSleepEnded));
1657 Subscribe(nameof(OnPlayerDisconnected));
1658
1659 boundary = TerrainMeta.Size.x / 2;
1660 CheckPerms(config.Home.VIPHomesLimits);
1661 CheckPerms(config.Home.VIPDailyLimits);
1662 CheckPerms(config.Home.VIPCooldowns);
1663 CheckPerms(config.TPR.VIPDailyLimits);
1664 CheckPerms(config.TPR.VIPCooldowns);
1665 CheckPerms(config.Town.VIPDailyLimits);
1666 CheckPerms(config.Town.VIPCooldowns);
1667 CheckPerms(config.Outpost.VIPDailyLimits);
1668 CheckPerms(config.Outpost.VIPCooldowns);
1669 CheckPerms(config.Bandit.VIPDailyLimits);
1670 CheckPerms(config.Bandit.VIPCooldowns);
1671
1672 foreach (var item in config.Settings.BlockedItems)
1673 {
1674 var definition = ItemManager.FindItemDefinition(item.Key);
1675 if (definition == null)
1676 {
1677 Puts("Blocked item not found: {0}", item.Key);
1678 continue;
1679 }
1680 ReverseBlockedItems[definition.itemid] = item.Value;
1681 }
1682
1683 if (CompoundTeleport == null)
1684 {
1685 if (outpostEnabled) AddCovalenceCommand("outpost", nameof(CommandOutpost));
1686 if (banditEnabled) AddCovalenceCommand("bandit", nameof(CommandBandit));
1687 }
1688 if (config.Settings.TownEnabled) AddCovalenceCommand("town", nameof(CommandTown));
1689 if (config.Settings.TPREnabled) AddCovalenceCommand("tpr", nameof(CommandTeleportRequest));
1690 if (config.Settings.HomesEnabled)
1691 {
1692 AddCovalenceCommand("home", nameof(CommandHome));
1693 AddCovalenceCommand("sethome", nameof(CommandSetHome));
1694 AddCovalenceCommand("listhomes", nameof(CommandListHomes));
1695 AddCovalenceCommand("removehome", nameof(CommandRemoveHome));
1696 AddCovalenceCommand("radiushome", nameof(CommandHomeRadius));
1697 AddCovalenceCommand("deletehome", nameof(CommandHomeDelete));
1698 AddCovalenceCommand("tphome", nameof(CommandHomeAdminTP));
1699 AddCovalenceCommand("homehomes", nameof(CommandHomeHomes));
1700 }
1701
1702 AddCovalenceCommand("tnt", nameof(CommandToggle));
1703 AddCovalenceCommand("tp", nameof(CommandTeleport));
1704 AddCovalenceCommand("tpn", nameof(CommandTeleportNear));
1705 AddCovalenceCommand("tpl", nameof(CommandTeleportLocation));
1706 AddCovalenceCommand("tpsave", nameof(CommandSaveTeleportLocation));
1707 AddCovalenceCommand("tpremove", nameof(CommandRemoveTeleportLocation));
1708 AddCovalenceCommand("tpb", nameof(CommandTeleportBack));
1709 AddCovalenceCommand("tpt", nameof(CommandTeleportTeam));
1710 AddCovalenceCommand("tpa", nameof(CommandTeleportAccept));
1711 AddCovalenceCommand("wipehomes", nameof(CommandWipeHomes));
1712 AddCovalenceCommand("tphelp", nameof(CommandTeleportHelp));
1713 AddCovalenceCommand("tpinfo", nameof(CommandTeleportInfo));
1714 AddCovalenceCommand("tpc", nameof(CommandTeleportCancel));
1715 AddCovalenceCommand("teleport.toplayer", nameof(CommandTeleportII));
1716 AddCovalenceCommand("teleport.topos", nameof(CommandTeleportII));
1717 AddCovalenceCommand("teleport.importhomes", nameof(CommandImportHomes));
1718 AddCovalenceCommand("spm", nameof(CommandSphereMonuments));
1719 FindMonuments(); // 1.2.2 location moved from Loaded() to fix outpost and bandit location not being set after a wipe
1720 }
1721
1722 List<string> validCommands = new List<string> { "outpost", "bandit", "tp", "home", "sethome", "listhomes", "tpn", "tpl", "tpsave", "tpremove", "tpb", "removehome", "radiushome", "deletehome", "tphome", "homehomes", "tpt", "tpr", "tpa", "wipehomes", "tphelp", "tpinfo", "teleport.toplayer", "teleport.topos", "teleport.importhomes", "town", "spm" };
1723
1724 void OnNewSave(string strFilename)
1725 {
1726 newSave = true;
1727 }
1728
1729 void OnServerSave()
1730 {
1731 SaveTeleportsAdmin();
1732 SaveTeleportsHome();
1733 SaveTeleportsTPR();
1734 SaveTeleportsTPT();
1735 SaveTeleportsTown();
1736 SaveTeleportsOutpost();
1737 SaveTeleportsBandit();
1738 }
1739
1740 void OnServerShutdown() => OnServerSave();
1741
1742 void Unload() => OnServerSave();
1743
1744 void OnPluginLoaded(Plugin plugin)
1745 {
1746 if (plugin.Name == "Economics")
1747 {
1748 Economics = plugin;
1749 }
1750 if (plugin.Name == "ServerRewards")
1751 {
1752 ServerRewards = plugin;
1753 }
1754 if (plugin.Name == "Friends")
1755 {
1756 Friends = plugin;
1757 }
1758 if (plugin.Name == "Clans")
1759 {
1760 Clans = plugin;
1761 }
1762 if (plugin.Name == "CompoundTeleport")
1763 {
1764 CompoundTeleport = plugin;
1765 }
1766 }
1767
1768 void OnPluginUnloaded(Plugin plugin)
1769 {
1770 if (plugin.Name == "Economics")
1771 {
1772 Economics = null;
1773 }
1774 if (plugin.Name == "ServerRewards")
1775 {
1776 ServerRewards = null;
1777 }
1778 if (plugin.Name == "Friends")
1779 {
1780 Friends = null;
1781 }
1782 if (plugin.Name == "Clans")
1783 {
1784 Clans = null;
1785 }
1786 if (plugin.Name == "CompoundTeleport")
1787 {
1788 CompoundTeleport = null;
1789 }
1790 }
1791
1792 void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
1793 {
1794 var player = entity.ToPlayer();
1795 if (player == null || hitInfo == null) return;
1796 if (hitInfo.damageTypes.Has(DamageType.Fall) && teleporting.ContainsKey(player.userID))
1797 {
1798 hitInfo.damageTypes = new DamageTypeList();
1799 teleporting.Remove(player.userID);
1800 }
1801 TeleportTimer teleportTimer;
1802 if (!TeleportTimers.TryGetValue(player.userID, out teleportTimer)) return;
1803 DamageType major = hitInfo.damageTypes.GetMajorityDamageType();
1804 if (!config.Settings.Interrupt.Hurt) return;
1805 NextTick(() =>
1806 {
1807 if (!player) return;
1808 if (!hitInfo.hasDamage || hitInfo.damageTypes.Total() <= 0) return;
1809 // 1.0.84 new checks for cold/heat based on major damage for the player
1810 if (major == DamageType.Cold && config.Settings.Interrupt.Cold)
1811 {
1812 if (player.metabolism.temperature.value <= config.Settings.MinimumTemp)
1813 {
1814 PrintMsgL(teleportTimer.OriginPlayer, "TPTooCold");
1815 if (teleportTimer.TargetPlayer != null)
1816 {
1817 PrintMsgL(teleportTimer.TargetPlayer, "InterruptedTarget", teleportTimer.OriginPlayer?.displayName);
1818 }
1819 teleportTimer.Timer.Destroy();
1820 TeleportTimers.Remove(player.userID);
1821 }
1822 }
1823 else if (major == DamageType.Heat && config.Settings.Interrupt.Hot)
1824 {
1825 if (player.metabolism.temperature.value >= config.Settings.MaximumTemp)
1826 {
1827 PrintMsgL(teleportTimer.OriginPlayer, "TPTooHot");
1828 if (teleportTimer.TargetPlayer != null)
1829 {
1830 PrintMsgL(teleportTimer.TargetPlayer, "InterruptedTarget", teleportTimer.OriginPlayer?.displayName);
1831 }
1832 teleportTimer.Timer.Destroy();
1833 TeleportTimers.Remove(player.userID);
1834 }
1835 }
1836 else
1837 {
1838 PrintMsgL(teleportTimer.OriginPlayer, "Interrupted");
1839 if (teleportTimer.TargetPlayer != null)
1840 {
1841 PrintMsgL(teleportTimer.TargetPlayer, "InterruptedTarget", teleportTimer.OriginPlayer?.displayName);
1842 }
1843 teleportTimer.Timer.Destroy();
1844 TeleportTimers.Remove(player.userID);
1845 }
1846 });
1847 }
1848
1849 void OnPlayerSleepEnded(BasePlayer player)
1850 {
1851 if (!player || !teleporting.ContainsKey(player.userID)) return;
1852 ulong userID = player.userID;
1853 timer.Once(3f, () => teleporting.Remove(userID));
1854 }
1855
1856 void OnPlayerDisconnected(BasePlayer player)
1857 {
1858 if (!player) return;
1859 Timer reqTimer;
1860 if (PendingRequests.TryGetValue(player.userID, out reqTimer))
1861 {
1862 var originPlayer = PlayersRequests[player.userID];
1863 if (originPlayer)
1864 {
1865 PlayersRequests.Remove(originPlayer.userID);
1866 PrintMsgL(originPlayer, "RequestTargetOff");
1867 }
1868 reqTimer.Destroy();
1869 PendingRequests.Remove(player.userID);
1870 PlayersRequests.Remove(player.userID);
1871 }
1872 TeleportTimer teleportTimer;
1873 if (TeleportTimers.TryGetValue(player.userID, out teleportTimer))
1874 {
1875 teleportTimer.Timer.Destroy();
1876 TeleportTimers.Remove(player.userID);
1877 }
1878 teleporting.Remove(player.userID);
1879 }
1880
1881 private void SaveTeleportsAdmin()
1882 {
1883 if (Admin == null || !changedAdmin) return;
1884 dataAdmin.WriteObject(Admin);
1885 changedAdmin = False;
1886 }
1887
1888 private void SaveTeleportsHome()
1889 {
1890 if (Home == null || !changedHome) return;
1891 dataHome.WriteObject(Home);
1892 changedHome = False;
1893 }
1894
1895 private void SaveTeleportsTPR()
1896 {
1897 if (TPR == null || !changedTPR) return;
1898 dataTPR.WriteObject(TPR);
1899 changedTPR = False;
1900 }
1901
1902 private void SaveTeleportsTPT()
1903 {
1904 if (TPT == null || !changedTPT) return;
1905 dataTPT.WriteObject(TPT);
1906 changedTPT = False;
1907 }
1908
1909 private void SaveTeleportsTown()
1910 {
1911 if (Town == null || !changedTown) return;
1912 dataTown.WriteObject(Town);
1913 changedTown = False;
1914 }
1915
1916 private void SaveTeleportsOutpost()
1917 {
1918 if (Outpost == null || !changedOutpost) return;
1919 dataOutpost.WriteObject(Outpost);
1920 changedOutpost = False;
1921 }
1922
1923 private void SaveTeleportsBandit()
1924 {
1925 if (Bandit == null || !changedBandit) return;
1926 dataBandit.WriteObject(Bandit);
1927 changedBandit = False;
1928 }
1929
1930 private void SaveLocation(BasePlayer player)
1931 {
1932 if (!IsAllowed(player, PermTpB)) return;
1933 AdminData adminData;
1934 if (!Admin.TryGetValue(player.userID, out adminData))
1935 Admin[player.userID] = adminData = new AdminData();
1936 adminData.PreviousLocation = player.transform.position;
1937 changedAdmin = True;
1938 PrintMsgL(player, "AdminTPBackSave");
1939 }
1940
1941 char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
1942 private readonly System.Text.StringBuilder _sb = new System.Text.StringBuilder();
1943
1944 string RandomString(int minAmount = 5, int maxAmount = 10)
1945 {
1946 _sb.Length = 0;
1947
1948 for (int i = 0; i <= UnityEngine.Random.Range(minAmount, maxAmount); i++)
1949 _sb.Append(chars[UnityEngine.Random.Range(0, chars.Length)]);
1950
1951 return _sb.ToString();
1952 }
1953
1954 void FindMonuments()
1955 {
1956 var realWidth = 0f;
1957 string name = null;
1958 foreach (var monument in UnityEngine.Object.FindObjectsOfType<MonumentInfo>())
1959 {
1960 var monPos = monument.transform.position;
1961 name = monument.displayPhrase.english.TrimEnd();
1962 if (string.IsNullOrEmpty(name))
1963 {
1964 if (monument.name.Contains("cave"))
1965 {
1966 name = (monument.name.Contains("cave_small") ? "Small Cave" : monument.name.Contains("cave_medium") ? "Medium Cave" : "Large Cave") + ":" + RandomString();
1967 }
1968 else name = monument.name;
1969 }
1970 realWidth = monument.name == "OilrigAI" ? 100f : monument.name == "OilrigAI2" ? 200f : 0f;
1971#if DEBUG
1972 Puts($"Found {name}, extents {monument.Bounds.extents}");
1973#endif
1974 if (realWidth > 0f)
1975 {
1976#if DEBUG
1977 Puts($" corrected to {realWidth}");
1978#endif
1979 }
1980 if (monument.name.Contains("cave"))
1981 {
1982#if DEBUG
1983 Puts(" Adding to cave list");
1984#endif
1985 if (caves.ContainsKey(name)) name += RandomString();
1986 caves.Add(name, monPos);
1987 }
1988 else if (monument.name == outpostPrefab)
1989 {
1990 if (config.Outpost.Location != Zero && Vector3.Distance(monument.transform.position, config.Outpost.Location) > 100f)
1991 {
1992#if DEBUG
1993 Puts("Invalid Outpost location detected");
1994#endif
1995 config.Outpost.Location = Zero;
1996 }
1997 if (config.Settings.AutoGenOutpost && config.Outpost.Location == Zero)
1998 {
1999#if DEBUG
2000 Puts(" Adding Outpost target");
2001#endif
2002 var ents = new List<BaseEntity>();
2003 Vis.Entities<BaseEntity>(monPos, 50, ents);
2004 foreach (BaseEntity entity in ents)
2005 {
2006 if (entity.prefabID == 3858860623)
2007 {
2008 config.Outpost.Location = entity.transform.position + entity.transform.forward + new Vector3(0f, 1f, 0f);
2009 SaveConfig();
2010 break;
2011 }
2012 else if (entity is Workbench)
2013 {
2014 config.Outpost.Location = entity.transform.position + entity.transform.forward + new Vector3(0f, 1f, 0f);
2015 SaveConfig();
2016 break;
2017 }
2018 else if (entity is BaseChair)
2019 {
2020 config.Outpost.Location = entity.transform.position + entity.transform.right + new Vector3(0f, 1f, 0f);
2021 SaveConfig();
2022 break;
2023 }
2024 }
2025
2026 if (!config.Settings.OutpostEnabled) OutpostTPDisabledMessage = "OutpostTPDisabledConfig";
2027 else if (config.Outpost.Location == Zero) OutpostTPDisabledMessage = "OutpostTPDisabledNoLocationAutoGen";
2028 }
2029 }
2030 else if (monument.name == banditPrefab)
2031 {
2032 if (config.Bandit.Location != Zero && Vector3.Distance(monument.transform.position, config.Bandit.Location) > 100f)
2033 {
2034#if DEBUG
2035 Puts("Invalid Bandit location detected");
2036#endif
2037 config.Bandit.Location = Zero;
2038 }
2039 if (config.Settings.AutoGenBandit && config.Bandit.Location == Zero)
2040 {
2041#if DEBUG
2042 Puts(" Adding BanditTown target");
2043#endif
2044 var ents = new List<BaseEntity>();
2045 Vis.Entities<BaseEntity>(monPos, 50, ents);
2046 foreach (BaseEntity entity in ents)
2047 {
2048 if (entity.prefabID == 3858860623)
2049 {
2050 config.Bandit.Location = entity.transform.position + entity.transform.forward + new Vector3(0f, 1f, 0f);
2051 SaveConfig();
2052 break;
2053 }
2054 else if (entity is Workbench)
2055 {
2056 config.Bandit.Location = entity.transform.position + entity.transform.forward + new Vector3(0f, 1f, 0f);
2057 SaveConfig();
2058 break;
2059 }
2060 else if (entity is BaseChair)
2061 {
2062 config.Bandit.Location = entity.transform.position + entity.transform.right + new Vector3(0f, 1f, 0f);
2063 SaveConfig();
2064 break;
2065 }
2066 }
2067 }
2068
2069 if (!config.Settings.BanditEnabled) BanditTPDisabledMessage = "BanditTPDisabledConfig";
2070 else if (config.Bandit.Location == Zero) BanditTPDisabledMessage = "BanditTPDisabledNoLocationAutoGen";
2071 }
2072 else
2073 {
2074 if (monuments.ContainsKey(name)) name += ":" + RandomString(5, 5);
2075 if (monument.name.Contains("power_sub")) name = monument.name.Substring(monument.name.LastIndexOf("/") + 1).Replace(".prefab", "") + ":" + RandomString(5, 5);
2076 float radius = GetMonumentFloat(name);
2077 monuments[name] = new MonInfo() { Position = monPos, Radius = radius };
2078#if DEBUG
2079 Puts($"Adding Monument: {name}, pos: {monPos}, size: {radius}");
2080#endif
2081 }
2082 }
2083
2084 if (config.Outpost.Location == Zero)
2085 {
2086 if (!config.Settings.AutoGenOutpost) OutpostTPDisabledMessage = "OutpostTPDisabledNoLocation";
2087 outpostEnabled = False;
2088 }
2089
2090 if (config.Bandit.Location == Zero)
2091 {
2092 if (!config.Settings.AutoGenBandit) BanditTPDisabledMessage = "BanditTPDisabledNoLocation";
2093 banditEnabled = False;
2094 }
2095 }
2096
2097 private void CommandToggle(IPlayer p, string command, string[] args)
2098 {
2099 if (!p.IsAdmin) return;
2100
2101 if (args.Length == 0)
2102 {
2103 p.Reply("tnt commandname");
2104 return;
2105 }
2106
2107 string arg = args[0].ToLower();
2108
2109 if (!validCommands.Contains(arg))
2110 {
2111 p.Reply("Invalid command name: {0}", null, string.Join(", ", validCommands.ToList()));
2112 return;
2113 }
2114
2115 if (arg == command.ToLower()) return;
2116
2117 if (!DisabledTPT.DisabledCommands.Contains(arg))
2118 DisabledTPT.DisabledCommands.Add(arg);
2119 else DisabledTPT.DisabledCommands.Remove(arg);
2120
2121 dataDisabled.WriteObject(DisabledTPT);
2122 p.Reply("{0} {1}", null, DisabledTPT.DisabledCommands.Contains(arg) ? "Disabled:" : "Enabled:", arg);
2123 }
2124
2125 private void CommandTeleport(IPlayer p, string command, string[] args)
2126 {
2127 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2128 var player = p.Object as BasePlayer;
2129 if (!player || !IsAllowedMsg(player, PermTp) || !player.IsConnected || player.IsSleeping()) return;
2130 BasePlayer target;
2131 float x, y, z;
2132 switch (args.Length)
2133 {
2134 case 1:
2135 target = FindPlayersSingle(args[0], player);
2136 if (target == null) return;
2137 if (target == player)
2138 {
2139#if DEBUG
2140 Puts("Debug mode - allowing self teleport.");
2141#else
2142 PrintMsgL(player, "CantTeleportToSelf");
2143 return;
2144#endif
2145 }
2146 Teleport(player, target);
2147 PrintMsgL(player, "AdminTP", target.displayName);
2148 Puts(_("LogTeleport", null, player.displayName, target.displayName));
2149 if (config.Admin.AnnounceTeleportToTarget)
2150 PrintMsgL(target, "AdminTPTarget", player.displayName);
2151 break;
2152 case 2:
2153 var origin = FindPlayersSingle(args[0], player);
2154 if (origin == null) return;
2155 target = FindPlayersSingle(args[1], player);
2156 if (target == null) return;
2157 if (target == origin)
2158 {
2159 PrintMsgL(player, "CantTeleportPlayerToSelf");
2160 return;
2161 }
2162 Teleport(origin, target);
2163 PrintMsgL(player, "AdminTPPlayers", origin.displayName, target.displayName);
2164 PrintMsgL(origin, "AdminTPPlayer", player.displayName, target.displayName);
2165 if (config.Admin.AnnounceTeleportToTarget)
2166 PrintMsgL(target, "AdminTPPlayerTarget", player.displayName, origin.displayName);
2167 Puts(_("LogTeleportPlayer", null, player.displayName, origin.displayName, target.displayName));
2168 break;
2169 case 3:
2170 if (!float.TryParse(args[0], out x) || !float.TryParse(args[1], out y) || !float.TryParse(args[2], out z))
2171 {
2172 PrintMsgL(player, "InvalidCoordinates");
2173 return;
2174 }
2175 if (config.Settings.CheckBoundaries && !CheckBoundaries(x, y, z)) // added this option because I HATE boundaries
2176 {
2177 PrintMsgL(player, "AdminTPOutOfBounds");
2178 PrintMsgL(player, "AdminTPBoundaries", boundary);
2179 return;
2180 }
2181 Teleport(player, x, y, z);
2182 PrintMsgL(player, "AdminTPCoordinates", player.transform.position);
2183 Puts(_("LogTeleport", null, player.displayName, player.transform.position));
2184 break;
2185 case 4:
2186 target = FindPlayersSingle(args[0], player);
2187 if (target == null) return;
2188 if (!float.TryParse(args[1], out x) || !float.TryParse(args[2], out y) || !float.TryParse(args[3], out z))
2189 {
2190 PrintMsgL(player, "InvalidCoordinates");
2191 return;
2192 }
2193 if (!CheckBoundaries(x, y, z))
2194 {
2195 PrintMsgL(player, "AdminTPOutOfBounds");
2196 PrintMsgL(player, "AdminTPBoundaries", boundary);
2197 return;
2198 }
2199 Teleport(target, x, y, z);
2200 if (player == target)
2201 {
2202 PrintMsgL(player, "AdminTPCoordinates", player.transform.position);
2203 Puts(_("LogTeleport", null, player.displayName, player.transform.position));
2204 }
2205 else
2206 {
2207 PrintMsgL(player, "AdminTPTargetCoordinates", target.displayName, player.transform.position);
2208 if (config.Admin.AnnounceTeleportToTarget)
2209 PrintMsgL(target, "AdminTPTargetCoordinatesTarget", player.displayName, player.transform.position);
2210 Puts(_("LogTeleportPlayer", null, player.displayName, target.displayName, player.transform.position));
2211 }
2212 break;
2213 default:
2214 PrintMsgL(player, "SyntaxCommandTP");
2215 break;
2216 }
2217 }
2218
2219 private void CommandTeleportNear(IPlayer p, string command, string[] args)
2220 {
2221 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2222 var player = p.Object as BasePlayer;
2223 if (!player || !IsAllowedMsg(player, PermTpN) || !player.IsConnected || player.IsSleeping()) return;
2224 switch (args.Length)
2225 {
2226 case 1:
2227 case 2:
2228 var target = FindPlayersSingle(args[0], player);
2229 if (target == null) return;
2230 if (target == player)
2231 {
2232#if DEBUG
2233 Puts("Debug mode - allowing self teleport.");
2234#else
2235 PrintMsgL(player, "CantTeleportToSelf");
2236 return;
2237#endif
2238 }
2239 int distance = config.Admin.TeleportNearDefaultDistance;
2240 if (args.Length == 2 && !int.TryParse(args[1], out distance))
2241 distance = config.Admin.TeleportNearDefaultDistance;
2242 float x = UnityEngine.Random.Range(-distance, distance);
2243 var z = (float)System.Math.Sqrt(System.Math.Pow(distance, 2) - System.Math.Pow(x, 2));
2244 var destination = target.transform.position;
2245 destination.x = destination.x - x;
2246 destination.z = destination.z - z;
2247 Teleport(player, GetGroundBuilding(destination));
2248 PrintMsgL(player, "AdminTP", target.displayName);
2249 Puts(_("LogTeleport", null, player.displayName, target.displayName));
2250 if (config.Admin.AnnounceTeleportToTarget)
2251 PrintMsgL(target, "AdminTPTarget", player.displayName);
2252 break;
2253 default:
2254 PrintMsgL(player, "SyntaxCommandTPN");
2255 break;
2256 }
2257 }
2258
2259 private void CommandTeleportLocation(IPlayer p, string command, string[] args)
2260 {
2261 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2262 var player = p.Object as BasePlayer;
2263 if (!player || !IsAllowedMsg(player, PermTpL) || !player.IsConnected || player.IsSleeping()) return;
2264 AdminData adminData;
2265 if (!Admin.TryGetValue(player.userID, out adminData) || adminData.Locations.Count <= 0)
2266 {
2267 PrintMsgL(player, "AdminLocationListEmpty");
2268 return;
2269 }
2270 switch (args.Length)
2271 {
2272 case 0:
2273 PrintMsgL(player, "AdminLocationList");
2274 foreach (var location in adminData.Locations)
2275 PrintMsgL(player, $"{location.Key} {location.Value}");
2276 break;
2277 case 1:
2278 Vector3 loc;
2279 if (!adminData.Locations.TryGetValue(args[0], out loc))
2280 {
2281 PrintMsgL(player, "LocationNotFound");
2282 return;
2283 }
2284 Teleport(player, loc);
2285 PrintMsgL(player, "AdminTPLocation", args[0]);
2286 break;
2287 default:
2288 PrintMsgL(player, "SyntaxCommandTPL");
2289 break;
2290 }
2291 }
2292
2293 private void CommandSaveTeleportLocation(IPlayer p, string command, string[] args)
2294 {
2295 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2296 var player = p.Object as BasePlayer;
2297 if (!player || !IsAllowedMsg(player, PermTpSave) || !player.IsConnected || player.IsSleeping()) return;
2298 if (args.Length != 1)
2299 {
2300 PrintMsgL(player, "SyntaxCommandTPSave");
2301 return;
2302 }
2303 AdminData adminData;
2304 if (!Admin.TryGetValue(player.userID, out adminData))
2305 Admin[player.userID] = adminData = new AdminData();
2306 Vector3 location;
2307 if (adminData.Locations.TryGetValue(args[0], out location))
2308 {
2309 PrintMsgL(player, "LocationExists", location);
2310 return;
2311 }
2312 var positionCoordinates = player.transform.position;
2313 foreach (var loc in adminData.Locations)
2314 {
2315 if ((positionCoordinates - loc.Value).magnitude < config.Admin.LocationRadius)
2316 {
2317 PrintMsgL(player, "LocationExistsNearby", loc.Key);
2318 return;
2319 }
2320 }
2321 adminData.Locations[args[0]] = positionCoordinates;
2322 PrintMsgL(player, "AdminTPLocationSave");
2323 changedAdmin = True;
2324 }
2325
2326 private void CommandRemoveTeleportLocation(IPlayer p, string command, string[] args)
2327 {
2328 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2329 var player = p.Object as BasePlayer;
2330 if (!player || !IsAllowedMsg(player, PermTpRemove) || !player.IsConnected || player.IsSleeping()) return;
2331 if (args.Length != 1)
2332 {
2333 PrintMsgL(player, "SyntaxCommandTPRemove");
2334 return;
2335 }
2336 AdminData adminData;
2337 if (!Admin.TryGetValue(player.userID, out adminData) || adminData.Locations.Count <= 0)
2338 {
2339 PrintMsgL(player, "AdminLocationListEmpty");
2340 return;
2341 }
2342 if (adminData.Locations.Remove(args[0]))
2343 {
2344 PrintMsgL(player, "AdminTPLocationRemove", args[0]);
2345 changedAdmin = True;
2346 return;
2347 }
2348 PrintMsgL(player, "LocationNotFound");
2349 }
2350
2351 private void CommandTeleportBack(IPlayer p, string command, string[] args)
2352 {
2353 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2354 var player = p.Object as BasePlayer;
2355 if (!player || !IsAllowedMsg(player, PermTpB) || !player.IsConnected || player.IsSleeping()) return;
2356 if (args.Length != 0)
2357 {
2358 PrintMsgL(player, "SyntaxCommandTPB");
2359 return;
2360 }
2361 AdminData adminData;
2362 if (!Admin.TryGetValue(player.userID, out adminData) || adminData.PreviousLocation == Zero)
2363 {
2364 PrintMsgL(player, "NoPreviousLocationSaved");
2365 return;
2366 }
2367
2368 Teleport(player, adminData.PreviousLocation);
2369 adminData.PreviousLocation = Zero;
2370 changedAdmin = True;
2371 PrintMsgL(player, "AdminTPBack");
2372 Puts(_("LogTeleportBack", null, player.displayName));
2373 }
2374
2375 private void CommandSetHome(IPlayer p, string command, string[] args)
2376 {
2377 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2378 var player = p.Object as BasePlayer;
2379 if (!player || !IsAllowed(player, PermHome) || !player.IsConnected || player.IsSleeping()) return;
2380 if (!config.Settings.HomesEnabled) { p.Reply("Homes are not enabled in the config."); return; }
2381 if (args.Length != 1)
2382 {
2383 PrintMsgL(player, "SyntaxCommandSetHome");
2384 return;
2385 }
2386 var err = CheckPlayer(player, False, CanCraftHome(player), True, "home");
2387 if (err != null)
2388 {
2389 PrintMsgL(player, err);
2390 return;
2391 }
2392 if (!player.CanBuild())
2393 {
2394 PrintMsgL(player, "HomeTPBuildingBlocked");
2395 return;
2396 }
2397 if (!args[0].All(char.IsLetterOrDigit))
2398 {
2399 PrintMsgL(player, "InvalidCharacter");
2400 return;
2401 }
2402 HomeData homeData;
2403 if (!Home.TryGetValue(player.userID, out homeData))
2404 Home[player.userID] = homeData = new HomeData();
2405 var limit = GetHigher(player, config.Home.VIPHomesLimits, config.Home.HomesLimit, true);
2406 if (limit > 0 && homeData.Locations.Count >= limit)
2407 {
2408 PrintMsgL(player, "HomeMaxLocations", limit);
2409 return;
2410 }
2411 Vector3 location;
2412 if (homeData.Locations.TryGetValue(args[0], out location))
2413 {
2414 PrintMsgL(player, "HomeExists", location);
2415 return;
2416 }
2417 var positionCoordinates = player.transform.position;
2418 foreach (var loc in homeData.Locations)
2419 {
2420 if ((positionCoordinates - loc.Value).magnitude < config.Home.LocationRadius)
2421 {
2422 PrintMsgL(player, "HomeExistsNearby", loc.Key);
2423 return;
2424 }
2425 }
2426 err = CanPlayerTeleport(player);
2427 if (err != null)
2428 {
2429 SendReply(player, err);
2430 return;
2431 }
2432
2433 if (player.IsAdmin && config.Settings.DrawHomeSphere) player.SendConsoleCommand("ddraw.sphere", 30f, Color.blue, GetGround(positionCoordinates), 2.5f);
2434
2435 err = CheckFoundation(player.userID, positionCoordinates);
2436 if (err != null)
2437 {
2438 PrintMsgL(player, err);
2439 return;
2440 }
2441 err = CheckInsideBlock(positionCoordinates);
2442 if (err != null)
2443 {
2444 PrintMsgL(player, err);
2445 return;
2446 }
2447 err = CheckInsideBattery(positionCoordinates);
2448 if (err != null)
2449 {
2450 PrintMsgL(player, err);
2451 return;
2452 }
2453 homeData.Locations[args[0]] = positionCoordinates;
2454 changedHome = True;
2455 PrintMsgL(player, "HomeSave");
2456 PrintMsgL(player, "HomeQuota", homeData.Locations.Count, limit);
2457 }
2458
2459 private void CommandRemoveHome(IPlayer p, string command, string[] args)
2460 {
2461 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2462 if (!config.Settings.HomesEnabled) { p.Reply("Homes are not enabled in the config."); return; }
2463 var player = p.Object as BasePlayer;
2464 if (!player || !IsAllowed(player, PermHome) || !player.IsConnected || player.IsSleeping()) return;
2465 if (args.Length != 1)
2466 {
2467 PrintMsgL(player, "SyntaxCommandRemoveHome");
2468 return;
2469 }
2470 HomeData homeData;
2471 if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
2472 {
2473 PrintMsgL(player, "HomeListEmpty");
2474 return;
2475 }
2476 if (homeData.Locations.Remove(args[0]))
2477 {
2478 changedHome = True;
2479 PrintMsgL(player, "HomeRemove", args[0]);
2480 }
2481 else
2482 PrintMsgL(player, "HomeNotFound");
2483 }
2484
2485 private void CommandHome(IPlayer p, string command, string[] args)
2486 {
2487 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2488 if (!config.Settings.HomesEnabled) { p.Reply("Homes are not enabled in the config."); return; }
2489 var player = p.Object as BasePlayer;
2490 if (!player || !IsAllowed(player, PermHome) || !player.IsConnected || player.IsSleeping()) return;
2491 if (args.Length == 0)
2492 {
2493 PrintMsgL(player, "SyntaxCommandHome");
2494 if (IsAllowed(player)) PrintMsgL(player, "SyntaxCommandHomeAdmin");
2495 return;
2496 }
2497 switch (args[0].ToLower())
2498 {
2499 case "add":
2500 CommandSetHome(p, command, args.Skip(1).ToArray());
2501 break;
2502 case "list":
2503 CommandListHomes(p, command, args.Skip(1).ToArray());
2504 break;
2505 case "remove":
2506 CommandRemoveHome(p, command, args.Skip(1).ToArray());
2507 break;
2508 case "radius":
2509 CommandHomeRadius(p, command, args.Skip(1).ToArray());
2510 break;
2511 case "delete":
2512 CommandHomeDelete(p, command, args.Skip(1).ToArray());
2513 break;
2514 case "tp":
2515 CommandHomeAdminTP(p, command, args.Skip(1).ToArray());
2516 break;
2517 case "homes":
2518 CommandHomeHomes(p, command, args.Skip(1).ToArray());
2519 break;
2520 case "wipe":
2521 CommandWipeHomes(p, command, args.Skip(1).ToArray());
2522 break;
2523 default:
2524 cmdChatHomeTP(player, command, args);
2525 break;
2526 }
2527 }
2528
2529 private void CommandHomeRadius(IPlayer p, string command, string[] args)
2530 {
2531 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2532 var player = p.Object as BasePlayer;
2533 if (!player || !IsAllowedMsg(player, PermRadiusHome) || !player.IsConnected || player.IsSleeping()) return;
2534 float radius;
2535 if (args.Length != 1 || !float.TryParse(args[0], out radius)) radius = 10;
2536 var found = False;
2537 foreach (var homeData in Home)
2538 {
2539 var toRemove = new List<string>();
2540 var target = RustCore.FindPlayerById(homeData.Key)?.displayName ?? homeData.Key.ToString();
2541 foreach (var location in homeData.Value.Locations)
2542 {
2543 if ((player.transform.position - location.Value).magnitude <= radius)
2544 {
2545 if (CheckFoundation(homeData.Key, location.Value) != null)
2546 {
2547 toRemove.Add(location.Key);
2548 continue;
2549 }
2550 var entity = GetFoundationOwned(location.Value, homeData.Key);
2551 if (entity == null) continue;
2552 player.SendConsoleCommand("ddraw.text", 30f, Color.blue, entity.CenterPoint() + new Vector3(0, .5f), $"<size=20>{target} - {location.Key} {location.Value}</size>");
2553 DrawBox(player, entity.CenterPoint(), entity.transform.rotation, entity.bounds.size);
2554 PrintMsg(player, $"{target} - {location.Key} {location.Value}");
2555 found = True;
2556 }
2557 }
2558 foreach (var loc in toRemove)
2559 {
2560 homeData.Value.Locations.Remove(loc);
2561 changedHome = True;
2562 }
2563 }
2564 if (!found)
2565 PrintMsgL(player, "HomeNoFound");
2566 }
2567
2568 private void CommandHomeDelete(IPlayer p, string command, string[] args)
2569 {
2570 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2571 var player = p.Object as BasePlayer;
2572 if (!player || !IsAllowedMsg(player, PermDeleteHome) || !player.IsConnected || player.IsSleeping()) return;
2573 if (args.Length != 2)
2574 {
2575 PrintMsgL(player, "SyntaxCommandHomeDelete");
2576 return;
2577 }
2578 var userId = FindPlayersSingleId(args[0], player);
2579 if (userId <= 0) return;
2580 HomeData targetHome;
2581 if (!Home.TryGetValue(userId, out targetHome) || !targetHome.Locations.Remove(args[1]))
2582 {
2583 PrintMsgL(player, "HomeNotFound");
2584 return;
2585 }
2586 changedHome = True;
2587 PrintMsgL(player, "HomeDelete", args[0], args[1]);
2588 }
2589
2590 private void CommandHomeAdminTP(IPlayer p, string command, string[] args)
2591 {
2592 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2593 var player = p.Object as BasePlayer;
2594 if (!player || !IsAllowedMsg(player, PermTpHome) || !player.IsConnected || player.IsSleeping()) return;
2595 if (args.Length != 2)
2596 {
2597 PrintMsgL(player, "SyntaxCommandHomeAdminTP");
2598 return;
2599 }
2600 var userId = FindPlayersSingleId(args[0], player);
2601 if (userId <= 0) return;
2602 HomeData targetHome;
2603 Vector3 location;
2604 if (!Home.TryGetValue(userId, out targetHome) || !targetHome.Locations.TryGetValue(args[1], out location))
2605 {
2606 PrintMsgL(player, "HomeNotFound");
2607 return;
2608 }
2609
2610 Teleport(player, location);
2611 PrintMsgL(player, "HomeAdminTP", args[0], args[1]);
2612 }
2613
2614 // Check that plugins are available and enabled for CheckEconomy()
2615 private bool UseEconomy()
2616 {
2617 if ((config.Settings.UseEconomics && Economics) ||
2618 (config.Settings.UseServerRewards && ServerRewards))
2619 {
2620 return True;
2621 }
2622 return False;
2623 }
2624
2625 // Check balance on multiple plugins and optionally withdraw money from the player
2626 private bool CheckEconomy(BasePlayer player, double bypass, bool withdraw = False, bool deposit = False)
2627 {
2628 double balance = 0;
2629 bool foundmoney = False;
2630
2631 // Check Economics first. If not in use or balance low, check ServerRewards below
2632 if (config.Settings.UseEconomics && Economics)
2633 {
2634 balance = (double)Economics?.CallHook("Balance", player.UserIDString);
2635 if (balance >= bypass)
2636 {
2637 foundmoney = True;
2638 if (withdraw)
2639 {
2640 var w = (bool)Economics?.CallHook("Withdraw", player.userID, bypass);
2641 return w;
2642 }
2643 else if (deposit)
2644 {
2645 Economics?.CallHook("Deposit", player.userID, bypass);
2646 }
2647 }
2648 }
2649
2650 // No money via Economics, or plugin not in use. Try ServerRewards.
2651 if (config.Settings.UseServerRewards && ServerRewards)
2652 {
2653 object bal = ServerRewards?.Call("CheckPoints", player.userID);
2654 balance = Convert.ToDouble(bal);
2655 if (balance >= bypass && !foundmoney)
2656 {
2657 foundmoney = True;
2658 if (withdraw)
2659 {
2660 var w = (bool)ServerRewards?.Call("TakePoints", player.userID, (int)bypass);
2661 return w;
2662 }
2663 else if (deposit)
2664 {
2665 ServerRewards?.Call("AddPoints", player.userID, (int)bypass);
2666 }
2667 }
2668 }
2669
2670 // Just checking balance without withdrawal - did we find anything?
2671 if (foundmoney)
2672 {
2673 return True;
2674 }
2675 return False;
2676 }
2677
2678 private void cmdChatHomeTP(BasePlayer player, string command, string[] args)
2679 {
2680 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { player.ChatMessage("Disabled command."); return; }
2681 if (!IsAllowed(player, PermHome) || !player.IsConnected || player.IsSleeping()) return;
2682 bool paidmoney = False;
2683 if (!config.Settings.HomesEnabled) { player.ChatMessage("Homes are not enabled in the config."); return; }
2684 if (args.Length < 1)
2685 {
2686 PrintMsgL(player, "SyntaxCommandHome");
2687 return;
2688 }
2689 var err = CheckPlayer(player, config.Home.UsableOutOfBuildingBlocked, CanCraftHome(player), True, "home");
2690 if (err != null)
2691 {
2692 PrintMsgL(player, err);
2693 return;
2694 }
2695 HomeData homeData;
2696 if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
2697 {
2698 PrintMsgL(player, "HomeListEmpty");
2699 return;
2700 }
2701 Vector3 location;
2702 if (!homeData.Locations.TryGetValue(args[0], out location))
2703 {
2704 PrintMsgL(player, "HomeNotFound");
2705 return;
2706 }
2707 err = CheckFoundation(player.userID, location) ?? CheckTargetLocation(player, location, config.Home.UsableIntoBuildingBlocked, config.Home.CupOwnerAllowOnBuildingBlocked);
2708 if (err != null)
2709 {
2710 PrintMsgL(player, "HomeRemovedInvalid", args[0]);
2711 homeData.Locations.Remove(args[0]);
2712 changedHome = True;
2713 return;
2714 }
2715 err = CheckInsideBlock(location);
2716 if (err != null)
2717 {
2718 PrintMsgL(player, "HomeRemovedInsideBlock", args[0]);
2719 homeData.Locations.Remove(args[0]);
2720 changedHome = True;
2721 return;
2722 }
2723 var timestamp = Facepunch.Math.Epoch.Current;
2724 var currentDate = DateTime.Now.ToString("d");
2725 if (homeData.Teleports.Date != currentDate)
2726 {
2727 homeData.Teleports.Amount = 0;
2728 homeData.Teleports.Date = currentDate;
2729 }
2730 var cooldown = GetLower(player, config.Home.VIPCooldowns, config.Home.Cooldown);
2731 if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
2732 {
2733 var cmdSent = "";
2734 bool foundmoney = CheckEconomy(player, config.Home.Bypass);
2735 try
2736 {
2737 cmdSent = args[1].ToLower();
2738 }
2739 catch { }
2740
2741 bool payalso = False;
2742 if (config.Home.Pay > 0)
2743 {
2744 payalso = True;
2745 }
2746 if ((config.Settings.BypassCMD != null) && (cmdSent == config.Settings.BypassCMD.ToLower()))
2747 {
2748 if (foundmoney)
2749 {
2750 CheckEconomy(player, config.Home.Bypass, True);
2751 paidmoney = True;
2752 PrintMsgL(player, "HomeTPCooldownBypass", config.Home.Bypass);
2753 if (payalso)
2754 {
2755 PrintMsgL(player, "PayToHome", config.Home.Pay);
2756 }
2757 }
2758 else
2759 {
2760 PrintMsgL(player, "HomeTPCooldownBypassF", config.Home.Bypass);
2761 return;
2762 }
2763 }
2764 else if (UseEconomy())
2765 {
2766 var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
2767 PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
2768 if (config.Home.Bypass > 0 && config.Settings.BypassCMD != null)
2769 {
2770 PrintMsgL(player, "HomeTPCooldownBypassP", config.Home.Bypass);
2771 PrintMsgL(player, "HomeTPCooldownBypassP2", config.Settings.BypassCMD);
2772 }
2773 return;
2774 }
2775 else
2776 {
2777 var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
2778 PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
2779 return;
2780 }
2781 }
2782 var limit = GetHigher(player, config.Home.VIPDailyLimits, config.Home.DailyLimit, true);
2783 if (limit > 0 && homeData.Teleports.Amount >= limit)
2784 {
2785 PrintMsgL(player, "HomeTPLimitReached", limit);
2786 return;
2787 }
2788 if (TeleportTimers.ContainsKey(player.userID))
2789 {
2790 PrintMsgL(player, "TeleportPending");
2791 return;
2792 }
2793 err = CanPlayerTeleport(player);
2794 if (err != null)
2795 {
2796 SendReply(player, err);
2797 return;
2798 }
2799 err = CheckItems(player);
2800 if (err != null)
2801 {
2802 PrintMsgL(player, "TPBlockedItem", err);
2803 return;
2804 }
2805
2806 var countdown = GetLower(player, config.Home.VIPCountdowns, config.Home.Countdown);
2807 TeleportTimers[player.userID] = new TeleportTimer
2808 {
2809 OriginPlayer = player,
2810 Timer = timer.Once(countdown, () =>
2811 {
2812#if DEBUG
2813 Puts("Calling CheckPlayer from cmdChatHomeTP");
2814#endif
2815 err = CheckPlayer(player, config.Home.UsableOutOfBuildingBlocked, CanCraftHome(player), True, "home");
2816 if (err != null)
2817 {
2818 PrintMsgL(player, "Interrupted");
2819 PrintMsgL(player, err);
2820 if (paidmoney)
2821 {
2822 paidmoney = False;
2823 CheckEconomy(player, config.Home.Bypass, False, True);
2824 }
2825 TeleportTimers.Remove(player.userID);
2826 return;
2827 }
2828 err = CanPlayerTeleport(player);
2829 if (err != null)
2830 {
2831 PrintMsgL(player, "Interrupted");
2832 PrintMsgL(player, err);
2833 if (paidmoney)
2834 {
2835 paidmoney = False;
2836 CheckEconomy(player, config.Home.Bypass, False, True);
2837 }
2838 TeleportTimers.Remove(player.userID);
2839 return;
2840 }
2841 err = CheckItems(player);
2842 if (err != null)
2843 {
2844 PrintMsgL(player, "Interrupted");
2845 PrintMsgL(player, "TPBlockedItem", err);
2846 if (paidmoney)
2847 {
2848 paidmoney = False;
2849 CheckEconomy(player, config.Home.Bypass, False, True);
2850 }
2851 TeleportTimers.Remove(player.userID);
2852 return;
2853 }
2854 err = CheckFoundation(player.userID, location) ?? CheckTargetLocation(player, location, config.Home.UsableIntoBuildingBlocked, config.Home.CupOwnerAllowOnBuildingBlocked);
2855 if (err != null)
2856 {
2857 PrintMsgL(player, "HomeRemovedInvalid", args[0]);
2858 homeData.Locations.Remove(args[0]);
2859 changedHome = True;
2860 if (paidmoney)
2861 {
2862 paidmoney = False;
2863 CheckEconomy(player, config.Home.Bypass, False, True);
2864 }
2865 return;
2866 }
2867 err = CheckInsideBlock(location);
2868 if (err != null)
2869 {
2870 PrintMsgL(player, "HomeRemovedInsideBlock", args[0]);
2871 homeData.Locations.Remove(args[0]);
2872 changedHome = True;
2873 if (paidmoney)
2874 {
2875 paidmoney = False;
2876 CheckEconomy(player, config.Home.Bypass, False, True);
2877 }
2878 return;
2879 }
2880
2881 if (UseEconomy())
2882 {
2883 if (config.Home.Pay > 0 && !CheckEconomy(player, config.Home.Pay))
2884 {
2885 PrintMsgL(player, "Interrupted");
2886 PrintMsgL(player, "TPNoMoney", config.Home.Pay);
2887
2888 TeleportTimers.Remove(player.userID);
2889 return;
2890 }
2891 else if (config.Home.Pay > 0)
2892 {
2893 var w = CheckEconomy(player, (double)config.Home.Pay, True);
2894 PrintMsgL(player, "TPMoney", (double)config.Home.Pay);
2895 }
2896 }
2897
2898 Teleport(player, location);
2899 homeData.Teleports.Amount++;
2900 homeData.Teleports.Timestamp = timestamp;
2901 changedHome = True;
2902 PrintMsgL(player, "HomeTP", args[0]);
2903 if (limit > 0) PrintMsgL(player, "HomeTPAmount", limit - homeData.Teleports.Amount);
2904 TeleportTimers.Remove(player.userID);
2905 })
2906 };
2907 PrintMsgL(player, "HomeTPStarted", args[0], countdown);
2908 }
2909
2910 private void CommandListHomes(IPlayer p, string command, string[] args)
2911 {
2912 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2913 var player = p.Object as BasePlayer;
2914 if (!player || !player.IsConnected || player.IsSleeping()) return;
2915 if (!config.Settings.HomesEnabled) { p.Reply("Homes are not enabled in the config."); return; }
2916 if (args.Length != 0)
2917 {
2918 PrintMsgL(player, "SyntaxCommandListHomes");
2919 return;
2920 }
2921 HomeData homeData;
2922 if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
2923 {
2924 PrintMsgL(player, "HomeListEmpty");
2925 return;
2926 }
2927 PrintMsgL(player, "HomeList");
2928 if (config.Home.CheckValidOnList)
2929 {
2930 var toRemove = new List<string>();
2931 foreach (var location in homeData.Locations)
2932 {
2933 var err = CheckFoundation(player.userID, location.Value);
2934 if (err != null)
2935 {
2936 toRemove.Add(location.Key);
2937 continue;
2938 }
2939 PrintMsgL(player, $"{location.Key} {location.Value}");
2940 }
2941 foreach (var loc in toRemove)
2942 {
2943 PrintMsgL(player, "HomeRemovedInvalid", loc);
2944 homeData.Locations.Remove(loc);
2945 changedHome = True;
2946 }
2947 return;
2948 }
2949 foreach (var location in homeData.Locations)
2950 PrintMsgL(player, $"{location.Key} {location.Value}");
2951 }
2952
2953 private void CommandHomeHomes(IPlayer p, string command, string[] args)
2954 {
2955 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2956 var player = p.Object as BasePlayer;
2957 if (!player || !IsAllowedMsg(player, PermHomeHomes) || !player.IsConnected || player.IsSleeping()) return;
2958 if (args.Length != 1)
2959 {
2960 PrintMsgL(player, "SyntaxCommandHomeHomes");
2961 return;
2962 }
2963 var userId = FindPlayersSingleId(args[0], player);
2964 if (userId <= 0) return;
2965 HomeData homeData;
2966 if (!Home.TryGetValue(userId, out homeData) || homeData.Locations.Count <= 0)
2967 {
2968 PrintMsgL(player, "HomeListEmpty");
2969 return;
2970 }
2971 PrintMsgL(player, "HomeList");
2972 var toRemove = new List<string>();
2973 foreach (var location in homeData.Locations)
2974 {
2975 var err = CheckFoundation(userId, location.Value);
2976 if (err != null)
2977 {
2978 toRemove.Add(location.Key);
2979 continue;
2980 }
2981 PrintMsgL(player, $"{location.Key} {location.Value}");
2982 }
2983 foreach (var loc in toRemove)
2984 {
2985 PrintMsgL(player, "HomeRemovedInvalid", loc);
2986 homeData.Locations.Remove(loc);
2987 changedHome = True;
2988 }
2989 }
2990
2991 private void CommandTeleportTeam(IPlayer p, string command, string[] args)
2992 {
2993 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
2994 if (!config.TPT.UseClans && !config.TPT.UseFriends && !config.TPT.UseTeams)
2995 return;
2996
2997 var player = p.Object as BasePlayer;
2998 if (!player || !IsAllowedMsg(player, PermTpT))
2999 return;
3000
3001 if (args.Length < 1)
3002 {
3003 PrintMsgL(player, "TPTInfo");
3004 return;
3005 }
3006
3007 switch (args[0].ToLower())
3008 {
3009 case "friend":
3010 case "clan":
3011 case "team":
3012 {
3013 SetDisabled(player, args[0].ToLower());
3014 return;
3015 }
3016 }
3017
3018 PrintMsgL(player, "TPTInfo");
3019 }
3020
3021 public bool IsOnSameTeam(ulong playerId, ulong targetId)
3022 {
3023 if (!config.TPT.UseTeams || !IsEnabled(targetId.ToString(), "team"))
3024 {
3025 return false;
3026 }
3027
3028 RelationshipManager.PlayerTeam team1;
3029 if (!RelationshipManager.Instance.playerToTeam.TryGetValue(playerId, out team1))
3030 {
3031 return false;
3032 }
3033
3034 RelationshipManager.PlayerTeam team2;
3035 if (!RelationshipManager.Instance.playerToTeam.TryGetValue(targetId, out team2))
3036 {
3037 return false;
3038 }
3039
3040 return team1.teamID == team2.teamID;
3041 }
3042
3043 private bool AreFriends(string playerId, string targetId)
3044 {
3045 if (!config.TPT.UseFriends || !IsEnabled(targetId, "friend") || !Friends || !Friends.IsLoaded)
3046 {
3047 return False;
3048 }
3049
3050 return Friends?.Call<bool>("AreFriends", playerId, targetId) ?? False;
3051 }
3052
3053 private bool IsInSameClan(string playerId, string targetId)
3054 {
3055 if (!config.TPT.UseClans || !IsEnabled(targetId, "clan") || !Clans || !Clans.IsLoaded)
3056 {
3057 return false;
3058 }
3059
3060 string targetClan = Clans?.Call<string>("GetClanOf", targetId);
3061
3062 if (targetClan == null)
3063 {
3064 return false;
3065 }
3066
3067 string playerClan = Clans?.Call<string>("GetClanOf", playerId);
3068
3069 if (playerClan == null)
3070 {
3071 return false;
3072 }
3073
3074 return targetClan == playerClan;
3075 }
3076
3077 private void OnTeleportRequested(BasePlayer target, BasePlayer player)
3078 {
3079 if (IsInSameClan(player.UserIDString, target.UserIDString) || AreFriends(player.UserIDString, target.UserIDString) || IsOnSameTeam(player.userID, target.userID))
3080 {
3081 target.SendConsoleCommand("chat.say /tpa");
3082 }
3083 }
3084
3085 bool IsEnabled(string targetId, string value)
3086 {
3087 if (TPT.ContainsKey(targetId) && TPT[targetId].Contains(value))
3088 {
3089 return false;
3090 }
3091
3092 return true;
3093 }
3094
3095 void SetDisabled(BasePlayer target, string value)
3096 {
3097 List<string> list;
3098 if (!TPT.TryGetValue(target.UserIDString, out list))
3099 {
3100 TPT[target.UserIDString] = list = new List<string>();
3101 }
3102
3103 if (list.Contains(value))
3104 {
3105 list.Remove(value);
3106 }
3107 else
3108 {
3109 list.Add(value);
3110 }
3111
3112 string status = lang.GetMessage($"TPT_{!list.Contains(value)}", this, target.UserIDString);
3113 string message = string.Format(lang.GetMessage($"TPT_{value}", this, target.UserIDString), status);
3114
3115 PrintMsg(target, message);
3116 changedTPT = True;
3117 }
3118
3119 private void CommandTeleportRequest(IPlayer p, string command, string[] args)
3120 {
3121 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
3122 var player = p.Object as BasePlayer;
3123 if (!player || !IsAllowedMsg(player, PermTpR) || !player.IsConnected || player.IsSleeping()) return;
3124 if (!config.Settings.TPREnabled) { p.Reply("TPR is not enabled in the config."); return; }
3125 if (args.Length == 0)
3126 {
3127 PrintMsgL(player, "SyntaxCommandTPR");
3128 return;
3129 }
3130 var targets = FindPlayersOnline(args[0]);
3131 if (targets.Count <= 0)
3132 {
3133 PrintMsgL(player, "PlayerNotFound");
3134 return;
3135 }
3136 if (targets.Count > 1)
3137 {
3138 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(x => x.displayName).ToArray()));
3139 return;
3140 }
3141 var target = targets[0];
3142 if (target == player && !player.IsAdmin)
3143 {
3144#if DEBUG
3145 Puts("Debug mode - allowing self teleport.");
3146#else
3147 PrintMsgL(player, "CantTeleportToSelf");
3148 return;
3149#endif
3150 }
3151#if DEBUG
3152 Puts("Calling CheckPlayer from cmdChatTeleportRequest");
3153#endif
3154
3155 var err = CheckPlayer(player, config.TPR.UsableOutOfBuildingBlocked, CanCraftTPR(player), True, "tpr");
3156 if (err != null)
3157 {
3158 PrintMsgL(player, err);
3159 return;
3160 }
3161 err = CheckTargetLocation(target, target.transform.position, config.TPR.UsableIntoBuildingBlocked, config.TPR.CupOwnerAllowOnBuildingBlocked);
3162 if (err != null)
3163 {
3164 PrintMsgL(player, err);
3165 return;
3166 }
3167 var timestamp = Facepunch.Math.Epoch.Current;
3168 var currentDate = DateTime.Now.ToString("d");
3169 TeleportData tprData;
3170 if (!TPR.TryGetValue(player.userID, out tprData))
3171 TPR[player.userID] = tprData = new TeleportData();
3172 if (tprData.Date != currentDate)
3173 {
3174 tprData.Amount = 0;
3175 tprData.Date = currentDate;
3176 }
3177
3178 var cooldown = player.IsAdmin ? 0 : GetLower(player, config.TPR.VIPCooldowns, config.TPR.Cooldown);
3179 if (cooldown > 0 && timestamp - tprData.Timestamp < cooldown)
3180 {
3181 var cmdSent = "";
3182 bool foundmoney = CheckEconomy(player, config.TPR.Bypass);
3183 try
3184 {
3185 cmdSent = args[1].ToLower();
3186 }
3187 catch { }
3188
3189 bool payalso = False;
3190 if (config.TPR.Pay > 0)
3191 {
3192 payalso = True;
3193 }
3194 if ((config.Settings.BypassCMD != null) && (cmdSent == config.Settings.BypassCMD.ToLower()))
3195 {
3196 if (foundmoney)
3197 {
3198 CheckEconomy(player, config.TPR.Bypass, True);
3199 PrintMsgL(player, "TPRCooldownBypass", config.TPR.Bypass);
3200 if (payalso)
3201 {
3202 PrintMsgL(player, "PayToTPR", config.TPR.Pay);
3203 }
3204 }
3205 else
3206 {
3207 PrintMsgL(player, "TPRCooldownBypassF", config.TPR.Bypass);
3208 return;
3209 }
3210 }
3211 else if (UseEconomy())
3212 {
3213 var remain = cooldown - (timestamp - tprData.Timestamp);
3214 PrintMsgL(player, "TPRCooldown", FormatTime(remain));
3215 if (config.TPR.Bypass > 0 && config.Settings.BypassCMD != null)
3216 {
3217 PrintMsgL(player, "TPRCooldownBypassP", config.TPR.Bypass);
3218 if (payalso)
3219 {
3220 PrintMsgL(player, "PayToTPR", config.TPR.Pay);
3221 }
3222 PrintMsgL(player, "TPRCooldownBypassP2a", config.Settings.BypassCMD);
3223 }
3224 return;
3225 }
3226 else
3227 {
3228 var remain = cooldown - (timestamp - tprData.Timestamp);
3229 PrintMsgL(player, "TPRCooldown", FormatTime(remain));
3230 return;
3231 }
3232 }
3233 var limit = GetHigher(player, config.TPR.VIPDailyLimits, config.TPR.DailyLimit, true);
3234 if (limit > 0 && tprData.Amount >= limit)
3235 {
3236 PrintMsgL(player, "TPRLimitReached", limit);
3237 return;
3238 }
3239 if (TeleportTimers.ContainsKey(player.userID))
3240 {
3241 PrintMsgL(player, "TeleportPending");
3242 return;
3243 }
3244 if (TeleportTimers.ContainsKey(target.userID))
3245 {
3246 PrintMsgL(player, "TeleportPendingTarget");
3247 return;
3248 }
3249 if (PlayersRequests.ContainsKey(player.userID))
3250 {
3251 PrintMsgL(player, "PendingRequest");
3252 return;
3253 }
3254 if (PlayersRequests.ContainsKey(target.userID))
3255 {
3256 PrintMsgL(player, "PendingRequestTarget");
3257 return;
3258 }
3259 err = CanPlayerTeleport(player);
3260 if (err != null)
3261 {
3262 SendReply(player, err);
3263 return;
3264 }
3265 err = CanPlayerTeleport(target);
3266 if (err != null)
3267 {
3268 PrintMsgL(player, "TPRTarget");
3269 return;
3270 }
3271 err = CheckItems(player);
3272 if (err != null)
3273 {
3274 PrintMsgL(player, "TPBlockedItem", err);
3275 return;
3276 }
3277
3278 PlayersRequests[player.userID] = target;
3279 PlayersRequests[target.userID] = player;
3280 PendingRequests[target.userID] = timer.Once(config.TPR.RequestDuration, () => { RequestTimedOut(player, target); });
3281 PrintMsgL(player, "Request", target.displayName);
3282 PrintMsgL(target, "RequestTarget", player.displayName);
3283 Interface.CallHook("OnTeleportRequested", target, player);
3284 }
3285
3286 private void CommandTeleportAccept(IPlayer p, string command, string[] args)
3287 {
3288 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
3289 var player = p.Object as BasePlayer;
3290 if (!player || !player.IsConnected || player.IsSleeping()) return;
3291 if (!config.Settings.TPREnabled) { p.Reply("TPR is not enabled in the config."); return; }
3292 if (args.Length != 0)
3293 {
3294 PrintMsgL(player, "SyntaxCommandTPA");
3295 return;
3296 }
3297 Timer reqTimer;
3298 if (!PendingRequests.TryGetValue(player.userID, out reqTimer))
3299 {
3300 PrintMsgL(player, "NoPendingRequest");
3301 return;
3302 }
3303#if DEBUG
3304 Puts("Calling CheckPlayer from cmdChatTeleportAccept");
3305#endif
3306 var err = CheckPlayer(player, False, CanCraftTPR(player), False, "tpa");
3307 if (err != null)
3308 {
3309 PrintMsgL(player, err);
3310 return;
3311 }
3312 err = CanPlayerTeleport(player);
3313 if (err != null)
3314 {
3315 SendReply(player, err);
3316 return;
3317 }
3318 var originPlayer = PlayersRequests[player.userID];
3319 err = CheckTargetLocation(originPlayer, player.transform.position, config.TPR.UsableIntoBuildingBlocked, config.TPR.CupOwnerAllowOnBuildingBlocked);
3320 if (err != null)
3321 {
3322 SendReply(player, err);
3323 return;
3324 }
3325 if (config.TPR.BlockTPAOnCeiling)
3326 {
3327 if (GetFloor(player.transform.position).Count > 0)
3328 {
3329 PrintMsgL(player, "AcceptOnRoof");
3330 return;
3331 }
3332 }
3333 var countdown = GetLower(originPlayer, config.TPR.VIPCountdowns, config.TPR.Countdown);
3334 PrintMsgL(originPlayer, "Accept", player.displayName, countdown);
3335 PrintMsgL(player, "AcceptTarget", originPlayer.displayName);
3336 var timestamp = Facepunch.Math.Epoch.Current;
3337 TeleportTimers[originPlayer.userID] = new TeleportTimer
3338 {
3339 OriginPlayer = originPlayer,
3340 TargetPlayer = player,
3341 Timer = timer.Once(countdown, () =>
3342 {
3343#if DEBUG
3344 Puts("Calling CheckPlayer from cmdChatTeleportAccept timer loop");
3345#endif
3346 err = CheckPlayer(originPlayer, config.TPR.UsableOutOfBuildingBlocked, CanCraftTPR(originPlayer), True, "tpa") ?? CheckPlayer(player, False, CanCraftTPR(player), True, "tpa");
3347 if (err != null)
3348 {
3349 PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
3350 PrintMsgL(originPlayer, "Interrupted");
3351 PrintMsgL(originPlayer, err);
3352 TeleportTimers.Remove(originPlayer.userID);
3353 return;
3354 }
3355 err = CheckTargetLocation(originPlayer, player.transform.position, config.TPR.UsableIntoBuildingBlocked, config.TPR.CupOwnerAllowOnBuildingBlocked);
3356 if (err != null)
3357 {
3358 SendReply(player, err);
3359 PrintMsgL(originPlayer, "Interrupted");
3360 SendReply(originPlayer, err);
3361 TeleportTimers.Remove(originPlayer.userID);
3362 return;
3363 }
3364 err = CanPlayerTeleport(originPlayer) ?? CanPlayerTeleport(player);
3365 if (err != null)
3366 {
3367 SendReply(player, err);
3368 PrintMsgL(originPlayer, "Interrupted");
3369 SendReply(originPlayer, err);
3370 TeleportTimers.Remove(originPlayer.userID);
3371 return;
3372 }
3373 err = CheckItems(originPlayer);
3374 if (err != null)
3375 {
3376 PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
3377 PrintMsgL(originPlayer, "Interrupted");
3378 PrintMsgL(originPlayer, "TPBlockedItem", err);
3379 TeleportTimers.Remove(originPlayer.userID);
3380 return;
3381 }
3382 if (UseEconomy())
3383 {
3384 if (config.TPR.Pay > 0)
3385 {
3386 if (!CheckEconomy(originPlayer, config.TPR.Pay))
3387 {
3388 PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
3389 PrintMsgL(originPlayer, "TPNoMoney", config.TPR.Pay);
3390 TeleportTimers.Remove(originPlayer.userID);
3391 return;
3392 }
3393 else
3394 {
3395 CheckEconomy(originPlayer, config.TPR.Pay, True);
3396 PrintMsgL(originPlayer, "TPMoney", (double)config.TPR.Pay);
3397 }
3398 }
3399 }
3400 Teleport(originPlayer, player.transform.position, config.TPR.AllowTPB);
3401 var tprData = TPR[originPlayer.userID];
3402 tprData.Amount++;
3403 tprData.Timestamp = timestamp;
3404 changedTPR = True;
3405 PrintMsgL(player, "SuccessTarget", originPlayer.displayName);
3406 PrintMsgL(originPlayer, "Success", player.displayName);
3407 var limit = GetHigher(player, config.TPR.VIPDailyLimits, config.TPR.DailyLimit, true);
3408 if (limit > 0) PrintMsgL(originPlayer, "TPRAmount", limit - tprData.Amount);
3409 TeleportTimers.Remove(originPlayer.userID);
3410 })
3411 };
3412 reqTimer.Destroy();
3413 PendingRequests.Remove(player.userID);
3414 PlayersRequests.Remove(player.userID);
3415 PlayersRequests.Remove(originPlayer.userID);
3416 }
3417
3418 private void CommandWipeHomes(IPlayer p, string command, string[] args)
3419 {
3420 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
3421 var player = p.Object as BasePlayer;
3422 if (!player || !IsAllowedMsg(player, PermWipeHomes) || !player.IsConnected || player.IsSleeping()) return;
3423 Home.Clear();
3424 changedHome = True;
3425 PrintMsgL(player, "HomesListWiped");
3426 }
3427
3428 private void CommandTeleportHelp(IPlayer p, string command, string[] args)
3429 {
3430 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
3431 var player = p.Object as BasePlayer;
3432 if (!player || !player.IsConnected || player.IsSleeping()) return;
3433 if (!config.Settings.HomesEnabled && !config.Settings.TPREnabled && !IsAllowedMsg(player)) return;
3434 if (args.Length == 1)
3435 {
3436 var key = $"TPHelp{args[0].ToLower()}";
3437 var msg = _(key, player);
3438 if (key.Equals(msg))
3439 PrintMsgL(player, "InvalidHelpModule");
3440 else
3441 PrintMsg(player, msg);
3442 }
3443 else
3444 {
3445 var msg = _("TPHelpGeneral", player);
3446 if (IsAllowed(player))
3447 msg += NewLine + "/tphelp AdminTP";
3448 if (config.Settings.HomesEnabled)
3449 msg += NewLine + "/tphelp Home";
3450 if (config.Settings.TPREnabled)
3451 msg += NewLine + "/tphelp TPR";
3452 PrintMsg(player, msg);
3453 }
3454 }
3455
3456 private void CommandTeleportInfo(IPlayer p, string command, string[] args)
3457 {
3458 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
3459 if (!config.Settings.HomesEnabled && !config.Settings.TPREnabled && !config.Settings.TownEnabled) { p.Reply($"{command} is not enabled in the config."); return; }
3460 var player = p.Object as BasePlayer;
3461 if (!player || !player.IsConnected || player.IsSleeping()) return;
3462 if (args.Length == 1)
3463 {
3464 var module = args[0].ToLower();
3465 var msg = _($"TPSettings{module}", player);
3466 var timestamp = Facepunch.Math.Epoch.Current;
3467 var currentDate = DateTime.Now.ToString("d");
3468 TeleportData teleportData;
3469 int limit;
3470 int cooldown;
3471 switch (module)
3472 {
3473 case "home":
3474 limit = GetHigher(player, config.Home.VIPDailyLimits, config.Home.DailyLimit, true);
3475 cooldown = GetLower(player, config.Home.VIPCooldowns, config.Home.Cooldown);
3476 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player), GetLower(player, config.Home.VIPHomesLimits, config.Home.HomesLimit)));
3477 HomeData homeData;
3478 if (!Home.TryGetValue(player.userID, out homeData))
3479 Home[player.userID] = homeData = new HomeData();
3480 if (homeData.Teleports.Date != currentDate)
3481 {
3482 homeData.Teleports.Amount = 0;
3483 homeData.Teleports.Date = currentDate;
3484 }
3485 if (limit > 0) PrintMsgL(player, "HomeTPAmount", limit - homeData.Teleports.Amount);
3486 if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
3487 {
3488 var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
3489 PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
3490 }
3491 break;
3492 case "tpr":
3493 limit = GetHigher(player, config.TPR.VIPDailyLimits, config.TPR.DailyLimit, true);
3494 cooldown = GetLower(player, config.TPR.VIPCooldowns, config.TPR.Cooldown);
3495 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
3496 if (!TPR.TryGetValue(player.userID, out teleportData))
3497 TPR[player.userID] = teleportData = new TeleportData();
3498 if (teleportData.Date != currentDate)
3499 {
3500 teleportData.Amount = 0;
3501 teleportData.Date = currentDate;
3502 }
3503 if (limit > 0) PrintMsgL(player, "TPRAmount", limit - teleportData.Amount);
3504 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
3505 {
3506 var remain = cooldown - (timestamp - teleportData.Timestamp);
3507 PrintMsgL(player, "TPRCooldown", FormatTime(remain));
3508 }
3509 break;
3510 case "town":
3511 limit = GetHigher(player, config.Town.VIPDailyLimits, config.Town.DailyLimit, true);
3512 cooldown = GetLower(player, config.Town.VIPCooldowns, config.Town.Cooldown);
3513 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
3514 if (!Town.TryGetValue(player.userID, out teleportData))
3515 Town[player.userID] = teleportData = new TeleportData();
3516 if (teleportData.Date != currentDate)
3517 {
3518 teleportData.Amount = 0;
3519 teleportData.Date = currentDate;
3520 }
3521 if (limit > 0) PrintMsgL(player, "TownTPAmount", limit - teleportData.Amount);
3522 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
3523 {
3524 var remain = cooldown - (timestamp - teleportData.Timestamp);
3525 PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
3526 PrintMsgL(player, "TownTPCooldownBypassP", config.Town.Bypass);
3527 PrintMsgL(player, "TownTPCooldownBypassP2", config.Settings.BypassCMD);
3528 }
3529 break;
3530 case "outpost":
3531 limit = GetHigher(player, config.Outpost.VIPDailyLimits, config.Outpost.DailyLimit, true);
3532 cooldown = GetLower(player, config.Outpost.VIPCooldowns, config.Outpost.Cooldown);
3533 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
3534 if (!Outpost.TryGetValue(player.userID, out teleportData))
3535 Outpost[player.userID] = teleportData = new TeleportData();
3536 if (teleportData.Date != currentDate)
3537 {
3538 teleportData.Amount = 0;
3539 teleportData.Date = currentDate;
3540 }
3541 if (limit > 0) PrintMsgL(player, "OutpostTPAmount", limit - teleportData.Amount);
3542 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
3543 {
3544 var remain = cooldown - (timestamp - teleportData.Timestamp);
3545 PrintMsgL(player, "OutpostTPCooldown", FormatTime(remain));
3546 PrintMsgL(player, "OutpostTPCooldownBypassP", config.Outpost.Bypass);
3547 PrintMsgL(player, "OutpostTPCooldownBypassP2", config.Settings.BypassCMD);
3548 }
3549 break;
3550 case "bandit":
3551 limit = GetHigher(player, config.Bandit.VIPDailyLimits, config.Bandit.DailyLimit, true);
3552 cooldown = GetLower(player, config.Bandit.VIPCooldowns, config.Bandit.Cooldown);
3553 PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
3554 if (!Bandit.TryGetValue(player.userID, out teleportData))
3555 Bandit[player.userID] = teleportData = new TeleportData();
3556 if (teleportData.Date != currentDate)
3557 {
3558 teleportData.Amount = 0;
3559 teleportData.Date = currentDate;
3560 }
3561 if (limit > 0) PrintMsgL(player, "BanditTPAmount", limit - teleportData.Amount);
3562 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
3563 {
3564 var remain = cooldown - (timestamp - teleportData.Timestamp);
3565 PrintMsgL(player, "BanditTPCooldown", FormatTime(remain));
3566 PrintMsgL(player, "BanditTPCooldownBypassP", config.Bandit.Bypass);
3567 PrintMsgL(player, "BanditTPCooldownBypassP2", config.Settings.BypassCMD);
3568 }
3569 break;
3570 default:
3571 PrintMsgL(player, "InvalidHelpModule");
3572 break;
3573 }
3574 }
3575 else
3576 {
3577 var msg = _("TPInfoGeneral", player);
3578 if (config.Settings.HomesEnabled)
3579 msg += NewLine + "/tpinfo Home";
3580 if (config.Settings.TPREnabled)
3581 msg += NewLine + "/tpinfo TPR";
3582 if (config.Settings.TownEnabled)
3583 msg += NewLine + "/tpinfo Town";
3584 if (outpostEnabled)
3585 msg += NewLine + "/tpinfo Outpost";
3586 if (banditEnabled)
3587 msg += NewLine + "/tpinfo Bandit";
3588 PrintMsgL(player, msg);
3589 }
3590 }
3591
3592 private void CommandTeleportCancel(IPlayer p, string command, string[] args)
3593 {
3594 var player = p.Object as BasePlayer;
3595 if (!player || !player.IsConnected || player.IsSleeping()) return;
3596 if (args.Length != 0)
3597 {
3598 PrintMsgL(player, "SyntaxCommandTPC");
3599 return;
3600 }
3601 TeleportTimer teleportTimer;
3602 if (TeleportTimers.TryGetValue(player.userID, out teleportTimer))
3603 {
3604 teleportTimer.Timer?.Destroy();
3605 PrintMsgL(player, "TPCancelled");
3606 PrintMsgL(teleportTimer.TargetPlayer, "TPCancelledTarget", player.displayName);
3607 TeleportTimers.Remove(player.userID);
3608 return;
3609 }
3610 foreach (var keyValuePair in TeleportTimers)
3611 {
3612 if (keyValuePair.Value.TargetPlayer != player) continue;
3613 keyValuePair.Value.Timer?.Destroy();
3614 PrintMsgL(keyValuePair.Value.OriginPlayer, "TPCancelledTarget", player.displayName);
3615 PrintMsgL(player, "TPYouCancelledTarget", keyValuePair.Value.OriginPlayer.displayName);
3616 TeleportTimers.Remove(keyValuePair.Key);
3617 return;
3618 }
3619 BasePlayer target;
3620 if (!PlayersRequests.TryGetValue(player.userID, out target))
3621 {
3622 PrintMsgL(player, "NoPendingRequest");
3623 return;
3624 }
3625 Timer reqTimer;
3626 if (PendingRequests.TryGetValue(player.userID, out reqTimer))
3627 {
3628 reqTimer.Destroy();
3629 PendingRequests.Remove(player.userID);
3630 }
3631 else if (PendingRequests.TryGetValue(target.userID, out reqTimer))
3632 {
3633 reqTimer.Destroy();
3634 PendingRequests.Remove(target.userID);
3635 var temp = player;
3636 player = target;
3637 target = temp;
3638 }
3639 PlayersRequests.Remove(target.userID);
3640 PlayersRequests.Remove(player.userID);
3641 PrintMsgL(player, "Cancelled", target.displayName);
3642 PrintMsgL(target, "CancelledTarget", player.displayName);
3643 }
3644
3645 private void CommandOutpost(IPlayer p, string command, string[] args)
3646 {
3647 CommandTown(p, "outpost", args);
3648 }
3649
3650 private void CommandBandit(IPlayer p, string command, string[] args)
3651 {
3652 CommandTown(p, "bandit", args);
3653 }
3654
3655 private void CommandTown(IPlayer p, string command, string[] args)
3656 {
3657 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
3658 var player = p.Object as BasePlayer;
3659 if (!player || !player.IsConnected || player.IsSleeping()) return;
3660#if DEBUG
3661 Puts($"cmdChatTown: command={command}");
3662#endif
3663 switch (command)
3664 {
3665 case "outpost":
3666 if (!IsAllowedMsg(player, PermTpOutpost)) return;
3667 break;
3668 case "bandit":
3669 if (!IsAllowedMsg(player, PermTpBandit)) return;
3670 break;
3671 case "town":
3672 default:
3673 if (!IsAllowedMsg(player, PermTpTown)) return;
3674 break;
3675 }
3676
3677 // For admin using set command
3678 if (args.Length == 1 && IsAllowed(player) && args[0].ToLower().Equals("set"))
3679 {
3680 switch (command)
3681 {
3682 case "outpost":
3683 config.Outpost.Location = player.transform.position;
3684 SaveConfig();
3685 PrintMsgL(player, "OutpostTPLocation", config.Outpost.Location);
3686 break;
3687 case "bandit":
3688 config.Bandit.Location = player.transform.position;
3689 SaveConfig();
3690 PrintMsgL(player, "BanditTPLocation", config.Bandit.Location);
3691 break;
3692 case "town":
3693 default:
3694 config.Town.Location = player.transform.position;
3695 SaveConfig();
3696 PrintMsgL(player, "TownTPLocation", config.Town.Location);
3697 break;
3698 }
3699 return;
3700 }
3701
3702 bool paidmoney = False;
3703
3704 // Is outpost/bandit/town usage enabled?
3705 if (!outpostEnabled && command == "outpost")
3706 {
3707 PrintMsgL(player, OutpostTPDisabledMessage);
3708 return;
3709 }
3710 else if (!banditEnabled && command == "bandit")
3711 {
3712 PrintMsgL(player, BanditTPDisabledMessage);
3713 return;
3714 }
3715 else if (!config.Settings.TownEnabled && command == "town")
3716 {
3717 PrintMsgL(player, "TownTPDisabled");
3718 return;
3719 }
3720
3721 // Are they trying to bypass cooldown or did they just type something else?
3722 if (args.Length == 1 && (args[0].ToLower() != config.Settings.BypassCMD.ToLower()))
3723 {
3724 string com = command ?? "town";
3725 string msg = "SyntaxCommand" + char.ToUpper(com[0]) + com.Substring(1);
3726 PrintMsgL(player, msg);
3727 if (IsAllowed(player)) PrintMsgL(player, msg + "Admin");
3728 return;
3729 }
3730
3731 // Is outpost/bandit/town location set?
3732 if (config.Outpost.Location == Zero && command == "outpost")
3733 {
3734 PrintMsgL(player, "OutpostTPNotSet");
3735 return;
3736 }
3737 else if (config.Bandit.Location == Zero && command == "bandit")
3738 {
3739 PrintMsgL(player, "BanditTPNotSet");
3740 return;
3741 }
3742 else if (config.Town.Location == Zero && command == "town")
3743 {
3744 PrintMsgL(player, "TownTPNotSet");
3745 return;
3746 }
3747
3748 TeleportData teleportData = new TeleportData();
3749 var timestamp = Facepunch.Math.Epoch.Current;
3750 var currentDate = DateTime.Now.ToString("d");
3751
3752 string err = null;
3753 int cooldown = 0;
3754 int limit = 0;
3755 int targetPay = 0;
3756 int targetBypass = 0;
3757 string msgPay = null;
3758 string msgCooldown = null;
3759 string msgCooldownBypass = null;
3760 string msgCooldownBypassF = null;
3761 string msgCooldownBypassP = null;
3762 string msgCooldownBypassP2 = null;
3763 string msgLimitReached = null;
3764#if DEBUG
3765 Puts("Calling CheckPlayer from cmdChatTown");
3766#endif
3767 // Setup vars for checks below
3768 switch (command)
3769 {
3770 case "outpost":
3771 err = CheckPlayer(player, config.Outpost.UsableOutOfBuildingBlocked, CanCraftOutpost(player), True, "outpost");
3772 if (err != null)
3773 {
3774 PrintMsgL(player, err);
3775 if (err == "TPHostile")
3776 {
3777 double stateHostileTime = Math.Round((player.State.unHostileTimestamp - TimeEx.currentTimestamp) / 60, 0, MidpointRounding.AwayFromZero);
3778 PrintMsgL(player, "HostileTimer", stateHostileTime);
3779 }
3780 return;
3781 }
3782 cooldown = GetLower(player, config.Outpost.VIPCooldowns, config.Outpost.Cooldown);
3783 if (!Outpost.TryGetValue(player.userID, out teleportData))
3784 {
3785 Outpost[player.userID] = teleportData = new TeleportData();
3786 }
3787 if (teleportData.Date != currentDate)
3788 {
3789 teleportData.Amount = 0;
3790 teleportData.Date = currentDate;
3791 }
3792
3793 targetPay = config.Outpost.Pay;
3794 targetBypass = config.Outpost.Bypass;
3795
3796 msgPay = "PayToOutpost";
3797 msgCooldown = "OutpostTPCooldown";
3798 msgCooldownBypass = "OutpostTPCooldownBypass";
3799 msgCooldownBypassF = "OutpostTPCooldownBypassF";
3800 msgCooldownBypassP = "OutpostTPCooldownBypassP";
3801 msgCooldownBypassP2 = "OutpostTPCooldownBypassP2";
3802 msgLimitReached = "OutpostTPLimitReached";
3803 limit = GetHigher(player, config.Outpost.VIPDailyLimits, config.Outpost.DailyLimit, true);
3804 break;
3805 case "bandit":
3806 err = CheckPlayer(player, config.Bandit.UsableOutOfBuildingBlocked, CanCraftBandit(player), True, "bandit");
3807 if (err != null)
3808 {
3809 PrintMsgL(player, err);
3810 if (err == "TPHostile")
3811 {
3812 double stateHostileTime = Math.Round((player.State.unHostileTimestamp - TimeEx.currentTimestamp) / 60, 0, MidpointRounding.AwayFromZero);
3813 PrintMsgL(player, "HostileTimer", stateHostileTime);
3814 }
3815 return;
3816 }
3817 cooldown = GetLower(player, config.Bandit.VIPCooldowns, config.Bandit.Cooldown);
3818 if (!Bandit.TryGetValue(player.userID, out teleportData))
3819 {
3820 Bandit[player.userID] = teleportData = new TeleportData();
3821 }
3822 if (teleportData.Date != currentDate)
3823 {
3824 teleportData.Amount = 0;
3825 teleportData.Date = currentDate;
3826 }
3827 targetPay = config.Bandit.Pay;
3828 targetBypass = config.Bandit.Bypass;
3829
3830 msgPay = "PayToBandit";
3831 msgCooldown = "BanditTPCooldown";
3832 msgCooldownBypass = "BanditTPCooldownBypass";
3833 msgCooldownBypassF = "BanditTPCooldownBypassF";
3834 msgCooldownBypassP = "BanditTPCooldownBypassP";
3835 msgCooldownBypassP2 = "BanditTPCooldownBypassP2";
3836 msgLimitReached = "BanditTPLimitReached";
3837 limit = GetHigher(player, config.Bandit.VIPDailyLimits, config.Bandit.DailyLimit, true);
3838 break;
3839 case "town":
3840 default:
3841 err = CheckPlayer(player, config.Town.UsableOutOfBuildingBlocked, CanCraftTown(player), True, "town");
3842 if (err != null)
3843 {
3844 PrintMsgL(player, err);
3845 return;
3846 }
3847 cooldown = GetLower(player, config.Town.VIPCooldowns, config.Town.Cooldown);
3848 if (!Town.TryGetValue(player.userID, out teleportData))
3849 {
3850 Town[player.userID] = teleportData = new TeleportData();
3851 }
3852 if (teleportData.Date != currentDate)
3853 {
3854 teleportData.Amount = 0;
3855 teleportData.Date = currentDate;
3856 }
3857 targetPay = config.Town.Pay;
3858 targetBypass = config.Town.Bypass;
3859
3860 msgPay = "PayToTown";
3861 msgCooldown = "TownTPCooldown";
3862 msgCooldownBypass = "TownTPCooldownBypass";
3863 msgCooldownBypassF = "TownTPCooldownBypassF";
3864 msgCooldownBypassP = "TownTPCooldownBypassP";
3865 msgCooldownBypassP2 = "TownTPCooldownBypassP2";
3866 msgLimitReached = "TownTPLimitReached";
3867 limit = GetHigher(player, config.Town.VIPDailyLimits, config.Town.DailyLimit, true);
3868 break;
3869 }
3870
3871 // Check and process cooldown, bypass, and payment for all modes
3872 if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
3873 {
3874 var cmdSent = "";
3875 bool foundmoney = CheckEconomy(player, targetBypass);
3876 try
3877 {
3878 cmdSent = args[0].ToLower();
3879 }
3880 catch { }
3881
3882 bool payalso = False;
3883 if (targetPay > 0)
3884 {
3885 payalso = True;
3886 }
3887 if ((config.Settings.BypassCMD != null) && (cmdSent == config.Settings.BypassCMD.ToLower()))
3888 {
3889 if (foundmoney)
3890 {
3891 CheckEconomy(player, targetBypass, True);
3892 paidmoney = True;
3893 PrintMsgL(player, msgCooldownBypass, targetBypass);
3894 if (payalso)
3895 {
3896 PrintMsgL(player, msgPay, targetPay);
3897 }
3898 }
3899 else
3900 {
3901 PrintMsgL(player, msgCooldownBypassF, targetBypass);
3902 return;
3903 }
3904 }
3905 else if (UseEconomy())
3906 {
3907 var remain = cooldown - (timestamp - teleportData.Timestamp);
3908 PrintMsgL(player, msgCooldown, FormatTime(remain));
3909 if (targetBypass > 0 && config.Settings.BypassCMD != null)
3910 {
3911 PrintMsgL(player, msgCooldownBypassP, targetBypass);
3912 PrintMsgL(player, msgCooldownBypassP2, config.Settings.BypassCMD);
3913 }
3914 return;
3915 }
3916 else
3917 {
3918 var remain = cooldown - (timestamp - teleportData.Timestamp);
3919 PrintMsgL(player, msgCooldown, FormatTime(remain));
3920 return;
3921 }
3922 }
3923
3924 if (limit > 0 && teleportData.Amount >= limit)
3925 {
3926 PrintMsgL(player, msgLimitReached, limit);
3927 return;
3928 }
3929 if (TeleportTimers.ContainsKey(player.userID))
3930 {
3931 PrintMsgL(player, "TeleportPending");
3932 return;
3933 }
3934 err = CanPlayerTeleport(player);
3935 if (err != null)
3936 {
3937 SendReply(player, err);
3938 return;
3939 }
3940 err = CheckItems(player);
3941 if (err != null)
3942 {
3943 PrintMsgL(player, "TPBlockedItem", err);
3944 return;
3945 }
3946
3947 int countdown = 0;
3948 switch (command)
3949 {
3950 case "outpost":
3951 countdown = GetLower(player, config.Outpost.VIPCountdowns, config.Outpost.Countdown);
3952 TeleportTimers[player.userID] = new TeleportTimer
3953 {
3954 OriginPlayer = player,
3955 Timer = timer.Once(countdown, () =>
3956 {
3957#if DEBUG
3958 Puts("Calling CheckPlayer from cmdChatTown outpost timer loop");
3959#endif
3960 err = CheckPlayer(player, config.Outpost.UsableOutOfBuildingBlocked, CanCraftOutpost(player), True, "outpost");
3961 if (err != null)
3962 {
3963 PrintMsgL(player, "Interrupted");
3964 PrintMsgL(player, err);
3965 if (paidmoney)
3966 {
3967 paidmoney = False;
3968 CheckEconomy(player, config.Outpost.Bypass, False, True);
3969 }
3970 TeleportTimers.Remove(player.userID);
3971 return;
3972 }
3973 err = CanPlayerTeleport(player);
3974 if (err != null)
3975 {
3976 PrintMsgL(player, "Interrupted");
3977 PrintMsgL(player, err);
3978 if (paidmoney)
3979 {
3980 paidmoney = False;
3981 CheckEconomy(player, config.Outpost.Bypass, False, True);
3982 }
3983 TeleportTimers.Remove(player.userID);
3984 return;
3985 }
3986 err = CheckItems(player);
3987 if (err != null)
3988 {
3989 PrintMsgL(player, "Interrupted");
3990 PrintMsgL(player, "TPBlockedItem", err);
3991 if (paidmoney)
3992 {
3993 paidmoney = False;
3994 CheckEconomy(player, config.Outpost.Bypass, False, True);
3995 }
3996 TeleportTimers.Remove(player.userID);
3997 return;
3998 }
3999
4000 if (UseEconomy())
4001 {
4002 if (config.Outpost.Pay > 0 && !CheckEconomy(player, config.Outpost.Pay))
4003 {
4004 PrintMsgL(player, "Interrupted");
4005 PrintMsgL(player, "TPNoMoney", config.Outpost.Pay);
4006 TeleportTimers.Remove(player.userID);
4007 return;
4008 }
4009 else if (config.Outpost.Pay > 0)
4010 {
4011 CheckEconomy(player, config.Outpost.Pay, True);
4012 PrintMsgL(player, "TPMoney", (double)config.Outpost.Pay);
4013 }
4014 }
4015 Teleport(player, config.Outpost.Location);
4016 teleportData.Amount++;
4017 teleportData.Timestamp = timestamp;
4018
4019 changedOutpost = True;
4020 PrintMsgL(player, "OutpostTP");
4021 if (limit > 0) PrintMsgL(player, "OutpostTPAmount", limit - teleportData.Amount);
4022 TeleportTimers.Remove(player.userID);
4023 })
4024 };
4025 PrintMsgL(player, "OutpostTPStarted", countdown);
4026 break;
4027 case "bandit":
4028 countdown = GetLower(player, config.Bandit.VIPCountdowns, config.Bandit.Countdown);
4029 TeleportTimers[player.userID] = new TeleportTimer
4030 {
4031 OriginPlayer = player,
4032 Timer = timer.Once(countdown, () =>
4033 {
4034#if DEBUG
4035 Puts("Calling CheckPlayer from cmdChatTown bandit timer loop");
4036#endif
4037 err = CheckPlayer(player, config.Bandit.UsableOutOfBuildingBlocked, CanCraftBandit(player), True, "bandit");
4038 if (err != null)
4039 {
4040 PrintMsgL(player, "Interrupted");
4041 PrintMsgL(player, err);
4042 if (paidmoney)
4043 {
4044 paidmoney = False;
4045 CheckEconomy(player, config.Bandit.Bypass, False, True);
4046 }
4047 TeleportTimers.Remove(player.userID);
4048 return;
4049 }
4050 err = CanPlayerTeleport(player);
4051 if (err != null)
4052 {
4053 PrintMsgL(player, "Interrupted");
4054 PrintMsgL(player, err);
4055 if (paidmoney)
4056 {
4057 paidmoney = False;
4058 CheckEconomy(player, config.Bandit.Bypass, False, True);
4059 }
4060 TeleportTimers.Remove(player.userID);
4061 return;
4062 }
4063 err = CheckItems(player);
4064 if (err != null)
4065 {
4066 PrintMsgL(player, "Interrupted");
4067 PrintMsgL(player, "TPBlockedItem", err);
4068 if (paidmoney)
4069 {
4070 paidmoney = False;
4071 CheckEconomy(player, config.Bandit.Bypass, False, True);
4072 }
4073 TeleportTimers.Remove(player.userID);
4074 return;
4075 }
4076
4077 if (UseEconomy())
4078 {
4079 if (config.Bandit.Pay > 0 && !CheckEconomy(player, config.Bandit.Pay))
4080 {
4081 PrintMsgL(player, "Interrupted");
4082 PrintMsgL(player, "TPNoMoney", config.Bandit.Pay);
4083 TeleportTimers.Remove(player.userID);
4084 return;
4085 }
4086 else if (config.Bandit.Pay > 0)
4087 {
4088 CheckEconomy(player, config.Bandit.Pay, True);
4089 PrintMsgL(player, "TPMoney", (double)config.Bandit.Pay);
4090 }
4091 }
4092 Teleport(player, config.Bandit.Location);
4093 teleportData.Amount++;
4094 teleportData.Timestamp = timestamp;
4095
4096 changedBandit = True;
4097 PrintMsgL(player, "BanditTP");
4098 if (limit > 0) PrintMsgL(player, "BanditTPAmount", limit - teleportData.Amount);
4099 TeleportTimers.Remove(player.userID);
4100 })
4101 };
4102 PrintMsgL(player, "BanditTPStarted", countdown);
4103 break;
4104 case "town":
4105 default:
4106 countdown = GetLower(player, config.Town.VIPCountdowns, config.Town.Countdown);
4107 TeleportTimers[player.userID] = new TeleportTimer
4108 {
4109 OriginPlayer = player,
4110 Timer = timer.Once(countdown, () =>
4111 {
4112#if DEBUG
4113 Puts("Calling CheckPlayer from cmdChatTown town timer loop");
4114#endif
4115 err = CheckPlayer(player, config.Town.UsableOutOfBuildingBlocked, CanCraftTown(player), True, "town");
4116 if (err != null)
4117 {
4118 PrintMsgL(player, "Interrupted");
4119 PrintMsgL(player, err);
4120 if (paidmoney)
4121 {
4122 paidmoney = False;
4123 CheckEconomy(player, config.Town.Bypass, False, True);
4124 }
4125 TeleportTimers.Remove(player.userID);
4126 return;
4127 }
4128 err = CanPlayerTeleport(player);
4129 if (err != null)
4130 {
4131 PrintMsgL(player, "Interrupted");
4132 PrintMsgL(player, err);
4133 if (paidmoney)
4134 {
4135 paidmoney = False;
4136 CheckEconomy(player, config.Town.Bypass, False, True);
4137 }
4138 TeleportTimers.Remove(player.userID);
4139 return;
4140 }
4141 err = CheckItems(player);
4142 if (err != null)
4143 {
4144 PrintMsgL(player, "Interrupted");
4145 PrintMsgL(player, "TPBlockedItem", err);
4146 if (paidmoney)
4147 {
4148 paidmoney = False;
4149 CheckEconomy(player, config.Town.Bypass, False, True);
4150 }
4151 TeleportTimers.Remove(player.userID);
4152 return;
4153 }
4154
4155 if (UseEconomy())
4156 {
4157 if (config.Town.Pay > 0 && !CheckEconomy(player, config.Town.Pay))
4158 {
4159 PrintMsgL(player, "Interrupted");
4160 PrintMsgL(player, "TPNoMoney", config.Town.Pay);
4161 TeleportTimers.Remove(player.userID);
4162 return;
4163 }
4164 else if (config.Town.Pay > 0)
4165 {
4166 CheckEconomy(player, config.Town.Pay, True);
4167 PrintMsgL(player, "TPMoney", (double)config.Town.Pay);
4168 }
4169 }
4170 Teleport(player, config.Town.Location);
4171 teleportData.Amount++;
4172 teleportData.Timestamp = timestamp;
4173
4174 changedTown = True;
4175 PrintMsgL(player, "TownTP");
4176 if (limit > 0) PrintMsgL(player, "TownTPAmount", limit - teleportData.Amount);
4177 TeleportTimers.Remove(player.userID);
4178 })
4179 };
4180 PrintMsgL(player, "TownTPStarted", countdown);
4181 break;
4182 }
4183 }
4184
4185 private void CommandTeleportII(IPlayer p, string command, string[] args)
4186 {
4187 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
4188 var player = p.Object as BasePlayer;
4189 if (player != null && (!IsAllowedMsg(player, PermTpConsole) || !player.IsConnected || player.IsSleeping())) return;
4190
4191 List<BasePlayer> players;
4192 switch (command)
4193 {
4194 case "teleport.topos":
4195 if (args.Length < 4)
4196 {
4197 p.Reply(_("SyntaxConsoleCommandToPos", player));
4198 return;
4199 }
4200 players = FindPlayers(args[0]);
4201 if (players.Count <= 0)
4202 {
4203 p.Reply(_("PlayerNotFound", player));
4204 return;
4205 }
4206 if (players.Count > 1)
4207 {
4208 p.Reply(_("MultiplePlayers", player, string.Join(", ", players.Select(t => t.displayName).ToArray())));
4209 return;
4210 }
4211 var targetPlayer = players.First();
4212 players.Clear();
4213 float x;
4214 if (!float.TryParse(args[1], out x)) x = -10000f;
4215 float y;
4216 if (!float.TryParse(args[2], out y)) y = -10000f;
4217 float z;
4218 if (!float.TryParse(args[3], out z)) z = -10000f;
4219 if (!CheckBoundaries(x, y, z))
4220 {
4221 p.Reply(_("AdminTPOutOfBounds", player) + Environment.NewLine + _("AdminTPBoundaries", player, boundary));
4222 return;
4223 }
4224 Teleport(targetPlayer, x, y, z);
4225 if (config.Admin.AnnounceTeleportToTarget)
4226 PrintMsgL(targetPlayer, "AdminTPConsoleTP", targetPlayer.transform.position);
4227 p.Reply(_("AdminTPTargetCoordinates", player, targetPlayer.displayName, targetPlayer.transform.position));
4228 Puts(_("LogTeleportPlayer", null, player?.displayName, targetPlayer.displayName, targetPlayer.transform.position));
4229 break;
4230 case "teleport.toplayer":
4231 if (args.Length < 2)
4232 {
4233 p.Reply(_("SyntaxConsoleCommandToPlayer", player));
4234 return;
4235 }
4236 players = FindPlayers(args[0]);
4237 if (players.Count <= 0)
4238 {
4239 p.Reply(_("PlayerNotFound", player));
4240 return;
4241 }
4242 if (players.Count > 1)
4243 {
4244 p.Reply(_("MultiplePlayers", player, string.Join(", ", players.Select(t => t.displayName).ToArray())));
4245 return;
4246 }
4247 var originPlayer = players.First();
4248 players = FindPlayers(args[1]);
4249 if (players.Count <= 0)
4250 {
4251 p.Reply(_("PlayerNotFound", player));
4252 return;
4253 }
4254 if (players.Count > 1)
4255 {
4256 p.Reply(_("MultiplePlayers", player, string.Join(", ", players.Select(t => t.displayName).ToArray())));
4257 players.Clear();
4258 return;
4259 }
4260 targetPlayer = players.First();
4261 if (targetPlayer == originPlayer)
4262 {
4263 players.Clear();
4264 p.Reply(_("CantTeleportPlayerToSelf", player));
4265 return;
4266 }
4267 players.Clear();
4268 Teleport(originPlayer, targetPlayer);
4269 p.Reply(_("AdminTPPlayers", player, originPlayer.displayName, targetPlayer.displayName));
4270 PrintMsgL(originPlayer, "AdminTPConsoleTPPlayer", targetPlayer.displayName);
4271 if (config.Admin.AnnounceTeleportToTarget)
4272 PrintMsgL(targetPlayer, "AdminTPConsoleTPPlayerTarget", originPlayer.displayName);
4273 Puts(_("LogTeleportPlayer", null, player?.displayName, originPlayer.displayName, targetPlayer.displayName));
4274 break;
4275 }
4276 }
4277
4278 float GetMonumentFloat(string monumentName)
4279 {
4280 string name = monumentName.Contains(":") ? monumentName.Substring(0, monumentName.LastIndexOf(":")) : monumentName.TrimEnd();
4281
4282 switch (name)
4283 {
4284 case "Abandoned Cabins":
4285 return 24f + 30f;
4286 case "Abandoned Supermarket":
4287 return 50f;
4288 case "Airfield":
4289 return 200f;
4290 case "Bandit Camp":
4291 return 100f + 25f;
4292 case "Giant Excavator Pit":
4293 return 200f + 25f;
4294 case "Harbor":
4295 return 100f + 50f;
4296 case "HQM Quarry":
4297 return 27.5f + 10f;
4298 case "Large Oil Rig":
4299 return 200f;
4300 case "Launch Site":
4301 return 200f + 100f;
4302 case "Lighthouse":
4303 return 24f + 24f;
4304 case "Military Tunnel":
4305 return 100f;
4306 case "Mining Outpost":
4307 return 25f + 15f;
4308 case "Oil Rig":
4309 return 100f;
4310 case "Outpost":
4311 return 100f + 25f;
4312 case "Oxum's Gas Station":
4313 return 50f + 15f;
4314 case "Power Plant":
4315 return 100f + 40f;
4316 case "power_sub_small_1":
4317 case "power_sub_small_2":
4318 case "power_sub_big_1":
4319 case "power_sub_big_2":
4320 return 30f;
4321 case "Satellite Dish":
4322 return 75f + 15f;
4323 case "Sewer Branch":
4324 return 75f + 25f;
4325 case "Stone Quarry":
4326 return 27.5f;
4327 case "Sulfur Quarry":
4328 return 27.5f;
4329 case "The Dome":
4330 return 50f + 20f;
4331 case "Train Yard":
4332 return 100 + 50f;
4333 case "Water Treatment Plant":
4334 return 100f + 85f;
4335 case "Water Well":
4336 return 24f;
4337 case "Wild Swamp":
4338 return 24f;
4339 }
4340
4341 return config.Settings.DefaultMonumentSize;
4342 }
4343
4344 private void CommandSphereMonuments(IPlayer p, string command, string[] args)
4345 {
4346 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
4347 var player = p?.Object as BasePlayer;
4348 if (!player || !player.IsAdmin || !player.IsConnected || player.IsSleeping()) return;
4349
4350 foreach (var monument in monuments)
4351 {
4352 string name = monument.Key.Contains(":") ? monument.Key.Substring(0, monument.Key.LastIndexOf(":")) : monument.Key.TrimEnd();
4353
4354 player.SendConsoleCommand("ddraw.sphere", 30f, Color.red, monument.Value.Position, GetMonumentFloat(name));
4355 player.SendConsoleCommand("ddraw.text", 30f, Color.blue, monument.Value.Position, name);
4356 }
4357
4358 foreach (var cave in caves)
4359 {
4360 string name = cave.Key.Contains(":") ? cave.Key.Substring(0, cave.Key.LastIndexOf(":")) : cave.Key.TrimEnd();
4361 float realdistance = cave.Key.Contains("Small") ? config.Settings.CaveDistanceSmall : cave.Key.Contains("Medium") ? config.Settings.CaveDistanceMedium : config.Settings.CaveDistanceLarge;
4362 realdistance += 50f;
4363
4364 player.SendConsoleCommand("ddraw.sphere", 30f, Color.black, cave.Value, realdistance);
4365 player.SendConsoleCommand("ddraw.text", 30f, Color.cyan, cave.Value, name);
4366 }
4367 }
4368
4369 private void CommandImportHomes(IPlayer p, string command, string[] args)
4370 {
4371 if (DisabledTPT.DisabledCommands.Contains(command.ToLower())) { p.Reply("Disabled command: " + command); return; }
4372 var player = p.Object as BasePlayer;
4373
4374 if (player != null && (!IsAllowedMsg(player, PermImportHomes) || !player.IsConnected || player.IsSleeping()))
4375 {
4376 p.Reply(_("NotAllowed", player));
4377 return;
4378 }
4379 var datafile = Interface.Oxide.DataFileSystem.GetFile("m-Teleportation");
4380 if (!datafile.Exists())
4381 {
4382 p.Reply("No m-Teleportation.json exists.");
4383 return;
4384 }
4385 datafile.Load();
4386 var allHomeData = datafile["HomeData"] as Dictionary<string, object>;
4387 if (allHomeData == null)
4388 {
4389 p.Reply(_("HomeListEmpty", player));
4390 return;
4391 }
4392 var count = 0;
4393 foreach (var kvp in allHomeData)
4394 {
4395 var homeDataOld = kvp.Value as Dictionary<string, object>;
4396 if (homeDataOld == null) continue;
4397 if (!homeDataOld.ContainsKey("HomeLocations")) continue;
4398 var homeList = homeDataOld["HomeLocations"] as Dictionary<string, object>;
4399 if (homeList == null) continue;
4400 var userId = Convert.ToUInt64(kvp.Key);
4401 HomeData homeData;
4402 if (!Home.TryGetValue(userId, out homeData))
4403 Home[userId] = homeData = new HomeData();
4404 foreach (var kvp2 in homeList)
4405 {
4406 var positionData = kvp2.Value as Dictionary<string, object>;
4407 if (positionData == null) continue;
4408 if (!positionData.ContainsKey("x") || !positionData.ContainsKey("y") || !positionData.ContainsKey("z")) continue;
4409 var position = new Vector3(Convert.ToSingle(positionData["x"]), Convert.ToSingle(positionData["y"]), Convert.ToSingle(positionData["z"]));
4410 homeData.Locations[kvp2.Key] = position;
4411 changedHome = True;
4412 count++;
4413 }
4414 }
4415 p.Reply(string.Format("Imported {0} homes.", count));
4416 }
4417
4418 private void RequestTimedOut(BasePlayer player, BasePlayer target)
4419 {
4420 PlayersRequests.Remove(player.userID);
4421 PlayersRequests.Remove(target.userID);
4422 PendingRequests.Remove(target.userID);
4423 PrintMsgL(player, "TimedOut", target.displayName);
4424 PrintMsgL(target, "TimedOutTarget", player.displayName);
4425 }
4426
4427 #region Util
4428 private string FormatTime(long seconds)
4429 {
4430 var timespan = TimeSpan.FromSeconds(seconds);
4431 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));
4432 }
4433
4434 private double ConvertToRadians(double angle)
4435 {
4436 return System.Math.PI / 180 * angle;
4437 }
4438 #endregion
4439
4440 #region Teleport
4441 public void Teleport(BasePlayer player, BasePlayer target) => Teleport(player, target.transform.position);
4442
4443 public void Teleport(BasePlayer player, float x, float y, float z) => Teleport(player, new Vector3(x, y, z));
4444
4445 public void Teleport(BasePlayer player, Vector3 newPosition, bool save = True)
4446 {
4447 if (!player.IsValid() || Vector3.Distance(newPosition, Zero) < 5f) return;
4448
4449 if (save) SaveLocation(player);
4450 if (!teleporting.ContainsKey(player.userID))
4451 teleporting.Add(player.userID, newPosition);
4452 else teleporting[player.userID] = newPosition;
4453
4454 var oldPosition = player.transform.position;
4455
4456 try
4457 {
4458 player.EnsureDismounted(); // 1.1.2 @Def
4459
4460 if (player.HasParent())
4461 {
4462 player.SetParent(null, True, True);
4463 }
4464
4465 if (player.IsConnected) // 1.1.2 @Def
4466 {
4467 player.EndLooting();
4468 StartSleeping(player);
4469 }
4470
4471 player.RemoveFromTriggers(); // 1.1.2 @Def recommendation to use natural method for issue with triggers
4472 player.EnableServerFall(True); // redundant, in OnEntityTakeDamage hook
4473 player.Teleport(newPosition); // 1.1.6
4474
4475 if (player.IsConnected && !Network.Net.sv.visibility.IsInside(player.net.group, newPosition))
4476 {
4477 player.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, True);
4478 player.ClientRPCPlayer(null, player, "StartLoading");
4479 player.SendEntityUpdate();
4480 if (!IsInvisible(player)) // fix for becoming networked briefly with vanish while teleporting
4481 {
4482 player.UpdateNetworkGroup(); // 1.1.1 building fix @ctv
4483 player.SendNetworkUpdateImmediate(False);
4484 }
4485 }
4486 }
4487 finally
4488 {
4489 player.EnableServerFall(False);
4490 player.ForceUpdateTriggers(); // 1.1.4 exploit fix for looting sleepers in safe zones
4491 }
4492
4493 Interface.CallHook("OnPlayerTeleported", player, oldPosition, newPosition);
4494 }
4495
4496 bool IsInvisible(BasePlayer player)
4497 {
4498 return Vanish != null && Vanish.Call<bool>("IsInvisible", player);
4499 }
4500
4501 public void StartSleeping(BasePlayer player) // custom as to not cancel crafting, or remove player from vanish
4502 {
4503 if (!player.IsSleeping())
4504 {
4505 Interface.CallHook("OnPlayerSleep", player);
4506 player.SetPlayerFlag(BasePlayer.PlayerFlags.Sleeping, True);
4507 player.sleepStartTime = Time.time;
4508 BasePlayer.sleepingPlayerList.Add(player);
4509 BasePlayer.bots.Remove(player);
4510 player.CancelInvoke("InventoryUpdate");
4511 player.CancelInvoke("TeamUpdate");
4512 }
4513 }
4514 #endregion
4515
4516 #region Checks
4517 private string CanPlayerTeleport(BasePlayer player)
4518 {
4519 return Interface.Oxide.CallHook("CanTeleport", player) as string;
4520 }
4521
4522 private bool CanCraftHome(BasePlayer player)
4523 {
4524 return config.Home.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftHome);
4525 }
4526
4527 private bool CanCraftTown(BasePlayer player)
4528 {
4529 return config.Town.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftTown);
4530 }
4531
4532 private bool CanCraftOutpost(BasePlayer player)
4533 {
4534 return config.Outpost.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftOutpost);
4535 }
4536
4537 private bool CanCraftBandit(BasePlayer player)
4538 {
4539 return config.Bandit.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftBandit);
4540 }
4541
4542 private bool CanCraftTPR(BasePlayer player)
4543 {
4544 return config.TPR.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftTpR);
4545 }
4546
4547 public bool AboveWater(BasePlayer player)
4548 {
4549 var pos = player.transform.position;
4550#if DEBUG
4551 Puts($"Player position: {pos.ToString()}. Checking for water...");
4552#endif
4553 if ((TerrainMeta.HeightMap.GetHeight(pos) - TerrainMeta.WaterMap.GetHeight(pos)) >= 0)
4554 {
4555#if DEBUG
4556 Puts("Player not above water.");
4557#endif
4558 return False;
4559 }
4560 else
4561 {
4562#if DEBUG
4563 Puts("Player is above water!");
4564#endif
4565 return True;
4566 }
4567 }
4568
4569 private string NearMonument(BasePlayer player)
4570 {
4571 foreach (var entry in monuments)
4572 {
4573 if (entry.Key.ToLower().Contains("power")) continue;
4574
4575 var pos = entry.Value.Position;
4576 pos.y = player.transform.position.y;
4577 float dist = (player.transform.position - pos).magnitude;
4578#if DEBUG
4579 Puts($"Checking {entry.Key} dist: {dist}, realdistance: {entry.Value.Radius}");
4580#endif
4581 if (dist < entry.Value.Radius)
4582 {
4583#if DEBUG
4584 Puts($"Player in range of {entry.Key}");
4585#endif
4586 return entry.Key;
4587 }
4588 }
4589 return null;
4590 }
4591
4592 private bool belowGround(Vector3 a, Vector3 b)
4593 {
4594 return a.y < TerrainMeta.HeightMap.GetHeight(b);
4595 }
4596
4597 private string NearCave(BasePlayer player)
4598 {
4599 foreach (var entry in caves)
4600 {
4601 string caveName = entry.Key.Contains(":") ? entry.Key.Substring(0, entry.Key.LastIndexOf(":")) : entry.Key;
4602 float realdistance = entry.Key.Contains("Small") ? config.Settings.CaveDistanceSmall : entry.Key.Contains("Medium") ? config.Settings.CaveDistanceMedium : config.Settings.CaveDistanceLarge;
4603
4604 if (Vector3.Distance(player.transform.position, entry.Value) < realdistance + 50f && !belowGround(player.transform.position, entry.Value))
4605 {
4606#if DEBUG
4607 Puts($"NearCave: {caveName} nearby.");
4608#endif
4609 return caveName;
4610 }
4611 else
4612 {
4613#if DEBUG
4614 Puts("NearCave: Not near this cave, or above it.");
4615#endif
4616 }
4617 }
4618 return null;
4619 }
4620
4621 private string CheckPlayer(BasePlayer player, bool build = False, bool craft = False, bool origin = True, string mode = "home")
4622 {
4623 var onship = player.GetComponentInParent<CargoShip>();
4624 var onballoon = player.GetComponentInParent<HotAirBalloon>();
4625 var inlift = player.GetComponentInParent<Lift>();
4626 var pos = player.transform.position;
4627
4628 string monname = NearMonument(player);
4629 if (config.Settings.Interrupt.Monument)
4630 {
4631 if (monname != null)
4632 {
4633 return _("TooCloseToMon", player, monname);
4634 }
4635 }
4636 if (config.Settings.Interrupt.Oilrig)
4637 {
4638 if (monname != null && monname.Contains("Oil Rig"))
4639 {
4640 return _("TooCloseToMon", player, monname);
4641 }
4642 }
4643 bool allowcave = True;
4644
4645#if DEBUG
4646 Puts($"CheckPlayer(): called mode is {mode}");
4647#endif
4648 switch (mode)
4649 {
4650 case "tpt":
4651 allowcave = config.TPT.AllowCave;
4652 break;
4653 case "home":
4654 allowcave = config.Home.AllowCave;
4655 break;
4656 case "tpa":
4657 case "tpr":
4658 allowcave = config.TPR.AllowCave;
4659 break;
4660 case "town":
4661 allowcave = config.Town.AllowCave;
4662 break;
4663 case "outpost":
4664 allowcave = config.Outpost.AllowCave;
4665 break;
4666 case "bandit":
4667 allowcave = config.Bandit.AllowCave;
4668 break;
4669 default:
4670#if DEBUG
4671 Puts("Skipping cave check...");
4672#endif
4673 break;
4674 }
4675 if (!allowcave)
4676 {
4677#if DEBUG
4678 Puts("Checking cave distance...");
4679#endif
4680 string cavename = NearCave(player);
4681 if (cavename != null)
4682 {
4683 return "TooCloseToCave";
4684 }
4685 }
4686
4687 if (config.Settings.Interrupt.Hostile && (mode == "bandit" || mode == "outpost"))
4688 {
4689 if (player.IsHostile())
4690 {
4691 return "TPHostile";
4692 }
4693 }
4694 if (player.isMounted && config.Settings.Interrupt.Mounted)
4695 return "TPMounted";
4696 if (!player.IsAlive())
4697 return "TPDead";
4698 // Block if hurt if the config is enabled. If the player is not the target in a tpa condition, allow.
4699 if ((player.IsWounded() && origin) && config.Settings.Interrupt.Hurt)
4700 return "TPWounded";
4701
4702 if (player.metabolism.temperature.value <= config.Settings.MinimumTemp && config.Settings.Interrupt.Cold)
4703 {
4704 return "TPTooCold";
4705 }
4706 if (player.metabolism.temperature.value >= config.Settings.MaximumTemp && config.Settings.Interrupt.Hot)
4707 {
4708 return "TPTooHot";
4709 }
4710
4711 if (config.Settings.Interrupt.AboveWater)
4712 if (AboveWater(player))
4713 return "TPAboveWater";
4714 if (!build && !player.CanBuild())
4715 return "TPBuildingBlocked";
4716 if (player.IsSwimming() && config.Settings.Interrupt.Swimming)
4717 return "TPSwimming";
4718 // This will have to do until we have a proper parent name for this
4719 if (monname != null && monname.Contains("Oil Rig") && config.Settings.Interrupt.Oilrig)
4720 return "TPOilRig";
4721 if (monname != null && monname.Contains("Excavator") && config.Settings.Interrupt.Excavator)
4722 return "TPExcavator";
4723 if (onship && config.Settings.Interrupt.Cargo)
4724 return "TPCargoShip";
4725 if (onballoon && config.Settings.Interrupt.Balloon)
4726 return "TPHotAirBalloon";
4727 if (inlift && config.Settings.Interrupt.Lift)
4728 return "TPBucketLift";
4729 if (GetLift(pos) && config.Settings.Interrupt.Lift)
4730 return "TPRegLift";
4731 if (player.InSafeZone() && config.Settings.Interrupt.Safe)
4732 return "TPSafeZone";
4733 if (!craft && player.inventory.crafting.queue.Count > 0)
4734 return "TPCrafting";
4735
4736 if (config.Settings.BlockZoneFlag && ZoneManager != null)
4737 {
4738 var success = ZoneManager?.Call("PlayerHasFlag", player, "notp");
4739
4740 if (success is bool && (bool)success)
4741 {
4742 return "TPFlagZone";
4743 }
4744 }
4745
4746 if (config.Settings.BlockNoEscape && NoEscape != null)
4747 {
4748 var success = NoEscape?.Call("IsBlocked", player);
4749
4750 if (success is bool && (bool)success)
4751 {
4752 return "TPNoEscapeBlocked";
4753 }
4754 }
4755
4756 return null;
4757 }
4758
4759 private string CheckTargetLocation(BasePlayer player, Vector3 targetLocation, bool ubb, bool obb)
4760 {
4761 // ubb == UsableIntoBuildingBlocked
4762 // obb == CupOwnerAllowOnBuildingBlocked
4763 var entities = Pool.GetList<BuildingBlock>();
4764 Vis.Entities(targetLocation, 3f, entities, Layers.Mask.Construction, QueryTriggerInteraction.Ignore);
4765 bool denied = False;
4766
4767 foreach (var block in entities)
4768 {
4769 if (CheckCupboardBlock(block, player, obb))
4770 {
4771 denied = False;
4772#if DEBUG
4773 Puts("Cupboard either owned or there is no cupboard");
4774#endif
4775 }
4776 else if (ubb && (player.userID != block.OwnerID))
4777 {
4778 denied = False;
4779#if DEBUG
4780 Puts("Player does not own block, but UsableIntoBuildingBlocked=true");
4781#endif
4782 }
4783 else if (player.userID == block.OwnerID)
4784 {
4785#if DEBUG
4786 Puts("Player owns block");
4787#endif
4788
4789 if (!player.IsBuildingBlocked(targetLocation, new Quaternion(), block.bounds))
4790 {
4791#if DEBUG
4792 Puts("Player not BuildingBlocked. Likely unprotected building.");
4793#endif
4794 denied = False;
4795 break;
4796 }
4797 else if (ubb)
4798 {
4799#if DEBUG
4800 Puts("Player not blocked because UsableIntoBuildingBlocked=true");
4801#endif
4802 denied = False;
4803 break;
4804 }
4805 else
4806 {
4807#if DEBUG
4808 Puts("Player owns block but blocked by UsableIntoBuildingBlocked=false");
4809#endif
4810 denied = True;
4811 break;
4812 }
4813 }
4814 else
4815 {
4816#if DEBUG
4817 Puts("Player blocked");
4818#endif
4819 denied = True;
4820 break;
4821 }
4822 }
4823 Pool.FreeList(ref entities);
4824
4825 return denied ? "TPTargetBuildingBlocked" : null;
4826 }
4827
4828 // Check that a building block is owned by/attached to a cupboard, allow tp if not blocked unless allowed by config
4829 private bool CheckCupboardBlock(BuildingBlock block, BasePlayer player, bool obb)
4830 {
4831 // obb == CupOwnerAllowOnBuildingBlocked
4832 var building = block.GetBuilding();
4833 if (building != null)
4834 {
4835#if DEBUG
4836 Puts("Found building, checking privileges...");
4837 Puts($"Building ID: {building.ID}");
4838#endif
4839 // cupboard overlap. Check privs.
4840 if (building.buildingPrivileges == null)
4841 {
4842#if DEBUG
4843 Puts("No cupboard found, allowing teleport");
4844#endif
4845 return player.CanBuild();
4846 }
4847
4848 foreach (var priv in building.buildingPrivileges)
4849 {
4850 if (priv.IsAuthed(player))
4851 {
4852 // player is authorized to the cupboard
4853#if DEBUG
4854 Puts("Player owns cupboard with auth");
4855#endif
4856 return True;
4857 }
4858 }
4859
4860 if (player.userID == block.OwnerID)
4861 {
4862 if (obb)
4863 {
4864#if DEBUG
4865 // player set the cupboard and is allowed in by config
4866 Puts("Player owns cupboard with no auth, but allowed by CupOwnerAllowOnBuildingBlocked=true");
4867#endif
4868 return True;
4869 }
4870#if DEBUG
4871 // player set the cupboard but is blocked by config
4872 Puts("Player owns cupboard with no auth, but blocked by CupOwnerAllowOnBuildingBlocked=false");
4873#endif
4874 return False;
4875 }
4876
4877#if DEBUG
4878 // player not authed
4879 Puts("Player does not own cupboard and is not authorized");
4880#endif
4881 return False;
4882 }
4883#if DEBUG
4884 Puts("No cupboard or building found - we cannot tell the status of this block");
4885#endif
4886 return True;
4887 }
4888
4889 private string CheckInsideBlock(Vector3 targetLocation)
4890 {
4891 List<BuildingBlock> blocks = Pool.GetList<BuildingBlock>();
4892 Vis.Entities(targetLocation + new Vector3(0, 0.25f), 0.1f, blocks, blockLayer);
4893 bool inside = blocks.Count > 0;
4894 Pool.FreeList(ref blocks);
4895
4896 return inside ? "TPTargetInsideBlock" : null;
4897 }
4898
4899 private string CheckInsideBattery(Vector3 targetLocation)
4900 {
4901 var batteries = new List<ElectricBattery>();
4902 Vis.Entities(targetLocation, 0.35f, batteries, deployedLayer);
4903 return batteries.Count > 0 ? "TPTargetInsideBlock" : null;
4904 }
4905
4906 private string CheckItems(BasePlayer player)
4907 {
4908 foreach (var blockedItem in ReverseBlockedItems)
4909 {
4910 if (player.inventory.FindItemID(blockedItem.Key) != null)
4911 {
4912 return blockedItem.Value;
4913 }
4914 }
4915 return null;
4916 }
4917
4918 private string CheckFoundation(ulong userID, Vector3 position)
4919 {
4920 if (CheckInsideBattery(position) != null)
4921 {
4922 return "HomeNoFoundation";
4923 }
4924 if (!config.Home.ForceOnTopOfFoundation) return null; // Foundation/floor not required
4925 if (UnderneathFoundation(position))
4926 {
4927 return "HomeFoundationUnderneathFoundation";
4928 }
4929
4930 var entities = new List<BuildingBlock>();
4931 if (config.Home.AllowAboveFoundation) // Can set on a foundation or floor
4932 {
4933#if DEBUG
4934 Puts($"CheckFoundation() looking for foundation or floor at {position}");
4935#endif
4936 entities = GetFoundationOrFloor(position);
4937 }
4938 else // Can only use foundation, not floor/ceiling
4939 {
4940#if DEBUG
4941 Puts($"CheckFoundation() looking for foundation at {position}");
4942#endif
4943 entities = GetFoundation(position);
4944 }
4945
4946 entities.RemoveAll(x => !x.IsValid() || x.IsDestroyed);
4947 if (entities.Count == 0) return "HomeNoFoundation";
4948
4949 if (!config.Home.CheckFoundationForOwner) return null;
4950 for (var i = 0; i < entities.Count; i++)
4951 {
4952 if (entities[i].OwnerID == userID || IsFriend(userID, entities[i].OwnerID)) return null;
4953 }
4954
4955 return "HomeFoundationNotFriendsOwned";
4956 }
4957
4958 private BuildingBlock GetFoundationOwned(Vector3 position, ulong userID)
4959 {
4960#if DEBUG
4961 Puts("GetFoundationOwned() called...");
4962#endif
4963 var entities = GetFoundation(position);
4964 if (entities.Count == 0) return null;
4965 if (!config.Home.CheckFoundationForOwner) return entities[0];
4966
4967 for (var i = 0; i < entities.Count; i++)
4968 {
4969 if (entities[i].OwnerID == userID) return entities[i];
4970 else if (IsFriend(userID, entities[i].OwnerID)) return entities[i];
4971 }
4972 return null;
4973 }
4974
4975 // Borrowed/modified from PreventLooting and Rewards
4976 // playerid = active player, ownerid = owner of building block, who may be offline
4977 bool IsFriend(ulong playerid, ulong ownerid)
4978 {
4979 if (config.Home.UseFriends && Friends != null && Friends.IsLoaded)
4980 {
4981#if DEBUG
4982 Puts("Checking Friends...");
4983#endif
4984 var fr = Friends?.CallHook("AreFriends", playerid, ownerid);
4985 if (fr != null && (bool)fr)
4986 {
4987#if DEBUG
4988 Puts(" IsFriend: true based on Friends plugin");
4989#endif
4990 return True;
4991 }
4992 }
4993 if (config.Home.UseClans && Clans != null && Clans.IsLoaded)
4994 {
4995#if DEBUG
4996 Puts("Checking Clans...");
4997#endif
4998 string playerclan = (string)Clans?.CallHook("GetClanOf", playerid);
4999 string ownerclan = (string)Clans?.CallHook("GetClanOf", ownerid);
5000 if (playerclan != null && ownerclan != null && playerclan == ownerclan)
5001 {
5002#if DEBUG
5003 Puts(" IsFriend: true based on Clans plugin");
5004#endif
5005 return True;
5006 }
5007 }
5008 if (config.Home.UseTeams)
5009 {
5010#if DEBUG
5011 Puts("Checking Rust teams...");
5012#endif
5013 BasePlayer player = BasePlayer.FindByID(playerid);
5014 if (player.currentTeam != (long)0)
5015 {
5016 RelationshipManager.PlayerTeam playerTeam = RelationshipManager.Instance.FindTeam(player.currentTeam);
5017 if (playerTeam == null) return False;
5018 if (playerTeam.members.Contains(ownerid))
5019 {
5020#if DEBUG
5021 Puts(" IsFriend: true based on Rust teams");
5022#endif
5023 return True;
5024 }
5025 }
5026 }
5027 return False;
5028 }
5029
5030 // Check that we are near the middle of a block. Also check for high wall overlap
5031 private bool ValidBlock(BaseEntity entity, Vector3 position)
5032 {
5033 if (!config.Settings.StrictFoundationCheck)
5034 {
5035 return True;
5036 }
5037#if DEBUG
5038 Puts($"ValidBlock() called for {entity.ShortPrefabName}");
5039#endif
5040 Vector3 center = entity.CenterPoint();
5041
5042 List<BaseEntity> ents = new List<BaseEntity>();
5043 Vis.Entities<BaseEntity>(center, 1.5f, ents);
5044 foreach (BaseEntity wall in ents)
5045 {
5046 if (wall.name.Contains("external.high"))
5047 {
5048#if DEBUG
5049 Puts($" Found: {wall.name} @ center {center}, pos {position}");
5050#endif
5051 return False;
5052 }
5053 }
5054#if DEBUG
5055 Puts($" Checking block: {entity.name} @ center {center}, pos: {position.ToString()}");
5056#endif
5057 if (entity.PrefabName.Contains("triangle.prefab"))
5058 {
5059 if (Math.Abs(center.x - position.x) < 0.45f && Math.Abs(center.z - position.z) < 0.45f)
5060 {
5061#if DEBUG
5062 Puts($" Found: {entity.ShortPrefabName} @ center: {center}, pos: {position}");
5063#endif
5064 return True;
5065 }
5066 }
5067 else if (entity.PrefabName.Contains("foundation.prefab") || entity.PrefabName.Contains("floor.prefab"))
5068 {
5069 if (Math.Abs(center.x - position.x) < 0.7f && Math.Abs(center.z - position.z) < 0.7f)
5070 {
5071#if DEBUG
5072 Puts($" Found: {entity.ShortPrefabName} @ center: {center}, pos: {position}");
5073#endif
5074 return True;
5075 }
5076 }
5077
5078 return False;
5079 }
5080
5081 private List<BuildingBlock> GetFoundation(Vector3 position)
5082 {
5083 RaycastHit hitinfo;
5084 var entities = new List<BuildingBlock>();
5085
5086 if (Physics.Raycast(position, Down, out hitinfo, 2.5f, blockLayer) && hitinfo.GetEntity().IsValid())
5087 {
5088 var entity = hitinfo.GetEntity();
5089 if (entity.PrefabName.Contains("foundation") || position.y < entity.WorldSpaceBounds().ToBounds().max.y)
5090 {
5091 if (ValidBlock(entity, position))
5092 {
5093#if DEBUG
5094 Puts($" GetFoundation() found {entity.PrefabName} at {entity.transform.position}");
5095#endif
5096 entities.Add(entity as BuildingBlock);
5097 }
5098 }
5099 }
5100 else
5101 {
5102#if DEBUG
5103 Puts(" GetFoundation() none found.");
5104#endif
5105 }
5106
5107 return entities;
5108 }
5109
5110 private List<BuildingBlock> GetFloor(Vector3 position)
5111 {
5112 RaycastHit hitinfo;
5113 var entities = new List<BuildingBlock>();
5114
5115 if (Physics.Raycast(position, Down, out hitinfo, 0.25f, Layers.Mask.Construction, QueryTriggerInteraction.Ignore) && hitinfo.GetEntity().IsValid())
5116 {
5117 var entity = hitinfo.GetEntity();
5118
5119 if (entity.IsValid() && entity.PrefabName.Contains("floor"))
5120 {
5121#if DEBUG
5122 Puts($" GetFloor() found {entity.PrefabName} at {entity.transform.position}");
5123#endif
5124 entities.Add(entity as BuildingBlock);
5125 }
5126 }
5127 else
5128 {
5129#if DEBUG
5130 Puts(" GetFloor() none found.");
5131#endif
5132 }
5133
5134 return entities;
5135 }
5136
5137 private List<BuildingBlock> GetFoundationOrFloor(Vector3 position)
5138 {
5139 RaycastHit hitinfo;
5140 var entities = new List<BuildingBlock>();
5141
5142 if (Physics.Raycast(position, Down, out hitinfo, 0.25f, blockLayer) && hitinfo.GetEntity().IsValid())
5143 {
5144 var entity = hitinfo.GetEntity();
5145 if (entity.PrefabName.Contains("floor") || entity.PrefabName.Contains("foundation"))// || position.y < entity.WorldSpaceBounds().ToBounds().max.y))
5146 {
5147#if DEBUG
5148 Puts($" GetFoundationOrFloor() found {entity.PrefabName} at {entity.transform.position}");
5149#endif
5150 if (ValidBlock(entity, position))
5151 {
5152 entities.Add(entity as BuildingBlock);
5153 }
5154 }
5155 }
5156 else
5157 {
5158#if DEBUG
5159 Puts(" GetFoundationOrFloor() none found.");
5160#endif
5161 }
5162
5163 return entities;
5164 }
5165
5166 private bool CheckBoundaries(float x, float y, float z)
5167 {
5168 return x <= boundary && x >= -boundary && y <= 2000 && y >= -100 && z <= boundary && z >= -boundary;
5169 }
5170
5171 private Vector3 GetGround(Vector3 sourcePos)
5172 {
5173 if (!config.Home.AllowAboveFoundation) return sourcePos;
5174 var newPos = sourcePos;
5175 newPos.y = TerrainMeta.HeightMap.GetHeight(newPos);
5176 sourcePos.y += .5f;
5177 RaycastHit hitinfo;
5178 var done = False;
5179
5180#if DEBUG
5181 Puts("GetGround(): Looking for iceberg or cave");
5182#endif
5183 //if (Physics.SphereCast(sourcePos, .1f, down, out hitinfo, 250, groundLayer))
5184 if (Physics.Raycast(sourcePos, Down, out hitinfo, 250f, groundLayer))
5185 {
5186 if ((config.Home.AllowIceberg && hitinfo.collider.name.Contains("iceberg")) || (config.Home.AllowCave && hitinfo.collider.name.Contains("cave_")))
5187 {
5188#if DEBUG
5189 Puts("GetGround(): found iceberg or cave");
5190#endif
5191 sourcePos.y = hitinfo.point.y;
5192 done = True;
5193 }
5194 else
5195 {
5196 var mesh = hitinfo.collider.GetComponentInChildren<MeshCollider>();
5197 if (mesh != null && mesh.sharedMesh.name.Contains("rock_"))
5198 {
5199 sourcePos.y = hitinfo.point.y;
5200 done = True;
5201 }
5202 }
5203 }
5204#if DEBUG
5205 Puts("GetGround(): Looking for cave or rock");
5206#endif
5207 //if (!_config.Home.AllowCave && Physics.SphereCast(sourcePos, .1f, up, out hitinfo, 250, groundLayer) && hitinfo.collider.name.Contains("rock_"))
5208 if (!config.Home.AllowCave && Physics.Raycast(sourcePos, Up, out hitinfo, 250f, groundLayer) && hitinfo.collider.name.Contains("rock_"))
5209 {
5210#if DEBUG
5211 Puts("GetGround(): found cave or rock");
5212#endif
5213 sourcePos.y = newPos.y - 10;
5214 done = True;
5215 }
5216 return done ? sourcePos : newPos;
5217 }
5218
5219 private bool GetLift(Vector3 position)
5220 {
5221 List<ProceduralLift> nearObjectsOfType = new List<ProceduralLift>();
5222 Vis.Entities<ProceduralLift>(position, 0.5f, nearObjectsOfType);
5223 if (nearObjectsOfType.Count > 0)
5224 {
5225 return True;
5226 }
5227 return False;
5228 }
5229
5230 private Vector3 GetGroundBuilding(Vector3 sourcePos)
5231 {
5232 sourcePos.y = TerrainMeta.HeightMap.GetHeight(sourcePos);
5233 RaycastHit hitinfo;
5234 if (Physics.Raycast(sourcePos, Down, out hitinfo, buildingLayer))
5235 {
5236 sourcePos.y = Mathf.Max(hitinfo.point.y, sourcePos.y);
5237 return sourcePos;
5238 }
5239 if (Physics.Raycast(sourcePos, Up, out hitinfo, buildingLayer))
5240 sourcePos.y = Mathf.Max(hitinfo.point.y, sourcePos.y);
5241 return sourcePos;
5242 }
5243
5244 private bool UnderneathFoundation(Vector3 position)
5245 {
5246 // Check for foundation half-height above where home was set
5247 foreach (var hit in Physics.RaycastAll(position, Up, 2f, buildingLayer))
5248 {
5249 if (hit.GetCollider().name.Contains("foundation"))
5250 {
5251 return True;
5252 }
5253 }
5254 // Check for foundation full-height above where home was set
5255 // Since you can't see from inside via ray, start above.
5256 foreach (var hit in Physics.RaycastAll(position + Up + Up + Up + Up, Down, 2f, buildingLayer))
5257 {
5258 if (hit.GetCollider().name.Contains("foundation"))
5259 {
5260 return True;
5261 }
5262 }
5263
5264 return False;
5265 }
5266
5267 private bool IsAllowed(BasePlayer player, string perm = null)
5268 {
5269 var playerAuthLevel = player.net?.connection?.authLevel;
5270
5271 int requiredAuthLevel = 3;
5272 if (config.Admin.UseableByModerators)
5273 {
5274 requiredAuthLevel = 1;
5275 }
5276 else if (config.Admin.UseableByAdmins)
5277 {
5278 requiredAuthLevel = 2;
5279 }
5280 if (playerAuthLevel >= requiredAuthLevel) return True;
5281
5282 return !string.IsNullOrEmpty(perm) && permission.UserHasPermission(player.UserIDString, perm);
5283 }
5284
5285 private bool IsAllowedMsg(BasePlayer player, string perm = null)
5286 {
5287 if (IsAllowed(player, perm)) return True;
5288 PrintMsg(player, "NotAllowed");
5289 return False;
5290 }
5291
5292 private int GetHigher(BasePlayer player, Dictionary<string, int> limits, int limit, bool unlimited)
5293 {
5294 if (unlimited && limit == 0) return limit;
5295
5296 foreach (var l in limits)
5297 {
5298 if (permission.UserHasPermission(player.UserIDString, l.Key))
5299 {
5300 if (unlimited && l.Value == 0) return l.Value;
5301
5302 limit = Math.Max(l.Value, limit);
5303 }
5304 }
5305 return limit;
5306 }
5307
5308 private int GetLower(BasePlayer player, Dictionary<string, int> times, int time)
5309 {
5310 foreach (var l in times)
5311 {
5312 if (permission.UserHasPermission(player.UserIDString, l.Key))
5313 {
5314 time = Math.Min(l.Value, time);
5315 }
5316 }
5317 return time;
5318 }
5319
5320 private void CheckPerms(Dictionary<string, int> limits)
5321 {
5322 foreach (var limit in limits)
5323 {
5324 if (!permission.PermissionExists(limit.Key))
5325 {
5326 permission.RegisterPermission(limit.Key, this);
5327 }
5328 }
5329 }
5330 #endregion
5331
5332 #region Message
5333 private string _(string msgId, BasePlayer player, params object[] args)
5334 {
5335 var msg = lang.GetMessage(msgId, this, player?.UserIDString);
5336 return args.Length > 0 ? string.Format(msg, args) : msg;
5337 }
5338
5339 private void PrintMsgL(BasePlayer player, string msgId, params object[] args)
5340 {
5341 if (player == null) return;
5342 PrintMsg(player, _(msgId, player, args));
5343 }
5344
5345 private void PrintMsg(BasePlayer player, string msg)
5346 {
5347 if (player == null) return;
5348 //SendReply(player, $"{config.Settings.ChatName}{msg}");
5349 Player.Message(player, $"{config.Settings.ChatName}{msg}", config.Settings.ChatID);
5350 }
5351 #endregion
5352
5353 #region DrawBox
5354 private static void DrawBox(BasePlayer player, Vector3 center, Quaternion rotation, Vector3 size)
5355 {
5356 size = size / 2;
5357 var point1 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z + size.z), center, rotation);
5358 var point2 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z + size.z), center, rotation);
5359 var point3 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z - size.z), center, rotation);
5360 var point4 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z - size.z), center, rotation);
5361 var point5 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z + size.z), center, rotation);
5362 var point6 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z + size.z), center, rotation);
5363 var point7 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z - size.z), center, rotation);
5364 var point8 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z - size.z), center, rotation);
5365
5366 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point2);
5367 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point3);
5368 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point5);
5369 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point2);
5370 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point3);
5371 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point8);
5372
5373 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point5, point6);
5374 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point5, point7);
5375 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point6, point2);
5376 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point8, point6);
5377 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point8, point7);
5378 player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point7, point3);
5379 }
5380
5381 private static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion rotation)
5382 {
5383 return rotation * (point - pivot) + pivot;
5384 }
5385 #endregion
5386
5387 #region FindPlayer
5388 private ulong FindPlayersSingleId(string nameOrIdOrIp, BasePlayer player)
5389 {
5390 var targets = FindPlayers(nameOrIdOrIp);
5391 if (targets.Count > 1)
5392 {
5393 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(p => p.displayName).ToArray()));
5394 targets.Clear();
5395 return 0;
5396 }
5397 ulong userId;
5398 if (targets.Count <= 0)
5399 {
5400 if (ulong.TryParse(nameOrIdOrIp, out userId)) return userId;
5401 PrintMsgL(player, "PlayerNotFound");
5402 return 0;
5403 }
5404 else
5405 userId = targets.First().userID;
5406 targets.Clear();
5407 return userId;
5408 }
5409
5410 private BasePlayer FindPlayersSingle(string nameOrIdOrIp, BasePlayer player)
5411 {
5412 var targets = FindPlayers(nameOrIdOrIp);
5413 if (targets.Count <= 0)
5414 {
5415 PrintMsgL(player, "PlayerNotFound");
5416 return null;
5417 }
5418 if (targets.Count > 1)
5419 {
5420 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(p => p.displayName).ToArray()));
5421 targets.Clear();
5422 return null;
5423 }
5424 var t = targets.First();
5425 targets.Clear();
5426 return t;
5427 }
5428
5429 private static List<BasePlayer> FindPlayers(string nameOrIdOrIp)
5430 {
5431 if (string.IsNullOrEmpty(nameOrIdOrIp)) return new List<BasePlayer>();
5432 return BasePlayer.allPlayerList.Where(p => p && (p.UserIDString == nameOrIdOrIp || p.displayName.Contains(nameOrIdOrIp, CompareOptions.OrdinalIgnoreCase) || (p.IsConnected && p.net.connection.ipaddress.Contains(nameOrIdOrIp)))).ToList();
5433 }
5434
5435 private static List<BasePlayer> FindPlayersOnline(string nameOrIdOrIp)
5436 {
5437 if (string.IsNullOrEmpty(nameOrIdOrIp)) return new List<BasePlayer>();
5438 return BasePlayer.activePlayerList.Where(p => p.UserIDString == nameOrIdOrIp || p.displayName.Contains(nameOrIdOrIp, CompareOptions.OrdinalIgnoreCase) || (p.IsConnected && p.net.connection.ipaddress.Contains(nameOrIdOrIp))).ToList();
5439 }
5440 #endregion
5441
5442 #region API
5443 private Dictionary<string, Vector3> GetHomes(object playerObj)
5444 {
5445 if (playerObj == null) return null;
5446 if (playerObj is string) playerObj = Convert.ToUInt64(playerObj);
5447 if (!(playerObj is ulong)) throw new ArgumentException("playerObj");
5448 var playerId = (ulong)playerObj;
5449 HomeData homeData;
5450 if (!Home.TryGetValue(playerId, out homeData) || homeData.Locations.Count == 0) return null;
5451 return homeData.Locations;
5452 }
5453
5454 private int GetLimitRemaining(BasePlayer player, string type)
5455 {
5456 if (player == null || string.IsNullOrEmpty(type)) return 0;
5457 var currentDate = DateTime.Now.ToString("d");
5458 int limit;
5459 var remaining = -1;
5460 switch (type.ToLower())
5461 {
5462 case "home":
5463 limit = GetHigher(player, config.Home.VIPDailyLimits, config.Home.DailyLimit, true);
5464 HomeData homeData;
5465 if (!Home.TryGetValue(player.userID, out homeData))
5466 {
5467 Home[player.userID] = homeData = new HomeData();
5468 }
5469 if (homeData.Teleports.Date != currentDate)
5470 {
5471 homeData.Teleports.Amount = 0;
5472 homeData.Teleports.Date = currentDate;
5473 }
5474 if (limit > 0)
5475 {
5476 remaining = limit - homeData.Teleports.Amount;
5477 }
5478 break;
5479 case "town":
5480 limit = GetHigher(player, config.Town.VIPDailyLimits, config.Town.DailyLimit, true);
5481 TeleportData townData;
5482 if (!Town.TryGetValue(player.userID, out townData))
5483 {
5484 Town[player.userID] = townData = new TeleportData();
5485 }
5486 if (townData.Date != currentDate)
5487 {
5488 townData.Amount = 0;
5489 townData.Date = currentDate;
5490 }
5491 if (limit > 0)
5492 {
5493 remaining = limit - townData.Amount;
5494 }
5495 break;
5496 case "outpost":
5497 limit = GetHigher(player, config.Outpost.VIPDailyLimits, config.Outpost.DailyLimit, true);
5498 TeleportData outpostData;
5499 if (!Outpost.TryGetValue(player.userID, out outpostData))
5500 {
5501 Outpost[player.userID] = outpostData = new TeleportData();
5502 }
5503 if (outpostData.Date != currentDate)
5504 {
5505 outpostData.Amount = 0;
5506 outpostData.Date = currentDate;
5507 }
5508 if (limit > 0)
5509 {
5510 remaining = limit - outpostData.Amount;
5511 }
5512 break;
5513 case "bandit":
5514 limit = GetHigher(player, config.Bandit.VIPDailyLimits, config.Bandit.DailyLimit, true);
5515 TeleportData banditData;
5516 if (!Bandit.TryGetValue(player.userID, out banditData))
5517 {
5518 Bandit[player.userID] = banditData = new TeleportData();
5519 }
5520 if (banditData.Date != currentDate)
5521 {
5522 banditData.Amount = 0;
5523 banditData.Date = currentDate;
5524 }
5525 if (limit > 0)
5526 {
5527 remaining = limit - banditData.Amount;
5528 }
5529 break;
5530 case "tpr":
5531 limit = GetHigher(player, config.TPR.VIPDailyLimits, config.TPR.DailyLimit, true);
5532 TeleportData tprData;
5533 if (!TPR.TryGetValue(player.userID, out tprData))
5534 {
5535 TPR[player.userID] = tprData = new TeleportData();
5536 }
5537 if (tprData.Date != currentDate)
5538 {
5539 tprData.Amount = 0;
5540 tprData.Date = currentDate;
5541 }
5542 if (limit > 0)
5543 {
5544 remaining = limit - tprData.Amount;
5545 }
5546 break;
5547 }
5548 return remaining;
5549 }
5550
5551 private int GetCooldownRemaining(BasePlayer player, string type)
5552 {
5553 if (player == null || string.IsNullOrEmpty(type)) return 0;
5554 var currentDate = DateTime.Now.ToString("d");
5555 var timestamp = Facepunch.Math.Epoch.Current;
5556 int cooldown;
5557 var remaining = -1;
5558 switch (type.ToLower())
5559 {
5560 case "home":
5561 cooldown = GetLower(player, config.Home.VIPCooldowns, config.Home.Cooldown);
5562 HomeData homeData;
5563 if (!Home.TryGetValue(player.userID, out homeData))
5564 {
5565 Home[player.userID] = homeData = new HomeData();
5566 }
5567 if (homeData.Teleports.Date != currentDate)
5568 {
5569 homeData.Teleports.Amount = 0;
5570 homeData.Teleports.Date = currentDate;
5571 }
5572 if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
5573 {
5574 remaining = cooldown - (timestamp - homeData.Teleports.Timestamp);
5575 }
5576 break;
5577 case "town":
5578 cooldown = GetLower(player, config.Town.VIPCooldowns, config.Town.Cooldown);
5579 TeleportData townData;
5580 if (!Town.TryGetValue(player.userID, out townData))
5581 {
5582 Town[player.userID] = townData = new TeleportData();
5583 }
5584 if (townData.Date != currentDate)
5585 {
5586 townData.Amount = 0;
5587 townData.Date = currentDate;
5588 }
5589 if (cooldown > 0 && timestamp - townData.Timestamp < cooldown)
5590 {
5591 remaining = cooldown - (timestamp - townData.Timestamp);
5592 }
5593 break;
5594 case "outpost":
5595 cooldown = GetLower(player, config.Outpost.VIPCooldowns, config.Outpost.Cooldown);
5596 TeleportData outpostData;
5597 if (!Outpost.TryGetValue(player.userID, out outpostData))
5598 {
5599 Outpost[player.userID] = outpostData = new TeleportData();
5600 }
5601 if (outpostData.Date != currentDate)
5602 {
5603 outpostData.Amount = 0;
5604 outpostData.Date = currentDate;
5605 }
5606 if (cooldown > 0 && timestamp - outpostData.Timestamp < cooldown)
5607 {
5608 remaining = cooldown - (timestamp - outpostData.Timestamp);
5609 }
5610 break;
5611 case "bandit":
5612 cooldown = GetLower(player, config.Bandit.VIPCooldowns, config.Bandit.Cooldown);
5613 TeleportData banditData;
5614 if (!Bandit.TryGetValue(player.userID, out banditData))
5615 {
5616 Bandit[player.userID] = banditData = new TeleportData();
5617 }
5618 if (banditData.Date != currentDate)
5619 {
5620 banditData.Amount = 0;
5621 banditData.Date = currentDate;
5622 }
5623 if (cooldown > 0 && timestamp - banditData.Timestamp < cooldown)
5624 {
5625 remaining = cooldown - (timestamp - banditData.Timestamp);
5626 }
5627 break;
5628 case "tpr":
5629 cooldown = GetLower(player, config.TPR.VIPCooldowns, config.TPR.Cooldown);
5630 TeleportData tprData;
5631 if (!TPR.TryGetValue(player.userID, out tprData))
5632 {
5633 TPR[player.userID] = tprData = new TeleportData();
5634 }
5635 if (tprData.Date != currentDate)
5636 {
5637 tprData.Amount = 0;
5638 tprData.Date = currentDate;
5639 }
5640 if (cooldown > 0 && timestamp - tprData.Timestamp < cooldown)
5641 {
5642 remaining = cooldown - (timestamp - tprData.Timestamp);
5643 }
5644 break;
5645 }
5646 return remaining;
5647 }
5648 #endregion
5649
5650 private class UnityVector3Converter : JsonConverter
5651 {
5652 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
5653 {
5654 var vector = (Vector3)value;
5655 writer.WriteValue($"{vector.x} {vector.y} {vector.z}");
5656 }
5657
5658 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
5659 {
5660 if (reader.TokenType == JsonToken.String)
5661 {
5662 var values = reader.Value.ToString().Trim().Split(' ');
5663 return new Vector3(Convert.ToSingle(values[0]), Convert.ToSingle(values[1]), Convert.ToSingle(values[2]));
5664 }
5665 var o = JObject.Load(reader);
5666 return new Vector3(Convert.ToSingle(o["x"]), Convert.ToSingle(o["y"]), Convert.ToSingle(o["z"]));
5667 }
5668
5669 public override bool CanConvert(Type objectType)
5670 {
5671 return objectType == typeof(Vector3);
5672 }
5673 }
5674
5675 private class CustomComparerDictionaryCreationConverter<T> : CustomCreationConverter<IDictionary>
5676 {
5677 private readonly IEqualityComparer<T> comparer;
5678
5679 public CustomComparerDictionaryCreationConverter(IEqualityComparer<T> comparer)
5680 {
5681 if (comparer == null)
5682 throw new ArgumentNullException(nameof(comparer));
5683 this.comparer = comparer;
5684 }
5685
5686 public override bool CanConvert(Type objectType)
5687 {
5688 return HasCompatibleInterface(objectType) && HasCompatibleConstructor(objectType);
5689 }
5690
5691 private static bool HasCompatibleInterface(Type objectType)
5692 {
5693 return objectType.GetInterfaces().Where(i => HasGenericTypeDefinition(i, typeof(IDictionary<,>))).Any(i => typeof(T).IsAssignableFrom(i.GetGenericArguments().First()));
5694 }
5695
5696 private static bool HasGenericTypeDefinition(Type objectType, Type typeDefinition)
5697 {
5698 return objectType.GetTypeInfo().IsGenericType && objectType.GetGenericTypeDefinition() == typeDefinition;
5699 }
5700
5701 private static bool HasCompatibleConstructor(Type objectType)
5702 {
5703 return objectType.GetConstructor(new[] { typeof(IEqualityComparer<T>) }) != null;
5704 }
5705
5706 public override IDictionary Create(Type objectType)
5707 {
5708 return Activator.CreateInstance(objectType, comparer) as IDictionary;
5709 }
5710 }
5711
5712 [HookMethod("SendHelpText")]
5713 private void SendHelpText(BasePlayer player)
5714 {
5715 PrintMsgL(player, "<size=14>NTeleportation</size> by <color=#ce422b>Nogrod</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");
5716 }
5717
5718 private bool API_HavePendingRequest(BasePlayer player)
5719 {
5720 return PendingRequests.ContainsKey(player.userID) || PlayersRequests.ContainsKey(player.userID) || TeleportTimers.ContainsKey(player.userID);
5721 }
5722
5723 private bool API_HaveAvailableHomes(BasePlayer player)
5724 {
5725 HomeData homeData;
5726 if (!Home.TryGetValue(player.userID, out homeData))
5727 {
5728 Home[player.userID] = homeData = new HomeData();
5729 }
5730
5731 var limit = GetHigher(player, config.Home.VIPHomesLimits, config.Home.HomesLimit, true);
5732
5733 if (limit == 0) return True;
5734
5735 return homeData.Locations.Count < limit;
5736 }
5737
5738 private List<string> API_GetHomes(BasePlayer player)
5739 {
5740 HomeData homeData;
5741 if (!Home.TryGetValue(player.userID, out homeData))
5742 {
5743 Home[player.userID] = homeData = new HomeData();
5744 }
5745
5746 return homeData.Locations.Keys.ToList();
5747 }
5748 }
5749}