6687 lines
182 KiB
C#
6687 lines
182 KiB
C#
#region References
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
using Server.ContextMenus;
|
|
using Server.Engines.Craft;
|
|
using Server.Engines.XmlSpawner2;
|
|
using Server.Ethics;
|
|
using Server.Factions;
|
|
using Server.Mobiles;
|
|
using Server.Network;
|
|
using Server.Services.Virtues;
|
|
using Server.SkillHandlers;
|
|
using Server.Spells;
|
|
using Server.Spells.Bushido;
|
|
using Server.Spells.Chivalry;
|
|
using Server.Spells.Necromancy;
|
|
using Server.Spells.Ninjitsu;
|
|
using Server.Spells.Sixth;
|
|
using Server.Spells.Spellweaving;
|
|
using Server.Spells.SkillMasteries;
|
|
#endregion
|
|
|
|
namespace Server.Items
|
|
{
|
|
public interface ISlayer
|
|
{
|
|
SlayerName Slayer { get; set; }
|
|
SlayerName Slayer2 { get; set; }
|
|
}
|
|
|
|
public abstract class BaseWeapon : Item, IWeapon, IFactionItem, IUsesRemaining, ICraftable, ISlayer, IDurability, ISetItem, IVvVItem, IOwnerRestricted, IResource, IArtifact, ICombatEquipment, IEngravable, IQuality
|
|
{
|
|
#region Damage Helpers
|
|
public static BaseWeapon GetDamageOutput(Mobile wielder, out int min, out int max)
|
|
{
|
|
return GetDamageOutput(wielder, null, out min, out max);
|
|
}
|
|
|
|
public static BaseWeapon GetDamageOutput(Mobile wielder, BaseWeapon weapon, out int min, out int max)
|
|
{
|
|
int minRaw, maxRaw;
|
|
|
|
return GetDamageOutput(wielder, weapon, out minRaw, out maxRaw, out min, out max);
|
|
}
|
|
|
|
public static BaseWeapon GetDamageOutput(
|
|
Mobile wielder,
|
|
out int minRaw,
|
|
out int maxRaw,
|
|
out int minVal,
|
|
out int maxVal)
|
|
{
|
|
return GetDamageOutput(wielder, null, out minRaw, out maxRaw, out minVal, out maxVal);
|
|
}
|
|
|
|
public static BaseWeapon GetDamageOutput(
|
|
Mobile wielder,
|
|
BaseWeapon weapon,
|
|
out int minRaw,
|
|
out int maxRaw,
|
|
out int minVal,
|
|
out int maxVal)
|
|
{
|
|
minRaw = maxRaw = 0;
|
|
minVal = maxVal = 0;
|
|
|
|
if (wielder == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (weapon == null)
|
|
{
|
|
weapon = wielder.Weapon as BaseWeapon ?? Fists;
|
|
}
|
|
|
|
if (weapon == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
weapon.GetBaseDamageRange(wielder, out minVal, out maxVal);
|
|
|
|
if (wielder is BaseCreature)
|
|
{
|
|
if (((BaseCreature)wielder).DamageMin >= 0 || (weapon is Fists && !wielder.Body.IsHuman))
|
|
{
|
|
minRaw = minVal;
|
|
maxRaw = maxVal;
|
|
return weapon;
|
|
}
|
|
}
|
|
|
|
minRaw = weapon.MinDamage;
|
|
maxRaw = weapon.MaxDamage;
|
|
|
|
if (Core.AOS)
|
|
{
|
|
minVal = (int)weapon.ScaleDamageAOS(wielder, minVal, false);
|
|
maxVal = (int)weapon.ScaleDamageAOS(wielder, maxVal, false);
|
|
}
|
|
else
|
|
{
|
|
minVal = (int)weapon.ScaleDamageOld(wielder, minVal, false);
|
|
maxVal = (int)weapon.ScaleDamageOld(wielder, maxVal, false);
|
|
}
|
|
|
|
return weapon;
|
|
}
|
|
#endregion
|
|
|
|
private string m_EngravedText;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string EngravedText
|
|
{
|
|
get { return m_EngravedText; }
|
|
set
|
|
{
|
|
m_EngravedText = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
#region Factions
|
|
private FactionItem m_FactionState;
|
|
|
|
public FactionItem FactionItemState
|
|
{
|
|
get { return m_FactionState; }
|
|
set
|
|
{
|
|
m_FactionState = value;
|
|
|
|
if (m_FactionState == null)
|
|
{
|
|
Hue = CraftResources.GetHue(Resource);
|
|
}
|
|
|
|
LootType = (m_FactionState == null ? LootType.Regular : LootType.Blessed);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region IUsesRemaining members
|
|
private int m_UsesRemaining;
|
|
private bool m_ShowUsesRemaining;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int UsesRemaining { get { return m_UsesRemaining; } set { m_UsesRemaining = value; InvalidateProperties(); } }
|
|
|
|
public bool ShowUsesRemaining { get { return m_ShowUsesRemaining; } set { m_ShowUsesRemaining = value; InvalidateProperties(); } }
|
|
|
|
public void ScaleUses()
|
|
{
|
|
m_UsesRemaining = (m_UsesRemaining * GetUsesScalar()) / 100;
|
|
InvalidateProperties();
|
|
}
|
|
|
|
public void UnscaleUses()
|
|
{
|
|
m_UsesRemaining = (m_UsesRemaining * 100) / GetUsesScalar();
|
|
}
|
|
|
|
public int GetUsesScalar()
|
|
{
|
|
if (m_Quality == ItemQuality.Exceptional)
|
|
return 200;
|
|
|
|
return 100;
|
|
}
|
|
#endregion
|
|
|
|
private bool _VvVItem;
|
|
private Mobile _Owner;
|
|
private string _OwnerName;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool IsVvVItem
|
|
{
|
|
get { return _VvVItem; }
|
|
set { _VvVItem = value; InvalidateProperties(); }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Mobile Owner
|
|
{
|
|
get { return _Owner; }
|
|
set { _Owner = value; if (_Owner != null) _OwnerName = _Owner.Name; InvalidateProperties(); }
|
|
}
|
|
|
|
public virtual string OwnerName
|
|
{
|
|
get { return _OwnerName; }
|
|
set { _OwnerName = value; InvalidateProperties(); }
|
|
}
|
|
|
|
/* Weapon internals work differently now (Mar 13 2003)
|
|
*
|
|
* The attributes defined below default to -1.
|
|
* If the value is -1, the corresponding virtual 'Aos/Old' property is used.
|
|
* If not, the attribute value itself is used. Here's the list:
|
|
* - MinDamage
|
|
* - MaxDamage
|
|
* - Speed
|
|
* - HitSound
|
|
* - MissSound
|
|
* - StrRequirement, DexRequirement, IntRequirement
|
|
* - WeaponType
|
|
* - WeaponAnimation
|
|
* - MaxRange
|
|
*/
|
|
|
|
#region Var declarations
|
|
// Instance values. These values are unique to each weapon.
|
|
private WeaponDamageLevel m_DamageLevel;
|
|
private WeaponAccuracyLevel m_AccuracyLevel;
|
|
private WeaponDurabilityLevel m_DurabilityLevel;
|
|
private ItemQuality m_Quality;
|
|
private Mobile m_Crafter;
|
|
private Poison m_Poison;
|
|
private int m_PoisonCharges;
|
|
private bool m_Identified;
|
|
private int m_Hits;
|
|
private int m_MaxHits;
|
|
private SlayerName m_Slayer;
|
|
private SlayerName m_Slayer2;
|
|
|
|
#region Mondain's Legacy
|
|
private TalismanSlayerName m_Slayer3;
|
|
#endregion
|
|
|
|
private SkillMod m_SkillMod, m_MageMod, m_MysticMod;
|
|
private CraftResource m_Resource;
|
|
private bool m_PlayerConstructed;
|
|
|
|
private bool m_Altered;
|
|
|
|
private AosAttributes m_AosAttributes;
|
|
private AosWeaponAttributes m_AosWeaponAttributes;
|
|
private AosSkillBonuses m_AosSkillBonuses;
|
|
private AosElementAttributes m_AosElementDamages;
|
|
private SAAbsorptionAttributes m_SAAbsorptionAttributes;
|
|
private NegativeAttributes m_NegativeAttributes;
|
|
private ExtendedWeaponAttributes m_ExtendedWeaponAttributes;
|
|
|
|
// Overridable values. These values are provided to override the defaults which get defined in the individual weapon scripts.
|
|
private int m_StrReq, m_DexReq, m_IntReq;
|
|
private int m_MinDamage, m_MaxDamage;
|
|
private int m_HitSound, m_MissSound;
|
|
private float m_Speed;
|
|
private int m_MaxRange;
|
|
private SkillName m_Skill;
|
|
private WeaponType m_Type;
|
|
private WeaponAnimation m_Animation;
|
|
|
|
#region Stygian Abyss
|
|
private int m_TimesImbued;
|
|
private bool m_IsImbued;
|
|
private bool m_DImodded;
|
|
#endregion
|
|
|
|
#region Runic Reforging
|
|
private ItemPower m_ItemPower;
|
|
private ReforgedPrefix m_ReforgedPrefix;
|
|
private ReforgedSuffix m_ReforgedSuffix;
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Virtual Properties
|
|
public virtual WeaponAbility PrimaryAbility { get { return null; } }
|
|
public virtual WeaponAbility SecondaryAbility { get { return null; } }
|
|
|
|
public virtual int DefMaxRange { get { return 1; } }
|
|
public virtual int DefHitSound { get { return 0; } }
|
|
public virtual int DefMissSound { get { return 0; } }
|
|
public virtual SkillName DefSkill { get { return SkillName.Swords; } }
|
|
public virtual WeaponType DefType { get { return WeaponType.Slashing; } }
|
|
public virtual WeaponAnimation DefAnimation { get { return WeaponAnimation.Slash1H; } }
|
|
|
|
public virtual int AosStrengthReq { get { return 0; } }
|
|
public virtual int AosDexterityReq { get { return 0; } }
|
|
public virtual int AosIntelligenceReq { get { return 0; } }
|
|
public virtual int AosMinDamage { get { return 0; } }
|
|
public virtual int AosMaxDamage { get { return 0; } }
|
|
public virtual int AosSpeed { get { return 0; } }
|
|
public virtual float MlSpeed { get { return 0.0f; } }
|
|
public virtual int AosMaxRange { get { return DefMaxRange; } }
|
|
public virtual int AosHitSound { get { return DefHitSound; } }
|
|
public virtual int AosMissSound { get { return DefMissSound; } }
|
|
public virtual SkillName AosSkill { get { return DefSkill; } }
|
|
public virtual WeaponType AosType { get { return DefType; } }
|
|
public virtual WeaponAnimation AosAnimation { get { return DefAnimation; } }
|
|
|
|
public virtual int OldStrengthReq { get { return 0; } }
|
|
public virtual int OldDexterityReq { get { return 0; } }
|
|
public virtual int OldIntelligenceReq { get { return 0; } }
|
|
public virtual int OldMinDamage { get { return 0; } }
|
|
public virtual int OldMaxDamage { get { return 0; } }
|
|
public virtual int OldSpeed { get { return 0; } }
|
|
public virtual int OldMaxRange { get { return DefMaxRange; } }
|
|
public virtual int OldHitSound { get { return DefHitSound; } }
|
|
public virtual int OldMissSound { get { return DefMissSound; } }
|
|
public virtual SkillName OldSkill { get { return DefSkill; } }
|
|
public virtual WeaponType OldType { get { return DefType; } }
|
|
public virtual WeaponAnimation OldAnimation { get { return DefAnimation; } }
|
|
|
|
public virtual int InitMinHits { get { return 0; } }
|
|
public virtual int InitMaxHits { get { return 0; } }
|
|
|
|
public virtual bool CanFortify { get { return !IsImbued && NegativeAttributes.Antique < 4; } }
|
|
public virtual bool CanRepair { get { return m_NegativeAttributes.NoRepair == 0; } }
|
|
public virtual bool CanAlter { get { return true; } }
|
|
|
|
public override int PhysicalResistance { get { return m_AosWeaponAttributes.ResistPhysicalBonus; } }
|
|
public override int FireResistance { get { return m_AosWeaponAttributes.ResistFireBonus; } }
|
|
public override int ColdResistance { get { return m_AosWeaponAttributes.ResistColdBonus; } }
|
|
public override int PoisonResistance { get { return m_AosWeaponAttributes.ResistPoisonBonus; } }
|
|
public override int EnergyResistance { get { return m_AosWeaponAttributes.ResistEnergyBonus; } }
|
|
|
|
public virtual SkillName AccuracySkill { get { return SkillName.Tactics; } }
|
|
|
|
public override double DefaultWeight
|
|
{
|
|
get
|
|
{
|
|
if (NegativeAttributes == null || NegativeAttributes.Unwieldly == 0)
|
|
return base.DefaultWeight;
|
|
|
|
return 50;
|
|
}
|
|
}
|
|
|
|
#region Personal Bless Deed
|
|
private Mobile m_BlessedBy;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Mobile BlessedBy
|
|
{
|
|
get { return m_BlessedBy; }
|
|
set
|
|
{
|
|
m_BlessedBy = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
private class UnBlessEntry : ContextMenuEntry
|
|
{
|
|
private readonly Mobile m_From;
|
|
private readonly BaseWeapon m_Weapon; // BaseArmor, BaseWeapon or BaseClothing
|
|
|
|
public UnBlessEntry(Mobile from, BaseWeapon weapon)
|
|
: base(6208, -1)
|
|
{
|
|
m_From = from;
|
|
m_Weapon = weapon;
|
|
}
|
|
|
|
public override void OnClick()
|
|
{
|
|
m_Weapon.BlessedFor = null;
|
|
m_Weapon.BlessedBy = null;
|
|
|
|
Container pack = m_From.Backpack;
|
|
|
|
if (pack != null)
|
|
{
|
|
pack.DropItem(new PersonalBlessDeed(m_From));
|
|
m_From.SendLocalizedMessage(1062200); // A personal bless deed has been placed in your backpack.
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Getters & Setters
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosAttributes Attributes { get { return m_AosAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosWeaponAttributes WeaponAttributes { get { return m_AosWeaponAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosSkillBonuses SkillBonuses { get { return m_AosSkillBonuses; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosElementAttributes AosElementDamages { get { return m_AosElementDamages; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public SAAbsorptionAttributes AbsorptionAttributes { get { return m_SAAbsorptionAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public NegativeAttributes NegativeAttributes { get { return m_NegativeAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ExtendedWeaponAttributes ExtendedWeaponAttributes { get { return m_ExtendedWeaponAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ConsecratedWeaponContext ConsecratedContext { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool Identified
|
|
{
|
|
get { return m_Identified; }
|
|
set
|
|
{
|
|
m_Identified = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int HitPoints
|
|
{
|
|
get { return m_Hits; }
|
|
set
|
|
{
|
|
if (m_Hits == value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (value > m_MaxHits)
|
|
{
|
|
value = m_MaxHits;
|
|
}
|
|
|
|
m_Hits = value;
|
|
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int MaxHitPoints
|
|
{
|
|
get { return m_MaxHits; }
|
|
set
|
|
{
|
|
m_MaxHits = value;
|
|
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int PoisonCharges
|
|
{
|
|
get { return m_PoisonCharges; }
|
|
set
|
|
{
|
|
m_PoisonCharges = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Poison Poison
|
|
{
|
|
get { return m_Poison; }
|
|
set
|
|
{
|
|
m_Poison = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ItemQuality Quality
|
|
{
|
|
get { return m_Quality; }
|
|
set
|
|
{
|
|
UnscaleDurability();
|
|
UnscaleUses();
|
|
m_Quality = value;
|
|
ScaleDurability();
|
|
ScaleUses();
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Mobile Crafter
|
|
{
|
|
get { return m_Crafter; }
|
|
set
|
|
{
|
|
m_Crafter = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public SlayerName Slayer
|
|
{
|
|
get { return m_Slayer; }
|
|
set
|
|
{
|
|
m_Slayer = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public SlayerName Slayer2
|
|
{
|
|
get { return m_Slayer2; }
|
|
set
|
|
{
|
|
m_Slayer2 = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public TalismanSlayerName Slayer3
|
|
{
|
|
get { return m_Slayer3; }
|
|
set
|
|
{
|
|
m_Slayer3 = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public CraftResource Resource
|
|
{
|
|
get { return m_Resource; }
|
|
set
|
|
{
|
|
UnscaleDurability();
|
|
m_Resource = value;
|
|
Hue = CraftResources.GetHue(m_Resource);
|
|
InvalidateProperties();
|
|
ScaleDurability();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public WeaponDamageLevel DamageLevel
|
|
{
|
|
get { return m_DamageLevel; }
|
|
set
|
|
{
|
|
m_DamageLevel = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public WeaponDurabilityLevel DurabilityLevel
|
|
{
|
|
get { return m_DurabilityLevel; }
|
|
set
|
|
{
|
|
UnscaleDurability();
|
|
m_DurabilityLevel = value;
|
|
InvalidateProperties();
|
|
ScaleDurability();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool PlayerConstructed { get { return m_PlayerConstructed; } set { m_PlayerConstructed = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int MaxRange
|
|
{
|
|
get { return (m_MaxRange == -1 ? Core.AOS ? AosMaxRange : OldMaxRange : m_MaxRange); }
|
|
set
|
|
{
|
|
m_MaxRange = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public WeaponAnimation Animation { get { return (m_Animation == (WeaponAnimation)(-1) ? Core.AOS ? AosAnimation : OldAnimation : m_Animation); } set { m_Animation = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public WeaponType Type { get { return (m_Type == (WeaponType)(-1) ? Core.AOS ? AosType : OldType : m_Type); } set { m_Type = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public SkillName Skill
|
|
{
|
|
get { return (m_Skill == (SkillName)(-1) ? Core.AOS ? AosSkill : OldSkill : m_Skill); }
|
|
set
|
|
{
|
|
m_Skill = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int HitSound { get { return (m_HitSound == -1 ? Core.AOS ? AosHitSound : OldHitSound : m_HitSound); } set { m_HitSound = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int MissSound { get { return (m_MissSound == -1 ? Core.AOS ? AosMissSound : OldMissSound : m_MissSound); } set { m_MissSound = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int MinDamage
|
|
{
|
|
get { return (m_MinDamage == -1 ? Core.AOS ? AosMinDamage : OldMinDamage : m_MinDamage); }
|
|
set
|
|
{
|
|
m_MinDamage = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int MaxDamage
|
|
{
|
|
get { return (m_MaxDamage == -1 ? Core.AOS ? AosMaxDamage : OldMaxDamage : m_MaxDamage); }
|
|
set
|
|
{
|
|
m_MaxDamage = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public float Speed
|
|
{
|
|
get
|
|
{
|
|
if (m_Speed != -1)
|
|
{
|
|
return m_Speed;
|
|
}
|
|
|
|
if (Core.ML)
|
|
{
|
|
return MlSpeed;
|
|
}
|
|
else if (Core.AOS)
|
|
{
|
|
return AosSpeed;
|
|
}
|
|
|
|
return OldSpeed;
|
|
}
|
|
set
|
|
{
|
|
m_Speed = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int StrRequirement
|
|
{
|
|
get
|
|
{
|
|
if (m_NegativeAttributes.Massive > 0)
|
|
{
|
|
return 125;
|
|
}
|
|
|
|
return m_StrReq == -1 ? Core.AOS ? AosStrengthReq : OldStrengthReq : m_StrReq;
|
|
}
|
|
set
|
|
{
|
|
m_StrReq = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int DexRequirement { get { return (m_DexReq == -1 ? Core.AOS ? AosDexterityReq : OldDexterityReq : m_DexReq); } set { m_DexReq = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int IntRequirement { get { return (m_IntReq == -1 ? Core.AOS ? AosIntelligenceReq : OldIntelligenceReq : m_IntReq); } set { m_IntReq = value; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public WeaponAccuracyLevel AccuracyLevel
|
|
{
|
|
get { return m_AccuracyLevel; }
|
|
set
|
|
{
|
|
if (m_AccuracyLevel != value)
|
|
{
|
|
m_AccuracyLevel = value;
|
|
|
|
if (UseSkillMod)
|
|
{
|
|
if (m_AccuracyLevel == WeaponAccuracyLevel.Regular)
|
|
{
|
|
if (m_SkillMod != null)
|
|
{
|
|
m_SkillMod.Remove();
|
|
}
|
|
|
|
m_SkillMod = null;
|
|
}
|
|
else if (m_SkillMod == null && Parent is Mobile)
|
|
{
|
|
m_SkillMod = new DefaultSkillMod(AccuracySkill, true, (int)m_AccuracyLevel * 5);
|
|
((Mobile)Parent).AddSkillMod(m_SkillMod);
|
|
}
|
|
else if (m_SkillMod != null)
|
|
{
|
|
m_SkillMod.Value = (int)m_AccuracyLevel * 5;
|
|
}
|
|
}
|
|
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
}
|
|
|
|
public Mobile FocusWeilder { get; set; }
|
|
public Mobile EnchantedWeilder { get; set; }
|
|
|
|
public int LastParryChance { get; set; }
|
|
|
|
#region Stygian Abyss
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int TimesImbued
|
|
{
|
|
get { return m_TimesImbued; }
|
|
set { m_TimesImbued = value; }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool IsImbued
|
|
{
|
|
get
|
|
{
|
|
if (TimesImbued >= 1 && !m_IsImbued)
|
|
m_IsImbued = true;
|
|
|
|
return m_IsImbued;
|
|
}
|
|
set
|
|
{
|
|
if (TimesImbued >= 1)
|
|
m_IsImbued = true;
|
|
else
|
|
m_IsImbued = value; InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool DImodded
|
|
{
|
|
get { return m_DImodded; }
|
|
set { m_DImodded = value; }
|
|
}
|
|
|
|
public int[] BaseResists
|
|
{
|
|
get
|
|
{
|
|
return new int[] { 0, 0, 0, 0, 0 };
|
|
}
|
|
}
|
|
|
|
public virtual void OnAfterImbued(Mobile m, int mod, int value)
|
|
{
|
|
}
|
|
#endregion
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool SearingWeapon
|
|
{
|
|
get { return HasSocket<SearingWeapon>(); }
|
|
set
|
|
{
|
|
if (!value)
|
|
{
|
|
RemoveSocket<SearingWeapon>();
|
|
}
|
|
else if (!SearingWeapon)
|
|
{
|
|
AttachSocket(new SearingWeapon(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Runic Reforging
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ItemPower ItemPower
|
|
{
|
|
get { return m_ItemPower; }
|
|
set { m_ItemPower = value; InvalidateProperties(); }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ReforgedPrefix ReforgedPrefix
|
|
{
|
|
get { return m_ReforgedPrefix; }
|
|
set { m_ReforgedPrefix = value; InvalidateProperties(); }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ReforgedSuffix ReforgedSuffix
|
|
{
|
|
get { return m_ReforgedSuffix; }
|
|
set { m_ReforgedSuffix = value; InvalidateProperties(); }
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
|
|
{
|
|
base.GetContextMenuEntries(from, list);
|
|
|
|
if (SearingWeapon && Parent == from)
|
|
{
|
|
list.Add(new SearingWeapon.ToggleExtinguishEntry(from, this));
|
|
}
|
|
|
|
if (BlessedFor == from && BlessedBy == from && RootParent == from)
|
|
{
|
|
list.Add(new UnBlessEntry(from, this));
|
|
}
|
|
}
|
|
|
|
public override void OnAfterDuped(Item newItem)
|
|
{
|
|
base.OnAfterDuped(newItem);
|
|
|
|
BaseWeapon weap = newItem as BaseWeapon;
|
|
|
|
if (weap == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
weap.m_AosAttributes = new AosAttributes(newItem, m_AosAttributes);
|
|
weap.m_AosElementDamages = new AosElementAttributes(newItem, m_AosElementDamages);
|
|
weap.m_AosSkillBonuses = new AosSkillBonuses(newItem, m_AosSkillBonuses);
|
|
weap.m_AosWeaponAttributes = new AosWeaponAttributes(newItem, m_AosWeaponAttributes);
|
|
weap.m_NegativeAttributes = new NegativeAttributes(newItem, m_NegativeAttributes);
|
|
weap.m_ExtendedWeaponAttributes = new ExtendedWeaponAttributes(newItem, m_ExtendedWeaponAttributes);
|
|
|
|
#region Mondain's Legacy
|
|
weap.m_SetAttributes = new AosAttributes(newItem, m_SetAttributes);
|
|
weap.m_SetSkillBonuses = new AosSkillBonuses(newItem, m_SetSkillBonuses);
|
|
#endregion
|
|
|
|
#region SA
|
|
weap.m_SAAbsorptionAttributes = new SAAbsorptionAttributes(newItem, m_SAAbsorptionAttributes);
|
|
#endregion
|
|
}
|
|
|
|
public virtual void UnscaleDurability()
|
|
{
|
|
int scale = 100 + GetDurabilityBonus();
|
|
|
|
m_Hits = ((m_Hits * 100) + (scale - 1)) / scale;
|
|
m_MaxHits = ((m_MaxHits * 100) + (scale - 1)) / scale;
|
|
|
|
InvalidateProperties();
|
|
}
|
|
|
|
public virtual void ScaleDurability()
|
|
{
|
|
int scale = 100 + GetDurabilityBonus();
|
|
|
|
m_Hits = ((m_Hits * scale) + 99) / 100;
|
|
m_MaxHits = ((m_MaxHits * scale) + 99) / 100;
|
|
|
|
if (m_MaxHits > 255)
|
|
m_MaxHits = 255;
|
|
|
|
if (m_Hits > 255)
|
|
m_Hits = 255;
|
|
|
|
InvalidateProperties();
|
|
}
|
|
|
|
public int GetDurabilityBonus()
|
|
{
|
|
int bonus = 0;
|
|
|
|
if (m_Quality == ItemQuality.Exceptional)
|
|
{
|
|
bonus += 20;
|
|
}
|
|
|
|
switch (m_DurabilityLevel)
|
|
{
|
|
case WeaponDurabilityLevel.Durable:
|
|
bonus += 20;
|
|
break;
|
|
case WeaponDurabilityLevel.Substantial:
|
|
bonus += 50;
|
|
break;
|
|
case WeaponDurabilityLevel.Massive:
|
|
bonus += 70;
|
|
break;
|
|
case WeaponDurabilityLevel.Fortified:
|
|
bonus += 100;
|
|
break;
|
|
case WeaponDurabilityLevel.Indestructible:
|
|
bonus += 120;
|
|
break;
|
|
}
|
|
|
|
if (Core.AOS)
|
|
{
|
|
bonus += m_AosWeaponAttributes.DurabilityBonus;
|
|
|
|
#region Mondain's Legacy
|
|
if (m_Resource == CraftResource.Heartwood)
|
|
{
|
|
return bonus;
|
|
}
|
|
#endregion
|
|
|
|
CraftResourceInfo resInfo = CraftResources.GetInfo(m_Resource);
|
|
CraftAttributeInfo attrInfo = null;
|
|
|
|
if (resInfo != null)
|
|
{
|
|
attrInfo = resInfo.AttributeInfo;
|
|
}
|
|
|
|
if (attrInfo != null)
|
|
{
|
|
bonus += attrInfo.WeaponDurability;
|
|
}
|
|
}
|
|
|
|
return bonus;
|
|
}
|
|
|
|
public int GetLowerStatReq()
|
|
{
|
|
if (!Core.AOS)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int v = m_AosWeaponAttributes.LowerStatReq;
|
|
|
|
#region Mondain's Legacy
|
|
if (m_Resource == CraftResource.Heartwood)
|
|
{
|
|
return v;
|
|
}
|
|
#endregion
|
|
|
|
CraftResourceInfo info = CraftResources.GetInfo(m_Resource);
|
|
|
|
if (info != null)
|
|
{
|
|
CraftAttributeInfo attrInfo = info.AttributeInfo;
|
|
|
|
if (attrInfo != null)
|
|
{
|
|
v += attrInfo.WeaponLowerRequirements;
|
|
}
|
|
}
|
|
|
|
if (v > 100)
|
|
{
|
|
v = 100;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static void BlockEquip(Mobile m, TimeSpan duration)
|
|
{
|
|
if (m.BeginAction(typeof(BaseWeapon)))
|
|
{
|
|
new ResetEquipTimer(m, duration).Start();
|
|
}
|
|
}
|
|
|
|
private class ResetEquipTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
|
|
public ResetEquipTimer(Mobile m, TimeSpan duration)
|
|
: base(duration)
|
|
{
|
|
m_Mobile = m;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.EndAction(typeof(BaseWeapon));
|
|
m_Mobile.SendLocalizedMessage(1060168); // Your confusion has passed, you may now arm a weapon!
|
|
}
|
|
}
|
|
|
|
public override bool CheckConflictingLayer(Mobile m, Item item, Layer layer)
|
|
{
|
|
if (base.CheckConflictingLayer(m, item, layer))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (Layer == Layer.TwoHanded && layer == Layer.OneHanded)
|
|
{
|
|
m.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500214); // You already have something in both hands.
|
|
return true;
|
|
}
|
|
else if (Layer == Layer.OneHanded && layer == Layer.TwoHanded && !(item is BaseShield) && !(item is BaseEquipableLight))
|
|
{
|
|
m.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500215); // // You can only wield one weapon at a time.
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted)
|
|
{
|
|
if (!Ethic.CheckTrade(from, to, newOwner, this))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return base.AllowSecureTrade(from, to, newOwner, accepted);
|
|
}
|
|
|
|
public virtual Race RequiredRace { get { return null; } }
|
|
//On OSI, there are no weapons with race requirements, this is for custom stuff
|
|
|
|
#region SA
|
|
public virtual bool CanBeWornByGargoyles { get { return false; } }
|
|
#endregion
|
|
|
|
public override bool CanEquip(Mobile from)
|
|
{
|
|
if (!Ethic.CheckEquip(from, this))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (from.IsPlayer())
|
|
{
|
|
if (_Owner != null && _Owner != from)
|
|
{
|
|
from.SendLocalizedMessage(501023); // You must be the owner to use this item.
|
|
return false;
|
|
}
|
|
|
|
if (this is IAccountRestricted && ((IAccountRestricted)this).Account != null)
|
|
{
|
|
Accounting.Account acct = from.Account as Accounting.Account;
|
|
|
|
if (acct == null || acct.Username != ((IAccountRestricted)this).Account)
|
|
{
|
|
from.SendLocalizedMessage(1071296); // This item is Account Bound and your character is not bound to it. You cannot use this item.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (IsVvVItem && !Engines.VvV.ViceVsVirtueSystem.IsVvV(from))
|
|
{
|
|
from.SendLocalizedMessage(1155496); // This item can only be used by VvV participants!
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool morph = from.FindItemOnLayer(Layer.Earrings) is MorphEarrings;
|
|
|
|
if (from.Race == Race.Gargoyle && !CanBeWornByGargoyles && from.IsPlayer())
|
|
{
|
|
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1111708); // Gargoyles can't wear this.
|
|
return false;
|
|
}
|
|
|
|
if (RequiredRace != null && from.Race != RequiredRace && !morph)
|
|
{
|
|
if (RequiredRace == Race.Elf)
|
|
{
|
|
from.SendLocalizedMessage(1072203); // Only Elves may use this.
|
|
}
|
|
else if (RequiredRace == Race.Gargoyle)
|
|
{
|
|
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1111707); // Only gargoyles can wear this.
|
|
}
|
|
else
|
|
{
|
|
from.SendMessage("Only {0} may use ", RequiredRace.PluralName);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else if (from.Dex < DexRequirement)
|
|
{
|
|
from.SendLocalizedMessage(1071936); // You cannot equip that.
|
|
return false;
|
|
}
|
|
else if (from.Str < AOS.Scale(StrRequirement, 100 - GetLowerStatReq()))
|
|
{
|
|
from.SendLocalizedMessage(500213); // You are not strong enough to equip that.
|
|
return false;
|
|
}
|
|
else if (from.Int < IntRequirement)
|
|
{
|
|
from.SendLocalizedMessage(1071936); // You cannot equip that.
|
|
return false;
|
|
}
|
|
else if (!from.CanBeginAction(typeof(BaseWeapon)))
|
|
{
|
|
from.SendLocalizedMessage(3000201); // You must wait to perform another action.
|
|
return false;
|
|
}
|
|
#region Personal Bless Deed
|
|
else if (BlessedBy != null && BlessedBy != from)
|
|
{
|
|
from.SendLocalizedMessage(1075277); // That item is blessed by another player.
|
|
|
|
return false;
|
|
}
|
|
else if (!XmlAttach.CheckCanEquip(this, from))
|
|
{
|
|
return false;
|
|
}
|
|
#endregion
|
|
|
|
else
|
|
{
|
|
return base.CanEquip(from);
|
|
}
|
|
}
|
|
|
|
public virtual bool UseSkillMod { get { return !Core.AOS; } }
|
|
|
|
public override bool OnEquip(Mobile from)
|
|
{
|
|
int strBonus = m_AosAttributes.BonusStr;
|
|
int dexBonus = m_AosAttributes.BonusDex;
|
|
int intBonus = m_AosAttributes.BonusInt;
|
|
|
|
if ((strBonus != 0 || dexBonus != 0 || intBonus != 0))
|
|
{
|
|
Mobile m = from;
|
|
|
|
string modName = Serial.ToString();
|
|
|
|
if (strBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Str, modName + "Str", strBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (dexBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (intBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Int, modName + "Int", intBonus, TimeSpan.Zero));
|
|
}
|
|
}
|
|
|
|
from.NextCombatTime = Core.TickCount + (int)GetDelay(from).TotalMilliseconds;
|
|
|
|
if (UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular)
|
|
{
|
|
if (m_SkillMod != null)
|
|
{
|
|
m_SkillMod.Remove();
|
|
}
|
|
|
|
m_SkillMod = new DefaultSkillMod(AccuracySkill, true, (int)m_AccuracyLevel * 5);
|
|
from.AddSkillMod(m_SkillMod);
|
|
}
|
|
|
|
if (Core.AOS && m_AosWeaponAttributes.MageWeapon != 0 && m_AosWeaponAttributes.MageWeapon != 30)
|
|
{
|
|
if (m_MageMod != null)
|
|
{
|
|
m_MageMod.Remove();
|
|
}
|
|
|
|
m_MageMod = new DefaultSkillMod(SkillName.Magery, true, -30 + m_AosWeaponAttributes.MageWeapon);
|
|
from.AddSkillMod(m_MageMod);
|
|
}
|
|
|
|
if (Core.TOL)
|
|
{
|
|
if ((m_ExtendedWeaponAttributes.MysticWeapon != 0 && m_ExtendedWeaponAttributes.MysticWeapon != 30) || Enhancement.GetValue(from, ExtendedWeaponAttribute.MysticWeapon) > 0)
|
|
AddMysticMod(from);
|
|
}
|
|
|
|
XmlAttach.CheckOnEquip(this, from);
|
|
|
|
InDoubleStrike = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public override void OnAdded(object parent)
|
|
{
|
|
base.OnAdded(parent);
|
|
|
|
if (parent is Mobile)
|
|
{
|
|
Mobile from = (Mobile)parent;
|
|
|
|
if (Core.AOS)
|
|
{
|
|
m_AosSkillBonuses.AddTo(from);
|
|
}
|
|
|
|
#region Mondain's Legacy Sets
|
|
if (IsSetItem)
|
|
{
|
|
m_SetEquipped = SetHelper.FullSetEquipped(from, SetID, Pieces);
|
|
|
|
if (m_SetEquipped)
|
|
{
|
|
m_LastEquipped = true;
|
|
SetHelper.AddSetBonus(from, SetID);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
if (HasSocket<Caddellite>())
|
|
{
|
|
Caddellite.UpdateBuff(from);
|
|
}
|
|
|
|
if (ExtendedWeaponAttributes.Focus > 0)
|
|
{
|
|
Focus.UpdateBuff(from);
|
|
}
|
|
|
|
from.CheckStatTimers();
|
|
from.Delta(MobileDelta.WeaponDamage);
|
|
}
|
|
}
|
|
|
|
public override void OnRemoved(object parent)
|
|
{
|
|
if (parent is Mobile)
|
|
{
|
|
Mobile m = (Mobile)parent;
|
|
BaseWeapon weapon = m.Weapon as BaseWeapon;
|
|
|
|
string modName = Serial.ToString();
|
|
|
|
m.RemoveStatMod(modName + "Str");
|
|
m.RemoveStatMod(modName + "Dex");
|
|
m.RemoveStatMod(modName + "Int");
|
|
|
|
if (weapon != null)
|
|
{
|
|
m.NextCombatTime = Core.TickCount + (int)weapon.GetDelay(m).TotalMilliseconds;
|
|
}
|
|
|
|
if (UseSkillMod && m_SkillMod != null)
|
|
{
|
|
m_SkillMod.Remove();
|
|
m_SkillMod = null;
|
|
}
|
|
|
|
if (m_MageMod != null)
|
|
{
|
|
m_MageMod.Remove();
|
|
m_MageMod = null;
|
|
}
|
|
|
|
if (Core.AOS)
|
|
{
|
|
m_AosSkillBonuses.Remove();
|
|
}
|
|
|
|
ImmolatingWeaponSpell.StopImmolating(this, (Mobile)parent);
|
|
Spells.Mysticism.EnchantSpell.OnWeaponRemoved(this, m);
|
|
|
|
if (FocusWeilder != null)
|
|
FocusWeilder = null;
|
|
|
|
SkillMasterySpell.OnWeaponRemoved(m, this);
|
|
|
|
#region Mondain's Legacy Sets
|
|
if (IsSetItem && m_SetEquipped)
|
|
{
|
|
SetHelper.RemoveSetBonus(m, SetID, this);
|
|
}
|
|
#endregion
|
|
|
|
if (HasSocket<Caddellite>())
|
|
{
|
|
Caddellite.UpdateBuff(m);
|
|
}
|
|
|
|
if (SearingWeapon)
|
|
{
|
|
Server.Items.SearingWeapon.OnWeaponRemoved(this);
|
|
}
|
|
|
|
if (ExtendedWeaponAttributes.Focus > 0)
|
|
{
|
|
Focus.UpdateBuff(m);
|
|
}
|
|
|
|
m.CheckStatTimers();
|
|
|
|
m.Delta(MobileDelta.WeaponDamage);
|
|
|
|
XmlAttach.CheckOnRemoved(this, parent);
|
|
}
|
|
|
|
LastParryChance = 0;
|
|
}
|
|
|
|
public void AddMysticMod(Mobile from)
|
|
{
|
|
if (m_MysticMod != null)
|
|
m_MysticMod.Remove();
|
|
|
|
int value = m_ExtendedWeaponAttributes.MysticWeapon;
|
|
|
|
if (Enhancement.GetValue(from, ExtendedWeaponAttribute.MysticWeapon) > value)
|
|
value = Enhancement.GetValue(from, ExtendedWeaponAttribute.MysticWeapon);
|
|
|
|
m_MysticMod = new DefaultSkillMod(SkillName.Mysticism, true, -30 + value);
|
|
from.AddSkillMod(m_MysticMod);
|
|
}
|
|
|
|
public void RemoveMysticMod()
|
|
{
|
|
if (m_MysticMod != null)
|
|
{
|
|
m_MysticMod.Remove();
|
|
m_MysticMod = null;
|
|
}
|
|
}
|
|
|
|
public virtual SkillName GetUsedSkill(Mobile m, bool checkSkillAttrs)
|
|
{
|
|
SkillName sk;
|
|
|
|
if (checkSkillAttrs && m_AosWeaponAttributes.UseBestSkill != 0)
|
|
{
|
|
double swrd = m.Skills[SkillName.Swords].Value;
|
|
double fenc = m.Skills[SkillName.Fencing].Value;
|
|
double mcng = m.Skills[SkillName.Macing].Value;
|
|
double val;
|
|
|
|
sk = SkillName.Swords;
|
|
val = swrd;
|
|
|
|
if (fenc > val)
|
|
{
|
|
sk = SkillName.Fencing;
|
|
val = fenc;
|
|
}
|
|
if (mcng > val)
|
|
{
|
|
sk = SkillName.Macing;
|
|
val = mcng;
|
|
}
|
|
}
|
|
else if (m_AosWeaponAttributes.MageWeapon != 0)
|
|
{
|
|
if (m.Skills[SkillName.Magery].Value > m.Skills[Skill].Value)
|
|
{
|
|
sk = SkillName.Magery;
|
|
}
|
|
else
|
|
{
|
|
sk = Skill;
|
|
}
|
|
}
|
|
else if (m_ExtendedWeaponAttributes.MysticWeapon != 0 || Enhancement.GetValue(m, ExtendedWeaponAttribute.MysticWeapon) > 0)
|
|
{
|
|
if (m.Skills[SkillName.Mysticism].Value > m.Skills[Skill].Value)
|
|
{
|
|
sk = SkillName.Mysticism;
|
|
}
|
|
else
|
|
{
|
|
sk = Skill;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sk = Skill;
|
|
|
|
if (sk != SkillName.Wrestling && !m.Player && !m.Body.IsHuman &&
|
|
m.Skills[SkillName.Wrestling].Value > m.Skills[sk].Value)
|
|
{
|
|
sk = SkillName.Wrestling;
|
|
}
|
|
}
|
|
|
|
return sk;
|
|
}
|
|
|
|
public virtual double GetAttackSkillValue(Mobile attacker, Mobile defender)
|
|
{
|
|
return attacker.Skills[GetUsedSkill(attacker, true)].Value;
|
|
}
|
|
|
|
public virtual double GetDefendSkillValue(Mobile attacker, Mobile defender)
|
|
{
|
|
return defender.Skills[GetUsedSkill(defender, true)].Value;
|
|
}
|
|
|
|
public static bool CheckAnimal(Mobile m, Type type)
|
|
{
|
|
return AnimalForm.UnderTransformation(m, type);
|
|
}
|
|
|
|
public virtual bool CheckHit(Mobile attacker, IDamageable damageable)
|
|
{
|
|
Mobile defender = damageable as Mobile;
|
|
|
|
if (defender == null)
|
|
{
|
|
if (damageable is IDamageableItem)
|
|
return ((IDamageableItem)damageable).CheckHit(attacker);
|
|
|
|
return true;
|
|
}
|
|
|
|
BaseWeapon atkWeapon = attacker.Weapon as BaseWeapon;
|
|
BaseWeapon defWeapon = defender.Weapon as BaseWeapon;
|
|
|
|
Skill atkSkill = attacker.Skills[atkWeapon.Skill];
|
|
Skill defSkill = defender.Skills[defWeapon.Skill];
|
|
|
|
double atkValue = atkWeapon.GetAttackSkillValue(attacker, defender);
|
|
double defValue = defWeapon.GetDefendSkillValue(attacker, defender);
|
|
|
|
double ourValue, theirValue;
|
|
|
|
int bonus = GetHitChanceBonus();
|
|
|
|
if (Core.AOS)
|
|
{
|
|
if (atkValue <= -20.0)
|
|
atkValue = -19.9;
|
|
|
|
if (defValue <= -20.0)
|
|
defValue = -19.9;
|
|
|
|
bonus += AosAttributes.GetValue(attacker, AosAttribute.AttackChance);
|
|
|
|
//SA Gargoyle cap is 50, else 45
|
|
bonus = Math.Min(attacker.Race == Race.Gargoyle ? 50 : 45, bonus);
|
|
|
|
ourValue = (atkValue + 20.0) * (100 + bonus);
|
|
|
|
bonus = AosAttributes.GetValue(defender, AosAttribute.DefendChance);
|
|
|
|
ForceArrow.ForceArrowInfo info = ForceArrow.GetInfo(attacker, defender);
|
|
|
|
if (info != null && info.Defender == defender)
|
|
bonus -= info.DefenseChanceMalus;
|
|
|
|
int max = 45 + BaseArmor.GetRefinedDefenseChance(defender);
|
|
|
|
// Defense Chance Increase = 45%
|
|
if (bonus > max)
|
|
bonus = max;
|
|
|
|
theirValue = (defValue + 20.0) * (100 + bonus);
|
|
|
|
bonus = 0;
|
|
}
|
|
else
|
|
{
|
|
if (atkValue <= -50.0)
|
|
{
|
|
atkValue = -49.9;
|
|
}
|
|
|
|
if (defValue <= -50.0)
|
|
{
|
|
defValue = -49.9;
|
|
}
|
|
|
|
ourValue = (atkValue + 50.0);
|
|
theirValue = (defValue + 50.0);
|
|
}
|
|
|
|
double chance = ourValue / (theirValue * 2.0);
|
|
|
|
chance *= 1.0 + ((double)bonus / 100);
|
|
|
|
if (Core.SA)
|
|
{
|
|
if (atkWeapon is BaseThrown)
|
|
{
|
|
//Distance malas
|
|
if (attacker.InRange(defender, 1)) //Close Quarters
|
|
{
|
|
chance -= (.12 - (double)Math.Min(12, (attacker.Skills[SkillName.Throwing].Value + (double)attacker.RawDex) / 20) / 10);
|
|
}
|
|
else if (attacker.GetDistanceToSqrt(defender) < ((BaseThrown)atkWeapon).MinThrowRange) //too close
|
|
{
|
|
chance -= .12;
|
|
}
|
|
|
|
//shield penalty
|
|
BaseShield shield = attacker.FindItemOnLayer(Layer.TwoHanded) as BaseShield;
|
|
|
|
if (shield != null)
|
|
{
|
|
double malus = Math.Min(90, 1200 / Math.Max(1.0, attacker.Skills[SkillName.Parry].Value));
|
|
|
|
chance = chance - (chance * (malus / 100));
|
|
}
|
|
}
|
|
|
|
if (defWeapon is BaseThrown)
|
|
{
|
|
BaseShield shield = defender.FindItemOnLayer(Layer.TwoHanded) as BaseShield;
|
|
|
|
if (shield != null)
|
|
{
|
|
double malus = Math.Min(90, 1200 / Math.Max(1.0, defender.Skills[SkillName.Parry].Value));
|
|
|
|
chance = chance + (chance * (malus / 100));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Core.AOS && chance < 0.02)
|
|
{
|
|
chance = 0.02;
|
|
}
|
|
|
|
if (Core.AOS && m_AosWeaponAttributes.MageWeapon > 0 && attacker.Skills[SkillName.Magery].Value > atkSkill.Value)
|
|
return attacker.CheckSkill(SkillName.Magery, chance);
|
|
|
|
return attacker.CheckSkill(atkSkill.SkillName, chance);
|
|
}
|
|
|
|
public virtual TimeSpan GetDelay(Mobile m)
|
|
{
|
|
double speed = Speed;
|
|
|
|
if (speed == 0)
|
|
{
|
|
return TimeSpan.FromHours(1.0);
|
|
}
|
|
|
|
double delayInSeconds;
|
|
|
|
if (Core.SE)
|
|
{
|
|
/*
|
|
* This is likely true for Core.AOS as well... both guides report the same
|
|
* formula, and both are wrong.
|
|
* The old formula left in for AOS for legacy & because we aren't quite 100%
|
|
* Sure that AOS has THIS formula
|
|
*/
|
|
int bonus = AosAttributes.GetValue(m, AosAttribute.WeaponSpeed);
|
|
|
|
if (bonus > 60)
|
|
{
|
|
bonus = 60;
|
|
}
|
|
|
|
double ticks;
|
|
|
|
if (Core.ML)
|
|
{
|
|
int stamTicks = m.Stam / 30;
|
|
|
|
ticks = speed * 4;
|
|
ticks = Math.Floor((ticks - stamTicks) * (100.0 / (100 + bonus)));
|
|
}
|
|
else
|
|
{
|
|
speed = Math.Floor(speed * (bonus + 100.0) / 100.0);
|
|
|
|
if (speed <= 0)
|
|
{
|
|
speed = 1;
|
|
}
|
|
|
|
ticks = Math.Floor((80000.0 / ((m.Stam + 100) * speed)) - 2);
|
|
}
|
|
|
|
// Swing speed currently capped at one swing every 1.25 seconds (5 ticks).
|
|
if (ticks < 5)
|
|
{
|
|
ticks = 5;
|
|
}
|
|
|
|
delayInSeconds = ticks * 0.25;
|
|
}
|
|
else if (Core.AOS)
|
|
{
|
|
int v = (m.Stam + 100) * (int)speed;
|
|
|
|
int bonus = AosAttributes.GetValue(m, AosAttribute.WeaponSpeed);
|
|
|
|
v += AOS.Scale(v, bonus);
|
|
|
|
if (v <= 0)
|
|
{
|
|
v = 1;
|
|
}
|
|
|
|
delayInSeconds = Math.Floor(40000.0 / v) * 0.5;
|
|
|
|
// Maximum swing rate capped at one swing per second
|
|
// OSI dev said that it has and is supposed to be 1.25
|
|
if (delayInSeconds < 1.25)
|
|
{
|
|
delayInSeconds = 1.25;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int v = (m.Stam + 100) * (int)speed;
|
|
|
|
if (v <= 0)
|
|
{
|
|
v = 1;
|
|
}
|
|
|
|
delayInSeconds = 15000.0 / v;
|
|
}
|
|
|
|
return TimeSpan.FromSeconds(delayInSeconds);
|
|
}
|
|
|
|
public virtual void OnBeforeSwing(Mobile attacker, IDamageable damageable)
|
|
{
|
|
Mobile defender = damageable as Mobile;
|
|
|
|
WeaponAbility a = WeaponAbility.GetCurrentAbility(attacker);
|
|
|
|
if (a != null && (!a.OnBeforeSwing(attacker, defender)))
|
|
{
|
|
WeaponAbility.ClearCurrentAbility(attacker);
|
|
}
|
|
|
|
SpecialMove move = SpecialMove.GetCurrentMove(attacker);
|
|
|
|
if (move != null && !move.OnBeforeSwing(attacker, defender))
|
|
{
|
|
SpecialMove.ClearCurrentMove(attacker);
|
|
}
|
|
}
|
|
|
|
public virtual TimeSpan OnSwing(Mobile attacker, IDamageable damageable)
|
|
{
|
|
return OnSwing(attacker, damageable, 1.0);
|
|
}
|
|
|
|
public virtual TimeSpan OnSwing(Mobile attacker, IDamageable damageable, double damageBonus)
|
|
{
|
|
bool canSwing = true;
|
|
|
|
if (Core.AOS)
|
|
{
|
|
canSwing = (!attacker.Paralyzed && !attacker.Frozen);
|
|
|
|
if (canSwing)
|
|
{
|
|
Spell sp = attacker.Spell as Spell;
|
|
|
|
canSwing = (sp == null || !sp.IsCasting || !sp.BlocksMovement);
|
|
}
|
|
|
|
if (canSwing)
|
|
{
|
|
PlayerMobile p = attacker as PlayerMobile;
|
|
|
|
canSwing = (p == null || p.PeacedUntil <= DateTime.UtcNow);
|
|
}
|
|
}
|
|
|
|
if (canSwing && attacker.HarmfulCheck(damageable))
|
|
{
|
|
attacker.DisruptiveAction();
|
|
|
|
if (attacker.NetState != null)
|
|
{
|
|
attacker.Send(new Swing(0, attacker, damageable));
|
|
}
|
|
|
|
if (attacker is BaseCreature)
|
|
{
|
|
BaseCreature bc = (BaseCreature)attacker;
|
|
WeaponAbility ab = bc.TryGetWeaponAbility();
|
|
|
|
if (ab != null)
|
|
{
|
|
if (bc.WeaponAbilityChance > Utility.RandomDouble())
|
|
{
|
|
WeaponAbility.SetCurrentAbility(bc, ab);
|
|
}
|
|
else
|
|
{
|
|
WeaponAbility.ClearCurrentAbility(bc);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CheckHit(attacker, damageable))
|
|
{
|
|
OnHit(attacker, damageable, damageBonus);
|
|
}
|
|
else
|
|
{
|
|
OnMiss(attacker, damageable);
|
|
}
|
|
}
|
|
|
|
return GetDelay(attacker);
|
|
}
|
|
|
|
#region Sounds
|
|
public virtual int GetHitAttackSound(Mobile attacker, Mobile defender)
|
|
{
|
|
int sound = attacker.GetAttackSound();
|
|
|
|
if (sound == -1)
|
|
{
|
|
sound = HitSound;
|
|
}
|
|
|
|
return sound;
|
|
}
|
|
|
|
public virtual int GetHitDefendSound(Mobile attacker, Mobile defender)
|
|
{
|
|
return defender.GetHurtSound();
|
|
}
|
|
|
|
public virtual int GetMissAttackSound(Mobile attacker, Mobile defender)
|
|
{
|
|
if (attacker.GetAttackSound() == -1)
|
|
{
|
|
return MissSound;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public virtual int GetMissDefendSound(Mobile attacker, Mobile defender)
|
|
{
|
|
return -1;
|
|
}
|
|
#endregion
|
|
|
|
public static bool CheckParry(Mobile defender)
|
|
{
|
|
if (defender == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
BaseShield shield = defender.FindItemOnLayer(Layer.TwoHanded) as BaseShield;
|
|
|
|
double parry = defender.Skills[SkillName.Parry].Value;
|
|
double bushidoNonRacial = defender.Skills[SkillName.Bushido].NonRacialValue;
|
|
double bushido = defender.Skills[SkillName.Bushido].Value;
|
|
|
|
if (shield != null || !defender.Player)
|
|
{
|
|
double chance = (parry - bushidoNonRacial) / 400.0;
|
|
// As per OSI, no negitive effect from the Racial stuffs, ie, 120 parry and '0' bushido with humans
|
|
|
|
if (chance < 0) // chance shouldn't go below 0
|
|
{
|
|
chance = defender.Player ? 0 : .1;
|
|
}
|
|
|
|
// Skill Masteries
|
|
chance += HeightenedSensesSpell.GetParryBonus(defender);
|
|
|
|
// Parry/Bushido over 100 grants a 5% bonus.
|
|
if (parry >= 100.0 || bushido >= 100.0)
|
|
{
|
|
chance += 0.05;
|
|
}
|
|
|
|
// Evasion grants a variable bonus post ML. 50% prior.
|
|
if (Evasion.IsEvading(defender))
|
|
{
|
|
chance *= Evasion.GetParryScalar(defender);
|
|
}
|
|
|
|
// Low dexterity lowers the chance.
|
|
if (defender.Player && defender.Dex < 80)
|
|
{
|
|
chance = chance * (20 + defender.Dex) / 100;
|
|
}
|
|
|
|
bool success = defender.CheckSkill(SkillName.Parry, chance);
|
|
|
|
if (shield != null && Core.EJ && success)
|
|
{
|
|
shield.LastParryChance = (int)(chance * 100);
|
|
shield.InvalidateProperties();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
else if (!(defender.Weapon is Fists) && !(defender.Weapon is BaseRanged))
|
|
{
|
|
BaseWeapon weapon = defender.Weapon as BaseWeapon;
|
|
|
|
if (Core.HS && weapon.Attributes.BalancedWeapon > 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
double divisor = (weapon.Layer == Layer.OneHanded && defender.Player) ? 48000.0 : 41140.0;
|
|
|
|
double chance = (parry * bushido) / divisor;
|
|
|
|
double aosChance = parry / 800.0;
|
|
|
|
// Parry or Bushido over 100 grant a 5% bonus.
|
|
if (parry >= 100.0)
|
|
{
|
|
chance += 0.05;
|
|
aosChance += 0.05;
|
|
}
|
|
else if (bushido >= 100.0)
|
|
{
|
|
chance += 0.05;
|
|
}
|
|
|
|
// Evasion grants a variable bonus post ML. 50% prior.
|
|
if (Evasion.IsEvading(defender))
|
|
{
|
|
chance *= Evasion.GetParryScalar(defender);
|
|
}
|
|
|
|
// Low dexterity lowers the chance.
|
|
if (defender.Dex < 80)
|
|
{
|
|
chance = chance * (20 + defender.Dex) / 100;
|
|
}
|
|
|
|
bool success;
|
|
|
|
if (chance > aosChance)
|
|
{
|
|
success = defender.CheckSkill(SkillName.Parry, chance);
|
|
}
|
|
else
|
|
{
|
|
success = (aosChance > Utility.RandomDouble());
|
|
// Only skillcheck if wielding a shield & there's no effect from Bushido
|
|
}
|
|
|
|
if (Core.EJ && success)
|
|
{
|
|
weapon.LastParryChance = (int)(chance * 100);
|
|
weapon.InvalidateProperties();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual int AbsorbDamageAOS(Mobile attacker, Mobile defender, int damage)
|
|
{
|
|
int originalDamage = damage;
|
|
|
|
bool blocked = false;
|
|
|
|
if (defender.Player || defender.Body.IsHuman || (defender is BaseCreature &&
|
|
((BaseCreature)defender).Controlled &&
|
|
defender.Skills[SkillName.Wrestling].Base >= 100))
|
|
{
|
|
blocked = CheckParry(defender);
|
|
BaseWeapon weapon = defender.Weapon as BaseWeapon;
|
|
|
|
if (blocked)
|
|
{
|
|
defender.FixedEffect(0x37B9, 10, 16);
|
|
damage = 0;
|
|
|
|
if (Core.SA)
|
|
{
|
|
defender.Animate(AnimationType.Parry, 0);
|
|
}
|
|
|
|
// Successful block removes the Honorable Execution penalty.
|
|
HonorableExecution.RemovePenalty(defender);
|
|
|
|
if (CounterAttack.IsCountering(defender) && defender.InRange(attacker.Location, 1))
|
|
{
|
|
if (weapon != null)
|
|
{
|
|
var combatant = defender.Combatant;
|
|
|
|
defender.FixedParticles(0x3779, 1, 15, 0x158B, 0x0, 0x3, EffectLayer.Waist);
|
|
weapon.OnSwing(defender, attacker);
|
|
|
|
if (combatant != null && defender.Combatant != combatant && combatant.Alive)
|
|
defender.Combatant = combatant;
|
|
}
|
|
|
|
CounterAttack.StopCountering(defender);
|
|
}
|
|
|
|
if (Confidence.IsConfident(defender))
|
|
{
|
|
defender.SendLocalizedMessage(1063117);
|
|
// Your confidence reassures you as you successfully block your opponent's blow.
|
|
|
|
double bushido = defender.Skills.Bushido.Value;
|
|
|
|
defender.Hits += Utility.RandomMinMax(1, (int)(bushido / 12)) + MasteryInfo.AnticipateHitBonus(defender) / 10;
|
|
defender.Stam += Utility.RandomMinMax(1, (int)(bushido / 5)) + MasteryInfo.AnticipateHitBonus(defender) / 10;
|
|
}
|
|
|
|
BaseShield shield = defender.FindItemOnLayer(Layer.TwoHanded) as BaseShield;
|
|
|
|
if (shield != null)
|
|
{
|
|
shield.OnHit(this, damage);
|
|
|
|
#region Stygian Abyss
|
|
if (shield.ArmorAttributes.ReactiveParalyze > 0 && 30 > Utility.Random(100))
|
|
{
|
|
int secs = (int)Math.Max(1, Math.Max(8, defender.Skills[SkillName.EvalInt].Value / 10) - attacker.Skills[SkillName.MagicResist].Value / 10);
|
|
|
|
attacker.Paralyze(TimeSpan.FromSeconds(secs));
|
|
attacker.PlaySound(0x204);
|
|
attacker.FixedEffect(0x376A, 6, 1);
|
|
}
|
|
#endregion
|
|
|
|
XmlAttach.OnArmorHit(attacker, defender, shield, this, originalDamage);
|
|
}
|
|
|
|
#region Stygian Abyss
|
|
else if (weapon != null && weapon.Layer == Layer.TwoHanded && weapon.WeaponAttributes.ReactiveParalyze > 0 && 30 > Utility.Random(100))
|
|
{
|
|
int secs = (int)Math.Max(1, Math.Max(8, defender.Skills[SkillName.EvalInt].Value / 10) - attacker.Skills[SkillName.MagicResist].Value / 10);
|
|
|
|
attacker.Paralyze(TimeSpan.FromSeconds(secs));
|
|
attacker.PlaySound(0x204);
|
|
attacker.FixedEffect(0x376A, 6, 1);
|
|
}
|
|
#endregion
|
|
|
|
//Skill Masteries
|
|
SkillMasterySpell.OnParried(attacker, defender);
|
|
}
|
|
}
|
|
|
|
if (!blocked)
|
|
{
|
|
IWearableDurability toHit = GetRandomValidItem(defender) as IWearableDurability;
|
|
|
|
if (toHit != null)
|
|
{
|
|
toHit.OnHit(this, damage); // call OnHit to lose durability
|
|
|
|
if (toHit is Item && !((Item)toHit).Deleted && (attacker is VeriteElemental || attacker is ValoriteElemental))
|
|
VeriteElemental.OnHit(defender, (Item)toHit, damage);
|
|
|
|
damage -= XmlAttach.OnArmorHit(attacker, defender, (Item)toHit, this, originalDamage);
|
|
}
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
|
|
private Item GetRandomValidItem(Mobile m)
|
|
{
|
|
Item[] items = m.Items.Where(item => _DamageLayers.Contains(item.Layer) && item is IWearableDurability).ToArray();
|
|
|
|
if (items.Length == 0)
|
|
return null;
|
|
|
|
return items[Utility.Random(items.Length)];
|
|
}
|
|
|
|
private List<Layer> _DamageLayers = new List<Layer>()
|
|
{
|
|
Layer.FirstValid,
|
|
Layer.OneHanded,
|
|
Layer.TwoHanded,
|
|
Layer.Shoes,
|
|
Layer.Pants,
|
|
Layer.Shirt,
|
|
Layer.Helm,
|
|
Layer.Arms,
|
|
Layer.Gloves,
|
|
Layer.Ring,
|
|
Layer.Talisman,
|
|
Layer.Neck,
|
|
Layer.Waist,
|
|
Layer.InnerTorso,
|
|
Layer.Bracelet,
|
|
Layer.MiddleTorso,
|
|
Layer.Earrings,
|
|
Layer.Cloak,
|
|
Layer.OuterTorso,
|
|
Layer.OuterLegs,
|
|
Layer.InnerLegs,
|
|
};
|
|
|
|
public virtual int AbsorbDamage(Mobile attacker, Mobile defender, int damage)
|
|
{
|
|
if (Core.AOS)
|
|
{
|
|
return AbsorbDamageAOS(attacker, defender, damage);
|
|
}
|
|
|
|
BaseShield shield = defender.FindItemOnLayer(Layer.TwoHanded) as BaseShield;
|
|
if (shield != null)
|
|
{
|
|
damage = shield.OnHit(this, damage);
|
|
}
|
|
|
|
double chance = Utility.RandomDouble();
|
|
|
|
Item armorItem;
|
|
|
|
if (chance < 0.07)
|
|
{
|
|
armorItem = defender.NeckArmor;
|
|
}
|
|
else if (chance < 0.14)
|
|
{
|
|
armorItem = defender.HandArmor;
|
|
}
|
|
else if (chance < 0.28)
|
|
{
|
|
armorItem = defender.ArmsArmor;
|
|
}
|
|
else if (chance < 0.43)
|
|
{
|
|
armorItem = defender.HeadArmor;
|
|
}
|
|
else if (chance < 0.65)
|
|
{
|
|
armorItem = defender.LegsArmor;
|
|
}
|
|
else
|
|
{
|
|
armorItem = defender.ChestArmor;
|
|
}
|
|
|
|
IWearableDurability armor = armorItem as IWearableDurability;
|
|
|
|
if (armor != null)
|
|
{
|
|
damage = armor.OnHit(this, damage);
|
|
}
|
|
|
|
var virtualArmor = defender.ArmorRating;
|
|
|
|
damage -= XmlAttach.OnArmorHit(attacker, defender, armorItem, this, damage);
|
|
damage -= XmlAttach.OnArmorHit(attacker, defender, shield, this, damage);
|
|
|
|
if (virtualArmor > 0)
|
|
{
|
|
double scalar;
|
|
|
|
if (chance < 0.14)
|
|
{
|
|
scalar = 0.07;
|
|
}
|
|
else if (chance < 0.28)
|
|
{
|
|
scalar = 0.14;
|
|
}
|
|
else if (chance < 0.43)
|
|
{
|
|
scalar = 0.15;
|
|
}
|
|
else if (chance < 0.65)
|
|
{
|
|
scalar = 0.22;
|
|
}
|
|
else
|
|
{
|
|
scalar = 0.35;
|
|
}
|
|
|
|
int from = (int)(virtualArmor * scalar) / 2;
|
|
int to = (int)(virtualArmor * scalar);
|
|
|
|
damage -= Utility.Random(from, (to - from) + 1);
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
|
|
public virtual int GetPackInstinctBonus(Mobile attacker, Mobile defender)
|
|
{
|
|
if (attacker.Player || defender.Player)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
BaseCreature bc = attacker as BaseCreature;
|
|
|
|
if (bc == null || bc.PackInstinct == PackInstinct.None || (!bc.Controlled && !bc.Summoned))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Mobile master = bc.ControlMaster;
|
|
|
|
if (master == null)
|
|
{
|
|
master = bc.SummonMaster;
|
|
}
|
|
|
|
if (master == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int inPack = 1;
|
|
|
|
IPooledEnumerable eable = defender.GetMobilesInRange(1);
|
|
|
|
foreach (Mobile m in eable)
|
|
{
|
|
if (m != attacker && m is BaseCreature)
|
|
{
|
|
BaseCreature tc = (BaseCreature)m;
|
|
|
|
if ((tc.PackInstinct & bc.PackInstinct) == 0 || (!tc.Controlled && !tc.Summoned))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Mobile theirMaster = tc.ControlMaster;
|
|
|
|
if (theirMaster == null)
|
|
{
|
|
theirMaster = tc.SummonMaster;
|
|
}
|
|
|
|
if (master == theirMaster && tc.Combatant == defender)
|
|
{
|
|
++inPack;
|
|
}
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if (inPack >= 5)
|
|
{
|
|
return 100;
|
|
}
|
|
else if (inPack >= 4)
|
|
{
|
|
return 75;
|
|
}
|
|
else if (inPack >= 3)
|
|
{
|
|
return 50;
|
|
}
|
|
else if (inPack >= 2)
|
|
{
|
|
return 25;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private bool m_InDoubleStrike;
|
|
private bool m_ProcessingMultipleHits;
|
|
|
|
public bool InDoubleStrike
|
|
{
|
|
get { return m_InDoubleStrike; }
|
|
set
|
|
{
|
|
m_InDoubleStrike = value;
|
|
|
|
if (m_InDoubleStrike)
|
|
ProcessingMultipleHits = true;
|
|
else
|
|
ProcessingMultipleHits = false;
|
|
}
|
|
}
|
|
|
|
public bool ProcessingMultipleHits
|
|
{
|
|
get { return m_ProcessingMultipleHits; }
|
|
set
|
|
{
|
|
m_ProcessingMultipleHits = value;
|
|
|
|
if (!m_ProcessingMultipleHits)
|
|
BlockHitEffects = false;
|
|
}
|
|
}
|
|
|
|
public bool EndDualWield { get; set; }
|
|
public bool BlockHitEffects { get; set; }
|
|
public DateTime NextSelfRepair { get; set; }
|
|
|
|
public void OnHit(Mobile attacker, IDamageable damageable)
|
|
{
|
|
OnHit(attacker, damageable, 1.0);
|
|
}
|
|
|
|
public virtual void OnHit(Mobile attacker, IDamageable damageable, double damageBonus)
|
|
{
|
|
if (EndDualWield)
|
|
{
|
|
ProcessingMultipleHits = false;
|
|
EndDualWield = false;
|
|
}
|
|
|
|
Mobile defender = damageable as Mobile;
|
|
Clone clone = null;
|
|
|
|
if (defender != null)
|
|
{
|
|
clone = MirrorImage.GetDeflect(attacker, defender);
|
|
}
|
|
|
|
if (clone != null)
|
|
{
|
|
defender = clone;
|
|
}
|
|
|
|
PlaySwingAnimation(attacker);
|
|
|
|
if(defender != null)
|
|
PlayHurtAnimation(defender);
|
|
|
|
attacker.PlaySound(GetHitAttackSound(attacker, defender));
|
|
|
|
if(defender != null)
|
|
defender.PlaySound(GetHitDefendSound(attacker, defender));
|
|
|
|
int damage = ComputeDamage(attacker, defender);
|
|
|
|
WeaponAbility a = WeaponAbility.GetCurrentAbility(attacker);
|
|
SpecialMove move = SpecialMove.GetCurrentMove(attacker);
|
|
|
|
bool ranged = this is BaseRanged;
|
|
int phys, fire, cold, pois, nrgy, chaos, direct;
|
|
|
|
if ((Core.TOL && a is MovingShot) || SkillMasterySpell.HasSpell<ShieldBashSpell>(attacker))
|
|
{
|
|
phys = 100;
|
|
fire = cold = pois = nrgy = chaos = direct = 0;
|
|
}
|
|
else
|
|
{
|
|
GetDamageTypes(attacker, out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct);
|
|
|
|
if (!OnslaughtSpell.HasOnslaught(attacker, defender) &&
|
|
ConsecratedContext != null &&
|
|
ConsecratedContext.Owner == attacker &&
|
|
ConsecratedContext.ConsecrateProcChance >= Utility.Random(100))
|
|
{
|
|
phys = damageable.PhysicalResistance;
|
|
fire = damageable.FireResistance;
|
|
cold = damageable.ColdResistance;
|
|
pois = damageable.PoisonResistance;
|
|
nrgy = damageable.EnergyResistance;
|
|
|
|
int low = phys, type = 0;
|
|
|
|
if (fire < low) { low = fire; type = 1; }
|
|
if (cold < low) { low = cold; type = 2; }
|
|
if (pois < low) { low = pois; type = 3; }
|
|
if (nrgy < low) { low = nrgy; type = 4; }
|
|
|
|
phys = fire = cold = pois = nrgy = chaos = direct = 0;
|
|
|
|
if (type == 0) phys = 100;
|
|
else if (type == 1) fire = 100;
|
|
else if (type == 2) cold = 100;
|
|
else if (type == 3) pois = 100;
|
|
else if (type == 4) nrgy = 100;
|
|
}
|
|
else if (Core.ML && ranged)
|
|
{
|
|
IRangeDamage rangeDamage = attacker.FindItemOnLayer(Layer.Cloak) as IRangeDamage;
|
|
|
|
if (rangeDamage != null)
|
|
{
|
|
rangeDamage.AlterRangedDamage(ref phys, ref fire, ref cold, ref pois, ref nrgy, ref chaos, ref direct);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool splintering = false;
|
|
|
|
if (m_AosWeaponAttributes.SplinteringWeapon > 0 && m_AosWeaponAttributes.SplinteringWeapon > Utility.Random(100))
|
|
{
|
|
if (SplinteringWeaponContext.CheckHit(attacker, defender, a, this))
|
|
splintering = true;
|
|
}
|
|
|
|
double chance = NegativeAttributes.Antique > 0 ? 5 : 0;
|
|
bool acidicTarget = MaxRange <= 1 && m_AosAttributes.SpellChanneling == 0 && !(this is Fists) && (defender is Slime || defender is ToxicElemental || defender is CorrosiveSlime);
|
|
|
|
if (acidicTarget || (defender != null && splintering) || Utility.Random(40) <= chance)
|
|
{
|
|
if (MaxRange <= 1 && acidicTarget)
|
|
{
|
|
attacker.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500263); // *Acid blood scars your weapon!*
|
|
}
|
|
|
|
int selfRepair = !Core.AOS ? 0 : m_AosWeaponAttributes.SelfRepair + (IsSetItem && m_SetEquipped ? m_SetSelfRepair : 0);
|
|
|
|
if (selfRepair > 0 && NextSelfRepair < DateTime.UtcNow)
|
|
{
|
|
HitPoints += selfRepair;
|
|
|
|
NextSelfRepair = DateTime.UtcNow + TimeSpan.FromSeconds(60);
|
|
}
|
|
else
|
|
{
|
|
if (m_MaxHits > 0)
|
|
{
|
|
if (m_Hits >= 1)
|
|
{
|
|
if (splintering)
|
|
{
|
|
HitPoints = Math.Max(0, HitPoints - 10);
|
|
}
|
|
else
|
|
{
|
|
HitPoints--;
|
|
}
|
|
}
|
|
else if (m_MaxHits > 0)
|
|
{
|
|
MaxHitPoints--;
|
|
|
|
if (Parent is Mobile)
|
|
((Mobile)Parent).LocalOverheadMessage(MessageType.Regular, 0x3B2, 1061121); // Your equipment is severely damaged.
|
|
|
|
if (m_MaxHits <= 0)
|
|
Delete();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
WeaponAbility weavabil;
|
|
bool bladeweaving = Bladeweave.BladeWeaving(attacker, out weavabil);
|
|
bool ignoreArmor = (a is ArmorIgnore || (move != null && move.IgnoreArmor(attacker)) || (bladeweaving && weavabil is ArmorIgnore));
|
|
|
|
// object is not a mobile, so we end here
|
|
if (defender == null)
|
|
{
|
|
AOS.Damage(damageable, attacker, damage, ignoreArmor, phys, fire, cold, pois, nrgy, chaos, direct, false, ranged ? Server.DamageType.Ranged : Server.DamageType.Melee);
|
|
|
|
// TODO: WeaponAbility/SpecialMove OnHit(...) convert target to IDamageable
|
|
// Figure out which specials work on items. For now AI only.
|
|
if (ignoreArmor)
|
|
{
|
|
Effects.PlaySound(damageable.Location, damageable.Map, 0x56);
|
|
Effects.SendTargetParticles(damageable, 0x3728, 200, 25, 0, 0, 9942, EffectLayer.Waist, 0);
|
|
}
|
|
|
|
WeaponAbility.ClearCurrentAbility(attacker);
|
|
SpecialMove.ClearCurrentMove(attacker);
|
|
|
|
if (WeaponAttributes.HitLeechHits > 0)
|
|
{
|
|
attacker.SendLocalizedMessage(1152566); // You fail to leech life from your target!
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#region Damage Multipliers
|
|
/*
|
|
* The following damage bonuses multiply damage by a factor.
|
|
* Capped at x3 (300%).
|
|
*/
|
|
int percentageBonus = 0;
|
|
|
|
if (a != null)
|
|
{
|
|
percentageBonus += (int)(a.DamageScalar * 100) - 100;
|
|
}
|
|
|
|
if (move != null)
|
|
{
|
|
percentageBonus += (int)(move.GetDamageScalar(attacker, defender) * 100) - 100;
|
|
}
|
|
|
|
if (ConsecratedContext != null && ConsecratedContext.Owner == attacker)
|
|
{
|
|
percentageBonus += ConsecratedContext.ConsecrateDamageBonus;
|
|
}
|
|
|
|
percentageBonus += (int)(damageBonus * 100) - 100;
|
|
|
|
CheckSlayerResult cs1 = CheckSlayers(attacker, defender, Slayer);
|
|
CheckSlayerResult cs2 = CheckSlayers(attacker, defender, Slayer2);
|
|
CheckSlayerResult suit = CheckSlayers(attacker, defender, SetHelper.GetSetSlayer(attacker));
|
|
CheckSlayerResult tal = CheckTalismanSlayer(attacker, defender);
|
|
|
|
if (cs1 == CheckSlayerResult.None && cs2 == CheckSlayerResult.None)
|
|
{
|
|
cs1 = CheckSlayers(attacker, defender, SlayerSocket.GetSlayer(this));
|
|
}
|
|
|
|
if (cs1 != CheckSlayerResult.None)
|
|
{
|
|
if (cs1 == CheckSlayerResult.SuperSlayer)
|
|
percentageBonus += 100;
|
|
else if (cs1 == CheckSlayerResult.Slayer)
|
|
percentageBonus += 200;
|
|
}
|
|
|
|
if (cs2 != CheckSlayerResult.None)
|
|
{
|
|
if (cs2 == CheckSlayerResult.SuperSlayer)
|
|
percentageBonus += 100;
|
|
else if (cs2 == CheckSlayerResult.Slayer)
|
|
percentageBonus += 200;
|
|
}
|
|
|
|
if (suit != CheckSlayerResult.None)
|
|
{
|
|
percentageBonus += 100;
|
|
}
|
|
|
|
if (tal != CheckSlayerResult.None)
|
|
{
|
|
percentageBonus += 100;
|
|
}
|
|
|
|
if (CheckSlayerOpposition(attacker, defender) != CheckSlayerResult.None)
|
|
{
|
|
percentageBonus += 100;
|
|
defender.FixedEffect(0x37B9, 10, 5);
|
|
}
|
|
else if (cs1 != CheckSlayerResult.None || cs2 != CheckSlayerResult.None || suit != CheckSlayerResult.None || tal != CheckSlayerResult.None)
|
|
{
|
|
defender.FixedEffect(0x37B9, 10, 5);
|
|
}
|
|
|
|
#region Enemy of One
|
|
var enemyOfOneContext = EnemyOfOneSpell.GetContext(defender);
|
|
|
|
if (enemyOfOneContext != null && !enemyOfOneContext.IsWaitingForEnemy && !enemyOfOneContext.IsEnemy(attacker))
|
|
{
|
|
percentageBonus += 100;
|
|
}
|
|
else
|
|
{
|
|
enemyOfOneContext = EnemyOfOneSpell.GetContext(attacker);
|
|
|
|
if (enemyOfOneContext != null)
|
|
{
|
|
enemyOfOneContext.OnHit(defender);
|
|
|
|
if (enemyOfOneContext.IsEnemy(defender))
|
|
{
|
|
defender.FixedEffect(0x37B9, 10, 5, 1160, 0);
|
|
percentageBonus += enemyOfOneContext.DamageScalar;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
int packInstinctBonus = GetPackInstinctBonus(attacker, defender);
|
|
|
|
if (packInstinctBonus != 0)
|
|
{
|
|
percentageBonus += packInstinctBonus;
|
|
}
|
|
|
|
if (m_InDoubleStrike)
|
|
{
|
|
percentageBonus -= 10;
|
|
}
|
|
|
|
TransformContext context = TransformationSpellHelper.GetContext(defender);
|
|
|
|
if ((m_Slayer == SlayerName.Silver || m_Slayer2 == SlayerName.Silver || SetHelper.GetSetSlayer(attacker) == SlayerName.Silver)
|
|
&& ((context != null && context.Spell is NecromancerSpell && context.Type != typeof(HorrificBeastSpell))
|
|
|| (defender is BaseCreature && (defender.Body == 747 || defender.Body == 748 || defender.Body == 749 || defender.Hue == 0x847E))))
|
|
{
|
|
// Every necromancer transformation other than horrific beast takes an additional 25% damage
|
|
percentageBonus += 25;
|
|
}
|
|
|
|
if (attacker is PlayerMobile && !(Core.ML && defender is PlayerMobile))
|
|
{
|
|
PlayerMobile pmAttacker = (PlayerMobile)attacker;
|
|
|
|
if (pmAttacker.HonorActive && pmAttacker.InRange(defender, 1))
|
|
{
|
|
percentageBonus += 25;
|
|
}
|
|
|
|
if (pmAttacker.SentHonorContext != null && pmAttacker.SentHonorContext.Target == defender)
|
|
{
|
|
percentageBonus += pmAttacker.SentHonorContext.PerfectionDamageBonus;
|
|
}
|
|
}
|
|
|
|
percentageBonus -= Block.GetMeleeReduction(defender);
|
|
|
|
#region Stygian Abyss
|
|
percentageBonus += BattleLust.GetBonus(attacker, defender);
|
|
|
|
if (this is BaseThrown)
|
|
{
|
|
double dist = attacker.GetDistanceToSqrt(defender);
|
|
int max = ((BaseThrown)this).MaxThrowRange;
|
|
|
|
if (dist > max)
|
|
percentageBonus -= 47;
|
|
}
|
|
|
|
if (RunedSashOfWarding.IsUnderEffects(defender, WardingEffect.WeaponDamage))
|
|
percentageBonus -= 10;
|
|
#endregion
|
|
|
|
#region Mondain's Legacy
|
|
if (Core.ML)
|
|
{
|
|
BaseTalisman talisman = attacker.Talisman as BaseTalisman;
|
|
|
|
if (talisman != null && talisman.Killer != null)
|
|
{
|
|
percentageBonus += talisman.Killer.DamageBonus(defender);
|
|
}
|
|
|
|
if (this is ButchersWarCleaver)
|
|
{
|
|
if (defender is Bull || defender is Cow || defender is Gaman)
|
|
{
|
|
percentageBonus += 100;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
percentageBonus += ForceOfNature.GetBonus(attacker, defender);
|
|
|
|
if (m_ExtendedWeaponAttributes.AssassinHoned > 0 && GetOppositeDir(attacker.Direction) == defender.Direction)
|
|
{
|
|
if (!ranged || 0.5 > Utility.RandomDouble())
|
|
{
|
|
percentageBonus += (int)(146.0 / MlSpeed);
|
|
}
|
|
}
|
|
|
|
if (m_ExtendedWeaponAttributes.Focus > 0)
|
|
{
|
|
percentageBonus += Focus.GetBonus(attacker, defender);
|
|
Focus.OnHit(attacker, defender);
|
|
}
|
|
|
|
percentageBonus = Math.Min(percentageBonus, 300);
|
|
|
|
// bonus is seprate from weapon damage, ie not capped
|
|
percentageBonus += Spells.Mysticism.StoneFormSpell.GetMaxResistBonus(attacker);
|
|
|
|
damage = AOS.Scale(damage, 100 + percentageBonus);
|
|
#endregion
|
|
|
|
damage = AbsorbDamage(attacker, defender, damage);
|
|
|
|
if (!Core.AOS && damage < 1)
|
|
{
|
|
damage = 1;
|
|
}
|
|
else if (Core.AOS && damage == 0) // parried
|
|
{
|
|
if ((a != null && a.Validate(attacker)) || (move != null && move.Validate(attacker)))
|
|
// Parried special moves have no mana cost - era specific
|
|
{
|
|
if (Core.SE || (a != null && a.CheckMana(attacker, true)))
|
|
{
|
|
WeaponAbility.ClearCurrentAbility(attacker);
|
|
SpecialMove.ClearCurrentMove(attacker);
|
|
|
|
attacker.SendLocalizedMessage(1061140); // Your attack was parried!
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Skill Masteries
|
|
if (WhiteTigerFormSpell.CheckEvasion(defender))
|
|
{
|
|
defender.Emote("*evades*"); // Is this right?
|
|
return;
|
|
}
|
|
|
|
#region Mondain's Legacy
|
|
if (ImmolatingWeaponSpell.IsImmolating(attacker, this))
|
|
{
|
|
int d = ImmolatingWeaponSpell.GetImmolatingDamage(attacker);
|
|
d = AOS.Damage(defender, attacker, d, 0, 100, 0, 0, 0);
|
|
|
|
ImmolatingWeaponSpell.DoDelayEffect(attacker, defender);
|
|
|
|
if (d > 0)
|
|
{
|
|
defender.Damage(d);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region SA
|
|
if (defender != null && Server.Items.SearingWeapon.CanSear(this) && attacker.Mana > 0)
|
|
{
|
|
int d = SearingWeaponContext.Damage;
|
|
|
|
if ((ranged && 10 > Utility.Random(100)) || 20 > Utility.Random(100))
|
|
{
|
|
AOS.Damage(defender, attacker, d, 0, 100, 0, 0, 0);
|
|
AOS.Damage(attacker, null, 4, false, 0, 0, 0, 0, 0, 0, 100, false, false, false);
|
|
|
|
defender.FixedParticles(0x36F4, 1, 11, 0x13A8, 0, 0, EffectLayer.Waist);
|
|
|
|
SearingWeaponContext.CheckHit(attacker, defender);
|
|
attacker.Mana--;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region BoneBreaker/Swarm/Sparks
|
|
bool sparks = false;
|
|
if (a == null && move == null)
|
|
{
|
|
if (m_ExtendedWeaponAttributes.BoneBreaker > 0)
|
|
damage += BoneBreakerContext.CheckHit(attacker, defender);
|
|
|
|
if (m_ExtendedWeaponAttributes.HitSwarm > 0 && Utility.Random(100) < m_ExtendedWeaponAttributes.HitSwarm)
|
|
SwarmContext.CheckHit(attacker, defender);
|
|
|
|
if (m_ExtendedWeaponAttributes.HitSparks > 0 && Utility.Random(100) < m_ExtendedWeaponAttributes.HitSparks)
|
|
{
|
|
SparksContext.CheckHit(attacker, defender);
|
|
sparks = true;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
Timer.DelayCall(d => AddBlood(d, damage), defender);
|
|
|
|
int damageGiven = damage;
|
|
|
|
if (a != null && !a.OnBeforeDamage(attacker, defender))
|
|
{
|
|
WeaponAbility.ClearCurrentAbility(attacker);
|
|
a = null;
|
|
}
|
|
|
|
if (move != null && !move.OnBeforeDamage(attacker, defender))
|
|
{
|
|
SpecialMove.ClearCurrentMove(attacker);
|
|
move = null;
|
|
}
|
|
|
|
if (Feint.Registry.ContainsKey(defender) && Feint.Registry[defender].Enemy == attacker)
|
|
damage -= (int)((double)damage * ((double)Feint.Registry[defender].DamageReduction / 100));
|
|
|
|
// Skill Masteries
|
|
if (this is Fists)
|
|
damage += (int)((double)damage * ((double)MasteryInfo.GetKnockoutModifier(attacker, defender is PlayerMobile) / 100.0));
|
|
|
|
SkillMasterySpell.OnHit(attacker, defender, ref damage);
|
|
|
|
// Bane
|
|
if (m_ExtendedWeaponAttributes.Bane > 0 && defender.Hits < defender.HitsMax / 2)
|
|
{
|
|
double inc = Math.Min(350, (double)defender.HitsMax * .3);
|
|
inc -= (double)((double)defender.Hits / (double)defender.HitsMax) * inc;
|
|
|
|
Effects.SendTargetEffect(defender, 0x37BE, 1, 4, 0x30, 3);
|
|
|
|
damage += (int)inc;
|
|
}
|
|
|
|
damageGiven = AOS.Damage(
|
|
defender,
|
|
attacker,
|
|
damage,
|
|
ignoreArmor,
|
|
phys,
|
|
fire,
|
|
cold,
|
|
pois,
|
|
nrgy,
|
|
chaos,
|
|
direct,
|
|
false,
|
|
ranged ? Server.DamageType.Ranged : Server.DamageType.Melee);
|
|
|
|
DualWield.DoHit(attacker, defender, damage);
|
|
|
|
if (sparks)
|
|
{
|
|
int mana = attacker.Mana + damageGiven;
|
|
if (!defender.Player) mana *= 2;
|
|
attacker.Mana = Math.Min(attacker.ManaMax, attacker.Mana + mana);
|
|
}
|
|
|
|
double propertyBonus = (move == null) ? 1.0 : move.GetPropertyBonus(attacker);
|
|
|
|
if (Core.AOS)
|
|
{
|
|
int lifeLeech = 0;
|
|
int stamLeech = 0;
|
|
int manaLeech = 0;
|
|
|
|
if ((int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitLeechStam) * propertyBonus) >
|
|
Utility.Random(100))
|
|
{
|
|
stamLeech += 100; // HitLeechStam% chance to leech 100% of damage as stamina
|
|
}
|
|
|
|
if (Core.SA)
|
|
{
|
|
lifeLeech = (int)(WeaponAttributes.HitLeechHits * propertyBonus);
|
|
manaLeech = (int)(WeaponAttributes.HitLeechMana * propertyBonus);
|
|
}
|
|
else
|
|
{
|
|
if ((int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitLeechHits) * propertyBonus) >
|
|
Utility.Random(100))
|
|
{
|
|
lifeLeech += 30; // HitLeechHits% chance to leech 30% of damage as hit points
|
|
}
|
|
|
|
if ((int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitLeechMana) * propertyBonus) >
|
|
Utility.Random(100))
|
|
{
|
|
manaLeech += 40; // HitLeechMana% chance to leech 40% of damage as mana
|
|
}
|
|
}
|
|
|
|
int toHealCursedWeaponSpell = 0;
|
|
|
|
if (CurseWeaponSpell.IsCursed(attacker, this))
|
|
{
|
|
toHealCursedWeaponSpell += (int)(AOS.Scale(damageGiven, 50)); // Additional 50% life leech for cursed weapons (necro spell)
|
|
}
|
|
|
|
context = TransformationSpellHelper.GetContext(attacker);
|
|
|
|
if (stamLeech != 0)
|
|
{
|
|
attacker.Stam += AOS.Scale(damageGiven, stamLeech);
|
|
}
|
|
|
|
if (Core.SA) // New formulas
|
|
{
|
|
if (lifeLeech != 0)
|
|
{
|
|
int toHeal = Utility.RandomMinMax(0, (int)(AOS.Scale(damageGiven, lifeLeech) * 0.3));
|
|
|
|
if (defender is BaseCreature && ((BaseCreature)defender).TaintedLifeAura)
|
|
{
|
|
AOS.Damage(attacker, defender, toHeal, false, 0, 0, 0, 0, 0, 0, 100, false, false, false);
|
|
attacker.SendLocalizedMessage(1116778); //The tainted life force energy damages you as your body tries to absorb it.
|
|
}
|
|
else
|
|
{
|
|
attacker.Hits += toHeal;
|
|
}
|
|
|
|
Effects.SendPacket(defender.Location, defender.Map, new ParticleEffect(EffectType.FixedFrom, defender.Serial, Serial.Zero, 0x377A, defender.Location, defender.Location, 1, 15, false, false, 1926, 0, 0, 9502, 1, defender.Serial, 16, 0));
|
|
Effects.SendPacket(defender.Location, defender.Map, new ParticleEffect(EffectType.FixedFrom, defender.Serial, Serial.Zero, 0x3728, defender.Location, defender.Location, 1, 12, false, false, 1963, 0, 0, 9042, 1, defender.Serial, 16, 0));
|
|
}
|
|
|
|
if (toHealCursedWeaponSpell != 0 && !(defender is BaseCreature && ((BaseCreature)defender).TaintedLifeAura))
|
|
{
|
|
attacker.Hits += toHealCursedWeaponSpell;
|
|
}
|
|
|
|
if (manaLeech != 0)
|
|
{
|
|
attacker.Mana += Utility.RandomMinMax(0, (int)(AOS.Scale(damageGiven, manaLeech) * 0.4));
|
|
}
|
|
}
|
|
else // Old formulas
|
|
{
|
|
if (toHealCursedWeaponSpell != 0)
|
|
{
|
|
attacker.Hits += toHealCursedWeaponSpell;
|
|
}
|
|
|
|
if (lifeLeech != 0)
|
|
{
|
|
attacker.Hits += AOS.Scale(damageGiven, lifeLeech);
|
|
}
|
|
|
|
if (manaLeech != 0)
|
|
{
|
|
attacker.Mana += AOS.Scale(damageGiven, manaLeech);
|
|
}
|
|
}
|
|
|
|
if (lifeLeech != 0 || stamLeech != 0 || manaLeech != 0 || toHealCursedWeaponSpell != 0)
|
|
{
|
|
attacker.PlaySound(0x44D);
|
|
}
|
|
}
|
|
|
|
if (attacker is VampireBatFamiliar || attacker is VampireBat)
|
|
{
|
|
BaseCreature bc = (BaseCreature)attacker;
|
|
Mobile caster = bc.ControlMaster;
|
|
|
|
if (caster == null)
|
|
{
|
|
caster = bc.SummonMaster;
|
|
}
|
|
|
|
if (caster != null && caster.Map == bc.Map && caster.InRange(bc, 2))
|
|
{
|
|
caster.Hits += damage;
|
|
}
|
|
else
|
|
{
|
|
bc.Hits += damage;
|
|
}
|
|
}
|
|
|
|
if (Core.AOS && !BlockHitEffects)
|
|
{
|
|
int physChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitPhysicalArea) * propertyBonus);
|
|
int fireChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitFireArea) * propertyBonus);
|
|
int coldChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitColdArea) * propertyBonus);
|
|
int poisChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitPoisonArea) * propertyBonus);
|
|
int nrgyChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitEnergyArea) * propertyBonus);
|
|
|
|
if (physChance != 0 && physChance > Utility.Random(100))
|
|
{
|
|
DoAreaAttack(attacker, defender, damageGiven, 0x10E, 50, 100, 0, 0, 0, 0);
|
|
}
|
|
|
|
if (fireChance != 0 && fireChance > Utility.Random(100))
|
|
{
|
|
DoAreaAttack(attacker, defender, damageGiven, 0x11D, 1160, 0, 100, 0, 0, 0);
|
|
}
|
|
|
|
if (coldChance != 0 && coldChance > Utility.Random(100))
|
|
{
|
|
DoAreaAttack(attacker, defender, damageGiven, 0x0FC, 2100, 0, 0, 100, 0, 0);
|
|
}
|
|
|
|
if (poisChance != 0 && poisChance > Utility.Random(100))
|
|
{
|
|
DoAreaAttack(attacker, defender, damageGiven, 0x205, 1166, 0, 0, 0, 100, 0);
|
|
}
|
|
|
|
if (nrgyChance != 0 && nrgyChance > Utility.Random(100))
|
|
{
|
|
DoAreaAttack(attacker, defender, damageGiven, 0x1F1, 120, 0, 0, 0, 0, 100);
|
|
}
|
|
|
|
int maChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitMagicArrow) * propertyBonus);
|
|
int harmChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitHarm) * propertyBonus);
|
|
int fireballChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitFireball) * propertyBonus);
|
|
int lightningChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitLightning) * propertyBonus);
|
|
int dispelChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitDispel) * propertyBonus);
|
|
int explosChance = (int)(ExtendedWeaponAttributes.GetValue(attacker, ExtendedWeaponAttribute.HitExplosion) * propertyBonus);
|
|
|
|
#region Mondains Legacy
|
|
int velocityChance = this is BaseRanged ? (int)((BaseRanged)this).Velocity : 0;
|
|
#endregion
|
|
|
|
#region Stygian Abyss
|
|
int curseChance = (int)(m_AosWeaponAttributes.HitCurse * propertyBonus);
|
|
int fatigueChance = (int)(m_AosWeaponAttributes.HitFatigue * propertyBonus);
|
|
int manadrainChance = (int)(m_AosWeaponAttributes.HitManaDrain * propertyBonus);
|
|
#endregion
|
|
|
|
if (maChance != 0 && maChance > Utility.Random(100))
|
|
{
|
|
DoMagicArrow(attacker, defender);
|
|
}
|
|
|
|
if (harmChance != 0 && harmChance > Utility.Random(100))
|
|
{
|
|
DoHarm(attacker, defender);
|
|
}
|
|
|
|
if (fireballChance != 0 && fireballChance > Utility.Random(100))
|
|
{
|
|
DoFireball(attacker, defender);
|
|
}
|
|
|
|
if (lightningChance != 0 && lightningChance > Utility.Random(100))
|
|
{
|
|
DoLightning(attacker, defender);
|
|
}
|
|
|
|
if (dispelChance != 0 && dispelChance > Utility.Random(100))
|
|
{
|
|
DoDispel(attacker, defender);
|
|
}
|
|
|
|
if (explosChance != 0 && explosChance > Utility.Random(100))
|
|
{
|
|
DoExplosion(attacker, defender);
|
|
}
|
|
|
|
#region Mondains Legacy
|
|
if (Core.ML && velocityChance != 0 && velocityChance > Utility.Random(100))
|
|
{
|
|
DoHitVelocity(attacker, damageable);
|
|
}
|
|
#endregion
|
|
|
|
#region Stygian Abyss
|
|
if (curseChance != 0 && curseChance > Utility.Random(100))
|
|
{
|
|
DoCurse(attacker, defender);
|
|
}
|
|
|
|
if (fatigueChance != 0 && fatigueChance > Utility.Random(100))
|
|
{
|
|
DoFatigue(attacker, defender, damageGiven);
|
|
}
|
|
|
|
if (manadrainChance != 0 && manadrainChance > Utility.Random(100))
|
|
{
|
|
DoManaDrain(attacker, defender, damageGiven);
|
|
}
|
|
#endregion
|
|
|
|
int laChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitLowerAttack) * propertyBonus);
|
|
|
|
if (laChance != 0 && laChance > Utility.Random(100))
|
|
{
|
|
DoLowerAttack(attacker, defender);
|
|
}
|
|
|
|
if (!Core.HS)
|
|
{
|
|
int ldChance = (int)(AosWeaponAttributes.GetValue(attacker, AosWeaponAttribute.HitLowerDefend) * propertyBonus);
|
|
|
|
if (ldChance != 0 && ldChance > Utility.Random(100))
|
|
{
|
|
DoLowerDefense(attacker, defender);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int hldWep = m_AosWeaponAttributes.HitLowerDefend;
|
|
int hldGlasses = 0;
|
|
|
|
var helm = attacker.FindItemOnLayer(Layer.Helm);
|
|
|
|
if (helm != null)
|
|
{
|
|
var attrs = RunicReforging.GetAosWeaponAttributes(helm);
|
|
|
|
if(attrs != null)
|
|
hldGlasses = attrs.HitLowerDefend;
|
|
}
|
|
|
|
if ((hldWep > 0 && hldWep > Utility.Random(100)) || (hldGlasses > 0 && hldGlasses > Utility.Random(100)))
|
|
{
|
|
DoLowerDefense(attacker, defender);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (attacker is BaseCreature)
|
|
{
|
|
((BaseCreature)attacker).OnGaveMeleeAttack(defender);
|
|
}
|
|
|
|
if (defender is BaseCreature)
|
|
{
|
|
((BaseCreature)defender).OnGotMeleeAttack(attacker);
|
|
}
|
|
|
|
if (a != null)
|
|
{
|
|
a.OnHit(attacker, defender, damage);
|
|
}
|
|
|
|
if (move != null)
|
|
{
|
|
move.OnHit(attacker, defender, damage);
|
|
}
|
|
|
|
if (defender is IHonorTarget && ((IHonorTarget)defender).ReceivedHonorContext != null)
|
|
{
|
|
((IHonorTarget)defender).ReceivedHonorContext.OnTargetHit(attacker);
|
|
}
|
|
|
|
if (!ranged)
|
|
{
|
|
if (AnimalForm.UnderTransformation(attacker, typeof(GiantSerpent)))
|
|
{
|
|
defender.ApplyPoison(attacker, Poison.Lesser);
|
|
}
|
|
|
|
if (AnimalForm.UnderTransformation(defender, typeof(BullFrog)))
|
|
{
|
|
attacker.ApplyPoison(defender, Poison.Regular);
|
|
}
|
|
}
|
|
|
|
BaseFamiliar.OnHit(attacker, damageable);
|
|
WhiteTigerFormSpell.OnHit(attacker, defender);
|
|
XmlAttach.OnWeaponHit(this, attacker, defender, damageGiven);
|
|
}
|
|
|
|
public Direction GetOppositeDir(Direction d)
|
|
{
|
|
Direction direction = Direction.Down;
|
|
|
|
if (d == Direction.West)
|
|
direction = Direction.East;
|
|
|
|
if (d == Direction.East)
|
|
direction = Direction.West;
|
|
|
|
if (d == Direction.North)
|
|
direction = Direction.South;
|
|
|
|
if (d == Direction.South)
|
|
direction = Direction.North;
|
|
|
|
if (d == Direction.Right)
|
|
direction = Direction.Left;
|
|
|
|
if (d == Direction.Left)
|
|
direction = Direction.Right;
|
|
|
|
if (d == Direction.Up)
|
|
direction = Direction.Down;
|
|
|
|
if (d == Direction.Down)
|
|
direction = Direction.Up;
|
|
|
|
return direction;
|
|
}
|
|
|
|
public virtual double GetAosSpellDamage(Mobile attacker, Mobile defender, int bonus, int dice, int sides)
|
|
{
|
|
int damage = Utility.Dice(dice, sides, bonus) * 100;
|
|
int damageBonus = 0;
|
|
|
|
int inscribeSkill = attacker.Skills[SkillName.Inscribe].Fixed;
|
|
int inscribeBonus = (inscribeSkill + (1000 * (inscribeSkill / 1000))) / 200;
|
|
|
|
damageBonus += inscribeBonus;
|
|
damageBonus += attacker.Int / 10;
|
|
damageBonus += SpellHelper.GetSpellDamageBonus(attacker, defender, SkillName.Magery, attacker is PlayerMobile && defender is PlayerMobile);
|
|
damage = AOS.Scale(damage, 100 + damageBonus);
|
|
|
|
if (defender != null && Feint.Registry.ContainsKey(defender) && Feint.Registry[defender].Enemy == attacker)
|
|
damage -= (int)((double)damage * ((double)Feint.Registry[defender].DamageReduction / 100));
|
|
|
|
// All hit spells use 80 eval
|
|
int evalScale = 30 + ((9 * 800) / 100);
|
|
|
|
damage = AOS.Scale(damage, evalScale);
|
|
|
|
return damage / 100;
|
|
}
|
|
|
|
#region Do<AoSEffect>
|
|
public virtual void DoMagicArrow(Mobile attacker, Mobile defender)
|
|
{
|
|
if (!attacker.CanBeHarmful(defender, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
attacker.DoHarmful(defender);
|
|
|
|
double damage = GetAosSpellDamage(attacker, defender, 10, 1, 4);
|
|
|
|
attacker.MovingParticles(defender, 0x36E4, 5, 0, false, true, 3006, 4006, 0);
|
|
attacker.PlaySound(0x1E5);
|
|
|
|
SpellHelper.Damage(TimeSpan.FromSeconds(1.0), defender, attacker, damage, 0, 100, 0, 0, 0);
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoHarm(Mobile attacker, Mobile defender)
|
|
{
|
|
if (!attacker.CanBeHarmful(defender, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
attacker.DoHarmful(defender);
|
|
|
|
double damage = GetAosSpellDamage(attacker, defender, 17, 1, 5);
|
|
|
|
if (!defender.InRange(attacker, 2))
|
|
{
|
|
damage *= 0.25; // 1/4 damage at > 2 tile range
|
|
}
|
|
else if (!defender.InRange(attacker, 1))
|
|
{
|
|
damage *= 0.50; // 1/2 damage at 2 tile range
|
|
}
|
|
|
|
defender.FixedParticles(0x374A, 10, 30, 5013, 1153, 2, EffectLayer.Waist);
|
|
defender.PlaySound(0x0FC);
|
|
|
|
SpellHelper.Damage(TimeSpan.Zero, defender, attacker, damage, 0, 0, 100, 0, 0);
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoFireball(Mobile attacker, Mobile defender)
|
|
{
|
|
if (!attacker.CanBeHarmful(defender, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
attacker.DoHarmful(defender);
|
|
|
|
double damage = GetAosSpellDamage(attacker, defender, 19, 1, 5);
|
|
|
|
attacker.MovingParticles(defender, 0x36D4, 7, 0, false, true, 9502, 4019, 0x160);
|
|
attacker.PlaySound(0x15E);
|
|
|
|
SpellHelper.Damage(TimeSpan.FromSeconds(1.0), defender, attacker, damage, 0, 100, 0, 0, 0);
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoLightning(Mobile attacker, Mobile defender)
|
|
{
|
|
if (!attacker.CanBeHarmful(defender, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
attacker.DoHarmful(defender);
|
|
|
|
double damage = GetAosSpellDamage(attacker, defender, 23, 1, 4);
|
|
|
|
defender.BoltEffect(0);
|
|
|
|
SpellHelper.Damage(TimeSpan.Zero, defender, attacker, damage, 0, 0, 0, 0, 100);
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoDispel(Mobile attacker, Mobile defender)
|
|
{
|
|
bool dispellable = false;
|
|
|
|
if (defender is BaseCreature)
|
|
{
|
|
dispellable = ((BaseCreature)defender).Summoned && !((BaseCreature)defender).IsAnimatedDead;
|
|
}
|
|
|
|
if (!dispellable)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!attacker.CanBeHarmful(defender, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
attacker.DoHarmful(defender);
|
|
|
|
MagerySpell sp = new DispelSpell(attacker, null);
|
|
|
|
if (sp.CheckResisted(defender))
|
|
{
|
|
defender.FixedEffect(0x3779, 10, 20);
|
|
}
|
|
else
|
|
{
|
|
Effects.SendLocationParticles(
|
|
EffectItem.Create(defender.Location, defender.Map, EffectItem.DefaultDuration), 0x3728, 8, 20, 5042);
|
|
Effects.PlaySound(defender, defender.Map, 0x201);
|
|
|
|
defender.Delete();
|
|
}
|
|
}
|
|
|
|
public virtual void DoExplosion(Mobile attacker, Mobile defender)
|
|
{
|
|
if (!attacker.CanBeHarmful(defender, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
attacker.DoHarmful(defender);
|
|
|
|
double damage = GetAosSpellDamage(attacker, defender, 40, 1, 5);
|
|
|
|
defender.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head);
|
|
defender.PlaySound(0x307);
|
|
|
|
SpellHelper.Damage(TimeSpan.FromSeconds(1.0), defender, attacker, damage, 0, 100, 0, 0, 0);
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoHitVelocity(Mobile attacker, IDamageable damageable)
|
|
{
|
|
int bonus = (int)attacker.GetDistanceToSqrt(damageable);
|
|
|
|
if (bonus > 0)
|
|
{
|
|
AOS.Damage(damageable, attacker, bonus * 3, 100, 0, 0, 0, 0);
|
|
|
|
if (attacker.Player)
|
|
{
|
|
attacker.SendLocalizedMessage(1072794); // Your arrow hits its mark with velocity!
|
|
}
|
|
|
|
if (damageable is Mobile && ((Mobile)damageable).Player)
|
|
{
|
|
((Mobile)damageable).SendLocalizedMessage(1072795); // You have been hit by an arrow with velocity!
|
|
}
|
|
}
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
#region Stygian Abyss
|
|
public virtual void DoCurse(Mobile attacker, Mobile defender)
|
|
{
|
|
attacker.SendLocalizedMessage(1113717); // You have hit your target with a curse effect.
|
|
defender.SendLocalizedMessage(1113718); // You have been hit with a curse effect.
|
|
|
|
defender.FixedParticles(0x374A, 10, 15, 5028, EffectLayer.Waist);
|
|
defender.PlaySound(0x1EA);
|
|
TimeSpan duration = TimeSpan.FromSeconds(30);
|
|
|
|
defender.AddStatMod(
|
|
new StatMod(StatType.Str, String.Format("[Magic] {0} Curse", StatType.Str), -10, duration));
|
|
defender.AddStatMod(
|
|
new StatMod(StatType.Dex, String.Format("[Magic] {0} Curse", StatType.Dex), -10, duration));
|
|
defender.AddStatMod(
|
|
new StatMod(StatType.Int, String.Format("[Magic] {0} Curse", StatType.Int), -10, duration));
|
|
|
|
int percentage = -10; //(int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100);
|
|
string args = String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", percentage, percentage, percentage, 10, 10, 10, 10);
|
|
|
|
Server.Spells.Fourth.CurseSpell.AddEffect(defender, duration, 10, 10, 10);
|
|
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.Curse, 1075835, 1075836, duration, defender, args));
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoFatigue(Mobile attacker, Mobile defender, int damagegiven)
|
|
{
|
|
// Message?
|
|
// Effects?
|
|
defender.Stam -= (damagegiven * (100 - m_AosWeaponAttributes.HitFatigue)) / 100;
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
|
|
public virtual void DoManaDrain(Mobile attacker, Mobile defender, int damagegiven)
|
|
{
|
|
// Message?
|
|
defender.FixedParticles(0x3789, 10, 25, 5032, EffectLayer.Head);
|
|
defender.PlaySound(0x1F8);
|
|
defender.Mana -= (damagegiven * (100 - m_AosWeaponAttributes.HitManaDrain)) / 100;
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
#endregion
|
|
|
|
public virtual void DoLowerAttack(Mobile from, Mobile defender)
|
|
{
|
|
if (HitLower.ApplyAttack(defender))
|
|
{
|
|
defender.PlaySound(0x28E);
|
|
Effects.SendTargetEffect(defender, 0x37BE, 1, 4, 0xA, 3);
|
|
}
|
|
}
|
|
|
|
public virtual void DoLowerDefense(Mobile from, Mobile defender)
|
|
{
|
|
if (HitLower.ApplyDefense(defender))
|
|
{
|
|
defender.PlaySound(0x28E);
|
|
Effects.SendTargetEffect(defender, 0x37BE, 1, 4, 0x23, 3);
|
|
}
|
|
}
|
|
|
|
public virtual void DoAreaAttack(Mobile from, Mobile defender, int damageGiven, int sound, int hue, int phys, int fire, int cold, int pois, int nrgy)
|
|
{
|
|
Map map = from.Map;
|
|
|
|
if (map == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var list = SpellHelper.AcquireIndirectTargets(from, from, from.Map, 5);
|
|
|
|
var count = 0;
|
|
|
|
foreach(var m in list)
|
|
{
|
|
++count;
|
|
|
|
from.DoHarmful(m, true);
|
|
m.FixedEffect(0x3779, 1, 15, hue, 0);
|
|
AOS.Damage(m, from, (int)(damageGiven / 2), phys, fire, cold, pois, nrgy, Server.DamageType.SpellAOE);
|
|
}
|
|
|
|
if (count > 0)
|
|
{
|
|
Effects.PlaySound(from.Location, map, sound);
|
|
}
|
|
|
|
if (ProcessingMultipleHits)
|
|
BlockHitEffects = true;
|
|
}
|
|
#endregion
|
|
|
|
public virtual CheckSlayerResult CheckSlayers(Mobile attacker, Mobile defender, SlayerName slayer)
|
|
{
|
|
if (slayer == SlayerName.None)
|
|
return CheckSlayerResult.None;
|
|
|
|
BaseWeapon atkWeapon = attacker.Weapon as BaseWeapon;
|
|
SlayerEntry atkSlayer = SlayerGroup.GetEntryByName(slayer);
|
|
|
|
if (atkSlayer != null && atkSlayer.Slays(defender) && _SuperSlayers.Contains(atkSlayer.Name))
|
|
{
|
|
return CheckSlayerResult.SuperSlayer;
|
|
}
|
|
|
|
if (atkSlayer != null && atkSlayer.Slays(defender))
|
|
{
|
|
return CheckSlayerResult.Slayer;
|
|
}
|
|
|
|
return CheckSlayerResult.None;
|
|
}
|
|
|
|
public CheckSlayerResult CheckSlayerOpposition(Mobile attacker, Mobile defender)
|
|
{
|
|
ISlayer defISlayer = Spellbook.FindEquippedSpellbook(defender);
|
|
|
|
if (defISlayer == null)
|
|
{
|
|
defISlayer = defender.Weapon as ISlayer;
|
|
}
|
|
|
|
if (defISlayer != null)
|
|
{
|
|
SlayerEntry defSlayer = SlayerGroup.GetEntryByName(defISlayer.Slayer);
|
|
SlayerEntry defSlayer2 = SlayerGroup.GetEntryByName(defISlayer.Slayer2);
|
|
SlayerEntry defSetSlayer = SlayerGroup.GetEntryByName(SetHelper.GetSetSlayer(defender));
|
|
|
|
if (defISlayer is Item && defSlayer == null && defSlayer2 == null)
|
|
{
|
|
defSlayer = SlayerGroup.GetEntryByName(SlayerSocket.GetSlayer((Item)defISlayer));
|
|
}
|
|
|
|
if (defSlayer != null && defSlayer.Group.OppositionSuperSlays(attacker) ||
|
|
defSlayer2 != null && defSlayer2.Group.OppositionSuperSlays(attacker) ||
|
|
defSetSlayer != null && defSetSlayer.Group.OppositionSuperSlays(attacker))
|
|
{
|
|
return CheckSlayerResult.Opposition;
|
|
}
|
|
}
|
|
|
|
return CheckSlayerResult.None;
|
|
}
|
|
|
|
public CheckSlayerResult CheckTalismanSlayer(Mobile attacker, Mobile defender)
|
|
{
|
|
BaseTalisman talisman = attacker.Talisman as BaseTalisman;
|
|
|
|
if (talisman != null && TalismanSlayer.Slays(talisman.Slayer, defender))
|
|
{
|
|
return CheckSlayerResult.Slayer;
|
|
}
|
|
else if (Slayer3 != TalismanSlayerName.None && TalismanSlayer.Slays(Slayer3, defender))
|
|
{
|
|
return CheckSlayerResult.Slayer;
|
|
}
|
|
|
|
return CheckSlayerResult.None;
|
|
}
|
|
|
|
private List<SlayerName> _SuperSlayers = new List<SlayerName>()
|
|
{
|
|
SlayerName.Repond, SlayerName.Silver, SlayerName.Fey,
|
|
SlayerName.ElementalBan, SlayerName.Exorcism, SlayerName.ArachnidDoom,
|
|
SlayerName.ReptilianDeath, SlayerName.Dinosaur, SlayerName.Myrmidex,
|
|
SlayerName.Eodon
|
|
};
|
|
|
|
#region Blood
|
|
public void AddBlood(Mobile defender, int damage)
|
|
{
|
|
if (damage <= 5 || defender == null || defender.Map == null || !defender.HasBlood || !CanDrawBlood(defender))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var m = defender.Map;
|
|
var b = new Rectangle2D(defender.X - 2, defender.Y - 2, 5, 5);
|
|
|
|
var count = Core.AOS ? Utility.RandomMinMax(2, 3) : Utility.RandomMinMax(1, 2);
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
AddBlood(defender, m.GetRandomSpawnPoint(b), m);
|
|
}
|
|
}
|
|
|
|
protected virtual void AddBlood(Mobile defender, Point3D target, Map map)
|
|
{
|
|
var blood = CreateBlood(defender);
|
|
|
|
var id = blood.ItemID;
|
|
|
|
blood.ItemID = 1; // No Draw
|
|
|
|
blood.OnBeforeSpawn(target, map);
|
|
blood.MoveToWorld(target, map);
|
|
blood.OnAfterSpawn();
|
|
|
|
Effects.SendMovingEffect(defender, blood, id, 7, 10, true, false, blood.Hue, 0);
|
|
|
|
Timer.DelayCall(TimeSpan.FromMilliseconds(500), b => b.ItemID = id, blood);
|
|
}
|
|
|
|
protected virtual bool CanDrawBlood(Mobile defender)
|
|
{
|
|
return defender.HasBlood;
|
|
}
|
|
|
|
protected virtual Blood CreateBlood(Mobile defender)
|
|
{
|
|
return new Blood
|
|
{
|
|
Hue = defender.BloodHue
|
|
};
|
|
}
|
|
#endregion
|
|
|
|
#region Elemental Damage
|
|
public static int[] GetElementDamages(Mobile m)
|
|
{
|
|
var o = new[] {100, 0, 0, 0, 0, 0, 0};
|
|
|
|
var w = m.Weapon as BaseWeapon ?? Fists;
|
|
|
|
if (w != null)
|
|
{
|
|
w.GetDamageTypes(m, out o[0], out o[1], out o[2], out o[3], out o[4], out o[5], out o[6]);
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
public virtual void GetDamageTypes(
|
|
Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy, out int chaos, out int direct)
|
|
{
|
|
if (wielder is BaseCreature)
|
|
{
|
|
BaseCreature bc = (BaseCreature)wielder;
|
|
|
|
phys = bc.PhysicalDamage;
|
|
fire = bc.FireDamage;
|
|
cold = bc.ColdDamage;
|
|
pois = bc.PoisonDamage;
|
|
nrgy = bc.EnergyDamage;
|
|
chaos = bc.ChaosDamage;
|
|
direct = bc.DirectDamage;
|
|
}
|
|
else
|
|
{
|
|
fire = m_AosElementDamages.Fire;
|
|
cold = m_AosElementDamages.Cold;
|
|
pois = m_AosElementDamages.Poison;
|
|
nrgy = m_AosElementDamages.Energy;
|
|
chaos = m_AosElementDamages.Chaos;
|
|
direct = m_AosElementDamages.Direct;
|
|
|
|
phys = 100 - fire - cold - pois - nrgy - chaos - direct;
|
|
|
|
CraftResourceInfo resInfo = CraftResources.GetInfo(m_Resource);
|
|
|
|
if (resInfo != null)
|
|
{
|
|
CraftAttributeInfo attrInfo = resInfo.AttributeInfo;
|
|
|
|
if (attrInfo != null)
|
|
{
|
|
int left = phys;
|
|
|
|
left = ApplyCraftAttributeElementDamage(attrInfo.WeaponColdDamage, ref cold, left);
|
|
left = ApplyCraftAttributeElementDamage(attrInfo.WeaponEnergyDamage, ref nrgy, left);
|
|
left = ApplyCraftAttributeElementDamage(attrInfo.WeaponFireDamage, ref fire, left);
|
|
left = ApplyCraftAttributeElementDamage(attrInfo.WeaponPoisonDamage, ref pois, left);
|
|
left = ApplyCraftAttributeElementDamage(attrInfo.WeaponChaosDamage, ref chaos, left);
|
|
left = ApplyCraftAttributeElementDamage(attrInfo.WeaponDirectDamage, ref direct, left);
|
|
|
|
phys = left;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private int ApplyCraftAttributeElementDamage(int attrDamage, ref int element, int totalRemaining)
|
|
{
|
|
if (totalRemaining <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (attrDamage <= 0)
|
|
{
|
|
return totalRemaining;
|
|
}
|
|
|
|
int appliedDamage = attrDamage;
|
|
|
|
if ((appliedDamage + element) > 100)
|
|
{
|
|
appliedDamage = 100 - element;
|
|
}
|
|
|
|
if (appliedDamage > totalRemaining)
|
|
{
|
|
appliedDamage = totalRemaining;
|
|
}
|
|
|
|
element += appliedDamage;
|
|
|
|
return totalRemaining - appliedDamage;
|
|
}
|
|
#endregion
|
|
|
|
public virtual void OnMiss(Mobile attacker, IDamageable damageable)
|
|
{
|
|
Mobile defender = damageable as Mobile;
|
|
|
|
PlaySwingAnimation(attacker);
|
|
attacker.PlaySound(GetMissAttackSound(attacker, defender));
|
|
|
|
if(defender != null)
|
|
defender.PlaySound(GetMissDefendSound(attacker, defender));
|
|
|
|
WeaponAbility ability = WeaponAbility.GetCurrentAbility(attacker);
|
|
|
|
if (ability != null)
|
|
{
|
|
ability.OnMiss(attacker, defender);
|
|
}
|
|
|
|
SpecialMove move = SpecialMove.GetCurrentMove(attacker);
|
|
|
|
if (move != null)
|
|
{
|
|
move.OnMiss(attacker, defender);
|
|
}
|
|
|
|
if (defender is IHonorTarget && ((IHonorTarget)defender).ReceivedHonorContext != null)
|
|
{
|
|
((IHonorTarget)defender).ReceivedHonorContext.OnTargetMissed(attacker);
|
|
}
|
|
|
|
SkillMasterySpell.OnMiss(attacker, defender);
|
|
}
|
|
|
|
public virtual void GetBaseDamageRange(Mobile attacker, out int min, out int max)
|
|
{
|
|
if (attacker is BaseCreature)
|
|
{
|
|
BaseCreature c = (BaseCreature)attacker;
|
|
|
|
if (c.DamageMin >= 0)
|
|
{
|
|
min = c.DamageMin;
|
|
max = c.DamageMax;
|
|
return;
|
|
}
|
|
|
|
if (this is Fists && !attacker.Body.IsHuman)
|
|
{
|
|
min = attacker.Str / 28;
|
|
max = attacker.Str / 28;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (this is Fists && TransformationSpellHelper.UnderTransformation(attacker, typeof(HorrificBeastSpell)))
|
|
{
|
|
min = 5;
|
|
max = 15;
|
|
}
|
|
else
|
|
{
|
|
min = MinDamage;
|
|
max = MaxDamage;
|
|
}
|
|
}
|
|
|
|
public virtual double GetBaseDamage(Mobile attacker)
|
|
{
|
|
int min, max;
|
|
|
|
GetBaseDamageRange(attacker, out min, out max);
|
|
|
|
int damage = Utility.RandomMinMax(min, max);
|
|
|
|
if (Core.AOS)
|
|
{
|
|
return damage;
|
|
}
|
|
|
|
/* Apply damage level offset
|
|
* : Regular : 0
|
|
* : Ruin : 1
|
|
* : Might : 3
|
|
* : Force : 5
|
|
* : Power : 7
|
|
* : Vanq : 9
|
|
*/
|
|
if (m_DamageLevel != WeaponDamageLevel.Regular)
|
|
{
|
|
damage += (2 * (int)m_DamageLevel) - 1;
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
|
|
public virtual double GetBonus(double value, double scalar, double threshold, double offset)
|
|
{
|
|
double bonus = value * scalar;
|
|
|
|
if (value >= threshold)
|
|
{
|
|
bonus += offset;
|
|
}
|
|
|
|
return bonus / 100;
|
|
}
|
|
|
|
public virtual int GetHitChanceBonus()
|
|
{
|
|
if (!Core.AOS)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int bonus = 0;
|
|
|
|
switch (m_AccuracyLevel)
|
|
{
|
|
case WeaponAccuracyLevel.Accurate:
|
|
bonus += 02;
|
|
break;
|
|
case WeaponAccuracyLevel.Surpassingly:
|
|
bonus += 04;
|
|
break;
|
|
case WeaponAccuracyLevel.Eminently:
|
|
bonus += 06;
|
|
break;
|
|
case WeaponAccuracyLevel.Exceedingly:
|
|
bonus += 08;
|
|
break;
|
|
case WeaponAccuracyLevel.Supremely:
|
|
bonus += 10;
|
|
break;
|
|
}
|
|
|
|
return bonus;
|
|
}
|
|
|
|
public virtual int GetDamageBonus()
|
|
{
|
|
#region Stygian Abyss
|
|
if (m_DImodded)
|
|
return 0;
|
|
#endregion
|
|
|
|
int bonus = VirtualDamageBonus;
|
|
|
|
if (!Core.AOS)
|
|
{
|
|
switch (m_Quality)
|
|
{
|
|
case ItemQuality.Low:
|
|
bonus -= 20;
|
|
break;
|
|
case ItemQuality.Exceptional:
|
|
bonus += 20;
|
|
break;
|
|
}
|
|
|
|
switch (m_DamageLevel)
|
|
{
|
|
case WeaponDamageLevel.Ruin:
|
|
bonus += 15;
|
|
break;
|
|
case WeaponDamageLevel.Might:
|
|
bonus += 20;
|
|
break;
|
|
case WeaponDamageLevel.Force:
|
|
bonus += 25;
|
|
break;
|
|
case WeaponDamageLevel.Power:
|
|
bonus += 30;
|
|
break;
|
|
case WeaponDamageLevel.Vanq:
|
|
bonus += 35;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bonus;
|
|
}
|
|
|
|
public virtual void GetStatusDamage(Mobile from, out int min, out int max)
|
|
{
|
|
int baseMin, baseMax;
|
|
|
|
GetBaseDamageRange(from, out baseMin, out baseMax);
|
|
|
|
if (Core.AOS)
|
|
{
|
|
min = Math.Max((int)ScaleDamageAOS(from, baseMin, false), 1);
|
|
max = Math.Max((int)ScaleDamageAOS(from, baseMax, false), 1);
|
|
}
|
|
else
|
|
{
|
|
min = Math.Max((int)ScaleDamageOld(from, baseMin, false), 1);
|
|
max = Math.Max((int)ScaleDamageOld(from, baseMax, false), 1);
|
|
}
|
|
}
|
|
|
|
public virtual double ScaleDamageAOS(Mobile attacker, double damage, bool checkSkills)
|
|
{
|
|
if (checkSkills)
|
|
{
|
|
attacker.CheckSkill(SkillName.Tactics, 0.0, attacker.Skills[SkillName.Tactics].Cap);
|
|
// Passively check tactics for gain
|
|
attacker.CheckSkill(SkillName.Anatomy, 0.0, attacker.Skills[SkillName.Anatomy].Cap);
|
|
// Passively check Anatomy for gain
|
|
|
|
if (Type == WeaponType.Axe)
|
|
{
|
|
attacker.CheckSkill(SkillName.Lumberjacking, 0.0, 100.0); // Passively check Lumberjacking for gain
|
|
}
|
|
}
|
|
|
|
#region Physical bonuses
|
|
/*
|
|
* These are the bonuses given by the physical characteristics of the mobile.
|
|
* No caps apply.
|
|
*/
|
|
double strengthBonus = GetBonus(attacker.Str, 0.300, 100.0, 5.00);
|
|
double anatomyBonus = GetBonus(attacker.Skills[SkillName.Anatomy].Value, 0.500, 100.0, 5.00);
|
|
double tacticsBonus = GetBonus(attacker.Skills[SkillName.Tactics].Value, 0.625, 100.0, 6.25);
|
|
double lumberBonus = GetBonus(attacker.Skills[SkillName.Lumberjacking].Value, 0.200, 100.0, 10.00);
|
|
|
|
if (Type != WeaponType.Axe)
|
|
{
|
|
lumberBonus = 0.0;
|
|
}
|
|
#endregion
|
|
|
|
#region Modifiers
|
|
/*
|
|
* The following are damage modifiers whose effect shows on the status bar.
|
|
* Capped at 100% total.
|
|
*/
|
|
int damageBonus = AosAttributes.GetValue(attacker, AosAttribute.WeaponDamage);
|
|
|
|
if (damageBonus > 100)
|
|
{
|
|
damageBonus = 100;
|
|
}
|
|
#endregion
|
|
|
|
double totalBonus = strengthBonus + anatomyBonus + tacticsBonus + lumberBonus +
|
|
((GetDamageBonus() + damageBonus) / 100.0);
|
|
|
|
return damage + (int)(damage * totalBonus);
|
|
}
|
|
|
|
public virtual int VirtualDamageBonus { get { return 0; } }
|
|
|
|
public virtual int ComputeDamageAOS(Mobile attacker, Mobile defender)
|
|
{
|
|
return (int)ScaleDamageAOS(attacker, GetBaseDamage(attacker), true);
|
|
}
|
|
|
|
public virtual double ScaleDamageOld(Mobile attacker, double damage, bool checkSkills)
|
|
{
|
|
if (checkSkills)
|
|
{
|
|
attacker.CheckSkill(SkillName.Tactics, 0.0, attacker.Skills[SkillName.Tactics].Cap);
|
|
// Passively check tactics for gain
|
|
attacker.CheckSkill(SkillName.Anatomy, 0.0, attacker.Skills[SkillName.Anatomy].Cap);
|
|
// Passively check Anatomy for gain
|
|
|
|
if (Type == WeaponType.Axe)
|
|
{
|
|
attacker.CheckSkill(SkillName.Lumberjacking, 0.0, 100.0); // Passively check Lumberjacking for gain
|
|
}
|
|
}
|
|
|
|
/* Compute tactics modifier
|
|
* : 0.0 = 50% loss
|
|
* : 50.0 = unchanged
|
|
* : 100.0 = 50% bonus
|
|
*/
|
|
damage += (damage * ((attacker.Skills[SkillName.Tactics].Value - 50.0) / 100.0));
|
|
|
|
/* Compute strength modifier
|
|
* : 1% bonus for every 5 strength
|
|
*/
|
|
double modifiers = (attacker.Str / 5.0) / 100.0;
|
|
|
|
/* Compute anatomy modifier
|
|
* : 1% bonus for every 5 points of anatomy
|
|
* : +10% bonus at Grandmaster or higher
|
|
*/
|
|
double anatomyValue = attacker.Skills[SkillName.Anatomy].Value;
|
|
modifiers += ((anatomyValue / 5.0) / 100.0);
|
|
|
|
if (anatomyValue >= 100.0)
|
|
{
|
|
modifiers += 0.1;
|
|
}
|
|
|
|
/* Compute lumberjacking bonus
|
|
* : 1% bonus for every 5 points of lumberjacking
|
|
* : +10% bonus at Grandmaster or higher
|
|
*/
|
|
|
|
if (Type == WeaponType.Axe)
|
|
{
|
|
double lumberValue = attacker.Skills[SkillName.Lumberjacking].Value;
|
|
lumberValue = (lumberValue/5.0)/100.0;
|
|
if (lumberValue > 0.2)
|
|
lumberValue = 0.2;
|
|
|
|
modifiers += lumberValue;
|
|
|
|
if (lumberValue >= 100.0)
|
|
{
|
|
modifiers += 0.1;
|
|
}
|
|
}
|
|
|
|
// New quality bonus:
|
|
if (m_Quality != ItemQuality.Normal)
|
|
{
|
|
modifiers += (((int)m_Quality - 1) * 0.2);
|
|
}
|
|
|
|
// Virtual damage bonus:
|
|
if (VirtualDamageBonus != 0)
|
|
{
|
|
modifiers += (VirtualDamageBonus / 100.0);
|
|
}
|
|
|
|
// Apply bonuses
|
|
damage += (damage * modifiers);
|
|
|
|
return ScaleDamageByDurability((int)damage);
|
|
}
|
|
|
|
public virtual int ScaleDamageByDurability(int damage)
|
|
{
|
|
int scale = 100;
|
|
|
|
if (m_MaxHits > 0 && m_Hits < m_MaxHits)
|
|
{
|
|
scale = 50 + ((50 * m_Hits) / m_MaxHits);
|
|
}
|
|
|
|
return AOS.Scale(damage, scale);
|
|
}
|
|
|
|
public virtual int ComputeDamage(Mobile attacker, Mobile defender)
|
|
{
|
|
if (Core.AOS)
|
|
{
|
|
return ComputeDamageAOS(attacker, defender);
|
|
}
|
|
|
|
int damage = (int)ScaleDamageOld(attacker, GetBaseDamage(attacker), true);
|
|
|
|
// pre-AOS, halve damage if the defender is a player or the attacker is not a player
|
|
if (defender is PlayerMobile || !(attacker is PlayerMobile))
|
|
{
|
|
damage = (int)(damage / 2.0);
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
|
|
public virtual void PlayHurtAnimation(Mobile from)
|
|
{
|
|
if (from.Mounted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Core.SA)
|
|
{
|
|
from.Animate(AnimationType.Impact, 0);
|
|
}
|
|
else
|
|
{
|
|
int action;
|
|
int frames;
|
|
|
|
switch (from.Body.Type)
|
|
{
|
|
case BodyType.Sea:
|
|
case BodyType.Animal:
|
|
{
|
|
action = 7;
|
|
frames = 5;
|
|
break;
|
|
}
|
|
case BodyType.Monster:
|
|
{
|
|
action = 10;
|
|
frames = 4;
|
|
break;
|
|
}
|
|
case BodyType.Human:
|
|
{
|
|
action = 20;
|
|
frames = 5;
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
from.Animate(action, frames, 1, true, false, 0);
|
|
}
|
|
}
|
|
|
|
public virtual void PlaySwingAnimation(Mobile from)
|
|
{
|
|
int action;
|
|
|
|
if (Core.SA)
|
|
{
|
|
action = GetNewAnimationAction(from);
|
|
|
|
from.Animate(AnimationType.Attack, action);
|
|
}
|
|
else
|
|
{
|
|
switch (from.Body.Type)
|
|
{
|
|
case BodyType.Sea:
|
|
case BodyType.Animal:
|
|
{
|
|
action = Utility.Random(5, 2);
|
|
break;
|
|
}
|
|
case BodyType.Monster:
|
|
{
|
|
switch (Animation)
|
|
{
|
|
default:
|
|
case WeaponAnimation.Wrestle:
|
|
case WeaponAnimation.Bash1H:
|
|
case WeaponAnimation.Pierce1H:
|
|
case WeaponAnimation.Slash1H:
|
|
case WeaponAnimation.Bash2H:
|
|
case WeaponAnimation.Pierce2H:
|
|
case WeaponAnimation.Slash2H:
|
|
action = Utility.Random(4, 3);
|
|
break;
|
|
case WeaponAnimation.ShootBow:
|
|
return; // 7
|
|
case WeaponAnimation.ShootXBow:
|
|
return; // 8
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BodyType.Human:
|
|
{
|
|
if (!from.Mounted)
|
|
{
|
|
action = (int)Animation;
|
|
}
|
|
else
|
|
{
|
|
switch (Animation)
|
|
{
|
|
default:
|
|
case WeaponAnimation.Wrestle:
|
|
case WeaponAnimation.Bash1H:
|
|
case WeaponAnimation.Pierce1H:
|
|
case WeaponAnimation.Slash1H:
|
|
action = 26;
|
|
break;
|
|
case WeaponAnimation.Bash2H:
|
|
case WeaponAnimation.Pierce2H:
|
|
case WeaponAnimation.Slash2H:
|
|
action = 29;
|
|
break;
|
|
case WeaponAnimation.ShootBow:
|
|
action = 27;
|
|
break;
|
|
case WeaponAnimation.ShootXBow:
|
|
action = 28;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
from.Animate(action, 7, 1, true, false, 0);
|
|
}
|
|
}
|
|
|
|
public int GetNewAnimationAction(Mobile from)
|
|
{
|
|
switch (Animation)
|
|
{
|
|
default:
|
|
case WeaponAnimation.Wrestle: return 0;
|
|
case WeaponAnimation.Bash1H: return 3;
|
|
case WeaponAnimation.Pierce1H: return 5;
|
|
case WeaponAnimation.Slash1H: return 4;
|
|
case WeaponAnimation.Bash2H: return 6;
|
|
case WeaponAnimation.Pierce2H: return 8;
|
|
case WeaponAnimation.Slash2H: return 7;
|
|
case WeaponAnimation.ShootBow: return 1;
|
|
case WeaponAnimation.ShootXBow: return 2;
|
|
case WeaponAnimation.Throwing: return 9;
|
|
}
|
|
}
|
|
|
|
#region Serialization/Deserialization
|
|
private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf)
|
|
{
|
|
if (setIf)
|
|
{
|
|
flags |= toSet;
|
|
}
|
|
}
|
|
|
|
private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet)
|
|
{
|
|
return ((flags & toGet) != 0);
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.Write(19); // version
|
|
|
|
// Version 19 - Removes m_SearingWeapon as its handled as a socket now
|
|
// Version 18 - removed VvV Item (handled in VvV System) and BlockRepair (Handled as negative attribute)
|
|
|
|
writer.Write(m_UsesRemaining);
|
|
writer.Write(m_ShowUsesRemaining);
|
|
|
|
writer.Write(_Owner);
|
|
writer.Write(_OwnerName);
|
|
|
|
// Version 15 converts old leech to new leech
|
|
|
|
//Version 14
|
|
writer.Write(m_IsImbued);
|
|
|
|
//version 13, converted SaveFlags to long, added negative attributes
|
|
|
|
//version 12
|
|
#region Runic Reforging
|
|
writer.Write((int)m_ReforgedPrefix);
|
|
writer.Write((int)m_ReforgedSuffix);
|
|
writer.Write((int)m_ItemPower);
|
|
#endregion
|
|
|
|
writer.Write(m_DImodded);
|
|
|
|
// Version 11
|
|
writer.Write(m_TimesImbued);
|
|
// Version 10
|
|
writer.Write(m_BlessedBy); // Bless Deed
|
|
|
|
#region Veteran Rewards
|
|
writer.Write(m_EngravedText);
|
|
#endregion
|
|
|
|
#region Mondain's Legacy
|
|
writer.Write((int)m_Slayer3);
|
|
#endregion
|
|
|
|
#region Mondain's Legacy Sets
|
|
SetFlag sflags = SetFlag.None;
|
|
|
|
SetSaveFlag(ref sflags, SetFlag.Attributes, !m_SetAttributes.IsEmpty);
|
|
SetSaveFlag(ref sflags, SetFlag.SkillBonuses, !m_SetSkillBonuses.IsEmpty);
|
|
SetSaveFlag(ref sflags, SetFlag.Hue, m_SetHue != 0);
|
|
SetSaveFlag(ref sflags, SetFlag.LastEquipped, m_LastEquipped);
|
|
SetSaveFlag(ref sflags, SetFlag.SetEquipped, m_SetEquipped);
|
|
SetSaveFlag(ref sflags, SetFlag.SetSelfRepair, m_SetSelfRepair != 0);
|
|
SetSaveFlag(ref sflags, SetFlag.PhysicalBonus, m_SetPhysicalBonus != 0);
|
|
SetSaveFlag(ref sflags, SetFlag.FireBonus, m_SetFireBonus != 0);
|
|
SetSaveFlag(ref sflags, SetFlag.ColdBonus, m_SetColdBonus != 0);
|
|
SetSaveFlag(ref sflags, SetFlag.PoisonBonus, m_SetPoisonBonus != 0);
|
|
SetSaveFlag(ref sflags, SetFlag.EnergyBonus, m_SetEnergyBonus != 0);
|
|
|
|
writer.WriteEncodedInt((int)sflags);
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.PhysicalBonus))
|
|
{
|
|
writer.WriteEncodedInt((int)m_SetPhysicalBonus);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.FireBonus))
|
|
{
|
|
writer.WriteEncodedInt((int)m_SetFireBonus);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.ColdBonus))
|
|
{
|
|
writer.WriteEncodedInt((int)m_SetColdBonus);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.PoisonBonus))
|
|
{
|
|
writer.WriteEncodedInt((int)m_SetPoisonBonus);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.EnergyBonus))
|
|
{
|
|
writer.WriteEncodedInt((int)m_SetEnergyBonus);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.Attributes))
|
|
{
|
|
m_SetAttributes.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.SkillBonuses))
|
|
{
|
|
m_SetSkillBonuses.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.Hue))
|
|
{
|
|
writer.Write(m_SetHue);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.LastEquipped))
|
|
{
|
|
writer.Write(m_LastEquipped);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.SetEquipped))
|
|
{
|
|
writer.Write(m_SetEquipped);
|
|
}
|
|
|
|
if (GetSaveFlag(sflags, SetFlag.SetSelfRepair))
|
|
{
|
|
writer.WriteEncodedInt(m_SetSelfRepair);
|
|
}
|
|
#endregion
|
|
|
|
// Version 9
|
|
SaveFlag flags = SaveFlag.None;
|
|
|
|
SetSaveFlag(ref flags, SaveFlag.DamageLevel, m_DamageLevel != WeaponDamageLevel.Regular);
|
|
SetSaveFlag(ref flags, SaveFlag.AccuracyLevel, m_AccuracyLevel != WeaponAccuracyLevel.Regular);
|
|
SetSaveFlag(ref flags, SaveFlag.DurabilityLevel, m_DurabilityLevel != WeaponDurabilityLevel.Regular);
|
|
SetSaveFlag(ref flags, SaveFlag.Quality, m_Quality != ItemQuality.Normal);
|
|
SetSaveFlag(ref flags, SaveFlag.Hits, m_Hits != 0);
|
|
SetSaveFlag(ref flags, SaveFlag.MaxHits, m_MaxHits != 0);
|
|
SetSaveFlag(ref flags, SaveFlag.Slayer, m_Slayer != SlayerName.None);
|
|
SetSaveFlag(ref flags, SaveFlag.Poison, m_Poison != null);
|
|
SetSaveFlag(ref flags, SaveFlag.PoisonCharges, m_PoisonCharges != 0);
|
|
SetSaveFlag(ref flags, SaveFlag.Crafter, m_Crafter != null);
|
|
SetSaveFlag(ref flags, SaveFlag.Identified, m_Identified);
|
|
SetSaveFlag(ref flags, SaveFlag.StrReq, m_StrReq != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.DexReq, m_DexReq != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.IntReq, m_IntReq != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.MinDamage, m_MinDamage != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.MaxDamage, m_MaxDamage != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.HitSound, m_HitSound != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.MissSound, m_MissSound != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.Speed, m_Speed != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.MaxRange, m_MaxRange != -1);
|
|
SetSaveFlag(ref flags, SaveFlag.Skill, m_Skill != (SkillName)(-1));
|
|
SetSaveFlag(ref flags, SaveFlag.Type, m_Type != (WeaponType)(-1));
|
|
SetSaveFlag(ref flags, SaveFlag.Animation, m_Animation != (WeaponAnimation)(-1));
|
|
SetSaveFlag(ref flags, SaveFlag.Resource, m_Resource != CraftResource.Iron);
|
|
SetSaveFlag(ref flags, SaveFlag.xAttributes, !m_AosAttributes.IsEmpty);
|
|
SetSaveFlag(ref flags, SaveFlag.xWeaponAttributes, !m_AosWeaponAttributes.IsEmpty);
|
|
SetSaveFlag(ref flags, SaveFlag.PlayerConstructed, m_PlayerConstructed);
|
|
SetSaveFlag(ref flags, SaveFlag.SkillBonuses, !m_AosSkillBonuses.IsEmpty);
|
|
SetSaveFlag(ref flags, SaveFlag.Slayer2, m_Slayer2 != SlayerName.None);
|
|
SetSaveFlag(ref flags, SaveFlag.ElementalDamages, !m_AosElementDamages.IsEmpty);
|
|
SetSaveFlag(ref flags, SaveFlag.EngravedText, !String.IsNullOrEmpty(m_EngravedText));
|
|
SetSaveFlag(ref flags, SaveFlag.xAbsorptionAttributes, !m_SAAbsorptionAttributes.IsEmpty);
|
|
SetSaveFlag(ref flags, SaveFlag.xNegativeAttributes, !m_NegativeAttributes.IsEmpty);
|
|
SetSaveFlag(ref flags, SaveFlag.Altered, m_Altered);
|
|
SetSaveFlag(ref flags, SaveFlag.xExtendedWeaponAttributes, !m_ExtendedWeaponAttributes.IsEmpty);
|
|
|
|
writer.Write((long)flags);
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.DamageLevel))
|
|
{
|
|
writer.Write((int)m_DamageLevel);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.AccuracyLevel))
|
|
{
|
|
writer.Write((int)m_AccuracyLevel);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.DurabilityLevel))
|
|
{
|
|
writer.Write((int)m_DurabilityLevel);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Quality))
|
|
{
|
|
writer.Write((int)m_Quality);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Hits))
|
|
{
|
|
writer.Write(m_Hits);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MaxHits))
|
|
{
|
|
writer.Write(m_MaxHits);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Slayer))
|
|
{
|
|
writer.Write((int)m_Slayer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Poison))
|
|
{
|
|
Poison.Serialize(m_Poison, writer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.PoisonCharges))
|
|
{
|
|
writer.Write(m_PoisonCharges);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Crafter))
|
|
{
|
|
writer.Write(m_Crafter);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.StrReq))
|
|
{
|
|
writer.Write(m_StrReq);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.DexReq))
|
|
{
|
|
writer.Write(m_DexReq);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.IntReq))
|
|
{
|
|
writer.Write(m_IntReq);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MinDamage))
|
|
{
|
|
writer.Write(m_MinDamage);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MaxDamage))
|
|
{
|
|
writer.Write(m_MaxDamage);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.HitSound))
|
|
{
|
|
writer.Write(m_HitSound);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MissSound))
|
|
{
|
|
writer.Write(m_MissSound);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Speed))
|
|
{
|
|
writer.Write(m_Speed);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MaxRange))
|
|
{
|
|
writer.Write(m_MaxRange);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Skill))
|
|
{
|
|
writer.Write((int)m_Skill);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Type))
|
|
{
|
|
writer.Write((int)m_Type);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Animation))
|
|
{
|
|
writer.Write((int)m_Animation);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Resource))
|
|
{
|
|
writer.Write((int)m_Resource);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xAttributes))
|
|
{
|
|
m_AosAttributes.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xWeaponAttributes))
|
|
{
|
|
m_AosWeaponAttributes.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.SkillBonuses))
|
|
{
|
|
m_AosSkillBonuses.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Slayer2))
|
|
{
|
|
writer.Write((int)m_Slayer2);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.ElementalDamages))
|
|
{
|
|
m_AosElementDamages.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.EngravedText))
|
|
{
|
|
writer.Write(m_EngravedText);
|
|
}
|
|
|
|
#region SA
|
|
if (GetSaveFlag(flags, SaveFlag.xAbsorptionAttributes))
|
|
{
|
|
m_SAAbsorptionAttributes.Serialize(writer);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xNegativeAttributes))
|
|
{
|
|
m_NegativeAttributes.Serialize(writer);
|
|
}
|
|
#endregion
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xExtendedWeaponAttributes))
|
|
{
|
|
m_ExtendedWeaponAttributes.Serialize(writer);
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
private enum SaveFlag : long
|
|
{
|
|
None = 0x00000000,
|
|
DamageLevel = 0x00000001,
|
|
AccuracyLevel = 0x00000002,
|
|
DurabilityLevel = 0x00000004,
|
|
Quality = 0x00000008,
|
|
Hits = 0x00000010,
|
|
MaxHits = 0x00000020,
|
|
Slayer = 0x00000040,
|
|
Poison = 0x00000080,
|
|
PoisonCharges = 0x00000100,
|
|
Crafter = 0x00000200,
|
|
Identified = 0x00000400,
|
|
StrReq = 0x00000800,
|
|
DexReq = 0x00001000,
|
|
IntReq = 0x00002000,
|
|
MinDamage = 0x00004000,
|
|
MaxDamage = 0x00008000,
|
|
HitSound = 0x00010000,
|
|
MissSound = 0x00020000,
|
|
Speed = 0x00040000,
|
|
MaxRange = 0x00080000,
|
|
Skill = 0x00100000,
|
|
Type = 0x00200000,
|
|
Animation = 0x00400000,
|
|
Resource = 0x00800000,
|
|
xAttributes = 0x01000000,
|
|
xWeaponAttributes = 0x02000000,
|
|
PlayerConstructed = 0x04000000,
|
|
SkillBonuses = 0x08000000,
|
|
Slayer2 = 0x10000000,
|
|
ElementalDamages = 0x20000000,
|
|
EngravedText = 0x40000000,
|
|
xAbsorptionAttributes = 0x80000000,
|
|
xNegativeAttributes = 0x100000000,
|
|
Altered = 0x200000000,
|
|
xExtendedWeaponAttributes = 0x400000000
|
|
}
|
|
|
|
#region Mondain's Legacy Sets
|
|
private static void SetSaveFlag(ref SetFlag flags, SetFlag toSet, bool setIf)
|
|
{
|
|
if (setIf)
|
|
{
|
|
flags |= toSet;
|
|
}
|
|
}
|
|
|
|
private static bool GetSaveFlag(SetFlag flags, SetFlag toGet)
|
|
{
|
|
return ((flags & toGet) != 0);
|
|
}
|
|
|
|
[Flags]
|
|
private enum SetFlag
|
|
{
|
|
None = 0x00000000,
|
|
Attributes = 0x00000001,
|
|
WeaponAttributes = 0x00000002,
|
|
SkillBonuses = 0x00000004,
|
|
Hue = 0x00000008,
|
|
LastEquipped = 0x00000010,
|
|
SetEquipped = 0x00000020,
|
|
SetSelfRepair = 0x00000040,
|
|
PhysicalBonus = 0x00000080,
|
|
FireBonus = 0x00000100,
|
|
ColdBonus = 0x00000200,
|
|
PoisonBonus = 0x00000400,
|
|
EnergyBonus = 0x00000800,
|
|
}
|
|
#endregion
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadInt();
|
|
|
|
switch (version)
|
|
{
|
|
case 19: // Removed SearingWeapon
|
|
case 18:
|
|
case 17:
|
|
{
|
|
m_UsesRemaining = reader.ReadInt();
|
|
m_ShowUsesRemaining = reader.ReadBool();
|
|
goto case 16;
|
|
}
|
|
case 16:
|
|
{
|
|
if(version == 17)
|
|
reader.ReadBool();
|
|
|
|
_Owner = reader.ReadMobile();
|
|
_OwnerName = reader.ReadString();
|
|
goto case 15;
|
|
}
|
|
case 15:
|
|
case 14:
|
|
{
|
|
m_IsImbued = reader.ReadBool();
|
|
goto case 13;
|
|
}
|
|
case 13:
|
|
case 12:
|
|
{
|
|
#region Runic Reforging
|
|
m_ReforgedPrefix = (ReforgedPrefix)reader.ReadInt();
|
|
m_ReforgedSuffix = (ReforgedSuffix)reader.ReadInt();
|
|
m_ItemPower = (ItemPower)reader.ReadInt();
|
|
|
|
if (version < 18 && reader.ReadBool())
|
|
{
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1), () =>
|
|
{
|
|
m_NegativeAttributes.NoRepair = 1;
|
|
});
|
|
}
|
|
#endregion
|
|
|
|
#region Stygian Abyss
|
|
m_DImodded = reader.ReadBool();
|
|
|
|
if (version == 18)
|
|
{
|
|
if (reader.ReadBool())
|
|
{
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1), () =>
|
|
{
|
|
AttachSocket(new SearingWeapon(this));
|
|
});
|
|
}
|
|
}
|
|
goto case 11;
|
|
}
|
|
case 11:
|
|
{
|
|
m_TimesImbued = reader.ReadInt();
|
|
|
|
#endregion
|
|
|
|
goto case 10;
|
|
}
|
|
case 10:
|
|
{
|
|
m_BlessedBy = reader.ReadMobile();
|
|
m_EngravedText = reader.ReadString();
|
|
m_Slayer3 = (TalismanSlayerName)reader.ReadInt();
|
|
|
|
SetFlag flags = (SetFlag)reader.ReadEncodedInt();
|
|
if (GetSaveFlag(flags, SetFlag.PhysicalBonus))
|
|
{
|
|
m_SetPhysicalBonus = reader.ReadEncodedInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.FireBonus))
|
|
{
|
|
m_SetFireBonus = reader.ReadEncodedInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.ColdBonus))
|
|
{
|
|
m_SetColdBonus = reader.ReadEncodedInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.PoisonBonus))
|
|
{
|
|
m_SetPoisonBonus = reader.ReadEncodedInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.EnergyBonus))
|
|
{
|
|
m_SetEnergyBonus = reader.ReadEncodedInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.Attributes))
|
|
{
|
|
m_SetAttributes = new AosAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_SetAttributes = new AosAttributes(this);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.WeaponAttributes))
|
|
{
|
|
m_SetSelfRepair = (new AosWeaponAttributes(this, reader)).SelfRepair;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.SkillBonuses))
|
|
{
|
|
m_SetSkillBonuses = new AosSkillBonuses(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_SetSkillBonuses = new AosSkillBonuses(this);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.Hue))
|
|
{
|
|
m_SetHue = reader.ReadInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.LastEquipped))
|
|
{
|
|
m_LastEquipped = reader.ReadBool();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.SetEquipped))
|
|
{
|
|
m_SetEquipped = reader.ReadBool();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SetFlag.SetSelfRepair))
|
|
{
|
|
m_SetSelfRepair = reader.ReadEncodedInt();
|
|
}
|
|
|
|
goto case 5;
|
|
}
|
|
case 9:
|
|
case 8:
|
|
case 7:
|
|
case 6:
|
|
case 5:
|
|
{
|
|
SaveFlag flags;
|
|
|
|
if(version < 13)
|
|
flags = (SaveFlag)reader.ReadInt();
|
|
else
|
|
flags = (SaveFlag)reader.ReadLong();
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.DamageLevel))
|
|
{
|
|
m_DamageLevel = (WeaponDamageLevel)reader.ReadInt();
|
|
|
|
if (m_DamageLevel > WeaponDamageLevel.Vanq)
|
|
{
|
|
m_DamageLevel = WeaponDamageLevel.Ruin;
|
|
}
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.AccuracyLevel))
|
|
{
|
|
m_AccuracyLevel = (WeaponAccuracyLevel)reader.ReadInt();
|
|
|
|
if (m_AccuracyLevel > WeaponAccuracyLevel.Supremely)
|
|
{
|
|
m_AccuracyLevel = WeaponAccuracyLevel.Accurate;
|
|
}
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.DurabilityLevel))
|
|
{
|
|
m_DurabilityLevel = (WeaponDurabilityLevel)reader.ReadInt();
|
|
|
|
if (m_DurabilityLevel > WeaponDurabilityLevel.Indestructible)
|
|
{
|
|
m_DurabilityLevel = WeaponDurabilityLevel.Durable;
|
|
}
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Quality))
|
|
{
|
|
m_Quality = (ItemQuality)reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_Quality = ItemQuality.Normal;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Hits))
|
|
{
|
|
m_Hits = reader.ReadInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MaxHits))
|
|
{
|
|
m_MaxHits = reader.ReadInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Slayer))
|
|
{
|
|
m_Slayer = (SlayerName)reader.ReadInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Poison))
|
|
{
|
|
m_Poison = Poison.Deserialize(reader);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.PoisonCharges))
|
|
{
|
|
m_PoisonCharges = reader.ReadInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Crafter))
|
|
{
|
|
m_Crafter = reader.ReadMobile();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Identified))
|
|
{
|
|
m_Identified = (version >= 6 || reader.ReadBool());
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.StrReq))
|
|
{
|
|
m_StrReq = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_StrReq = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.DexReq))
|
|
{
|
|
m_DexReq = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_DexReq = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.IntReq))
|
|
{
|
|
m_IntReq = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_IntReq = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MinDamage))
|
|
{
|
|
m_MinDamage = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_MinDamage = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MaxDamage))
|
|
{
|
|
m_MaxDamage = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_MaxDamage = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.HitSound))
|
|
{
|
|
m_HitSound = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_HitSound = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MissSound))
|
|
{
|
|
m_MissSound = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_MissSound = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Speed))
|
|
{
|
|
if (version < 9)
|
|
{
|
|
m_Speed = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_Speed = reader.ReadFloat();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Speed = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.MaxRange))
|
|
{
|
|
m_MaxRange = reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_MaxRange = -1;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Skill))
|
|
{
|
|
m_Skill = (SkillName)reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_Skill = (SkillName)(-1);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Type))
|
|
{
|
|
m_Type = (WeaponType)reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_Type = (WeaponType)(-1);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Animation))
|
|
{
|
|
m_Animation = (WeaponAnimation)reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_Animation = (WeaponAnimation)(-1);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Resource))
|
|
{
|
|
m_Resource = (CraftResource)reader.ReadInt();
|
|
}
|
|
else
|
|
{
|
|
m_Resource = CraftResource.Iron;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xAttributes))
|
|
{
|
|
m_AosAttributes = new AosAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_AosAttributes = new AosAttributes(this);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xWeaponAttributes))
|
|
{
|
|
m_AosWeaponAttributes = new AosWeaponAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_AosWeaponAttributes = new AosWeaponAttributes(this);
|
|
}
|
|
|
|
if (UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular && Parent is Mobile)
|
|
{
|
|
m_SkillMod = new DefaultSkillMod(AccuracySkill, true, (int)m_AccuracyLevel * 5);
|
|
((Mobile)Parent).AddSkillMod(m_SkillMod);
|
|
}
|
|
|
|
if (version < 7 && m_AosWeaponAttributes.MageWeapon != 0)
|
|
{
|
|
m_AosWeaponAttributes.MageWeapon = 30 - m_AosWeaponAttributes.MageWeapon;
|
|
}
|
|
|
|
if (Core.AOS && m_AosWeaponAttributes.MageWeapon != 0 && m_AosWeaponAttributes.MageWeapon != 30 &&
|
|
Parent is Mobile)
|
|
{
|
|
m_MageMod = new DefaultSkillMod(SkillName.Magery, true, -30 + m_AosWeaponAttributes.MageWeapon);
|
|
((Mobile)Parent).AddSkillMod(m_MageMod);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.PlayerConstructed))
|
|
{
|
|
m_PlayerConstructed = true;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.SkillBonuses))
|
|
{
|
|
m_AosSkillBonuses = new AosSkillBonuses(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_AosSkillBonuses = new AosSkillBonuses(this);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Slayer2))
|
|
{
|
|
m_Slayer2 = (SlayerName)reader.ReadInt();
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.ElementalDamages))
|
|
{
|
|
m_AosElementDamages = new AosElementAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_AosElementDamages = new AosElementAttributes(this);
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.EngravedText))
|
|
{
|
|
m_EngravedText = reader.ReadString();
|
|
}
|
|
|
|
#region Stygian Abyss
|
|
if (version > 9 && GetSaveFlag(flags, SaveFlag.xAbsorptionAttributes))
|
|
{
|
|
m_SAAbsorptionAttributes = new SAAbsorptionAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_SAAbsorptionAttributes = new SAAbsorptionAttributes(this);
|
|
}
|
|
|
|
if (version >= 13 && GetSaveFlag(flags, SaveFlag.xNegativeAttributes))
|
|
{
|
|
m_NegativeAttributes = new NegativeAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_NegativeAttributes = new NegativeAttributes(this);
|
|
}
|
|
#endregion
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.Altered))
|
|
{
|
|
m_Altered = true;
|
|
}
|
|
|
|
if (GetSaveFlag(flags, SaveFlag.xExtendedWeaponAttributes))
|
|
{
|
|
m_ExtendedWeaponAttributes = new ExtendedWeaponAttributes(this, reader);
|
|
}
|
|
else
|
|
{
|
|
m_ExtendedWeaponAttributes = new ExtendedWeaponAttributes(this);
|
|
}
|
|
|
|
if (Core.TOL && m_ExtendedWeaponAttributes.MysticWeapon != 0 && m_ExtendedWeaponAttributes.MysticWeapon != 30 && Parent is Mobile)
|
|
{
|
|
m_MysticMod = new DefaultSkillMod(SkillName.Mysticism, true, -30 + m_ExtendedWeaponAttributes.MysticWeapon);
|
|
((Mobile)Parent).AddSkillMod(m_MysticMod);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
m_Slayer = (SlayerName)reader.ReadInt();
|
|
|
|
goto case 3;
|
|
}
|
|
case 3:
|
|
{
|
|
m_StrReq = reader.ReadInt();
|
|
m_DexReq = reader.ReadInt();
|
|
m_IntReq = reader.ReadInt();
|
|
|
|
goto case 2;
|
|
}
|
|
case 2:
|
|
{
|
|
m_Identified = reader.ReadBool();
|
|
|
|
goto case 1;
|
|
}
|
|
case 1:
|
|
{
|
|
m_MaxRange = reader.ReadInt();
|
|
|
|
goto case 0;
|
|
}
|
|
case 0:
|
|
{
|
|
if (version == 0)
|
|
{
|
|
m_MaxRange = 1; // default
|
|
}
|
|
|
|
if (version < 5)
|
|
{
|
|
m_Resource = CraftResource.Iron;
|
|
m_AosAttributes = new AosAttributes(this);
|
|
m_AosWeaponAttributes = new AosWeaponAttributes(this);
|
|
m_AosElementDamages = new AosElementAttributes(this);
|
|
m_AosSkillBonuses = new AosSkillBonuses(this);
|
|
}
|
|
|
|
m_MinDamage = reader.ReadInt();
|
|
m_MaxDamage = reader.ReadInt();
|
|
|
|
m_Speed = reader.ReadInt();
|
|
|
|
m_HitSound = reader.ReadInt();
|
|
m_MissSound = reader.ReadInt();
|
|
|
|
m_Skill = (SkillName)reader.ReadInt();
|
|
m_Type = (WeaponType)reader.ReadInt();
|
|
m_Animation = (WeaponAnimation)reader.ReadInt();
|
|
m_DamageLevel = (WeaponDamageLevel)reader.ReadInt();
|
|
m_AccuracyLevel = (WeaponAccuracyLevel)reader.ReadInt();
|
|
m_DurabilityLevel = (WeaponDurabilityLevel)reader.ReadInt();
|
|
m_Quality = (ItemQuality)reader.ReadInt();
|
|
|
|
m_Crafter = reader.ReadMobile();
|
|
|
|
m_Poison = Poison.Deserialize(reader);
|
|
m_PoisonCharges = reader.ReadInt();
|
|
|
|
if (m_StrReq == OldStrengthReq)
|
|
{
|
|
m_StrReq = -1;
|
|
}
|
|
|
|
if (m_DexReq == OldDexterityReq)
|
|
{
|
|
m_DexReq = -1;
|
|
}
|
|
|
|
if (m_IntReq == OldIntelligenceReq)
|
|
{
|
|
m_IntReq = -1;
|
|
}
|
|
|
|
if (m_MinDamage == OldMinDamage)
|
|
{
|
|
m_MinDamage = -1;
|
|
}
|
|
|
|
if (m_MaxDamage == OldMaxDamage)
|
|
{
|
|
m_MaxDamage = -1;
|
|
}
|
|
|
|
if (m_HitSound == OldHitSound)
|
|
{
|
|
m_HitSound = -1;
|
|
}
|
|
|
|
if (m_MissSound == OldMissSound)
|
|
{
|
|
m_MissSound = -1;
|
|
}
|
|
|
|
if (m_Speed == OldSpeed)
|
|
{
|
|
m_Speed = -1;
|
|
}
|
|
|
|
if (m_MaxRange == OldMaxRange)
|
|
{
|
|
m_MaxRange = -1;
|
|
}
|
|
|
|
if (m_Skill == OldSkill)
|
|
{
|
|
m_Skill = (SkillName)(-1);
|
|
}
|
|
|
|
if (m_Type == OldType)
|
|
{
|
|
m_Type = (WeaponType)(-1);
|
|
}
|
|
|
|
if (m_Animation == OldAnimation)
|
|
{
|
|
m_Animation = (WeaponAnimation)(-1);
|
|
}
|
|
|
|
if (UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular && Parent is Mobile)
|
|
{
|
|
m_SkillMod = new DefaultSkillMod(AccuracySkill, true, (int)m_AccuracyLevel * 5);
|
|
((Mobile)Parent).AddSkillMod(m_SkillMod);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (version < 15)
|
|
{
|
|
if (WeaponAttributes.HitLeechHits > 0 || WeaponAttributes.HitLeechMana > 0)
|
|
{
|
|
WeaponAttributes.ScaleLeech(Attributes.WeaponSpeed);
|
|
}
|
|
}
|
|
|
|
#region Mondain's Legacy Sets
|
|
if (m_SetAttributes == null)
|
|
{
|
|
m_SetAttributes = new AosAttributes(this);
|
|
}
|
|
|
|
if (m_SetSkillBonuses == null)
|
|
{
|
|
m_SetSkillBonuses = new AosSkillBonuses(this);
|
|
}
|
|
#endregion
|
|
|
|
if (Core.AOS && Parent is Mobile)
|
|
{
|
|
m_AosSkillBonuses.AddTo((Mobile)Parent);
|
|
}
|
|
|
|
int strBonus = m_AosAttributes.BonusStr;
|
|
int dexBonus = m_AosAttributes.BonusDex;
|
|
int intBonus = m_AosAttributes.BonusInt;
|
|
|
|
if (Parent is Mobile && (strBonus != 0 || dexBonus != 0 || intBonus != 0))
|
|
{
|
|
Mobile m = (Mobile)Parent;
|
|
|
|
string modName = Serial.ToString();
|
|
|
|
if (strBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Str, modName + "Str", strBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (dexBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (intBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Int, modName + "Int", intBonus, TimeSpan.Zero));
|
|
}
|
|
}
|
|
|
|
if (Parent is Mobile)
|
|
{
|
|
((Mobile)Parent).CheckStatTimers();
|
|
}
|
|
|
|
if (m_Hits <= 0 && m_MaxHits <= 0)
|
|
{
|
|
m_Hits = m_MaxHits = Utility.RandomMinMax(InitMinHits, InitMaxHits);
|
|
}
|
|
|
|
if (version < 6)
|
|
{
|
|
m_PlayerConstructed = true; // we don't know, so, assume it's crafted
|
|
}
|
|
|
|
if (m_Slayer == SlayerName.DaemonDismissal || m_Slayer == SlayerName.BalronDamnation)
|
|
m_Slayer = SlayerName.Exorcism;
|
|
|
|
if (m_Slayer2 == SlayerName.DaemonDismissal || m_Slayer2 == SlayerName.BalronDamnation)
|
|
m_Slayer2 = SlayerName.Exorcism;
|
|
}
|
|
#endregion
|
|
|
|
public BaseWeapon(int itemID)
|
|
: base(itemID)
|
|
{
|
|
Layer = (Layer)ItemData.Quality;
|
|
|
|
m_Quality = ItemQuality.Normal;
|
|
m_StrReq = -1;
|
|
m_DexReq = -1;
|
|
m_IntReq = -1;
|
|
m_MinDamage = -1;
|
|
m_MaxDamage = -1;
|
|
m_HitSound = -1;
|
|
m_MissSound = -1;
|
|
m_Speed = -1;
|
|
m_MaxRange = -1;
|
|
m_Skill = (SkillName)(-1);
|
|
m_Type = (WeaponType)(-1);
|
|
m_Animation = (WeaponAnimation)(-1);
|
|
|
|
m_Hits = m_MaxHits = Utility.RandomMinMax(InitMinHits, InitMaxHits);
|
|
|
|
m_Resource = CraftResource.Iron;
|
|
|
|
m_AosAttributes = new AosAttributes(this);
|
|
m_AosWeaponAttributes = new AosWeaponAttributes(this);
|
|
m_AosSkillBonuses = new AosSkillBonuses(this);
|
|
m_AosElementDamages = new AosElementAttributes(this);
|
|
m_NegativeAttributes = new NegativeAttributes(this);
|
|
m_ExtendedWeaponAttributes = new ExtendedWeaponAttributes(this);
|
|
|
|
#region Stygian Abyss
|
|
m_SAAbsorptionAttributes = new SAAbsorptionAttributes(this);
|
|
#endregion
|
|
|
|
#region Mondain's Legacy Sets
|
|
m_SetAttributes = new AosAttributes(this);
|
|
m_SetSkillBonuses = new AosSkillBonuses(this);
|
|
#endregion
|
|
|
|
m_AosSkillBonuses = new AosSkillBonuses(this);
|
|
|
|
if (this is ITool)
|
|
{
|
|
m_UsesRemaining = Utility.RandomMinMax(25, 75);
|
|
}
|
|
else
|
|
{
|
|
m_UsesRemaining = 150;
|
|
}
|
|
}
|
|
|
|
public BaseWeapon(Serial serial)
|
|
: base(serial)
|
|
{ }
|
|
|
|
private string GetNameString()
|
|
{
|
|
string name = Name;
|
|
|
|
if (name == null)
|
|
{
|
|
name = String.Format("#{0}", LabelNumber);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
[Hue, CommandProperty(AccessLevel.GameMaster)]
|
|
public override int Hue
|
|
{
|
|
get { return base.Hue; }
|
|
set
|
|
{
|
|
base.Hue = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
public int GetElementalDamageHue()
|
|
{
|
|
int phys, fire, cold, pois, nrgy, chaos, direct;
|
|
GetDamageTypes(null, out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct);
|
|
//Order is Cold, Energy, Fire, Poison, Physical left
|
|
|
|
int currentMax = 50;
|
|
int hue = 0;
|
|
|
|
if (pois >= currentMax)
|
|
{
|
|
hue = 1267 + (pois - 50) / 10;
|
|
currentMax = pois;
|
|
}
|
|
|
|
if (fire >= currentMax)
|
|
{
|
|
hue = 1255 + (fire - 50) / 10;
|
|
currentMax = fire;
|
|
}
|
|
|
|
if (nrgy >= currentMax)
|
|
{
|
|
hue = 1273 + (nrgy - 50) / 10;
|
|
currentMax = nrgy;
|
|
}
|
|
|
|
if (cold >= currentMax)
|
|
{
|
|
hue = 1261 + (cold - 50) / 10;
|
|
currentMax = cold;
|
|
}
|
|
|
|
return hue;
|
|
}
|
|
|
|
public override void AddNameProperty(ObjectPropertyList list)
|
|
{
|
|
if (m_ExtendedWeaponAttributes.AssassinHoned > 0)
|
|
{
|
|
list.Add(1152207); // Assassin's Edge
|
|
return;
|
|
}
|
|
|
|
int oreType;
|
|
|
|
switch (m_Resource)
|
|
{
|
|
case CraftResource.DullCopper:
|
|
oreType = 1053108;
|
|
break; // dull copper
|
|
case CraftResource.ShadowIron:
|
|
oreType = 1053107;
|
|
break; // shadow iron
|
|
case CraftResource.Copper:
|
|
oreType = 1053106;
|
|
break; // copper
|
|
case CraftResource.Bronze:
|
|
oreType = 1053105;
|
|
break; // bronze
|
|
case CraftResource.Gold:
|
|
oreType = 1053104;
|
|
break; // golden
|
|
case CraftResource.Agapite:
|
|
oreType = 1053103;
|
|
break; // agapite
|
|
case CraftResource.Verite:
|
|
oreType = 1053102;
|
|
break; // verite
|
|
case CraftResource.Valorite:
|
|
oreType = 1053101;
|
|
break; // valorite
|
|
case CraftResource.SpinedLeather:
|
|
oreType = 1061118;
|
|
break; // spined
|
|
case CraftResource.HornedLeather:
|
|
oreType = 1061117;
|
|
break; // horned
|
|
case CraftResource.BarbedLeather:
|
|
oreType = 1061116;
|
|
break; // barbed
|
|
case CraftResource.RedScales:
|
|
oreType = 1060814;
|
|
break; // red
|
|
case CraftResource.YellowScales:
|
|
oreType = 1060818;
|
|
break; // yellow
|
|
case CraftResource.BlackScales:
|
|
oreType = 1060820;
|
|
break; // black
|
|
case CraftResource.GreenScales:
|
|
oreType = 1060819;
|
|
break; // green
|
|
case CraftResource.WhiteScales:
|
|
oreType = 1060821;
|
|
break; // white
|
|
case CraftResource.BlueScales:
|
|
oreType = 1060815;
|
|
break; // blue
|
|
|
|
#region Mondain's Legacy
|
|
case CraftResource.OakWood:
|
|
oreType = 1072533;
|
|
break; // oak
|
|
case CraftResource.AshWood:
|
|
oreType = 1072534;
|
|
break; // ash
|
|
case CraftResource.YewWood:
|
|
oreType = 1072535;
|
|
break; // yew
|
|
case CraftResource.Heartwood:
|
|
oreType = 1072536;
|
|
break; // heartwood
|
|
case CraftResource.Bloodwood:
|
|
oreType = 1072538;
|
|
break; // bloodwood
|
|
case CraftResource.Frostwood:
|
|
oreType = 1072539;
|
|
break; // frostwood
|
|
#endregion
|
|
|
|
default:
|
|
oreType = 0;
|
|
break;
|
|
}
|
|
|
|
if (m_ReforgedPrefix != ReforgedPrefix.None || m_ReforgedSuffix != ReforgedSuffix.None)
|
|
{
|
|
if (m_ReforgedPrefix != ReforgedPrefix.None)
|
|
{
|
|
int prefix = RunicReforging.GetPrefixName(m_ReforgedPrefix);
|
|
|
|
if (m_ReforgedSuffix == ReforgedSuffix.None)
|
|
list.Add(1151757, String.Format("#{0}\t{1}", prefix, GetNameString())); // ~1_PREFIX~ ~2_ITEM~
|
|
else
|
|
list.Add(1151756, String.Format("#{0}\t{1}\t#{2}", prefix, GetNameString(), RunicReforging.GetSuffixName(m_ReforgedSuffix))); // ~1_PREFIX~ ~2_ITEM~ of ~3_SUFFIX~
|
|
}
|
|
else if (m_ReforgedSuffix != ReforgedSuffix.None)
|
|
{
|
|
RunicReforging.AddSuffixName(list, m_ReforgedSuffix, GetNameString());
|
|
}
|
|
}
|
|
else if (oreType != 0)
|
|
{
|
|
list.Add(1053099, "#{0}\t{1}", oreType, GetNameString()); // ~1_oretype~ ~2_armortype~
|
|
}
|
|
#region High Seas
|
|
else if (SearingWeapon)
|
|
{
|
|
list.Add(1151318, String.Format("#{0}", LabelNumber));
|
|
}
|
|
#endregion
|
|
else if (Name == null)
|
|
{
|
|
list.Add(LabelNumber);
|
|
}
|
|
else
|
|
{
|
|
list.Add(Name);
|
|
}
|
|
|
|
/*
|
|
* Want to move this to the engraving tool, let the non-harmful
|
|
* formatting show, and remove CLILOCs embedded: more like OSI
|
|
* did with the books that had markup, etc.
|
|
*
|
|
* This will have a negative effect on a few event things imgame
|
|
* as is.
|
|
*
|
|
* If we cant find a more OSI-ish way to clean it up, we can
|
|
* easily put this back, and use it in the deserialize
|
|
* method and engraving tool, to make it perm cleaned up.
|
|
*/
|
|
|
|
if (!String.IsNullOrEmpty(m_EngravedText))
|
|
{
|
|
list.Add(1062613, Utility.FixHtml(m_EngravedText));
|
|
}
|
|
}
|
|
|
|
public override bool AllowEquipedCast(Mobile from)
|
|
{
|
|
if (base.AllowEquipedCast(from))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return m_AosAttributes.SpellChanneling > 0 || Enhancement.GetValue(from, AosAttribute.SpellChanneling) > 0;
|
|
}
|
|
|
|
public virtual int ArtifactRarity { get { return 0; } }
|
|
|
|
public override bool DisplayWeight
|
|
{
|
|
get
|
|
{
|
|
if (IsVvVItem)
|
|
return true;
|
|
|
|
return base.DisplayWeight;
|
|
}
|
|
}
|
|
|
|
public virtual int GetLuckBonus()
|
|
{
|
|
#region Mondain's Legacy
|
|
if (m_Resource == CraftResource.Heartwood)
|
|
{
|
|
return 0;
|
|
}
|
|
#endregion
|
|
|
|
CraftResourceInfo resInfo = CraftResources.GetInfo(m_Resource);
|
|
|
|
if (resInfo == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
CraftAttributeInfo attrInfo = resInfo.AttributeInfo;
|
|
|
|
if (attrInfo == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return attrInfo.WeaponLuck;
|
|
}
|
|
|
|
public override void AddCraftedProperties(ObjectPropertyList list)
|
|
{
|
|
if (OwnerName != null)
|
|
{
|
|
list.Add(1153213, OwnerName);
|
|
}
|
|
|
|
if (m_Crafter != null)
|
|
{
|
|
list.Add(1050043, m_Crafter.TitleName); // crafted by ~1_NAME~
|
|
}
|
|
|
|
if (m_Quality == ItemQuality.Exceptional)
|
|
{
|
|
list.Add(1060636); // Exceptional
|
|
}
|
|
|
|
if (IsImbued)
|
|
{
|
|
list.Add(1080418); // (Imbued)
|
|
}
|
|
|
|
if (m_Altered)
|
|
{
|
|
list.Add(1111880); // Altered
|
|
}
|
|
}
|
|
|
|
public override void AddWeightProperty(ObjectPropertyList list)
|
|
{
|
|
base.AddWeightProperty(list);
|
|
|
|
if (IsVvVItem)
|
|
list.Add(1154937); // VvV Item
|
|
}
|
|
|
|
public override void AddUsesRemainingProperties(ObjectPropertyList list)
|
|
{
|
|
if (ShowUsesRemaining)
|
|
{
|
|
list.Add(1060584, UsesRemaining.ToString()); // uses remaining: ~1_val~
|
|
}
|
|
}
|
|
|
|
public override void AddNameProperties(ObjectPropertyList list)
|
|
{
|
|
base.AddNameProperties(list);
|
|
|
|
#region Factions
|
|
FactionEquipment.AddFactionProperties(this, list);
|
|
#endregion
|
|
|
|
#region Mondain's Legacy Sets
|
|
if (IsSetItem)
|
|
{
|
|
list.Add(1073491, Pieces.ToString()); // Part of a Weapon/Armor Set (~1_val~ pieces)
|
|
|
|
if (SetID == SetItem.Bestial)
|
|
list.Add(1151541, BestialSetHelper.GetTotalBerserk(this).ToString()); // Berserk ~1_VAL~
|
|
|
|
if (BardMasteryBonus)
|
|
list.Add(1151553); // Activate: Bard Mastery Bonus x2<br>(Effect: 1 min. Cooldown: 30 min.)
|
|
|
|
if (m_SetEquipped)
|
|
{
|
|
list.Add(1073492); // Full Weapon/Armor Set Present
|
|
GetSetProperties(list);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
if (m_ExtendedWeaponAttributes.Focus > 0)
|
|
{
|
|
list.Add(1150018); // Focus
|
|
}
|
|
|
|
if (m_NegativeAttributes.Brittle == 0 && m_AosAttributes.Brittle != 0)
|
|
{
|
|
list.Add(1116209); // Brittle
|
|
}
|
|
|
|
if (m_NegativeAttributes != null)
|
|
m_NegativeAttributes.GetProperties(list, this);
|
|
|
|
if (m_AosSkillBonuses != null)
|
|
{
|
|
m_AosSkillBonuses.GetProperties(list);
|
|
}
|
|
|
|
if (RequiredRace == Race.Elf)
|
|
{
|
|
list.Add(1075086); // Elves Only
|
|
}
|
|
|
|
#region Stygian Abyss
|
|
else if (RequiredRace == Race.Gargoyle)
|
|
{
|
|
list.Add(1111709); // Gargoyles Only
|
|
}
|
|
#endregion
|
|
|
|
if (ArtifactRarity > 0)
|
|
{
|
|
list.Add(1061078, ArtifactRarity.ToString()); // artifact rarity ~1_val~
|
|
}
|
|
|
|
if (m_Poison != null && m_PoisonCharges > 0 && CanShowPoisonCharges())
|
|
{
|
|
#region Mondain's Legacy mod
|
|
list.Add(m_Poison.LabelNumber, m_PoisonCharges.ToString());
|
|
#endregion
|
|
}
|
|
|
|
if (m_Slayer != SlayerName.None)
|
|
{
|
|
SlayerEntry entry = SlayerGroup.GetEntryByName(m_Slayer);
|
|
if (entry != null)
|
|
{
|
|
list.Add(entry.Title);
|
|
}
|
|
}
|
|
|
|
if (m_Slayer2 != SlayerName.None)
|
|
{
|
|
SlayerEntry entry = SlayerGroup.GetEntryByName(m_Slayer2);
|
|
if (entry != null)
|
|
{
|
|
list.Add(entry.Title);
|
|
}
|
|
}
|
|
|
|
#region Mondain's Legacy
|
|
if (m_Slayer3 != TalismanSlayerName.None)
|
|
{
|
|
if (m_Slayer3 == TalismanSlayerName.Wolf)
|
|
{
|
|
list.Add(1075462);
|
|
}
|
|
else if (m_Slayer3 == TalismanSlayerName.Goblin)
|
|
{
|
|
list.Add(1095010);
|
|
}
|
|
else if (m_Slayer3 == TalismanSlayerName.Undead)
|
|
{
|
|
list.Add(1060479);
|
|
}
|
|
else
|
|
{
|
|
list.Add(1072503 + (int)m_Slayer3);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
if (HasSocket<Caddellite>())
|
|
{
|
|
list.Add(1158662); // Caddellite Infused
|
|
}
|
|
|
|
double focusBonus = 1;
|
|
int enchantBonus = 0;
|
|
bool fcMalus = false;
|
|
int damBonus = 0;
|
|
SpecialMove move = null;
|
|
AosWeaponAttribute bonus = AosWeaponAttribute.HitColdArea;
|
|
|
|
#region Focus Attack
|
|
if (FocusWeilder != null)
|
|
{
|
|
move = SpecialMove.GetCurrentMove(FocusWeilder);
|
|
|
|
if (move is FocusAttack)
|
|
{
|
|
focusBonus = move.GetPropertyBonus(FocusWeilder);
|
|
damBonus = (int)(move.GetDamageScalar(FocusWeilder, null) * 100) - 100;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Stygian Abyss
|
|
if (EnchantedWeilder != null)
|
|
{
|
|
if (Server.Spells.Mysticism.EnchantSpell.IsUnderSpellEffects(EnchantedWeilder, this))
|
|
{
|
|
bonus = Server.Spells.Mysticism.EnchantSpell.BonusAttribute(EnchantedWeilder);
|
|
enchantBonus = Server.Spells.Mysticism.EnchantSpell.BonusValue(EnchantedWeilder);
|
|
fcMalus = Server.Spells.Mysticism.EnchantSpell.CastingMalus(EnchantedWeilder, this);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
int prop;
|
|
double fprop;
|
|
|
|
if ((prop = m_AosWeaponAttributes.DurabilityBonus) != 0)
|
|
{
|
|
list.Add(1151780, prop.ToString()); // durability +~1_VAL~%
|
|
}
|
|
|
|
if (Core.TOL)
|
|
{
|
|
if (m_ExtendedWeaponAttributes.Bane > 0)
|
|
{
|
|
list.Add(1154671); // Bane
|
|
}
|
|
|
|
if (m_ExtendedWeaponAttributes.BoneBreaker > 0)
|
|
{
|
|
list.Add(1157318); // Bone Breaker
|
|
}
|
|
|
|
if ((prop = m_ExtendedWeaponAttributes.HitSwarm) != 0)
|
|
{
|
|
list.Add(1157325, prop.ToString()); // Swarm ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_ExtendedWeaponAttributes.HitSparks) != 0)
|
|
{
|
|
list.Add(1157326, prop.ToString()); // Sparks ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_ExtendedWeaponAttributes.AssassinHoned) != 0)
|
|
{
|
|
list.Add(1152206); // Assassin Honed
|
|
}
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.SplinteringWeapon) != 0)
|
|
{
|
|
list.Add(1112857, prop.ToString()); //splintering weapon ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitDispel * focusBonus) != 0)
|
|
{
|
|
list.Add(1060417, ((int)fprop).ToString()); // hit dispel ~1_val~%
|
|
}
|
|
else if (bonus == AosWeaponAttribute.HitDispel && enchantBonus != 0)
|
|
{
|
|
list.Add(1060417, ((int)(enchantBonus * focusBonus)).ToString()); // hit dispel ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitFireball * focusBonus) != 0)
|
|
{
|
|
list.Add(1060420, ((int)fprop).ToString()); // hit fireball ~1_val~%
|
|
}
|
|
else if (bonus == AosWeaponAttribute.HitFireball && enchantBonus != 0)
|
|
{
|
|
list.Add(1060420, ((int)((double)enchantBonus * focusBonus)).ToString()); // hit fireball ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitLightning * focusBonus) != 0)
|
|
{
|
|
list.Add(1060423, ((int)fprop).ToString()); // hit lightning ~1_val~%
|
|
}
|
|
else if (bonus == AosWeaponAttribute.HitLightning && enchantBonus != 0)
|
|
{
|
|
list.Add(1060423, ((int)(enchantBonus * focusBonus)).ToString()); // hit lightning ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitHarm * focusBonus) != 0)
|
|
{
|
|
list.Add(1060421, ((int)fprop).ToString()); // hit harm ~1_val~%
|
|
}
|
|
else if (bonus == AosWeaponAttribute.HitHarm && enchantBonus != 0)
|
|
{
|
|
list.Add(1060421, ((int)(enchantBonus * focusBonus)).ToString()); // hit harm ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_ExtendedWeaponAttributes.HitExplosion * focusBonus) != 0)
|
|
{
|
|
list.Add(1158922, ((int)fprop).ToString()); // hit explosion ~1_val~%
|
|
}
|
|
|
|
if (SearingWeapon)
|
|
{
|
|
list.Add(1151183); // Searing Weapon
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitMagicArrow * focusBonus) != 0)
|
|
{
|
|
list.Add(1060426, ((int)fprop).ToString()); // hit magic arrow ~1_val~%
|
|
}
|
|
else if (bonus == AosWeaponAttribute.HitMagicArrow && enchantBonus != 0)
|
|
{
|
|
list.Add(1060426, ((int)(enchantBonus * focusBonus)).ToString()); // hit magic arrow ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitPhysicalArea * focusBonus) != 0)
|
|
{
|
|
list.Add(1060428, ((int)fprop).ToString()); // hit physical area ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitFireArea * focusBonus) != 0)
|
|
{
|
|
list.Add(1060419, ((int)fprop).ToString()); // hit fire area ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitColdArea * focusBonus) != 0)
|
|
{
|
|
list.Add(1060416, ((int)fprop).ToString()); // hit cold area ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitPoisonArea * focusBonus) != 0)
|
|
{
|
|
list.Add(1060429, ((int)fprop).ToString()); // hit poison area ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitEnergyArea * focusBonus) != 0)
|
|
{
|
|
list.Add(1060418, ((int)fprop).ToString()); // hit energy area ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitLeechStam * focusBonus) != 0)
|
|
{
|
|
list.Add(1060430, Math.Min(100, (int)fprop).ToString()); // hit stamina leech ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitLeechMana * focusBonus) != 0)
|
|
{
|
|
list.Add(1060427, Math.Min(100, (int)fprop).ToString()); // hit mana leech ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitLeechHits * focusBonus) != 0)
|
|
{
|
|
list.Add(1060422, Math.Min(100, (int)fprop).ToString()); // hit life leech ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitFatigue * focusBonus) != 0)
|
|
{
|
|
list.Add(1113700, ((int)fprop).ToString()); // Hit Fatigue ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitManaDrain * focusBonus) != 0)
|
|
{
|
|
list.Add(1113699, ((int)fprop).ToString()); // Hit Mana Drain ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitCurse * focusBonus) != 0)
|
|
{
|
|
list.Add(1113712, ((int)fprop).ToString()); // Hit Curse ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitLowerAttack * focusBonus) != 0)
|
|
{
|
|
list.Add(1060424, ((int)fprop).ToString()); // hit lower attack ~1_val~%
|
|
}
|
|
|
|
if ((fprop = (double)m_AosWeaponAttributes.HitLowerDefend * focusBonus) != 0)
|
|
{
|
|
list.Add(1060425, ((int)fprop).ToString()); // hit lower defense ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.BloodDrinker) != 0)
|
|
{
|
|
list.Add(1113591, prop.ToString()); // Blood Drinker
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.BattleLust) != 0)
|
|
{
|
|
list.Add(1113710, prop.ToString()); // Battle Lust
|
|
}
|
|
|
|
if (ImmolatingWeaponSpell.IsImmolating(RootParent as Mobile, this))
|
|
{
|
|
list.Add(1111917); // Immolated
|
|
}
|
|
|
|
if (Core.ML && this is BaseRanged && (prop = ((BaseRanged)this).Velocity) != 0)
|
|
{
|
|
list.Add(1072793, prop.ToString()); // Velocity ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.LowerAmmoCost) != 0)
|
|
{
|
|
list.Add(1075208, prop.ToString()); // Lower Ammo Cost ~1_Percentage~%
|
|
}
|
|
|
|
if ((prop = m_ExtendedWeaponAttributes.MysticWeapon) != 0)
|
|
{
|
|
list.Add(1155881, (30 - prop).ToString()); // mystic weapon -~1_val~ skill
|
|
}
|
|
else if ((prop = Parent is Mobile ? Enhancement.GetValue((Mobile)Parent, ExtendedWeaponAttribute.MysticWeapon) : 0) != 0)
|
|
{
|
|
list.Add(1155881, (30 - prop).ToString()); // mystic weapon -~1_val~ skill
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.SelfRepair) != 0)
|
|
{
|
|
list.Add(1060450, prop.ToString()); // self repair ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.NightSight) != 0)
|
|
{
|
|
list.Add(1060441); // night sight
|
|
}
|
|
|
|
if ((prop = fcMalus ? 1 : m_AosAttributes.SpellChanneling) != 0)
|
|
{
|
|
list.Add(1060482); // spell channeling
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.MageWeapon) != 0)
|
|
{
|
|
list.Add(1060438, (30 - prop).ToString()); // mage weapon -~1_val~ skill
|
|
}
|
|
|
|
if (Core.ML && m_AosAttributes.BalancedWeapon > 0 && Layer == Layer.TwoHanded)
|
|
{
|
|
list.Add(1072792); // Balanced
|
|
}
|
|
|
|
if ((prop = (GetLuckBonus() + m_AosAttributes.Luck)) != 0)
|
|
{
|
|
list.Add(1060436, prop.ToString()); // luck ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.EnhancePotions) != 0)
|
|
{
|
|
list.Add(1060411, prop.ToString()); // enhance potions ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.ReactiveParalyze) != 0)
|
|
{
|
|
list.Add(1112364); // reactive paralyze
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusStr) != 0)
|
|
{
|
|
list.Add(1060485, prop.ToString()); // strength bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusInt) != 0)
|
|
{
|
|
list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusDex) != 0)
|
|
{
|
|
list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusHits) != 0)
|
|
{
|
|
list.Add(1060431, prop.ToString()); // hit point increase ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusStam) != 0)
|
|
{
|
|
list.Add(1060484, prop.ToString()); // stamina increase ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusMana) != 0)
|
|
{
|
|
list.Add(1060439, prop.ToString()); // mana increase ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.RegenHits) != 0)
|
|
{
|
|
list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.RegenStam) != 0)
|
|
{
|
|
list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.RegenMana) != 0)
|
|
{
|
|
list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.ReflectPhysical) != 0)
|
|
{
|
|
list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.SpellDamage) != 0)
|
|
{
|
|
list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.CastRecovery) != 0)
|
|
{
|
|
list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~
|
|
}
|
|
|
|
if ((prop = fcMalus ? m_AosAttributes.CastSpeed - 1 : m_AosAttributes.CastSpeed) != 0)
|
|
{
|
|
list.Add(1060413, prop.ToString()); // faster casting ~1_val~
|
|
}
|
|
|
|
if ((prop = (GetHitChanceBonus() + m_AosAttributes.AttackChance)) != 0)
|
|
{
|
|
list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.DefendChance) != 0)
|
|
{
|
|
list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.LowerManaCost) != 0)
|
|
{
|
|
list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.LowerRegCost) != 0)
|
|
{
|
|
list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.WeaponSpeed) != 0)
|
|
{
|
|
list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = (GetDamageBonus() + m_AosAttributes.WeaponDamage + damBonus)) != 0)
|
|
{
|
|
list.Add(1060401, prop.ToString()); // damage increase ~1_val~%
|
|
}
|
|
|
|
if (Core.ML && (prop = m_AosAttributes.IncreasedKarmaLoss) != 0)
|
|
{
|
|
list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~%
|
|
}
|
|
|
|
#region Stygian Abyss
|
|
if ((prop = m_SAAbsorptionAttributes.CastingFocus) != 0)
|
|
{
|
|
list.Add(1113696, prop.ToString()); // Casting Focus ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.EaterFire) != 0)
|
|
{
|
|
list.Add(1113593, prop.ToString()); // Fire Eater ~1_Val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.EaterCold) != 0)
|
|
{
|
|
list.Add(1113594, prop.ToString()); // Cold Eater ~1_Val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.EaterPoison) != 0)
|
|
{
|
|
list.Add(1113595, prop.ToString()); // Poison Eater ~1_Val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.EaterEnergy) != 0)
|
|
{
|
|
list.Add(1113596, prop.ToString()); // Energy Eater ~1_Val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.EaterKinetic) != 0)
|
|
{
|
|
list.Add(1113597, prop.ToString()); // Kinetic Eater ~1_Val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.EaterDamage) != 0)
|
|
{
|
|
list.Add(1113598, prop.ToString()); // Damage Eater ~1_Val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.ResonanceFire) != 0)
|
|
{
|
|
list.Add(1113691, prop.ToString()); // Fire Resonance ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.ResonanceCold) != 0)
|
|
{
|
|
list.Add(1113692, prop.ToString()); // Cold Resonance ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.ResonancePoison) != 0)
|
|
{
|
|
list.Add(1113693, prop.ToString()); // Poison Resonance ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.ResonanceEnergy) != 0)
|
|
{
|
|
list.Add(1113694, prop.ToString()); // Energy Resonance ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_SAAbsorptionAttributes.ResonanceKinetic) != 0)
|
|
{
|
|
list.Add(1113695, prop.ToString()); // Kinetic Resonance ~1_val~%
|
|
}
|
|
#endregion
|
|
|
|
base.AddResistanceProperties(list);
|
|
|
|
if ((prop = GetLowerStatReq()) != 0)
|
|
{
|
|
list.Add(1060435, prop.ToString()); // lower requirements ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosWeaponAttributes.UseBestSkill) != 0)
|
|
{
|
|
list.Add(1060400); // use best weapon skill
|
|
}
|
|
|
|
int phys, fire, cold, pois, nrgy, chaos, direct;
|
|
|
|
GetDamageTypes(null, out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct);
|
|
|
|
#region Mondain's Legacy
|
|
if (chaos != 0)
|
|
{
|
|
list.Add(1072846, chaos.ToString()); // chaos damage ~1_val~%
|
|
}
|
|
|
|
if (direct != 0)
|
|
{
|
|
list.Add(1079978, direct.ToString()); // Direct Damage: ~1_PERCENT~%
|
|
}
|
|
#endregion
|
|
|
|
if (phys != 0)
|
|
{
|
|
list.Add(1060403, phys.ToString()); // physical damage ~1_val~%
|
|
}
|
|
|
|
if (fire != 0)
|
|
{
|
|
list.Add(1060405, fire.ToString()); // fire damage ~1_val~%
|
|
}
|
|
|
|
if (cold != 0)
|
|
{
|
|
list.Add(1060404, cold.ToString()); // cold damage ~1_val~%
|
|
}
|
|
|
|
if (pois != 0)
|
|
{
|
|
list.Add(1060406, pois.ToString()); // poison damage ~1_val~%
|
|
}
|
|
|
|
if (nrgy != 0)
|
|
{
|
|
list.Add(1060407, nrgy.ToString()); // energy damage ~1_val
|
|
}
|
|
|
|
if (Core.ML && chaos != 0)
|
|
{
|
|
list.Add(1072846, chaos.ToString()); // chaos damage ~1_val~%
|
|
}
|
|
|
|
if (Core.ML && direct != 0)
|
|
{
|
|
list.Add(1079978, direct.ToString()); // Direct Damage: ~1_PERCENT~%
|
|
}
|
|
|
|
list.Add(1061168, "{0}\t{1}", MinDamage.ToString(), MaxDamage.ToString()); // weapon damage ~1_val~ - ~2_val~
|
|
|
|
if (Core.ML)
|
|
{
|
|
list.Add(1061167, String.Format("{0}s", Speed)); // weapon speed ~1_val~
|
|
}
|
|
else
|
|
{
|
|
list.Add(1061167, Speed.ToString());
|
|
}
|
|
|
|
if (MaxRange > 1)
|
|
{
|
|
list.Add(1061169, MaxRange.ToString()); // range ~1_val~
|
|
}
|
|
|
|
int strReq = AOS.Scale(StrRequirement, 100 - GetLowerStatReq());
|
|
|
|
if (strReq > 0)
|
|
{
|
|
list.Add(1061170, strReq.ToString()); // strength requirement ~1_val~
|
|
}
|
|
|
|
if (Layer == Layer.TwoHanded)
|
|
{
|
|
list.Add(1061171); // two-handed weapon
|
|
}
|
|
else
|
|
{
|
|
list.Add(1061824); // one-handed weapon
|
|
}
|
|
|
|
if (Core.SE || m_AosWeaponAttributes.UseBestSkill == 0)
|
|
{
|
|
switch (Skill)
|
|
{
|
|
case SkillName.Swords:
|
|
list.Add(1061172);
|
|
break; // skill required: swordsmanship
|
|
case SkillName.Macing:
|
|
list.Add(1061173);
|
|
break; // skill required: mace fighting
|
|
case SkillName.Fencing:
|
|
list.Add(1061174);
|
|
break; // skill required: fencing
|
|
case SkillName.Archery:
|
|
list.Add(1061175);
|
|
break; // skill required: archery
|
|
case SkillName.Throwing:
|
|
list.Add(1112075); // skill required: throwing
|
|
break;
|
|
}
|
|
}
|
|
|
|
XmlAttach.AddAttachmentProperties(this, list);
|
|
|
|
if (m_Hits >= 0 && m_MaxHits > 0)
|
|
{
|
|
list.Add(1060639, "{0}\t{1}", m_Hits, m_MaxHits); // durability ~1_val~ / ~2_val~
|
|
}
|
|
|
|
if (IsSetItem && !m_SetEquipped)
|
|
{
|
|
list.Add(1072378); // <br>Only when full set is present:
|
|
GetSetProperties(list);
|
|
}
|
|
|
|
if (Core.EJ && LastParryChance > 0)
|
|
{
|
|
list.Add(1158861, LastParryChance.ToString()); // Last Parry Chance: ~1_val~%
|
|
}
|
|
}
|
|
|
|
public override void AddItemPowerProperties(ObjectPropertyList list)
|
|
{
|
|
if (m_ItemPower != ItemPower.None)
|
|
{
|
|
if (m_ItemPower <= ItemPower.LegendaryArtifact)
|
|
list.Add(1151488 + ((int)m_ItemPower - 1));
|
|
else
|
|
list.Add(1152281 + ((int)m_ItemPower - 9));
|
|
}
|
|
}
|
|
|
|
public bool CanShowPoisonCharges()
|
|
{
|
|
if (PrimaryAbility == WeaponAbility.InfectiousStrike || SecondaryAbility == WeaponAbility.InfectiousStrike)
|
|
return true;
|
|
|
|
return RootParent is Mobile && SkillMasterySpell.HasSpell((Mobile)RootParent, typeof(InjectedStrikeSpell));
|
|
}
|
|
|
|
public override void OnSingleClick(Mobile from)
|
|
{
|
|
var attrs = new List<EquipInfoAttribute>();
|
|
|
|
if (DisplayLootType)
|
|
{
|
|
if (LootType == LootType.Blessed)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1038021)); // blessed
|
|
}
|
|
else if (LootType == LootType.Cursed)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1049643)); // cursed
|
|
}
|
|
}
|
|
|
|
#region Factions
|
|
if (m_FactionState != null)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1041350)); // faction item
|
|
}
|
|
#endregion
|
|
|
|
if (m_Quality == ItemQuality.Exceptional)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1018305 - (int)m_Quality));
|
|
}
|
|
|
|
if (m_Identified || from.AccessLevel >= AccessLevel.GameMaster)
|
|
{
|
|
if (m_Slayer != SlayerName.None)
|
|
{
|
|
SlayerEntry entry = SlayerGroup.GetEntryByName(m_Slayer);
|
|
if (entry != null)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(entry.Title));
|
|
}
|
|
}
|
|
|
|
if (m_Slayer2 != SlayerName.None)
|
|
{
|
|
SlayerEntry entry = SlayerGroup.GetEntryByName(m_Slayer2);
|
|
if (entry != null)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(entry.Title));
|
|
}
|
|
}
|
|
|
|
if (m_DurabilityLevel != WeaponDurabilityLevel.Regular)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1038000 + (int)m_DurabilityLevel));
|
|
}
|
|
|
|
if (m_DamageLevel != WeaponDamageLevel.Regular)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1038015 + (int)m_DamageLevel));
|
|
}
|
|
|
|
if (m_AccuracyLevel != WeaponAccuracyLevel.Regular)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1038010 + (int)m_AccuracyLevel));
|
|
}
|
|
}
|
|
else if (m_Slayer != SlayerName.None || m_Slayer2 != SlayerName.None ||
|
|
m_DurabilityLevel != WeaponDurabilityLevel.Regular || m_DamageLevel != WeaponDamageLevel.Regular ||
|
|
m_AccuracyLevel != WeaponAccuracyLevel.Regular)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1038000)); // Unidentified
|
|
}
|
|
|
|
if (m_Poison != null && m_PoisonCharges > 0)
|
|
{
|
|
attrs.Add(new EquipInfoAttribute(1017383, m_PoisonCharges));
|
|
}
|
|
|
|
int number;
|
|
|
|
if (Name == null)
|
|
{
|
|
number = LabelNumber;
|
|
}
|
|
else
|
|
{
|
|
LabelTo(from, Name);
|
|
number = 1041000;
|
|
}
|
|
|
|
if (attrs.Count == 0 && Crafter == null && Name != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EquipmentInfo eqInfo = new EquipmentInfo(number, m_Crafter, false, attrs.ToArray());
|
|
|
|
from.Send(new DisplayEquipmentInfo(this, eqInfo));
|
|
}
|
|
|
|
public override bool DropToWorld(Mobile from, Point3D p)
|
|
{
|
|
bool drop = base.DropToWorld(from, p);
|
|
|
|
EnchantedHotItemSocket.CheckDrop(from, this);
|
|
|
|
return drop;
|
|
}
|
|
|
|
public static BaseWeapon Fists { get; set; }
|
|
|
|
#region ICraftable Members
|
|
public int OnCraft(
|
|
int quality,
|
|
bool makersMark,
|
|
Mobile from,
|
|
CraftSystem craftSystem,
|
|
Type typeRes,
|
|
ITool tool,
|
|
CraftItem craftItem,
|
|
int resHue)
|
|
{
|
|
Quality = (ItemQuality)quality;
|
|
|
|
if (makersMark)
|
|
{
|
|
Crafter = from;
|
|
}
|
|
|
|
PlayerConstructed = true;
|
|
|
|
if (typeRes == null)
|
|
{
|
|
typeRes = craftItem.Resources.GetAt(0).ItemType;
|
|
}
|
|
|
|
if (Core.AOS)
|
|
{
|
|
if (!craftItem.ForceNonExceptional)
|
|
{
|
|
Resource = CraftResources.GetFromType(typeRes);
|
|
}
|
|
|
|
CraftContext context = craftSystem.GetContext(from);
|
|
|
|
if (Quality == ItemQuality.Exceptional)
|
|
{
|
|
Attributes.WeaponDamage += 35;
|
|
}
|
|
|
|
if (!craftItem.ForceNonExceptional)
|
|
{
|
|
if (tool is BaseRunicTool)
|
|
{
|
|
((BaseRunicTool)tool).ApplyAttributesTo(this);
|
|
}
|
|
}
|
|
|
|
if (Core.ML && Quality == ItemQuality.Exceptional)
|
|
{
|
|
double div = Siege.SiegeShard ? 12.5 : 20;
|
|
|
|
Attributes.WeaponDamage += (int)(from.Skills.ArmsLore.Value / div);
|
|
from.CheckSkill(SkillName.ArmsLore, 0, 100);
|
|
}
|
|
}
|
|
else if (tool is BaseRunicTool)
|
|
{
|
|
if (craftItem != null && !craftItem.ForceNonExceptional)
|
|
{
|
|
CraftResource thisResource = CraftResources.GetFromType(typeRes);
|
|
|
|
if (thisResource == ((BaseRunicTool)tool).Resource)
|
|
{
|
|
Resource = thisResource;
|
|
|
|
switch (thisResource)
|
|
{
|
|
case CraftResource.DullCopper:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Durable;
|
|
AccuracyLevel = WeaponAccuracyLevel.Accurate;
|
|
break;
|
|
}
|
|
case CraftResource.ShadowIron:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Durable;
|
|
DamageLevel = WeaponDamageLevel.Ruin;
|
|
break;
|
|
}
|
|
case CraftResource.Copper:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Fortified;
|
|
DamageLevel = WeaponDamageLevel.Ruin;
|
|
AccuracyLevel = WeaponAccuracyLevel.Surpassingly;
|
|
break;
|
|
}
|
|
case CraftResource.Bronze:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Fortified;
|
|
DamageLevel = WeaponDamageLevel.Might;
|
|
AccuracyLevel = WeaponAccuracyLevel.Surpassingly;
|
|
break;
|
|
}
|
|
case CraftResource.Gold:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
|
|
DamageLevel = WeaponDamageLevel.Force;
|
|
AccuracyLevel = WeaponAccuracyLevel.Eminently;
|
|
break;
|
|
}
|
|
case CraftResource.Agapite:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
|
|
DamageLevel = WeaponDamageLevel.Power;
|
|
AccuracyLevel = WeaponAccuracyLevel.Eminently;
|
|
break;
|
|
}
|
|
case CraftResource.Verite:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
|
|
DamageLevel = WeaponDamageLevel.Power;
|
|
AccuracyLevel = WeaponAccuracyLevel.Exceedingly;
|
|
break;
|
|
}
|
|
case CraftResource.Valorite:
|
|
{
|
|
Identified = true;
|
|
DurabilityLevel = WeaponDurabilityLevel.Indestructible;
|
|
DamageLevel = WeaponDamageLevel.Vanq;
|
|
AccuracyLevel = WeaponAccuracyLevel.Supremely;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (craftItem != null && !craftItem.ForceNonExceptional)
|
|
{
|
|
CraftResourceInfo resInfo = CraftResources.GetInfo(m_Resource);
|
|
|
|
if (resInfo == null)
|
|
{
|
|
return quality;
|
|
}
|
|
|
|
CraftAttributeInfo attrInfo = resInfo.AttributeInfo;
|
|
|
|
if (attrInfo == null)
|
|
{
|
|
return quality;
|
|
}
|
|
|
|
DistributeMaterialBonus(attrInfo);
|
|
}
|
|
#endregion
|
|
|
|
return quality;
|
|
}
|
|
|
|
public virtual void DistributeMaterialBonus(CraftAttributeInfo attrInfo)
|
|
{
|
|
if (m_Resource != CraftResource.Heartwood)
|
|
{
|
|
m_AosAttributes.WeaponDamage += attrInfo.WeaponDamage;
|
|
m_AosAttributes.WeaponSpeed += attrInfo.WeaponSwingSpeed;
|
|
m_AosAttributes.AttackChance += attrInfo.WeaponHitChance;
|
|
m_AosAttributes.RegenHits += attrInfo.WeaponRegenHits;
|
|
m_AosWeaponAttributes.HitLeechHits += attrInfo.WeaponHitLifeLeech;
|
|
}
|
|
else
|
|
{
|
|
switch (Utility.Random(6))
|
|
{
|
|
case 0: m_AosAttributes.WeaponDamage += attrInfo.WeaponDamage; break;
|
|
case 1: m_AosAttributes.WeaponSpeed += attrInfo.WeaponSwingSpeed; break;
|
|
case 2: m_AosAttributes.AttackChance += attrInfo.WeaponHitChance; break;
|
|
case 3: m_AosAttributes.Luck += attrInfo.WeaponLuck; break;
|
|
case 4: m_AosWeaponAttributes.LowerStatReq += attrInfo.WeaponLowerRequirements; break;
|
|
case 5: m_AosWeaponAttributes.HitLeechHits += attrInfo.WeaponHitLifeLeech; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Mondain's Legacy Sets
|
|
public override bool OnDragLift(Mobile from)
|
|
{
|
|
if (Parent is Mobile && from == Parent)
|
|
{
|
|
if (IsSetItem && m_SetEquipped)
|
|
{
|
|
SetHelper.RemoveSetBonus(from, SetID, this);
|
|
}
|
|
}
|
|
|
|
return base.OnDragLift(from);
|
|
}
|
|
|
|
public virtual SetItem SetID { get { return SetItem.None; } }
|
|
public virtual int Pieces { get { return 0; } }
|
|
|
|
public virtual bool BardMasteryBonus
|
|
{
|
|
get
|
|
{
|
|
return (SetID == SetItem.Virtuoso);
|
|
}
|
|
}
|
|
|
|
public bool IsSetItem { get { return SetID != SetItem.None; } }
|
|
|
|
private int m_SetHue;
|
|
private bool m_SetEquipped;
|
|
private bool m_LastEquipped;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetHue
|
|
{
|
|
get { return m_SetHue; }
|
|
set
|
|
{
|
|
m_SetHue = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
public bool SetEquipped { get { return m_SetEquipped; } set { m_SetEquipped = value; } }
|
|
|
|
public bool LastEquipped { get { return m_LastEquipped; } set { m_LastEquipped = value; } }
|
|
|
|
private AosAttributes m_SetAttributes;
|
|
private AosSkillBonuses m_SetSkillBonuses;
|
|
private int m_SetSelfRepair;
|
|
private int m_SetPhysicalBonus, m_SetFireBonus, m_SetColdBonus, m_SetPoisonBonus, m_SetEnergyBonus;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosAttributes SetAttributes { get { return m_SetAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosSkillBonuses SetSkillBonuses { get { return m_SetSkillBonuses; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetSelfRepair
|
|
{
|
|
get { return m_SetSelfRepair; }
|
|
set
|
|
{
|
|
m_SetSelfRepair = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetPhysicalBonus
|
|
{
|
|
get
|
|
{
|
|
return m_SetPhysicalBonus;
|
|
}
|
|
set
|
|
{
|
|
m_SetPhysicalBonus = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetFireBonus
|
|
{
|
|
get
|
|
{
|
|
return m_SetFireBonus;
|
|
}
|
|
set
|
|
{
|
|
m_SetFireBonus = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetColdBonus
|
|
{
|
|
get
|
|
{
|
|
return m_SetColdBonus;
|
|
}
|
|
set
|
|
{
|
|
m_SetColdBonus = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetPoisonBonus
|
|
{
|
|
get
|
|
{
|
|
return m_SetPoisonBonus;
|
|
}
|
|
set
|
|
{
|
|
m_SetPoisonBonus = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SetEnergyBonus
|
|
{
|
|
get
|
|
{
|
|
return m_SetEnergyBonus;
|
|
}
|
|
set
|
|
{
|
|
m_SetEnergyBonus = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
public virtual void GetSetProperties(ObjectPropertyList list)
|
|
{
|
|
int prop;
|
|
|
|
if ((prop = m_SetSelfRepair) != 0 && WeaponAttributes.SelfRepair == 0)
|
|
{
|
|
list.Add(1060450, prop.ToString()); // self repair ~1_val~
|
|
}
|
|
|
|
SetHelper.GetSetProperties(list, this);
|
|
}
|
|
|
|
public int SetResistBonus(ResistanceType resist)
|
|
{
|
|
switch (resist)
|
|
{
|
|
case ResistanceType.Physical: return PhysicalResistance;
|
|
case ResistanceType.Fire: return FireResistance;
|
|
case ResistanceType.Cold: return ColdResistance;
|
|
case ResistanceType.Poison: return PoisonResistance;
|
|
case ResistanceType.Energy: return EnergyResistance;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endregion
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool Altered
|
|
{
|
|
get { return m_Altered; }
|
|
set
|
|
{
|
|
m_Altered = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum CheckSlayerResult
|
|
{
|
|
None,
|
|
Slayer,
|
|
SuperSlayer,
|
|
Opposition
|
|
}
|
|
}
|