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 m_Tentacles = new List(); 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(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 list = new List(); 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 tents = new List(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(); } } }