ModernUO/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs
mark1145 053dbcbad0
fix: Fixes ignoring mobs, champions, party crash, slayer rarity, boat speedhack, etc (#1222)
* Movie ignore mobiles to Mobile class
* Allow necromancer familiars to ignore mobiles
* ChampionSpawn should not quietly fail when creating new spawn
* Fix client party crash bug: 2 people partied, the leader logs out, other client is hung
* Fix spellbooks creating with magery/meditation only and creating magery multiple times
* Fix BaseRunicTool.GetRandomSlayer() creating more undead slayers than intended
* Do not allow players to dismount each other whilst mounted
* Fix AOS onwards damage increase tooltip and wrong formula in AOS
* Fix monsters killing other monster revenants + animate dead should not attack player pets + other animates
* Fix fire steed having loot pack added twice
* Fix oaks spawn not able to create Unicorns + Kirins via Activator.CreateInstace() due to constructor having name as parametr
* Fix ignoreMobiles flag not propagated to client for non-players.
* Fix harrower tents not leeching from players
* Fix boats speedhacking around the map
* Fix bless, agility etc. not renewing duration
* Fix reveal always worked
* Fix empty constructor for steeds so they don't throw now.
2022-10-31 22:16:50 -07:00

399 lines
11 KiB
C#

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(
int bodyID, int itemID, AIType aiType, FightMode fightMode = FightMode.Closest,
int rangePerception = 10, int rangeFight = 1
) : base(aiType, fightMode, rangePerception, rangeFight)
{
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<Mobile>();
InternalItem = reader.ReadEntity<Item>();
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<BaseMount>();
if (m_Mount == null)
{
Delete();
}
break;
}
}
}
}
}