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 parryNonRacial = defender.Skills[SkillName.Parry].NonRacialValue;
double bushido = defender.Skills[SkillName.Bushido].Value;
if ( shield != null )
{
double chance = (parryNonRacial - bushido) / 400.0; //As per OSI, no negitive effect from the Racial stuffs, ie, 120 bushido and '0' parry with humans
// Parry over 100 grants a 5% bonus.
if ( parry >= 100.0 )
chance += 0.05;
// Evasion grants a 50% bonus.
if ( Evasion.IsEvading( defender ) )
chance *= 1.5;
// Low dexterity lowers the chance.
if ( defender.Dex < 80 )
chance = chance * (20 + defender.Dex) / 100;
return defender.CheckSkill( SkillName.Parry, chance );
}
else if ( !(defender.Weapon is Fists) && !(defender.Weapon is BaseRanged) )
{
BaseWeapon weapon = defender.Weapon as BaseWeapon;
double divisor = (weapon.Layer == Layer.OneHanded) ? 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 50% bonus.
if( Evasion.IsEvading( defender ) )
chance *= 1.5;
// Low dexterity lowers the chance.
if( defender.Dex < 80 )
chance = chance * (20 + defender.Dex) / 100;
if ( chance > aosChance )
return defender.CheckSkill( SkillName.Parry, chance );
else
return (aosChance > Utility.RandomDouble()); // Only skillcheck if wielding a shield & there's no effect from Bushido
}
return false;
}
public virtual int AbsorbDamageAOS( Mobile attacker, Mobile defender, int damage )
{
bool blocked = false;
int originaldamage = damage;
if ( defender.Player || defender.Body.IsHuman )
{
blocked = CheckParry( defender );
if ( blocked )
{
defender.FixedEffect( 0x37B9, 10, 16 );
damage = 0;
// Successful block removes the Honorable Execution penalty.
HonorableExecution.RemovePenalty( defender );
if ( CounterAttack.IsCountering( defender ) )
{
BaseWeapon weapon = defender.Weapon as BaseWeapon;
if ( weapon != null )
weapon.OnSwing( defender, attacker );
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) );
defender.Stam += Utility.RandomMinMax( 1, (int)(bushido / 5) );
}
BaseShield shield = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseShield;
}
if ( shield != null )
{
shield.OnHit( this, damage );
// XmlAttachment check for OnArmorHit
Server.Engines.XmlSpawner2.XmlAttach.OnArmorHit(attacker, defender, shield, this, originaldamage);
}
if ( !blocked )
{
double positionChance = Utility.RandomDouble();
Item armorItem;
if( positionChance < 0.07 )
armorItem = defender.NeckArmor;
else if( positionChance < 0.14 )
armorItem = defender.HandArmor;
else if( positionChance < 0.28 )
armorItem = defender.ArmsArmor;
else if( positionChance < 0.43 )
armorItem = defender.HeadArmor;
else if( positionChance < 0.65 )
armorItem = defender.LegsArmor;
else
armorItem = defender.ChestArmor;
IWearableDurability armor = armorItem as IWearableDurability;
if (armor != null)
{
armor.OnHit(this, damage); // call OnHit to lose durability
// XmlAttachment check for OnArmorHit
damage -= Server.Engines.XmlSpawner2.XmlAttach.OnArmorHit(attacker, defender, armorItem, this, originaldamage);
}
}
return damage;
[COLOR="Red"]}[/COLOR]
public virtual int AbsorbDamage( Mobile attacker, Mobile defender, int damage )
{
if ( Core.AOS )
return AbsorbDamageAOS( attacker, defender, 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 );
BaseShield shield = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseShield;
if ( shield != null )
damage = shield.OnHit( this, damage );
// XmlAttachment check for OnArmorHit
damage -= Server.Engines.XmlSpawner2.XmlAttach.OnArmorHit(attacker, defender, armorItem, this, damage);
damage -= Server.Engines.XmlSpawner2.XmlAttach.OnArmorHit(attacker, defender, shield, this, damage);
int virtualArmor = defender.VirtualArmor + defender.VirtualArmorMod;
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;
foreach ( Mobile m in defender.GetMobilesInRange( 1 ) )
{
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;
}
}
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 static bool m_InDoubleStrike;
public static bool InDoubleStrike
{
get{ return m_InDoubleStrike; }
set{ m_InDoubleStrike = value; }
}
public virtual void OnHit( Mobile attacker, Mobile defender )
{
OnHit( attacker, defender, 1.0 );
}
public virtual void OnHit( Mobile attacker, Mobile defender, double damageBonus )
{
if ( MirrorImage.HasClone( defender ) && (defender.Skills.Ninjitsu.Value / 150.0) > Utility.RandomDouble() )
{
Clone bc;
foreach ( Mobile m in defender.GetMobilesInRange( 4 ) )
{
bc = m as Clone;
if ( bc != null && bc.Summoned && bc.SummonMaster == defender )
{
attacker.SendLocalizedMessage( 1063141 ); // Your attack has been diverted to a nearby mirror image of your target!
defender.SendLocalizedMessage( 1063140 ); // You manage to divert the attack onto one of your nearby mirror images.
/*
* TODO: What happens if the Clone parries a blow?
* And what about if the attacker is using Honorable Execution
* and kills it?
*/
defender = m;
break;
}
}
}
PlaySwingAnimation( attacker );
PlayHurtAnimation( defender );
attacker.PlaySound( GetHitAttackSound( attacker, defender ) );
defender.PlaySound( GetHitDefendSound( attacker, defender ) );
int damage = ComputeDamage( attacker, defender );
#region Damage Multipliers
/*
* The following damage bonuses multiply damage by a factor.
* Capped at x3 (300%).
*/
double factor = 1.0;
WeaponAbility a = WeaponAbility.GetCurrentAbility( attacker );
SpecialMove move = SpecialMove.GetCurrentMove( attacker );
if ( a != null )
factor *= a.DamageScalar;
if ( move != null )
factor *= move.GetDamageScalar( attacker, defender );
factor *= damageBonus;
CheckSlayerResult cs = CheckSlayers( attacker, defender );
if ( cs != CheckSlayerResult.None )
{
if ( cs == CheckSlayerResult.Slayer )
defender.FixedEffect( 0x37B9, 10, 5 );
factor *= 2.0;
}
if ( !attacker.Player )
{
if ( defender is PlayerMobile )
{
PlayerMobile pm = (PlayerMobile)defender;
if ( pm.EnemyOfOneType != null && pm.EnemyOfOneType != attacker.GetType() )
factor *= 2.0;
}
}
else if ( !defender.Player )
{
if ( attacker is PlayerMobile )
{
PlayerMobile pm = (PlayerMobile)attacker;
if ( pm.WaitingForEnemy )
{
pm.EnemyOfOneType = defender.GetType();
pm.WaitingForEnemy = false;
}
if ( pm.EnemyOfOneType == defender.GetType() )
{
defender.FixedEffect( 0x37B9, 10, 5, 1160, 0 );
factor *= 1.5;
}
}
}
int packInstinctBonus = GetPackInstinctBonus( attacker, defender );
if ( packInstinctBonus != 0 )
factor *= 1.0 + (double)packInstinctBonus / 100.0;
if ( m_InDoubleStrike )
factor *= 0.9; // 10% loss when attacking with double-strike
TransformContext context = TransformationSpell.GetContext( defender );
if ( (m_Slayer == SlayerName.Silver || m_Slayer2 == SlayerName.Silver) && context != null && context.Type != typeof( HorrificBeastSpell ) )
factor *= 1.25; // Every necromancer transformation other than horrific beast takes an additional 25% damage
if ( attacker is PlayerMobile )
{
PlayerMobile pmAttacker = (PlayerMobile) attacker;
if ( pmAttacker.HonorActive && pmAttacker.InRange( defender, 1 ) )
factor *= 1.25;
if ( pmAttacker.SentHonorContext != null && pmAttacker.SentHonorContext.Target == defender )
pmAttacker.SentHonorContext.ApplyPerfectionDamageBonus( ref factor );
}
if ( factor > 3.0 )
factor = 3.0;
damage = (int)(damage * factor);
#endregion
if ( attacker is BaseCreature )
((BaseCreature)attacker).AlterMeleeDamageTo( defender, ref damage );
if ( defender is BaseCreature )
((BaseCreature)defender).AlterMeleeDamageFrom( attacker, ref damage );
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 ) /*&& a.CheckMana( attacker, true )*/ ) // Parried special moves have no mana cost
{
a = null;
WeaponAbility.ClearCurrentAbility( attacker );
attacker.SendLocalizedMessage( 1061140 ); // Your attack was parried!
}
}
AddBlood( attacker, defender, damage );
int phys, fire, cold, pois, nrgy;
GetDamageTypes( attacker, out phys, out fire, out cold, out pois, out nrgy );
if ( m_Consecrated )
{
phys = defender.PhysicalResistance;
fire = defender.FireResistance;
cold = defender.ColdResistance;
pois = defender.PoisonResistance;
nrgy = defender.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 = 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;
}
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;
}
bool ignoreArmor = ( a is ArmorIgnore || (move != null && move.IgnoreArmor( attacker )) );
damageGiven = AOS.Damage( defender, attacker, damage, ignoreArmor, phys, fire, cold, pois, nrgy );
double propertyBonus = ( move == null ) ? 1.0 : move.GetPropertyBonus( attacker );
if ( Core.AOS )
{
int lifeLeech = 0;
int stamLeech = 0;
int manaLeech = 0;
int wraithLeech = 0;
if ( (int)(m_AosWeaponAttributes.HitLeechHits * propertyBonus) > Utility.Random( 100 ) )
lifeLeech += 30; // HitLeechHits% chance to leech 30% of damage as hit points
if ( (int)(m_AosWeaponAttributes.HitLeechStam * propertyBonus) > Utility.Random( 100 ) )
stamLeech += 100; // HitLeechStam% chance to leech 100% of damage as stamina
if ( (int)(m_AosWeaponAttributes.HitLeechMana * propertyBonus) > Utility.Random( 100 ) )
manaLeech += 40; // HitLeechMana% chance to leech 40% of damage as mana
if ( m_Cursed )
lifeLeech += 50; // Additional 50% life leech for cursed weapons (necro spell)
context = TransformationSpell.GetContext( attacker );
if ( context != null && context.Type == typeof( VampiricEmbraceSpell ) )
lifeLeech += 20; // Vampiric embrace gives an additional 20% life leech
if ( context != null && context.Type == typeof( WraithFormSpell ) )
{
wraithLeech = (5 + (int)((15 * attacker.Skills.SpiritSpeak.Value) / 100)); // Wraith form gives an additional 5-20% mana leech
// Mana leeched by the Wraith Form spell is actually stolen, not just leeched.
defender.Mana -= AOS.Scale( damageGiven, wraithLeech );
manaLeech += wraithLeech;
}
if ( lifeLeech != 0 )
attacker.Hits += AOS.Scale( damageGiven, lifeLeech );
if ( stamLeech != 0 )
attacker.Stam += AOS.Scale( damageGiven, stamLeech );
if ( manaLeech != 0 )
attacker.Mana += AOS.Scale( damageGiven, manaLeech );
if ( lifeLeech != 0 || stamLeech != 0 || manaLeech != 0 )
attacker.PlaySound( 0x44D );
}
if ( m_MaxHits > 0 && ((MaxRange <= 1 && (defender is Slime || defender is ToxicElemental)) || Utility.Random( 25 ) == 0) ) // Stratics says 50% chance, seems more like 4%..
{
if ( MaxRange <= 1 && (defender is Slime || defender is ToxicElemental) )
attacker.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500263 ); // *Acid blood scars your weapon!*
if ( Core.AOS && m_AosWeaponAttributes.SelfRepair > Utility.Random( 10 ) )
{
HitPoints += 2;
}
else
{
if ( m_Hits > 0 )
{
--HitPoints;
}
else if ( m_MaxHits > 1 )
{
--MaxHitPoints;
if ( Parent is Mobile )
((Mobile)Parent).LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061121 ); // Your equipment is severely damaged.
}
else
{
Delete();
}
}
}
if ( attacker is VampireBatFamiliar )
{
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 )
{
int physChance = (int)(m_AosWeaponAttributes.HitPhysicalArea * propertyBonus);
int fireChance = (int)(m_AosWeaponAttributes.HitFireArea * propertyBonus);
int coldChance = (int)(m_AosWeaponAttributes.HitColdArea * propertyBonus);
int poisChance = (int)(m_AosWeaponAttributes.HitPoisonArea * propertyBonus);
int nrgyChance = (int)(m_AosWeaponAttributes.HitEnergyArea * propertyBonus);
if ( physChance != 0 && physChance > Utility.Random( 100 ) )
DoAreaAttack( attacker, defender, 0x10E, 50, 100, 0, 0, 0, 0 );
if ( fireChance != 0 && fireChance > Utility.Random( 100 ) )
DoAreaAttack( attacker, defender, 0x11D, 1160, 0, 100, 0, 0, 0 );
if ( coldChance != 0 && coldChance > Utility.Random( 100 ) )
DoAreaAttack( attacker, defender, 0x0FC, 2100, 0, 0, 100, 0, 0 );
if ( poisChance != 0 && poisChance > Utility.Random( 100 ) )
DoAreaAttack( attacker, defender, 0x205, 1166, 0, 0, 0, 100, 0 );
if ( nrgyChance != 0 && nrgyChance > Utility.Random( 100 ) )
DoAreaAttack( attacker, defender, 0x1F1, 120, 0, 0, 0, 0, 100 );
int maChance = (int)(m_AosWeaponAttributes.HitMagicArrow * propertyBonus);
int harmChance = (int)(m_AosWeaponAttributes.HitHarm * propertyBonus);
int fireballChance = (int)(m_AosWeaponAttributes.HitFireball * propertyBonus);
int lightningChance = (int)(m_AosWeaponAttributes.HitLightning * propertyBonus);
int dispelChance = (int)(m_AosWeaponAttributes.HitDispel * propertyBonus);
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 );
int laChance = (int)(m_AosWeaponAttributes.HitLowerAttack * propertyBonus);
int ldChance = (int)(m_AosWeaponAttributes.HitLowerDefend * propertyBonus);
if ( laChance != 0 && laChance > Utility.Random( 100 ) )
DoLowerAttack( attacker, defender );
if ( ldChance != 0 && ldChance > 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 ( !(this is BaseRanged) )
{
if ( AnimalForm.UnderTransformation( attacker, typeof( GiantSerpent ) ) )
defender.ApplyPoison( attacker, Poison.Lesser );
if ( AnimalForm.UnderTransformation( defender, typeof( BullFrog ) ) )
attacker.ApplyPoison( defender, Poison.Regular );
}
}
public virtual double GetAosDamage( Mobile attacker, int bonus, int dice, int sides )
{
int damage = Utility.Dice( dice, sides, bonus ) * 100;
int damageBonus = 0;
// Inscription bonus
int inscribeSkill = attacker.Skills[SkillName.Inscribe].Fixed;
damageBonus += inscribeSkill / 200;
if ( inscribeSkill >= 1000 )
damageBonus += 5;
if ( attacker.Player )
{
// Int bonus
damageBonus += (attacker.Int / 10);
// SDI bonus
damageBonus += AosAttributes.GetValue( attacker, AosAttribute.SpellDamage );
}
damage = AOS.Scale( damage, 100 + damageBonus );
return damage / 100;
}