* Fixes a bug with the main loop. Removes unnecessary optimizations for RDRand. * WIP - Rewriting packets. * Cleanup and updates README * Converts more packets * Fixes header * Converts Effects packets. * Forgot the send command * Adds the start of containers packets. * Adds message packets with caching * Extends the send methods * Starts to add Acquire methods * Converted the packets * Cleanup * Cleanup * Adds more packets * Adds more packets * Fixes clearing arrays from pool. Adds Mobile Incoming * Converts more packets. * Moves more packets * Moves more packets * Test compression * Merges * Migrating to an idiomatic syntax that also supports compression, and proxying. * Optimize the stack alloc. Changes attributes * Converted container packets * Visual Studio doesn't auto save files. I am still getting used to subpar IDEs. * Adds static packet caching. Will profile later. Converts more packets * Fixes packets and changes how compression is configured * WIP - Adds basics for Gumps. Not finished though. * Fix file * Fixes formatting * Changed SpanWriter to be more idiomatic. * Fixes Span vs RawSpan and missing stackallocs * Converts over some more gumps * WIP - Deletes 32bit support. * Drops RDRand32 support. * Fixes gump compilation * Converting gump components * Removes old huffman compression function * Revert signature for backwards compatibility * WIP - Converting more gump components * Creates ArraySet for the strings. Updates AppendTo to reference that. * Converts the maining gump components * Cleans up gump components * Cleans up directives * Cleans up ArraySet and moves it. Adds null-coalescing-assignment * Cleanup, Target Packets, and C# 8 changes. * Removed OPL Packet * World packets * Fixing packet uses * Cleans up code. Fixes packet uses in various places. * More cleanup for packets * Code cleanup * Converts more packet uses and cleans up more code * More code cleanup * Finishes fixing the packets in Item * Updates secure trade packets * Updates core and gets it to compile. * Moved packets to scripts. Fixed account handler use of packets * Code cleanup * Updates chat packets * Code cleanuo * Adds party packets, but need to implement them. * Party packets WIP * Finishes party packets * Rearrange movement namespaces and classes * Finishes plant packets * Code Formatting * Fixes moving effects * Code cleanup, eliminates equipinfo * Adds more packets. Fixes bugs with various packets. * Finishes mahjon packets * Fixes mahjong packet * Code cleanup * Finishes mahjong packets * Adds Map packets * Add multifacet maps and charts * Cleans up some packets with UTF8 * Optimizes packets * Cleans up more packets. Moves the MessageHelper * Removes assistant support. Removes extended protocol. Incorporates MapUO packets as normal packets. * Updates protocol extensions packet receiver * Fixes a few bugs. Fixes a few more packets. * Code cleanup and fixing more packets * Fixes packet effects * Cleaned up more code * Buff Icon cleanup * Removed unused constructors * Code Cleanup. Adds BoatHS Packets * Moves house files. Updates house foundation packets. * Deployment cleanup * Fixes: * Code cleanup * More code cleanup * More code cleanup * Cleaned up BaseHouse * Enforces styling * Converts foreach to linq where possible. * Dont need that * Goals/Readme updates * Removes 32bit support at the highest level. Turns on HRT by default. * Code cleanup. Fixes extended features packet. * Fixes various bugs * Code cleanup * Code cleanup * Code cleanup. Fixes gump X/Y assignment. * Code cleanup using |= operator * More code cleanup * Cleanup * Fixes spacing issues. Thanks Visual Studio. You suck. * Compiler error * Fixes NPE from RunUO 2.7 * Renames ScriptCompiler to AssemblyHandler. Fixes packets. Updates README * Fixes more packets. Stupid trailing nulls. * Fixes various bugs. * Fixes for gumps * Fixes more gump stuff. Going to split it out later since it is getting insane * Recoded the gump writing * WIP * *Added output path of scripts project to dev branch *Activated debugging in code
430 lines
13 KiB
C#
430 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Server.Engines.ConPVP;
|
|
using Server.Mobiles;
|
|
using Server.Network;
|
|
using Server.Spells;
|
|
using Server.Spells.Bushido;
|
|
using Server.Spells.Necromancy;
|
|
using Server.Spells.Ninjitsu;
|
|
|
|
namespace Server.Items
|
|
{
|
|
public abstract class WeaponAbility
|
|
{
|
|
public static WeaponAbility[] Abilities{ get; } = {
|
|
null,
|
|
new ArmorIgnore(),
|
|
new BleedAttack(),
|
|
new ConcussionBlow(),
|
|
new CrushingBlow(),
|
|
new Disarm(),
|
|
new Dismount(),
|
|
new DoubleStrike(),
|
|
new InfectiousStrike(),
|
|
new MortalStrike(),
|
|
new MovingShot(),
|
|
new ParalyzingBlow(),
|
|
new ShadowStrike(),
|
|
new WhirlwindAttack(),
|
|
|
|
new RidingSwipe(),
|
|
new FrenziedWhirlwind(),
|
|
new Block(),
|
|
new DefenseMastery(),
|
|
new NerveStrike(),
|
|
new TalonStrike(),
|
|
new Feint(),
|
|
new DualWield(),
|
|
new DoubleShot(),
|
|
new ArmorPierce(),
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
new Disrobe()
|
|
};
|
|
|
|
public static readonly WeaponAbility ArmorIgnore = Abilities[1];
|
|
public static readonly WeaponAbility BleedAttack = Abilities[2];
|
|
public static readonly WeaponAbility ConcussionBlow = Abilities[3];
|
|
public static readonly WeaponAbility CrushingBlow = Abilities[4];
|
|
public static readonly WeaponAbility Disarm = Abilities[5];
|
|
public static readonly WeaponAbility Dismount = Abilities[6];
|
|
public static readonly WeaponAbility DoubleStrike = Abilities[7];
|
|
public static readonly WeaponAbility InfectiousStrike = Abilities[8];
|
|
public static readonly WeaponAbility MortalStrike = Abilities[9];
|
|
public static readonly WeaponAbility MovingShot = Abilities[10];
|
|
public static readonly WeaponAbility ParalyzingBlow = Abilities[11];
|
|
public static readonly WeaponAbility ShadowStrike = Abilities[12];
|
|
public static readonly WeaponAbility WhirlwindAttack = Abilities[13];
|
|
|
|
public static readonly WeaponAbility RidingSwipe = Abilities[14];
|
|
public static readonly WeaponAbility FrenziedWhirlwind = Abilities[15];
|
|
public static readonly WeaponAbility Block = Abilities[16];
|
|
public static readonly WeaponAbility DefenseMastery = Abilities[17];
|
|
public static readonly WeaponAbility NerveStrike = Abilities[18];
|
|
public static readonly WeaponAbility TalonStrike = Abilities[19];
|
|
public static readonly WeaponAbility Feint = Abilities[20];
|
|
public static readonly WeaponAbility DualWield = Abilities[21];
|
|
public static readonly WeaponAbility DoubleShot = Abilities[22];
|
|
public static readonly WeaponAbility ArmorPierce = Abilities[23];
|
|
|
|
public static readonly WeaponAbility Bladeweave = Abilities[24];
|
|
public static readonly WeaponAbility ForceArrow = Abilities[25];
|
|
public static readonly WeaponAbility LightningArrow = Abilities[26];
|
|
public static readonly WeaponAbility PsychicAttack = Abilities[27];
|
|
public static readonly WeaponAbility SerpentArrow = Abilities[28];
|
|
public static readonly WeaponAbility ForceOfNature = Abilities[29];
|
|
|
|
public static readonly WeaponAbility Disrobe = Abilities[30];
|
|
|
|
private static Dictionary<Mobile, WeaponAbilityContext> m_PlayersTable = new Dictionary<Mobile, WeaponAbilityContext>();
|
|
|
|
public virtual int BaseMana => 0;
|
|
|
|
public virtual int AccuracyBonus => 0;
|
|
public virtual double DamageScalar => 1.0;
|
|
|
|
public virtual bool RequiresSE => false;
|
|
|
|
public static Dictionary<Mobile, WeaponAbility> Table{ get; } = new Dictionary<Mobile, WeaponAbility>();
|
|
|
|
public virtual bool ValidatesDuringHit => true;
|
|
|
|
public virtual void OnHit(Mobile attacker, Mobile defender, int damage)
|
|
{
|
|
}
|
|
|
|
public virtual void OnMiss(Mobile attacker, Mobile defender)
|
|
{
|
|
}
|
|
|
|
public virtual bool OnBeforeSwing(Mobile attacker, Mobile defender) => true;
|
|
|
|
public virtual bool OnBeforeDamage(Mobile attacker, Mobile defender) => true;
|
|
|
|
public virtual bool RequiresTactics(Mobile from) => true;
|
|
|
|
public virtual double GetRequiredSkill(Mobile from)
|
|
{
|
|
if (from.Weapon is BaseWeapon weapon)
|
|
{
|
|
if (weapon.PrimaryAbility == this)
|
|
return 70.0;
|
|
if (weapon.SecondaryAbility == this)
|
|
return 90.0;
|
|
}
|
|
|
|
return 200.0;
|
|
}
|
|
|
|
public virtual int CalculateMana(Mobile from)
|
|
{
|
|
int mana = BaseMana;
|
|
|
|
double skillTotal = GetSkill(from, SkillName.Swords) + GetSkill(from, SkillName.Macing) +
|
|
GetSkill(from, SkillName.Fencing) +
|
|
GetSkill(from, SkillName.Archery) +
|
|
GetSkill(from, SkillName.Parry) +
|
|
GetSkill(from, SkillName.Lumberjacking) +
|
|
GetSkill(from, SkillName.Stealth) +
|
|
GetSkill(from, SkillName.Poisoning) +
|
|
GetSkill(from, SkillName.Bushido) +
|
|
GetSkill(from, SkillName.Ninjitsu);
|
|
|
|
if (skillTotal >= 300.0)
|
|
mana -= 10;
|
|
else if (skillTotal >= 200.0)
|
|
mana -= 5;
|
|
|
|
double scalar = 1.0;
|
|
if (!MindRotSpell.GetMindRotScalar(from, ref scalar))
|
|
scalar = 1.0;
|
|
|
|
// Lower Mana Cost = 40%
|
|
int lmc = Math.Min(AosAttributes.GetValue(from, AosAttribute.LowerManaCost), 40);
|
|
|
|
scalar -= (double)lmc / 100;
|
|
mana = (int)(mana * scalar);
|
|
|
|
// Using a special move within 3 seconds of the previous special move costs double mana
|
|
if (GetContext(from) != null)
|
|
mana *= 2;
|
|
|
|
return mana;
|
|
}
|
|
|
|
public virtual bool CheckWeaponSkill(Mobile from)
|
|
{
|
|
if (!(from.Weapon is BaseWeapon weapon))
|
|
return false;
|
|
|
|
Skill skill = from.Skills[weapon.Skill];
|
|
double reqSkill = GetRequiredSkill(from);
|
|
bool reqTactics = Core.ML && RequiresTactics(from);
|
|
|
|
if (Core.ML && reqTactics && from.Skills.Tactics.Base < reqSkill)
|
|
{
|
|
from.SendLocalizedMessage(1079308,
|
|
reqSkill.ToString()); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
|
return false;
|
|
}
|
|
|
|
if (skill?.Base >= reqSkill)
|
|
return true;
|
|
|
|
/* <UBWS> */
|
|
if (weapon.WeaponAttributes.UseBestSkill > 0 && (from.Skills.Swords.Base >= reqSkill ||
|
|
from.Skills.Macing.Base >= reqSkill ||
|
|
from.Skills.Fencing.Base >= reqSkill))
|
|
return true;
|
|
/* </UBWS> */
|
|
|
|
if (reqTactics)
|
|
from.SendLocalizedMessage(1079308,
|
|
reqSkill.ToString()); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
|
else
|
|
from.SendLocalizedMessage(1060182,
|
|
reqSkill.ToString()); // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual bool CheckSkills(Mobile from) => CheckWeaponSkill(from);
|
|
|
|
public virtual double GetSkill(Mobile from, SkillName skillName) => from.Skills[skillName]?.Value ?? 0.0;
|
|
|
|
public virtual bool CheckMana(Mobile from, bool consume)
|
|
{
|
|
int mana = CalculateMana(from);
|
|
|
|
if (from.Mana < mana)
|
|
{
|
|
if (from is BaseCreature creature && creature.HasManaOveride) return true;
|
|
|
|
from.SendLocalizedMessage(1060181,
|
|
mana.ToString()); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack
|
|
return false;
|
|
}
|
|
|
|
if (consume)
|
|
{
|
|
if (GetContext(from) == null)
|
|
{
|
|
Timer timer = new WeaponAbilityTimer(from);
|
|
timer.Start();
|
|
|
|
AddContext(from, new WeaponAbilityContext(timer));
|
|
}
|
|
|
|
from.Mana -= mana;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public virtual bool Validate(Mobile from)
|
|
{
|
|
if (!from.Player)
|
|
return true;
|
|
|
|
NetState state = from.NetState;
|
|
|
|
if (state == null)
|
|
return false;
|
|
|
|
if (RequiresSE && !state.SupportsExpansion(Expansion.SE))
|
|
{
|
|
from.SendLocalizedMessage(1063456); // You must upgrade to Samurai Empire in order to use that ability.
|
|
return false;
|
|
}
|
|
|
|
if (HonorableExecution.IsUnderPenalty(from) || AnimalForm.UnderTransformation(from))
|
|
{
|
|
from.SendLocalizedMessage(1063024); // You cannot perform this special move right now.
|
|
return false;
|
|
}
|
|
|
|
if (Core.ML && from.Spell != null)
|
|
{
|
|
from.SendLocalizedMessage(1063024); // You cannot perform this special move right now.
|
|
return false;
|
|
}
|
|
|
|
#region Dueling
|
|
|
|
string option = this switch
|
|
{
|
|
ArmorIgnore _ => "Armor Ignore",
|
|
BleedAttack _ => "Bleed Attack",
|
|
ConcussionBlow _ => "Concussion Blow",
|
|
CrushingBlow _ => "Crushing Blow",
|
|
Disarm _ => "Disarm",
|
|
Dismount _ => "Dismount",
|
|
DoubleStrike _ => "Double Strike",
|
|
InfectiousStrike _ => "Infectious Strike",
|
|
MortalStrike _ => "Mortal Strike",
|
|
MovingShot _ => "Moving Shot",
|
|
ParalyzingBlow _ => "Paralyzing Blow",
|
|
ShadowStrike _ => "Shadow Strike",
|
|
WhirlwindAttack _ => "Whirlwind Attack",
|
|
RidingSwipe _ => "Riding Swipe",
|
|
FrenziedWhirlwind _ => "Frenzied Whirlwind",
|
|
Block _ => "Block",
|
|
DefenseMastery _ => "Defense Mastery",
|
|
NerveStrike _ => "Nerve Strike",
|
|
TalonStrike _ => "Talon Strike",
|
|
Feint _ => "Feint",
|
|
DualWield _ => "Dual Wield",
|
|
DoubleShot _ => "Double Shot",
|
|
ArmorPierce _ => "Armor Pierce",
|
|
_ => null
|
|
};
|
|
|
|
if (option != null && !DuelContext.AllowSpecialAbility(from, option, true))
|
|
return false;
|
|
|
|
#endregion
|
|
|
|
return CheckSkills(from) && CheckMana(from, false);
|
|
}
|
|
|
|
public static bool IsWeaponAbility(Mobile m, WeaponAbility a) =>
|
|
a == null || !m.Player || m.Weapon is BaseWeapon weapon &&
|
|
(weapon.PrimaryAbility == a || weapon.SecondaryAbility == a);
|
|
|
|
public static WeaponAbility GetCurrentAbility(Mobile m)
|
|
{
|
|
if (!Core.AOS)
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return null;
|
|
}
|
|
|
|
Table.TryGetValue(m, out WeaponAbility a);
|
|
|
|
if (!IsWeaponAbility(m, a))
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return null;
|
|
}
|
|
|
|
if (a?.ValidatesDuringHit == true && !a.Validate(m))
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return null;
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
public static bool SetCurrentAbility(Mobile m, WeaponAbility a)
|
|
{
|
|
if (!Core.AOS)
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return false;
|
|
}
|
|
|
|
if (!IsWeaponAbility(m, a))
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return false;
|
|
}
|
|
|
|
if (a?.Validate(m) == false)
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return false;
|
|
}
|
|
|
|
if (a == null)
|
|
{
|
|
Table.Remove(m);
|
|
}
|
|
else
|
|
{
|
|
SpecialMove.ClearCurrentMove(m);
|
|
Table[m] = a;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static void ClearCurrentAbility(Mobile m)
|
|
{
|
|
Table.Remove(m);
|
|
|
|
if (Core.AOS && m.NetState != null)
|
|
Packets.SendClearWeaponAbility(m.NetState);
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
EventSink.SetAbility += EventSink_SetAbility;
|
|
}
|
|
|
|
private static void EventSink_SetAbility(SetAbilityEventArgs e)
|
|
{
|
|
int index = e.Index;
|
|
|
|
if (index == 0)
|
|
ClearCurrentAbility(e.Mobile);
|
|
else if (index >= 1 && index < Abilities.Length)
|
|
SetCurrentAbility(e.Mobile, Abilities[index]);
|
|
}
|
|
|
|
private static void AddContext(Mobile m, WeaponAbilityContext context)
|
|
{
|
|
m_PlayersTable[m] = context;
|
|
}
|
|
|
|
private static void RemoveContext(Mobile m)
|
|
{
|
|
WeaponAbilityContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
RemoveContext(m, context);
|
|
}
|
|
|
|
private static void RemoveContext(Mobile m, WeaponAbilityContext context)
|
|
{
|
|
m_PlayersTable.Remove(m);
|
|
|
|
context.Timer.Stop();
|
|
}
|
|
|
|
private static WeaponAbilityContext GetContext(Mobile m)
|
|
{
|
|
m_PlayersTable.TryGetValue(m, out WeaponAbilityContext context);
|
|
return context;
|
|
}
|
|
|
|
private class WeaponAbilityTimer : Timer
|
|
{
|
|
private Mobile m_Mobile;
|
|
|
|
public WeaponAbilityTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0))
|
|
{
|
|
m_Mobile = from;
|
|
|
|
Priority = TimerPriority.TwentyFiveMS;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
RemoveContext(m_Mobile);
|
|
}
|
|
}
|
|
|
|
private class WeaponAbilityContext
|
|
{
|
|
public WeaponAbilityContext(Timer timer) => Timer = timer;
|
|
|
|
public Timer Timer{ get; }
|
|
}
|
|
}
|
|
}
|