## Special moves Special moves were added [27 Apr, 2000.](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2000-2/2000-publish-05-27th-april/). This is well past the discussed cutoff date for T2A, which is **[Nov 23, 1999 - Publish 1](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/1999-2/1999-publish-01-23rd-november/)** as per the UOSecondAge shard which is the golden standard for T2A accuracy and represents a logical cutoff for this era. Regarding the specific date: **Nov 23, 1999 (Publish 1)** - the game significantly changed as a "pre-patch" to UOR on this date, and UOSecondAge picks and chooses from this date based on QoL features, while keeping the gameplay accurate to what is widely known as the "T2A era". However this date is ~4 months before the actual UOR release date. ## Defensive wrestling Defensively wrestling as (eval+anat)/2 was added in [publish 16](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2002-2/publish-16-changes-and-bug-fixes-24th-july/) and should be gated behind !Core.LBR * "Unarmed “to-be-hit” changes (chance to get hit while unarmed is now the better of either Wrestling skill, or an average of Anatomy & Evaluate Intelligence)." ## Lumberjacking damage bonus Lumberjacking damage bonus was added in [UOR](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2000-2/2000-publish-05-27th-april/) ## Anatomy and LJ +10% GM bonus Anatomy/LJ +10% GM bonus was added in [pub 13](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2001-2/2001-publish-13-19th-august/), which was during UOTD.
287 lines
9 KiB
C#
287 lines
9 KiB
C#
using System;
|
|
using ModernUO.Serialization;
|
|
using Server.Engines.ConPVP;
|
|
|
|
namespace Server.Items
|
|
{
|
|
[SerializationGenerator(0, false)]
|
|
public partial class Fists : BaseMeleeWeapon
|
|
{
|
|
public Fists() : base(0)
|
|
{
|
|
Visible = false;
|
|
Movable = false;
|
|
Quality = WeaponQuality.Regular;
|
|
}
|
|
|
|
public override bool SkipSerialization => true;
|
|
|
|
public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm;
|
|
public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow;
|
|
|
|
public override int AosStrengthReq => 0;
|
|
public override int AosMinDamage => 1;
|
|
public override int AosMaxDamage => 4;
|
|
public override int AosSpeed => 50;
|
|
public override float MlSpeed => 2.50f;
|
|
|
|
public override int OldStrengthReq => 0;
|
|
public override int OldMinDamage => 1;
|
|
public override int OldMaxDamage => 8;
|
|
public override int OldSpeed => 30;
|
|
|
|
public override int DefHitSound => -1;
|
|
public override int DefMissSound => -1;
|
|
|
|
public override SkillName DefSkill => SkillName.Wrestling;
|
|
public override WeaponType DefType => WeaponType.Fists;
|
|
public override WeaponAnimation DefAnimation => WeaponAnimation.Wrestle;
|
|
|
|
public static void Initialize()
|
|
{
|
|
Mobile.DefaultWeapon = new Fists();
|
|
}
|
|
|
|
public override double GetDefendSkillValue(Mobile attacker, Mobile defender)
|
|
{
|
|
var wresValue = defender.Skills.Wrestling.Value;
|
|
|
|
if (!Core.LBR)
|
|
{
|
|
return wresValue;
|
|
}
|
|
|
|
var anatValue = defender.Skills.Anatomy.Value;
|
|
var evalValue = defender.Skills.EvalInt.Value;
|
|
var incrValue = Math.Min((anatValue + evalValue + 20.0) * 0.5, 120.0);
|
|
|
|
return wresValue > incrValue ? wresValue : incrValue;
|
|
}
|
|
|
|
private static void CheckPreAOSMoves(Mobile attacker, Mobile defender)
|
|
{
|
|
if (!Core.UOR || !attacker.CanBeginAction<Fists>())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (attacker.StunReady)
|
|
{
|
|
if (attacker.Skills.Anatomy.Value < 80.0 || attacker.Skills.Wrestling.Value < 80.0)
|
|
{
|
|
attacker.SendLocalizedMessage(1004008); // You are not skilled enough to stun your opponent.
|
|
attacker.StunReady = false;
|
|
return;
|
|
}
|
|
|
|
if (attacker.Stam < 15)
|
|
{
|
|
attacker.SendLocalizedMessage(1004009); // You are too fatigued to attempt anything.
|
|
return;
|
|
}
|
|
|
|
attacker.Stam -= 15;
|
|
|
|
if (CheckMove(attacker, SkillName.Anatomy))
|
|
{
|
|
StartMoveDelay(attacker);
|
|
|
|
attacker.StunReady = false;
|
|
|
|
attacker.SendLocalizedMessage(1004013); // You successfully stun your opponent!
|
|
defender.SendLocalizedMessage(1004014); // You have been stunned!
|
|
|
|
defender.Freeze(TimeSpan.FromSeconds(4.0));
|
|
}
|
|
else
|
|
{
|
|
attacker.SendLocalizedMessage(1004010); // You failed in your attempt to stun.
|
|
defender.SendLocalizedMessage(1004011); // Your opponent tried to stun you and failed.
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!attacker.DisarmReady)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!defender.Player && !defender.Body.IsHuman)
|
|
{
|
|
attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent.
|
|
return;
|
|
}
|
|
|
|
if (attacker.Skills.ArmsLore.Value < 80.0 || attacker.Skills.Wrestling.Value < 80.0)
|
|
{
|
|
attacker.SendLocalizedMessage(1004002); // You are not skilled enough to disarm your opponent.
|
|
attacker.DisarmReady = false;
|
|
return;
|
|
}
|
|
|
|
if (attacker.Stam < 15)
|
|
{
|
|
attacker.SendLocalizedMessage(1004003); // You are too fatigued to attempt anything.
|
|
return;
|
|
}
|
|
|
|
var toDisarm = defender.FindItemOnLayer(Layer.OneHanded);
|
|
|
|
if (toDisarm?.Movable != true)
|
|
{
|
|
toDisarm = defender.FindItemOnLayer(Layer.TwoHanded);
|
|
}
|
|
|
|
var pack = defender.Backpack;
|
|
|
|
if (pack == null || toDisarm?.Movable != true)
|
|
{
|
|
attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent.
|
|
return;
|
|
}
|
|
|
|
if (CheckMove(attacker, SkillName.ArmsLore))
|
|
{
|
|
StartMoveDelay(attacker);
|
|
|
|
attacker.Stam -= 15;
|
|
attacker.DisarmReady = false;
|
|
|
|
attacker.SendLocalizedMessage(1004006); // You successfully disarm your opponent!
|
|
defender.SendLocalizedMessage(1004007); // You have been disarmed!
|
|
|
|
pack.DropItem(toDisarm);
|
|
}
|
|
else
|
|
{
|
|
attacker.Stam -= 15;
|
|
|
|
attacker.SendLocalizedMessage(1004004); // You failed in your attempt to disarm.
|
|
defender.SendLocalizedMessage(1004005); // Your opponent tried to disarm you but failed.
|
|
}
|
|
}
|
|
|
|
public override TimeSpan OnSwing(Mobile attacker, Mobile defender, double damageBonus = 1.0)
|
|
{
|
|
if (!Core.AOS)
|
|
{
|
|
CheckPreAOSMoves(attacker, defender);
|
|
}
|
|
|
|
return base.OnSwing(attacker, defender);
|
|
}
|
|
|
|
/*public override void OnMiss( Mobile attacker, Mobile defender )
|
|
{
|
|
base.PlaySwingAnimation( attacker );
|
|
}*/
|
|
|
|
/* Wrestling moves */
|
|
|
|
private static bool CheckMove(Mobile m, SkillName other)
|
|
{
|
|
var wresValue = m.Skills.Wrestling.Value;
|
|
var scndValue = m.Skills[other].Value;
|
|
|
|
/* 40% chance at 80, 80
|
|
* 50% chance at 100, 100
|
|
* 60% chance at 120, 120
|
|
*/
|
|
|
|
var chance = (wresValue + scndValue) / 400.0;
|
|
|
|
return chance >= Utility.RandomDouble();
|
|
}
|
|
|
|
private static bool HasFreeHands(Mobile m)
|
|
{
|
|
var item = m.FindItemOnLayer(Layer.OneHanded);
|
|
|
|
return item is null or Spellbook && m.FindItemOnLayer(Layer.TwoHanded) == null;
|
|
}
|
|
|
|
public static void DisarmRequest(Mobile m)
|
|
{
|
|
if (Core.AOS || !Core.UOR)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!DuelContext.AllowSpecialAbility(m, "Disarm", true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var armsValue = m.Skills.ArmsLore.Value;
|
|
var wresValue = m.Skills.Wrestling.Value;
|
|
|
|
if (!HasFreeHands(m))
|
|
{
|
|
m.SendLocalizedMessage(1004029); // You must have your hands free to attempt to disarm your opponent.
|
|
m.DisarmReady = false;
|
|
}
|
|
else if (armsValue >= 80.0 && wresValue >= 80.0)
|
|
{
|
|
m.DisruptiveAction();
|
|
m.DisarmReady = !m.DisarmReady;
|
|
m.SendLocalizedMessage(m.DisarmReady ? 1019013 : 1019014);
|
|
}
|
|
else
|
|
{
|
|
m.SendLocalizedMessage(1004002); // You are not skilled enough to disarm your opponent.
|
|
m.DisarmReady = false;
|
|
}
|
|
}
|
|
|
|
public static void StunRequest(Mobile m)
|
|
{
|
|
if (Core.AOS || !Core.UOR || !DuelContext.AllowSpecialAbility(m, "Stun", true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var anatValue = m.Skills.Anatomy.Value;
|
|
var wresValue = m.Skills.Wrestling.Value;
|
|
|
|
if (!HasFreeHands(m))
|
|
{
|
|
m.SendLocalizedMessage(1004031); // You must have your hands free to attempt to stun your opponent.
|
|
m.StunReady = false;
|
|
}
|
|
else if (anatValue >= 80.0 && wresValue >= 80.0)
|
|
{
|
|
m.DisruptiveAction();
|
|
m.StunReady = !m.StunReady;
|
|
m.SendLocalizedMessage(m.StunReady ? 1019011 : 1019012);
|
|
}
|
|
else
|
|
{
|
|
m.SendLocalizedMessage(1004008); // You are not skilled enough to stun your opponent.
|
|
m.StunReady = false;
|
|
}
|
|
}
|
|
|
|
private static void StartMoveDelay(Mobile m)
|
|
{
|
|
new MoveDelayTimer(m).Start();
|
|
}
|
|
|
|
private class MoveDelayTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
|
|
public MoveDelayTimer(Mobile m) : base(TimeSpan.FromSeconds(10.0))
|
|
{
|
|
m_Mobile = m;
|
|
|
|
m_Mobile.BeginAction<Fists>();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.EndAction<Fists>();
|
|
}
|
|
}
|
|
}
|
|
}
|