Files
abysmal-isle/Scripts/Services/Expansions/High Seas/Mobiles/Charybdis.cs
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

777 lines
25 KiB
C#

using System;
using Server;
using Server.Multis;
using System.Collections.Generic;
using Server.Items;
using Server.Misc;
using System.Linq;
namespace Server.Mobiles
{
public class Charydbis : BaseSeaChampion
{
public static readonly TimeSpan SpawnRate = TimeSpan.FromSeconds(30);
public static readonly TimeSpan TeleportRate = TimeSpan.FromSeconds(60);
public static readonly int SpawnMax = 25;
private List<Mobile> m_Tentacles = new List<Mobile>();
private DateTime m_NextSpawn;
private DateTime m_NextTeleport;
public override bool CanDamageBoats { get { return true; } }
public override TimeSpan BoatDamageCooldown { get { return TimeSpan.FromSeconds(Utility.RandomMinMax(45, 80)); } }
public override int MinBoatDamage { get { return 5; } }
public override int MaxBoatDamage { get { return 15; } }
public override int DamageRange { get { return 10; } }
public override int Meat { get { return 5; } }
public override double TreasureMapChance { get { return .50; } }
public override int TreasureMapLevel { get { return 7; } }
public override Type[] UniqueList { get { return new Type[] { typeof(FishermansHat), typeof(FishermansVest), typeof(FishermansEelskinGloves), typeof(FishermansTrousers) }; } }
public override Type[] SharedList { get { return new Type[] { typeof(HelmOfVengence), typeof(RingOfTheSoulbinder), typeof(RuneEngravedPegLeg), typeof(CullingBlade) }; } }
public override Type[] DecorativeList { get { return new Type[] { typeof(EnchantedBladeDeed), typeof(EnchantedVortexDeed) }; } }
[CommandProperty(AccessLevel.GameMaster)]
public int Tentacles { get { return m_Tentacles.Count; } }
[Constructable]
public Charydbis() : this(null) { }
public Charydbis(Mobile fisher)
: base(fisher, AIType.AI_Mage, FightMode.Closest)
{
RangeFight = 8;
Name = "charydbis";
Body = 1244;
BaseSoundID = 353;
m_NextSpawn = DateTime.UtcNow + SpawnRate;
m_NextTeleport = DateTime.UtcNow + TeleportRate;
CanSwim = true;
CantWalk = true;
SetStr(533, 586);
SetDex(113, 131);
SetInt(110, 155);
SetHits(30000);
SetMana(8000);
SetDamage(24, 33);
SetDamageType(ResistanceType.Physical, 50);
SetDamageType(ResistanceType.Energy, 50);
SetResistance(ResistanceType.Physical, 70, 80);
SetResistance(ResistanceType.Fire, 70, 80);
SetResistance(ResistanceType.Cold, 45, 55);
SetResistance(ResistanceType.Poison, 80, 90);
SetResistance(ResistanceType.Energy, 60, 70);
SetSkill(SkillName.Wrestling, 120.1, 121.2);
SetSkill(SkillName.Tactics, 120.15, 123.1);
SetSkill(SkillName.MagicResist, 165.2, 178.7);
SetSkill(SkillName.Anatomy, 111.0, 111.7);
SetSkill(SkillName.Magery, 134.6, 140.5);
SetSkill(SkillName.EvalInt, 200.8, 243.6);
SetSkill(SkillName.Meditation, 565);
Fame = 32000;
Karma = -32000;
if (IsSoulboundEnemies)
IsSoulbound = true;
}
public void AddTentacle(Mobile tent)
{
if (!m_Tentacles.Contains(tent))
m_Tentacles.Add(tent);
}
public void RemoveTentacle(Mobile tent)
{
if (m_Tentacles.Contains(tent))
m_Tentacles.Remove(tent);
}
public override void OnThink()
{
base.OnThink();
if (m_NextSpawn < DateTime.UtcNow && m_Tentacles.Count < SpawnMax)
SpawnTentacle();
if (m_NextTeleport < DateTime.UtcNow)
DoTeleport();
}
private Point3D m_LastLocation;
private Map m_LastMap;
public void DoTeleport()
{
var combatant = Combatant as Mobile;
if (combatant == null)
{
m_NextTeleport = DateTime.UtcNow + TeleportRate;
return;
}
m_LastLocation = Location;
m_LastMap = Map;
DoTeleportEffects(m_LastLocation, m_LastMap);
Hidden = true;
Internalize();
DoAreaLightningAttack(combatant);
Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(3), FinishTeleport, combatant);
m_NextTeleport = DateTime.UtcNow + TeleportRate;
}
public void FinishTeleport(Mobile combatant)
{
Point3D focusLoc;
if (combatant == null || combatant.Map == null)
{
focusLoc = Location;
}
else
{
focusLoc = combatant.Location;
}
Map map = m_LastMap;
Point3D newLoc = Point3D.Zero;
BaseBoat boat = BaseBoat.FindBoatAt(focusLoc, map);
for (int i = 0; i < 25; i++)
{
if (boat != null)
{
newLoc = GetValidPoint(boat, map, 10);
}
else
{
int x = focusLoc.X + Utility.RandomMinMax(-12, 12);
int y = focusLoc.Y + Utility.RandomMinMax(-12, 12);
int z = map.GetAverageZ(x, y);
newLoc = new Point3D(x, y, z);
}
LandTile t = map.Tiles.GetLandTile(newLoc.X, newLoc.Y);
if (!Spells.SpellHelper.CheckMulti(new Point3D(newLoc.X, newLoc.Y, newLoc.Z), map) && IsSeaTile(t))
break;
}
if (newLoc == Point3D.Zero || GetDistanceToSqrt(newLoc) > 15)
newLoc = m_LastLocation;
DoTeleportEffects(newLoc, map);
Hidden = false;
Timer.DelayCall(TimeSpan.FromSeconds(.5), new TimerStateCallback(TimedMoveToWorld), new object[] { newLoc, map, combatant });
}
public void TimedMoveToWorld(object o)
{
object[] ojs = (object[])o;
Point3D pnt = (Point3D)ojs[0];
Map map = ojs[1] as Map;
Mobile focus = ojs[2] as Mobile;
MoveToWorld(pnt, map);
Combatant = focus;
DoAreaLightningAttack(focus);
}
public void DoAreaLightningAttack(Mobile focus)
{
if (focus == null)
return;
BaseBoat boat = BaseBoat.FindBoatAt(focus, focus.Map);
if (boat != null)
{
foreach (var mob in boat.GetMobilesOnBoard().Where(m => CanBeHarmful(m, false) && m.Alive))
{
double damage = Math.Max(40, Utility.RandomMinMax(50, 100) * ((double)Hits / (double)HitsMax));
mob.BoltEffect(0);
AOS.Damage((Mobile)mob, this, (int)damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false);
mob.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
}
}
}
public void DoTeleportEffects(Point3D p, Map map)
{
for (int x = -2; x <= 2; x++)
{
for (int y = -2; y <= 2; y++)
{
if (Math.Abs(x) == 2 && Math.Abs(y) == 2)
continue;
Point3D pnt = new Point3D(p.X + x, p.Y + y, Map.GetAverageZ(p.X + x, p.Y + y));
Effects.SendLocationEffect(pnt, map, 0x3728, 16, 4);
}
}
Effects.PlaySound(p, map, 0x025);
Effects.PlaySound(p, map, 0x026);
Effects.PlaySound(p, map, 0x027);
}
private bool m_HasPushed;
public override void DoDamageBoat(BaseBoat boat)
{
if (boat == null)
return;
m_HasPushed = false;
IPoint2D pnt = boat;
if (Combatant != null && boat.Contains(Combatant))
pnt = Combatant;
Direction dir = Utility.GetDirection(this, pnt);
Point3DList path = new Point3DList();
for (int i = 0; i < DamageRange; i++)
{
int x = 0, y = 0;
switch ((int)dir)
{
case (int)Direction.Running:
case (int)Direction.North: { y -= i; break; }
case 129:
case (int)Direction.Right: { y -= i; x += i; break; }
case 130:
case (int)Direction.East: { x += i; break; }
case 131:
case (int)Direction.Down: { x += i; y += i; break; }
case 132:
case (int)Direction.South: { y += i; break; }
case 133:
case (int)Direction.Left: { y += i; x -= i; break; }
case 134:
case (int)Direction.West: { x -= i; break; }
case (int)Direction.ValueMask:
case (int)Direction.Up: { x -= i; y -= i; break; }
}
path.Add(X + x, Y + y, Z);
}
new EffectsTimer(this, path, dir, DamageRange);
}
public void OnTick(Point3DList path, Direction dir, int i)
{
if (path.Count > i)
{
Point3D point = path[i];
int o = i - 1;
Server.Effects.PlaySound(point, Map, 278);
Server.Effects.PlaySound(point, Map, 279);
for (int rn = 0; rn < (o * 2) + 1; rn++)
{
int y = 0, x = 0, y2 = 0, x2 = 0;
bool diag = false;
switch ((int)dir)
{
case (int)Direction.Running:
case (int)Direction.North: { x = x - o + rn; break; }
case 129:
case (int)Direction.Right: { x = x - o + rn; y = y - o + rn; break; }
case 130:
case (int)Direction.East: { y = y - o + rn; break; }
case 131:
case (int)Direction.Down: { y = y - o + rn; x = x + o - rn; break; }
case 132:
case (int)Direction.South: { x = x + o - rn; break; }
case 133:
case (int)Direction.Left: { x = x + o - rn; y = y + o - rn; break; }
case 134:
case (int)Direction.West: { y = y + o - rn; break; }
case (int)Direction.ValueMask:
case (int)Direction.Up: { y = y + o - rn; x = x - o + rn; break; }
}
switch ((int)dir)
{
case 129:
case (int)Direction.Right: { y2++; diag = true; break; }
case 131:
case (int)Direction.Down: { x2--; diag = true; break; }
case 133:
case (int)Direction.Left: { y2--; diag = true; break; }
case (int)Direction.ValueMask:
case (int)Direction.Up: { x2++; diag = true; break; }
default: { break; }
}
Point3D ep = new Point3D(point.X + x, point.Y + y, point.Z);
Point3D ep2 = new Point3D(ep.X + x2, ep.Y + y2, ep.Z);
if (diag && i >= ((2 * path.Count) / 3))
return;
Point3D p;
if (diag && rn < (o * 2))
p = ep2;
else
p = ep;
if (Spells.SpellHelper.CheckMulti(p, Map))
{
BaseBoat boat = BaseBoat.FindBoatAt(p, Map);
if (boat != null && !m_HasPushed)
{
int damage = Utility.RandomMinMax(MinBoatDamage, MaxBoatDamage);
boat.OnTakenDamage(this, damage);
boat.StartMove(dir, 1, 0x2, boat.SlowDriftInterval, true, false);
m_HasPushed = true;
}
continue;
}
LandTile t = Map.Tiles.GetLandTile(x, y);
if (IsSeaTile(t))
{
Mobile spawn = new EffectSpawn();
spawn.MoveToWorld(p, Map);
}
}
}
}
public class EffectSpawn : BaseCreature
{
public EffectSpawn()
: base(AIType.AI_Vendor, FightMode.Closest, 10, 1, 0.2, 0.4)
{
Body = 16;
BaseSoundID = 278;
CantWalk = true;
CanSwim = false;
Frozen = true;
SetHits(150000);
SetResistance(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Fire, 100);
SetResistance(ResistanceType.Cold, 100);
SetResistance(ResistanceType.Poison, 100);
SetResistance(ResistanceType.Energy, 100);
Timer.DelayCall(TimeSpan.FromSeconds(2), new TimerCallback(DoDelete));
}
public override bool DeleteCorpseOnDeath
{
get
{
return true;
}
}
public void DoDelete()
{
if (Alive)
Kill();
}
public override void OnDelete()
{
Effects.SendLocationEffect(Location, Map, 0x352D, 16, 4);
Effects.PlaySound(Location, Map, 0x364);
Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3728, 1, 14, 0, 7, 9915, 0);
base.OnDelete();
}
public override bool AutoDispel { get { return true; } }
public override double AutoDispelChance { get { return 1.0; } }
public override bool BardImmune { get { return true; } }
public override bool Unprovokable { get { return true; } }
public override bool Uncalmable { get { return true; } }
public override Poison PoisonImmune { get { return Poison.Lethal; } }
public override int Meat { get { return 1; } }
public EffectSpawn(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
private class EffectsTimer : Timer
{
private Direction m_Dir;
private int m_I, m_IMax;
private Point3DList m_Path;
private Charydbis m_Mobile;
public EffectsTimer(Charydbis mobile, Point3DList path, Direction dir, int imax)
: base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.25))
{
m_Dir = dir;
m_I = 1;
m_IMax = imax;
m_Path = path;
m_Mobile = mobile;
Priority = TimerPriority.FiftyMS;
Start();
}
protected override void OnTick()
{
m_Mobile.OnTick(m_Path, m_Dir, m_I);
if (m_I >= m_IMax)
{
Stop();
return;
}
m_I++;
}
}
public void SpawnTentacle()
{
if (Combatant == null)
{
m_NextSpawn = DateTime.UtcNow + SpawnRate;
return;
}
Map map = Map;
List<Mobile> list = new List<Mobile>();
IPooledEnumerable eable = GetMobilesInRange(15);
foreach (Mobile m in eable)
{
if (m == this || !CanBeHarmful(m))
continue;
if (m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != Team))
list.Add(m);
else if (m.Player)
list.Add(m);
}
eable.Free();
if (list.Count > 0)
{
Mobile spawn = list[Utility.Random(list.Count)];
BaseBoat boat = BaseBoat.FindBoatAt(spawn, map);
Point3D loc = spawn.Location;
for (int i = 0; i < 25; i++)
{
Point3D spawnLoc = Point3D.Zero;
if (boat != null)
spawnLoc = GetValidPoint(boat, map, 4);
else
{
int y = Utility.RandomMinMax(loc.X - 10, loc.Y + 10);
int x = Utility.RandomMinMax(loc.X - 10, loc.Y + 10);
int z = map.GetAverageZ(x, y);
spawnLoc = new Point3D(x, y, z);
}
if (Spells.SpellHelper.CheckMulti(spawnLoc, map))
continue;
LandTile t = map.Tiles.GetLandTile(spawnLoc.X, spawnLoc.Y);
if (IsSeaTile(t) && spawnLoc != Point3D.Zero)
{
GiantTentacle tent = new GiantTentacle(this);
tent.MoveToWorld(spawnLoc, map);
tent.Home = tent.Location;
tent.RangeHome = 15;
tent.Team = Team;
if (spawn != this)
tent.Combatant = spawn;
break;
}
}
}
m_NextSpawn = DateTime.UtcNow + SpawnRate;
}
public bool IsSeaTile(LandTile t)
{
return t.Z == -5 && ((t.ID >= 0xA8 && t.ID <= 0xAB) || (t.ID >= 0x136 && t.ID <= 0x137));
}
public override bool OnBeforeDeath()
{
if (Map == Map.Internal)
MoveToWorld(m_LastLocation, m_LastMap);
if (CharydbisSpawner.SpawnInstance != null && CharydbisSpawner.SpawnInstance.Charydbis == this)
CharydbisSpawner.SpawnInstance.OnCharybdisKilled();
return base.OnBeforeDeath();
}
public override void OnDeath(Container c)
{
base.OnDeath(c);
int drop = Utility.RandomMinMax(2, 5);
for (int i = 0; i < drop; i++)
{
Type pieType = m_Pies[Utility.Random(m_Pies.Length)];
Item pie = Loot.Construct(pieType);
if (pie != null)
c.DropItem(pie);
}
drop = Utility.RandomMinMax(2, 5);
for (int i = 0; i < drop; i++)
{
Type steakType = m_Steaks[Utility.Random(m_Steaks.Length)];
Item steak = Loot.Construct(steakType);
if (steak != null)
c.DropItem(steak);
}
c.DropItem(new MessageInABottle(c.Map));
c.DropItem(new SpecialFishingNet());
c.DropItem(new SpecialFishingNet());
c.DropItem(new SpecialFishingNet());
c.DropItem(new SpecialFishingNet());
FishingPole pole = new FishingPole();
BaseRunicTool.ApplyAttributesTo(pole, false, 0, Utility.RandomMinMax(2, 5), 50, 100);
c.DropItem(pole);
#region TOL
if (Core.TOL)
SkillMasteryPrimer.CheckPrimerDrop(this);
#endregion
}
public override void Delete()
{
if (m_Tentacles != null)
{
List<Mobile> tents = new List<Mobile>(m_Tentacles);
for (int i = 0; i < tents.Count; i++)
{
if (tents[i] != null)
tents[i].Kill();
}
}
base.Delete();
}
private Type[] m_Pies = new Type[]
{
typeof(AutumnDragonfishPie),
typeof(BlueLobsterPie),
typeof(BullFishPie),
typeof(CrystalFishPie),
typeof(FairySalmonPie),
typeof(FireFishPie),
typeof(GiantKoiPie),
typeof(GreatBarracudaPie),
typeof(HolyMackerelPie),
typeof(LavaFishPie),
typeof(ReaperFishPie),
typeof(SpiderCrabPie),
typeof(StoneCrabPie),
typeof(SummerDragonfishPie),
typeof(UnicornFishPie),
typeof(YellowtailBarracudaPie),
};
private Type[] m_Steaks = new Type[]
{
typeof(AutumnDragonfishSteak),
typeof(BlueLobsterMeat),
typeof(BullFishSteak),
typeof(CrystalFishSteak),
typeof(FairySalmonSteak),
typeof(FireFishSteak),
typeof(GiantKoiSteak),
typeof(GreatBarracudaSteak),
typeof(HolyMackerelSteak),
typeof(LavaFishSteak),
typeof(ReaperFishSteak),
typeof(SpiderCrabMeat),
typeof(StoneCrabMeat),
typeof(SummerDragonfishSteak),
typeof(UnicornFishSteak),
typeof(YellowtailBarracudaSteak),
};
public override void GenerateLoot()
{
AddLoot(LootPack.SuperBoss, 8);
}
public Charydbis(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0);
writer.Write(m_Tentacles.Count);
foreach (Mobile tent in m_Tentacles)
writer.Write(tent);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
int cnt = reader.ReadInt();
for (int i = 0; i < cnt; i++)
{
Mobile tent = reader.ReadMobile();
if (tent != null && !tent.Deleted && tent.Alive)
m_Tentacles.Add(tent);
}
m_NextSpawn = DateTime.UtcNow;
}
}
public class GiantTentacle : BaseCreature
{
private Mobile m_Master;
public GiantTentacle(Mobile master) : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4)
{
if (master is Charydbis)
{
m_Master = master;
((Charydbis)master).AddTentacle(this);
}
Name = "a giant tentacle";
Body = 1245;
BaseSoundID = 0x161;
CanSwim = true;
SetStr(127, 155);
SetDex(66, 85);
SetInt(102, 123);
SetHits(105, 113);
SetDamage(10, 15);
SetDamageType(ResistanceType.Physical, 50);
SetDamageType(ResistanceType.Cold, 50);
SetResistance(ResistanceType.Physical, 35, 45);
SetResistance(ResistanceType.Fire, 10, 25);
SetResistance(ResistanceType.Cold, 10, 25);
SetResistance(ResistanceType.Poison, 60, 70);
SetResistance(ResistanceType.Energy, 5, 10);
SetSkill(SkillName.Wrestling, 52.0, 70.0);
SetSkill(SkillName.Tactics, 0.0);
SetSkill(SkillName.MagicResist, 100.4, 113.5);
SetSkill(SkillName.Anatomy, 1.0, 0.0);
SetSkill(SkillName.Magery, 60.2, 72.4);
SetSkill(SkillName.EvalInt, 60.1, 73.4);
SetSkill(SkillName.Meditation, 100.0);
Fame = 2500;
Karma = -2500;
if (IsSoulboundEnemies)
IsSoulbound = true;
}
public override void GenerateLoot()
{
AddLoot(LootPack.FilthyRich, 1);
}
public override void Delete()
{
if (m_Master != null && m_Master is Charydbis)
((Charydbis)m_Master).RemoveTentacle(this);
base.Delete();
}
public GiantTentacle(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0);
writer.Write(m_Master);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Master = reader.ReadMobile();
}
}
}