using System; using Server.Items; using Server.Multis; using Server.Network; using Server.Targeting; namespace Server.Mobiles { public abstract class BaseMount : BaseCreature, IMount { private Mobile m_Rider; public BaseMount( string name, int bodyID, int itemID, AIType aiType, FightMode fightMode, int rangePerception, int rangeFight, double activeSpeed, double passiveSpeed ) : base( aiType, fightMode, rangePerception, rangeFight, activeSpeed, passiveSpeed ) { Name = name; Body = bodyID; InternalItem = new MountItem(this, itemID); } public BaseMount(Serial serial) : base(serial) { } public virtual TimeSpan MountAbilityDelay => TimeSpan.Zero; [CommandProperty(AccessLevel.GameMaster)] public DateTime NextMountAbility { get; set; } protected Item InternalItem { get; private set; } public virtual bool AllowMaleRider => true; public virtual bool AllowFemaleRider => true; [Hue, CommandProperty(AccessLevel.GameMaster)] public override int Hue { get => base.Hue; set { base.Hue = value; if (InternalItem != null) { InternalItem.Hue = value; } } } [CommandProperty(AccessLevel.GameMaster)] public int ItemID { get => InternalItem?.ItemID ?? 0; set { if (InternalItem != null) { InternalItem.ItemID = value; } } } [CommandProperty(AccessLevel.GameMaster)] public Mobile Rider { get => m_Rider; set { if (m_Rider != value) { if (value == null) { var loc = m_Rider.Location; var map = m_Rider.Map; if (map == null || map == Map.Internal) { loc = m_Rider.LogoutLocation; map = m_Rider.LogoutMap; } Direction = m_Rider.Direction; Location = loc; Map = map; InternalItem?.Internalize(); } else { if (m_Rider != null) { Dismount(m_Rider); } Dismount(value); if (InternalItem != null) { value.AddItem(InternalItem); } value.Direction = Direction; Internalize(); if (value.Target is Bola.BolaTarget) { Target.Cancel(value); } } m_Rider = value; } } } public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill) { if (m_Rider == null) { return; } var attacker = from ?? m_Rider.FindMostRecentDamager(true); if (!(attacker == this || attacker == m_Rider || willKill || Core.Now < NextMountAbility) && DoMountAbility(amount, from)) { NextMountAbility = Core.Now + MountAbilityDelay; } } public override bool OnBeforeDeath() { Rider = null; return base.OnBeforeDeath(); } public override void OnAfterDelete() { InternalItem?.Delete(); InternalItem = null; base.OnAfterDelete(); } public override void OnDelete() { Rider = null; base.OnDelete(); } public override void Serialize(IGenericWriter writer) { base.Serialize(writer); writer.Write(1); // version writer.Write(NextMountAbility); writer.Write(m_Rider); writer.Write(InternalItem); } public override void Deserialize(IGenericReader reader) { base.Deserialize(reader); var version = reader.ReadInt(); switch (version) { case 1: { NextMountAbility = reader.ReadDateTime(); goto case 0; } case 0: { m_Rider = reader.ReadEntity(); InternalItem = reader.ReadEntity(); if (InternalItem == null) { Delete(); } break; } } } public virtual void OnDisallowedRider(Mobile m) { m.SendMessage("You may not ride this creature."); } public override void OnDoubleClick(Mobile from) { if (IsDeadPet) { return; } if (from.IsBodyMod && !from.Body.IsHuman) { if (Core.AOS) // You cannot ride a mount in your current form. { PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1062061, from.NetState); } else { from.SendLocalizedMessage(1061628); // You can't do that while polymorphed. } return; } if (!CheckMountAllowed(from)) { return; } if (from.Mounted) { from.SendLocalizedMessage(1005583); // Please dismount first. return; } if (from.Female ? !AllowFemaleRider : !AllowMaleRider) { OnDisallowedRider(from); return; } if (!DesignContext.Check(from)) { return; } if (from.HasTrade) { from.SendLocalizedMessage(1042317, "", 0x41); // You may not ride at this time return; } if (from.InRange(this, 1)) { var canAccess = from.AccessLevel >= AccessLevel.GameMaster || Controlled && ControlMaster == from || Summoned && SummonMaster == from; if (canAccess) { if (Poisoned) { PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1049692, from.NetState ); // This mount is too ill to ride. } else { Rider = from; } } else if (!Controlled && !Summoned) { // That mount does not look broken! You would have to tame it to ride it. PrivateOverheadMessage(MessageType.Regular, 0x3B2, 501263, from.NetState); } else { // This isn't your mount; it refuses to let you ride. PrivateOverheadMessage(MessageType.Regular, 0x3B2, 501264, from.NetState); } } else { from.SendLocalizedMessage(500206); // That is too far away to ride. } } public static void Dismount(Mobile m) { var mount = m.Mount; if (mount != null) { mount.Rider = null; } } // 1040024 You are still too dazed from being knocked off your mount to ride! // 1062910 You cannot mount while recovering from a bola throw. // 1070859 You cannot mount while recovering from a dismount special maneuver. public static bool CheckMountAllowed(Mobile mob) { var result = true; if (mob is PlayerMobile mobile) { if (mobile.MountBlockReason != BlockMountType.None) { mobile.SendLocalizedMessage((int)mobile.MountBlockReason); result = false; } if (mobile.Race == Race.Gargoyle) { mobile.PrivateOverheadMessage(MessageType.Regular, mobile.SpeechHue, 1112281, mobile.NetState); // Gargoyles are unable to ride animals. result = false; } } return result; } public virtual bool DoMountAbility(int damage, Mobile attacker) => false; } public class MountItem : Item, IMountItem { private BaseMount m_Mount; public MountItem(BaseMount mount, int itemID) : base(itemID) { Layer = Layer.Mount; Movable = false; m_Mount = mount; } public MountItem(Serial serial) : base(serial) { } public override double DefaultWeight => 0; public IMount Mount => m_Mount; public override void OnAfterDelete() { m_Mount?.Delete(); m_Mount = null; base.OnAfterDelete(); } public override DeathMoveResult OnParentDeath(Mobile parent) { if (m_Mount != null) { m_Mount.Rider = null; } return DeathMoveResult.RemainEquipped; } public override void Serialize(IGenericWriter writer) { base.Serialize(writer); writer.Write(0); // version writer.Write(m_Mount); } public override void Deserialize(IGenericReader reader) { base.Deserialize(reader); var version = reader.ReadInt(); switch (version) { case 0: { m_Mount = reader.ReadEntity(); if (m_Mount == null) { Delete(); } break; } } } } }