using ModernUO.Serialization; using System; using System.Runtime.CompilerServices; using Server.Engines.VeteranRewards; using Server.Multis; using Server.Spells; namespace Server.Mobiles { [SerializationGenerator(4, false)] public partial class EtherealMount : Item, IMount, IMountItem, IRewardItem { [SerializableField(0)] [SaveFlag(nameof(ShouldSerializeIsDonationItem))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool _isDonationItem; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ShouldSerializeIsDonationItem() => _isDonationItem; [SerializableField(1)] [SaveFlag(nameof(ShouldSerializeIsRewardItem))] [SerializedCommandProperty(AccessLevel.GameMaster)] public bool _isRewardItem; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ShouldSerializeIsRewardItem() => _isRewardItem; [Constructible] public EtherealMount(int itemID, int mountID) : base(itemID) { _mountedID = mountID; _regularID = itemID; _rider = null; Layer = Layer.Invalid; LootType = LootType.Blessed; } public override double DefaultWeight => 1.0; [SerializableProperty(2)] [CommandProperty(AccessLevel.GameMaster)] public int MountedID { get => _mountedID; set { if (_mountedID != value) { _mountedID = value; if (_rider != null) { ItemID = value; } this.MarkDirty(); } } } [SerializableProperty(3)] [CommandProperty(AccessLevel.GameMaster)] public int RegularID { get => _regularID; set { if (_regularID != value) { _regularID = value; if (_rider == null) { ItemID = value; } this.MarkDirty(); } } } public override bool DisplayLootType => false; public virtual int FollowerSlots => 1; public virtual int EtherealHue => 0x4001; [SerializableProperty(4)] [SaveFlag(nameof(ShouldSerializeRider))] [CommandProperty(AccessLevel.GameMaster)] public Mobile Rider { get => _rider; set { if (value != _rider) { if (value == null) { Internalize(); UnmountMe(); RemoveFollowers(); _rider = null; } else { if (_rider != null) { Dismount(_rider); } Dismount(value); RemoveFollowers(); _rider = value; AddFollowers(); MountMe(); } this.MarkDirty(); } } } private bool ShouldSerializeRider() => _rider != null; [CommandProperty(AccessLevel.GameMaster)] [SerializableProperty(5)] [SaveFlag(nameof(ShouldSerializeSteps))] public int Steps { get => _steps; set { _steps = Math.Clamp(value, 0, StepsMax); this.MarkDirty(); } } private bool ShouldSerializeSteps() => _steps != StepsMax; public virtual int StepsMax => 3840; // Should be same as horse public virtual int StepsGainedPerIdleTime => 1; public virtual TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(1); public void OnRiderDamaged(int amount, Mobile from, bool willKill) { } public IMount Mount => this; public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (_isDonationItem) { list.Add("Donation Ethereal"); list.Add("7.5 sec slower cast time if not a 9mo. Veteran"); } if (Core.ML && IsRewardItem) { list.Add(RewardSystem.GetRewardYearLabel(this, Array.Empty())); // X Year Veteran Reward } } public void RemoveFollowers() { if (_rider != null) { _rider.Followers -= Math.Min(_rider.Followers, FollowerSlots); } } public void AddFollowers() { if (_rider != null) { _rider.Followers += FollowerSlots; } } public virtual bool Validate(Mobile from) { if (Parent == null) { from.SayTo(from, 1010095); // This must be on your person to use. return false; } if (IsRewardItem && !RewardSystem.CheckIsUsableBy(from, this) || !BaseMount.CheckMountAllowed(from)) { return false; } if (from.Mounted) { from.SendLocalizedMessage(1005583); // Please dismount first. return false; } if (from.IsBodyMod && !from.Body.IsHuman) { from.SendLocalizedMessage(1061628); // You can't do that while polymorphed. return false; } if (from.HasTrade) { from.SendLocalizedMessage(1042317, "", 0x41); // You may not ride at this time return false; } if (from.Followers + FollowerSlots > from.FollowersMax) { from.SendLocalizedMessage(1049679); // You have too many followers to summon your mount. return false; } return DesignContext.Check(from); } public override void OnDoubleClick(Mobile from) { if (Validate(from)) { new EtherealSpell(this, from).Cast(); } } public override void OnSingleClick(Mobile from) { base.OnSingleClick(from); LabelTo(from, _isDonationItem ? "Donation Ethereal" : "Veteran Reward"); } private void Deserialize(IGenericReader reader, int version) { _isDonationItem = reader.ReadBool(); _isRewardItem = reader.ReadBool(); _mountedID = reader.ReadInt(); _regularID = reader.ReadInt(); _rider = reader.ReadEntity(); _steps = StepsMax; } [AfterDeserialization] private void AfterDeserialize() { AddFollowers(); } public override DeathMoveResult OnParentDeath(Mobile parent) { Rider = null; // get off, move to pack return DeathMoveResult.RemainEquipped; } public static void Dismount(Mobile m) { var mount = m.Mount; if (mount != null) { mount.Rider = null; } } public void UnmountMe() { var bp = _rider.Backpack; ItemID = _regularID; Layer = Layer.Invalid; Movable = true; if (Hue == EtherealHue) { Hue = 0; } if (bp != null) { bp.DropItem(this); } else { var loc = _rider.Location; var map = _rider.Map; if (map == null || map == Map.Internal) { loc = _rider.LogoutLocation; map = _rider.LogoutMap; } MoveToWorld(loc, map); } } public void MountMe() { ItemID = _mountedID; Layer = Layer.Mount; Movable = false; if (Hue == 0) { Hue = EtherealHue; } ProcessDelta(); _rider.ProcessDelta(); _rider.EquipItem(this); _rider.ProcessDelta(); ProcessDelta(); } public static void StopMounting(Mobile mob) { (mob.Spell as EtherealSpell)?.Stop(); } private class EtherealSpell : Spell { private static readonly SpellInfo m_Info = new("Ethereal Mount", "", 230); private readonly EtherealMount m_Mount; private readonly Mobile m_Rider; private bool m_Stop; public EtherealSpell(EtherealMount mount, Mobile rider) : base(rider, null, m_Info) { m_Rider = rider; m_Mount = mount; } public override bool ClearHandsOnCast => false; public override bool RevealOnCast => false; public override double CastDelayFastScalar => 0; public override TimeSpan CastDelayBase => TimeSpan.FromSeconds( m_Mount.IsDonationItem && RewardSystem.GetRewardLevel(m_Rider) < 3 ? 7.5 + (Core.AOS ? 3.0 : 2.0) : Core.AOS ? 3.0 : 2.0 ); public override TimeSpan GetCastRecovery() => TimeSpan.Zero; public override int GetMana() => 0; public override bool ConsumeReagents() => true; public override bool CheckFizzle() => true; public void Stop() { m_Stop = true; Disturb(DisturbType.Hurt, false); } public override bool CheckDisturb(DisturbType type, bool checkFirst, bool resistable) => type != DisturbType.EquipRequest && type != DisturbType.UseRequest; public override void DoHurtFizzle() { if (!m_Stop) { base.DoHurtFizzle(); } } public override void DoFizzle() { if (!m_Stop) { base.DoFizzle(); } } public override void OnDisturb(DisturbType type, bool message) { if (message && !m_Stop) { Caster.SendLocalizedMessage( 1049455 ); // You have been disrupted while attempting to summon your ethereal mount! } // m_Mount.UnmountMe(); } public override void OnCast() { if (!m_Mount.Deleted && m_Mount.Rider == null && m_Mount.Validate(m_Rider)) { m_Mount.Rider = m_Rider; } FinishSequence(); } } } [SerializationGenerator(0, false)] public partial class EtherealHorse : EtherealMount { [Constructible] public EtherealHorse() : base(0x20DD, 0x3EAA) { } public override int LabelNumber => 1041298; // Ethereal Horse Statuette } [SerializationGenerator(0, false)] public partial class EtherealLlama : EtherealMount { [Constructible] public EtherealLlama() : base(0x20F6, 0x3EAB) { } public override int LabelNumber => 1041300; // Ethereal Llama Statuette } [SerializationGenerator(0, false)] public partial class EtherealOstard : EtherealMount { [Constructible] public EtherealOstard() : base(0x2135, 0x3EAC) { } public override int LabelNumber => 1041299; // Ethereal Ostard Statuette } [SerializationGenerator(0, false)] public partial class EtherealRidgeback : EtherealMount { [Constructible] public EtherealRidgeback() : base(0x2615, 0x3E9A) { } public override int LabelNumber => 1049747; // Ethereal Ridgeback Statuette } [SerializationGenerator(0, false)] public partial class EtherealUnicorn : EtherealMount { [Constructible] public EtherealUnicorn() : base(0x25CE, 0x3E9B) { } public override int LabelNumber => 1049745; // Ethereal Unicorn Statuette } [SerializationGenerator(0, false)] public partial class EtherealBeetle : EtherealMount { [Constructible] public EtherealBeetle() : base(0x260F, 0x3E97) { } public override int LabelNumber => 1049748; // Ethereal Beetle Statuette } [SerializationGenerator(0, false)] public partial class EtherealKirin : EtherealMount { [Constructible] public EtherealKirin() : base(0x25A0, 0x3E9C) { } public override int LabelNumber => 1049746; // Ethereal Ki-Rin Statuette } [SerializationGenerator(0, false)] public partial class EtherealSwampDragon : EtherealMount { [Constructible] public EtherealSwampDragon() : base(0x2619, 0x3E98) { } public override int LabelNumber => 1049749; // Ethereal Swamp Dragon Statuette } [SerializationGenerator(0)] public partial class RideablePolarBear : EtherealMount { [Constructible] public RideablePolarBear() : base(0x20E1, 0x3EC5) { } public override int LabelNumber => 1076159; // Rideable Polar Bear public override int EtherealHue => 0; } [SerializationGenerator(0)] public partial class EtherealCuSidhe : EtherealMount { [Constructible] public EtherealCuSidhe() : base(0x2D96, 0x3E91) { } public override int LabelNumber => 1080386; // Ethereal Cu Sidhe Statuette } [SerializationGenerator(0)] public partial class EtherealHiryu : EtherealMount { [Constructible] public EtherealHiryu() : base(0x276A, 0x3E94) { } public override int LabelNumber => 1113813; // Ethereal Hiryu Statuette } [SerializationGenerator(0)] public partial class EtherealReptalon : EtherealMount { [Constructible] public EtherealReptalon() : base(0x2d95, 0x3e90) { } public override int LabelNumber => 1113812; // Ethereal Reptalon Statuette } [SerializationGenerator(0, false)] public partial class ChargerOfTheFallen : EtherealMount { [Constructible] public ChargerOfTheFallen() : base(0x2D9C, 0x3E92) { } public override int LabelNumber => 1074816; // Charger of the Fallen Statuette public override int EtherealHue => 0; } }