1158 lines
35 KiB
C#
1158 lines
35 KiB
C#
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.Engines.CityLoyalty;
|
|
using Server.SkillHandlers;
|
|
using Server.Multis;
|
|
using Server.Regions;
|
|
using Server.Misc;
|
|
|
|
namespace Server.Engines.VvV
|
|
{
|
|
public enum UpdateType
|
|
{
|
|
Kill,
|
|
Assist,
|
|
Steal,
|
|
TurnInVice,
|
|
TurnInVirtue,
|
|
Disarm
|
|
}
|
|
|
|
[PropertyObject]
|
|
public class VvVBattle
|
|
{
|
|
public static readonly int Duration = 30;
|
|
public static readonly int Cooldown = 5;
|
|
public static readonly int Announcement = 2;
|
|
public static readonly int KillCooldownDuration = 5;
|
|
public static readonly int MaxTraps = 20;
|
|
public static readonly int MaxTurrets = 10;
|
|
|
|
//Battle Scoring
|
|
public static readonly double ScoreToWin = 10000;
|
|
public static readonly double OccupyPoints = 300;
|
|
public static readonly double AltarPoints = 1000;
|
|
public static readonly double KillPoints = 600;
|
|
public static readonly double TurnInPoints = 500;
|
|
|
|
public static readonly int AltarSilver = 100;
|
|
public static readonly int TurnInSilver = 100;
|
|
public static readonly int KillSilver = 50;
|
|
public static readonly int WinSilver = 100;
|
|
public static readonly int DisarmSilver = 50;
|
|
|
|
// silver penalty in the event the battle is uncontested
|
|
public static readonly double Penalty = 0.5;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public double SilverPenalty
|
|
{
|
|
get
|
|
{
|
|
if (!ViceVsVirtueSystem.EnhancedRules)
|
|
return 1.0;
|
|
|
|
return UnContested ? Penalty : 1.0;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ViceVsVirtueSystem System { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime StartTime { get; private set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime CooldownEnds { get; private set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime LastOccupationCheck { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime NextSigilSpawn { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime NextAnnouncement { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime NextAltarActivate { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime NextManaSpike { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime ManaSpikeEndEffects { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public VvVCity City { get; private set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public VvVSigil Sigil { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool OnGoing { get; private set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool UnContested { get; private set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Region Region
|
|
{
|
|
get
|
|
{
|
|
CityInfo info = CityInfo.Infos[City];
|
|
|
|
if (info != null)
|
|
{
|
|
return info.Region;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool ForceStart
|
|
{
|
|
get { return false; }
|
|
set
|
|
{
|
|
if (!OnGoing && value)
|
|
Begin();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool ForceEnd
|
|
{
|
|
get { return false; }
|
|
set
|
|
{
|
|
if (OnGoing && value)
|
|
EndBattle();
|
|
}
|
|
}
|
|
|
|
public List<BattleTeam> Teams { get; set; }
|
|
|
|
public Dictionary<Mobile, DateTime> KillCooldown { get; set; }
|
|
public Dictionary<Mobile, DateTime> BattleAggression { get; set; }
|
|
public List<string> Messages { get; set; }
|
|
|
|
public List<VvVAltar> Altars { get; set; }
|
|
public int AltarIndex { get; set; }
|
|
|
|
public List<VvVTrap> Traps { get; set; }
|
|
public List<CannonTurret> Turrets { get; set; }
|
|
public List<Mobile> Warned { get; set; }
|
|
|
|
public VvVPriest VicePriest { get; private set; }
|
|
public VvVPriest VirtuePriest { get; private set; }
|
|
|
|
public Timer Timer { get; private set; }
|
|
|
|
public int TrapCount { get { return Traps.Where(t => !t.Deleted).Count(); } }
|
|
public int TurretCount { get { return Turrets.Where(t => !t.Deleted).Count(); } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool InCooldown
|
|
{
|
|
get { return CooldownEnds > DateTime.UtcNow; }
|
|
}
|
|
|
|
public DateTime NextCombatHeatCycle { get; private set; }
|
|
|
|
public VvVBattle(ViceVsVirtueSystem sys)
|
|
{
|
|
System = sys;
|
|
}
|
|
|
|
public void Begin()
|
|
{
|
|
OnGoing = true;
|
|
NextCombatHeatCycle = DateTime.UtcNow;
|
|
VvVCity newCity = City;
|
|
List<VvVCity> cities = new List<VvVCity>();
|
|
|
|
Teams = new List<BattleTeam>();
|
|
KillCooldown = new Dictionary<Mobile, DateTime>();
|
|
Messages = new List<string>();
|
|
Altars = new List<VvVAltar>();
|
|
Traps = new List<VvVTrap>();
|
|
Warned = new List<Mobile>();
|
|
Turrets = new List<CannonTurret>();
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (!System.ExemptCities.Contains((VvVCity)i) && (VvVCity)i != newCity)
|
|
cities.Add((VvVCity)i);
|
|
}
|
|
|
|
if (cities.Count > 0)
|
|
{
|
|
newCity = cities[Utility.Random(cities.Count)];
|
|
}
|
|
else if (System.ExemptCities.Contains(newCity))
|
|
{
|
|
System.SendVvVMessage("All VvV cities are currently exempt.");
|
|
return;
|
|
}
|
|
|
|
ColUtility.Free(cities);
|
|
City = newCity;
|
|
BeginTimer();
|
|
|
|
StartTime = DateTime.UtcNow;
|
|
NextSigilSpawn = DateTime.UtcNow + TimeSpan.FromMinutes(Utility.RandomMinMax(1, 3));
|
|
|
|
AltarIndex = 0;
|
|
SpawnAltars();
|
|
SpawnPriests(false);
|
|
|
|
if (Region is GuardedRegion)
|
|
{
|
|
GuardedRegion.Disable((GuardedRegion)Region);
|
|
}
|
|
|
|
NextAltarActivate = DateTime.UtcNow + TimeSpan.FromMinutes(1);
|
|
|
|
System.SendVvVMessage(1154721, String.Format("#{0}", ViceVsVirtueSystem.GetCityLocalization(City).ToString()));
|
|
// A Battle between Vice and Virtue is active! To Arms! The City of ~1_CITY~ is besieged!
|
|
}
|
|
|
|
public void SpawnAltars()
|
|
{
|
|
foreach (Point3D p in CityInfo.Infos[City].AltarLocs)
|
|
{
|
|
VvVAltar altar = new VvVAltar(this);
|
|
altar.MoveToWorld(p, Map.Felucca);
|
|
|
|
Altars.Add(altar);
|
|
}
|
|
}
|
|
|
|
public void SpawnPriests(bool movetoworld = true)
|
|
{
|
|
if(VicePriest == null || VicePriest.Deleted)
|
|
VicePriest = new VvVPriest(VvVType.Vice, this);
|
|
|
|
if(VirtuePriest == null || VirtuePriest.Deleted)
|
|
VirtuePriest = new VvVPriest(VvVType.Virtue, this);
|
|
|
|
if (movetoworld)
|
|
{
|
|
Point3D p;
|
|
|
|
do
|
|
{
|
|
p = Map.Felucca.GetRandomSpawnPoint(CityInfo.Infos[City].PriestLocation);
|
|
}
|
|
while (!Map.Felucca.CanSpawnMobile(p));
|
|
|
|
VicePriest.MoveToWorld(p, Map.Felucca);
|
|
|
|
do
|
|
{
|
|
p = Map.Felucca.GetRandomSpawnPoint(CityInfo.Infos[City].PriestLocation);
|
|
}
|
|
while (!Map.Felucca.CanSpawnMobile(p));
|
|
|
|
VirtuePriest.MoveToWorld(p, Map.Felucca);
|
|
}
|
|
}
|
|
|
|
public void BeginTimer()
|
|
{
|
|
EndTimer();
|
|
|
|
Timer = Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnTick);
|
|
Timer.Start();
|
|
}
|
|
|
|
public void EndTimer()
|
|
{
|
|
if (Timer != null)
|
|
{
|
|
Timer.Stop();
|
|
Timer = null;
|
|
}
|
|
}
|
|
|
|
public void OnTick()
|
|
{
|
|
if (!OnGoing)
|
|
return;
|
|
|
|
CheckParticipation();
|
|
UpdateAllGumps();
|
|
|
|
if (StartTime + TimeSpan.FromMinutes(Duration) < DateTime.UtcNow)
|
|
{
|
|
EndBattle();
|
|
return;
|
|
}
|
|
|
|
if (LastOccupationCheck + TimeSpan.FromMinutes(1) < DateTime.UtcNow)
|
|
{
|
|
CheckOccupation();
|
|
|
|
LastOccupationCheck = DateTime.UtcNow;
|
|
}
|
|
|
|
if (NextAltarActivate != DateTime.MinValue && NextAltarActivate < DateTime.UtcNow)
|
|
{
|
|
ActivateAltar();
|
|
NextAltarActivate = DateTime.MinValue;
|
|
}
|
|
|
|
if (NextSigilSpawn != DateTime.MinValue && NextSigilSpawn < DateTime.UtcNow)
|
|
{
|
|
if (Sigil == null || Sigil.Deleted)
|
|
{
|
|
SpawnSigil();
|
|
}
|
|
}
|
|
|
|
ActivateArrows();
|
|
|
|
if (KillCooldown != null)
|
|
{
|
|
List<Mobile> list = KillCooldown.Keys.Where(mob => KillCooldown[mob] < DateTime.UtcNow).ToList();
|
|
|
|
foreach (Mobile m in list)
|
|
{
|
|
KillCooldown.Remove(m);
|
|
}
|
|
|
|
ColUtility.Free(list);
|
|
}
|
|
|
|
if (Turrets != null)
|
|
{
|
|
Turrets.ForEach(t => { t.Scan(); });
|
|
}
|
|
}
|
|
|
|
public void CheckParticipation()
|
|
{
|
|
if (Region == null)
|
|
return;
|
|
|
|
BattleTeam team = null;
|
|
UnContested = true;
|
|
bool checkAggression = ViceVsVirtueSystem.EnhancedRules && NextCombatHeatCycle < DateTime.UtcNow;
|
|
|
|
foreach (PlayerMobile pm in this.Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
bool vvv = ViceVsVirtueSystem.IsVvV(pm);
|
|
|
|
if (!vvv && !Warned.Contains(pm) && pm.AccessLevel == AccessLevel.Player)
|
|
{
|
|
pm.SendGump(new BattleWarningGump(pm));
|
|
Warned.Add(pm);
|
|
}
|
|
else if (vvv && pm.Alive && !pm.Hidden && BaseBoat.FindBoatAt(pm.Location, pm.Map) == null && BaseHouse.FindHouseAt(pm) == null)
|
|
{
|
|
Guild g = pm.Guild as Guild;
|
|
|
|
if (g != null)
|
|
{
|
|
var t = GetTeam(g);
|
|
|
|
if (team == null)
|
|
{
|
|
team = t;
|
|
}
|
|
else if (t != team && UnContested)
|
|
{
|
|
UnContested = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (checkAggression && (vvv || ViceVsVirtueSystem.IsVvVCombatant(pm)))
|
|
{
|
|
AddAggression(pm);
|
|
}
|
|
}
|
|
|
|
if (checkAggression)
|
|
{
|
|
CheckBattleAggression();
|
|
|
|
NextCombatHeatCycle = DateTime.UtcNow + TimeSpan.FromMinutes(1);
|
|
}
|
|
}
|
|
|
|
public void AddAggression(Mobile m)
|
|
{
|
|
if (m is PlayerMobile)
|
|
{
|
|
if (BattleAggression == null)
|
|
{
|
|
BattleAggression = new Dictionary<Mobile, DateTime>();
|
|
}
|
|
|
|
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.HeatOfBattleStatus, 1153801, 1153827, Aggression.CombatHeatDelay, m, true));
|
|
BattleAggression[m] = DateTime.UtcNow + TimeSpan.FromMinutes(2);
|
|
}
|
|
}
|
|
|
|
private void CheckBattleAggression()
|
|
{
|
|
if (BattleAggression == null)
|
|
return;
|
|
|
|
var list = new List<Mobile>(BattleAggression.Keys);
|
|
|
|
foreach (var m in list)
|
|
{
|
|
if (BattleAggression[m] < DateTime.UtcNow && !m.Region.IsPartOf(Region))
|
|
{
|
|
BattleAggression.Remove(m);
|
|
}
|
|
}
|
|
|
|
ColUtility.Free(list);
|
|
}
|
|
|
|
public bool HasBattleAggression(Mobile m)
|
|
{
|
|
if (BattleAggression == null || !BattleAggression.ContainsKey(m))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (BattleAggression[m] < DateTime.UtcNow)
|
|
{
|
|
BattleAggression.Remove(m);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void EndBattle()
|
|
{
|
|
OnGoing = false;
|
|
EndTimer();
|
|
|
|
if (Region is GuardedRegion)
|
|
{
|
|
((GuardedRegion)Region).Disabled = false;
|
|
|
|
foreach (PlayerMobile pm in Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
pm.RecheckTownProtection();
|
|
}
|
|
}
|
|
|
|
foreach (VvVAltar altar in Altars)
|
|
{
|
|
if(!altar.Deleted)
|
|
altar.Delete();
|
|
}
|
|
|
|
foreach (VvVTrap trap in Traps)
|
|
{
|
|
if(!trap.Deleted)
|
|
trap.Delete();
|
|
}
|
|
|
|
foreach (CannonTurret turret in Turrets)
|
|
{
|
|
if (!turret.Deleted)
|
|
turret.Delete();
|
|
}
|
|
|
|
if (VicePriest != null)
|
|
{
|
|
VicePriest.Delete();
|
|
VicePriest = null;
|
|
}
|
|
|
|
if (VirtuePriest != null)
|
|
{
|
|
VirtuePriest.Delete();
|
|
VirtuePriest = null;
|
|
}
|
|
|
|
if (Sigil != null)
|
|
{
|
|
Sigil.Delete();
|
|
Sigil = null;
|
|
}
|
|
|
|
TallyStats();
|
|
SendBattleStatsGump();
|
|
|
|
System.SendVvVMessage(1154722); // A VvV battle has just concluded. The next battle will begin in less than five minutes!
|
|
|
|
if (BattleAggression != null)
|
|
{
|
|
BattleAggression.Clear();
|
|
}
|
|
|
|
ColUtility.Free(Altars);
|
|
ColUtility.Free(Teams);
|
|
KillCooldown.Clear();
|
|
ColUtility.Free(Messages);
|
|
ColUtility.Free(Traps);
|
|
ColUtility.Free(Warned);
|
|
ColUtility.Free(Turrets);
|
|
|
|
if (Region is GuardedRegion)
|
|
{
|
|
((GuardedRegion)Region).Disabled = false;
|
|
}
|
|
|
|
NextSigilSpawn = DateTime.MinValue;
|
|
LastOccupationCheck = DateTime.MinValue;
|
|
NextAnnouncement = DateTime.MinValue;
|
|
StartTime = DateTime.MinValue;
|
|
NextAltarActivate = DateTime.MinValue;
|
|
ManaSpikeEndEffects = DateTime.MinValue;
|
|
NextManaSpike = DateTime.MinValue;
|
|
|
|
CooldownEnds = DateTime.UtcNow + TimeSpan.FromMinutes(Cooldown);
|
|
|
|
Timer.DelayCall(TimeSpan.FromMinutes(Cooldown), () =>
|
|
{
|
|
System.CheckBattleStatus();
|
|
});
|
|
}
|
|
|
|
public void TallyStats()
|
|
{
|
|
BattleTeam leader = GetLeader();
|
|
List<Guild> added = new List<Guild>();
|
|
|
|
if (leader == null || leader.Guild == null)
|
|
return;
|
|
|
|
leader.Silver += AwardSilver(WinSilver + (OppositionCount(leader.Guild) * 50));
|
|
|
|
foreach (Mobile m in this.Region.GetEnumeratedMobiles())
|
|
{
|
|
Guild g = m.Guild as Guild;
|
|
|
|
if (g == null)
|
|
continue;
|
|
|
|
PlayerMobile pm = m as PlayerMobile;
|
|
|
|
if (pm != null)
|
|
{
|
|
BattleTeam team = GetTeam(g);
|
|
VvVPlayerBattleStats stats = GetPlayerStats(pm);
|
|
VvVPlayerEntry entry = ViceVsVirtueSystem.Instance.GetPlayerEntry<VvVPlayerEntry>(pm);
|
|
|
|
if (entry != null)
|
|
{
|
|
entry.Score += team.Score;
|
|
entry.Points += team.Silver;
|
|
entry.Kills += stats.Kills;
|
|
entry.Deaths += stats.Deaths;
|
|
entry.Assists += stats.Assists;
|
|
entry.ReturnedSigils += stats.ReturnedSigils;
|
|
entry.DisarmedTraps += stats.Disarmed;
|
|
entry.StolenSigils += stats.Stolen;
|
|
|
|
if (added.Contains(g))
|
|
continue;
|
|
else
|
|
added.Add(g);
|
|
|
|
if (!ViceVsVirtueSystem.Instance.GuildStats.ContainsKey(g))
|
|
ViceVsVirtueSystem.Instance.GuildStats[g] = new VvVGuildStats(g);
|
|
|
|
VvVGuildStats gstats = ViceVsVirtueSystem.Instance.GuildStats[g];
|
|
|
|
gstats.Kills += team.Kills;
|
|
gstats.ReturnedSigils += team.ReturnedSigils;
|
|
gstats.Score += team.Score;
|
|
}
|
|
}
|
|
}
|
|
|
|
ColUtility.Free(added);
|
|
}
|
|
|
|
public void SpawnSigil()
|
|
{
|
|
Point3D p = CityInfo.Infos[City].SigilLocs[Utility.Random(CityInfo.Infos[City].SigilLocs.Length)];
|
|
Sigil = new VvVSigil(this, p);
|
|
|
|
Sigil.MoveToWorld(p, Map.Felucca);
|
|
|
|
UpdateAllGumps();
|
|
}
|
|
|
|
public void ActivateAltar()
|
|
{
|
|
if (AltarIndex == 2)
|
|
AltarIndex = 0;
|
|
else
|
|
AltarIndex++;
|
|
|
|
Altars[AltarIndex].Activate();
|
|
ActivateArrows();
|
|
|
|
SendStatusMessage("Fight for the altar!", true);
|
|
}
|
|
|
|
public void CheckArrow(PlayerMobile pm)
|
|
{
|
|
if (pm.NetState == null)
|
|
return;
|
|
|
|
foreach (VvVAltar altar in Altars)
|
|
{
|
|
if (altar.IsActive)
|
|
{
|
|
pm.QuestArrow = new AltarArrow(pm, altar);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ActivateArrows()
|
|
{
|
|
if (Altars == null || Region == null)
|
|
return;
|
|
|
|
foreach (PlayerMobile pm in this.Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
if (pm.NetState != null && pm.QuestArrow == null)
|
|
{
|
|
foreach (VvVAltar altar in Altars)
|
|
{
|
|
if (altar.IsActive)
|
|
{
|
|
pm.QuestArrow = new AltarArrow(pm, altar);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public VvVPlayerBattleStats GetPlayerStats(PlayerMobile pm)
|
|
{
|
|
if (pm == null || pm.Guild == null)
|
|
return null;
|
|
|
|
Guild g = pm.Guild as Guild;
|
|
|
|
BattleTeam team = GetTeam(g);
|
|
VvVPlayerBattleStats stats = team.PlayerStats.FirstOrDefault(s => s.Player == pm);
|
|
|
|
if (stats == null)
|
|
{
|
|
stats = new VvVPlayerBattleStats(pm);
|
|
team.PlayerStats.Add(stats);
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
public BattleTeam GetTeam(Guild g)
|
|
{
|
|
BattleTeam team = Teams.FirstOrDefault(t => t.Guild != null && (t.Guild == g || t.Guild.IsAlly(g)));
|
|
|
|
if (team != null)
|
|
return team;
|
|
|
|
team = new BattleTeam(g);
|
|
Teams.Add(team);
|
|
|
|
return team;
|
|
}
|
|
|
|
public void Update(Mobile m, UpdateType type)
|
|
{
|
|
VvVPlayerEntry entry = System.GetPlayerEntry<VvVPlayerEntry>(m);
|
|
|
|
if (entry != null)
|
|
{
|
|
Update(null, entry, type);
|
|
}
|
|
}
|
|
|
|
public void Update(VvVPlayerEntry victim, VvVPlayerEntry killer, UpdateType type)
|
|
{
|
|
if (killer == null || killer.Player == null || killer.Guild == null)
|
|
return;
|
|
|
|
VvVPlayerBattleStats killerStats = GetPlayerStats(killer.Player);
|
|
VvVPlayerBattleStats victimStats = victim == null ? null : GetPlayerStats(victim.Player);
|
|
|
|
BattleTeam killerTeam = GetTeam(killer.Guild);
|
|
BattleTeam victimTeam = null;
|
|
|
|
if(victim != null)
|
|
victimTeam = GetTeam(victim.Guild);
|
|
|
|
switch (type)
|
|
{
|
|
case UpdateType.Kill:
|
|
if (killerStats != null) killerStats.Kills++;
|
|
if (victimStats != null) victimStats.Deaths++;
|
|
|
|
if (killerTeam != null)
|
|
killerTeam.Kills++;
|
|
|
|
if (victimTeam != null)
|
|
victimTeam.Deaths++;
|
|
|
|
if (victim != null && victim.Player != null)
|
|
{
|
|
if (!KillCooldown.ContainsKey(victim.Player) || KillCooldown[victim.Player] < DateTime.UtcNow)
|
|
{
|
|
if (killerTeam != null)
|
|
{
|
|
killerTeam.Score += (int)KillPoints;
|
|
killerTeam.Silver += AwardSilver(KillSilver + (OppositionCount(killer.Guild) * 50));
|
|
}
|
|
|
|
SendStatusMessage(String.Format("{0} has killed {1}!", killer.Player.Name, victim.Player.Name));
|
|
KillCooldown[victim.Player] = DateTime.UtcNow + TimeSpan.FromMinutes(KillCooldownDuration);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case UpdateType.Assist:
|
|
if (killerStats != null)
|
|
killerStats.Assists++;
|
|
|
|
if (killerTeam != null)
|
|
killerTeam.Assists++;
|
|
|
|
break;
|
|
case UpdateType.Steal:
|
|
if (killerStats != null)
|
|
{
|
|
killerStats.Stolen++;
|
|
SendStatusMessage(String.Format("{0} has stolen the sigil!", killer.Player.Name));
|
|
}
|
|
|
|
if (killerTeam != null)
|
|
killerTeam.Stolen++;
|
|
|
|
break;
|
|
case UpdateType.TurnInVice:
|
|
case UpdateType.TurnInVirtue:
|
|
if (killerTeam != null)
|
|
{
|
|
killerTeam.Score += (int)TurnInPoints;
|
|
killerTeam.Silver += AwardSilver(TurnInSilver + (OppositionCount(killer.Guild) * 50));
|
|
}
|
|
|
|
if (killerStats != null && killerTeam != null)
|
|
{
|
|
if (type == UpdateType.TurnInVirtue)
|
|
{
|
|
killerStats.VirtueReturned++;
|
|
killerTeam.VirtueReturned++;
|
|
}
|
|
else
|
|
{
|
|
killerStats.ViceReturned++;
|
|
killerTeam.ViceReturned++;
|
|
}
|
|
}
|
|
|
|
SendStatusMessage(String.Format("{0} has returned the sigil!", killer.Player.Name));
|
|
|
|
NextSigilSpawn = DateTime.UtcNow + TimeSpan.FromMinutes(1);
|
|
RemovePriests();
|
|
|
|
break;
|
|
case UpdateType.Disarm:
|
|
SendStatusMessage(String.Format("{0} has disarmed a trap!", killer.Player.Name));
|
|
|
|
if (killerStats != null)
|
|
{
|
|
killerStats.Disarmed++;
|
|
}
|
|
|
|
if (killerTeam != null)
|
|
{
|
|
killerTeam.Silver += AwardSilver(DisarmSilver + (OppositionCount(killer.Guild) * 50));
|
|
killerTeam.Disarmed++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
CheckScore();
|
|
}
|
|
|
|
public int AwardSilver(int amount)
|
|
{
|
|
return (int)((double)amount * SilverPenalty);
|
|
}
|
|
|
|
public void RemovePriests()
|
|
{
|
|
Timer.DelayCall(TimeSpan.FromSeconds(4), () =>
|
|
{
|
|
if (VicePriest != null)
|
|
VicePriest.Internalize();
|
|
|
|
if (VirtuePriest != null)
|
|
VirtuePriest.Internalize();
|
|
});
|
|
}
|
|
|
|
public void OccupyAltar(Guild g)
|
|
{
|
|
if (!OnGoing || g == null)
|
|
return;
|
|
|
|
BattleTeam team = GetTeam(g);
|
|
|
|
team.Score += (int)AltarPoints;
|
|
team.Silver += AwardSilver(AltarSilver + (OppositionCount(g) * 50));
|
|
|
|
SendStatusMessage(String.Format("{0} claimed the altar!", g != null ? g.Abbreviation : "somebody"));
|
|
|
|
foreach (PlayerMobile p in Region.GetEnumeratedMobiles().Where(player => player is PlayerMobile))
|
|
{
|
|
if (p.QuestArrow != null)
|
|
p.QuestArrow = null;
|
|
}
|
|
|
|
CheckScore();
|
|
NextAltarActivate = DateTime.UtcNow + TimeSpan.FromMinutes(2);
|
|
}
|
|
|
|
public void CheckOccupation()
|
|
{
|
|
if (!OnGoing)
|
|
return;
|
|
|
|
if (Teams.Count == 1)
|
|
{
|
|
BattleTeam team = Teams[0];
|
|
|
|
team.Score += (int)OccupyPoints;
|
|
UpdateAllGumps();
|
|
CheckScore();
|
|
|
|
if (OnGoing && NextAnnouncement < DateTime.UtcNow)
|
|
{
|
|
System.SendVvVMessage(1154957, team.Guild.Name); // ~1_NAME~ is occupying the city!
|
|
NextAnnouncement = DateTime.UtcNow + TimeSpan.FromMinutes(Announcement);
|
|
}
|
|
}
|
|
else // Is this a bug? Verified on EA this is how it behaves
|
|
{
|
|
if (NextAnnouncement < DateTime.UtcNow)
|
|
{
|
|
System.SendVvVMessage(1154958); // The City is unoccupied! Slay opposing forces to claim the city for your guild!
|
|
NextAnnouncement = DateTime.UtcNow + TimeSpan.FromMinutes(Announcement);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CheckScore()
|
|
{
|
|
int score;
|
|
GetLeader(out score);
|
|
|
|
if (score >= ScoreToWin)
|
|
{
|
|
EndBattle();
|
|
return;
|
|
}
|
|
|
|
UpdateAllGumps();
|
|
}
|
|
|
|
public BattleTeam GetLeader()
|
|
{
|
|
int score;
|
|
return GetLeader(out score);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets real time leader of battle, out parameter of their score
|
|
/// </summary>
|
|
/// <param name="score"></param>
|
|
/// <returns></returns>
|
|
public BattleTeam GetLeader(out int score)
|
|
{
|
|
score = 0;
|
|
|
|
List<BattleTeam> teams = new List<BattleTeam>(Teams);
|
|
teams.Sort();
|
|
|
|
if (teams.Count > 0)
|
|
{
|
|
score = teams[0].Score;
|
|
return teams[0];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns enemy count, by alliance
|
|
/// </summary>
|
|
/// <param name="g"></param>
|
|
/// <returns></returns>
|
|
public int OppositionCount(Guild g)
|
|
{
|
|
List<Guild> exempt = new List<Guild>();
|
|
int count = 0;
|
|
|
|
foreach (BattleTeam team in Teams)
|
|
{
|
|
if (team.Guild == null || team.Guild == g || team.Guild.IsAlly(g) || exempt.Contains(g))
|
|
continue;
|
|
|
|
count++;
|
|
|
|
if (team.Guild.Alliance != null)
|
|
{
|
|
foreach (Guild guil in team.Guild.Alliance.Members.Where(guil => !exempt.Contains(guil)))
|
|
exempt.Add(guil);
|
|
}
|
|
}
|
|
|
|
ColUtility.Free(exempt);
|
|
|
|
return count;
|
|
}
|
|
|
|
public bool IsInActiveBattle(Mobile one, Mobile two)
|
|
{
|
|
return IsInActiveBattle(one) && IsInActiveBattle(two);
|
|
}
|
|
|
|
public bool IsInActiveBattle(Mobile m)
|
|
{
|
|
if (!OnGoing)
|
|
return false;
|
|
|
|
Region r = Region.Find(m.Location, m.Map);
|
|
|
|
return r == this.Region;
|
|
}
|
|
|
|
public void OnEnterRegion(Mobile m)
|
|
{
|
|
if (m is PlayerMobile && OnGoing)
|
|
{
|
|
if (m.HasGump(typeof(VvVBattleStatusGump)))
|
|
m.CloseGump(typeof(VvVBattleStatusGump));
|
|
|
|
BaseGump.SendGump(new VvVBattleStatusGump((PlayerMobile)m, this));
|
|
}
|
|
}
|
|
|
|
public void CheckGump(Mobile m)
|
|
{
|
|
if (m is PlayerMobile && OnGoing)
|
|
{
|
|
BaseGump.SendGump(new VvVBattleStatusGump((PlayerMobile)m, this));
|
|
}
|
|
}
|
|
|
|
public void UpdateAllGumps()
|
|
{
|
|
if (Region == null)
|
|
return;
|
|
|
|
foreach (PlayerMobile m in this.Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
if (!ViceVsVirtueSystem.IsVvV(m) || m.NetState == null)
|
|
continue;
|
|
|
|
VvVBattleStatusGump g = m.FindGump(typeof(VvVBattleStatusGump)) as VvVBattleStatusGump;
|
|
|
|
if (g == null)
|
|
{
|
|
BaseGump.SendGump(new VvVBattleStatusGump(m, this));
|
|
}
|
|
else
|
|
{
|
|
g.Refresh(true, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SendBattleStatsGump()
|
|
{
|
|
if (Region == null)
|
|
return;
|
|
|
|
foreach (PlayerMobile m in this.Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
if (ViceVsVirtueSystem.IsVvV(m))
|
|
{
|
|
m.CloseGump(typeof(VvVBattleStatusGump));
|
|
m.SendGump(new BattleStatsGump((PlayerMobile)m, this));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SendStatusMessage(string message, bool sendgumps = false)
|
|
{
|
|
Messages.Add(message);
|
|
|
|
if(sendgumps)
|
|
UpdateAllGumps();
|
|
}
|
|
|
|
public void AddCannonTurret(CannonTurret turret)
|
|
{
|
|
if (!Turrets.Contains(turret))
|
|
Turrets.Add(turret);
|
|
}
|
|
|
|
public VvVBattle(GenericReader reader, ViceVsVirtueSystem system)
|
|
{
|
|
int version = reader.ReadInt();
|
|
System = system;
|
|
|
|
Altars = new List<VvVAltar>();
|
|
KillCooldown = new Dictionary<Mobile, DateTime>();
|
|
Messages = new List<string>();
|
|
Traps = new List<VvVTrap>();
|
|
Warned = new List<Mobile>();
|
|
Turrets = new List<CannonTurret>();
|
|
Teams = new List<BattleTeam>();
|
|
|
|
OnGoing = reader.ReadBool();
|
|
|
|
if (reader.ReadInt() == 0)
|
|
{
|
|
StartTime = reader.ReadDateTime();
|
|
CooldownEnds = reader.ReadDateTime();
|
|
LastOccupationCheck = reader.ReadDateTime();
|
|
NextSigilSpawn = reader.ReadDateTime();
|
|
NextAnnouncement = reader.ReadDateTime();
|
|
NextAltarActivate = reader.ReadDateTime();
|
|
City = (VvVCity)reader.ReadInt();
|
|
Sigil = reader.ReadItem() as VvVSigil;
|
|
VicePriest = reader.ReadMobile() as VvVPriest;
|
|
VirtuePriest = reader.ReadMobile() as VvVPriest;
|
|
|
|
if (Sigil != null)
|
|
Sigil.Battle = this;
|
|
|
|
if (VicePriest != null)
|
|
VicePriest.Battle = this;
|
|
|
|
if (VirtuePriest != null)
|
|
VirtuePriest.Battle = this;
|
|
|
|
int count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
VvVAltar altar = reader.ReadItem() as VvVAltar;
|
|
if (altar != null)
|
|
{
|
|
altar.Battle = this;
|
|
Altars.Add(altar);
|
|
}
|
|
}
|
|
|
|
if (version == 1)
|
|
{
|
|
count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
BattleTeam team = new BattleTeam(reader);
|
|
Teams.Add(team);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Guild g = reader.ReadGuild() as Guild;
|
|
VvVGuildBattleStats stats = new VvVGuildBattleStats(reader, g);
|
|
}
|
|
}
|
|
|
|
count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
VvVTrap t = reader.ReadItem() as VvVTrap;
|
|
|
|
if (t != null)
|
|
Traps.Add(t);
|
|
}
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(10), () =>
|
|
{
|
|
if (Region is GuardedRegion)
|
|
{
|
|
GuardedRegion.Disable((GuardedRegion)Region);
|
|
}
|
|
|
|
BeginTimer();
|
|
|
|
ActivateArrows();
|
|
});
|
|
}
|
|
}
|
|
|
|
public void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(1);
|
|
|
|
writer.Write(OnGoing);
|
|
|
|
if (OnGoing)
|
|
{
|
|
writer.Write(0);
|
|
writer.Write(StartTime);
|
|
writer.Write(CooldownEnds);
|
|
writer.Write(LastOccupationCheck);
|
|
writer.Write(NextSigilSpawn);
|
|
writer.Write(NextAnnouncement);
|
|
writer.Write(NextAltarActivate);
|
|
writer.Write((int)City);
|
|
writer.Write(Sigil);
|
|
writer.Write(VicePriest);
|
|
writer.Write(VirtuePriest);
|
|
|
|
writer.Write(Altars.Count);
|
|
Altars.ForEach(altar => writer.Write(altar));
|
|
|
|
/*writer.Write(GuildStats.Count);
|
|
foreach (KeyValuePair<Guild, VvVGuildBattleStats> kvp in GuildStats)
|
|
{
|
|
writer.Write(kvp.Key);
|
|
kvp.Value.Serialize(writer);
|
|
}*/
|
|
writer.Write(Teams.Count);
|
|
foreach (BattleTeam team in Teams)
|
|
{
|
|
team.Serialize(writer);
|
|
}
|
|
|
|
writer.Write(Traps.Count);
|
|
Traps.ForEach(t => writer.Write(t));
|
|
}
|
|
else
|
|
writer.Write(1);
|
|
|
|
}
|
|
}
|
|
}
|