using System; using Server.Items; using Server.Targeting; namespace Server.Mobiles { public abstract class BaseMount : BaseCreature, IMount { private Mobile m_Rider; private Item m_InternalItem; private DateTime m_NextMountAbility; public virtual TimeSpan MountAbilityDelay => TimeSpan.Zero; [CommandProperty( AccessLevel.GameMaster )] public DateTime NextMountAbility { get => m_NextMountAbility; set => m_NextMountAbility = value; } protected Item InternalItem => m_InternalItem; public virtual bool AllowMaleRider => true; public virtual bool AllowFemaleRider => true; 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; m_InternalItem = new MountItem( this, itemID ); } [Hue, CommandProperty( AccessLevel.GameMaster )] public override int Hue { get => base.Hue; set { base.Hue = value; if ( m_InternalItem != null ) m_InternalItem.Hue = value; } } public override bool OnBeforeDeath() { Rider = null; return base.OnBeforeDeath(); } public override void OnAfterDelete() { m_InternalItem?.Delete(); m_InternalItem = null; base.OnAfterDelete(); } public override void OnDelete() { Rider = null; base.OnDelete(); } public BaseMount( Serial serial ) : base( serial ) { } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); writer.Write( ( int )1 ); // version writer.Write( m_NextMountAbility ); writer.Write( m_Rider ); writer.Write( m_InternalItem ); } public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); switch ( version ) { case 1: { m_NextMountAbility = reader.ReadDateTime(); goto case 0; } case 0: { m_Rider = reader.ReadMobile(); m_InternalItem = reader.ReadItem(); if ( m_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( Network.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 ( !Multis.DesignContext.Check( from ) ) return; if ( from.HasTrade ) { from.SendLocalizedMessage( 1042317, "", 0x41 ); // You may not ride at this time return; } if ( from.InRange( this, 1 ) ) { bool canAccess = ( from.AccessLevel >= AccessLevel.GameMaster ) || ( Controlled && ControlMaster == from ) || ( Summoned && SummonMaster == from ); if ( canAccess ) { if ( Poisoned ) PrivateOverheadMessage( Network.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( Network.MessageType.Regular, 0x3B2, 501263, from.NetState ); } else { // This isn't your mount; it refuses to let you ride. PrivateOverheadMessage( Network.MessageType.Regular, 0x3B2, 501264, from.NetState ); } } else { from.SendLocalizedMessage( 500206 ); // That is too far away to ride. } } [CommandProperty( AccessLevel.GameMaster )] public int ItemID { get => m_InternalItem?.ItemID ?? 0; set { if ( m_InternalItem != null ) m_InternalItem.ItemID = value; } } public static void Dismount( Mobile m ) { IMount mount = m.Mount; if ( mount != null ) mount.Rider = null; } [CommandProperty( AccessLevel.GameMaster )] public Mobile Rider { get => m_Rider; set { if ( m_Rider != value ) { if ( value == null ) { Point3D loc = m_Rider.Location; Map 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; m_InternalItem?.Internalize(); } else { if ( m_Rider != null ) { Dismount( m_Rider ); } Dismount( value ); if ( m_InternalItem != null ) value.AddItem( m_InternalItem ); value.Direction = Direction; Internalize(); if ( value.Target is Bola.BolaTarget ) { Target.Cancel( value ); } } m_Rider = value; } } } // 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 ) { bool result = true; if ((mob is PlayerMobile mobile) && mobile.MountBlockReason != BlockMountType.None) { mobile.SendLocalizedMessage((int)mobile.MountBlockReason); result = false; } return result; } public virtual void OnRiderDamaged( int amount, Mobile from, bool willKill ) { if ( m_Rider == null ) return; Mobile attacker = from; if ( attacker == null ) attacker = m_Rider.FindMostRecentDamager( true ); if ( !(attacker == this || attacker == m_Rider || willKill || DateTime.UtcNow < m_NextMountAbility) ) { if ( DoMountAbility( amount, from ) ) m_NextMountAbility = DateTime.UtcNow + MountAbilityDelay; } } public virtual bool DoMountAbility( int damage, Mobile attacker ) { return false; } } public class MountItem : Item, IMountItem { private BaseMount m_Mount; public override double DefaultWeight => 0; public MountItem( BaseMount mount, int itemID ) : base( itemID ) { Layer = Layer.Mount; Movable = false; m_Mount = mount; } public MountItem( Serial serial ) : base( serial ) { } 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( GenericWriter writer ) { base.Serialize( writer ); writer.Write( (int) 0 ); // version writer.Write( m_Mount ); } public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); switch ( version ) { case 0: { m_Mount = reader.ReadMobile() as BaseMount; if ( m_Mount == null ) Delete(); break; } } } public IMount Mount => m_Mount; } }