using System; using System.Collections.Generic; using Server.Engines.CannedEvil; using Server.Items; namespace Server.Mobiles { public abstract class BaseChampion : BaseCreature { public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18, 1, 0.1, 0.2) { } public BaseChampion(Serial serial) : base(serial) { } public override bool CanMoveOverObstacles => true; public override bool CanDestroyObstacles => true; public abstract ChampionSkullType SkullType { get; } public abstract Type[] UniqueList { get; } public abstract Type[] SharedList { get; } public abstract Type[] DecorativeList { get; } public abstract MonsterStatuetteType[] StatueTypes { get; } public virtual bool NoGoodies => false; public override void Serialize(IGenericWriter writer) { base.Serialize(writer); writer.Write(0); // version } public override void Deserialize(IGenericReader reader) { base.Deserialize(reader); var version = reader.ReadInt(); } public Item GetArtifact() { var random = Utility.RandomDouble(); if (random <= 0.05) { return CreateArtifact(UniqueList); } if (random <= 0.15) { return CreateArtifact(SharedList); } if (random <= 0.30) { return CreateArtifact(DecorativeList); } return null; } public Item CreateArtifact(Type[] list) { if (list.Length == 0) { return null; } var type = list.RandomElement(); var artifact = Loot.Construct(type); if (StatueTypes.Length > 0 && artifact is MonsterStatuette statuette) { statuette.Type = StatueTypes.RandomElement(); statuette.LootType = LootType.Regular; } return artifact; } private PowerScroll CreateRandomPowerScroll() { int level; var random = Utility.RandomDouble(); if (random <= 0.05) { level = 20; } else if (random <= 0.4) { level = 15; } else { level = 10; } return PowerScroll.CreateRandomNoCraft(level, level); } public void GivePowerScrolls() { if (Map != Map.Felucca) { return; } var toGive = new List(); var rights = GetLootingRights(DamageEntries, HitsMax); for (var i = rights.Count - 1; i >= 0; --i) { var ds = rights[i]; if (ds.m_HasRight) { toGive.Add(ds.m_Mobile); } } if (toGive.Count == 0) { return; } for (var i = 0; i < toGive.Count; i++) { var m = toGive[i]; if (!(m is PlayerMobile)) { continue; } var gainedPath = false; var pointsToGain = 800; if (VirtueHelper.Award(m, VirtueName.Valor, pointsToGain, ref gainedPath)) { if (gainedPath) { m.SendLocalizedMessage(1054032); // You have gained a path in Valor! } else { m.SendLocalizedMessage(1054030); // You have gained in Valor! } // No delay on Valor gains } } // Randomize toGive.Shuffle(); for (var i = 0; i < 6; ++i) { var m = toGive[i % toGive.Count]; var ps = CreateRandomPowerScroll(); GivePowerScrollTo(m, ps); } } public static void GivePowerScrollTo(Mobile m, PowerScroll ps) { if (ps == null || m == null) // sanity { return; } m.SendLocalizedMessage(1049524); // You have received a scroll of power! if (!Core.SE || m.Alive) { m.AddToBackpack(ps); } else { if (m.Corpse?.Deleted == false) { m.Corpse.DropItem(ps); } else { m.AddToBackpack(ps); } } if (!(m is PlayerMobile pm)) { return; } for (var j = 0; j < pm.JusticeProtectors.Count; ++j) { var prot = pm.JusticeProtectors[j]; if (prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion(pm, prot)) { continue; } var chance = VirtueHelper.GetLevel(prot, VirtueName.Justice) switch { VirtueLevel.Seeker => 60, VirtueLevel.Follower => 80, VirtueLevel.Knight => 100, _ => 0 }; if (chance > Utility.Random(100)) { var powerScroll = new PowerScroll(ps.Skill, ps.Value); prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice! if (!Core.SE || prot.Alive) { prot.AddToBackpack(powerScroll); } else { if (prot.Corpse?.Deleted == false) { prot.Corpse.DropItem(powerScroll); } else { prot.AddToBackpack(powerScroll); } } } } } public override bool OnBeforeDeath() { if (!NoKillAwards) { GivePowerScrolls(); if (NoGoodies) { return base.OnBeforeDeath(); } var map = Map; if (map != null) { for (var x = -12; x <= 12; ++x) { for (var y = -12; y <= 12; ++y) { var dist = Math.Sqrt(x * x + y * y); if (dist <= 12) { new GoodiesTimer(map, X + x, Y + y).Start(); } } } } } return base.OnBeforeDeath(); } public override void OnDeath(Container c) { if (Map == Map.Felucca) { // TODO: Confirm SE change or AoS one too? var rights = GetLootingRights(DamageEntries, HitsMax); var toGive = new List(); for (var i = rights.Count - 1; i >= 0; --i) { var ds = rights[i]; if (ds.m_HasRight) { toGive.Add(ds.m_Mobile); } } if (toGive.Count > 0) { toGive.RandomElement().AddToBackpack(new ChampionSkull(SkullType)); } else { c.DropItem(new ChampionSkull(SkullType)); } } base.OnDeath(c); } private class GoodiesTimer : Timer { private readonly Map m_Map; private readonly int m_X; private readonly int m_Y; public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0)) { m_Map = map; m_X = x; m_Y = y; } protected override void OnTick() { var z = m_Map.GetAverageZ(m_X, m_Y); var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false); for (var i = -3; !canFit && i <= 3; ++i) { canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false); if (canFit) { z += i; } } if (!canFit) { return; } var g = new Gold(500, 1000); g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map); if (Utility.RandomDouble() <= 0.5) { switch (Utility.Random(3)) { case 0: // Fire column { Effects.SendLocationParticles( EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052 ); Effects.PlaySound(g, 0x208); break; } case 1: // Explosion { Effects.SendLocationParticles( EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), 0x36BD, 20, 10, 5044 ); Effects.PlaySound(g, 0x307); break; } case 2: // Ball of fire { Effects.SendLocationParticles( EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration), 0x36FE, 10, 10, 5052 ); break; } } } } } } }