Files
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

775 lines
22 KiB
C#

using Server;
using System;
using System.Collections.Generic;
using Server.Mobiles;
using Server.Items;
using System.Linq;
using Server.Engines.Points;
using Server.Engines.Quests;
namespace Server.Engines.VoidPool
{
public enum VoidType
{
Abyss,
Repond,
Undead,
Reptile,
Elemental
}
public class VoidPoolController : Item
{
public static VoidPoolController InstanceTram { get; set; }
public static VoidPoolController InstanceFel { get; set; }
private readonly int RestartSpan = 15;
private readonly int PoolStartHits = 15;
private readonly int StartPointVariance = 8;
private readonly Point3D StartPoint1 = new Point3D(5592, 2012, 0);
private readonly Point3D StartPoint2 = new Point3D(5466, 2007, 0);
private readonly Point3D EndPoint = new Point3D(5500, 1998, 5);
private readonly Rectangle2D PoolWalls = new Rectangle2D(5495, 1993, 10, 10);
private bool _Active;
[CommandProperty(AccessLevel.GameMaster)]
public bool Active
{
get { return _Active; }
set
{
if(!value)
{
if(Timer != null)
{
Timer.Stop();
Timer = null;
}
if (Region != null)
{
Region.Unregister();
Region = null;
}
if (Level3Spawner != null)
{
Level3Spawner.Deactivate();
Level3Spawner = null;
}
}
else
{
if (Region == null)
{
Region = new VoidPoolRegion(this, this.Map);
Region.Register();
}
if(Timer == null)
{
Timer = Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), OnTick);
Timer.Start();
NextStart = DateTime.UtcNow + TimeSpan.FromMinutes(RestartSpan);
if(Region != null)
Region.SendRegionMessage(1152526, RestartSpan.ToString()); // The battle for the Void Pool will begin in ~1_VALUE~ minutes.
}
if (Level3Spawner == null)
{
Level3Spawner = new Level3Spawner(this);
}
}
_Active = value;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int Wave { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Stage
{
get
{
if (Wave < 2)
return 0;
return Math.Max(1, Wave / 5);
}
}
public List<WaveInfo> Waves { get; set; }
public List<WayPoint> WaypointsA { get; set; }
public List<WayPoint> WaypointsB { get; set; }
public int WaypointACount { get; set; }
public int WaypointBCount { get; set; }
public VoidPoolRegion Region { get; set; }
public Timer Timer { get; set; }
public Dictionary<Mobile, long> CurrentScore { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool OnGoing { get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public VoidType VoidType { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public DateTime NextStart { get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public DateTime NextWave { get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public int PoolHits { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int RespawnMin { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int RespawnMax { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Level3Spawner Level3Spawner { get; set; }
public VoidPoolController(Map map)
: base(3803)
{
Name = "Void Pool Controller";
Visible = false;
Movable = false;
PoolHits = PoolStartHits;
if(map == Map.Trammel)
{
if(InstanceTram != null)
Delete();
else
InstanceTram = this;
}
else if(map == Map.Felucca)
{
if(InstanceFel != null)
Delete();
else
InstanceFel = this;
}
else
Delete();
WaypointsA = new List<WayPoint>();
WaypointsB = new List<WayPoint>();
Region = new VoidPoolRegion(this, map);
Region.Register();
RespawnMin = 60;
RespawnMax = 90;
ClearSpawners();
Active = true;
ResetLevel3Spawners();
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster)
from.SendGump(new Server.Gumps.PropertiesGump(from, this));
}
private void OnTick()
{
if(!OnGoing && DateTime.UtcNow > NextStart && Region != null && Region.GetPlayerCount() > 0)
{
NextStart = DateTime.MaxValue;
OnGoing = true;
VoidType = (VoidType)Utility.Random(5);
PoolHits = PoolStartHits;
Wave = 0;
if (CurrentScore != null)
CurrentScore.Clear();
if (Waves != null)
{
Waves.Clear();
Waves.TrimExcess();
}
CurrentScore = new Dictionary<Mobile, long>();
Waves = new List<WaveInfo>();
Region.SendRegionMessage(1152527, 0x2B); // The battle for the Void Pool is beginning now!
if (WaypointACount != WaypointsA.Count || WaypointBCount != WaypointsB.Count)
Generate.AddWaypoints();
SpawnWave();
}
else if(OnGoing)
{
if(DateTime.UtcNow > NextWave)
{
SpawnWave();
}
IPooledEnumerable eable = this.Map.GetMobilesInBounds(PoolWalls);
foreach(Mobile m in eable)
{
if (!OnGoing)
break;
if(m is BaseCreature && !((BaseCreature)m).Controlled && !((BaseCreature)m).Summoned && Utility.RandomDouble() > 0.25)
OnVoidWallDamaged(m);
}
eable.Free();
}
}
public void SpawnWave()
{
Wave++;
Region.SendRegionMessage(1152528, Wave.ToString());
int toSpawn = (int)Math.Ceiling(Math.Max(5, Math.Sqrt(Wave) * 2) * 1.5);
List<BaseCreature> creatures = new List<BaseCreature>();
for (int i = 0; i < toSpawn; i++)
{
Point3D start = i % 2 == 0 ? StartPoint1 : StartPoint2;
for(int j = 0; j < 25; j++)
{
int x = start.X + Utility.RandomMinMax(start.X - (StartPointVariance / 2), start.X + (StartPointVariance / 2));
int y = start.Y + Utility.RandomMinMax(start.Y - (StartPointVariance / 2), start.Y + (StartPointVariance / 2));
int z = this.Map.GetAverageZ(x, y);
if(this.Map.CanSpawnMobile(x, y, z))
{
start = new Point3D(x, y, z);
break;
}
}
int ran = Utility.RandomMinMax(0, Stage < 10 ? 12 : Stage < 15 ? 14 : 15);
Type t;
switch(ran)
{
default:
case 0: case 1: case 3: case 4: t = SpawnTable[(int)VoidType][0]; break;
case 5: case 6: case 7: case 8: t = SpawnTable[(int)VoidType][1]; break;
case 9: case 10: case 11: t = SpawnTable[(int)VoidType][2]; break;
case 12: case 13: t = SpawnTable[(int)VoidType][3]; break;
case 14: case 15: t = SpawnTable[(int)VoidType][4]; break;
}
BaseCreature bc = Activator.CreateInstance(t, Wave, true) as BaseCreature;
if(bc != null)
{
bc.NoLootOnDeath = true;
Timer.DelayCall(TimeSpan.FromSeconds((double)i * .75), () =>
{
if (OnGoing)
{
bc.MoveToWorld(start, this.Map);
bc.Home = EndPoint;
bc.RangeHome = 1;
creatures.Add(bc);
bc.CurrentWayPoint = GetNearestWaypoint(bc);
}
else
bc.Delete();
});
}
}
var gate1 = new VoidPoolGate();
gate1.MoveToWorld(StartPoint1, Map);
Effects.PlaySound(StartPoint1, Map, 0x20E);
var gate2 = new VoidPoolGate();
gate2.MoveToWorld(StartPoint2, Map);
Effects.PlaySound(StartPoint2, Map, 0x20E);
Timer.DelayCall(TimeSpan.FromSeconds(toSpawn * .80), () =>
{
Effects.SendLocationParticles(EffectItem.Create(gate1.Location, gate1.Map, EffectItem.DefaultDuration), 0x376A, 9, 20, 5042);
Effects.PlaySound(gate1.GetWorldLocation(), gate1.Map, 0x201);
Effects.SendLocationParticles(EffectItem.Create(gate2.Location, gate2.Map, EffectItem.DefaultDuration), 0x376A, 9, 20, 5042);
Effects.PlaySound(gate2.GetWorldLocation(), gate2.Map, 0x201);
gate1.Delete();
gate2.Delete();
});
Waves.Add(new WaveInfo(Wave, creatures));
NextWave = GetNextWaveTime();
}
public WayPoint GetNearestWaypoint(Mobile m, int range = 15)
{
IPooledEnumerable eable = this.Map.GetItemsInRange(m.Location, range);
int closestRange = 15;
WayPoint closest = null;
foreach (Item item in eable)
{
int dist = 0;
if (item is WayPoint)
{
dist = (int)m.GetDistanceToSqrt(item);
if (dist < closestRange || closest == null)
{
closest = item as WayPoint;
closestRange = dist;
}
}
}
return closest;
}
public Item GetNearestVoidPoolWall(Mobile m)
{
IPooledEnumerable eable = this.Map.GetItemsInRange(m.Location, 5);
int closestRange = 5;
Item closest = null;
foreach (Item item in eable)
{
int dist = 0;
if (item.Name == "Void Pool")
{
dist = (int)m.GetDistanceToSqrt(item);
if (dist < closestRange || closest == null)
{
closest = item;
closestRange = dist;
}
}
}
return closest;
}
public DateTime GetNextWaveTime()
{
if(Wave == 1)
return DateTime.UtcNow + TimeSpan.FromSeconds(10);
int min = Math.Max(30, RespawnMin - Wave) + Utility.RandomMinMax(0, 10);
int max = Math.Max(45, RespawnMax - Wave) - Utility.RandomMinMax(0, 5);
return DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(min, max));
}
public void OnVoidWallDamaged(Mobile damager)
{
if(0.5 > Utility.RandomDouble())
PoolHits--;
Region.SendRegionMessage(1152529); // The Void Pool walls have been damaged! Defend the Void Pool!
Item item = GetNearestVoidPoolWall(damager);
if (item != null)
{
Point3D p = new Point3D(item.X, item.Y, item.Z + 5);
Effects.SendLocationParticles(EffectItem.Create(p, item.Map, EffectItem.DefaultDuration), Utility.RandomList(0x36BD, 0x36B0, 0x3728), 20, 10, 5044);
Effects.PlaySound(p, item.Map, 0x307);
}
if (PoolHits <= 0 && OnGoing)
{
OnGoing = false;
EndInvasion();
}
}
public void EndInvasion()
{
Region.SendRegionMessage(1152530); // Cora's forces have destroyed the Void Pool walls. The battle is lost!
VoidPoolStats.OnInvasionEnd(this /*CurrentScore, Wave*/);
NextStart = DateTime.UtcNow + TimeSpan.FromMinutes(RestartSpan);
Region.SendRegionMessage(1152526, RestartSpan.ToString()); // The battle for the Void Pool will begin in ~1_VALUE~ minutes.
List<Mobile> list = Region.GetPlayers();
foreach (Mobile m in list.Where(m => GetCurrentPoints(m) > 0))
PointsSystem.VoidPool.AwardPoints(m, GetCurrentPoints(m));
foreach(Mobile m in list.Where(m => CurrentScore.ContainsKey(m)))
{
m.SendLocalizedMessage(1152650, String.Format("{0}\t{1}\t{2}\t{3}", GetTotalWaves(m), Wave.ToString(), Wave.ToString(), CurrentScore[m]));
// During the battle, you helped fight back ~1_COUNT~ out of ~2_TOTAL~ waves of enemy forces. Your final wave was ~3_MAX~. Your total score for the battle was ~4_SCORE~ points.
if (m is PlayerMobile)
{
var quest = QuestHelper.GetQuest<AForcedSacraficeQuest>((PlayerMobile)m);
if (quest != null)
{
quest.CompleteQuest();
}
}
}
ColUtility.Free(list);
ClearSpawn(true);
}
public void OnCreatureKilled(BaseCreature killed)
{
if (Waves == null)
return;
Waves.ForEach(info =>
{
if (info.Creatures.Contains(killed))
{
List<DamageStore> list = killed.GetLootingRights();
list.Sort();
for (int i = 0; i < list.Count; i++)
{
DamageStore ds = list[i];
Mobile m = ds.m_Mobile;
if (ds.m_Mobile is BaseCreature && ((BaseCreature)ds.m_Mobile).GetMaster() is PlayerMobile)
m = ((BaseCreature)ds.m_Mobile).GetMaster();
if (!info.Credit.Contains(m))
info.Credit.Add(m);
if (!CurrentScore.ContainsKey(m))
CurrentScore[m] = killed.Fame / 998;
else
CurrentScore[m] += killed.Fame / 998;
}
list.Clear();
list.TrimExcess();
info.Creatures.Remove(killed);
if (info.Creatures.Count == 0)
{
foreach (Mobile m in info.Credit.Where(m => m.Region == this.Region && m is PlayerMobile))
{
double award = Math.Max(0, this.Map == Map.Felucca ? Stage * 2 : Stage);
if (award > 0)
{
//Score Bonus
if (!CurrentScore.ContainsKey(m))
CurrentScore[m] = Stage * 125;
else
CurrentScore[m] += Stage * 125;
}
}
}
if (killed.Corpse != null && !killed.Corpse.Deleted)
((Corpse)killed.Corpse).BeginDecay(TimeSpan.FromMinutes(1));
}
});
}
public void ClearSpawners()
{
if (Region == null)
return;
foreach (Item item in Region.GetEnumeratedItems().Where(i => i is ISpawner))
{
if (item is XmlSpawner)
{
((XmlSpawner)item).DoReset = true;
}
else if (item is Spawner)
{
((Spawner)item).RemoveSpawned();
((Spawner)item).Running = false;
}
}
}
public void ResetLevel3Spawners()
{
ResetLevel3Spawners(Map.Trammel);
ResetLevel3Spawners(Map.Felucca);
}
public void ResetLevel3Spawners(Map map)
{
Server.Region r = Server.Region.Find(new Point3D(5574, 1859, 0), map);
foreach (Item item in r.GetEnumeratedItems().Where(i => i is ISpawner
&& i.X >= 5501 && i.X <= 5627 && i.Y >= 1799 && i.Y <= 1927))
{
if (item is XmlSpawner)
{
((XmlSpawner)item).DoReset = true;
}
else if (item is Spawner)
{
((Spawner)item).RemoveSpawned();
((Spawner)item).Running = false;
}
}
}
public void ClearSpawn()
{
ClearSpawn(false);
}
public void ClearSpawn(bool effects)
{
if (Region == null)
return;
foreach (Mobile m in Region.GetEnumeratedMobiles().Where(m => m is CovetousCreature))
{
if (effects)
Effects.SendLocationEffect(m.Location, m.Map, 0xDDA, 30, 10, 0, 0);
m.Delete();
}
}
public int GetCurrentPoints(Mobile from)
{
if (Waves == null)
return 0;
int points = 0;
foreach (var info in Waves.Where(i => i.Credit.Contains(from)))
{
points += this.Map == Map.Felucca ? Stage * 2 : Stage;
}
return points;
}
public int GetTotalWaves(Mobile from)
{
return Waves.Where(i => i.Wave > 2 && i.Credit.Contains(from)).Count();
}
public static int GetPlayerScore(Dictionary<Mobile, long> score, Mobile m)
{
if(score == null || m == null || !score.ContainsKey(m))
return 0;
return (int)score[m];
}
public static Type[][] SpawnTable = new Type[][]
{
new Type[] { typeof(DaemonMongbat), typeof(GargoyleAssassin), typeof(CovetousDoppleganger), typeof(LesserOni), typeof(CovetousFireDaemon) },
new Type[] { typeof(LizardmanWitchdoctor), typeof(OrcFootSoldier), typeof(RatmanAssassin), typeof(OgreBoneCrusher), typeof(TitanRockHunter) },
new Type[] { typeof(AngeredSpirit), typeof(BoneSwordSlinger), typeof(VileCadaver), typeof(DiseasedLich), typeof(CovetousRevenant) },
new Type[] { typeof(WarAlligator), typeof(MagmaLizard), typeof(ViciousDrake), typeof(CorruptedWyvern), typeof(CovetousWyrm) },
new Type[] { typeof(CovetousEarthElemental),typeof(CovetousWaterElemental), typeof(VortexElemental), typeof(SearingElemental),typeof(VenomElemental) },
};
public override void Delete()
{
if(OnGoing)
EndInvasion();
if(Region != null)
{
Region.Unregister();
Region = null;
}
if(Timer != null)
{
Timer.Stop();
Timer = null;
}
foreach (var wp in WaypointsA.Where(w => w != null && !w.Deleted))
wp.Delete();
foreach (var wp in WaypointsB.Where(w => w != null && !w.Deleted))
wp.Delete();
if (Level3Spawner != null)
{
Level3Spawner.Deactivate();
Level3Spawner = null;
}
base.Delete();
}
public VoidPoolController(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)2);
if (Level3Spawner != null)
{
writer.Write(0);
Level3Spawner.Serialize(writer);
}
else
{
writer.Write(1);
}
writer.Write(RespawnMin);
writer.Write(RespawnMax);
writer.Write(_Active);
writer.Write(WaypointsA.Count);
writer.Write(WaypointsB.Count);
WaypointsA.ForEach(w => writer.Write(w));
WaypointsB.ForEach(w => writer.Write(w));
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 2:
case 1:
if (version == 1 || reader.ReadInt() == 0)
{
Level3Spawner = new Level3Spawner(reader, this);
}
goto case 0;
case 0:
if (version == 0)
{
Level3Spawner = new Level3Spawner(this);
Timer.DelayCall(() =>
{
ResetLevel3Spawners();
});
}
RespawnMin = reader.ReadInt();
RespawnMax = reader.ReadInt();
WaypointsA = new List<WayPoint>();
WaypointsB = new List<WayPoint>();
Active = reader.ReadBool();
NextStart = DateTime.UtcNow;
int counta = reader.ReadInt();
int countb = reader.ReadInt();
for (int i = 0; i < counta; i++)
{
WayPoint wp = reader.ReadItem() as WayPoint;
if (wp != null)
WaypointsA.Add(wp);
}
for (int i = 0; i < countb; i++)
{
WayPoint wp = reader.ReadItem() as WayPoint;
if (wp != null)
WaypointsB.Add(wp);
}
break;
}
if (Map == Map.Felucca)
InstanceFel = this;
else
InstanceTram = this;
Timer.DelayCall(TimeSpan.FromSeconds(10), () => { ClearSpawn(); ClearSpawners(); } );
}
}
public class VoidPoolGate : Static
{
public VoidPoolGate()
: base(0xF6C)
{
Light = LightType.Circle300;
}
public VoidPoolGate(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
reader.ReadInt();
Delete();
}
}
}
/*
wave 1: 13:30:00
wave 2: 13:30:10 +10
wave 3: 13:31:43 +93
wave 4: 13:33:05 +83
wave 5: 13:34:17 +75
wave 6: 13:35:08 +51
wave 7: 13:36:37 +89
wave 8: 13:37:38 +61
wave 9: 13:38:19 +41
wave 10: 13:39:32 +73
wave 11: 13:40:02 +30
wave 12: 13:40:54 +52
wave 13: 13:41:45 +51
wave 14: 13:42:36 +51
wave 15: 13:43:28 +52
wave 16: 13:44:09 +41
wave 17: 13:45:01 +52
*/