515 lines
17 KiB
C#
515 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Mobiles;
|
|
using Server.Items;
|
|
using Server.Gumps;
|
|
using Server.Engines.PartySystem;
|
|
using System.Linq;
|
|
|
|
namespace Server.Engines.Shadowguard
|
|
{
|
|
[PropertyObject]
|
|
public abstract class ShadowguardEncounter
|
|
{
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ShadowguardController Controller { get { return ShadowguardController.Instance; } }
|
|
|
|
public Rectangle2D[] Bounds { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ShadowguardRegion Region { get { return Instance.Region; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Point3D StartLoc { get { return Def.StartLoc; } }
|
|
|
|
public Point3D[] SpawnPoints { get { return Def.SpawnPoints; } }
|
|
public Rectangle2D[] SpawnRecs { get { return Def.SpawnRecs; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public EncounterType Encounter { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool HasBegun { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool DoneWarning { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool Completed { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime StartTime { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Mobile PartyLeader { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ShadowguardInstance Instance { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool ForceExpire
|
|
{
|
|
get { return false; }
|
|
set
|
|
{
|
|
if (value)
|
|
Expire();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool ForceComplete
|
|
{
|
|
get { return false; }
|
|
set
|
|
{
|
|
if (value)
|
|
CompleteEncounter();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public BaseAddon Addon { get; set; }
|
|
|
|
public abstract Type AddonType { get; }
|
|
public EncounterDef Def { get { return Defs[Encounter]; } }
|
|
|
|
public virtual TimeSpan EncounterDuration { get { return TimeSpan.FromMinutes(30); } }
|
|
public virtual TimeSpan ResetDuration { get { return TimeSpan.FromSeconds(60); } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool Active { get { return Controller != null && Controller.Encounters.Contains(this); } }
|
|
|
|
public List<PlayerMobile> Participants { get; private set; } = new List<PlayerMobile>();
|
|
|
|
public ShadowguardEncounter(EncounterType encounter, ShadowguardInstance instance = null)
|
|
{
|
|
Encounter = encounter;
|
|
Instance = instance;
|
|
|
|
if(instance != null)
|
|
instance.Encounter = this;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Encounter.ToString();
|
|
}
|
|
|
|
public int PartySize()
|
|
{
|
|
if (PartyLeader == null || this.Region == null)
|
|
return 0;
|
|
|
|
int inRegion = this.Region.GetPlayerCount();
|
|
|
|
if (inRegion > 0)
|
|
return inRegion;
|
|
|
|
Party p = Party.Get(PartyLeader);
|
|
|
|
if (p == null)
|
|
return 1;
|
|
|
|
return p.Members.Count;
|
|
}
|
|
|
|
public void OnBeforeBegin(Mobile m)
|
|
{
|
|
PartyLeader = m;
|
|
StartTime = DateTime.UtcNow;
|
|
|
|
DoneWarning = false;
|
|
Setup();
|
|
|
|
SendPartyMessage(1156185);
|
|
/*Please wait while your Shadowguard encounter is prepared.
|
|
Do no leave the area or logoff during this time. You will
|
|
teleported when the encounter is ready and if you leave
|
|
there is no way to enter once the encounter begins.*/
|
|
CheckAddon();
|
|
|
|
Timer.DelayCall(ShadowguardController.ReadyDuration, OnBeginEncounter);
|
|
}
|
|
|
|
public void CheckAddon()
|
|
{
|
|
Type addon = AddonType;
|
|
|
|
if (addon == null)
|
|
return;
|
|
|
|
BaseAddon ad = Controller.Addons.FirstOrDefault(a => a.GetType() == addon && a.Map == Map.Internal);
|
|
|
|
if (ad == null)
|
|
{
|
|
ad = Activator.CreateInstance(addon) as BaseAddon;
|
|
Controller.Addons.Add(ad);
|
|
}
|
|
|
|
ad.MoveToWorld(new Point3D(Instance.Center.X - 1, Instance.Center.Y - 1, Instance.Center.Z), Map.TerMur);
|
|
Addon = ad;
|
|
}
|
|
|
|
public void OnBeginEncounter()
|
|
{
|
|
AddPlayers(PartyLeader);
|
|
HasBegun = true;
|
|
|
|
SendPartyMessage(1156251, 0x20);
|
|
//There is a 30 minute time limit for each encounter. You will receive a time limit warning at 5 minutes.
|
|
}
|
|
|
|
public void AddPlayers(Mobile m)
|
|
{
|
|
if(m == null || !m.Alive || !m.InRange(Controller.Location, 25) || m.NetState == null)
|
|
{
|
|
Reset(true);
|
|
}
|
|
else
|
|
{
|
|
Party p = Party.Get(m);
|
|
|
|
if(p != null)
|
|
{
|
|
foreach (var pm in p.Members.Select(x => x.Mobile))
|
|
{
|
|
AddPlayer(pm);
|
|
}
|
|
}
|
|
|
|
AddPlayer(m);
|
|
}
|
|
}
|
|
|
|
protected void SendPartyMessage(int cliloc, int hue = 0x3B2)
|
|
{
|
|
if (HasBegun)
|
|
{
|
|
foreach (var pm in Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
pm.SendLocalizedMessage(cliloc, null, hue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PartyLeader == null)
|
|
return;
|
|
|
|
Party p = Party.Get(PartyLeader);
|
|
|
|
if (p != null)
|
|
p.Members.ForEach(info => info.Mobile.SendLocalizedMessage(cliloc, null, hue));
|
|
else
|
|
PartyLeader.SendLocalizedMessage(cliloc, null, hue);
|
|
}
|
|
}
|
|
|
|
protected void SendPartyMessage(string message, int hue = 0x3B2)
|
|
{
|
|
if (HasBegun)
|
|
{
|
|
foreach (var pm in Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
pm.SendMessage(hue, message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PartyLeader == null)
|
|
return;
|
|
|
|
Party p = Party.Get(PartyLeader);
|
|
|
|
if (p != null)
|
|
p.Members.ForEach(info => info.Mobile.SendMessage(hue, message));
|
|
else
|
|
PartyLeader.SendMessage(hue, message);
|
|
}
|
|
}
|
|
|
|
public void AddPlayer(Mobile m)
|
|
{
|
|
Point3D p = StartLoc;
|
|
ConvertOffset(ref p);
|
|
|
|
MovePlayer(m, p);
|
|
m.CloseGump(typeof(ShadowguardGump));
|
|
|
|
if (m is PlayerMobile)
|
|
{
|
|
Participants.Add((PlayerMobile)m);
|
|
}
|
|
}
|
|
|
|
public void DoWarning()
|
|
{
|
|
ColUtility.ForEach(this.Region.GetEnumeratedMobiles().Where(m => m is PlayerMobile), m =>
|
|
{
|
|
m.SendLocalizedMessage(1156252); // You have 5 minutes remaining in the encounter!
|
|
});
|
|
|
|
DoneWarning = true;
|
|
}
|
|
|
|
public virtual void Expire(bool message = true)
|
|
{
|
|
if(message)
|
|
{
|
|
ColUtility.ForEach(this.Region.GetEnumeratedMobiles().Where(m => m is PlayerMobile), m =>
|
|
{
|
|
m.SendLocalizedMessage(1156253, "", 0x32); // The encounter timer has expired!
|
|
});
|
|
}
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(5), () =>
|
|
{
|
|
Reset(true);
|
|
});
|
|
}
|
|
|
|
public virtual void CompleteEncounter()
|
|
{
|
|
if (Completed)
|
|
return;
|
|
|
|
Timer.DelayCall(ResetDuration, () =>
|
|
{
|
|
Reset();
|
|
});
|
|
|
|
if (this is RoofEncounter)
|
|
SendPartyMessage(1156250); // Congratulations! You have bested Shadowguard and prevented Minax from exploiting the Time Gate! You will be teleported out in a few minutes.
|
|
else
|
|
SendPartyMessage(1156244); //You have bested this tower of Shadowguard! You will be teleported out of the tower in 60 seconds!
|
|
|
|
Completed = true;
|
|
}
|
|
|
|
public virtual void Reset(bool expired = false)
|
|
{
|
|
if (!Active)
|
|
return;
|
|
|
|
Controller.OnEncounterComplete(this, expired);
|
|
|
|
RemovePlayers();
|
|
PartyLeader = null;
|
|
HasBegun = false;
|
|
|
|
ClearItems();
|
|
|
|
if (Addon != null)
|
|
{
|
|
Addon.Internalize();
|
|
Addon = null;
|
|
}
|
|
|
|
Instance.ClearRegion();
|
|
Instance.CompleteEncounter();
|
|
}
|
|
|
|
private void RemovePlayers()
|
|
{
|
|
ColUtility.ForEach(Region.GetEnumeratedMobiles().Where(
|
|
m => m is PlayerMobile ||
|
|
(m is BaseCreature &&
|
|
((BaseCreature)m).GetMaster() is PlayerMobile)),
|
|
m =>
|
|
{
|
|
MovePlayer(m, Controller.KickLocation, false);
|
|
|
|
if (m is PlayerMobile && Participants.Contains((PlayerMobile)m))
|
|
{
|
|
Participants.Remove((PlayerMobile)m);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void MovePlayer(Mobile m, Point3D p, bool pets = true)
|
|
{
|
|
if (pets)
|
|
BaseCreature.TeleportPets(m, p, m.Map);
|
|
|
|
m.MoveToWorld(p, m.Map);
|
|
Effects.SendLocationParticles(EffectItem.Create(m.Location, m.Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 5023);
|
|
m.PlaySound(0x1FE);
|
|
}
|
|
|
|
public virtual void OnTick()
|
|
{
|
|
}
|
|
|
|
public virtual void ClearItems()
|
|
{
|
|
}
|
|
|
|
public virtual void Setup()
|
|
{
|
|
}
|
|
|
|
public virtual void CheckEncounter()
|
|
{
|
|
}
|
|
|
|
public virtual void OnCreatureKilled(BaseCreature bc)
|
|
{
|
|
}
|
|
|
|
public void CheckPlayerStatus(Mobile m)
|
|
{
|
|
if (m is PlayerMobile)
|
|
{
|
|
foreach (var pm in Region.GetEnumeratedMobiles().OfType<PlayerMobile>())
|
|
{
|
|
if (pm.Alive && pm.NetState != null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
Expire(false);
|
|
SendPartyMessage(1156267); // All members of your party are dead, have logged off, or have chosen to exit Shadowguard. You will be removed from the encounter shortly.
|
|
}
|
|
}
|
|
|
|
public void ConvertOffset(ref Point3D p)
|
|
{
|
|
p = new Point3D(Instance.Center.X + p.X, Instance.Center.Y + p.Y, Instance.Center.Z + p.Z);
|
|
}
|
|
|
|
public void ConvertOffset(ref Rectangle2D rec)
|
|
{
|
|
rec = new Rectangle2D(Instance.Center.X + rec.X, Instance.Center.Y + rec.Y, rec.Width, rec.Height);
|
|
}
|
|
|
|
public virtual void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(2);
|
|
|
|
writer.WriteMobileList<PlayerMobile>(Participants);
|
|
|
|
writer.WriteDeltaTime(StartTime);
|
|
writer.Write(Completed);
|
|
writer.Write(HasBegun);
|
|
|
|
writer.Write(Instance.Index);
|
|
writer.Write(PartyLeader);
|
|
writer.Write(Addon);
|
|
}
|
|
|
|
public virtual void Deserialize(GenericReader reader)
|
|
{
|
|
int version = reader.ReadInt();
|
|
|
|
switch (version)
|
|
{
|
|
case 2:
|
|
Participants = reader.ReadStrongMobileList<PlayerMobile>();
|
|
goto case 1;
|
|
case 1:
|
|
StartTime = reader.ReadDeltaTime();
|
|
Completed = reader.ReadBool();
|
|
HasBegun = reader.ReadBool();
|
|
goto case 0;
|
|
case 0:
|
|
Instance = Controller.Instances[reader.ReadInt()];
|
|
PartyLeader = reader.ReadMobile();
|
|
Addon = reader.ReadItem() as BaseAddon;
|
|
break;
|
|
}
|
|
|
|
if (Instance != null)
|
|
{
|
|
Instance.Encounter = this;
|
|
}
|
|
|
|
if (Completed)
|
|
{
|
|
Timer.DelayCall(ResetDuration, () =>
|
|
{
|
|
Reset();
|
|
});
|
|
}
|
|
}
|
|
|
|
public static Dictionary<EncounterType, EncounterDef> Defs { get; set; }
|
|
|
|
public static void Configure()
|
|
{
|
|
Defs = new Dictionary<EncounterType, EncounterDef>();
|
|
|
|
Defs[EncounterType.Bar] = new EncounterDef(
|
|
new Point3D(0, 0, 0),
|
|
new Point3D[] { new Point3D(-16, 8, 0), new Point3D(-16, 4, 0), new Point3D(-16, -6, 0), new Point3D(-16, -10, 0) },
|
|
new Rectangle2D[] { new Rectangle2D(-15, -12, 1, 8), new Rectangle2D(-15, 2, 1, 8) });
|
|
|
|
Defs[EncounterType.Orchard] = new EncounterDef(
|
|
new Point3D(0, 0, 0),
|
|
new Point3D[] { new Point3D(-10, -11, 0), new Point3D(-18, -15, 0), new Point3D(-11, -19, 0), new Point3D(-17, -10, 0),
|
|
new Point3D(-21, 10, 0), new Point3D(-17, 16, 0), new Point3D(-13, 12, 0), new Point3D(-11, 18, 0),
|
|
new Point3D(10, -20, 0), new Point3D(10, -11, 0), new Point3D(14, -15, 0), new Point3D(17, -10, 0),
|
|
new Point3D(10, 10, 0), new Point3D(9, 16, 0), new Point3D(13, 16, 0), new Point3D(15, 10, 0)},
|
|
new Rectangle2D[] { });
|
|
|
|
Defs[EncounterType.Armory] = new EncounterDef(
|
|
new Point3D(0, 0, 0),
|
|
new Point3D[] { new Point3D(5, -7, 0), new Point3D(5, -9, 0), new Point3D(5, -11, 0), new Point3D(5, -13, 0),
|
|
new Point3D(5, -17, 0), new Point3D(5, -19, 0), new Point3D(5, -21, 0), new Point3D(5, 16, 0),
|
|
new Point3D(5, 18, 0), new Point3D(5, 11, 0), new Point3D(5, 9, 0),
|
|
new Point3D(-23, -10, 0), new Point3D(-20, -15, 0), new Point3D(-16, -19, 0),
|
|
|
|
new Point3D(9, 5, 0), new Point3D(11, 5, 0), new Point3D(16, 5, 0), new Point3D(18, 5, 0),
|
|
new Point3D(-21, 5, 0), new Point3D(-19, 5, 0), new Point3D(-17, 5, 0), new Point3D(-12, 5, 0),
|
|
new Point3D(-10, 5, 0), new Point3D(-8, 5, 0), new Point3D(-23, 5, 0),
|
|
new Point3D(-18, -17, 0), new Point3D(-10, -23, 0), new Point3D(-13, -21, 0)},
|
|
new Rectangle2D[] { new Rectangle2D(-25, -24, 18, 18), new Rectangle2D(-25, 4, 18, 18), new Rectangle2D(4, 20, 18, 18), new Rectangle2D(4, -6, 18, 18), });
|
|
|
|
Defs[EncounterType.Fountain] = new EncounterDef(
|
|
new Point3D(11, 11, 0),
|
|
new Point3D[] { new Point3D(-6, 7, 0), new Point3D(5, 7, 0), new Point3D(7, 5, 0), new Point3D(7, -6, 0) },
|
|
new Rectangle2D[] { new Rectangle2D(-24, 8, 45, 17), new Rectangle2D(-24, -25, 45, 16), new Rectangle2D(-25, -8, 16, 15), new Rectangle2D(8, -8, 16, 15 ),
|
|
new Rectangle2D(12, -4, 2, 6), new Rectangle2D(-4, 12, 6, 2)});
|
|
|
|
Defs[EncounterType.Belfry] = new EncounterDef(
|
|
new Point3D(15, 1, 0),
|
|
new Point3D[] { new Point3D(0, 0, 22), new Point3D(-5, -5, 22) },
|
|
new Rectangle2D[] { new Rectangle2D(8, -9, 15, 15), new Rectangle2D(-24, -9, 15, 18) });
|
|
|
|
Defs[EncounterType.Roof] = new EncounterDef(
|
|
new Point3D(-8, -8, 0),
|
|
new Point3D[] { new Point3D(0, 0, 30) },
|
|
new Rectangle2D[] { });
|
|
}
|
|
|
|
public static ShadowguardEncounter ConstructEncounter(EncounterType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
default:
|
|
case EncounterType.Bar: return new BarEncounter();
|
|
case EncounterType.Orchard: return new OrchardEncounter();
|
|
case EncounterType.Armory: return new ArmoryEncounter();
|
|
case EncounterType.Fountain: return new FountainEncounter();
|
|
case EncounterType.Belfry: return new BelfryEncounter();
|
|
case EncounterType.Roof: return new RoofEncounter();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class EncounterDef
|
|
{
|
|
public Point3D StartLoc { get; private set; }
|
|
public Point3D[] SpawnPoints { get; private set; }
|
|
public Rectangle2D[] SpawnRecs { get; private set; }
|
|
|
|
public EncounterDef(Point3D start, Point3D[] points, Rectangle2D[] recs)
|
|
{
|
|
StartLoc = start;
|
|
SpawnPoints = points;
|
|
SpawnRecs = recs;
|
|
}
|
|
}
|
|
}
|