· 4 years ago · Sep 04, 2021, 07:48 AM
1using System;
2using System.Linq;
3using System.Collections.Generic;
4using System.Text.RegularExpressions;
5
6using Oxide.Core;
7using Oxide.Core.Plugins;
8using Oxide.Game.Rust.Cui;
9
10using Newtonsoft.Json.Linq;
11
12using UnityEngine;
13using Facepunch;
14using Rust;
15
16namespace Oxide.Plugins
17{
18 [Info ("No Escape", "Calytic (edit by Steenamaroo)", "2.1.4")]
19 [Description ("Prevent commands/actions while raid and/or combat is occuring")]
20 class NoEscape : RustPlugin
21 {
22 #region Setup & Configuration
23
24 List<string> blockTypes = new List<string> ()
25 {
26 "remove",
27 "tp",
28 "bank",
29 "trade",
30 "recycle",
31 "shop",
32 "bgrade",
33 "build",
34 "repair",
35 "upgrade",
36 "vend",
37 "kit",
38 "assignbed",
39 "craft",
40 "mailbox",
41 "backpack"
42 };
43
44 // COMBAT SETTINGS
45 bool combatBlock;
46 static float combatDuration;
47 bool combatOnHitPlayer;
48 float combatOnHitPlayerMinCondition;
49 float combatOnHitPlayerMinDamage;
50
51 bool combatOnTakeDamage;
52 float combatOnTakeDamageMinCondition;
53 float combatOnTakeDamageMinDamage;
54
55 bool combatOnHitNPC;
56 bool combatOnTakeDamageNPC;
57
58 // RAID BLOCK SETTINGS
59 bool raidBlock;
60 static float raidDuration;
61 float raidDistance;
62 bool blockOnDamage;
63 float blockOnDamageMinCondition;
64 bool blockOnDestroy;
65
66 // RAID-ONLY SETTINGS
67 bool ownerCheck;
68 bool blockUnowned;
69 bool blockAll;
70 // IGNORES ALL OTHER CHECKS
71 bool ownerBlock;
72 bool cupboardShare;
73 bool friendShare;
74 bool clanShare;
75 bool clanCheck;
76 bool friendCheck;
77 bool raiderBlock;
78 List<string> raidDamageTypes;
79 List<string> raidDeathTypes;
80 List<string> combatDamageTypes;
81
82 // RAID UNBLOCK SETTINGS
83 bool raidUnblockOnDeath;
84 bool raidUnblockOnWakeup;
85 bool raidUnblockOnRespawn;
86
87 // COMBAT UNBLOCK SETTINGS
88 bool combatUnblockOnDeath;
89 bool combatUnblockOnWakeup;
90 bool combatUnblockOnRespawn;
91
92 float cacheTimer;
93
94 // MESSAGES
95 bool raidBlockNotify;
96 bool combatBlockNotify;
97
98 bool useZoneManager;
99 bool zoneEnter;
100 bool zoneLeave;
101
102 bool useRaidableBases;
103 bool raidableZoneEnter;
104 bool raidableZoneLeave;
105
106 bool sendUINotification;
107 bool sendChatNotification;
108 bool sendGUIAnnouncementsNotification;
109 bool sendLustyMapNotification;
110
111 string GUIAnnouncementTintColor = "Red";
112 string GUIAnnouncementTextColor = "White";
113
114 string LustyMapIcon = "special";
115 float LustyMapDuration = 150f;
116
117 Dictionary<string, RaidZone> zones = new Dictionary<string, RaidZone> ();
118 Dictionary<string, List<string>> memberCache = new Dictionary<string, List<string>> ();
119 Dictionary<string, string> clanCache = new Dictionary<string, string> ();
120 Dictionary<string, List<string>> friendCache = new Dictionary<string, List<string>> ();
121 Dictionary<string, DateTime> lastClanCheck = new Dictionary<string, DateTime> ();
122 Dictionary<string, DateTime> lastCheck = new Dictionary<string, DateTime> ();
123 Dictionary<string, DateTime> lastFriendCheck = new Dictionary<string, DateTime> ();
124 Dictionary<string, bool> prefabBlockCache = new Dictionary<string, bool> ();
125 internal Dictionary<ulong, BlockBehavior> blockBehaviors = new Dictionary<ulong, BlockBehavior> ();
126
127 public static NoEscape plugin;
128
129 [PluginReference]
130 Plugin Clans, Friends, ZoneManager, GUIAnnouncements, LustyMap;
131
132 readonly int cupboardMask = LayerMask.GetMask ("Deployed");
133 readonly int blockLayer = LayerMask.GetMask ("Player (Server)");
134 Dictionary<string, bool> _cachedExcludedWeapons = new Dictionary<string, bool> ();
135
136 List<string> blockedPrefabs = new List<string> ()
137 {
138 "door",
139 "window.bars",
140 "floor.ladder.hatch",
141 "floor.frame",
142 "wall.frame",
143 "shutter",
144 "external"
145 };
146
147 List<string> exceptionPrefabs = new List<string> ()
148 {
149 "ladder.wooden"
150 };
151
152 List<string> exceptionWeapons = new List<string> ()
153 {
154 "torch"
155 };
156
157 private List<string> GetDefaultRaidDamageTypes ()
158 {
159 return new List<DamageType> ()
160 {
161 DamageType.Bullet,
162 DamageType.Blunt,
163 DamageType.Stab,
164 DamageType.Slash,
165 DamageType.Explosion,
166 DamageType.Heat
167 }.Select (x => x.ToString ()).ToList<string> ();
168 }
169
170 private List<string> GetDefaultCombatDamageTypes ()
171 {
172 return new List<DamageType> ()
173 {
174 DamageType.Bullet,
175 DamageType.Arrow,
176 DamageType.Blunt,
177 DamageType.Stab,
178 DamageType.Slash,
179 DamageType.Explosion,
180 DamageType.Heat,
181 DamageType.ElectricShock
182 }.Select (x => x.ToString ()).ToList<string> ();
183 }
184
185 Dictionary<string, object> blockWhenRaidDamageDefault = new Dictionary<string, object> () {
186 {"enabled", true},
187 {"minCondition", 100f},
188 };
189
190 Dictionary<string, object> blockWhenCombatDamageDefault = new Dictionary<string, object> () {
191 {"enabled", false},
192 {"minCondition", 100f},
193 {"minDamage", 1f},
194 };
195
196 static Regex _htmlRegex = new Regex ("<.*?>", RegexOptions.Compiled);
197
198 protected override void LoadDefaultConfig ()
199 {
200 Config ["VERSION"] = Version.ToString ();
201
202 // RAID SETTINGS
203 Config ["Raid", "Block", "enabled"] = true;
204 Config ["Raid", "Block", "duration"] = 300f; // 5 minutes
205 Config ["Raid", "Block", "distance"] = 100f;
206 Config ["Raid", "Block", "notify"] = true;
207 Config ["Raid", "Block", "damageTypes"] = GetDefaultRaidDamageTypes ();
208 Config ["Raid", "Block", "deathTypes"] = GetDefaultRaidDamageTypes();
209 Config ["Raid", "Block", "includePrefabs"] = blockedPrefabs;
210 Config ["Raid", "Block", "excludePrefabs"] = exceptionPrefabs;
211 Config ["Raid", "Block", "excludeWeapons"] = exceptionWeapons;
212
213 Config ["Raid", "BlockWhen", "damage"] = blockWhenRaidDamageDefault;
214
215 Config ["Raid", "BlockWhen", "destroy"] = true;
216 Config ["Raid", "BlockWhen", "unowned"] = false;
217
218 Config ["Raid", "BlockWho", "everyone"] = true;
219 Config ["Raid", "BlockWho", "owner"] = false;
220 Config ["Raid", "BlockWho", "cupboardAuthorized"] = false;
221 Config ["Raid", "BlockWho", "clan"] = false;
222 Config ["Raid", "BlockWho", "friends"] = false;
223 Config ["Raid", "BlockWho", "raider"] = false;
224
225 Config ["Raid", "BlockExcept", "owner"] = true;
226 Config ["Raid", "BlockExcept", "friends"] = false;
227 Config ["Raid", "BlockExcept", "clan"] = false;
228
229 Config ["Raid", "Zone", "enabled"] = false;
230 Config ["Raid", "Zone", "enter"] = true;
231 Config ["Raid", "Zone", "leave"] = false;
232
233 Config ["Raid", "Map", "enabled"] = false;
234 Config ["Raid", "Map", "icon"] = "special";
235 Config ["Raid", "Map", "duration"] = 150f;
236
237 Config ["Raid", "UnblockWhen", "death"] = true;
238 Config ["Raid", "UnblockWhen", "wakeup"] = false;
239 Config ["Raid", "UnblockWhen", "respawn"] = true;
240
241 // COMBAT SETTINGS
242 Config ["Combat", "Block", "enabled"] = false;
243 Config ["Combat", "Block", "duration"] = 180f; // 3 minutes
244 Config ["Combat", "Block", "notify"] = true;
245 Config ["Combat", "Block", "damageTypes"] = GetDefaultCombatDamageTypes ();
246
247 Config ["Combat", "BlockWhen", "giveDamage"] = blockWhenCombatDamageDefault;
248 Config ["Combat", "BlockWhen", "takeDamage"] = blockWhenCombatDamageDefault;
249
250 Config ["Combat", "BlockWhen", "npcGiveDamage"] = false;
251 Config ["Combat", "BlockWhen", "npcTakeDamage"] = false;
252
253 Config ["Combat", "UnblockWhen", "death"] = true;
254 Config ["Combat", "UnblockWhen", "wakeup"] = false;
255 Config ["Combat", "UnblockWhen", "respawn"] = true;
256
257 Config ["Settings", "cacheMinutes"] = 1f;
258 Config ["Settings", "Block", "Types"] = blockTypes;
259
260 Config ["Notifications", "UI"] = true;
261 Config ["Notifications", "Chat"] = true;
262 Config ["Notifications", "GUIAnnouncements", "enabled"] = false;
263 Config ["Notifications", "GUIAnnouncements", "backgroundColor"] = "Red";
264 Config ["Notifications", "GUIAnnouncements", "textColor"] = "White";
265
266 Config ["VERSION"] = Version.ToString ();
267 }
268
269 void Loaded ()
270 {
271 LoadMessages ();
272 }
273
274 void Unload ()
275 {
276 if (useZoneManager)
277 foreach (var zone in zones.ToList ())
278 EraseZone (zone.Value.zoneid);
279
280 var objects = GameObject.FindObjectsOfType (typeof (RaidBlock));
281 if (objects != null)
282 foreach (var gameObj in objects)
283 if (!((RaidBlock)gameObj).Active)
284 GameObject.Destroy (gameObj);
285
286 objects = GameObject.FindObjectsOfType (typeof (CombatBlock));
287 if (objects != null)
288 foreach (var gameObj in objects)
289 if (!((CombatBlock)gameObj).Active)
290 GameObject.Destroy (gameObj);
291 }
292
293 void LoadMessages ()
294 {
295 lang.RegisterMessages (new Dictionary<string, string>
296 {
297 { "Raid Blocked Message", "You may not do that while raid blocked ({time})" },
298 { "Combat Blocked Message", "You may do that while a in combat ({time})" },
299 { "Raid Block Complete", "You are no longer raid blocked." },
300 { "Combat Block Complete", "You are no longer combat blocked." },
301 { "Raid Block Notifier", "You are raid blocked for {time}" },
302 { "Combat Block Notifier", "You are combat blocked for {time}" },
303 { "Combat Block UI Message", "COMBAT BLOCK" },
304 { "Raid Block UI Message", "RAID BLOCK" },
305 { "Unit Seconds", "second(s)" },
306 { "Unit Minutes", "minute(s)" },
307 { "Prefix", string.Empty }
308 }, this);
309 }
310
311 void CheckConfig ()
312 {
313 if (Config ["VERSION"] == null) {
314 // FOR COMPATIBILITY WITH INITIAL VERSIONS WITHOUT VERSIONED CONFIG
315 ReloadConfig ();
316 } else if (GetConfig ("VERSION", string.Empty) != Version.ToString ()) {
317 // ADDS NEW, IF ANY, CONFIGURATION OPTIONS
318 ReloadConfig ();
319 }
320 }
321
322 protected void ReloadConfig ()
323 {
324 Config ["VERSION"] = Version.ToString ();
325
326 // NEW CONFIGURATION OPTIONS HERE
327 // END NEW CONFIGURATION OPTIONS
328
329 PrintToConsole ("Upgrading configuration file");
330 SaveConfig ();
331 }
332
333 void OnServerInitialized ()
334 {
335 NoEscape.plugin = this;
336
337 permission.RegisterPermission ("noescape.disable", this);
338
339 blockTypes = GetConfig ("Settings", "Block", "Types", blockTypes);
340
341 foreach (string command in blockTypes) {
342 permission.RegisterPermission ("noescape.raid." + command + "block", this);
343 permission.RegisterPermission ("noescape.combat." + command + "block", this);
344 }
345
346 CheckConfig ();
347
348 // RAID SETTINGS
349 raidBlock = GetConfig ("Raid", "Block", "enabled", true);
350 raidDuration = GetConfig ("Raid", "Block", "duration", 300f);
351 raidDistance = GetConfig ("Raid", "Block", "distance", 100f);
352 raidBlockNotify = GetConfig ("Raid", "Block", "notify", true);
353 raidDamageTypes = GetConfig ("Raid", "Block", "damageTypes", GetDefaultRaidDamageTypes ());
354 raidDeathTypes = GetConfig("Raid", "Block", "deathTypes", GetDefaultRaidDamageTypes());
355 blockedPrefabs = GetConfig ("Raid", "Block", "includePrefabs", blockedPrefabs);
356 exceptionPrefabs = GetConfig ("Raid", "Block", "excludePrefabs", exceptionPrefabs);
357 exceptionWeapons = GetConfig ("Raid", "Block", "excludeWeapons", exceptionWeapons);
358
359 Dictionary<string, object> blockOnRaidDamageDetails = GetConfig ("Raid", "BlockWhen", "damage", blockWhenRaidDamageDefault);
360 if (blockOnRaidDamageDetails.ContainsKey ("enabled")) {
361 blockOnDamage = (bool)blockOnRaidDamageDetails ["enabled"];
362 } else {
363 blockOnDamage = true;
364 }
365
366 if (blockOnRaidDamageDetails.ContainsKey ("minCondition")) {
367 blockOnDamageMinCondition = Convert.ToSingle (blockOnRaidDamageDetails ["minCondition"]);
368 } else {
369 blockOnDamageMinCondition = 100f;
370 }
371
372 blockOnDestroy = GetConfig ("Raid", "BlockWhen", "destroy", true);
373 blockUnowned = GetConfig ("Raid", "BlockWhen", "unowned", false);
374
375 blockAll = GetConfig ("Raid", "BlockWho", "everyone", true);
376 ownerBlock = GetConfig ("Raid", "BlockWho", "owner", false);
377 friendShare = GetConfig ("Raid", "BlockWho", "friends", false);
378 clanShare = GetConfig ("Raid", "BlockWho", "clan", false);
379 cupboardShare = GetConfig ("Raid", "BlockWho", "cupboardAuthorized", false);
380 raiderBlock = GetConfig ("Raid", "BlockWho", "raider", false);
381
382 ownerCheck = GetConfig ("Raid", "BlockExcept", "owner", true);
383 friendCheck = GetConfig ("Raid", "BlockExcept", "friends", false);
384 clanCheck = GetConfig ("Raid", "BlockExcept", "clan", false);
385
386 useZoneManager = GetConfig ("Raid", "Zone", "enabled", false);
387 zoneEnter = GetConfig ("Raid", "Zone", "enter", true);
388 zoneLeave = GetConfig ("Raid", "Zone", "leave", false);
389
390 useRaidableBases = GetConfig ("Raid", "RaidableBases", "enabled", false);
391 raidableZoneEnter = GetConfig ("Raid", "RaidableBases", "enter", true);
392 raidableZoneLeave = GetConfig ("Raid", "RaidableBases", "leave", false);
393
394 sendLustyMapNotification = GetConfig ("Raid", "Map", "enabled", false);
395 LustyMapIcon = GetConfig ("Raid", "Map", "icon", "special");
396 LustyMapDuration = GetConfig ("Raid", "Map", "duration", 150f);
397
398 raidUnblockOnDeath = GetConfig ("Raid", "UnblockWhen", "death", true);
399 raidUnblockOnWakeup = GetConfig ("Raid", "UnblockWhen", "wakeup", false);
400 raidUnblockOnRespawn = GetConfig ("Raid", "UnblockWhen", "respawn", true);
401
402 // COMBAT SETTINGS
403 combatBlock = GetConfig ("Combat", "Block", "enabled", false);
404 combatDuration = GetConfig ("Combat", "Block", "duration", 180f);
405 combatBlockNotify = GetConfig ("Combat", "Block", "notify", true);
406 combatDamageTypes = GetConfig ("Combat", "Block", "damageTypes", GetDefaultCombatDamageTypes ());
407
408 //combatOnHitPlayer = GetConfig ("Combat", "BlockWhen", "giveDamage", true);
409 Dictionary<string, object> blockOnCombatGiveDamageDetails = GetConfig ("Combat", "BlockWhen", "giveDamage", blockWhenCombatDamageDefault);
410 if (blockOnCombatGiveDamageDetails.ContainsKey ("enabled")) {
411 combatOnHitPlayer = (bool)blockOnCombatGiveDamageDetails ["enabled"];
412 } else {
413 combatOnHitPlayer = false;
414 }
415
416 if (blockOnCombatGiveDamageDetails.ContainsKey ("minCondition")) {
417 combatOnHitPlayerMinCondition = Convert.ToSingle (blockOnCombatGiveDamageDetails ["minCondition"]);
418 } else {
419 combatOnHitPlayerMinCondition = 100f;
420 }
421
422 //combatOnTakeDamage = GetConfig ("Combat", "BlockWhen", "takeDamage", true);
423 Dictionary<string, object> blockOnCombatTakeDamageDetails = GetConfig ("Combat", "BlockWhen", "takeDamage", blockWhenCombatDamageDefault);
424 if (blockOnCombatTakeDamageDetails.ContainsKey ("enabled")) {
425 combatOnTakeDamage = (bool)blockOnCombatTakeDamageDetails ["enabled"];
426 } else {
427 combatOnTakeDamage = false;
428 }
429
430 if (blockOnCombatTakeDamageDetails.ContainsKey ("minCondition")) {
431 combatOnTakeDamageMinCondition = Convert.ToSingle (blockOnCombatTakeDamageDetails ["minCondition"]);
432 } else {
433 combatOnTakeDamageMinCondition = 100f;
434 }
435
436 if (blockOnCombatTakeDamageDetails.ContainsKey ("minDamage")) {
437 combatOnTakeDamageMinDamage = Convert.ToSingle (blockOnCombatTakeDamageDetails ["minDamage"]);
438 } else {
439 combatOnTakeDamageMinDamage = 1f;
440 }
441
442 combatOnHitNPC = GetConfig ("Combat", "BlockWhen", "npcGiveDamage", false);
443 combatOnTakeDamageNPC = GetConfig ("Combat", "BlockWhen", "npcTakeDamage", false);
444
445 combatUnblockOnDeath = GetConfig ("Combat", "UnblockWhen", "death", true);
446 combatUnblockOnWakeup = GetConfig ("Combat", "UnblockWhen", "wakeup", false);
447 combatUnblockOnRespawn = GetConfig ("Combat", "UnblockWhen", "respawn", true);
448
449 cacheTimer = GetConfig ("Settings", "cacheMinutes", 1f);
450
451 sendUINotification = GetConfig ("Notifications", "UI", true);
452 sendChatNotification = GetConfig ("Notifications", "Chat", true);
453
454 sendGUIAnnouncementsNotification = GetConfig ("Notifications", "GUIAnnouncements", "enabled", false);
455 GUIAnnouncementTintColor = GetConfig ("Notifications", "GUIAnnouncements", "backgroundColor", "Red");
456 GUIAnnouncementTextColor = GetConfig ("Notifications", "GUIAnnouncements", "textColor", "White");
457
458 if ((clanShare || clanCheck) && !Clans) {
459 clanShare = false;
460 clanCheck = false;
461 PrintWarning ("Clans not found! All clan options disabled. Cannot use clan options without this plugin. http://oxidemod.org/plugins/clans.2087");
462 }
463
464 if (friendShare && !Friends) {
465 friendShare = false;
466 friendCheck = false;
467 PrintWarning ("Friends not found! All friend options disabled. Cannot use friend options without this plugin. http://oxidemod.org/plugins/friends-api.686");
468 }
469
470 if (useZoneManager && !ZoneManager) {
471 useZoneManager = false;
472 PrintWarning ("ZoneManager not found! All zone options disabled. Cannot use zone options without this plugin. http://oxidemod.org/plugins/zones-manager.739");
473 }
474
475 if (sendGUIAnnouncementsNotification && !GUIAnnouncements) {
476 sendGUIAnnouncementsNotification = false;
477 PrintWarning ("GUIAnnouncements not found! GUI announcement option disabled. Cannot use gui announcement integration without this plugin. http://oxidemod.org/plugins/gui-announcements.1222");
478 }
479
480 if (sendLustyMapNotification && !LustyMap) {
481 sendLustyMapNotification = false;
482 PrintWarning ("LustyMap not found! LustyMap notification option disabled. Cannot use LustyMap integration without this plugin. http://oxidemod.org/plugins/lustymap.1333");
483 }
484
485 if (sendLustyMapNotification && LustyMap && LustyMapDuration <= 0) {
486 PrintWarning ("LustyMap icon duration is zero, no icon will be displayed");
487 }
488
489 UnsubscribeHooks ();
490 }
491
492 void UnsubscribeHooks ()
493 {
494 if (!blockOnDestroy && !raidUnblockOnDeath && !combatUnblockOnDeath)
495 Unsubscribe ("OnEntityDeath");
496
497 if (!raidUnblockOnWakeup && !combatUnblockOnWakeup)
498 Unsubscribe ("OnPlayerSleepEnded");
499
500 if (!combatOnTakeDamage && !combatOnHitPlayer)
501 Unsubscribe ("OnPlayerAttack");
502
503 if (!blockTypes.Contains ("repair"))
504 Unsubscribe ("OnStructureRepair");
505
506 if (!blockTypes.Contains ("upgrade"))
507 Unsubscribe ("OnStructureUpgrade");
508
509 if (!blockTypes.Contains ("mailbox"))
510 Unsubscribe ("CanUseMailbox");
511
512 if (!blockTypes.Contains ("vend"))
513 Unsubscribe ("CanUseVending");
514
515 if (!blockTypes.Contains ("build"))
516 Unsubscribe ("CanBuild");
517
518 if (!blockTypes.Contains ("assignbed"))
519 Unsubscribe ("CanAssignBed");
520
521 if (!blockTypes.Contains ("craft"))
522 Unsubscribe ("CanCraft");
523
524 if (!blockTypes.Contains ("backpack"))
525 Unsubscribe ("CanOpenBackpack");
526
527 if(!useZoneManager)
528 {
529 Unsubscribe ("OnEnterZone");
530 Unsubscribe ("OnExitZone");
531 }
532
533 if(!useRaidableBases)
534 {
535 Unsubscribe ("OnPlayerEnteredRaidableBase");
536 Unsubscribe ("OnPlayerExitedRaidableBase");
537 }
538 }
539
540 #endregion
541
542 #region Classes
543
544 public class RaidZone
545 {
546 public string zoneid;
547 public Vector3 position;
548 public Timer timer;
549
550 public RaidZone (string zoneid, Vector3 position)
551 {
552 this.zoneid = zoneid;
553 this.position = position;
554 }
555
556 public float Distance (RaidZone zone)
557 {
558 return Vector3.Distance (position, zone.position);
559 }
560
561 public float Distance (Vector3 pos)
562 {
563 return Vector3.Distance (position, pos);
564 }
565
566 public RaidZone ResetTimer ()
567 {
568 if (timer is Timer && !timer.Destroyed)
569 timer.Destroy ();
570
571 return this;
572 }
573 }
574
575 public abstract class BlockBehavior : MonoBehaviour
576 {
577 protected BasePlayer player;
578 public DateTime lastBlock = DateTime.MinValue;
579 public DateTime lastNotification = DateTime.MinValue;
580 internal DateTime lastUINotification = DateTime.MinValue;
581 internal Timer timer;
582 internal Action notifyCallback;
583 internal string iconUID;
584 internal bool moved;
585
586 public void CopyFrom (BlockBehavior behavior)
587 {
588 lastBlock = behavior.lastBlock;
589 lastNotification = behavior.lastNotification;
590 lastUINotification = behavior.lastUINotification;
591 timer = behavior.timer;
592 notifyCallback = behavior.notifyCallback;
593 iconUID = behavior.iconUID;
594 NotificationWindow = behavior.NotificationWindow;
595 }
596
597 internal abstract float Duration { get; }
598
599 internal abstract CuiRectTransformComponent NotificationWindow { get; set; }
600
601 internal abstract string notifyMessage { get; }
602
603 internal string BlockName {
604 get {
605 return GetType ().Name;
606 }
607 }
608
609 public bool Active {
610 get {
611 if (lastBlock > DateTime.MinValue) {
612 TimeSpan ts = DateTime.Now - lastBlock;
613 if (ts.TotalSeconds < Duration) {
614 return true;
615 }
616 }
617
618 GameObject.Destroy (this);
619
620 return false;
621 }
622 }
623
624 void Awake ()
625 {
626 player = GetComponent<BasePlayer> ();
627 if (plugin.blockBehaviors.ContainsKey (player.userID)) {
628 plugin.blockBehaviors.Remove (player.userID);
629 }
630 plugin.blockBehaviors.Add (player.userID, this);
631 }
632
633 void Destroy ()
634 {
635 if (!moved) {
636 Stop ();
637 }
638 CancelInvoke ("Update");
639 }
640
641 void Update ()
642 {
643 if (!plugin.sendUINotification)
644 return;
645 bool send = false;
646 if (lastUINotification == DateTime.MinValue) {
647 lastUINotification = DateTime.Now;
648 send = true;
649 } else {
650 TimeSpan ts = DateTime.Now - lastUINotification;
651 if (ts.TotalSeconds > 2) {
652 send = true;
653 } else {
654 send = false;
655 }
656 }
657
658 if (player is BasePlayer && player.IsConnected) {
659 if (!Active) {
660 CuiHelper.DestroyUi (player, "BlockMsg" + BlockName);
661 }
662
663 if (send && Active) {
664 lastUINotification = DateTime.Now;
665 SendGUI ();
666 }
667 }
668 }
669
670 public void Stop ()
671 {
672 if (notifyCallback is Action)
673 notifyCallback.Invoke ();
674
675 if (timer is Timer && !timer.Destroyed)
676 timer.Destroy ();
677
678 if (plugin.sendUINotification && player is BasePlayer && player.IsConnected)
679 CuiHelper.DestroyUi (player, "BlockMsg" + BlockName);
680
681 plugin.blockBehaviors.Remove (player.userID);
682
683 GameObject.Destroy (this);
684 }
685
686 public void Notify (Action callback)
687 {
688 if (plugin.sendUINotification)
689 SendGUI ();
690
691 notifyCallback = callback;
692 if (timer is Timer && !timer.Destroyed)
693 timer.Destroy ();
694
695 timer = plugin.timer.In (Duration, callback);
696 }
697
698 private string FormatTime (TimeSpan ts)
699 {
700 if (ts.Days > 0)
701 return string.Format ("{0}D, {1}H", ts.Days, ts.Hours);
702
703 if (ts.Hours > 0)
704 return string.Format ("{0}H {1}M", ts.Hours, ts.Minutes);
705
706 return string.Format ("{0}M {1}S", ts.Minutes, ts.Seconds);
707 }
708
709 void SendGUI ()
710 {
711 TimeSpan ts = lastBlock.AddSeconds (Duration) - DateTime.Now;
712
713 string countDown = FormatTime (ts);
714 CuiHelper.DestroyUi (player, "BlockMsg" + BlockName);
715 var elements = new CuiElementContainer ();
716 var BlockMsg = elements.Add (new CuiPanel {
717 Image =
718 {
719 Color = "0.95 0 0.02 0.67"
720 },
721 RectTransform =
722 {
723 AnchorMax = NotificationWindow.AnchorMax,
724 AnchorMin = NotificationWindow.AnchorMin
725 }
726 }, "Hud", "BlockMsg" + BlockName);
727 elements.Add (new CuiElement {
728 Parent = BlockMsg,
729 Components =
730 {
731 new CuiRawImageComponent
732 {
733 Sprite = "assets/icons/explosion.png",
734 Color = "0.95 0 0.02 0.67"
735 },
736 new CuiRectTransformComponent
737 {
738 AnchorMin = "0 0",
739 AnchorMax = "0.13 1"
740 }
741 }
742 });
743 elements.Add (new CuiLabel {
744 RectTransform =
745 {
746 AnchorMin = "0.15 0",
747 AnchorMax = "0.82 1"
748 },
749 Text =
750 {
751 Text = notifyMessage,
752 FontSize = 11,
753 Align = TextAnchor.MiddleLeft,
754 }
755 }, BlockMsg);
756 elements.Add (new CuiElement {
757 Name = "TimerPanel",
758 Parent = BlockMsg,
759 Components =
760 {
761 new CuiImageComponent
762 {
763 Color = "0 0 0 0.64",
764 ImageType = UnityEngine.UI.Image.Type.Filled
765 },
766 new CuiRectTransformComponent
767 {
768 AnchorMin = "0.73 0",
769 AnchorMax = "1 1"
770 }
771 }
772 });
773 elements.Add (new CuiLabel {
774 RectTransform =
775 {
776 AnchorMin = "0 0",
777 AnchorMax = "1 1"
778 },
779 Text =
780 {
781 Text = countDown,
782 FontSize = 12,
783 Align = TextAnchor.MiddleCenter,
784 }
785 }, "TimerPanel");
786 CuiHelper.AddUi (player, elements);
787 }
788
789
790 }
791
792 public class CombatBlock : BlockBehavior
793 {
794 internal override float Duration {
795 get {
796 return combatDuration;
797 }
798 }
799
800 internal override string notifyMessage {
801 get { return GetMsg ("Combat Block UI Message", player); }
802 }
803
804 CuiRectTransformComponent _notificationWindow = null;
805
806 internal override CuiRectTransformComponent NotificationWindow {
807 get {
808 if (_notificationWindow != null) {
809 return _notificationWindow;
810 }
811 return _notificationWindow = new CuiRectTransformComponent () {
812 AnchorMin = "0.44 0.15",
813 AnchorMax = "0.56 0.18"
814 };
815 }
816 set {
817 _notificationWindow = value;
818 }
819 }
820 }
821
822 public class RaidBlock : BlockBehavior
823 {
824 internal override float Duration {
825 get {
826 return raidDuration;
827 }
828 }
829
830 internal override string notifyMessage {
831 get { return GetMsg ("Raid Block UI Message", player); }
832 }
833
834 private CuiRectTransformComponent _notificationWindow = null;
835
836 internal override CuiRectTransformComponent NotificationWindow {
837 get {
838 if (_notificationWindow != null) {
839 return _notificationWindow;
840 }
841 return _notificationWindow = new CuiRectTransformComponent () {
842 AnchorMin = "0.87 0.39",
843 AnchorMax = "0.99 0.42"
844 };
845 }
846 set {
847 _notificationWindow = value;
848 }
849 }
850 }
851
852 #endregion
853
854 #region Oxide Hooks
855
856 void OnEntityTakeDamage (BaseCombatEntity entity, HitInfo hitInfo)
857 {
858 BasePlayer attacker = hitInfo?.InitiatorPlayer;
859 BasePlayer victim = entity as BasePlayer;
860
861 if (victim == attacker)
862 return;
863
864 if (victim != null && attacker != null && hitInfo.Weapon == null)
865 {
866 hitInfo.HitEntity = victim;
867 OnPlayerAttack(attacker, hitInfo);
868 return;
869 }
870
871 if (!blockOnDamage || !raidBlock)
872 return;
873 if (hitInfo == null || hitInfo.Initiator == null || !IsEntityBlocked (entity) || hitInfo.Initiator.transform == null)
874 return;
875 if (!IsRaidDamage (hitInfo.damageTypes))
876 return;
877 if (IsExcludedWeapon (hitInfo?.WeaponPrefab?.ShortPrefabName))
878 return;
879 if (GetHealthPercent (entity, hitInfo.damageTypes.Total ()) > blockOnDamageMinCondition) {
880 return;
881 }
882
883 StructureAttack (entity, hitInfo.Initiator, hitInfo?.WeaponPrefab?.ShortPrefabName, hitInfo.HitPositionWorld);
884 }
885
886 void OnPlayerConnected (BasePlayer player)
887 {
888 BlockBehavior behavior;
889 if (blockBehaviors.TryGetValue (player.userID, out behavior)) {
890 if (behavior is RaidBlock) {
891 var raidBlockComponent = player.gameObject.AddComponent<RaidBlock> ();
892 raidBlockComponent.CopyFrom (behavior);
893 } else if (behavior is CombatBlock) {
894 var combatBlockComponent = player.gameObject.AddComponent<CombatBlock> ();
895 combatBlockComponent.CopyFrom (behavior);
896 }
897
898 behavior.moved = true;
899 GameObject.Destroy (behavior);
900 }
901 }
902
903 void OnPlayerAttack (BasePlayer attacker, HitInfo hitInfo)
904 {
905 if (!combatBlock || !(hitInfo.HitEntity is BasePlayer))
906 return;
907
908 if (!combatOnHitNPC && hitInfo.HitEntity != null && hitInfo.HitEntity.IsNpc)
909 return;
910
911 if (!combatOnTakeDamageNPC && attacker.IsNpc)
912 return;
913
914
915 if (!IsCombatDamage(hitInfo.damageTypes))
916 return;
917
918 float totalDamage = Mathf.Max(1,hitInfo.damageTypes.Total ());
919 BasePlayer target = hitInfo.HitEntity as BasePlayer;
920
921 if (combatOnTakeDamage)
922 {
923
924 if (GetHealthPercent (target, hitInfo.damageTypes.Total ()) > combatOnTakeDamageMinCondition)
925 return;
926
927 if (totalDamage < combatOnTakeDamageMinDamage)
928 return;
929
930 StartCombatBlocking (target);
931 }
932
933 if (combatOnHitPlayer)
934 {
935 if (GetHealthPercent (attacker, hitInfo.damageTypes.Total ()) > combatOnHitPlayerMinCondition)
936 return;
937
938 if (totalDamage < combatOnHitPlayerMinDamage)
939 return;
940
941 StartCombatBlocking (attacker);
942 }
943 }
944
945 void OnEntityDeath (BaseCombatEntity entity, HitInfo hitInfo)
946 {
947 if (blockOnDestroy && raidBlock) {
948 if (hitInfo == null || hitInfo.Initiator == null || !IsDeathDamage (hitInfo.damageTypes) || !IsEntityBlocked (entity))
949 return;
950
951 StructureAttack (entity, hitInfo.Initiator, hitInfo?.WeaponPrefab?.ShortPrefabName, hitInfo.HitPositionWorld);
952 }
953
954 if (entity.ToPlayer () == null)
955 return;
956
957 var player = entity.ToPlayer ();
958 RaidBlock raidBlocker;
959 if (raidBlock && raidUnblockOnDeath && TryGetBlocker (player, out raidBlocker)) {
960 timer.In (0.3f, delegate () {
961 raidBlocker.Stop ();
962 });
963 }
964
965 CombatBlock combatBlocker;
966 if (combatBlock && combatUnblockOnDeath && TryGetBlocker (player, out combatBlocker)) {
967 timer.In (0.3f, delegate () {
968 combatBlocker.Stop ();
969 });
970 }
971 }
972
973 void OnPlayerSleepEnded (BasePlayer player)
974 {
975 if (player == null) return;
976 RaidBlock raidBlocker;
977 if (raidBlock && raidUnblockOnWakeup && TryGetBlocker (player, out raidBlocker)) {
978 timer.In (0.3f, delegate () {
979 raidBlocker.Stop ();
980 });
981 }
982
983 CombatBlock combatBlocker;
984 if (combatBlock && combatUnblockOnWakeup && TryGetBlocker (player, out combatBlocker)) {
985 timer.In (0.3f, delegate () {
986 combatBlocker.Stop ();
987 });
988 }
989 }
990
991 void OnPlayerRespawned (BasePlayer player)
992 {
993 if (player == null) return;
994 RaidBlock raidBlocker;
995 if (raidBlock && raidUnblockOnRespawn && TryGetBlocker (player, out raidBlocker)) {
996 timer.In (0.3f, delegate () {
997 raidBlocker.Stop ();
998 });
999 }
1000
1001 CombatBlock combatBlocker;
1002 if (combatBlock && combatUnblockOnRespawn && TryGetBlocker (player, out combatBlocker)) {
1003 timer.In (0.3f, delegate () {
1004 combatBlocker.Stop ();
1005 });
1006 }
1007 }
1008
1009 object CanLootEntity(BasePlayer player, StorageContainer container)
1010 {
1011 if(container?.GetEntity() is Recycler)
1012 {
1013 object reply = CanDo("recycle", player);
1014
1015 if (reply != null && IsBlocked(player))
1016 {
1017 player.ChatMessage((string)reply);
1018 return true;
1019 }
1020 }
1021
1022 return null;
1023 }
1024
1025 #endregion
1026
1027 #region Block Handling
1028
1029 void StructureAttack (BaseEntity targetEntity, BaseEntity sourceEntity, string weapon, Vector3 hitPosition)
1030 {
1031 BasePlayer source = null;
1032
1033 if (sourceEntity.ToPlayer () is BasePlayer)
1034 source = sourceEntity.ToPlayer ();
1035 else {
1036 ulong ownerID = sourceEntity.OwnerID;
1037 if (ownerID.IsSteamId ())
1038 source = BasePlayer.FindByID (ownerID);
1039 else
1040 return;
1041 }
1042
1043 if (source == null)
1044 return;
1045
1046 List<string> sourceMembers = null;
1047
1048 if (targetEntity.OwnerID.IsSteamId () || (blockUnowned && !targetEntity.OwnerID.IsSteamId ())) {
1049 if (clanCheck || friendCheck)
1050 sourceMembers = getFriends (source.UserIDString);
1051
1052 if (blockAll) {
1053 BlockAll (source, targetEntity, sourceMembers);
1054 } else {
1055 if (ownerBlock)
1056 OwnerBlock (source, sourceEntity, targetEntity.OwnerID, targetEntity.transform.position, sourceMembers);
1057
1058 if (raiderBlock)
1059 RaiderBlock (source, targetEntity.OwnerID, targetEntity.transform.position, sourceMembers);
1060 }
1061 }
1062 }
1063
1064 float GetHealthPercent (BaseEntity entity, float damage = 0f)
1065 {
1066 if (entity == null)
1067 return 0f;
1068 return (entity.Health () - damage) * 100f / entity.MaxHealth ();
1069 }
1070
1071 void BlockAll (BasePlayer source, BaseEntity targetEntity, List<string> sourceMembers = null)
1072 {
1073 if (ShouldBlockEscape (targetEntity.OwnerID, source.userID, sourceMembers)) {
1074 StartRaidBlocking (source, targetEntity.transform.position);
1075 }
1076
1077 var checkSourceMembers = false;
1078 if (targetEntity.OwnerID == source.userID || (sourceMembers is List<string> && sourceMembers.Contains (targetEntity.OwnerID.ToString ()))) {
1079 checkSourceMembers = true;
1080 }
1081
1082 var nearbyTargets = Pool.GetList<BasePlayer> ();
1083 Vis.Entities (targetEntity.transform.position, raidDistance, nearbyTargets, blockLayer);
1084 if (nearbyTargets.Count > 0) {
1085 RaidBlock blocker;
1086 foreach (BasePlayer nearbyTarget in nearbyTargets) {
1087 if (nearbyTarget.IsNpc) continue;
1088 if (nearbyTarget.userID == source.userID) continue;
1089 if (TryGetBlocker (nearbyTarget, out blocker) && blocker.Active) {
1090 StartRaidBlocking (nearbyTarget, targetEntity.transform.position);
1091 } else if (ShouldBlockEscape (nearbyTarget.userID, source.userID, checkSourceMembers ? sourceMembers : null)) {
1092 StartRaidBlocking (nearbyTarget, targetEntity.transform.position);
1093 }
1094 }
1095 }
1096
1097 Pool.FreeList (ref nearbyTargets);
1098 }
1099
1100 void OwnerBlock (BasePlayer source, BaseEntity sourceEntity, ulong target, Vector3 position, List<string> sourceMembers = null)
1101 {
1102 if (!ShouldBlockEscape (target, source.userID, sourceMembers))
1103 return;
1104
1105 var targetMembers = new List<string> ();
1106
1107 if (clanShare || friendShare)
1108 targetMembers = getFriends (target.ToString ());
1109
1110 var nearbyTargets = Pool.GetList<BasePlayer> ();
1111 Vis.Entities (position, raidDistance, nearbyTargets, blockLayer);
1112 if (cupboardShare)
1113 sourceMembers = CupboardShare (target.ToString (), position, sourceEntity, sourceMembers);
1114
1115 if (nearbyTargets.Count > 0) {
1116 foreach (BasePlayer nearbyTarget in nearbyTargets) {
1117 if (nearbyTarget.IsNpc) continue;
1118 if (nearbyTarget.userID == target || (targetMembers != null && targetMembers.Contains (nearbyTarget.UserIDString)))
1119 StartRaidBlocking (nearbyTarget, position);
1120 }
1121 }
1122
1123 Pool.FreeList (ref nearbyTargets);
1124 }
1125
1126 List<string> CupboardShare (string owner, Vector3 position, BaseEntity sourceEntity, List<string> sourceMembers = null)
1127 {
1128 var nearbyCupboards = Pool.GetList<BuildingPrivlidge> ();
1129 Vis.Entities (position, raidDistance, nearbyCupboards, cupboardMask);
1130 if (sourceMembers == null)
1131 sourceMembers = new List<string> ();
1132
1133 List<string> cupboardMembers = new List<string> ();
1134
1135 var sourcePlayer = sourceEntity as BasePlayer;
1136
1137 if (sourcePlayer != null) {
1138 foreach (var cup in nearbyCupboards) {
1139 if (cup.IsAuthed (sourcePlayer)) {
1140 bool ownerOrFriend = false;
1141
1142 if (owner == cup.OwnerID.ToString ())
1143 ownerOrFriend = true;
1144
1145 foreach (var member in sourceMembers) {
1146 if (member == cup.OwnerID.ToString ())
1147 ownerOrFriend = true;
1148 }
1149
1150 if (ownerOrFriend)
1151 foreach (var proto in cup.authorizedPlayers)
1152 if (!sourceMembers.Contains (proto.userid.ToString ()))
1153 cupboardMembers.Add (proto.userid.ToString ());
1154 }
1155 }
1156 }
1157
1158 sourceMembers.AddRange (cupboardMembers);
1159 Pool.FreeList (ref nearbyCupboards);
1160
1161 return sourceMembers;
1162 }
1163
1164 void RaiderBlock (BasePlayer source, ulong target, Vector3 position, List<string> sourceMembers = null)
1165 {
1166 if (!ShouldBlockEscape (target, source.userID, sourceMembers))
1167 return;
1168
1169 var targetMembers = new List<string> ();
1170
1171 if ((clanShare || friendShare) && sourceMembers == null)
1172 sourceMembers = getFriends (source.UserIDString);
1173
1174 var nearbyTargets = Pool.GetList<BasePlayer> ();
1175 Vis.Entities (position, raidDistance, nearbyTargets, blockLayer);
1176 if (nearbyTargets.Count > 0) {
1177 foreach (BasePlayer nearbyTarget in nearbyTargets) {
1178 if (nearbyTarget.IsNpc) continue;
1179 if (nearbyTarget == source || (sourceMembers != null && sourceMembers.Contains (nearbyTarget.UserIDString)))
1180 StartRaidBlocking (nearbyTarget, position);
1181 }
1182 }
1183
1184 Pool.FreeList (ref nearbyTargets);
1185 }
1186
1187 #endregion
1188
1189 #region API
1190
1191 bool IsBlocked (string target)
1192 {
1193 var player = BasePlayer.Find (target);
1194 if (player is BasePlayer) {
1195 return IsBlocked (player);
1196 }
1197
1198 return false;
1199 }
1200
1201 bool IsBlocked (BasePlayer target)
1202 {
1203 if (IsBlocked<RaidBlock> (target) || IsBlocked<CombatBlock> (target))
1204 return true;
1205
1206 return false;
1207 }
1208
1209 public bool IsBlocked<T> (BasePlayer target) where T : BlockBehavior
1210 {
1211 T behavior;
1212 if (TryGetBlocker<T> (target, out behavior) && behavior.Active)
1213 return true;
1214
1215 return false;
1216 }
1217
1218 bool IsRaidBlocked (BasePlayer target)
1219 {
1220 return IsBlocked<RaidBlock> (target);
1221 }
1222
1223 bool IsCombatBlocked (BasePlayer target)
1224 {
1225 return IsBlocked<CombatBlock> (target);
1226 }
1227
1228 bool IsEscapeBlocked (string target)
1229 {
1230 var player = BasePlayer.Find (target);
1231 if (player is BasePlayer) {
1232 return IsBlocked (player);
1233 }
1234
1235 return false;
1236 }
1237
1238 bool IsRaidBlocked (string target)
1239 {
1240 var player = BasePlayer.Find (target);
1241 if (player is BasePlayer) {
1242 return IsBlocked<RaidBlock> (player);
1243 }
1244
1245 return false;
1246 }
1247
1248 bool IsCombatBlocked (string target)
1249 {
1250 var player = BasePlayer.Find (target);
1251 if (player is BasePlayer) {
1252 return IsBlocked<CombatBlock> (player);
1253 }
1254
1255 return false;
1256 }
1257
1258 bool ShouldBlockEscape (ulong target, ulong source, List<string> sourceMembers = null)
1259 {
1260 if (target == source) {
1261 if ((ownerBlock || raiderBlock || blockAll) && (!ownerCheck))
1262 return true;
1263
1264 return false;
1265 }
1266
1267 if (sourceMembers is List<string> && sourceMembers.Contains (target.ToString ()))
1268 return false;
1269
1270 return true;
1271 }
1272
1273 //[ChatCommand ("bblocked")]
1274 //void cmdBBlocked (BasePlayer player, string command, string [] args)
1275 //{
1276 // StartCombatBlocking (player);
1277 // StartRaidBlocking (player);
1278 //}
1279
1280 //[ChatCommand ("bunblocked")]
1281 //void cmdBUnblocked (BasePlayer player, string command, string [] args)
1282 //{
1283 // StopCombatBlocking (player);
1284 // StopRaidBlocking (player);
1285 //}
1286
1287 void StartRaidBlocking (BasePlayer target, bool createZone = true)
1288 {
1289 StartRaidBlocking (target, target.transform.position, createZone);
1290 }
1291
1292 void StartRaidBlocking (BasePlayer target, Vector3 position, bool createZone = true)
1293 {
1294 if (HasPerm (target.UserIDString, "disable")) {
1295 return;
1296 }
1297
1298 if (target.gameObject == null) {
1299 return;
1300 }
1301
1302 if (Interface.Call ("CanRaidBlock", target, position, createZone) != null) {
1303 return;
1304 }
1305
1306 if (target.gameObject == null)
1307 return;
1308
1309 foreach (var comp in target.gameObject.GetComponents<Component>().ToList())
1310 if (!(comp is CombatBlock) && comp.ToString().Contains("NoEscape"))
1311 UnityEngine.Object.Destroy(comp);
1312
1313 var raidBlocker = target.gameObject.GetComponent<RaidBlock> ();
1314 if (raidBlocker == null) {
1315 raidBlocker = target.gameObject.AddComponent<RaidBlock> ();
1316 }
1317
1318 Interface.CallHook ("OnRaidBlock", target, position);
1319
1320 raidBlocker.lastBlock = DateTime.Now;
1321
1322 if (raidBlockNotify)
1323 SendBlockMessage (target, raidBlocker, "Raid Block Notifier", "Raid Block Complete");
1324
1325 if (useZoneManager && createZone && (zoneEnter || zoneLeave))
1326 CreateRaidZone (position);
1327 }
1328
1329 void StartCombatBlocking (BasePlayer target)
1330 {
1331 if (HasPerm (target.UserIDString, "disable")) {
1332 return;
1333 }
1334 if (target.gameObject == null) {
1335 return;
1336 }
1337
1338 if (Interface.Call ("CanCombatBlock", target) != null) {
1339 return;
1340 }
1341
1342 foreach (var comp in target.gameObject.GetComponents<Component>().ToList())
1343 if (!(comp is CombatBlock) && comp.ToString().Contains("NoEscape"))
1344 UnityEngine.Object.Destroy(comp);
1345
1346 var combatBlocker = target.gameObject.GetComponent<CombatBlock> ();
1347 if (combatBlocker == null) {
1348 combatBlocker = target.gameObject.AddComponent<CombatBlock> ();
1349 }
1350 Interface.CallHook ("OnCombatBlock", target);
1351 combatBlocker.lastBlock = DateTime.Now;
1352
1353 if (combatBlockNotify)
1354 SendBlockMessage(target, combatBlocker, "Combat Block Notifier", "Combat Block Complete");
1355 }
1356
1357 void StopBlocking (BasePlayer target)
1358 {
1359 if (IsRaidBlocked (target))
1360 StopBlocking<RaidBlock> (target);
1361 if (IsCombatBlocked (target))
1362 StopBlocking<CombatBlock> (target);
1363 }
1364
1365 public void StopBlocking<T> (BasePlayer target) where T : BlockBehavior
1366 {
1367 if (target.gameObject == null)
1368 return;
1369 var block = target.gameObject.GetComponent<T> ();
1370 if (block is BlockBehavior)
1371 block.Stop ();
1372
1373 if (block is RaidBlock) {
1374 Interface.CallHook ("OnRaidBlockStopped", target);
1375 } else if (block is CombatBlock) {
1376 Interface.CallHook ("OnCombatBlockStopped", target);
1377 }
1378 }
1379
1380 void ClearRaidBlockingS (string target)
1381 {
1382 StopRaidBlocking (target);
1383 }
1384
1385 void StopRaidBlocking (BasePlayer player)
1386 {
1387 if (player is BasePlayer && IsRaidBlocked (player))
1388 StopBlocking<RaidBlock> (player);
1389 }
1390
1391 void StopRaidBlocking (string target)
1392 {
1393 var player = BasePlayer.Find (target);
1394 StopRaidBlocking (player);
1395 }
1396
1397 void StopCombatBlocking (BasePlayer player)
1398 {
1399 if (player is BasePlayer && IsRaidBlocked (player))
1400 StopBlocking<CombatBlock> (player);
1401 }
1402
1403 void StopCombatBlocking (string target)
1404 {
1405 var player = BasePlayer.Find (target);
1406 StopCombatBlocking (player);
1407 }
1408
1409 void ClearCombatBlocking (string target)
1410 {
1411 StopCombatBlocking (target);
1412 }
1413
1414 #endregion
1415
1416 #region Zone Handling
1417
1418 void EraseZone (string zoneid)
1419 {
1420 ZoneManager.CallHook ("EraseZone", zoneid);
1421 zones.Remove (zoneid);
1422 }
1423
1424 void ResetZoneTimer (RaidZone zone)
1425 {
1426 zone.ResetTimer ().timer = timer.In (raidDuration, delegate () {
1427 EraseZone (zone.zoneid);
1428 });
1429 }
1430
1431 void CreateRaidZone (Vector3 position)
1432 {
1433 var zoneid = position.ToString ();
1434
1435 RaidZone zone;
1436 if (zones.TryGetValue (zoneid, out zone)) {
1437 ResetZoneTimer (zone);
1438 return;
1439 }
1440
1441 foreach (var nearbyZone in zones) {
1442 if (nearbyZone.Value.Distance (position) < (raidDistance / 2)) {
1443 ResetZoneTimer (nearbyZone.Value);
1444 return;
1445 }
1446 }
1447
1448 ZoneManager.CallHook ("CreateOrUpdateZone", zoneid, new string []
1449 {
1450 "radius",
1451 raidDistance.ToString()
1452 }, position);
1453
1454 zones.Add (zoneid, zone = new RaidZone (zoneid, position));
1455
1456 ResetZoneTimer (zone);
1457 }
1458
1459 [HookMethod ("OnEnterZone")]
1460 void OnEnterZone (string zoneid, BasePlayer player)
1461 {
1462 if (!zoneEnter)
1463 return;
1464 if (!zones.ContainsKey (zoneid))
1465 return;
1466
1467 StartRaidBlocking (player, player.transform.position, false);
1468 }
1469
1470 [HookMethod ("OnExitZone")]
1471 void OnExitZone (string zoneid, BasePlayer player)
1472 {
1473 if (!zoneLeave)
1474 return;
1475 if (!zones.ContainsKey (zoneid))
1476 return;
1477
1478 if (IsRaidBlocked (player)) {
1479 StopBlocking<RaidBlock> (player);
1480 }
1481 }
1482
1483 [HookMethod ("OnPlayerEnteredRaidableBase")]
1484 void OnPlayerEnteredRaidableBase(BasePlayer player, Vector3 raidPos, bool allowPVP)
1485 {
1486 if (!raidableZoneEnter)
1487 return;
1488
1489 StartRaidBlocking (player, false);
1490 }
1491
1492 [HookMethod ("OnPlayerExitedRaidableBase")]
1493 void OnPlayerExitedRaidableBase(BasePlayer player, Vector3 raidPos, bool allowPVP)
1494 {
1495 if (!raidableZoneLeave)
1496 return;
1497
1498 if (IsRaidBlocked (player)) {
1499 StopBlocking<RaidBlock> (player);
1500 }
1501 }
1502
1503 #endregion
1504
1505 #region Friend/Clan Integration
1506
1507 public List<string> getFriends (string player)
1508 {
1509 var players = new List<string> ();
1510 if (player == null)
1511 return players;
1512
1513 if (friendShare || friendCheck) {
1514 var friendList = getFriendList (player);
1515 if (friendList != null)
1516 players.AddRange (friendList);
1517 }
1518
1519 if (clanShare || clanCheck) {
1520 var members = getClanMembers (player);
1521 if (members != null)
1522 players.AddRange (members);
1523 }
1524 return players;
1525 }
1526
1527 public List<string> getFriendList (string player)
1528 {
1529 object friends_obj = null;
1530 DateTime lastFriendCheckPlayer;
1531 var players = new List<string> ();
1532
1533 if (lastFriendCheck.TryGetValue (player, out lastFriendCheckPlayer)) {
1534 if ((DateTime.Now - lastFriendCheckPlayer).TotalMinutes <= cacheTimer && friendCache.TryGetValue (player, out players)) {
1535 return players;
1536 } else {
1537 friends_obj = Friends?.CallHook ("IsFriendOfS", player);
1538 lastFriendCheck [player] = DateTime.Now;
1539 }
1540 } else {
1541 friends_obj = Friends?.CallHook ("IsFriendOfS", player);
1542 lastFriendCheck.Add (player, DateTime.Now);
1543 }
1544
1545 if (friends_obj == null)
1546 return players;
1547
1548 string [] friends = friends_obj as string [];
1549
1550 foreach (string fid in friends)
1551 players.Add (fid);
1552
1553 if (friendCache.ContainsKey (player))
1554 friendCache [player] = players;
1555 else
1556 friendCache.Add (player, players);
1557
1558 return players;
1559 }
1560
1561 public List<string> getClanMembers (string player)
1562 {
1563 string tag = null;
1564 DateTime lastClanCheckPlayer;
1565 string lastClanCached;
1566 if (lastClanCheck.TryGetValue (player, out lastClanCheckPlayer) && clanCache.TryGetValue (player, out lastClanCached)) {
1567 if ((DateTime.Now - lastClanCheckPlayer).TotalMinutes <= cacheTimer)
1568 tag = lastClanCached;
1569 else {
1570 tag = Clans.Call<string> ("GetClanOf", player);
1571 clanCache [player] = tag;
1572 lastClanCheck [player] = DateTime.Now;
1573 }
1574 } else {
1575 tag = Clans.Call<string> ("GetClanOf", player);
1576 if (lastClanCheck.ContainsKey (player))
1577 lastClanCheck.Remove (player);
1578
1579 if (clanCache.ContainsKey (player))
1580 clanCache.Remove (player);
1581
1582 clanCache.Add (player, tag);
1583 lastClanCheck.Add (player, DateTime.Now);
1584 }
1585
1586 if (tag == null)
1587 return null;
1588
1589 List<string> lastMemberCache;
1590 if (memberCache.TryGetValue (tag, out lastMemberCache))
1591 return lastMemberCache;
1592
1593 var clan = GetClan (tag);
1594
1595 if (clan == null)
1596 return null;
1597
1598 return CacheClan (clan);
1599 }
1600
1601 JObject GetClan (string tag)
1602 {
1603 if (string.IsNullOrEmpty (tag)) {
1604 return null;
1605 }
1606 return Clans.Call<JObject> ("GetClan", tag);
1607 }
1608
1609 List<string> CacheClan (JObject clan)
1610 {
1611 string tag = clan ["tag"].ToString ();
1612 List<string> players = new List<string> ();
1613 foreach (string memberid in clan ["members"]) {
1614 if (clanCache.ContainsKey (memberid))
1615 clanCache [memberid] = tag;
1616 else
1617 clanCache.Add (memberid, tag);
1618
1619 players.Add (memberid);
1620 }
1621
1622 if (memberCache.ContainsKey (tag))
1623 memberCache [tag] = players;
1624 else
1625 memberCache.Add (tag, players);
1626
1627 if (lastCheck.ContainsKey (tag))
1628 lastCheck [tag] = DateTime.Now;
1629 else
1630 lastCheck.Add (tag, DateTime.Now);
1631
1632 return players;
1633 }
1634
1635 [HookMethod ("OnClanCreate")]
1636 void OnClanCreate (string tag)
1637 {
1638 var clan = GetClan (tag);
1639 if (clan != null) {
1640 CacheClan (clan);
1641 } else {
1642 PrintWarning ("Unable to find clan after creation: " + tag);
1643 }
1644 }
1645
1646 [HookMethod ("OnClanUpdate")]
1647 void OnClanUpdate (string tag)
1648 {
1649 var clan = GetClan (tag);
1650 if (clan != null) {
1651 CacheClan (clan);
1652 } else {
1653 PrintWarning ("Unable to find clan after update: " + tag);
1654 }
1655 }
1656
1657 [HookMethod ("OnClanDestroy")]
1658 void OnClanDestroy (string tag)
1659 {
1660 if (lastCheck.ContainsKey (tag)) {
1661 lastCheck.Remove (tag);
1662 }
1663
1664 if (memberCache.ContainsKey (tag)) {
1665 memberCache.Remove (tag);
1666 }
1667 }
1668
1669 #endregion
1670
1671 #region Permission Checking & External API Handling
1672
1673 bool HasPerm (string userid, string perm)
1674 {
1675 return permission.UserHasPermission (userid, "noescape." + perm);
1676 }
1677
1678 bool CanRaidCommand (BasePlayer player, string command)
1679 {
1680 return raidBlock && HasPerm (player.UserIDString, "raid." + command + "block") && IsRaidBlocked (player);
1681 }
1682
1683 bool CanRaidCommand (string playerID, string command)
1684 {
1685 return raidBlock && HasPerm (playerID, "raid." + command + "block") && IsRaidBlocked (playerID);
1686 }
1687
1688 bool CanCombatCommand (BasePlayer player, string command)
1689 {
1690 return combatBlock && HasPerm (player.UserIDString, "combat." + command + "block") && IsCombatBlocked (player);
1691 }
1692
1693 bool CanCombatCommand (string playerID, string command)
1694 {
1695 return combatBlock && HasPerm (playerID, "combat." + command + "block") && IsCombatBlocked (playerID);
1696 }
1697
1698 object CanDo (string command, BasePlayer player)
1699 {
1700 if (CanRaidCommand (player, command))
1701 return GetMessage<RaidBlock> (player, "Raid Blocked Message", raidDuration);
1702 else if (CanCombatCommand (player, command))
1703 return GetMessage<CombatBlock> (player, "Combat Blocked Message", combatDuration);
1704
1705 return null;
1706 }
1707
1708 object OnStructureRepair (BaseCombatEntity entity, BasePlayer player)
1709 {
1710 var result = CanDo ("repair", player);
1711 if (result is string) {
1712 if (entity.health > entity.MaxHealth ()) {
1713 return null;
1714 }
1715 SendReply (player, result.ToString ());
1716 return true;
1717 }
1718
1719 return null;
1720 }
1721
1722 object OnStructureUpgrade (BuildingBlock block, BasePlayer player, BuildingGrade.Enum grade)
1723 {
1724 var result = CanDo ("upgrade", player);
1725 if (result is string) {
1726 SendReply (player, result.ToString ());
1727 return true;
1728 }
1729
1730 return null;
1731 }
1732
1733 object canRedeemKit (BasePlayer player)
1734 {
1735 return CanDo ("kit", player);
1736 }
1737
1738 object CanUseMailbox (BasePlayer player, Mailbox mailbox)
1739 {
1740 var result = CanDo ("mailbox", player);
1741 if (result is string) {
1742 SendReply (player, result.ToString ());
1743 return true;
1744 }
1745
1746 return null;
1747 }
1748
1749 object CanUseVending (VendingMachine machine, BasePlayer player)
1750 {
1751 var result = CanDo ("vend", player);
1752 if (result is string) {
1753 SendReply (player, result.ToString ());
1754 return true;
1755 }
1756
1757 return null;
1758 }
1759
1760 object CanBuild (Planner plan, Construction prefab)
1761 {
1762 var player = plan.GetOwnerPlayer ();
1763 var result = CanDo ("build", player);
1764 if (result is string) {
1765 if (isEntityException (prefab.fullName)) {
1766 return null;
1767 }
1768
1769 SendReply (player, result.ToString ());
1770 return true;
1771 }
1772
1773 return null;
1774 }
1775
1776 object CanAssignBed (SleepingBag bag, BasePlayer player, ulong targetPlayerId)
1777 {
1778 var result = CanDo ("assignbed", player);
1779 if (result is string) {
1780 SendReply (player, result.ToString ());
1781 return true;
1782 }
1783
1784 return null;
1785 }
1786
1787 object CanOpenBackpack(BasePlayer player, ulong backpackOwnerID)
1788 {
1789 return CanDo ("backpack", player);
1790 }
1791
1792 object CanBank (BasePlayer player)
1793 {
1794 return CanDo ("bank", player);
1795 }
1796
1797 object CanTrade (BasePlayer player)
1798 {
1799 return CanDo ("trade", player);
1800 }
1801
1802 object canRemove (BasePlayer player)
1803 {
1804 return CanDo ("remove", player);
1805 }
1806
1807 object canShop (BasePlayer player)
1808 {
1809 return CanDo ("shop", player);
1810 }
1811
1812 object CanShop (BasePlayer player)
1813 {
1814 return CanDo ("shop", player);
1815 }
1816
1817 object CanTeleport (BasePlayer player)
1818 {
1819 return CanDo ("tp", player);
1820 }
1821
1822 object canTeleport (BasePlayer player) // ALIAS FOR MagicTeleportation
1823 {
1824 return CanTeleport (player);
1825 }
1826
1827 object CanGridTeleport (BasePlayer player) // ALIAS FOR GrTeleport
1828 {
1829 return CanTeleport (player);
1830 }
1831
1832 object CanCraft (ItemCrafter itemCrafter, ItemBlueprint bp, int amount)
1833 {
1834 BasePlayer player = itemCrafter.containers [0].GetOwnerPlayer ();
1835
1836 if (player != null) {
1837 var result = CanDo ("craft", player);
1838 if (result is string) {
1839 SendReply (player, result.ToString ());
1840 return false;
1841 }
1842 }
1843
1844 return null;
1845 }
1846
1847 object CanRecycleCommand (BasePlayer player)
1848 {
1849 return CanDo ("recycle", player);
1850 }
1851
1852 object CanBGrade (BasePlayer player, int grade, BuildingBlock buildingBlock, Planner planner)
1853 {
1854 if (CanRaidCommand (player, "bgrade") || CanCombatCommand (player, "bgrade"))
1855 return -1;
1856 return null;
1857 }
1858
1859 #endregion
1860
1861 #region Messages
1862
1863 void SendBlockMessage (BasePlayer target, BlockBehavior blocker, string langMessage, string completeMessage)
1864 {
1865 var send = false;
1866 if (blocker.lastNotification != DateTime.MinValue) {
1867 TimeSpan diff = DateTime.Now - blocker.lastNotification;
1868 if (diff.TotalSeconds >= (blocker.Duration / 2))
1869 send = true;
1870 } else
1871 send = true;
1872
1873 if (send) {
1874 string message = string.Empty;
1875
1876 if (sendChatNotification || sendGUIAnnouncementsNotification)
1877 message = GetPrefix (target.UserIDString) + GetMsg (langMessage, target.UserIDString).Replace ("{time}", GetCooldownTime (blocker.Duration, target.UserIDString));
1878
1879 if (sendChatNotification)
1880 SendReply (target, message);
1881
1882 if (sendGUIAnnouncementsNotification)
1883 GUIAnnouncements?.Call ("CreateAnnouncement", message, GUIAnnouncementTintColor, GUIAnnouncementTextColor, target);
1884
1885 if (sendLustyMapNotification && LustyMapDuration > 0) {
1886 blocker.iconUID = Guid.NewGuid ().ToString ("N");
1887 var obj = LustyMap?.Call ("AddMarker", target.transform.position.x, target.transform.position.z, blocker.iconUID, LustyMapIcon);
1888 if (obj is bool && (bool)obj == true) {
1889 timer.In (LustyMapDuration, delegate () {
1890 LustyMap?.Call ("RemoveMarker", blocker.iconUID);
1891 });
1892 }
1893 }
1894
1895 blocker.lastNotification = DateTime.Now;
1896 }
1897
1898 blocker.Notify (delegate () {
1899 blocker.notifyCallback = null;
1900 if (target?.IsConnected == true) {
1901 string message = string.Empty;
1902
1903 if (sendChatNotification || sendGUIAnnouncementsNotification)
1904 message = GetPrefix (target.UserIDString) + GetMsg (completeMessage, target.UserIDString);
1905
1906 if (sendChatNotification)
1907 SendReply (target, message);
1908
1909 if (sendGUIAnnouncementsNotification)
1910 GUIAnnouncements?.Call ("CreateAnnouncement", message, GUIAnnouncementTintColor, GUIAnnouncementTextColor, target);
1911
1912 if (sendLustyMapNotification && LustyMapDuration > 0)
1913 LustyMap?.Call ("RemoveMarker", blocker.iconUID);
1914 }
1915 });
1916 }
1917
1918 string GetCooldownTime (float f, string userID)
1919 {
1920 if (f > 60)
1921 return Math.Round (f / 60, 1) + " " + GetMsg ("Unit Minutes", userID);
1922
1923 return f + " " + GetMsg ("Unit Seconds", userID);
1924 }
1925
1926 public string GetMessage (BasePlayer player)
1927 {
1928 if (IsRaidBlocked (player))
1929 return GetMessage<RaidBlock> (player, "Raid Blocked Message", raidDuration);
1930 else if (IsCombatBlocked (player))
1931 return GetMessage<CombatBlock> (player, "Combat Blocked Message", combatDuration);
1932
1933 return null;
1934 }
1935
1936 public string GetPrefix (string player)
1937 {
1938 string prefix = GetMsg ("Prefix", player);
1939 if (!string.IsNullOrEmpty (prefix)) {
1940 return prefix + ": ";
1941 }
1942
1943 return string.Empty;
1944 }
1945
1946 public string GetMessage<T> (BasePlayer player, string blockMsg, float duration) where T : BlockBehavior
1947 {
1948 T blocker;
1949 if (duration > 0 && TryGetBlocker<T> (player, out blocker)) {
1950 var ts = DateTime.Now - blocker.lastBlock;
1951 var unblocked = Math.Round ((duration / 60) - Convert.ToSingle (ts.TotalMinutes), 2);
1952
1953 if (ts.TotalMinutes <= duration) {
1954 if (unblocked < 1) {
1955 var timelefts = Math.Round (Convert.ToDouble (duration) - ts.TotalSeconds);
1956 return GetPrefix (player.UserIDString) + GetMsg (blockMsg, player).Replace ("{time}", timelefts.ToString () + " " + GetMsg ("Unit Seconds", player));
1957 }
1958
1959 return GetPrefix (player.UserIDString) + GetMsg (blockMsg, player).Replace ("{time}", unblocked.ToString () + " " + GetMsg ("Unit Minutes", player));
1960 }
1961 }
1962
1963 return null;
1964 }
1965
1966 #endregion
1967
1968 #region Utility Methods
1969
1970 bool TryGetBlocker<T> (BasePlayer player, out T blocker) where T : BlockBehavior
1971 {
1972 blocker = null;
1973 if (player.gameObject == null)
1974 return false;
1975 if ((blocker = player.gameObject.GetComponent<T> ()) != null)
1976 return true;
1977
1978 return false;
1979 }
1980
1981 public bool isEntityException (string prefabName)
1982 {
1983 var result = false;
1984
1985 foreach (string p in exceptionPrefabs) {
1986 if (prefabName.IndexOf (p) != -1) {
1987 result = true;
1988 break;
1989 }
1990 }
1991
1992 return result;
1993 }
1994
1995 public bool IsEntityBlocked (BaseCombatEntity entity)
1996 {
1997 if (entity is BuildingBlock) {
1998 if (((BuildingBlock)entity).grade == BuildingGrade.Enum.Twigs)
1999 return false;
2000
2001 return true;
2002 }
2003
2004 var prefabName = entity.ShortPrefabName;
2005 var result = false;
2006 if (prefabBlockCache.TryGetValue (prefabName, out result))
2007 return result;
2008
2009 result = false;
2010
2011 foreach (string p in blockedPrefabs) {
2012 if (prefabName.IndexOf (p) != -1) {
2013 result = true;
2014 break;
2015 }
2016 }
2017
2018
2019 prefabBlockCache.Add (prefabName, result);
2020 return result;
2021 }
2022
2023 bool IsRaidDamage (DamageType dt)
2024 {
2025 return raidDamageTypes.Contains (dt.ToString ());
2026 }
2027
2028 bool IsDeathDamage (DamageType dt)
2029 {
2030 return raidDeathTypes.Contains (dt.ToString ());
2031 }
2032
2033 bool IsRaidDamage (DamageTypeList dtList)
2034 {
2035 for (int index = 0; index < dtList.types.Length; ++index) {
2036 if (dtList.types [index] > 0 && IsRaidDamage ((DamageType)index)) {
2037 return true;
2038 }
2039 }
2040
2041 return false;
2042 }
2043
2044 bool IsDeathDamage (DamageTypeList dtList)
2045 {
2046 for (int index = 0; index < dtList.types.Length; ++index) {
2047 if (dtList.types [index] > 0 && IsDeathDamage ((DamageType)index)) {
2048 return true;
2049 }
2050 }
2051
2052 return false;
2053 }
2054
2055 bool IsExcludedWeapon (string name)
2056 {
2057 if (string.IsNullOrEmpty (name)) {
2058 return false;
2059 }
2060
2061 bool cachedValue;
2062
2063 if (_cachedExcludedWeapons.TryGetValue (name, out cachedValue)) {
2064 return cachedValue;
2065 }
2066
2067 foreach (var weaponName in exceptionWeapons) {
2068 if (name.Contains (weaponName)) {
2069 _cachedExcludedWeapons.Add (name, true);
2070 return true;
2071 }
2072 }
2073
2074 _cachedExcludedWeapons.Add (name, false);
2075 return false;
2076 }
2077
2078 bool IsCombatDamage (DamageType dt)
2079 {
2080 return combatDamageTypes.Contains (dt.ToString ());
2081 }
2082
2083 bool IsCombatDamage (DamageTypeList dtList)
2084 {
2085 for (int index = 0; index < dtList.types.Length; ++index) {
2086 if (IsCombatDamage ((DamageType)index)) {
2087 return true;
2088 }
2089 }
2090
2091 return false;
2092 }
2093
2094 T GetConfig<T> (string name, string name2, string name3, T defaultValue)
2095 {
2096 try {
2097 var val = Config [name, name2, name3];
2098
2099 return ParseValue<T> (val, defaultValue);
2100 } catch (Exception ex) {
2101 //PrintWarning ("Invalid config value: " + name + "/" + name2 + "/" + name3 + " (" + ex.Message + ")");
2102 Config [name, name2, name3] = defaultValue;
2103 Config.Save ();
2104 return defaultValue;
2105 }
2106 }
2107
2108 T GetConfig<T> (string name, string name2, T defaultValue)
2109 {
2110 try {
2111 var val = Config [name, name2];
2112
2113 return ParseValue<T> (val, defaultValue);
2114 } catch (Exception ex) {
2115 //PrintWarning ("Invalid config value: " + name + "/" + name2 + " (" + ex.Message + ")");
2116 Config [name, name2] = defaultValue;
2117 Config.Save ();
2118 return defaultValue;
2119 }
2120 }
2121
2122 T GetConfig<T> (string name, T defaultValue)
2123 {
2124 try {
2125 var val = Config [name];
2126
2127 return ParseValue<T> (val, defaultValue);
2128 } catch (Exception ex) {
2129 //PrintWarning ("Invalid config value: " + name + " (" + ex.Message + ")");
2130 Config [name] = defaultValue;
2131 Config.Save ();
2132 return defaultValue;
2133 }
2134 }
2135
2136 T ParseValue<T> (object val, T defaultValue)
2137 {
2138 if (val == null)
2139 return defaultValue;
2140
2141 if (val is List<object>) {
2142 var t = typeof (T).GetGenericArguments () [0];
2143 if (t == typeof (String)) {
2144 var cval = new List<string> ();
2145 foreach (var v in val as List<object>)
2146 cval.Add ((string)v);
2147 val = cval;
2148 } else if (t == typeof (int)) {
2149 var cval = new List<int> ();
2150 foreach (var v in val as List<object>)
2151 cval.Add (Convert.ToInt32 (v));
2152 val = cval;
2153 }
2154 } else if (val is Dictionary<string, object>) {
2155 var t = typeof (T).GetGenericArguments () [1];
2156 if (t == typeof (int)) {
2157 var cval = new Dictionary<string, int> ();
2158 foreach (var v in val as Dictionary<string, object>)
2159 cval.Add (Convert.ToString (v.Key), Convert.ToInt32 (v.Value));
2160 val = cval;
2161 }
2162 }
2163
2164 return (T)Convert.ChangeType (val, typeof (T));
2165 }
2166
2167 static string GetMsg (string key, object user = null)
2168 {
2169 if (user is BasePlayer) {
2170 user = ((BasePlayer)user).UserIDString;
2171 }
2172 return plugin.lang.GetMessage (key, plugin, user == null ? null : user.ToString ());
2173 }
2174
2175 #endregion
2176 }
2177}