1172 lines
36 KiB
C#
1172 lines
36 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.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()
|
|
{
|
|
if (TempCombatants == null)
|
|
{
|
|
StopTempCombatantTimer();
|
|
}
|
|
|
|
TempCombatants.IterateReverse(c =>
|
|
{
|
|
if (c.Expired)
|
|
{
|
|
TempCombatants.Remove(c);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static TemporaryCombatant GetTempCombatant(Mobile from, Mobile to)
|
|
{
|
|
foreach (var combatant in TempCombatants.Where(c => c.From == from))
|
|
{
|
|
if (combatant.Friendly == null && to == null)
|
|
return combatant;
|
|
|
|
if (combatant.Friendly == to || (combatant.FriendlyGuild != null && combatant.FriendlyGuild == from.Guild as Guild))
|
|
return combatant;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static void AddTempParticipant(Mobile m, Mobile friendlyTo)
|
|
{
|
|
if (TempCombatants == null)
|
|
{
|
|
TempCombatants = new List<TemporaryCombatant>();
|
|
AddTempCombatantTimer();
|
|
}
|
|
|
|
var combatant = GetTempCombatant(m, friendlyTo);
|
|
|
|
if (combatant == null)
|
|
{
|
|
combatant = new TemporaryCombatant(m, friendlyTo);
|
|
}
|
|
else
|
|
{
|
|
combatant.Reset();
|
|
}
|
|
|
|
TempCombatants.Add(combatant);
|
|
|
|
m.Delta(MobileDelta.Noto);
|
|
m.ProcessDelta();
|
|
}
|
|
|
|
public static void OnMapChange(PlayerMobile pm)
|
|
{
|
|
if (TempCombatants == null || pm.Map == Map.Internal || pm.Map == null)
|
|
return;
|
|
|
|
TempCombatants.Where(t => t.From == pm).IterateReverse(temp =>
|
|
{
|
|
RemoveTempCombatant(temp);
|
|
});
|
|
}
|
|
|
|
public static void RemoveTempCombatant(TemporaryCombatant tempCombatant)
|
|
{
|
|
if (TempCombatants == null)
|
|
return;
|
|
|
|
TempCombatants.Remove(tempCombatant);
|
|
tempCombatant.From.Delta(MobileDelta.Noto);
|
|
tempCombatant.From.ProcessDelta();
|
|
|
|
if (TempCombatants.Count == 0)
|
|
{
|
|
TempCombatants = null;
|
|
StopTempCombatantTimer();
|
|
}
|
|
}
|
|
|
|
public static bool HasBattleAggression(Mobile m)
|
|
{
|
|
if (!EnhancedRules || Instance == null || Instance.Battle == null || !Instance.Battle.OnGoing)
|
|
return false;
|
|
|
|
return Instance.Battle.HasBattleAggression(m);
|
|
}
|
|
|
|
public static bool IsBattleRegion(Region r)
|
|
{
|
|
if (r == null || Instance == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Instance.Battle.OnGoing && r.IsPartOf(Instance.Battle.Region);
|
|
}
|
|
|
|
public static int GetCityLocalization(VvVCity city)
|
|
{
|
|
switch (city)
|
|
{
|
|
default: return 0;
|
|
case VvVCity.Moonglow: return 1011344;
|
|
case VvVCity.Britain: return 1011028;
|
|
case VvVCity.Jhelom: return 1011343;
|
|
case VvVCity.Yew: return 1011032;
|
|
case VvVCity.Minoc: return 1011031;
|
|
case VvVCity.Trinsic: return 1011029;
|
|
case VvVCity.SkaraBrae: return 1011347;
|
|
case VvVCity.Ocllo: return 1076027;
|
|
}
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(4);
|
|
|
|
writer.Write(ShowNewRules == null ? 0 : ShowNewRules.Count);
|
|
if (ShowNewRules != null)
|
|
{
|
|
foreach (var pm in ShowNewRules)
|
|
writer.Write(pm);
|
|
}
|
|
|
|
writer.Write(EnhancedRules);
|
|
writer.Write(Enabled);
|
|
|
|
writer.Write(ExemptCities.Count);
|
|
ExemptCities.ForEach(c => writer.Write((int)c));
|
|
|
|
writer.Write(HasGenerated);
|
|
Battle.Serialize(writer);
|
|
|
|
writer.Write(VvVItems.Count);
|
|
foreach (Item item in VvVItems)
|
|
{
|
|
writer.Write(item);
|
|
}
|
|
|
|
writer.Write(GuildStats.Count);
|
|
foreach (KeyValuePair<Guild, VvVGuildStats> kvp in GuildStats)
|
|
{
|
|
writer.Write(kvp.Key);
|
|
kvp.Value.Serialize(writer);
|
|
}
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
GuildStats = new Dictionary<Guild, VvVGuildStats>();
|
|
ExemptCities = new List<VvVCity>();
|
|
|
|
bool enabled = false;
|
|
bool enhanced = false;
|
|
|
|
switch (version)
|
|
{
|
|
case 4:
|
|
int c = reader.ReadInt();
|
|
for (int i = 0; i < c; i++)
|
|
{
|
|
var pm = reader.ReadMobile() as PlayerMobile;
|
|
|
|
if (pm != null)
|
|
{
|
|
if (ShowNewRules == null)
|
|
{
|
|
ShowNewRules = new List<PlayerMobile>();
|
|
}
|
|
|
|
ShowNewRules.Add(pm);
|
|
}
|
|
}
|
|
|
|
enhanced = reader.ReadBool();
|
|
goto case 3;
|
|
case 3:
|
|
enabled = reader.ReadBool();
|
|
goto case 2;
|
|
case 2:
|
|
case 1:
|
|
{
|
|
int count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ExemptCities.Add((VvVCity)reader.ReadInt());
|
|
}
|
|
|
|
goto case 0;
|
|
}
|
|
case 0:
|
|
{
|
|
HasGenerated = reader.ReadBool();
|
|
|
|
Battle = new VvVBattle(reader, this);
|
|
|
|
int count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Item item = reader.ReadItem();
|
|
|
|
if (item != null)
|
|
AddVvVItem(item);
|
|
}
|
|
|
|
count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Guild g = reader.ReadGuild() as Guild;
|
|
VvVGuildStats stats = new VvVGuildStats(g, reader);
|
|
|
|
if (g != null)
|
|
GuildStats[g] = stats;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (version == 1)
|
|
Timer.DelayCall(FixVvVItems);
|
|
|
|
if (Enabled && !enabled)
|
|
{
|
|
Timer.DelayCall(() =>
|
|
{
|
|
Server.Factions.Generator.RemoveFactions();
|
|
CreateSilverTraders();
|
|
});
|
|
}
|
|
else if (!Enabled && enabled)
|
|
{
|
|
DeleteSilverTraders();
|
|
}
|
|
|
|
if (EnhancedRules && !enhanced)
|
|
{
|
|
OnEnhancedRulesEnabled();
|
|
}
|
|
}
|
|
|
|
public void FixVvVItems()
|
|
{
|
|
foreach (var item in VvVItems.Where(i => i is Spellbook))
|
|
{
|
|
var book = item as Spellbook;
|
|
var attrs = RunicReforging.GetNegativeAttributes(item);
|
|
|
|
if (attrs != null)
|
|
{
|
|
attrs.Antique = 0;
|
|
}
|
|
|
|
book.MaxHitPoints = 0;
|
|
book.HitPoints = 0;
|
|
}
|
|
}
|
|
|
|
public static void CreateSilverTraders()
|
|
{
|
|
if (!Enabled)
|
|
return;
|
|
|
|
Map map = Map.Felucca;
|
|
|
|
foreach (CityInfo info in CityInfo.Infos.Values)
|
|
{
|
|
IPooledEnumerable eable = map.GetMobilesInRange(info.TraderLoc, 3);
|
|
bool found = false;
|
|
|
|
foreach (Mobile m in eable)
|
|
{
|
|
if (m is SilverTrader)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if (!found)
|
|
{
|
|
SilverTrader trader = new SilverTrader();
|
|
trader.MoveToWorld(info.TraderLoc, map);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void DeleteSilverTraders()
|
|
{
|
|
var list = new List<Mobile>(World.Mobiles.Values.Where(m => m is SilverTrader));
|
|
|
|
foreach (var mob in list)
|
|
{
|
|
mob.Delete();
|
|
}
|
|
|
|
ColUtility.Free(list);
|
|
}
|
|
|
|
public static List<PlayerMobile> ShowNewRules { get; private set; }
|
|
|
|
private static void OnEnhancedRulesEnabled()
|
|
{
|
|
if (Instance == null || !Enabled)
|
|
return;
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1), () =>
|
|
{
|
|
foreach (var pm in World.Mobiles.Values.OfType<PlayerMobile>().Where(pm => IsVvV(pm)))
|
|
{
|
|
VvVPlayerEntry entry = Instance.GetPlayerEntry<VvVPlayerEntry>(pm);
|
|
|
|
if (entry != null)
|
|
{
|
|
if (ShowNewRules == null)
|
|
ShowNewRules = new List<PlayerMobile>();
|
|
|
|
ShowNewRules.Add(pm);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private static string _EnhancedRulesNotice = String.Format("Notice: The Vice Vs Virtue system has recently enabled enhanced rules. To avoid any issues and " +
|
|
"unexpected deaths due to the new game mechanics, it is important that you read this message. " +
|
|
"<br><br>New VvV Mechanics:<br><br>" +
|
|
"- VvV combatants are attackable on all facets.<br>" +
|
|
"- Uncontested VvV battles will reduce reduce reward silver by {0}%.<br>" +
|
|
"- VvV players in the battle region during a battle will be subject to combat heat travel restrictions.", VvVBattle.Penalty * 100);
|
|
|
|
public static bool RestrictSilver(Mobile a, Mobile b)
|
|
{
|
|
Account accountA = a.Account as Account;
|
|
Account accountB = b.Account as Account;
|
|
|
|
return accountA != null && (accountA == accountB || PVPArenaSystem.IsSameIP(a, b));
|
|
}
|
|
}
|
|
|
|
public class VvVPlayerEntry : PointsEntry
|
|
{
|
|
private bool _Active;
|
|
|
|
public bool OneTimePointsRetention { get; set; }
|
|
|
|
public int TotalKills { get; set; }
|
|
public int TotalDeaths { get; set; }
|
|
|
|
public EnemyKilledEntry KilledEntry { get; set; }
|
|
|
|
public int Score { get; set; }
|
|
public int Kills { get; set; }
|
|
public int Deaths { get; set; }
|
|
public int Assists { get; set; }
|
|
public int ReturnedSigils { get; set; }
|
|
public int DisarmedTraps { get; set; }
|
|
public int StolenSigils { get; set; }
|
|
|
|
public Guild Guild
|
|
{
|
|
get
|
|
{
|
|
return Player != null ? Player.Guild as Guild : null;
|
|
}
|
|
}
|
|
|
|
public bool Active
|
|
{
|
|
get
|
|
{
|
|
return _Active;
|
|
}
|
|
set
|
|
{
|
|
if (!_Active && value)
|
|
{
|
|
if (OneTimePointsRetention)
|
|
{
|
|
OneTimePointsRetention = false;
|
|
}
|
|
else
|
|
{
|
|
Points = 0;
|
|
}
|
|
}
|
|
|
|
_Active = value;
|
|
}
|
|
}
|
|
|
|
public DateTime ResignExpiration { get; set; }
|
|
public bool Resigning { get { return ResignExpiration > DateTime.MinValue; } }
|
|
|
|
public VvVPlayerEntry(PlayerMobile pm)
|
|
: base(pm)
|
|
{
|
|
_Active = true;
|
|
Points = ViceVsVirtueSystem.StartSilver;
|
|
}
|
|
|
|
public void AwardSilver(Mobile victim)
|
|
{
|
|
if (!ViceVsVirtueSystem.RestrictSilver(Player, victim))
|
|
{
|
|
Player.SendMessage("You cannot earn silver from killing {0}!", victim.Name);
|
|
return;
|
|
}
|
|
|
|
var entry = KilledEntry;
|
|
|
|
if (entry == null)
|
|
{
|
|
KilledEntry = entry = new EnemyKilledEntry(victim);
|
|
}
|
|
else
|
|
{
|
|
if (entry.Expired)
|
|
{
|
|
entry.TimesKilled = 1;
|
|
}
|
|
else
|
|
{
|
|
entry.TimesKilled++;
|
|
}
|
|
}
|
|
|
|
if (entry.TimesKilled > EnemyKilledEntry.MaxKillsForSilver)
|
|
{
|
|
Player.SendMessage("You cannot earn any more silver from killing {0}.", victim.Name);
|
|
}
|
|
|
|
int silver = (int)((double)EnemyKilledEntry.KillSilver / (double)entry.TimesKilled);
|
|
|
|
if (silver > 0)
|
|
{
|
|
Player.SendLocalizedMessage(1042736, String.Format("{0:N0} silver\t{1}", silver, victim.Name));
|
|
// You have earned ~1_SILVER_AMOUNT~ pieces for vanquishing ~2_PLAYER_NAME~!
|
|
|
|
Points += silver;
|
|
}
|
|
}
|
|
|
|
public class EnemyKilledEntry
|
|
{
|
|
public static int KillSilver = 20;
|
|
public static int MaxKillsForSilver = 5;
|
|
public static TimeSpan ExpireTime = TimeSpan.FromHours(3);
|
|
|
|
public Mobile Killed { get; set; }
|
|
public int TimesKilled { get; set; }
|
|
public DateTime Expires { get; set; }
|
|
|
|
public bool Expired { get { return Expires < DateTime.UtcNow; } }
|
|
|
|
public EnemyKilledEntry(Mobile killed)
|
|
{
|
|
Killed = killed;
|
|
TimesKilled = 1;
|
|
Expires = DateTime.UtcNow + ExpireTime;
|
|
}
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
writer.Write(4);
|
|
|
|
writer.Write(OneTimePointsRetention);
|
|
|
|
writer.Write(TotalDeaths);
|
|
writer.Write(TotalKills);
|
|
|
|
writer.Write(Active);
|
|
|
|
writer.Write(Score);
|
|
writer.Write(Kills);
|
|
writer.Write(Deaths);
|
|
writer.Write(Assists);
|
|
writer.Write(ReturnedSigils);
|
|
writer.Write(DisarmedTraps);
|
|
writer.Write(StolenSigils);
|
|
writer.Write(ResignExpiration);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
int version = reader.ReadInt();
|
|
|
|
switch (version)
|
|
{
|
|
case 4:
|
|
OneTimePointsRetention = reader.ReadBool();
|
|
goto case 3;
|
|
case 3:
|
|
TotalDeaths = reader.ReadInt();
|
|
TotalKills = reader.ReadInt();
|
|
goto case 2;
|
|
case 2:
|
|
Active = reader.ReadBool();
|
|
|
|
if(version == 0)
|
|
reader.ReadBool();
|
|
|
|
if(version < 2)
|
|
reader.ReadGuild();
|
|
|
|
Score = reader.ReadInt();
|
|
Kills = reader.ReadInt();
|
|
Deaths = reader.ReadInt();
|
|
Assists = reader.ReadInt();
|
|
ReturnedSigils = reader.ReadInt();
|
|
DisarmedTraps = reader.ReadInt();
|
|
StolenSigils = reader.ReadInt();
|
|
ResignExpiration = reader.ReadDateTime();
|
|
break;
|
|
}
|
|
|
|
if (version == 3)
|
|
{
|
|
OneTimePointsRetention = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TemporaryCombatant
|
|
{
|
|
public static TimeSpan TempCombatTime = TimeSpan.FromMinutes(10);
|
|
|
|
public Mobile From { get; private set; }
|
|
public Mobile Friendly { get; private set; }
|
|
public DateTime StartTime { get; private set; }
|
|
|
|
public Guild FriendlyGuild
|
|
{
|
|
get
|
|
{
|
|
if (Friendly == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Friendly.Guild as Guild;
|
|
}
|
|
}
|
|
|
|
public bool Expired { get { return StartTime + TempCombatTime < DateTime.UtcNow; } }
|
|
|
|
public TemporaryCombatant(Mobile from, Mobile friendlyTo)
|
|
{
|
|
From = from;
|
|
Friendly = friendlyTo;
|
|
StartTime = DateTime.UtcNow;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
StartTime = DateTime.UtcNow;
|
|
}
|
|
}
|
|
}
|