· 6 years ago · Mar 31, 2020, 11:30 PM
1using System.Collections.Generic;
2using Oxide.Core;
3using Oxide.Core.Configuration;
4using Oxide.Core.Plugins;
5using System.Linq;
6using Oxide.Core.Libraries.Covalence;
7using Newtonsoft.Json;
8using Newtonsoft.Json.Linq;
9using System;
10
11namespace Oxide.Plugins
12{
13 [Info("Clans", "k1lly0u", "0.1.53")]
14 [Description("Universal Instance with alliance support")]
15 public class Clans : CovalencePlugin
16 {
17 #region Fields
18 [PluginReference] Plugin BetterChat;
19
20 private Dictionary<string, Clan> clanData = new Dictionary<string, Clan>();
21
22 private DynamicConfigFile data;
23
24 public Dictionary<string, string> playerClans;
25
26 public Dictionary<string, Clan> clanCache;
27
28 public static Clans Instance { get; private set; }
29 #endregion
30
31 #region Oxide Hooks
32 private void Loaded()
33 {
34 data = Interface.Oxide.DataFileSystem.GetFile("clans_data");
35 lang.RegisterMessages(Messages, this);
36 Instance = this;
37 clanCache = new Dictionary<string, Clan>();
38 playerClans = new Dictionary<string, string>();
39 }
40
41 private void OnServerInitialized()
42 {
43 LoadVariables();
44 LoadData();
45
46 FillClanList();
47 SaveLoop();
48
49 SetClanTag();
50
51 foreach (IPlayer player in players.Connected)
52 OnUserConnected(player);
53 }
54
55 private void OnPluginLoaded(Plugin plugin)
56 {
57 if (plugin.Title == "BetterChat")
58 SetClanTag();
59 }
60
61 private void Unload()
62 {
63 configData = null;
64 Instance = null;
65 }
66
67 private void OnUserConnected(IPlayer player)
68 {
69 timer.In(3, () =>
70 {
71 if (player == null)
72 return;
73
74 if (!playerClans.ContainsKey(player.Id))
75 return;
76
77 Clan clan = clanCache[playerClans[player.Id]];
78 if (!clan.members.ContainsKey(player.Id))
79 return;
80
81 clan.members[player.Id] = player.Name;
82
83 if (configData.Settings.ShowJoinMessage)
84 clan.Broadcast(string.Format(Message("playerCon"), player.Name));
85 });
86 }
87
88 private void OnUserDisconnected(IPlayer player)
89 {
90 if (configData.Settings.ShowLeaveMessage)
91 {
92 if (player == null)
93 return;
94
95 if (!playerClans.ContainsKey(player.Id))
96 return;
97
98 Clan clan = clanCache[playerClans[player.Id]];
99 clan.Broadcast(string.Format(Message("playerDiscon"), player.Name));
100 }
101 }
102 #endregion
103
104 #region Functions
105 private void FillClanList()
106 {
107 if (clanCache == null)
108 {
109 clanCache = new Dictionary<string, Clan>();
110 return;
111 }
112
113 foreach (KeyValuePair<string, Clan> clan in clanCache)
114 {
115 foreach (KeyValuePair<string, string> member in clan.Value.members)
116 {
117 playerClans.Add(member.Key, clan.Key);
118 }
119 }
120 }
121
122 public bool PlayerHasClan(string id) => playerClans.ContainsKey(id);
123
124 public Clan FindClanByID(string id)
125 {
126 if (!PlayerHasClan(id))
127 return null;
128
129 Clan clan = FindClanByTag(playerClans[id]);
130 if (clan == null)
131 return null;
132
133 return clan;
134 }
135
136 public Clan FindClanByTag(string tag)
137 {
138 Clan clan;
139 if (clanCache.TryGetValue(tag, out clan))
140 return clan;
141
142 return null;
143 }
144
145 private void Reply(IPlayer player, string message, string message2 = null)
146 {
147 string formatMsg = $"<color={configData.MessageOptions.MSG}>{message}</color>";
148
149 if (!string.IsNullOrEmpty(message2))
150 formatMsg = $"<color={configData.MessageOptions.Main}>{message2}</color> {formatMsg}";
151
152 player.Reply(formatMsg);
153 }
154
155 private void ReplyKey(IPlayer player, string message, string arg)
156 {
157 string formatMsg = $"<color={configData.MessageOptions.MSG}>{message}</color>".Replace("{0}", $"</color><color={configData.MessageOptions.Main}>{arg}</color><color={configData.MessageOptions.MSG}>");
158
159 if (formatMsg.StartsWith("</color>"))
160 formatMsg = formatMsg.Substring(9).Trim();
161
162 player.Reply(formatMsg);
163 }
164
165 private IPlayer FindPlayer(IPlayer player, string arg)
166 {
167 IEnumerable<IPlayer> targets = from p in players.All
168 where (p.Name.ToLower().Contains(arg.ToLower()) ? true : p.Id == arg)
169 select p;
170
171 int targetCount = targets.Count();
172
173 if (targetCount == 0)
174 {
175 if (player != null)
176 player.Reply(Message("noPlayers", player.Id));
177 return null;
178 }
179
180 if (targetCount > 1)
181 {
182 for (int i = 0; i < targetCount; i++)
183 {
184 IPlayer target = targets.ElementAt(i);
185 if (target.Name.Equals(arg) || target.Id.Equals(arg))
186 return target;
187 }
188
189 if (player != null)
190 player.Reply(Message("multiPlayers", player.Id));
191 return null;
192 }
193
194 if (targets.Single() != null)
195 return targets.Single();
196 else player.Reply(Message("noPlayers", player.Id));
197
198 return null;
199 }
200
201 private void SetClanTag() => BetterChat?.Call("API_RegisterThirdPartyTitle", new object[] { this, new Func<IPlayer, string>(GetClanTag) });
202
203 private string GetClanTag(IPlayer player) => playerClans.ContainsKey(player.Id) ? $"[{configData.MessageOptions.ClanTag}][{playerClans[player.Id]}][/#]" : string.Empty;
204 #endregion
205
206 #region Commands
207 [Command("clan")]
208 private void cmdClan(IPlayer player, string command, string[] args)
209 {
210 if (args == null || args.Length == 0)
211 {
212 Reply(player, "", $"{Title} v {Version}");
213 ReplyKey(player, Message("cMessHelp", player.Id), "/c <message>");
214 ReplyKey(player, Message("aMessHelp", player.Id), "/a <message>");
215 ReplyKey(player, Message("clanHelp", player.Id), "/clanhelp");
216 ReplyKey(player, Message("clanMembers", player.Id), "/clan members");
217 return;
218 }
219
220 switch (args[0].ToLower())
221 {
222 case "create":
223 CreateClan(player, args);
224 return;
225 case "join":
226 if (args.Length == 2)
227 JoinClan(player, args[1]);
228 else Reply(player, "/clan join <tag>");
229 return;
230 case "leave":
231 LeaveClan(player);
232 return;
233 case "invite":
234 if (args.Length >= 2)
235 InviteMember(player, args);
236 else Reply(player, "", Message("noName", player.Id));
237 return;
238 case "kick":
239 if (args.Length >= 2)
240 KickMember(player, args[1]);
241 else Reply(player, "", Message("noID", player.Id));
242 return;
243 case "members":
244 ShowMembers(player);
245 return;
246 case "promote":
247 if (args.Length >= 2)
248 PromoteMember(player, args[1]);
249 else Reply(player, "", Message("noID", player.Id));
250 return;
251 case "demote":
252 if (args.Length >= 2)
253 DemoteMember(player, args[1]);
254 else Reply(player, "", Message("noID", player.Id));
255 return;
256 case "disband":
257 DisbandClan(player);
258 return;
259 case "ally":
260 Alliance(player, args);
261 return;
262 default:
263 cmdClanHelp(player, "clanhelp", null);
264 break;
265 }
266 }
267
268 [Command("c")]
269 private void cmdClanChat(IPlayer player, string command, string[] args)
270 {
271 if (!PlayerHasClan(player.Id))
272 {
273 Reply(player, "", Message("noClanData", player.Id));
274 return;
275 }
276
277 string clanName = playerClans[player.Id];
278 if (!string.IsNullOrEmpty(clanName))
279 {
280 Clan clan = clanCache[clanName];
281 string str = string.Join(" ", args);
282
283 clan.Broadcast($"{player.Name} : {str}");
284 Interface.CallHook("OnClanChat", player, str);
285 return;
286 }
287 }
288
289 [Command("a")]
290 private void cmdAllianceChat(IPlayer player, string command, string[] args)
291 {
292 if (!PlayerHasClan(player.Id))
293 {
294 Reply(player, "", Message("noClanData", player.Id));
295 return;
296 }
297
298 string clanName = playerClans[player.Id];
299 if (!string.IsNullOrEmpty(clanName))
300 {
301 Clan clan = clanCache[clanName];
302 if (clan.clanAlliances.Count == 0)
303 Reply(player, "", Message("noClanAlly", player.Id));
304 else
305 {
306 foreach (string clanAllyName in clan.clanAlliances)
307 {
308 Clan clanAlly = clanCache[clanAllyName];
309 clanAlly.Broadcast($"{player.Name} : {string.Join(" ", args)}", clan.clanTag);
310 }
311 clan.Broadcast($"{player.Name} : {string.Join(" ", args)}", clan.clanTag);
312 }
313 }
314 }
315
316 [Command("clanhelp")]
317 private void cmdClanHelp(IPlayer player, string command, string[] args)
318 {
319 if (args == null || args.Length == 0)
320 {
321 Reply(player, "", Message("comHelp", player.Id));
322 ReplyKey(player, Message("memHelp", player.Id), "/clanhelp member");
323 ReplyKey(player, Message("modHelp", player.Id), "/clanhelp moderator");
324 ReplyKey(player, Message("ownHelp", player.Id), "/clanhelp owner");
325 return;
326 }
327
328 switch (args[0].ToLower())
329 {
330 case "member":
331 Reply(player, "", Message("memCom", player.Id));
332 ReplyKey(player, Message("cMessHelp", player.Id), "/c <message>");
333 ReplyKey(player, Message("aMessHelp", player.Id), "/a <message>");
334 ReplyKey(player, Message("createHelp", player.Id), "/clan create <tag>");
335 ReplyKey(player, Message("joinHelp", player.Id), "/clan join <tag>");
336 ReplyKey(player, Message("leaveHelp", player.Id), "/clan leave");
337 return;
338 case "moderator":
339 Reply(player, "", Message("modCom", player.Id));
340 ReplyKey(player, Message("inviteHelp", player.Id), "/clan invite <playername>");
341 ReplyKey(player, Message("cancelHelp", player.Id), "/clan invite cancel <partialname/ID>");
342 ReplyKey(player, Message("kickHelp", player.Id), "/clan kick <partialname/ID>");
343 return;
344 case "owner":
345 Reply(player, "", Message("ownerCom", player.Id));
346 ReplyKey(player, Message("promoteHelp", player.Id), "/clan promote <playername>");
347 ReplyKey(player, Message("demoteHelp", player.Id), "/clan demote <playername>");
348 ReplyKey(player, Message("disbandHelp", player.Id), "/clan disband");
349 ReplyKey(player, Message("allyReqHelp", player.Id), "/clan ally request <clantag>");
350 ReplyKey(player, Message("allyAccHelp", player.Id), "/clan ally accept <clantag>");
351 ReplyKey(player, Message("allyDecHelp", player.Id), "/clan ally decline <clantag>");
352 ReplyKey(player, Message("allyCanHelp", player.Id), "/clan ally cancel <clantag>");
353 return;
354 default:
355 break;
356 }
357 }
358 #endregion
359
360 #region Command Functions
361 public void CreateClan(IPlayer player, string[] args)
362 {
363 if (args.Length != 2)
364 Reply(player, "", "/clan create <tag>");
365 else
366 {
367 if (PlayerHasClan(player.Id))
368 {
369 Reply(player, "", Message("alreadyMember", player.Id));
370 return;
371 }
372
373 string tag = new string(args[1].Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray());
374 if (tag.Length < configData.Settings.TagMinimum || tag.Length > configData.Settings.TagMaximum)
375 {
376 Reply(player, "", string.Format(Message("tagForm1", player.Id), configData.Settings.TagMinimum, configData.Settings.TagMaximum));
377 return;
378 }
379
380 if (clanCache.ContainsKey(tag))
381 Reply(player, tag, Message("clanExists", player.Id));
382 else
383 {
384 Clan newClan = new Clan().CreateNewClan(tag, player.Id, player.Name);
385 clanCache.Add(tag, newClan);
386 playerClans.Add(player.Id, tag);
387 Reply(player, tag, Message("createSucc", player.Id));
388 }
389 }
390 }
391
392 public void JoinClan(IPlayer player, string tag)
393 {
394 if (PlayerHasClan(player.Id))
395 {
396 Reply(player, "", Message("alreadyMember", player.Id));
397 return;
398 }
399
400 if (!clanCache.ContainsKey(tag))
401 Reply(player, tag, Message("noFindClan", player.Id));
402 else
403 {
404 Clan clan = clanCache[tag];
405 if (!clan.invitedPlayers.ContainsKey(player.Id))
406 Reply(player, tag, Message("noInvite", player.Id));
407 else
408 {
409 if (configData.ClanLimits.Members != 0 && clan.members.Count >= configData.ClanLimits.Members)
410 {
411 Reply(player, "", Message("configData.ClanLimits.Members", player.Id));
412 clan.invitedPlayers.Remove(player.Id);
413 return;
414 }
415
416 clan.Broadcast(string.Format(Message("hasJoined"), player.Name));
417 clan.members.Add(player.Id, player.Name);
418 clan.invitedPlayers.Remove(player.Id);
419 playerClans.Add(player.Id, clan.clanTag);
420 Reply(player, tag, Message("joinSucc", player.Id));
421 ClanUpdate(clan.clanTag);
422 }
423 }
424 }
425
426 public void LeaveClan(IPlayer player)
427 {
428 if (PlayerHasClan(player.Id))
429 {
430 Clan clan = clanCache[playerClans[player.Id]];
431 if (clan.IsOwner(player.Id))
432 {
433 clan.RemoveOwner(player);
434 return;
435 }
436 if (clan.IsModerator(player.Id))
437 clan.moderators.Remove(player.Id);
438
439 if (clan.IsMember(player.Id))
440 clan.RemoveUser(player.Id, ref clan.members);
441
442 Reply(player, clan.clanTag, Message("leaveSucc", player.Id));
443 ClanUpdate(clan.clanTag);
444 }
445 else Reply(player, "", Message("notInClan", player.Id));
446 }
447
448 public void InviteMember(IPlayer player, string[] args)
449 {
450 if (!PlayerHasClan(player.Id))
451 Reply(player, "", Message("notInClan", player.Id));
452 else
453 {
454 Clan clan = clanCache[playerClans[player.Id]];
455 if (!clan.IsOwner(player.Id) && !clan.IsModerator(player.Id))
456 {
457 Reply(player, "", Message("noInvPerm", player.Id));
458 return;
459 }
460
461 if (configData.ClanLimits.Members != 0 && clan.members.Count >= configData.ClanLimits.Members)
462 {
463 Reply(player, "", Message("invMemberLimit", player.Id));
464 return;
465 }
466
467 if (args[1].ToLower() == "cancel")
468 {
469 string targetName = clan.FindPlayer(args[2], clan.invitedPlayers, false);
470 if (string.IsNullOrEmpty(targetName))
471 {
472 Reply(player, args[2], Message("noPlayerInv", player.Id));
473 return;
474 }
475 else if (clan.RemoveUser(targetName, ref clan.invitedPlayers))
476 ReplyKey(player, Message("invCancelled", player.Id), targetName);
477 return;
478 }
479
480 IPlayer target = FindPlayer(player, args[1]);
481 if (target != null)
482 {
483 if (PlayerHasClan(target.Id))
484 {
485 ReplyKey(player, Message("playerInClan", player.Id), target.Name);
486 return;
487 }
488
489 if (clan.invitedPlayers.ContainsKey(target.Id))
490 {
491 ReplyKey(player, Message("alreadyInvited", player.Id), target.Name);
492 return;
493 }
494
495 clan.invitedPlayers.Add(target.Id, target.Name);
496 Reply(target, clan.clanTag, Message("clanInv"));
497 ReplyKey(player, "You have invited {0} to join your clan", target.Name);
498 ClanUpdate(clan.clanTag);
499 return;
500 }
501 }
502 }
503
504 public object KickMember(IPlayer player, string targetplayer)
505 {
506 if (!PlayerHasClan(player.Id))
507 Reply(player, "", Message("notInClan", player.Id));
508 else
509 {
510 Clan clan = clanCache[playerClans[player.Id]];
511 if (!clan.IsOwner(player.Id) && !clan.IsModerator(player.Id))
512 {
513 Reply(player, "", Message("noKickPerm", player.Id));
514 return null;
515 }
516
517 string target = clan.FindPlayer(targetplayer, clan.members, true);
518 if (string.IsNullOrEmpty(target))
519 {
520 Reply(player, "", Message("noClanMember", player.Id));
521 return null;
522 }
523
524 string targetName = clan.members[target];
525 if (clan.IsOwner(target))
526 {
527 Reply(player, "", Message("noKickOwner", player.Id));
528 return null;
529 }
530
531 if (!clan.IsOwner(player.Id) && clan.IsModerator(target))
532 {
533 Reply(player, "", Message("noKickMod", player.Id));
534 return null;
535 }
536
537 if (target == player.Id)
538 {
539 Reply(player, "", Message("noKickSelf", player.Id));
540 return null;
541 }
542
543 if (!clan.RemoveUser(target, ref clan.members))
544 ReplyKey(player, Message("kickError", player.Id), targetName);
545 else
546 {
547 if (clan.IsModerator(target))
548 clan.moderators.Remove(target);
549
550 playerClans.Remove(target);
551
552 Reply(player, string.Format(Message("kickSucc", player.Id), targetName));
553 ClanUpdate(clan.clanTag);
554
555 IPlayer targetPlayer = players.FindPlayer(target);
556 if (targetPlayer != null && targetPlayer.IsConnected)
557 Reply(targetPlayer, clan.clanTag, Message("kicked"));
558
559 return true;
560 }
561 }
562 return null;
563 }
564
565 public object PromoteMember(IPlayer player, string targetplayer)
566 {
567 if (!PlayerHasClan(player.Id))
568 Reply(player, "", Message("notInClan", player.Id));
569 else
570 {
571 Clan clan = clanCache[playerClans[player.Id]];
572 if (clan == null)
573 {
574 Reply(player, "", Message("noClanData", player.Id));
575 return null;
576 }
577
578 if (!clan.IsOwner(player.Id))
579 {
580 Reply(player, "", Message("notOwnerProm", player.Id));
581 return null;
582 }
583
584 if (clan.moderators.Count != 0 && clan.moderators.Count >= configData.ClanLimits.Moderators)
585 {
586 Reply(player, "", Message("modLimit", player.Id));
587 return null;
588 }
589
590 string target = clan.FindPlayer(targetplayer, clan.members, true);
591 if (string.IsNullOrEmpty(target))
592 {
593 Reply(player, "", Message("noClanMember", player.Id));
594 return null;
595 }
596
597 string targetName = clan.members[target];
598 if (clan.IsModerator(target) || clan.IsOwner(target))
599 {
600 ReplyKey(player, Message("alreadyMod", player.Id), targetName);
601 return null;
602 }
603
604 clan.moderators.Add(target);
605
606 Reply(player, targetName, Message("promSucc", player.Id));
607 ClanUpdate(clan.clanTag);
608
609 IPlayer targetPlayer = players.FindPlayer(target);
610 if (targetPlayer != null && targetPlayer.IsConnected)
611 ReplyKey(targetPlayer, Message("beenProm", targetPlayer.Id), player.Name);
612
613 return true;
614 }
615 return null;
616 }
617
618 public object DemoteMember(IPlayer player, string targetplayer)
619 {
620 if (!PlayerHasClan(player.Id))
621 Reply(player, "", Message("notInClan", player.Id));
622 else
623 {
624 Clan clan = clanCache[playerClans[player.Id]];
625
626 string target = clan.FindPlayer(targetplayer, clan.members, true);
627 if (string.IsNullOrEmpty(target))
628 {
629 Reply(player, "", Message("noClanMember", player.Id));
630 return null;
631 }
632
633 string targetName = clan.members[target];
634
635 if (clan.IsModerator(target) && clan.IsOwner(player.Id))
636 {
637 clan.moderators.Remove(target);
638 Reply(player, string.Format(Message("demSucc", player.Id), targetName));
639 ClanUpdate(clan.clanTag);
640
641 IPlayer targetPlayer = players.FindPlayer(target);
642 if (targetPlayer != null && targetPlayer.IsConnected)
643 ReplyKey(targetPlayer, Message("beenDem", targetPlayer.Id), player.Name);
644
645 return true;
646 }
647 else ReplyKey(player, Message("notMod", player.Id), targetName);
648 }
649 return null;
650 }
651
652 public void DisbandClan(IPlayer player)
653 {
654 if (!PlayerHasClan(player.Id))
655 Reply(player, "", Message("notInClan", player.Id));
656 else
657 {
658 Clan clan = clanCache[playerClans[player.Id]];
659 if (!clan.IsOwner(player.Id))
660 Reply(player, "", Message("notOwnerDisb", player.Id));
661 else
662 {
663 foreach (KeyValuePair<string, string> member in clan.members)
664 {
665 if (member.Key != player.Id)
666 {
667 IPlayer targetPlayer = players.FindPlayer(member.Key);
668 if (targetPlayer != null && targetPlayer.IsConnected)
669 ReplyKey(targetPlayer, Message("beenDisb", targetPlayer.Id), player.Name);
670 }
671 playerClans.Remove(member.Key);
672 }
673 ClanDestroy(clan.clanTag);
674 clanCache.Remove(clan.clanTag);
675 Reply(player, "", Message("disbSucc", player.Id));
676 }
677 }
678 }
679
680 public void ShowMembers(IPlayer player)
681 {
682 if (!PlayerHasClan(player.Id))
683 Reply(player, "", Message("notInClan", player.Id));
684 else
685 {
686 Clan clan = clanCache[playerClans[player.Id]];
687 string returnString = $"<color={configData.MessageOptions.ClanChat}>{Message("clanmembers", player.Id)}</color>:\n";
688 int i = 1;
689 foreach (KeyValuePair<string, string> member in clan.members)
690 {
691 returnString += $"{(clan.IsOwner(member.Key) ? $"{Message("owner", player.Id)} - " : clan.IsModerator(member.Key) ? $"{Message("moderator", player.Id)} - " : $"{Message("member", player.Id)} - ")}{member.Value} - {((covalence.Players.FindPlayerById(member.Key)?.IsConnected ?? false) ? $"<color=#3CD751>{Message("online", player.Id)}" : $"<color=#d85540>{Message("offline", player.Id)}")}</color>{(i < clan.members.Count ? "\n" : "")}";
692 i++;
693 }
694 Reply(player, returnString);
695 }
696 }
697
698 public void Alliance(IPlayer player, string[] args)
699 {
700 if (!PlayerHasClan(player.Id))
701 Reply(player, "", Message("notInClan", player.Id));
702 else
703 {
704 Clan clan = clanCache[playerClans[player.Id]];
705 if (!clan.IsOwner(player.Id))
706 Reply(player, "", Message("ownerAlly", player.Id));
707 else
708 {
709 if (args.Length != 3)
710 Reply(player, "Syntax:\n/clan ally request <clantag>\n/clan ally accept <clantag>");
711 else
712 {
713 if (!clanCache.ContainsKey(args[2]))
714 ReplyKey(player, Message("clanNoExist", player.Id), args[2]);
715 else
716 {
717 Clan targetClan = clanCache[args[2]];
718
719 switch (args[1].ToLower())
720 {
721 case "request":
722 if (configData.ClanLimits.Alliances != 0 && clan.clanAlliances.Count >= configData.ClanLimits.Alliances)
723 {
724 Reply(player, "", Message("configData.ClanLimits.Alliances", player.Id));
725 return;
726 }
727
728 if (clan.invitedAllies.Contains(targetClan.clanTag))
729 {
730 Reply(player, args[2], Message("invitePending", player.Id));
731 return;
732 }
733
734 if (clan.clanAlliances.Contains(targetClan.clanTag))
735 {
736 Reply(player, args[2], Message("alreadyAllies", player.Id));
737 return;
738 }
739 else
740 {
741 targetClan.pendingInvites.Add(clan.clanTag);
742 clan.invitedAllies.Add(targetClan.clanTag);
743
744 Reply(player, targetClan.clanTag, Message("allyReq", player.Id));
745 ClanUpdate(clan.clanTag);
746
747 IPlayer targetOwner = players.FindPlayer(targetClan.ownerID);
748 if (targetOwner != null && targetOwner.IsConnected)
749 ReplyKey(targetOwner, Message("reqAlliance", targetOwner.Id), clan.clanTag);
750 return;
751 }
752
753 case "accept":
754 if (clan.pendingInvites.Contains(targetClan.clanTag))
755 {
756 if (configData.ClanLimits.Alliances != 0 && targetClan.clanAlliances.Count >= configData.ClanLimits.Alliances)
757 {
758 ReplyKey(player, Message("allyAccLimit", player.Id), targetClan.clanTag);
759 targetClan.invitedAllies.Remove(clan.clanTag);
760 clan.pendingInvites.Remove(targetClan.clanTag);
761 return;
762 }
763
764 targetClan.invitedAllies.Remove(clan.clanTag);
765 targetClan.clanAlliances.Add(clan.clanTag);
766 clan.pendingInvites.Remove(targetClan.clanTag);
767 clan.clanAlliances.Add(targetClan.clanTag);
768
769 Reply(player, targetClan.clanTag, Message("allyAcc", player.Id));
770 ClanUpdate(clan.clanTag);
771
772 IPlayer targetOwner = players.FindPlayer(targetClan.ownerID);
773 if (targetOwner != null && targetOwner.IsConnected)
774 ReplyKey(targetOwner, Message("allyAccSucc", targetOwner.Id), clan.clanTag);
775 }
776 else Reply(player, args[2], Message("noAllyInv", player.Id));
777
778 return;
779 case "decline":
780 if (clan.pendingInvites.Contains(targetClan.clanTag))
781 {
782 targetClan.invitedAllies.Remove(clan.clanTag);
783 Reply(player, targetClan.clanTag, Message("allyDec", player.Id));
784 ClanUpdate(clan.clanTag);
785
786 IPlayer targetOwner = players.FindPlayer(targetClan.ownerID);
787 if (targetOwner != null && targetOwner.IsConnected)
788 ReplyKey(targetOwner, Message("allyDecSucc", targetOwner.Id), clan.clanTag);
789 }
790 else Reply(player, args[2], Message("noAllyInv", player.Id));
791 return;
792
793 case "cancel":
794 if (clan.clanAlliances.Contains(args[2]))
795 {
796 targetClan.clanAlliances.Remove(clan.clanTag);
797 clan.clanAlliances.Remove(clan.clanTag);
798 Reply(player, targetClan.clanTag, Message("allyCan", player.Id));
799 ClanUpdate(clan.clanTag);
800
801 IPlayer targetOwner = players.FindPlayer(targetClan.ownerID);
802 if (targetOwner != null && targetOwner.IsConnected)
803 ReplyKey(targetOwner, Message("allyCanSucc", targetOwner.Id), clan.clanTag);
804 }
805 else if (clan.invitedAllies.Contains(args[2]))
806 {
807 targetClan.pendingInvites.Remove(clan.clanTag);
808 clan.invitedAllies.Remove(clan.clanTag);
809 Reply(player, targetClan.clanTag, Message("allyInvCan", player.Id));
810 ClanUpdate(clan.clanTag);
811
812 IPlayer targetOwner = players.FindPlayer(targetClan.ownerID);
813 if (targetOwner != null && targetOwner.IsConnected)
814 ReplyKey(targetOwner, Message("allyInvCan", targetOwner.Id), clan.clanTag);
815 }
816 else Reply(player, args[2], Message("noAlly", player.Id));
817 return;
818
819 default:
820 Reply(player, "Syntax:\n/clan ally request <clantag>\n/clan ally accept <clantag>\n/clan ally decline <clantag>\n/clan ally cancel <clantag>");
821 return;
822 }
823 }
824 }
825 }
826 }
827 }
828 #endregion
829
830 #region API
831 private JObject GetClan(string tag)
832 {
833 Clan clan = FindClanByTag(tag);
834 if (clan == null)
835 return null;
836 return clan.ToJObject();
837 }
838
839 private JArray GetAllClans() => new JArray(clanCache.Keys);
840
841 private string GetClanOf(object player)
842 {
843 if (player == null)
844 throw new ArgumentException("player");
845
846 if (player is ulong)
847 player = player.ToString();
848
849 else if (player is IPlayer)
850 player = (player as IPlayer).Id;
851 #if RUST
852 else if (player is BasePlayer)
853 player = (player as BasePlayer).UserIDString;
854 #endif
855 if (!(player is string))
856 throw new ArgumentException("player");
857
858 Clan clan = FindClanByID((string)player);
859 if (clan == null)
860 return null;
861
862 return clan.clanTag;
863 }
864
865 private void ClanCreate(string tag) => Interface.CallHook("OnClanCreate", tag);
866
867 private void ClanUpdate(string tag) => Interface.CallHook("OnClanUpdate", tag);
868
869 private void ClanDestroy(string tag) => Interface.CallHook("OnClanDestroy", tag);
870
871 private bool IsClanMember(string playerId, string otherId)
872 {
873 Clan playerClan = FindClanByID(playerId);
874 if (playerClan == null)
875 return false;
876
877 Clan otherClan = FindClanByID(otherId);
878 if (otherClan == null)
879 return false;
880
881 if (playerClan.clanTag != otherClan.clanTag)
882 return false;
883
884 return true;
885 }
886
887 private bool IsMemberOrAlly(string playerId, string otherId)
888 {
889 Clan playerClan = FindClanByID(playerId);
890 if (playerClan == null)
891 return false;
892
893 Clan otherClan = FindClanByID(otherId);
894 if (otherClan == null)
895 return false;
896
897 if ((playerClan.clanTag == otherClan.clanTag) || playerClan.clanAlliances.Contains(otherClan.clanTag))
898 return true;
899
900 return false;
901 }
902
903 private bool IsAllyPlayer(string playerId, string otherId)
904 {
905 Clan playerClan = FindClanByID(playerId);
906 if (playerClan == null)
907 return false;
908
909 Clan otherClan = FindClanByID(otherId);
910 if (otherClan == null)
911 return false;
912
913 if (playerClan.clanAlliances.Contains(otherClan.clanTag))
914 return true;
915
916 return false;
917 }
918 #endregion
919
920 #region Config
921 private static ConfigData configData;
922
923 private class ConfigData
924 {
925 [JsonProperty(PropertyName = "Limitations")]
926 public ClanLimit ClanLimits { get; set; }
927 [JsonProperty(PropertyName = "Message colors")]
928 public Messaging MessageOptions { get; set; }
929 [JsonProperty(PropertyName = "Settings")]
930 public Options Settings { get; set; }
931
932 public class Messaging
933 {
934 [JsonProperty(PropertyName = "Clan tag color")]
935 public string ClanTag { get; set; }
936 [JsonProperty(PropertyName = "Clan and Alliance chat color")]
937 public string ClanChat { get; set; }
938 [JsonProperty(PropertyName = "Highlight color")]
939 public string Main { get; set; }
940 [JsonProperty(PropertyName = "Message color")]
941 public string MSG { get; set; }
942 }
943 public class ClanLimit
944 {
945 [JsonProperty(PropertyName = "Maximum clan member count")]
946 public int Members { get; set; }
947 [JsonProperty(PropertyName = "Maximum clan moderator count")]
948 public int Moderators { get; set; }
949 [JsonProperty(PropertyName = "Maximum clan alliance count")]
950 public int Alliances { get; set; }
951 }
952 public class Options
953 {
954 [JsonProperty(PropertyName = "Minimum clan tag characters")]
955 public int TagMinimum { get; set; }
956 [JsonProperty(PropertyName = "Maximum clan tag characters")]
957 public int TagMaximum { get; set; }
958 [JsonProperty(PropertyName = "Show clan member connection message")]
959 public bool ShowJoinMessage { get; set; }
960 [JsonProperty(PropertyName = "Show clan member disconnection message")]
961 public bool ShowLeaveMessage { get; set; }
962 [JsonProperty(PropertyName = "Data save timer (seconds)")]
963 public int SaveTimer { get; set; }
964 }
965 }
966
967 private void LoadVariables()
968 {
969 LoadConfigVariables();
970 SaveConfig();
971 }
972
973 protected override void LoadDefaultConfig()
974 {
975 ConfigData config = new ConfigData
976 {
977 ClanLimits = new ConfigData.ClanLimit
978 {
979 Members = 8,
980 Moderators = 2,
981 Alliances = 2
982 },
983 MessageOptions = new ConfigData.Messaging
984 {
985 ClanTag = "#783CD7",
986 ClanChat = "#3999D5",
987 Main = "#D85540",
988 MSG = "#D8D8D8"
989 },
990 Settings = new ConfigData.Options
991 {
992 TagMaximum = 6,
993 TagMinimum = 2,
994 ShowJoinMessage = true,
995 ShowLeaveMessage = true,
996 SaveTimer = 600,
997 }
998 };
999 SaveConfig(config);
1000 }
1001
1002 private void LoadConfigVariables() => configData = Config.ReadObject<ConfigData>();
1003
1004 private void SaveConfig(ConfigData config) => Config.WriteObject(config, true);
1005 #endregion
1006
1007 #region Data Management
1008 private void SaveLoop() => timer.Once(configData.Settings.SaveTimer * 60, () => { SaveData(); SaveLoop(); });
1009
1010 private void SaveData()
1011 {
1012 clanData = clanCache;
1013 data.WriteObject(clanData);
1014 }
1015
1016 private void LoadData()
1017 {
1018 try
1019 {
1020 clanData = data.ReadObject<Dictionary<string, Clan>>();
1021 clanCache = clanData;
1022 }
1023 catch
1024 {
1025 clanData = new Dictionary<string, Clan>();
1026 }
1027 }
1028
1029 public class Clan
1030 {
1031 public string clanTag = string.Empty;
1032 public string ownerID = string.Empty;
1033
1034 public List<string> moderators = new List<string>();
1035 public Dictionary<string, string> members = new Dictionary<string, string>();
1036 public List<string> clanAlliances = new List<string>();
1037
1038 public Dictionary<string, string> invitedPlayers = new Dictionary<string, string>();
1039 public List<string> invitedAllies = new List<string>();
1040 public List<string> pendingInvites = new List<string>();
1041
1042 public Clan CreateNewClan(string clanTag, string ownerID, string ownerName)
1043 {
1044 this.clanTag = clanTag;
1045 this.ownerID = ownerID;
1046 members.Add(ownerID, ownerName);
1047 Instance.ClanCreate(clanTag);
1048 return this;
1049 }
1050
1051 public bool IsOwner(string ID) => ownerID == ID;
1052
1053 public bool IsModerator(string ID) => moderators.Contains(ID);
1054
1055 public bool IsMember(string ID) => members.ContainsKey(ID);
1056
1057 public bool IsInvited(string ID) => invitedPlayers.ContainsKey(ID);
1058
1059 public void RemoveOwner(IPlayer player)
1060 {
1061 RemoveUser(ownerID, ref members);
1062 string newOwner = null;
1063
1064 if (moderators.Count > 0)
1065 newOwner = moderators.First();
1066 else if (members.Count > 0)
1067 newOwner = members.First().Key;
1068
1069 if (!string.IsNullOrEmpty(newOwner))
1070 {
1071 ownerID = newOwner;
1072 IPlayer target = Instance.players.FindPlayer(newOwner);
1073 if (target != null && target.IsConnected)
1074 Instance.Reply(target, Instance.Message("ownerProm", target.Id));
1075
1076 Instance.Reply(player, clanTag, Instance.Message("leaveSucc", target.Id));
1077 Instance.ClanUpdate(clanTag);
1078 }
1079 else
1080 {
1081 Instance.ReplyKey(player, Instance.Message("clanDestroy", player.Id), clanTag);
1082 Instance.clanCache.Remove(clanTag);
1083 Instance.playerClans.Remove(player.Id);
1084 Instance.ClanDestroy(clanTag);
1085 }
1086 }
1087
1088 public bool RemoveUser(string IDName, ref Dictionary<string, string> targetDict)
1089 {
1090 if (targetDict.ContainsKey(IDName))
1091 {
1092 targetDict.Remove(IDName);
1093 Instance.playerClans.Remove(IDName);
1094 return true;
1095 }
1096
1097 if (targetDict.ContainsValue(IDName))
1098 {
1099 string player = targetDict.FirstOrDefault(x => x.Value == IDName).Key;
1100 targetDict.Remove(player);
1101 Instance.playerClans.Remove(player);
1102 return true;
1103 }
1104 else
1105 {
1106 foreach (KeyValuePair<string, string> player in targetDict)
1107 {
1108 if (player.Value.Contains(IDName))
1109 {
1110 targetDict.Remove(player.Key);
1111 Instance.playerClans.Remove(player.Key);
1112 return true;
1113 }
1114 }
1115 }
1116 return false;
1117 }
1118
1119 public string FindPlayer(string IDName, Dictionary<string, string> targetDict, bool ID)
1120 {
1121 if (targetDict.ContainsKey(IDName))
1122 {
1123 if (ID)
1124 return IDName;
1125 else return targetDict[IDName];
1126 }
1127 else if (targetDict.ContainsValue(IDName))
1128 {
1129 if (ID)
1130 return targetDict.FirstOrDefault(x => x.Value == IDName).Key;
1131 else return IDName;
1132 }
1133 else
1134 {
1135 foreach (KeyValuePair<string, string> player in targetDict)
1136 {
1137 if (player.Value.Contains(IDName))
1138 {
1139 if (ID)
1140 return player.Key;
1141 else return player.Value;
1142 }
1143 }
1144 }
1145 return null;
1146 }
1147
1148 public void Broadcast(string message, string sender = "Clan")
1149 {
1150 foreach (KeyValuePair<string, string> member in members)
1151 {
1152 IPlayer target = Instance.players.FindPlayer(member.Value);
1153 if (target != null && target.IsConnected)
1154 target.Reply($"<color={configData.MessageOptions.ClanChat}>[{sender}]</color> :<color={configData.MessageOptions.MSG}> {message}</color>");
1155 }
1156 }
1157
1158 internal JObject ToJObject()
1159 {
1160 JObject obj = new JObject();
1161 obj["tag"] = clanTag;
1162 obj["owner"] = ownerID;
1163
1164 JArray jmoderators = new JArray();
1165 foreach (string moderator in moderators)
1166 jmoderators.Add(moderator);
1167 obj["moderators"] = jmoderators;
1168
1169 JArray jmembers = new JArray();
1170 foreach (KeyValuePair<string, string> member in members)
1171 jmembers.Add(member.Key);
1172 obj["members"] = jmembers;
1173
1174 JArray jinvited = new JArray();
1175 foreach (KeyValuePair<string, string> invite in invitedPlayers)
1176 jinvited.Add(invite.Key);
1177 obj["invited"] = jinvited;
1178
1179 JArray jallies = new JArray();
1180 foreach (string ally in clanAlliances)
1181 jallies.Add(ally);
1182 obj["allies"] = jallies;
1183
1184 JArray jinvallies = new JArray();
1185 foreach (string ally in invitedAllies)
1186 jinvallies.Add(ally);
1187 obj["invitedallies"] = jinvallies;
1188
1189 return obj;
1190 }
1191 }
1192
1193 #endregion
1194
1195 #region Messaging
1196 private string Message(string key, string id = null) => lang.GetMessage(key, this, id);
1197
1198 private Dictionary<string, string> Messages = new Dictionary<string, string>
1199 {
1200 {"playerCon", "{0} has connected to the server" },
1201 {"outstandingMsgs", "You have {0} outstanding messages. Type /cmessage to view them" },
1202 {"playerDiscon", "{0} has disconnected from the server"},
1203 {"cMessHelp", "{0} - Sends a message to all your clan members"},
1204 {"aMessHelp", "{0} - Sends a message to all your allied clan members"},
1205 {"createHelp", "{0} - Creates a new clan"},
1206 {"joinHelp", "{0} - Joins a clan you have been invited to"},
1207 {"leaveHelp", "{0} - Leaves your current clan"},
1208 {"modCom", "Moderator Commands:"},
1209 {"inviteHelp", "{0} - Invites a player to your clan"},
1210 {"cancelHelp", "{0} - Cancel a players invite to your clan"},
1211 {"kickHelp", "{0} - Kicks a player from your clan"},
1212 {"ownerCom", "Owner Commands:"},
1213 {"promoteHelp", "{0} - Promotes a member to clan moderator"},
1214 {"demoteHelp", "{0} - Demotes a clan moderator to member"},
1215 {"disbandHelp", "{0} - Disbands your clan" },
1216 {"allyReqHelp", "{0} - Offer an alliance to another clan"},
1217 {"allyAccHelp", "{0} - Accept an alliance from another clan"},
1218 {"allyDecHelp", "{0} - Decline an alliance from another clan"},
1219 {"allyCanHelp", "{0} - Cancel an alliance with another clan"},
1220 {"tagForm1", "You clan tag must be between {0} and {1} characters, and must not contain any symbols"},
1221 {"createSucc", "You have successfully created a new clan with the tag:"},
1222 {"clanExists", "A clan already exists with the tag:"},
1223 {"alreadyMember", "You are already in a clan"},
1224 {"configData.ClanLimits.Members", "You can not join this clan as it has already reached its member limit"},
1225 {"joinSucc", "You have joined the clan:"},
1226 {"noInvite", "You do not have a pending invite from the clan:"},
1227 {"noFindClan", "Unable to find a clan with the tag:"},
1228 {"leaveSucc", "You have left the clan:"},
1229 {"notInClan", "You are not currently in a clan"},
1230 {"noInvPerm", "You do not have permission to invite players"},
1231 {"invMemberLimit", "You can not invite any more players to join your clan as it has reached its member limit"},
1232 {"noPlayerInv", "Unable to find a invite for player:"},
1233 {"invCancelled", "You have cancelled {0}'s clan invite"},
1234 {"playerInClan", "{0} is already in a clan"},
1235 {"alreadyInvited", "{0} already has a invitation to join your clan" },
1236 {"clanInv", "You have been invited to join the clan:"},
1237 {"noPlayerName", "Unable to find a player with that name"},
1238 {"noname", "You must enter a player's name"},
1239 {"nokickPerm", "You do not have permission to kick players"},
1240 {"noKickOwner", "You can not kick the clan owner"},
1241 {"noKickMod", "Only owners can kick moderators"},
1242 {"noKickSelf", "You can not kick yourself..."},
1243 {"kickSucc", "You have successfully kicked {0} from your clan"},
1244 {"kicked", "You have been kicked from the clan:"},
1245 {"kickError", "Error whilst removing {0} from your clan"},
1246 {"noClanMember", "Unable to find a clan member with that name"},
1247 {"noID", "You must enter a player's name or ID"},
1248 {"modLimit", "You can not assign any more moderators as you already have the maximum allowed amount"},
1249 {"alreadyMod", "{0} is already promoted"},
1250 {"promSucc", "You have successfully promoted"},
1251 {"beenProm", "{0} has promoted you to moderator"},
1252 {"demSucc", "You have successfully demoted {0}"},
1253 {"beenDem", "{0} has demoted you to member"},
1254 {"notMod", "{0} is not a clan moderator"},
1255 {"beenDisb", "{0} has disbanded your clan"},
1256 {"disbSucc", "You have successfully disbanded your clan"},
1257 {"notOwnerDisb", "Only the owner can disband a clan"},
1258 {"notOwnerProm", "Only the owner can promote clan members"},
1259 {"configData.ClanLimits.Alliances", "You can not request any more alliances as you already have the maximum allowed amount"},
1260 {"alreadyAllies", "You are already allies with"},
1261 {"allyReq", "You have requested a clan alliance from"},
1262 {"reqAlliance", "{0} has requested a clan alliance"},
1263 {"invitePending", "You already have a pending alliance invite for"},
1264 {"clanNoExist", "The clan {0} does not exist"},
1265 {"allyAccLimit", "You can not accept this clan alliance as {0} has already have the maximum allowed amount"},
1266 {"allyAcc", "You have accepted the clan alliance from"},
1267 {"allyAccSucc", "{0} has accepted your alliance request"},
1268 {"noAllyInv", "You do not have a alliance invite from"},
1269 {"allyDec", "You have declined the clan alliance from"},
1270 {"allyDecSucc", "{0} has declined your alliance request"},
1271 {"allyCan", "You have cancelled your alliance with"},
1272 {"allyCanSucc", "{0} has cancelled your clan alliance"},
1273 {"allyInvCan", "You have withdrawn your clan alliance invitation"},
1274 {"allyInvCanSucc", "{0} has withdrawn your clan alliance invitation"},
1275 {"noAlly", "You do not have a alliance with"},
1276 {"ownerAlly", "Only the clan owner can form alliances"},
1277 {"noClanData", "Unable to find your clan data or you are not a clan member"},
1278 {"noClanAlly", "You do not have any clan alliances"},
1279 {"cleardMsg", "You have cleared all outstanding messages"},
1280 {"clearSyn", "You can clear these messages by typing \"/cmessage clear\""},
1281 {"noOM", "You do not have any outstanding messages"},
1282 {"ownerProm", "You have been promoted to clan owner"},
1283 {"clanDestroy", "You have left the clan: {0} and it has been removed due to lack of members"},
1284 {"memCom", "Member Commands:" },
1285 {"comHelp", "Clans Help:" },
1286 {"clanHelp", "{0} - Display Clan commands" },
1287 {"clanMembers", "{0} - Show online and offline clan members" },
1288 {"memHelp", "{0} - Display member commands"},
1289 {"modHelp", "{0} - Display moderator commands"},
1290 {"ownHelp", "{0} - Display owner commands"},
1291 {"hasJoined", "{0} has joined the clan" },
1292 {"noPlayers", "Unable to find a player with the name or ID" },
1293 {"multiPlayers", "Multiple players found with that name" },
1294 {"owner", "[Owner]" },
1295 {"moderator", "[Moderator]" },
1296 {"member", "[Member]" },
1297 {"online", "Online" },
1298 {"offline", "Offline" },
1299 {"clanmembers", "Clan Members" }
1300 };
1301 #endregion
1302 }
1303}