mirror of
https://github.com/ServUO/ServUO
synced 2026-08-11 20:23:04 -04:00
* - Internal Galleon Update: Galleon Facing now uses the Multi Component List to change the ID's of fixtures. Some of the ID's (weapon pads) were not using the correct ID's, and this way the code is much more simplified. All fixture lists have been consolidated into one list. - Gargish Jewels no longer drop on non-SA Core - Fixed issue with trapped boxes - Added Event Sink for sending detailed house foundation packet. - Fixed issue where imbued jewelry weren't getting durability. * update solution fixed graphical issue * Bug Fixes - Added Blackthorne DUngeon stealables - Added proper base resists to Leather Ninja Hood - Medusa now drops Medusa Floor Tile Addon Deed (THANKS MILVA!) - AOSWeaponAttributes now work on Armor - Protection now expires after player death - Fixed crash in PVP Arena Gumps - Exceptional Damage Inc no longer gives extra points for Clean up Britannia Turn In - Fixed Loyalty Gump Crash - Fixed Stygian Dragon arty drops per EA * Potential Crash Fix * csproj update * Fixed conflict issue... WTF? * Bug Fixes *Requires Core Recompile* - Fixed cliloc on new belt craftables - failed craft no longer deletes crimson cincture and luck mempo - Added a core edit to Container.cs (had to do it folks) for better resource evaluation when considering resources to consume while crafting. In this instance, we don't want VvV or Faction items being used as craftable resources due to thier ease of obtaining. - Consolidated no-delete resources in CraftSystem.cs - Fellowship Data now serializes to prevent generation at each server start * Resolve Conflicts * Bug Fixes - Shield bash forumula no more EA like (still not perfect) - Healers now show to dead players (needs work) - Honor buff now persists through death - * crash fix * Initial Commit - Publish 105 Forgotten Treasures - Removed Remove trap skill prerequisites * Moongate Fix - You have to be 1 tile to double click it. All other range checks are correct. * Commit #2 * crash fix * Commit #3 * Crash Fix * Commit #3 * More updates and additions. Compiles! * Final Commit * Project Update * Shields no longer engraveable w/ armor engraving tool. * Fixed THunting Town Crier Quest Fixed issue where initial guardians were not spawning for level 1 maps * Compile error fix. * COmpire error #2 fix * Bug Fixes - Summoners Kilt is now repairable - Orange petals are now 1 stone each (HS era check) - Enabled altering artifact (IsArtifact) footwear - Altering certain artifact (IsArtifact) items now carry over the proper resistance bonuses - Fixed Primeval Lich Unholy Touch special ability - Orc brutes now have a chance to throw an orc on any damage Received - Name changes now show up on the paperdoll (you have to close/re-open) - Removed "Gravewater Lake" region. It was conflicting with region functionality in Exploring the Deep Quest - Shadowguard Orchard Encounter apples now spawn a treefellow on each player in the region when deleted and thrown at the incorrect Tree - Shanty the Pirate no longer does reflect damage attack - Animal Lore gump now shows adjusted stats, ie cursed/blessed - Vendor Maps in Tokuno no longer crash the client - Nether Blase cast delay moved to 2.0 seconds, per EA - Arcane Empowerment SDI buff now shows in the status bar - Wildfire should no longer stack * Bug Fixes - Toggling weapon specials now expires Injected Strike - Crafting stacked items will now drastically reduce the amount of skill gains, per EA - Fixed issue where items were invisible to players in castle courtyards - Fixed issue where Assistants were kicking EC clients during Handshake - Added proper area effects to Fire Beetle * Added singing ball
561 lines
17 KiB
C#
561 lines
17 KiB
C#
using System;
|
|
using System.Collections;
|
|
|
|
using Server.Network;
|
|
using Server.Spells;
|
|
using Server.Spells.SkillMasteries;
|
|
|
|
namespace Server.Items
|
|
{
|
|
public abstract class WeaponAbility
|
|
{
|
|
public virtual int BaseMana
|
|
{
|
|
get
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public virtual int AccuracyBonus
|
|
{
|
|
get
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
public virtual double DamageScalar
|
|
{
|
|
get
|
|
{
|
|
return 1.0;
|
|
}
|
|
}
|
|
|
|
public virtual bool RequiresSE
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return false to make this special ability consume no ammo from ranged weapons
|
|
/// </summary>
|
|
public virtual bool ConsumeAmmo
|
|
{
|
|
get
|
|
{
|
|
return 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)
|
|
{
|
|
// Here because you must be sure you can use the skill before calling CheckHit if the ability has a HCI bonus for example
|
|
return true;
|
|
}
|
|
|
|
public virtual bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public virtual bool RequiresSecondarySkill(Mobile from)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public virtual double GetRequiredSkill(Mobile from)
|
|
{
|
|
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
|
|
|
if (weapon != null && (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave))
|
|
return 70.0;
|
|
else if (weapon != null && (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave))
|
|
return 90.0;
|
|
|
|
return 200.0;
|
|
}
|
|
|
|
public virtual double GetRequiredSecondarySkill(Mobile from)
|
|
{
|
|
if (!RequiresSecondarySkill(from))
|
|
return 0.0;
|
|
|
|
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
|
|
|
if (weapon != null && (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave))
|
|
return Core.TOL ? 30.0 : 70.0;
|
|
else if (weapon != null && (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave))
|
|
return Core.TOL ? 60.0 : 90.0;
|
|
|
|
return 200.0;
|
|
}
|
|
|
|
public virtual SkillName GetSecondarySkill(Mobile from)
|
|
{
|
|
return SkillName.Tactics;
|
|
}
|
|
|
|
public virtual int CalculateMana(Mobile from)
|
|
{
|
|
int mana = BaseMana;
|
|
|
|
double skillTotal = GetSkillTotal(from);
|
|
|
|
if (skillTotal >= 300.0)
|
|
mana -= 10;
|
|
else if (skillTotal >= 200.0)
|
|
mana -= 5;
|
|
|
|
double scalar = 1.0;
|
|
|
|
if (!Server.Spells.Necromancy.MindRotSpell.GetMindRotScalar(from, ref scalar))
|
|
{
|
|
scalar = 1.0;
|
|
}
|
|
|
|
if (Server.Spells.Mysticism.PurgeMagicSpell.IsUnderCurseEffects(from))
|
|
{
|
|
scalar += .5;
|
|
}
|
|
|
|
// Lower Mana Cost = 40%
|
|
int lmc = Math.Min(AosAttributes.GetValue(from, AosAttribute.LowerManaCost), 40);
|
|
|
|
lmc += BaseArmor.GetInherentLowerManaCost(from);
|
|
|
|
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)
|
|
{
|
|
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
|
|
|
if (weapon == null)
|
|
return false;
|
|
|
|
Skill skill = from.Skills[weapon.Skill];
|
|
|
|
double reqSkill = GetRequiredSkill(from);
|
|
double reqSecondarySkill = GetRequiredSecondarySkill(from);
|
|
SkillName secondarySkill = Core.TOL ? GetSecondarySkill(from) : SkillName.Tactics;
|
|
|
|
if (Core.ML && from.Skills[secondarySkill].Base < reqSecondarySkill)
|
|
{
|
|
int loc = GetSkillLocalization(secondarySkill);
|
|
|
|
if (loc == 1060184)
|
|
{
|
|
from.SendLocalizedMessage(loc);
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage(loc, reqSecondarySkill.ToString());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (skill != null && skill.Base >= reqSkill)
|
|
return true;
|
|
|
|
/* <UBWS> */
|
|
if (weapon.WeaponAttributes.UseBestSkill > 0 && (from.Skills[SkillName.Swords].Base >= reqSkill || from.Skills[SkillName.Macing].Base >= reqSkill || from.Skills[SkillName.Fencing].Base >= reqSkill))
|
|
return true;
|
|
/* </UBWS> */
|
|
|
|
if (reqSecondarySkill != 0.0 && !Core.TOL)
|
|
{
|
|
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;
|
|
}
|
|
|
|
private int GetSkillLocalization(SkillName skill)
|
|
{
|
|
switch (skill)
|
|
{
|
|
default: return Core.TOL ? 1157351 : 1079308;
|
|
// You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
|
// You need ~1_SKILL_REQUIREMENT~ tactics skill to perform that attack
|
|
case SkillName.Bushido:
|
|
case SkillName.Ninjitsu: return 1063347;
|
|
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
|
case SkillName.Poisoning: return 1060184;
|
|
// You lack the required poisoning to perform that attack
|
|
}
|
|
}
|
|
|
|
public virtual bool CheckSkills(Mobile from)
|
|
{
|
|
return CheckWeaponSkill(from);
|
|
}
|
|
|
|
public virtual double GetSkillTotal(Mobile from)
|
|
{
|
|
return 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.Throwing) +
|
|
GetSkill(from, SkillName.Poisoning) + GetSkill(from, SkillName.Bushido) + GetSkill(from, SkillName.Ninjitsu);
|
|
}
|
|
|
|
public virtual double GetSkill(Mobile from, SkillName skillName)
|
|
{
|
|
Skill skill = from.Skills[skillName];
|
|
|
|
if (skill == null)
|
|
return 0.0;
|
|
|
|
return skill.Value;
|
|
}
|
|
|
|
public virtual bool CheckMana(Mobile from, bool consume)
|
|
{
|
|
int mana = CalculateMana(from);
|
|
|
|
if (from.Mana < mana)
|
|
{
|
|
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));
|
|
}
|
|
|
|
if (ManaPhasingOrb.IsInManaPhase(from))
|
|
ManaPhasingOrb.RemoveFromTable(from);
|
|
else
|
|
from.Mana -= mana;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public virtual bool Validate(Mobile from)
|
|
{
|
|
if (!from.Player && (!Core.TOL || CheckMana(from, false)))
|
|
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 (Spells.Bushido.HonorableExecution.IsUnderPenalty(from) || Spells.Ninjitsu.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;
|
|
}
|
|
|
|
return CheckSkills(from) && CheckMana(from, false);
|
|
}
|
|
|
|
private static readonly WeaponAbility[] m_Abilities = new WeaponAbility[34]
|
|
{
|
|
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(),
|
|
new Bladeweave(),
|
|
new ForceArrow(),
|
|
new LightningArrow(),
|
|
new PsychicAttack(),
|
|
new SerpentArrow(),
|
|
new ForceOfNature(),
|
|
new InfusedThrow(),
|
|
new MysticArc(),
|
|
new Disrobe(),
|
|
new ColdWind()
|
|
};
|
|
|
|
public static WeaponAbility[] Abilities
|
|
{
|
|
get
|
|
{
|
|
return m_Abilities;
|
|
}
|
|
}
|
|
|
|
private static readonly Hashtable m_Table = new Hashtable();
|
|
|
|
public static Hashtable Table
|
|
{
|
|
get
|
|
{
|
|
return m_Table;
|
|
}
|
|
}
|
|
|
|
public static readonly WeaponAbility ArmorIgnore = m_Abilities[1];
|
|
public static readonly WeaponAbility BleedAttack = m_Abilities[2];
|
|
public static readonly WeaponAbility ConcussionBlow = m_Abilities[3];
|
|
public static readonly WeaponAbility CrushingBlow = m_Abilities[4];
|
|
public static readonly WeaponAbility Disarm = m_Abilities[5];
|
|
public static readonly WeaponAbility Dismount = m_Abilities[6];
|
|
public static readonly WeaponAbility DoubleStrike = m_Abilities[7];
|
|
public static readonly WeaponAbility InfectiousStrike = m_Abilities[8];
|
|
public static readonly WeaponAbility MortalStrike = m_Abilities[9];
|
|
public static readonly WeaponAbility MovingShot = m_Abilities[10];
|
|
public static readonly WeaponAbility ParalyzingBlow = m_Abilities[11];
|
|
public static readonly WeaponAbility ShadowStrike = m_Abilities[12];
|
|
public static readonly WeaponAbility WhirlwindAttack = m_Abilities[13];
|
|
|
|
public static readonly WeaponAbility RidingSwipe = m_Abilities[14];
|
|
public static readonly WeaponAbility FrenziedWhirlwind = m_Abilities[15];
|
|
public static readonly WeaponAbility Block = m_Abilities[16];
|
|
public static readonly WeaponAbility DefenseMastery = m_Abilities[17];
|
|
public static readonly WeaponAbility NerveStrike = m_Abilities[18];
|
|
public static readonly WeaponAbility TalonStrike = m_Abilities[19];
|
|
public static readonly WeaponAbility Feint = m_Abilities[20];
|
|
public static readonly WeaponAbility DualWield = m_Abilities[21];
|
|
public static readonly WeaponAbility DoubleShot = m_Abilities[22];
|
|
public static readonly WeaponAbility ArmorPierce = m_Abilities[23];
|
|
|
|
public static readonly WeaponAbility Bladeweave = m_Abilities[24];
|
|
public static readonly WeaponAbility ForceArrow = m_Abilities[25];
|
|
public static readonly WeaponAbility LightningArrow = m_Abilities[26];
|
|
public static readonly WeaponAbility PsychicAttack = m_Abilities[27];
|
|
public static readonly WeaponAbility SerpentArrow = m_Abilities[28];
|
|
public static readonly WeaponAbility ForceOfNature = m_Abilities[29];
|
|
|
|
public static readonly WeaponAbility InfusedThrow = m_Abilities[30];
|
|
public static readonly WeaponAbility MysticArc = m_Abilities[31];
|
|
|
|
public static readonly WeaponAbility Disrobe = m_Abilities[32];
|
|
public static readonly WeaponAbility ColdWind = m_Abilities[33];
|
|
|
|
public static bool IsWeaponAbility(Mobile m, WeaponAbility a)
|
|
{
|
|
if (a == null)
|
|
return true;
|
|
|
|
if (!m.Player)
|
|
return true;
|
|
|
|
BaseWeapon weapon = m.Weapon as BaseWeapon;
|
|
|
|
return (weapon != null && (weapon.PrimaryAbility == a || weapon.SecondaryAbility == a));
|
|
}
|
|
|
|
public virtual bool ValidatesDuringHit
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public static WeaponAbility GetCurrentAbility(Mobile m)
|
|
{
|
|
if (!Core.AOS)
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return null;
|
|
}
|
|
|
|
WeaponAbility a = (WeaponAbility)m_Table[m];
|
|
|
|
if (!IsWeaponAbility(m, a))
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return null;
|
|
}
|
|
|
|
if (a != null && a.ValidatesDuringHit && !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 != null && !a.Validate(m))
|
|
{
|
|
ClearCurrentAbility(m);
|
|
return false;
|
|
}
|
|
|
|
if (a == null)
|
|
{
|
|
m_Table.Remove(m);
|
|
}
|
|
else
|
|
{
|
|
SpecialMove.ClearCurrentMove(m);
|
|
|
|
m_Table[m] = a;
|
|
|
|
SkillMasterySpell.CancelWeaponAbility(m);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static void ClearCurrentAbility(Mobile m)
|
|
{
|
|
m_Table.Remove(m);
|
|
|
|
if (Core.AOS && m.NetState != null)
|
|
m.Send(ClearWeaponAbility.Instance);
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
EventSink.SetAbility += new SetAbilityEventHandler(EventSink_SetAbility);
|
|
}
|
|
|
|
public WeaponAbility()
|
|
{
|
|
}
|
|
|
|
private static void EventSink_SetAbility(SetAbilityEventArgs e)
|
|
{
|
|
int index = e.Index;
|
|
|
|
if (index == 0)
|
|
ClearCurrentAbility(e.Mobile);
|
|
else if (index >= 1 && index < m_Abilities.Length)
|
|
SetCurrentAbility(e.Mobile, m_Abilities[index]);
|
|
}
|
|
|
|
private static readonly Hashtable m_PlayersTable = new Hashtable();
|
|
|
|
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)
|
|
{
|
|
return (m_PlayersTable[m] as WeaponAbilityContext);
|
|
}
|
|
|
|
private class WeaponAbilityTimer : Timer
|
|
{
|
|
private readonly 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
|
|
{
|
|
private readonly Timer m_Timer;
|
|
|
|
public Timer Timer
|
|
{
|
|
get
|
|
{
|
|
return m_Timer;
|
|
}
|
|
}
|
|
|
|
public WeaponAbilityContext(Timer timer)
|
|
{
|
|
m_Timer = timer;
|
|
}
|
|
}
|
|
}
|
|
}
|