· 4 years ago · Sep 02, 2021, 08:48 PM
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.34")]
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 (!blockOnDamage)
504 Unsubscribe ("OnEntityTakeDamage");
505
506 if (!blockTypes.Contains ("repair"))
507 Unsubscribe ("OnStructureRepair");
508
509 if (!blockTypes.Contains ("upgrade"))
510 Unsubscribe ("OnStructureUpgrade");
511
512 if (!blockTypes.Contains ("mailbox"))
513 Unsubscribe ("CanUseMailbox");
514
515 if (!blockTypes.Contains ("vend"))
516 Unsubscribe ("CanUseVending");
517
518 if (!blockTypes.Contains ("build"))
519 Unsubscribe ("CanBuild");
520
521 if (!blockTypes.Contains ("assignbed"))
522 Unsubscribe ("CanAssignBed");
523
524 if (!blockTypes.Contains ("craft"))
525 Unsubscribe ("CanCraft");
526
527 if (!blockTypes.Contains ("backpack"))
528 Unsubscribe ("CanOpenBackpack");
529
530 if(!useZoneManager)
531 {
532 Unsubscribe ("OnEnterZone");
533 Unsubscribe ("OnExitZone");
534 }
535
536 if(!useRaidableBases)
537 {
538 Unsubscribe ("OnPlayerEnteredRaidableBase");
539 Unsubscribe ("OnPlayerExitedRaidableBase");
540 }
541 }
542
543 #endregion
544
545 #region Classes
546
547 public class RaidZone
548 {
549 public string zoneid;
550 public Vector3 position;
551 public Timer timer;
552
553 public RaidZone (string zoneid, Vector3 position)
554 {
555 this.zoneid = zoneid;
556 this.position = position;
557 }
558
559 public float Distance (RaidZone zone)
560 {
561 return Vector3.Distance (position, zone.position);
562 }
563
564 public float Distance (Vector3 pos)
565 {
566 return Vector3.Distance (position, pos);
567 }
568
569 public RaidZone ResetTimer ()
570 {
571 if (timer is Timer && !timer.Destroyed)
572 timer.Destroy ();
573
574 return this;
575 }
576 }
577
578 public abstract class BlockBehavior : MonoBehaviour
579 {
580 protected BasePlayer player;
581 public DateTime lastBlock = DateTime.MinValue;
582 public DateTime lastNotification = DateTime.MinValue;
583 internal DateTime lastUINotification = DateTime.MinValue;
584 internal Timer timer;
585 internal Action notifyCallback;
586 internal string iconUID;
587 internal bool moved;
588
589 public void CopyFrom (BlockBehavior behavior)
590 {
591 lastBlock = behavior.lastBlock;
592 lastNotification = behavior.lastNotification;
593 lastUINotification = behavior.lastUINotification;
594 timer = behavior.timer;
595 notifyCallback = behavior.notifyCallback;
596 iconUID = behavior.iconUID;
597 NotificationWindow = behavior.NotificationWindow;
598 }
599
600 internal abstract float Duration { get; }
601
602 internal abstract CuiRectTransformComponent NotificationWindow { get; set; }
603
604 internal abstract string notifyMessage { get; }
605
606 internal string BlockName {
607 get {
608 return GetType ().Name;
609 }
610 }
611
612 public bool Active {
613 get {
614 if (lastBlock > DateTime.MinValue) {
615 TimeSpan ts = DateTime.Now - lastBlock;
616 if (ts.TotalSeconds < Duration) {
617 return true;
618 }
619 }
620
621 GameObject.Destroy (this);
622
623 return false;
624 }
625 }
626
627 void Awake ()
628 {
629 player = GetComponent<BasePlayer> ();
630 if (plugin.blockBehaviors.ContainsKey (player.userID)) {
631 plugin.blockBehaviors.Remove (player.userID);
632 }
633 plugin.blockBehaviors.Add (player.userID, this);
634 }
635
636 void Destroy ()
637 {
638 if (!moved) {
639 Stop ();
640 }
641 CancelInvoke ("Update");
642 }
643
644 void Update ()
645 {
646 if (!plugin.sendUINotification)
647 return;
648 bool send = false;
649 if (lastUINotification == DateTime.MinValue) {
650 lastUINotification = DateTime.Now;
651 send = true;
652 } else {
653 TimeSpan ts = DateTime.Now - lastUINotification;
654 if (ts.TotalSeconds > 2) {
655 send = true;
656 } else {
657 send = false;
658 }
659 }
660
661 if (player is BasePlayer && player.IsConnected) {
662 if (!Active) {
663 CuiHelper.DestroyUi (player, "BlockMsg" + BlockName);
664 }
665
666 if (send && Active) {
667 lastUINotification = DateTime.Now;
668 SendGUI ();
669 }
670 }
671 }
672
673 public void Stop ()
674 {
675 if (notifyCallback is Action)
676 notifyCallback.Invoke ();
677
678 if (timer is Timer && !timer.Destroyed)
679 timer.Destroy ();
680
681 if (plugin.sendUINotification && player is BasePlayer && player.IsConnected)
682 CuiHelper.DestroyUi (player, "BlockMsg" + BlockName);
683
684 plugin.blockBehaviors.Remove (player.userID);
685
686 GameObject.Destroy (this);
687 }
688
689 public void Notify (Action callback)
690 {
691 if (plugin.sendUINotification)
692 SendGUI ();
693
694 notifyCallback = callback;
695 if (timer is Timer && !timer.Destroyed)
696 timer.Destroy ();
697
698 timer = plugin.timer.In (Duration, callback);
699 }
700
701 private string FormatTime (TimeSpan ts)
702 {
703 if (ts.Days > 0)
704 return string.Format ("{0}D, {1}H", ts.Days, ts.Hours);
705
706 if (ts.Hours > 0)
707 return string.Format ("{0}H {1}M", ts.Hours, ts.Minutes);
708
709 return string.Format ("{0}M {1}S", ts.Minutes, ts.Seconds);
710 }
711
712 void SendGUI ()
713 {
714 TimeSpan ts = lastBlock.AddSeconds (Duration) - DateTime.Now;
715
716 string countDown = FormatTime (ts);
717 CuiHelper.DestroyUi (player, "BlockMsg" + BlockName);
718 var elements = new CuiElementContainer ();
719 var BlockMsg = elements.Add (new CuiPanel {
720 Image =
721 {
722 Color = "0.95 0 0.02 0.67"
723 },
724 RectTransform =
725 {
726 AnchorMax = NotificationWindow.AnchorMax,
727 AnchorMin = NotificationWindow.AnchorMin
728 }
729 }, "Hud", "BlockMsg" + BlockName);
730 elements.Add (new CuiElement {
731 Parent = BlockMsg,
732 Components =
733 {
734 new CuiRawImageComponent
735 {
736 Sprite = "assets/icons/explosion.png",
737 Color = "0.95 0 0.02 0.67"
738 },
739 new CuiRectTransformComponent
740 {
741 AnchorMin = "0 0",
742 AnchorMax = "0.13 1"
743 }
744 }
745 });
746 elements.Add (new CuiLabel {
747 RectTransform =
748 {
749 AnchorMin = "0.15 0",
750 AnchorMax = "0.82 1"
751 },
752 Text =
753 {
754 Text = notifyMessage,
755 FontSize = 11,
756 Align = TextAnchor.MiddleLeft,
757 }
758 }, BlockMsg);
759 elements.Add (new CuiElement {
760 Name = "TimerPanel",
761 Parent = BlockMsg,
762 Components =
763 {
764 new CuiImageComponent
765 {
766 Color = "0 0 0 0.64",
767 ImageType = UnityEngine.UI.Image.Type.Filled
768 },
769 new CuiRectTransformComponent
770 {
771 AnchorMin = "0.73 0",
772 AnchorMax = "1 1"
773 }
774 }
775 });
776 elements.Add (new CuiLabel {
777 RectTransform =
778 {
779 AnchorMin = "0 0",
780 AnchorMax = "1 1"
781 },
782 Text =
783 {
784 Text = countDown,
785 FontSize = 12,
786 Align = TextAnchor.MiddleCenter,
787 }
788 }, "TimerPanel");
789 CuiHelper.AddUi (player, elements);
790 }
791
792
793 }
794
795 public class CombatBlock : BlockBehavior
796 {
797 internal override float Duration {
798 get {
799 return combatDuration;
800 }
801 }
802
803 internal override string notifyMessage {
804 get { return GetMsg ("Combat Block UI Message", player); }
805 }
806
807 CuiRectTransformComponent _notificationWindow = null;
808
809 internal override CuiRectTransformComponent NotificationWindow {
810 get {
811 if (_notificationWindow != null) {
812 return _notificationWindow;
813 }
814 return _notificationWindow = new CuiRectTransformComponent () {
815 AnchorMin = "0.44 0.15",
816 AnchorMax = "0.56 0.18"
817 };
818 }
819 set {
820 _notificationWindow = value;
821 }
822 }
823 }
824
825 public class RaidBlock : BlockBehavior
826 {
827 internal override float Duration {
828 get {
829 return raidDuration;
830 }
831 }
832
833 internal override string notifyMessage {
834 get { return GetMsg ("Raid Block UI Message", player); }
835 }
836
837 private CuiRectTransformComponent _notificationWindow = null;
838
839 internal override CuiRectTransformComponent NotificationWindow {
840 get {
841 if (_notificationWindow != null) {
842 return _notificationWindow;
843 }
844 return _notificationWindow = new CuiRectTransformComponent () {
845 AnchorMin = "0.87 0.39",
846 AnchorMax = "0.99 0.42"
847 };
848 }
849 set {
850 _notificationWindow = value;
851 }
852 }
853 }
854
855 #endregion
856
857 #region Oxide Hooks
858
859 void OnEntityTakeDamage (BaseCombatEntity entity, HitInfo hitInfo)
860 {
861 if (!blockOnDamage || !raidBlock)
862 return;
863 if (hitInfo == null || hitInfo.Initiator == null || !IsEntityBlocked (entity) || hitInfo.Initiator.transform == null)
864 return;
865 if (!IsRaidDamage (hitInfo.damageTypes))
866 return;
867 if (IsExcludedWeapon (hitInfo?.WeaponPrefab?.ShortPrefabName))
868 return;
869
870 if (GetHealthPercent (entity, hitInfo.damageTypes.Total ()) > blockOnDamageMinCondition) {
871 return;
872 }
873
874 StructureAttack (entity, hitInfo.Initiator, hitInfo?.WeaponPrefab?.ShortPrefabName, hitInfo.HitPositionWorld);
875 }
876
877 void OnPlayerConnected (BasePlayer player)
878 {
879 BlockBehavior behavior;
880 if (blockBehaviors.TryGetValue (player.userID, out behavior)) {
881 if (behavior is RaidBlock) {
882 var raidBlockComponent = player.gameObject.AddComponent<RaidBlock> ();
883 raidBlockComponent.CopyFrom (behavior);
884 } else if (behavior is CombatBlock) {
885 var combatBlockComponent = player.gameObject.AddComponent<CombatBlock> ();
886 combatBlockComponent.CopyFrom (behavior);
887 }
888
889 behavior.moved = true;
890 GameObject.Destroy (behavior);
891 }
892 }
893
894 void OnPlayerAttack (BasePlayer attacker, HitInfo hitInfo)
895 {
896 if (!combatBlock || !(hitInfo.HitEntity is BasePlayer))
897 return;
898 if (!combatOnHitNPC && hitInfo.HitEntity.IsNpc)
899 return;
900 if (!combatOnTakeDamageNPC && attacker.IsNpc) {
901 return;
902 }
903 if (!IsCombatDamage (hitInfo.damageTypes))
904 return;
905
906 float totalDamage = hitInfo.damageTypes.Total ();
907 BasePlayer target = hitInfo.HitEntity as BasePlayer;
908
909 if (combatOnTakeDamage) {
910 if (GetHealthPercent (target, hitInfo.damageTypes.Total ()) > combatOnTakeDamageMinCondition) {
911 return;
912 }
913
914 if (totalDamage < combatOnTakeDamageMinDamage) {
915 return;
916 }
917
918
919 StartCombatBlocking (target);
920 }
921
922 if (combatOnHitPlayer) {
923 if (GetHealthPercent (attacker, hitInfo.damageTypes.Total ()) > combatOnHitPlayerMinCondition) {
924 return;
925 }
926
927 if (totalDamage < combatOnHitPlayerMinDamage) {
928 return;
929 }
930
931 StartCombatBlocking (attacker);
932 }
933 }
934
935 void OnEntityDeath (BaseCombatEntity entity, HitInfo hitInfo)
936 {
937 if (blockOnDestroy && raidBlock) {
938 if (hitInfo == null || hitInfo.Initiator == null || !IsDeathDamage (hitInfo.damageTypes) || !IsEntityBlocked (entity))
939 return;
940
941 StructureAttack (entity, hitInfo.Initiator, hitInfo?.WeaponPrefab?.ShortPrefabName, hitInfo.HitPositionWorld);
942 }
943
944 if (entity.ToPlayer () == null)
945 return;
946
947 var player = entity.ToPlayer ();
948 RaidBlock raidBlocker;
949 if (raidBlock && raidUnblockOnDeath && TryGetBlocker (player, out raidBlocker)) {
950 timer.In (0.3f, delegate () {
951 raidBlocker.Stop ();
952 });
953 }
954
955 CombatBlock combatBlocker;
956 if (combatBlock && combatUnblockOnDeath && TryGetBlocker (player, out combatBlocker)) {
957 timer.In (0.3f, delegate () {
958 combatBlocker.Stop ();
959 });
960 }
961 }
962
963 void OnPlayerSleepEnded (BasePlayer player)
964 {
965 if (player == null) return;
966 RaidBlock raidBlocker;
967 if (raidBlock && raidUnblockOnWakeup && TryGetBlocker (player, out raidBlocker)) {
968 timer.In (0.3f, delegate () {
969 raidBlocker.Stop ();
970 });
971 }
972
973 CombatBlock combatBlocker;
974 if (combatBlock && combatUnblockOnWakeup && TryGetBlocker (player, out combatBlocker)) {
975 timer.In (0.3f, delegate () {
976 combatBlocker.Stop ();
977 });
978 }
979 }
980
981 void OnPlayerRespawned (BasePlayer player)
982 {
983 if (player == null) return;
984 RaidBlock raidBlocker;
985 if (raidBlock && raidUnblockOnRespawn && TryGetBlocker (player, out raidBlocker)) {
986 timer.In (0.3f, delegate () {
987 raidBlocker.Stop ();
988 });
989 }
990
991 CombatBlock combatBlocker;
992 if (combatBlock && combatUnblockOnRespawn && TryGetBlocker (player, out combatBlocker)) {
993 timer.In (0.3f, delegate () {
994 combatBlocker.Stop ();
995 });
996 }
997 }
998
999 object CanLootEntity(BasePlayer player, StorageContainer container)
1000 {
1001 if(container?.GetEntity() is Recycler)
1002 {
1003 object reply = CanDo("recycle", player);
1004
1005 if (reply != null && IsBlocked(player))
1006 {
1007 player.ChatMessage((string)reply);
1008 return true;
1009 }
1010 }
1011
1012 return null;
1013 }
1014
1015 #endregion
1016
1017 #region Block Handling
1018
1019 void StructureAttack (BaseEntity targetEntity, BaseEntity sourceEntity, string weapon, Vector3 hitPosition)
1020 {
1021 BasePlayer source = null;
1022
1023 if (sourceEntity.ToPlayer () is BasePlayer)
1024 source = sourceEntity.ToPlayer ();
1025 else {
1026 ulong ownerID = sourceEntity.OwnerID;
1027 if (ownerID.IsSteamId ())
1028 source = BasePlayer.FindByID (ownerID);
1029 else
1030 return;
1031 }
1032
1033 if (source == null)
1034 return;
1035
1036 List<string> sourceMembers = null;
1037
1038 if (targetEntity.OwnerID.IsSteamId () || (blockUnowned && !targetEntity.OwnerID.IsSteamId ())) {
1039 if (clanCheck || friendCheck)
1040 sourceMembers = getFriends (source.UserIDString);
1041
1042 if (blockAll) {
1043 BlockAll (source, targetEntity, sourceMembers);
1044 } else {
1045 if (ownerBlock)
1046 OwnerBlock (source, sourceEntity, targetEntity.OwnerID, targetEntity.transform.position, sourceMembers);
1047
1048 if (raiderBlock)
1049 RaiderBlock (source, targetEntity.OwnerID, targetEntity.transform.position, sourceMembers);
1050 }
1051 }
1052 }
1053
1054 float GetHealthPercent (BaseEntity entity, float damage = 0f)
1055 {
1056 return (entity.Health () - damage) * 100f / entity.MaxHealth ();
1057 }
1058
1059 void BlockAll (BasePlayer source, BaseEntity targetEntity, List<string> sourceMembers = null)
1060 {
1061 if (ShouldBlockEscape (targetEntity.OwnerID, source.userID, sourceMembers)) {
1062 StartRaidBlocking (source, targetEntity.transform.position);
1063 }
1064
1065 var checkSourceMembers = false;
1066 if (targetEntity.OwnerID == source.userID || (sourceMembers is List<string> && sourceMembers.Contains (targetEntity.OwnerID.ToString ()))) {
1067 checkSourceMembers = true;
1068 }
1069
1070 var nearbyTargets = Pool.GetList<BasePlayer> ();
1071 Vis.Entities (targetEntity.transform.position, raidDistance, nearbyTargets, blockLayer);
1072 if (nearbyTargets.Count > 0) {
1073 RaidBlock blocker;
1074 foreach (BasePlayer nearbyTarget in nearbyTargets) {
1075 if (nearbyTarget.IsNpc) continue;
1076 if (nearbyTarget.userID == source.userID) continue;
1077 if (TryGetBlocker (nearbyTarget, out blocker) && blocker.Active) {
1078 StartRaidBlocking (nearbyTarget, targetEntity.transform.position);
1079 } else if (ShouldBlockEscape (nearbyTarget.userID, source.userID, checkSourceMembers ? sourceMembers : null)) {
1080 StartRaidBlocking (nearbyTarget, targetEntity.transform.position);
1081 }
1082 }
1083 }
1084
1085 Pool.FreeList (ref nearbyTargets);
1086 }
1087
1088 void OwnerBlock (BasePlayer source, BaseEntity sourceEntity, ulong target, Vector3 position, List<string> sourceMembers = null)
1089 {
1090 if (!ShouldBlockEscape (target, source.userID, sourceMembers))
1091 return;
1092
1093 var targetMembers = new List<string> ();
1094
1095 if (clanShare || friendShare)
1096 targetMembers = getFriends (target.ToString ());
1097
1098 var nearbyTargets = Pool.GetList<BasePlayer> ();
1099 Vis.Entities (position, raidDistance, nearbyTargets, blockLayer);
1100 if (cupboardShare)
1101 sourceMembers = CupboardShare (target.ToString (), position, sourceEntity, sourceMembers);
1102
1103 if (nearbyTargets.Count > 0) {
1104 foreach (BasePlayer nearbyTarget in nearbyTargets) {
1105 if (nearbyTarget.IsNpc) continue;
1106 if (nearbyTarget.userID == target || (targetMembers != null && targetMembers.Contains (nearbyTarget.UserIDString)))
1107 StartRaidBlocking (nearbyTarget, position);
1108 }
1109 }
1110
1111 Pool.FreeList (ref nearbyTargets);
1112 }
1113
1114 List<string> CupboardShare (string owner, Vector3 position, BaseEntity sourceEntity, List<string> sourceMembers = null)
1115 {
1116 var nearbyCupboards = Pool.GetList<BuildingPrivlidge> ();
1117 Vis.Entities (position, raidDistance, nearbyCupboards, cupboardMask);
1118 if (sourceMembers == null)
1119 sourceMembers = new List<string> ();
1120
1121 List<string> cupboardMembers = new List<string> ();
1122
1123 var sourcePlayer = sourceEntity as BasePlayer;
1124
1125 if (sourcePlayer != null) {
1126 foreach (var cup in nearbyCupboards) {
1127 if (cup.IsAuthed (sourcePlayer)) {
1128 bool ownerOrFriend = false;
1129
1130 if (owner == cup.OwnerID.ToString ())
1131 ownerOrFriend = true;
1132
1133 foreach (var member in sourceMembers) {
1134 if (member == cup.OwnerID.ToString ())
1135 ownerOrFriend = true;
1136 }
1137
1138 if (ownerOrFriend)
1139 foreach (var proto in cup.authorizedPlayers)
1140 if (!sourceMembers.Contains (proto.userid.ToString ()))
1141 cupboardMembers.Add (proto.userid.ToString ());
1142 }
1143 }
1144 }
1145
1146 sourceMembers.AddRange (cupboardMembers);
1147 Pool.FreeList (ref nearbyCupboards);
1148
1149 return sourceMembers;
1150 }
1151
1152 void RaiderBlock (BasePlayer source, ulong target, Vector3 position, List<string> sourceMembers = null)
1153 {
1154 if (!ShouldBlockEscape (target, source.userID, sourceMembers))
1155 return;
1156
1157 var targetMembers = new List<string> ();
1158
1159 if ((clanShare || friendShare) && sourceMembers == null)
1160 sourceMembers = getFriends (source.UserIDString);
1161
1162 var nearbyTargets = Pool.GetList<BasePlayer> ();
1163 Vis.Entities (position, raidDistance, nearbyTargets, blockLayer);
1164 if (nearbyTargets.Count > 0) {
1165 foreach (BasePlayer nearbyTarget in nearbyTargets) {
1166 if (nearbyTarget.IsNpc) continue;
1167 if (nearbyTarget == source || (sourceMembers != null && sourceMembers.Contains (nearbyTarget.UserIDString)))
1168 StartRaidBlocking (nearbyTarget, position);
1169 }
1170 }
1171
1172 Pool.FreeList (ref nearbyTargets);
1173 }
1174
1175 #endregion
1176
1177 #region API
1178
1179 bool IsBlocked (string target)
1180 {
1181 var player = BasePlayer.Find (target);
1182 if (player is BasePlayer) {
1183 return IsBlocked (player);
1184 }
1185
1186 return false;
1187 }
1188
1189 bool IsBlocked (BasePlayer target)
1190 {
1191 if (IsBlocked<RaidBlock> (target) || IsBlocked<CombatBlock> (target))
1192 return true;
1193
1194 return false;
1195 }
1196
1197 public bool IsBlocked<T> (BasePlayer target) where T : BlockBehavior
1198 {
1199 T behavior;
1200 if (TryGetBlocker<T> (target, out behavior) && behavior.Active)
1201 return true;
1202
1203 return false;
1204 }
1205
1206 bool IsRaidBlocked (BasePlayer target)
1207 {
1208 return IsBlocked<RaidBlock> (target);
1209 }
1210
1211 bool IsCombatBlocked (BasePlayer target)
1212 {
1213 return IsBlocked<CombatBlock> (target);
1214 }
1215
1216 bool IsEscapeBlocked (string target)
1217 {
1218 var player = BasePlayer.Find (target);
1219 if (player is BasePlayer) {
1220 return IsBlocked (player);
1221 }
1222
1223 return false;
1224 }
1225
1226 bool IsRaidBlocked (string target)
1227 {
1228 var player = BasePlayer.Find (target);
1229 if (player is BasePlayer) {
1230 return IsBlocked<RaidBlock> (player);
1231 }
1232
1233 return false;
1234 }
1235
1236 bool IsCombatBlocked (string target)
1237 {
1238 var player = BasePlayer.Find (target);
1239 if (player is BasePlayer) {
1240 return IsBlocked<CombatBlock> (player);
1241 }
1242
1243 return false;
1244 }
1245
1246 bool ShouldBlockEscape (ulong target, ulong source, List<string> sourceMembers = null)
1247 {
1248 if (target == source) {
1249 if ((ownerBlock || raiderBlock || blockAll) && (!ownerCheck))
1250 return true;
1251
1252 return false;
1253 }
1254
1255 if (sourceMembers is List<string> && sourceMembers.Contains (target.ToString ()))
1256 return false;
1257
1258 return true;
1259 }
1260
1261 //[ChatCommand ("bblocked")]
1262 //void cmdBBlocked (BasePlayer player, string command, string [] args)
1263 //{
1264 // StartCombatBlocking (player);
1265 // StartRaidBlocking (player);
1266 //}
1267
1268 //[ChatCommand ("bunblocked")]
1269 //void cmdBUnblocked (BasePlayer player, string command, string [] args)
1270 //{
1271 // StopCombatBlocking (player);
1272 // StopRaidBlocking (player);
1273 //}
1274
1275 void StartRaidBlocking (BasePlayer target, bool createZone = true)
1276 {
1277 StartRaidBlocking (target, target.transform.position, createZone);
1278 }
1279
1280 void StartRaidBlocking (BasePlayer target, Vector3 position, bool createZone = true)
1281 {
1282 if (HasPerm (target.UserIDString, "disable")) {
1283 return;
1284 }
1285
1286 if (target.gameObject == null) {
1287 return;
1288 }
1289
1290 if (Interface.Call ("CanRaidBlock", target, position, createZone) != null) {
1291 return;
1292 }
1293
1294 if (target.gameObject == null)
1295 return;
1296 var raidBlocker = target.gameObject.GetComponent<RaidBlock> ();
1297 if (raidBlocker == null) {
1298 raidBlocker = target.gameObject.AddComponent<RaidBlock> ();
1299 }
1300
1301 Interface.CallHook ("OnRaidBlock", target, position);
1302
1303 raidBlocker.lastBlock = DateTime.Now;
1304
1305 if (raidBlockNotify)
1306 SendBlockMessage (target, raidBlocker, "Raid Block Notifier", "Raid Block Complete");
1307
1308 if (useZoneManager && createZone && (zoneEnter || zoneLeave))
1309 CreateRaidZone (position);
1310 }
1311
1312 void StartCombatBlocking (BasePlayer target)
1313 {
1314 if (HasPerm (target.UserIDString, "disable")) {
1315 return;
1316 }
1317
1318 if (target.gameObject == null) {
1319 return;
1320 }
1321
1322 if (Interface.Call ("CanCombatBlock", target) != null) {
1323 return;
1324 }
1325
1326 var combatBlocker = target.gameObject.GetComponent<CombatBlock> ();
1327 if (combatBlocker == null) {
1328 combatBlocker = target.gameObject.AddComponent<CombatBlock> ();
1329 }
1330
1331 Interface.CallHook ("OnCombatBlock", target);
1332
1333 combatBlocker.lastBlock = DateTime.Now;
1334
1335 if (combatBlockNotify)
1336 SendBlockMessage (target, combatBlocker, "Combat Block Notifier", "Combat Block Complete");
1337 }
1338
1339 void StopBlocking (BasePlayer target)
1340 {
1341 if (IsRaidBlocked (target))
1342 StopBlocking<RaidBlock> (target);
1343 if (IsCombatBlocked (target))
1344 StopBlocking<CombatBlock> (target);
1345 }
1346
1347 public void StopBlocking<T> (BasePlayer target) where T : BlockBehavior
1348 {
1349 if (target.gameObject == null)
1350 return;
1351 var block = target.gameObject.GetComponent<T> ();
1352 if (block is BlockBehavior)
1353 block.Stop ();
1354
1355 if (block is RaidBlock) {
1356 Interface.CallHook ("OnRaidBlockStopped", target);
1357 } else if (block is CombatBlock) {
1358 Interface.CallHook ("OnCombatBlockStopped", target);
1359 }
1360 }
1361
1362 void ClearRaidBlockingS (string target)
1363 {
1364 StopRaidBlocking (target);
1365 }
1366
1367 void StopRaidBlocking (BasePlayer player)
1368 {
1369 if (player is BasePlayer && IsRaidBlocked (player))
1370 StopBlocking<RaidBlock> (player);
1371 }
1372
1373 void StopRaidBlocking (string target)
1374 {
1375 var player = BasePlayer.Find (target);
1376 StopRaidBlocking (player);
1377 }
1378
1379 void StopCombatBlocking (BasePlayer player)
1380 {
1381 if (player is BasePlayer && IsRaidBlocked (player))
1382 StopBlocking<CombatBlock> (player);
1383 }
1384
1385 void StopCombatBlocking (string target)
1386 {
1387 var player = BasePlayer.Find (target);
1388 StopCombatBlocking (player);
1389 }
1390
1391 void ClearCombatBlocking (string target)
1392 {
1393 StopCombatBlocking (target);
1394 }
1395
1396 #endregion
1397
1398 #region Zone Handling
1399
1400 void EraseZone (string zoneid)
1401 {
1402 ZoneManager.CallHook ("EraseZone", zoneid);
1403 zones.Remove (zoneid);
1404 }
1405
1406 void ResetZoneTimer (RaidZone zone)
1407 {
1408 zone.ResetTimer ().timer = timer.In (raidDuration, delegate () {
1409 EraseZone (zone.zoneid);
1410 });
1411 }
1412
1413 void CreateRaidZone (Vector3 position)
1414 {
1415 var zoneid = position.ToString ();
1416
1417 RaidZone zone;
1418 if (zones.TryGetValue (zoneid, out zone)) {
1419 ResetZoneTimer (zone);
1420 return;
1421 }
1422
1423 foreach (var nearbyZone in zones) {
1424 if (nearbyZone.Value.Distance (position) < (raidDistance / 2)) {
1425 ResetZoneTimer (nearbyZone.Value);
1426 return;
1427 }
1428 }
1429
1430 ZoneManager.CallHook ("CreateOrUpdateZone", zoneid, new string []
1431 {
1432 "radius",
1433 raidDistance.ToString()
1434 }, position);
1435
1436 zones.Add (zoneid, zone = new RaidZone (zoneid, position));
1437
1438 ResetZoneTimer (zone);
1439 }
1440
1441 [HookMethod ("OnEnterZone")]
1442 void OnEnterZone (string zoneid, BasePlayer player)
1443 {
1444 if (!zoneEnter)
1445 return;
1446 if (!zones.ContainsKey (zoneid))
1447 return;
1448
1449 StartRaidBlocking (player, player.transform.position, false);
1450 }
1451
1452 [HookMethod ("OnExitZone")]
1453 void OnExitZone (string zoneid, BasePlayer player)
1454 {
1455 if (!zoneLeave)
1456 return;
1457 if (!zones.ContainsKey (zoneid))
1458 return;
1459
1460 if (IsRaidBlocked (player)) {
1461 StopBlocking<RaidBlock> (player);
1462 }
1463 }
1464
1465 [HookMethod ("OnPlayerEnteredRaidableBase")]
1466 void OnPlayerEnteredRaidableBase(BasePlayer player, Vector3 raidPos, bool allowPVP)
1467 {
1468 if (!raidableZoneEnter)
1469 return;
1470
1471 StartRaidBlocking (player, false);
1472 }
1473
1474 [HookMethod ("OnPlayerExitedRaidableBase")]
1475 void OnPlayerExitedRaidableBase(BasePlayer player, Vector3 raidPos, bool allowPVP)
1476 {
1477 if (!raidableZoneLeave)
1478 return;
1479
1480 if (IsRaidBlocked (player)) {
1481 StopBlocking<RaidBlock> (player);
1482 }
1483 }
1484
1485 #endregion
1486
1487 #region Friend/Clan Integration
1488
1489 public List<string> getFriends (string player)
1490 {
1491 var players = new List<string> ();
1492 if (player == null)
1493 return players;
1494
1495 if (friendShare || friendCheck) {
1496 var friendList = getFriendList (player);
1497 if (friendList != null)
1498 players.AddRange (friendList);
1499 }
1500
1501 if (clanShare || clanCheck) {
1502 var members = getClanMembers (player);
1503 if (members != null)
1504 players.AddRange (members);
1505 }
1506 return players;
1507 }
1508
1509 public List<string> getFriendList (string player)
1510 {
1511 object friends_obj = null;
1512 DateTime lastFriendCheckPlayer;
1513 var players = new List<string> ();
1514
1515 if (lastFriendCheck.TryGetValue (player, out lastFriendCheckPlayer)) {
1516 if ((DateTime.Now - lastFriendCheckPlayer).TotalMinutes <= cacheTimer && friendCache.TryGetValue (player, out players)) {
1517 return players;
1518 } else {
1519 friends_obj = Friends?.CallHook ("IsFriendOfS", player);
1520 lastFriendCheck [player] = DateTime.Now;
1521 }
1522 } else {
1523 friends_obj = Friends?.CallHook ("IsFriendOfS", player);
1524 lastFriendCheck.Add (player, DateTime.Now);
1525 }
1526
1527 if (friends_obj == null)
1528 return players;
1529
1530 string [] friends = friends_obj as string [];
1531
1532 foreach (string fid in friends)
1533 players.Add (fid);
1534
1535 if (friendCache.ContainsKey (player))
1536 friendCache [player] = players;
1537 else
1538 friendCache.Add (player, players);
1539
1540 return players;
1541 }
1542
1543 public List<string> getClanMembers (string player)
1544 {
1545 string tag = null;
1546 DateTime lastClanCheckPlayer;
1547 string lastClanCached;
1548 if (lastClanCheck.TryGetValue (player, out lastClanCheckPlayer) && clanCache.TryGetValue (player, out lastClanCached)) {
1549 if ((DateTime.Now - lastClanCheckPlayer).TotalMinutes <= cacheTimer)
1550 tag = lastClanCached;
1551 else {
1552 tag = Clans.Call<string> ("GetClanOf", player);
1553 clanCache [player] = tag;
1554 lastClanCheck [player] = DateTime.Now;
1555 }
1556 } else {
1557 tag = Clans.Call<string> ("GetClanOf", player);
1558 if (lastClanCheck.ContainsKey (player))
1559 lastClanCheck.Remove (player);
1560
1561 if (clanCache.ContainsKey (player))
1562 clanCache.Remove (player);
1563
1564 clanCache.Add (player, tag);
1565 lastClanCheck.Add (player, DateTime.Now);
1566 }
1567
1568 if (tag == null)
1569 return null;
1570
1571 List<string> lastMemberCache;
1572 if (memberCache.TryGetValue (tag, out lastMemberCache))
1573 return lastMemberCache;
1574
1575 var clan = GetClan (tag);
1576
1577 if (clan == null)
1578 return null;
1579
1580 return CacheClan (clan);
1581 }
1582
1583 JObject GetClan (string tag)
1584 {
1585 if (string.IsNullOrEmpty (tag)) {
1586 return null;
1587 }
1588 return Clans.Call<JObject> ("GetClan", tag);
1589 }
1590
1591 List<string> CacheClan (JObject clan)
1592 {
1593 string tag = clan ["tag"].ToString ();
1594 List<string> players = new List<string> ();
1595 foreach (string memberid in clan ["members"]) {
1596 if (clanCache.ContainsKey (memberid))
1597 clanCache [memberid] = tag;
1598 else
1599 clanCache.Add (memberid, tag);
1600
1601 players.Add (memberid);
1602 }
1603
1604 if (memberCache.ContainsKey (tag))
1605 memberCache [tag] = players;
1606 else
1607 memberCache.Add (tag, players);
1608
1609 if (lastCheck.ContainsKey (tag))
1610 lastCheck [tag] = DateTime.Now;
1611 else
1612 lastCheck.Add (tag, DateTime.Now);
1613
1614 return players;
1615 }
1616
1617 [HookMethod ("OnClanCreate")]
1618 void OnClanCreate (string tag)
1619 {
1620 var clan = GetClan (tag);
1621 if (clan != null) {
1622 CacheClan (clan);
1623 } else {
1624 PrintWarning ("Unable to find clan after creation: " + tag);
1625 }
1626 }
1627
1628 [HookMethod ("OnClanUpdate")]
1629 void OnClanUpdate (string tag)
1630 {
1631 var clan = GetClan (tag);
1632 if (clan != null) {
1633 CacheClan (clan);
1634 } else {
1635 PrintWarning ("Unable to find clan after update: " + tag);
1636 }
1637 }
1638
1639 [HookMethod ("OnClanDestroy")]
1640 void OnClanDestroy (string tag)
1641 {
1642 if (lastCheck.ContainsKey (tag)) {
1643 lastCheck.Remove (tag);
1644 }
1645
1646 if (memberCache.ContainsKey (tag)) {
1647 memberCache.Remove (tag);
1648 }
1649 }
1650
1651 #endregion
1652
1653 #region Permission Checking & External API Handling
1654
1655 bool HasPerm (string userid, string perm)
1656 {
1657 return permission.UserHasPermission (userid, "noescape." + perm);
1658 }
1659
1660 bool CanRaidCommand (BasePlayer player, string command)
1661 {
1662 return raidBlock && HasPerm (player.UserIDString, "raid." + command + "block") && IsRaidBlocked (player);
1663 }
1664
1665 bool CanRaidCommand (string playerID, string command)
1666 {
1667 return raidBlock && HasPerm (playerID, "raid." + command + "block") && IsRaidBlocked (playerID);
1668 }
1669
1670 bool CanCombatCommand (BasePlayer player, string command)
1671 {
1672 return combatBlock && HasPerm (player.UserIDString, "combat." + command + "block") && IsCombatBlocked (player);
1673 }
1674
1675 bool CanCombatCommand (string playerID, string command)
1676 {
1677 return combatBlock && HasPerm (playerID, "combat." + command + "block") && IsCombatBlocked (playerID);
1678 }
1679
1680 object CanDo (string command, BasePlayer player)
1681 {
1682 if (CanRaidCommand (player, command))
1683 return GetMessage<RaidBlock> (player, "Raid Blocked Message", raidDuration);
1684 else if (CanCombatCommand (player, command))
1685 return GetMessage<CombatBlock> (player, "Combat Blocked Message", combatDuration);
1686
1687 return null;
1688 }
1689
1690 object OnStructureRepair (BaseCombatEntity entity, BasePlayer player)
1691 {
1692 var result = CanDo ("repair", player);
1693 if (result is string) {
1694 if (entity.health > entity.MaxHealth ()) {
1695 return null;
1696 }
1697 SendReply (player, result.ToString ());
1698 return true;
1699 }
1700
1701 return null;
1702 }
1703
1704 object OnStructureUpgrade (BuildingBlock block, BasePlayer player, BuildingGrade.Enum grade)
1705 {
1706 var result = CanDo ("upgrade", player);
1707 if (result is string) {
1708 SendReply (player, result.ToString ());
1709 return true;
1710 }
1711
1712 return null;
1713 }
1714
1715 object canRedeemKit (BasePlayer player)
1716 {
1717 return CanDo ("kit", player);
1718 }
1719
1720 object CanUseMailbox (BasePlayer player, Mailbox mailbox)
1721 {
1722 var result = CanDo ("mailbox", player);
1723 if (result is string) {
1724 SendReply (player, result.ToString ());
1725 return true;
1726 }
1727
1728 return null;
1729 }
1730
1731 object CanUseVending (VendingMachine machine, BasePlayer player)
1732 {
1733 var result = CanDo ("vend", player);
1734 if (result is string) {
1735 SendReply (player, result.ToString ());
1736 return true;
1737 }
1738
1739 return null;
1740 }
1741
1742 object CanBuild (Planner plan, Construction prefab)
1743 {
1744 var player = plan.GetOwnerPlayer ();
1745 var result = CanDo ("build", player);
1746 if (result is string) {
1747 if (isEntityException (prefab.fullName)) {
1748 return null;
1749 }
1750
1751 SendReply (player, result.ToString ());
1752 return true;
1753 }
1754
1755 return null;
1756 }
1757
1758 object CanAssignBed (SleepingBag bag, BasePlayer player, ulong targetPlayerId)
1759 {
1760 var result = CanDo ("assignbed", player);
1761 if (result is string) {
1762 SendReply (player, result.ToString ());
1763 return true;
1764 }
1765
1766 return null;
1767 }
1768
1769 object CanOpenBackpack(BasePlayer player, ulong backpackOwnerID)
1770 {
1771 return CanDo ("backpack", player);
1772 }
1773
1774 object CanBank (BasePlayer player)
1775 {
1776 return CanDo ("bank", player);
1777 }
1778
1779 object CanTrade (BasePlayer player)
1780 {
1781 return CanDo ("trade", player);
1782 }
1783
1784 object canRemove (BasePlayer player)
1785 {
1786 return CanDo ("remove", player);
1787 }
1788
1789 object canShop (BasePlayer player)
1790 {
1791 return CanDo ("shop", player);
1792 }
1793
1794 object CanShop (BasePlayer player)
1795 {
1796 return CanDo ("shop", player);
1797 }
1798
1799 object CanTeleport (BasePlayer player)
1800 {
1801 return CanDo ("tp", player);
1802 }
1803
1804 object canTeleport (BasePlayer player) // ALIAS FOR MagicTeleportation
1805 {
1806 return CanTeleport (player);
1807 }
1808
1809 object CanGridTeleport (BasePlayer player) // ALIAS FOR GrTeleport
1810 {
1811 return CanTeleport (player);
1812 }
1813
1814 object CanCraft (ItemCrafter itemCrafter, ItemBlueprint bp, int amount)
1815 {
1816 BasePlayer player = itemCrafter.containers [0].GetOwnerPlayer ();
1817
1818 if (player != null) {
1819 var result = CanDo ("craft", player);
1820 if (result is string) {
1821 SendReply (player, result.ToString ());
1822 return false;
1823 }
1824 }
1825
1826 return null;
1827 }
1828
1829 object CanRecycleCommand (BasePlayer player)
1830 {
1831 return CanDo ("recycle", player);
1832 }
1833
1834 object CanBGrade (BasePlayer player, int grade, BuildingBlock buildingBlock, Planner planner)
1835 {
1836 if (CanRaidCommand (player, "bgrade") || CanCombatCommand (player, "bgrade"))
1837 return -1;
1838 return null;
1839 }
1840
1841 #endregion
1842
1843 #region Messages
1844
1845 void SendBlockMessage (BasePlayer target, BlockBehavior blocker, string langMessage, string completeMessage)
1846 {
1847 var send = false;
1848 if (blocker.lastNotification != DateTime.MinValue) {
1849 TimeSpan diff = DateTime.Now - blocker.lastNotification;
1850 if (diff.TotalSeconds >= (blocker.Duration / 2))
1851 send = true;
1852 } else
1853 send = true;
1854
1855 if (send) {
1856 string message = string.Empty;
1857
1858 if (sendChatNotification || sendGUIAnnouncementsNotification)
1859 message = GetPrefix (target.UserIDString) + GetMsg (langMessage, target.UserIDString).Replace ("{time}", GetCooldownTime (blocker.Duration, target.UserIDString));
1860
1861 if (sendChatNotification)
1862 SendReply (target, message);
1863
1864 if (sendGUIAnnouncementsNotification)
1865 GUIAnnouncements?.Call ("CreateAnnouncement", message, GUIAnnouncementTintColor, GUIAnnouncementTextColor, target);
1866
1867 if (sendLustyMapNotification && LustyMapDuration > 0) {
1868 blocker.iconUID = Guid.NewGuid ().ToString ("N");
1869 var obj = LustyMap?.Call ("AddMarker", target.transform.position.x, target.transform.position.z, blocker.iconUID, LustyMapIcon);
1870 if (obj is bool && (bool)obj == true) {
1871 timer.In (LustyMapDuration, delegate () {
1872 LustyMap?.Call ("RemoveMarker", blocker.iconUID);
1873 });
1874 }
1875 }
1876
1877 blocker.lastNotification = DateTime.Now;
1878 }
1879
1880 blocker.Notify (delegate () {
1881 blocker.notifyCallback = null;
1882 if (target?.IsConnected == true) {
1883 string message = string.Empty;
1884
1885 if (sendChatNotification || sendGUIAnnouncementsNotification)
1886 message = GetPrefix (target.UserIDString) + GetMsg (completeMessage, target.UserIDString);
1887
1888 if (sendChatNotification)
1889 SendReply (target, message);
1890
1891 if (sendGUIAnnouncementsNotification)
1892 GUIAnnouncements?.Call ("CreateAnnouncement", message, GUIAnnouncementTintColor, GUIAnnouncementTextColor, target);
1893
1894 if (sendLustyMapNotification && LustyMapDuration > 0)
1895 LustyMap?.Call ("RemoveMarker", blocker.iconUID);
1896 }
1897 });
1898 }
1899
1900 string GetCooldownTime (float f, string userID)
1901 {
1902 if (f > 60)
1903 return Math.Round (f / 60, 1) + " " + GetMsg ("Unit Minutes", userID);
1904
1905 return f + " " + GetMsg ("Unit Seconds", userID);
1906 }
1907
1908 public string GetMessage (BasePlayer player)
1909 {
1910 if (IsRaidBlocked (player))
1911 return GetMessage<RaidBlock> (player, "Raid Blocked Message", raidDuration);
1912 else if (IsCombatBlocked (player))
1913 return GetMessage<CombatBlock> (player, "Combat Blocked Message", combatDuration);
1914
1915 return null;
1916 }
1917
1918 public string GetPrefix (string player)
1919 {
1920 string prefix = GetMsg ("Prefix", player);
1921 if (!string.IsNullOrEmpty (prefix)) {
1922 return prefix + ": ";
1923 }
1924
1925 return string.Empty;
1926 }
1927
1928 public string GetMessage<T> (BasePlayer player, string blockMsg, float duration) where T : BlockBehavior
1929 {
1930 T blocker;
1931 if (duration > 0 && TryGetBlocker<T> (player, out blocker)) {
1932 var ts = DateTime.Now - blocker.lastBlock;
1933 var unblocked = Math.Round ((duration / 60) - Convert.ToSingle (ts.TotalMinutes), 2);
1934
1935 if (ts.TotalMinutes <= duration) {
1936 if (unblocked < 1) {
1937 var timelefts = Math.Round (Convert.ToDouble (duration) - ts.TotalSeconds);
1938 return GetPrefix (player.UserIDString) + GetMsg (blockMsg, player).Replace ("{time}", timelefts.ToString () + " " + GetMsg ("Unit Seconds", player));
1939 }
1940
1941 return GetPrefix (player.UserIDString) + GetMsg (blockMsg, player).Replace ("{time}", unblocked.ToString () + " " + GetMsg ("Unit Minutes", player));
1942 }
1943 }
1944
1945 return null;
1946 }
1947
1948 #endregion
1949
1950 #region Utility Methods
1951
1952 bool TryGetBlocker<T> (BasePlayer player, out T blocker) where T : BlockBehavior
1953 {
1954 blocker = null;
1955 if (player.gameObject == null)
1956 return false;
1957 if ((blocker = player.gameObject.GetComponent<T> ()) != null)
1958 return true;
1959
1960 return false;
1961 }
1962
1963 public bool isEntityException (string prefabName)
1964 {
1965 var result = false;
1966
1967 foreach (string p in exceptionPrefabs) {
1968 if (prefabName.IndexOf (p) != -1) {
1969 result = true;
1970 break;
1971 }
1972 }
1973
1974 return result;
1975 }
1976
1977 public bool IsEntityBlocked (BaseCombatEntity entity)
1978 {
1979 if (entity is BuildingBlock) {
1980 if (((BuildingBlock)entity).grade == BuildingGrade.Enum.Twigs)
1981 return false;
1982
1983 return true;
1984 }
1985
1986 var prefabName = entity.ShortPrefabName;
1987 var result = false;
1988 if (prefabBlockCache.TryGetValue (prefabName, out result))
1989 return result;
1990
1991 result = false;
1992
1993 foreach (string p in blockedPrefabs) {
1994 if (prefabName.IndexOf (p) != -1) {
1995 result = true;
1996 break;
1997 }
1998 }
1999
2000
2001 prefabBlockCache.Add (prefabName, result);
2002 return result;
2003 }
2004
2005 bool IsRaidDamage (DamageType dt)
2006 {
2007 return raidDamageTypes.Contains (dt.ToString ());
2008 }
2009
2010 bool IsDeathDamage (DamageType dt)
2011 {
2012 return raidDeathTypes.Contains (dt.ToString ());
2013 }
2014
2015 bool IsRaidDamage (DamageTypeList dtList)
2016 {
2017 for (int index = 0; index < dtList.types.Length; ++index) {
2018 if (dtList.types [index] > 0 && IsRaidDamage ((DamageType)index)) {
2019 return true;
2020 }
2021 }
2022
2023 return false;
2024 }
2025
2026 bool IsDeathDamage (DamageTypeList dtList)
2027 {
2028 for (int index = 0; index < dtList.types.Length; ++index) {
2029 if (dtList.types [index] > 0 && IsDeathDamage ((DamageType)index)) {
2030 return true;
2031 }
2032 }
2033
2034 return false;
2035 }
2036
2037 bool IsExcludedWeapon (string name)
2038 {
2039 if (string.IsNullOrEmpty (name)) {
2040 return false;
2041 }
2042
2043 bool cachedValue;
2044
2045 if (_cachedExcludedWeapons.TryGetValue (name, out cachedValue)) {
2046 return cachedValue;
2047 }
2048
2049 foreach (var weaponName in exceptionWeapons) {
2050 if (name.Contains (weaponName)) {
2051 _cachedExcludedWeapons.Add (name, true);
2052 return true;
2053 }
2054 }
2055
2056 _cachedExcludedWeapons.Add (name, false);
2057 return false;
2058 }
2059
2060 bool IsCombatDamage (DamageType dt)
2061 {
2062 return combatDamageTypes.Contains (dt.ToString ());
2063 }
2064
2065 bool IsCombatDamage (DamageTypeList dtList)
2066 {
2067 for (int index = 0; index < dtList.types.Length; ++index) {
2068 if (dtList.types [index] > 0 && IsCombatDamage ((DamageType)index)) {
2069 return true;
2070 }
2071 }
2072
2073 return false;
2074 }
2075
2076 T GetConfig<T> (string name, string name2, string name3, T defaultValue)
2077 {
2078 try {
2079 var val = Config [name, name2, name3];
2080
2081 return ParseValue<T> (val, defaultValue);
2082 } catch (Exception ex) {
2083 //PrintWarning ("Invalid config value: " + name + "/" + name2 + "/" + name3 + " (" + ex.Message + ")");
2084 Config [name, name2, name3] = defaultValue;
2085 Config.Save ();
2086 return defaultValue;
2087 }
2088 }
2089
2090 T GetConfig<T> (string name, string name2, T defaultValue)
2091 {
2092 try {
2093 var val = Config [name, name2];
2094
2095 return ParseValue<T> (val, defaultValue);
2096 } catch (Exception ex) {
2097 //PrintWarning ("Invalid config value: " + name + "/" + name2 + " (" + ex.Message + ")");
2098 Config [name, name2] = defaultValue;
2099 Config.Save ();
2100 return defaultValue;
2101 }
2102 }
2103
2104 T GetConfig<T> (string name, T defaultValue)
2105 {
2106 try {
2107 var val = Config [name];
2108
2109 return ParseValue<T> (val, defaultValue);
2110 } catch (Exception ex) {
2111 //PrintWarning ("Invalid config value: " + name + " (" + ex.Message + ")");
2112 Config [name] = defaultValue;
2113 Config.Save ();
2114 return defaultValue;
2115 }
2116 }
2117
2118 T ParseValue<T> (object val, T defaultValue)
2119 {
2120 if (val == null)
2121 return defaultValue;
2122
2123 if (val is List<object>) {
2124 var t = typeof (T).GetGenericArguments () [0];
2125 if (t == typeof (String)) {
2126 var cval = new List<string> ();
2127 foreach (var v in val as List<object>)
2128 cval.Add ((string)v);
2129 val = cval;
2130 } else if (t == typeof (int)) {
2131 var cval = new List<int> ();
2132 foreach (var v in val as List<object>)
2133 cval.Add (Convert.ToInt32 (v));
2134 val = cval;
2135 }
2136 } else if (val is Dictionary<string, object>) {
2137 var t = typeof (T).GetGenericArguments () [1];
2138 if (t == typeof (int)) {
2139 var cval = new Dictionary<string, int> ();
2140 foreach (var v in val as Dictionary<string, object>)
2141 cval.Add (Convert.ToString (v.Key), Convert.ToInt32 (v.Value));
2142 val = cval;
2143 }
2144 }
2145
2146 return (T)Convert.ChangeType (val, typeof (T));
2147 }
2148
2149 static string GetMsg (string key, object user = null)
2150 {
2151 if (user is BasePlayer) {
2152 user = ((BasePlayer)user).UserIDString;
2153 }
2154 return plugin.lang.GetMessage (key, plugin, user == null ? null : user.ToString ());
2155 }
2156
2157 #endregion
2158 }
2159}