context: using System;
  using System.Collections.Generic;
  using System.Linq;
  
  using Server;
  using Server.Items;
  using Server.Mobiles;
  using Server.Gumps;
  using Server.Network;
  using Server.Guilds;
  using Server.Engines.Points;
  using Server.Factions;
  using Server.Engines.CityLoyalty;
  using Server.Regions;
  using Server.Accounting;
  using Server.Engines.ArenaSystem;
  
  namespace Server.Engines.VvV
  {
      public enum VvVType
      {
          Virtue, 
          Vice
      }
  
      public enum VvVCity
      {
          Britain,
          Jhelom,
          Minoc,
          Moonglow,
          Ocllo, 
          SkaraBrae,
          Trinsic,
          Yew
      }
  
      public class ViceVsVirtueSystem : PointsSystem
      {
          public static int VirtueHue = 2124;
          public static int ViceHue = 2118;
  
          public static bool Enabled = Config.Get("VvV.Enabled", true);
          public static int StartSilver = Config.Get("VvV.StartSilver", 2000);
          public static bool EnhancedRules = Config.Get("VvV.EnhancedRules", false);
  
          public static ViceVsVirtueSystem Instance { get; set; }
  
          public override TextDefinition Name { get { return new TextDefinition("Vice Vs Virtue"); } }
          public override PointsType Loyalty { get { return PointsType.ViceVsVirtue; } }
          public override bool AutoAdd { get { return false; } }
          public override double MaxPoints { get { return 10000; } }
  
          public bool HasGenerated { get; set; }
  
          public Dictionary<Guild, VvVGuildStats> GuildStats { get; set; }
          public static List<TemporaryCombatant> TempCombatants { get; set; }
  
          public List<VvVCity> ExemptCities { get; set; }
  
          public VvVBattle Battle { get; private set; }
  
          public override string ToString()
          {
              return "...";
          }
  
          public ViceVsVirtueSystem()
          {
              Instance = this;
              Battle = new VvVBattle(this);
  
              GuildStats = new Dictionary<Guild, VvVGuildStats>();
              ExemptCities = new List<VvVCity>();
          }
  
          public override void SendMessage(PlayerMobile from, double old, double points, bool quest)
          {
          }
  
          public override PointsEntry GetSystemEntry(PlayerMobile pm)
          {
              return new VvVPlayerEntry(pm);
          }
  
          public void HandlePlayerDeath(PlayerMobile victim)
          {
              VvVPlayerEntry ventry = GetPlayerEntry<VvVPlayerEntry>(victim);
  
              if (ventry != null && ventry.Active)
              {
                  List<DamageEntry> list = victim.DamageEntries.OrderBy(d => -d.DamageGiven).ToList();
                  List<Mobile> handled = new List<Mobile>();
                  bool statloss = false;
  
                  for(int i = 0; i < list.Count; i++)
                  {
                      Mobile dam = list[i].Damager;
  
                      if (dam == victim || dam == null)
                          continue;
  
                      if (dam is BaseCreature && ((BaseCreature)dam).GetMaster() is PlayerMobile)
                          dam = ((BaseCreature)dam).GetMaster();
  
                      bool isEnemy = IsEnemy(victim, dam);
  
                      if (isEnemy)
                      {
                          VvVPlayerEntry kentry = GetPlayerEntry<VvVPlayerEntry>(dam);
  
                          if (kentry != null && kentry.Active && !handled.Contains(dam))
                          {
                              if (Battle.IsInActiveBattle(dam, victim))
                              {
                                  if (i == 0)
                                      Battle.Update(ventry, kentry, UpdateType.Kill);
                                  else
                                      Battle.Update(ventry, kentry, UpdateType.Assist);
                              }
  
                              handled.Add(dam);
                              kentry.TotalKills++;
  
                              if (EnhancedRules && kentry != null)
                              {
                                  kentry.AwardSilver(victim);
                              }
                          }
  
                          if (!handled.Contains(victim))
                          {
                              ventry.TotalDeaths++;
                              handled.Add(victim);
                          }
                      }
  
                      if (!statloss && isEnemy)
                          statloss = true;
                  }
  
                  if (statloss)
                      Faction.ApplySkillLoss(victim);
  
                  ColUtility.Free(list);
                  ColUtility.Free(handled);
              }
          }
  
          public void AddPlayer(PlayerMobile pm)
          {
              if (pm == null || pm.Guild == null)
                  return;
  
              Guild g = pm.Guild as Guild;
              VvVPlayerEntry entry = GetEntry(pm, true) as VvVPlayerEntry;
  
              if (!entry.Active)
                  entry.Active = true;
  
              pm.SendLocalizedMessage(1155564); // You have joined Vice vs Virtue!
              pm.SendLocalizedMessage(1063156, g.Name); // The guild information for ~1_val~ has been updated.
  
              pm.Delta(MobileDelta.Noto);
              pm.ProcessDelta();
  
              CheckBattleStatus(pm);
          }
  
          public bool IsResigning(PlayerMobile pm, Guild g)
          {
              VvVPlayerEntry entry = GetPlayerEntry<VvVPlayerEntry>(pm);
  
              return entry != null && entry.Guild == g && entry.Resigning;
          }
  
          public void OnResign(Mobile m, bool quitguild = false)
          {
              VvVPlayerEntry entry = GetPlayerEntry<VvVPlayerEntry>(m as PlayerMobile);
  
              if (entry != null && entry.Active && !entry.Resigning)
              {
                  if (m.AccessLevel == AccessLevel.Player)
                      entry.ResignExpiration = DateTime.UtcNow + TimeSpan.FromDays(3);
                  else
                      entry.ResignExpiration = DateTime.UtcNow + TimeSpan.FromMinutes(1);
  
                  if (quitguild)
                      m.SendLocalizedMessage(1155580); // You have quit a guild while participating in Vice vs Virtue.  You will be freely attackable by members of Vice vs Virtue until your resignation period has ended!
              }
          }
  
          public void CheckResignation(PlayerMobile pm)
          {
              VvVPlayerEntry entry = GetPlayerEntry<VvVPlayerEntry>(pm);
  
              if (entry != null && entry.Resigning && entry.ResignExpiration < DateTime.UtcNow)
              {
                  pm.PrivateOverheadMessage(MessageType.Regular, 1154, 1155561, pm.NetState); // You are no longer in Vice vs Virtue!
  
                  entry.Active = false;
                  entry.ResignExpiration = DateTime.MinValue;
                  pm.Delta(MobileDelta.Noto);
                  pm.ValidateEquipment();
              }
          }
  
          public void CheckBattleStatus()
          {
              if (Battle.OnGoing)
                  return;
  
              int count = EnemyGuildCount();
  
              if (count > 0)
              {
                  Battle.Begin();
              }
          }
  
          public void CheckBattleStatus(PlayerMobile pm)
          {
              if (!IsVvV(pm) || !Enabled)
                  return;
  
              if (Battle.OnGoing)
              {
                  SendVvVMessageTo(pm, 1154721, String.Format("#{0}", GetCityLocalization(Battle.City).ToString()));
                  // A Battle between Vice and Virtue is active! To Arms! The City of ~1_CITY~ is besieged!
  
                  if (Battle != null && Battle.IsInActiveBattle(pm))
                  {
                      Battle.CheckGump(pm);
                      Battle.CheckArrow(pm);
                  }
              }
              else
              {
                  int count = EnemyGuildCount();
  
                  if (count < 1)
                  {
                      SendVvVMessageTo(pm, 1154936); // More players are needed before a VvV battle can begin! 
                  }
                  else if (Battle.InCooldown)
                  {
                      SendVvVMessageTo(pm, 1154722); // A VvV battle has just concluded. The next battle will begin in less than five minutes!
                  }
                  else
                  {
                      Battle.Begin();
                  }
              }
          }
  
          public int EnemyGuildCount()
          {
              List<Guild> guilds = new List<Guild>();
  
              foreach (NetState ns in NetState.Instances)
              {
                  Mobile m = ns.Mobile;
  
                  if (m != null)
                  {
                      Guild g = m.Guild as Guild;
                      VvVPlayerEntry entry = GetPlayerEntry<VvVPlayerEntry>(m);
  
                      if (g != null && entry != null && entry.Guild == g && !guilds.Contains(g))
                      {
                          bool hasally = false;
  
                          foreach (Guild guild in guilds)
                          {
                              if (guild.IsAlly(g))
                              {
                                  hasally = true;
                                  break;
                              }
                          }
  
                          if(!hasally)
                              guilds.Add(g);
                      }
                  }
              }
  
              int count = guilds.Count;
              guilds.Clear();
              guilds.TrimExcess();
  
              return count - 1;
          }
  
          public void SendVvVMessage(string message)
          {
              foreach (NetState state in NetState.Instances.Where(st => st.Mobile != null && IsVvV(st.Mobile)))
              {
                  Mobile m = state.Mobile;
  
                  if (m != null)
                  {
                      m.SendMessage("[Guild][VvV] {0}", message);
                  }
              }
          }
  
          public void SendVvVMessage(int cliloc, string args = "")
          {
              foreach(NetState state in NetState.Instances.Where(st => st.Mobile != null && IsVvV(st.Mobile)))
              {
                  Mobile m = state.Mobile;
  
                  if(m != null)
                  {
                      SendVvVMessageTo(m, cliloc, args);
                  }
              }
          }
  
          public void SendVvVMessageTo(Mobile m, int cliloc, string args = "")
          {
              m.SendLocalizedMessage(cliloc, false, "[Guild][VvV] ", args, m is PlayerMobile ? ((PlayerMobile)m).GuildMessageHue : 0x34);
          }
  
          private List<Item> VvVItems = new List<Item>();
  
          public void AddVvVItem(Item item, bool initial = false)
          {
              if (ViceVsVirtueSystem.Enabled && item is IVvVItem)
              {
                  ((IVvVItem)item).IsVvVItem = true;
  
                  if (!VvVItems.Contains(item))
                  {
                      VvVItems.Add(item);
                  }
  
                  if (initial)
                  {
                      FactionEquipment.CheckProperties(item);
                  }
              }
          }
  
          public static void Initialize()
          {
              if (!Enabled)
                  return;
  
              EventSink.Login += OnLogin;
              EventSink.PlayerDeath += OnPlayerDeath;
  
              Server.Commands.CommandSystem.Register("BattleProps", AccessLevel.GameMaster, e =>
                  {
                      if(Instance.Battle != null)
                          e.Mobile.SendGump(new PropertiesGump(e.Mobile, Instance.Battle));
                  });
  
              Server.Commands.CommandSystem.Register("ForceStartBattle", AccessLevel.GameMaster, e =>
              {
                  if (Instance.Battle != null && !Instance.Battle.OnGoing)
                      Instance.Battle.Begin();
              });
  
              Server.Commands.CommandSystem.Register("ExemptCities", AccessLevel.Administrator, e =>
              {
                  e.Mobile.SendGump(new ExemptCitiesGump());
              });
  
              Server.Commands.CommandSystem.Register("VvVKick", AccessLevel.GameMaster, e =>
              {
                  e.Mobile.SendMessage("Target the person you'd like to remove from VvV.");
                  e.Mobile.BeginTarget(-1, false, Server.Targeting.TargetFlags.None, (from, targeted) =>
                      {
                          if (targeted is PlayerMobile)
                          {
                              var pm = targeted as PlayerMobile;
                              VvVPlayerEntry entry = Instance.GetPlayerEntry<VvVPlayerEntry>(pm);
  
                              if (entry != null && entry.Active)
                              {
                                  pm.PrivateOverheadMessage(MessageType.Regular, 1154, 1155561, pm.NetState); // You are no longer in Vice vs Virtue!
  
                                  entry.Active = false;
                                  entry.ResignExpiration = DateTime.MinValue;
                                  pm.Delta(MobileDelta.Noto);
                                  pm.ValidateEquipment();
  
                                  from.SendMessage("{0} has been removed from VvV.", pm.Name);
                                  pm.SendMessage("You have been removed from VvV.");
                              }
                              else
                              {
                                  from.SendMessage("{0} is not an active VvV member.", pm.Name);
                              }
                          }
                      });
              });
  
              if (!Instance.HasGenerated)
              {
                  CreateSilverTraders();
              }
          }
  
          public static void OnLogin(LoginEventArgs e)
          {
              if (!Enabled)
                  return;
  
              PlayerMobile pm = e.Mobile as PlayerMobile;
  
              if (pm != null && Instance != null)
              {
                  Timer.DelayCall(TimeSpan.FromSeconds(1), Instance.CheckResignation, pm);
                  Timer.DelayCall(TimeSpan.FromSeconds(2), Instance.CheckBattleStatus, pm);
  
                  if (EnhancedRules && ShowNewRules != null && ShowNewRules.Contains(pm))
                  {
                      Timer.DelayCall(TimeSpan.FromSeconds(1), player =>
                          {
                              player.SendGump(new BasicInfoGump(_EnhancedRulesNotice));
  
                              ShowNewRules.Remove(player);
  
                              if (ShowNewRules.Count == 0)
                                  ShowNewRules = null;
                          }, pm);
                  }
              }
          }
  
          public static void OnPlayerDeath(PlayerDeathEventArgs e)
          {
              if (!Enabled)
                  return;
  
              PlayerMobile pm = e.Mobile as PlayerMobile;
  
              if (pm != null && Instance != null)
              {
                  Instance.HandlePlayerDeath(pm);
              }
          }
  
          public static bool IsVvV(Mobile m, bool checkpet = true, bool guildedonly = false)
          {
              if (!Enabled)
                  return false;
  
              if (m is BaseCreature && checkpet)
              {
                  if (((BaseCreature)m).GetMaster() is PlayerMobile)
                      m = ((BaseCreature)m).GetMaster();
              }
  
              VvVPlayerEntry entry = Instance.GetPlayerEntry<VvVPlayerEntry>(m as PlayerMobile);
  
              if (entry == null)
                  return false;
  
              return entry.Active && (!guildedonly || entry.Guild != null);
          }
  
          public static bool IsVvV(Mobile m, out VvVPlayerEntry entry, bool checkpet = true, bool guildedonly = false)
          {
              if (!Enabled)
              {
                  entry = null;
                  return false;
              }
  
              if (m is BaseCreature && checkpet)
              {
                  if (((BaseCreature)m).GetMaster() is PlayerMobile)
                      m = ((BaseCreature)m).GetMaster();
              }
  
              entry = Instance.GetPlayerEntry<VvVPlayerEntry>(m as PlayerMobile);
  
              if (entry != null && !entry.Active)
                  entry = null;
  
              return entry != null && entry.Active && (!guildedonly || entry.Guild != null);
          }
  
          public static bool IsEnemy(IDamageable from, IDamageable to)
          {
              if (from is Mobile && to is Mobile)
                  return IsEnemy((Mobile)to, (Mobile)from);
  
              //TODO: VvV items, such as traps, turrets, etc
              return false;
          }
  
          public static bool IsEnemy(Mobile from, Mobile to)
          {
              if (!Enabled || from == to)
                  return false;
  
              if (from is BaseCreature && ((BaseCreature)from).GetMaster() is PlayerMobile)
                  from = ((BaseCreature)from).GetMaster();
  
              if (to is BaseCreature && ((BaseCreature)to).GetMaster() is PlayerMobile)
                  to = ((BaseCreature)to).GetMaster();
  
              // one or the other is not a combatant
              if (!IsVvVCombatant(to) || !IsVvVCombatant(from))
                  return false;
  
              return !IsAllied(from, to);
          }
  
          public static bool IsAllied(Mobile a, Mobile b)
          {
              var guildA = a.Guild as Guild;
              var guildB = b.Guild as Guild;
  
              if (guildA != null && guildB != null && (guildA == guildB || guildA.IsAlly(guildB)))
              {
                  return true;
              }
  
              if (TempCombatants == null)
              {
                  return false;
              }
  
              var tempA = TempCombatants.FirstOrDefault(c => c.From == a);
              var tempB = TempCombatants.FirstOrDefault(c => c.From == b);
  
              if (tempA != null && (tempA.Friendly == b || (tempA.FriendlyGuild != null && tempA.FriendlyGuild == guildB)))
              {
                  return true;
              }
  
              if (tempB != null && (tempB.Friendly == a || (tempB.FriendlyGuild != null && tempB.FriendlyGuild == guildA)))
              {
                  return true;
              }
  
              return false;
          }
  
          public static bool IsVvVCombatant(Mobile mobile)
          {
              CheckTempCombatants();
  
              return IsVvV(mobile) || (TempCombatants != null && TempCombatants.Any(c => c.From == mobile));
          }
  
          public static void CheckHarmful(Mobile attacker, Mobile defender)
          {
              CheckTempCombatants();
  
              if (attacker == null || defender == null || IsAllied(attacker, defender))
                  return;
  
              if (!IsVvV(attacker) && IsVvV(defender) && !defender.Aggressed.Any(info => info.Defender == attacker))
              {
                  AddTempParticipant(attacker, null);
              }
          }
  
          public static void CheckBeneficial(Mobile from, Mobile target)
          {
              CheckTempCombatants();
  
              if (from == null || target == null || (IsVvV(from) && IsAllied(from, target)))
                  return;
  
              if (!IsVvV(from) && IsVvV(target))
              {
                  if (target.Aggressors.Any(info => IsVvV(info.Attacker)) ||
                      target.Aggressed.Any(info => IsVvV(info.Defender)))
                  {
                      AddTempParticipant(from, target);
                  }
              }
          }
  
          public static Timer TempCombatantTimer { get; private set; }
  
          public static void AddTempCombatantTimer()
          {
              if (TempCombatantTimer == null)
              {
                  TempCombatantTimer = Timer.DelayCall(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), CheckTempCombatants);
                  TempCombatantTimer.Start();
              }
          }
  
          public static void StopTempCombatantTimer()
          {
              if (TempCombatantTimer != null)
              {
                  TempCombatantTimer.Stop();
                  TempCombatantTimer = null;
              }
          }
  
          public static void CheckTempCombatants()
          {
  
gt:             if (TempCombatants == null)