* 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
313 lines
8 KiB
C#
313 lines
8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Server.Engines.ConPVP;
|
|
using Server.Items;
|
|
using Server.Network;
|
|
using Server.Spells.Bushido;
|
|
using Server.Spells.Necromancy;
|
|
using Server.Spells.Ninjitsu;
|
|
|
|
namespace Server.Spells
|
|
{
|
|
public abstract class SpecialMove
|
|
{
|
|
private static readonly Dictionary<Mobile, SpecialMoveContext> m_PlayersTable = new Dictionary<Mobile, SpecialMoveContext>();
|
|
|
|
public virtual int BaseMana => 0;
|
|
|
|
public virtual SkillName MoveSkill => SkillName.Bushido;
|
|
public virtual double RequiredSkill => 0.0;
|
|
|
|
public virtual TextDefinition AbilityMessage => 0;
|
|
|
|
public virtual bool BlockedByAnimalForm => true;
|
|
public virtual bool DelayedContext => false;
|
|
|
|
public static Dictionary<Mobile, SpecialMove> Table{ get; } = new Dictionary<Mobile, SpecialMove>();
|
|
|
|
public virtual bool ValidatesDuringHit => true;
|
|
|
|
public virtual int GetAccuracyBonus(Mobile attacker) => 0;
|
|
|
|
public virtual double GetDamageScalar(Mobile attacker, Mobile defender) => 1.0;
|
|
|
|
// Called before swinging, to make sure the accuracy scalar is to be computed.
|
|
public virtual bool OnBeforeSwing(Mobile attacker, Mobile defender) => true;
|
|
|
|
// Called when a hit connects, but before damage is calculated.
|
|
public virtual bool OnBeforeDamage(Mobile attacker, Mobile defender) => true;
|
|
|
|
// Called as soon as the ability is used.
|
|
public virtual void OnUse(Mobile from)
|
|
{
|
|
}
|
|
|
|
// Called when a hit connects, at the end of the weapon.OnHit() method.
|
|
public virtual void OnHit(Mobile attacker, Mobile defender, int damage)
|
|
{
|
|
}
|
|
|
|
// Called when a hit misses.
|
|
public virtual void OnMiss(Mobile attacker, Mobile defender)
|
|
{
|
|
}
|
|
|
|
// Called when the move is cleared.
|
|
public virtual void OnClearMove(Mobile from)
|
|
{
|
|
}
|
|
|
|
public virtual bool IgnoreArmor(Mobile attacker) => false;
|
|
|
|
public virtual double GetPropertyBonus(Mobile attacker) => 1.0;
|
|
|
|
public virtual bool CheckSkills(Mobile m)
|
|
{
|
|
if (m.Skills[MoveSkill].Value < RequiredSkill)
|
|
{
|
|
string args = $"{RequiredSkill.ToString("F1")}\t{MoveSkill.ToString()}\t ";
|
|
m.SendLocalizedMessage(1063013,
|
|
args); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public virtual int ScaleMana(Mobile m, int mana)
|
|
{
|
|
double scalar = 1.0;
|
|
|
|
if (!MindRotSpell.GetMindRotScalar(m, ref scalar))
|
|
scalar = 1.0;
|
|
|
|
// Lower Mana Cost = 40%
|
|
int lmc = Math.Min(AosAttributes.GetValue(m, AosAttribute.LowerManaCost), 40);
|
|
|
|
scalar -= (double)lmc / 100;
|
|
|
|
int total = (int)(mana * scalar);
|
|
|
|
if (m.Skills[MoveSkill].Value < 50.0 && GetContext(m) != null)
|
|
total *= 2;
|
|
|
|
return total;
|
|
}
|
|
|
|
public virtual bool CheckMana(Mobile from, bool consume)
|
|
{
|
|
int mana = ScaleMana(from, BaseMana);
|
|
|
|
if (from.Mana < mana)
|
|
{
|
|
from.SendLocalizedMessage(1060181,
|
|
mana.ToString()); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack
|
|
return false;
|
|
}
|
|
|
|
if (consume)
|
|
{
|
|
if (!DelayedContext)
|
|
SetContext(from);
|
|
|
|
from.Mana -= mana;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public virtual void SetContext(Mobile from)
|
|
{
|
|
if (GetContext(from) == null)
|
|
if (DelayedContext || from.Skills[MoveSkill].Value < 50.0)
|
|
{
|
|
Timer timer = new SpecialMoveTimer(from);
|
|
timer.Start();
|
|
|
|
AddContext(from, new SpecialMoveContext(timer, GetType()));
|
|
}
|
|
}
|
|
|
|
public virtual bool Validate(Mobile from)
|
|
{
|
|
if (!from.Player)
|
|
return true;
|
|
|
|
if (HonorableExecution.IsUnderPenalty(from))
|
|
{
|
|
from.SendLocalizedMessage(1063024); // You cannot perform this special move right now.
|
|
return false;
|
|
}
|
|
|
|
if (AnimalForm.UnderTransformation(from))
|
|
{
|
|
from.SendLocalizedMessage(1063024); // You cannot perform this special move right now.
|
|
return false;
|
|
}
|
|
|
|
#region Dueling
|
|
|
|
string option = null;
|
|
|
|
if (this is Backstab)
|
|
option = "Backstab";
|
|
else if (this is DeathStrike)
|
|
option = "Death Strike";
|
|
else if (this is FocusAttack)
|
|
option = "Focus Attack";
|
|
else if (this is KiAttack)
|
|
option = "Ki Attack";
|
|
else if (this is SurpriseAttack)
|
|
option = "Surprise Attack";
|
|
else if (this is HonorableExecution)
|
|
option = "Honorable Execution";
|
|
else if (this is LightningStrike)
|
|
option = "Lightning Strike";
|
|
else if (this is MomentumStrike)
|
|
option = "Momentum Strike";
|
|
|
|
if (option != null && !DuelContext.AllowSpecialMove(from, option, this))
|
|
return false;
|
|
|
|
#endregion
|
|
|
|
return CheckSkills(from) && CheckMana(from, false);
|
|
}
|
|
|
|
public virtual void CheckGain(Mobile m)
|
|
{
|
|
m.CheckSkill(MoveSkill, RequiredSkill, RequiredSkill + 37.5);
|
|
}
|
|
|
|
public static void ClearAllMoves(Mobile m)
|
|
{
|
|
foreach (var (moveID, _) in SpellRegistry.SpecialMoves.Where(kvp => kvp.Key != -1))
|
|
Packets.SendToggleSpecialAbility(m.NetState, (short)(moveID + 1), false);
|
|
}
|
|
|
|
public static SpecialMove GetCurrentMove(Mobile m)
|
|
{
|
|
if (m == null)
|
|
return null;
|
|
|
|
if (!Core.SE)
|
|
{
|
|
ClearCurrentMove(m);
|
|
return null;
|
|
}
|
|
|
|
if (Table.TryGetValue(m, out SpecialMove move) && move.ValidatesDuringHit && !move.Validate(m))
|
|
{
|
|
ClearCurrentMove(m);
|
|
return null;
|
|
}
|
|
|
|
return move;
|
|
}
|
|
|
|
public static bool SetCurrentMove(Mobile m, SpecialMove move)
|
|
{
|
|
if (!Core.SE)
|
|
{
|
|
ClearCurrentMove(m);
|
|
return false;
|
|
}
|
|
|
|
if (move?.Validate(m) == false)
|
|
{
|
|
ClearCurrentMove(m);
|
|
return false;
|
|
}
|
|
|
|
bool sameMove = move == GetCurrentMove(m);
|
|
|
|
ClearCurrentMove(m);
|
|
|
|
if (sameMove)
|
|
return true;
|
|
|
|
if (move != null)
|
|
{
|
|
WeaponAbility.ClearCurrentAbility(m);
|
|
|
|
Table[m] = move;
|
|
|
|
move.OnUse(m);
|
|
|
|
int moveID = SpellRegistry.GetRegistryNumber(move);
|
|
|
|
if (moveID > 0)
|
|
Packets.SendToggleSpecialAbility(m.NetState, (short)(moveID + 1), true);
|
|
|
|
TextDefinition.SendMessageTo(m, move.AbilityMessage);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static void ClearCurrentMove(Mobile m)
|
|
{
|
|
if (Table.TryGetValue(m, out SpecialMove move))
|
|
{
|
|
move.OnClearMove(m);
|
|
|
|
int moveID = SpellRegistry.GetRegistryNumber(move);
|
|
|
|
if (moveID > 0)
|
|
Packets.SendToggleSpecialAbility(m.NetState, (short)(moveID + 1), false);
|
|
}
|
|
|
|
Table.Remove(m);
|
|
}
|
|
|
|
private static void AddContext(Mobile m, SpecialMoveContext context)
|
|
{
|
|
m_PlayersTable[m] = context;
|
|
}
|
|
|
|
private static void RemoveContext(Mobile m)
|
|
{
|
|
SpecialMoveContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
m_PlayersTable.Remove(m);
|
|
|
|
context.Timer.Stop();
|
|
}
|
|
}
|
|
|
|
private static SpecialMoveContext GetContext(Mobile m) => m_PlayersTable.TryGetValue(m, out SpecialMoveContext context) ? context : null;
|
|
|
|
private class SpecialMoveTimer : Timer
|
|
{
|
|
private Mobile m_Mobile;
|
|
|
|
public SpecialMoveTimer(Mobile from) : base(TimeSpan.FromSeconds(3.0))
|
|
{
|
|
m_Mobile = from;
|
|
|
|
Priority = TimerPriority.TwentyFiveMS;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
RemoveContext(m_Mobile);
|
|
}
|
|
}
|
|
|
|
public class SpecialMoveContext
|
|
{
|
|
public SpecialMoveContext(Timer timer, Type type)
|
|
{
|
|
Timer = timer;
|
|
Type = type;
|
|
}
|
|
|
|
public Timer Timer{ get; }
|
|
|
|
public Type Type{ get; }
|
|
}
|
|
}
|
|
}
|