Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
44
Scripts/Abilities/ArmorIgnore.cs
Normal file
44
Scripts/Abilities/ArmorIgnore.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// This special move allows the skilled warrior to bypass his target's physical resistance, for one shot only.
|
||||
/// The Armor Ignore shot does slightly less damage than normal.
|
||||
/// Against a heavily armored opponent, this ability is a big win, but when used against a very lightly armored foe, it might be better to use a standard strike!
|
||||
/// </summary>
|
||||
public class ArmorIgnore : WeaponAbility
|
||||
{
|
||||
public ArmorIgnore()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
public override double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0.9;
|
||||
}
|
||||
}
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060076); // Your attack penetrates their armor!
|
||||
defender.SendLocalizedMessage(1060077); // The blow penetrated your armor!
|
||||
|
||||
defender.PlaySound(0x56);
|
||||
defender.FixedParticles(0x3728, 200, 25, 9942, EffectLayer.Waist);
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Scripts/Abilities/ArmorPierce.cs
Normal file
93
Scripts/Abilities/ArmorPierce.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Strike your opponent with great force, partially bypassing their armor and inflicting greater damage. Requires either Bushido or Ninjitsu skill
|
||||
/// </summary>
|
||||
public class ArmorPierce : WeaponAbility
|
||||
{
|
||||
public static Dictionary<Mobile, Timer> _Table = new Dictionary<Mobile, Timer>();
|
||||
|
||||
public ArmorPierce()
|
||||
{
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
public override double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Core.HS ? 1.0 : 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RequiresSE
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1063350); // You pierce your opponent's armor!
|
||||
|
||||
defender.SendLocalizedMessage(1153764); // Your armor has been pierced!
|
||||
defender.SendLocalizedMessage(1063351); // Your attacker pierced your armor!
|
||||
|
||||
if (Core.HS)
|
||||
{
|
||||
if (_Table.ContainsKey(defender))
|
||||
{
|
||||
if (attacker.Weapon is BaseRanged)
|
||||
return;
|
||||
|
||||
_Table[defender].Stop();
|
||||
}
|
||||
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ArmorPierce, 1028860, 1154367, TimeSpan.FromSeconds(3), defender, "10"));
|
||||
_Table[defender] = Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(3), RemoveEffects, defender);
|
||||
}
|
||||
|
||||
defender.PlaySound(0x28E);
|
||||
defender.FixedParticles(0x3728, 1, 26, 0x26D6, 0, 0, EffectLayer.Waist);
|
||||
}
|
||||
|
||||
public static void RemoveEffects(Mobile m)
|
||||
{
|
||||
if (IsUnderEffects(m))
|
||||
{
|
||||
m.SendLocalizedMessage(1153904); // Your armor has returned to normal.
|
||||
_Table.Remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsUnderEffects(Mobile m)
|
||||
{
|
||||
if(m == null)
|
||||
return false;
|
||||
|
||||
return _Table.ContainsKey(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Scripts/Abilities/BaseSummoned.cs
Normal file
89
Scripts/Abilities/BaseSummoned.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Created by Peoharen for the Mobile Abilities Package.
|
||||
using System;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
[CorpseName("a corpse")]
|
||||
public class BaseSummoned : BaseCreature
|
||||
{
|
||||
private DateTime m_DecayTime;
|
||||
public BaseSummoned(AIType aitype, FightMode fightmode, int spot, int meleerange, double passivespeed, double activespeed)
|
||||
: base(aitype, fightmode, spot, meleerange, passivespeed, activespeed)
|
||||
{
|
||||
this.m_DecayTime = DateTime.UtcNow + this.m_Delay;
|
||||
}
|
||||
|
||||
public BaseSummoned(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool AlwaysAttackable
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override bool DeleteCorpseOnDeath
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override double DispelDifficulty
|
||||
{
|
||||
get
|
||||
{
|
||||
return 117.5;
|
||||
}
|
||||
}
|
||||
public override double DispelFocus
|
||||
{
|
||||
get
|
||||
{
|
||||
return 45.0;
|
||||
}
|
||||
}
|
||||
public override bool IsDispellable
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public virtual TimeSpan m_Delay
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromMinutes(2.0);
|
||||
}
|
||||
}
|
||||
public override void OnThink()
|
||||
{
|
||||
if (DateTime.UtcNow > this.m_DecayTime)
|
||||
{
|
||||
this.FixedParticles(14120, 10, 15, 5012, EffectLayer.Waist);
|
||||
this.PlaySound(510);
|
||||
this.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write((int)0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
int version = reader.ReadInt();
|
||||
|
||||
this.m_DecayTime = DateTime.UtcNow + TimeSpan.FromMinutes(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Scripts/Abilities/BattleLust.cs
Normal file
107
Scripts/Abilities/BattleLust.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class BattleLust
|
||||
{
|
||||
private static readonly Dictionary<Mobile, BattleLustTimer> m_Table = new Dictionary<Mobile, BattleLustTimer>();
|
||||
public static bool UnderBattleLust(Mobile m)
|
||||
{
|
||||
return m_Table.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static int GetBonus(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!m_Table.ContainsKey(attacker))
|
||||
return 0;
|
||||
|
||||
int bonus = m_Table[attacker].Bonus * attacker.Aggressed.Count;
|
||||
|
||||
if (defender is PlayerMobile && bonus > 45)
|
||||
bonus = 45;
|
||||
else if (bonus > 90)
|
||||
bonus = 90;
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
public static void IncreaseBattleLust(Mobile m, int damage)
|
||||
{
|
||||
if (damage < 30)
|
||||
return;
|
||||
else if (AosWeaponAttributes.GetValue(m, AosWeaponAttribute.BattleLust) == 0)
|
||||
return;
|
||||
else if (m_Table.ContainsKey(m))
|
||||
{
|
||||
if (m_Table[m].CanGain)
|
||||
{
|
||||
if (m_Table[m].Bonus < 16)
|
||||
m_Table[m].Bonus++;
|
||||
|
||||
m_Table[m].CanGain = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BattleLustTimer blt = new BattleLustTimer(m, 1);
|
||||
blt.Start();
|
||||
m_Table.Add(m, blt);
|
||||
m.SendLocalizedMessage(1113748); // The damage you received fuels your battle fury.
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DecreaseBattleLust(Mobile m)
|
||||
{
|
||||
if (m_Table.ContainsKey(m))
|
||||
{
|
||||
m_Table[m].Bonus--;
|
||||
|
||||
if (m_Table[m].Bonus <= 0)
|
||||
{
|
||||
m_Table.Remove(m);
|
||||
|
||||
// No Message?
|
||||
//m.SendLocalizedMessage( 0 ); //
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public class BattleLustTimer : Timer
|
||||
{
|
||||
public int Bonus;
|
||||
public bool CanGain;
|
||||
private readonly Mobile m_Mobile;
|
||||
private int m_Count;
|
||||
public BattleLustTimer(Mobile m, int bonus)
|
||||
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
this.m_Mobile = m;
|
||||
this.Bonus = bonus;
|
||||
this.m_Count = 1;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
this.m_Count %= 3;
|
||||
|
||||
if (this.m_Count == 0)
|
||||
{
|
||||
if (!DecreaseBattleLust(this.m_Mobile))
|
||||
this.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.CanGain = true;
|
||||
}
|
||||
|
||||
this.m_Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Scripts/Abilities/Bladeweave.cs
Normal file
149
Scripts/Abilities/Bladeweave.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class Bladeweave : WeaponAbility
|
||||
{
|
||||
private class BladeWeaveRedirect
|
||||
{
|
||||
public WeaponAbility NewAbility;
|
||||
public int ClilocEntry;
|
||||
|
||||
public BladeWeaveRedirect(WeaponAbility ability, int cliloc)
|
||||
{
|
||||
NewAbility = ability;
|
||||
ClilocEntry = cliloc;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, BladeWeaveRedirect> m_NewAttack = new Dictionary<Mobile, BladeWeaveRedirect>();
|
||||
|
||||
public static bool BladeWeaving(Mobile attacker, out WeaponAbility a)
|
||||
{
|
||||
BladeWeaveRedirect bwr;
|
||||
bool success = m_NewAttack.TryGetValue(attacker, out bwr);
|
||||
if (success)
|
||||
a = bwr.NewAbility;
|
||||
else
|
||||
a = null;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public Bladeweave()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 30; } }
|
||||
|
||||
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, false))
|
||||
return false;
|
||||
|
||||
int ran = -1;
|
||||
|
||||
if (attacker is BaseCreature && PetTrainingHelper.CheckSecondarySkill((BaseCreature)attacker, SkillName.Bushido))
|
||||
{
|
||||
ran = Utility.Random(9);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool canfeint = attacker.Skills[WeaponAbility.Feint.GetSecondarySkill(attacker)].Value >= WeaponAbility.Feint.GetRequiredSecondarySkill(attacker);
|
||||
bool canblock = attacker.Skills[WeaponAbility.Block.GetSecondarySkill(attacker)].Value >= WeaponAbility.Block.GetRequiredSecondarySkill(attacker);
|
||||
|
||||
if (canfeint && canblock)
|
||||
{
|
||||
ran = Utility.Random(9);
|
||||
}
|
||||
else if (canblock)
|
||||
{
|
||||
ran = Utility.Random(8);
|
||||
}
|
||||
else
|
||||
{
|
||||
ran = Utility.RandomList(0, 1, 2, 3, 4, 5, 6, 8);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ran)
|
||||
{
|
||||
case 0:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.ArmorIgnore, 1028838);
|
||||
break;
|
||||
case 1:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.BleedAttack, 1028839);
|
||||
break;
|
||||
case 2:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.ConcussionBlow, 1028840);
|
||||
break;
|
||||
case 3:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.CrushingBlow, 1028841);
|
||||
break;
|
||||
case 4:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.DoubleStrike, 1028844);
|
||||
break;
|
||||
case 5:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.MortalStrike, 1028846);
|
||||
break;
|
||||
case 6:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.ParalyzingBlow, 1028848);
|
||||
break;
|
||||
case 7:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.Block, 1028853);
|
||||
break;
|
||||
case 8:
|
||||
m_NewAttack[attacker] = new BladeWeaveRedirect(WeaponAbility.Feint, 1028857);
|
||||
break;
|
||||
default:
|
||||
// should never happen
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return ((BladeWeaveRedirect)m_NewAttack[attacker]).NewAbility.OnBeforeSwing(attacker, defender);
|
||||
}
|
||||
|
||||
public override bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
||||
{
|
||||
BladeWeaveRedirect bwr;
|
||||
if (m_NewAttack.TryGetValue(attacker, out bwr))
|
||||
return bwr.NewAbility.OnBeforeDamage(attacker, defender);
|
||||
else
|
||||
return base.OnBeforeDamage(attacker, defender);
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (CheckMana(attacker, false))
|
||||
{
|
||||
BladeWeaveRedirect bwr;
|
||||
if (m_NewAttack.TryGetValue(attacker, out bwr))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1072841, "#" + bwr.ClilocEntry.ToString());
|
||||
bwr.NewAbility.OnHit(attacker, defender, damage);
|
||||
}
|
||||
else
|
||||
base.OnHit(attacker, defender, damage);
|
||||
|
||||
m_NewAttack.Remove(attacker);
|
||||
ClearCurrentAbility(attacker);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMiss(Mobile attacker, Mobile defender)
|
||||
{
|
||||
BladeWeaveRedirect bwr;
|
||||
if (m_NewAttack.TryGetValue(attacker, out bwr))
|
||||
bwr.NewAbility.OnMiss(attacker, defender);
|
||||
else
|
||||
base.OnMiss(attacker, defender);
|
||||
|
||||
m_NewAttack.Remove(attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
183
Scripts/Abilities/BleedAttack.cs
Normal file
183
Scripts/Abilities/BleedAttack.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Necromancy;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Make your opponent bleed profusely with this wicked use of your weapon.
|
||||
/// When successful, the target will bleed for several seconds, taking damage as time passes for up to ten seconds.
|
||||
/// The rate of damage slows down as time passes, and the blood loss can be completely staunched with the use of bandages.
|
||||
/// </summary>
|
||||
public class BleedAttack : WeaponAbility
|
||||
{
|
||||
private static readonly Dictionary<Mobile, BleedTimer> m_BleedTable = new Dictionary<Mobile, BleedTimer>();
|
||||
|
||||
public BleedAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsBleeding(Mobile m)
|
||||
{
|
||||
return m_BleedTable.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static void BeginBleed(Mobile m, Mobile from, bool splintering = false)
|
||||
{
|
||||
BleedTimer timer = null;
|
||||
|
||||
if (m_BleedTable.ContainsKey(m))
|
||||
{
|
||||
if (splintering)
|
||||
{
|
||||
timer = m_BleedTable[m];
|
||||
timer.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Bleed, 1075829, 1075830, TimeSpan.FromSeconds(10), m, String.Format("{0}\t{1}\t{2}", "1", "10", "2")));
|
||||
|
||||
timer = new BleedTimer(from, m, CheckBloodDrink(from));
|
||||
m_BleedTable[m] = timer;
|
||||
timer.Start();
|
||||
|
||||
from.SendLocalizedMessage(1060159); // Your target is bleeding!
|
||||
m.SendLocalizedMessage(1060160); // You are bleeding!
|
||||
|
||||
if (m is PlayerMobile)
|
||||
{
|
||||
m.LocalOverheadMessage(MessageType.Regular, 0x21, 1060757); // You are bleeding profusely
|
||||
m.NonlocalOverheadMessage(MessageType.Regular, 0x21, 1060758, m.Name); // ~1_NAME~ is bleeding profusely
|
||||
}
|
||||
|
||||
m.PlaySound(0x133);
|
||||
m.FixedParticles(0x377A, 244, 25, 9950, 31, 0, EffectLayer.Waist);
|
||||
}
|
||||
|
||||
public static void DoBleed(Mobile m, Mobile from, int damage, bool blooddrinker)
|
||||
{
|
||||
if (m.Alive && !m.IsDeadBondedPet)
|
||||
{
|
||||
if (!m.Player)
|
||||
damage *= 2;
|
||||
|
||||
m.PlaySound(0x133);
|
||||
AOS.Damage(m, from, damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false);
|
||||
|
||||
if (blooddrinker && from.Hits < from.HitsMax)
|
||||
{
|
||||
from.SendLocalizedMessage(1113606); //The blood drinker effect heals you.
|
||||
from.Heal(damage);
|
||||
}
|
||||
|
||||
Blood blood = new Blood();
|
||||
blood.ItemID = Utility.Random(0x122A, 5);
|
||||
blood.MoveToWorld(m.Location, m.Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
EndBleed(m, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndBleed(Mobile m, bool message)
|
||||
{
|
||||
Timer t = null;
|
||||
|
||||
if (m_BleedTable.ContainsKey(m))
|
||||
{
|
||||
t = m_BleedTable[m];
|
||||
m_BleedTable.Remove(m);
|
||||
}
|
||||
|
||||
if (t == null)
|
||||
return;
|
||||
|
||||
t.Stop();
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Bleed);
|
||||
|
||||
if (message)
|
||||
m.SendLocalizedMessage(1060167); // The bleeding wounds have healed, you are no longer bleeding!
|
||||
}
|
||||
|
||||
public static bool CheckBloodDrink(Mobile attacker)
|
||||
{
|
||||
return attacker.Weapon is BaseWeapon && ((BaseWeapon)attacker.Weapon).WeaponAttributes.BloodDrinker > 0;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
// Necromancers under Lich or Wraith Form are immune to Bleed Attacks.
|
||||
TransformContext context = TransformationSpellHelper.GetContext(defender);
|
||||
|
||||
if ((context != null && (context.Type == typeof(LichFormSpell) || context.Type == typeof(WraithFormSpell))) ||
|
||||
(defender is BaseCreature && ((BaseCreature)defender).BleedImmune) || Server.Spells.Mysticism.StoneFormSpell.CheckImmunity(defender))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1062052); // Your target is not affected by the bleed attack!
|
||||
return;
|
||||
}
|
||||
|
||||
BeginBleed(defender, attacker);
|
||||
}
|
||||
|
||||
private class BleedTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_From;
|
||||
private readonly Mobile m_Mobile;
|
||||
private int m_Count;
|
||||
private int m_MaxCount;
|
||||
private readonly bool m_BloodDrinker;
|
||||
|
||||
public BleedTimer(Mobile from, Mobile m, bool blooddrinker)
|
||||
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
m_From = from;
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
m_BloodDrinker = blooddrinker;
|
||||
|
||||
m_MaxCount = Spells.SkillMasteries.ResilienceSpell.UnderEffects(m) ? 3 : 5;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (!m_Mobile.Alive || m_Mobile.Deleted)
|
||||
{
|
||||
EndBleed(m_Mobile, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
int damage = 0;
|
||||
|
||||
if (!Server.Spells.SkillMasteries.WhiteTigerFormSpell.HasBleedMod(m_From, out damage))
|
||||
damage = Math.Max(1, Utility.RandomMinMax(5 - m_Count, (5 - m_Count) * 2));
|
||||
|
||||
DoBleed(m_Mobile, m_From, damage, m_BloodDrinker);
|
||||
|
||||
if (++m_Count == m_MaxCount)
|
||||
EndBleed(m_Mobile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Scripts/Abilities/Block.cs
Normal file
155
Scripts/Abilities/Block.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises your defenses for a short time. Requires Bushido or Ninjitsu skill.
|
||||
/// </summary>
|
||||
///
|
||||
// spell/melee
|
||||
// 0 parry - 70/80
|
||||
// 100 parry - 40/65
|
||||
// 120 parry - 20/55
|
||||
// .6875
|
||||
|
||||
public class Block : WeaponAbility
|
||||
{
|
||||
private static Dictionary<Mobile, BlockInfo> _Table;
|
||||
|
||||
public Block()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 20; } }
|
||||
|
||||
public override int AccuracyBonus { get { return -15; } }
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public static bool IsBlocking(Mobile m)
|
||||
{
|
||||
return _Table != null && _Table.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static int GetBonus(Mobile targ)
|
||||
{
|
||||
if (targ == null || _Table == null)
|
||||
return 0;
|
||||
|
||||
if (_Table.ContainsKey(targ))
|
||||
return _Table[targ]._DCIBonus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int GetSpellReduction(Mobile m)
|
||||
{
|
||||
if (m == null || _Table == null)
|
||||
return 0;
|
||||
|
||||
if (_Table.ContainsKey(m))
|
||||
{
|
||||
return _Table[m]._SpellReduction;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int GetMeleeReduction(Mobile m)
|
||||
{
|
||||
if (m == null || _Table == null)
|
||||
return 0;
|
||||
|
||||
if (_Table.ContainsKey(m))
|
||||
{
|
||||
return _Table[m]._MeleeReduction;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void BeginBlock(Mobile m, int dciBonus, int spellblock, int meleeblock)
|
||||
{
|
||||
EndBlock(m);
|
||||
|
||||
if (_Table == null)
|
||||
_Table = new Dictionary<Mobile, BlockInfo>();
|
||||
|
||||
BlockInfo info = new BlockInfo(dciBonus, spellblock, meleeblock);
|
||||
_Table[m] = info;
|
||||
|
||||
string args = String.Format("{0}\t{1}\t{2}\t{3}\t{4}", dciBonus, spellblock, meleeblock, "15", "30");
|
||||
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Block, 1151291, 1151292, TimeSpan.FromSeconds(Core.TOL ? 6 : 3), m, args));
|
||||
// Next incoming damage reduced.<br>Defense Chance Increase: +~1_val~%<br>Incoming Spell Damage: -~2_val~%<br>Incoming Attack Damage: -~3_val~%<br>Hit Chance Penalty: ~4_val~%<br>Damage Penalty: ~5_val~%
|
||||
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(Core.TOL ? 6 : 3), () =>
|
||||
{
|
||||
if(IsBlocking(m))
|
||||
EndBlock(m);
|
||||
});
|
||||
}
|
||||
|
||||
public static void EndBlock(Mobile m)
|
||||
{
|
||||
if (_Table != null && _Table.ContainsKey(m))
|
||||
{
|
||||
_Table.Remove(m);
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.Block);
|
||||
|
||||
m.SendLocalizedMessage(1150286); // You no longer try to block the next attack.
|
||||
|
||||
if (_Table.Count == 0)
|
||||
_Table = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1063345); // You block an attack!
|
||||
defender.SendLocalizedMessage(1063346); // Your attack was blocked!
|
||||
|
||||
attacker.FixedParticles(0x37C4, 1, 16, 0x251D, 0x39D, 0x3, EffectLayer.RightHand);
|
||||
|
||||
int parry = (int)attacker.Skills[SkillName.Parry].Value;
|
||||
|
||||
bool creature = attacker is BaseCreature;
|
||||
double skill = creature ? attacker.Skills[SkillName.Bushido].Value :
|
||||
Math.Max(attacker.Skills[SkillName.Bushido].Value, attacker.Skills[SkillName.Ninjitsu].Value);
|
||||
|
||||
int dcibonus = (int)(10.0 * ((skill - 50.0) / 70.0 + 5));
|
||||
int spellblock = parry <= 70 ? 70 : parry <= 100 ? 40 : 20;
|
||||
int meleeblock = parry <= 70 ? 80 : parry <= 100 ? 65 : 55;
|
||||
|
||||
BeginBlock(attacker, dcibonus, spellblock, meleeblock);
|
||||
|
||||
if(creature)
|
||||
PetTrainingHelper.OnWeaponAbilityUsed((BaseCreature)attacker, SkillName.Bushido);
|
||||
}
|
||||
|
||||
private class BlockInfo
|
||||
{
|
||||
public readonly int _DCIBonus;
|
||||
public readonly int _SpellReduction;
|
||||
public readonly int _MeleeReduction;
|
||||
|
||||
public BlockInfo(int bonus, int spellred, int meleered)
|
||||
{
|
||||
_DCIBonus = bonus;
|
||||
_SpellReduction = spellred;
|
||||
_MeleeReduction = meleered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Scripts/Abilities/ColdWind.cs
Normal file
103
Scripts/Abilities/ColdWind.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Server.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Currently on EA, this is only available for Creatures
|
||||
/// </summary>
|
||||
public class ColdWind : WeaponAbility
|
||||
{
|
||||
private static readonly Dictionary<Mobile, ExpireTimer> m_Table = new Dictionary<Mobile, ExpireTimer>();
|
||||
|
||||
public ColdWind()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
public override double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.5;
|
||||
}
|
||||
}
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
if (attacker.Map == null || attacker.Map == Map.Internal)
|
||||
return;
|
||||
|
||||
ExpireTimer timer = null;
|
||||
|
||||
if (m_Table.ContainsKey(defender))
|
||||
timer = m_Table[defender];
|
||||
|
||||
if (timer != null)
|
||||
{
|
||||
timer.DoExpire();
|
||||
defender.SendLocalizedMessage(1070831); // The freezing wind continues to blow!
|
||||
}
|
||||
else
|
||||
defender.SendLocalizedMessage(1070832); // An icy wind surrounds you, freezing your lungs as you breathe!
|
||||
|
||||
timer = new ExpireTimer(defender, attacker);
|
||||
timer.Start();
|
||||
m_Table[defender] = timer;
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
private readonly Mobile m_From;
|
||||
private int m_Count;
|
||||
|
||||
public ExpireTimer(Mobile m, Mobile from)
|
||||
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_From = from;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
public void DoExpire()
|
||||
{
|
||||
Stop();
|
||||
m_Table.Remove(m_Mobile);
|
||||
}
|
||||
|
||||
public void DrainLife()
|
||||
{
|
||||
if (m_Mobile.Alive)
|
||||
{
|
||||
AOS.Damage(m_Mobile, m_From, 14, 0, 0, 100, 0, 0);
|
||||
Effects.SendPacket(m_Mobile.Location, m_Mobile.Map, new ParticleEffect(EffectType.FixedFrom, m_Mobile.Serial, Serial.Zero, 0x374A, m_Mobile.Location, m_Mobile.Location, 1, 15, false, false, 97, 0, 4, 9502, 1, m_Mobile.Serial, 163, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
DoExpire();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
DrainLife();
|
||||
|
||||
if (++m_Count >= 5)
|
||||
{
|
||||
DoExpire();
|
||||
m_Mobile.SendLocalizedMessage(1070830); // The icy wind dissipates.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Scripts/Abilities/ConcussionBlow.cs
Normal file
57
Scripts/Abilities/ConcussionBlow.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// This devastating strike is most effective against those who are in good health and whose reserves of mana are low, or vice versa.
|
||||
/// </summary>
|
||||
public class ConcussionBlow : WeaponAbility
|
||||
{
|
||||
public ConcussionBlow()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
public override bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return false;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060165); // You have delivered a concussion!
|
||||
defender.SendLocalizedMessage(1060166); // You feel disoriented!
|
||||
|
||||
defender.PlaySound(0x213);
|
||||
defender.FixedParticles(0x377A, 1, 32, 9949, 1153, 0, EffectLayer.Head);
|
||||
|
||||
Effects.SendMovingParticles(new Entity(Serial.Zero, new Point3D(defender.X, defender.Y, defender.Z + 10), defender.Map), new Entity(Serial.Zero, new Point3D(defender.X, defender.Y, defender.Z + 20), defender.Map), 0x36FE, 1, 0, false, false, 1133, 3, 9501, 1, 0, EffectLayer.Waist, 0x100);
|
||||
|
||||
int damage = 10; // Base damage is 10.
|
||||
|
||||
if (defender.HitsMax > 0)
|
||||
{
|
||||
double hitsPercent = ((double)defender.Hits / (double)defender.HitsMax) * 100.0;
|
||||
|
||||
double manaPercent = 0;
|
||||
|
||||
if (defender.ManaMax > 0)
|
||||
manaPercent = ((double)defender.Mana / (double)defender.ManaMax) * 100.0;
|
||||
|
||||
damage += Math.Min((int)(Math.Abs(hitsPercent - manaPercent) / 4), 20);
|
||||
}
|
||||
|
||||
// Total damage is 10 + (0~20) = 10~30, physical, non-resistable.
|
||||
|
||||
defender.Damage(damage, attacker);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Scripts/Abilities/CrushingBlow.cs
Normal file
44
Scripts/Abilities/CrushingBlow.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Also known as the Haymaker, this attack dramatically increases the damage done by a weapon reaching its mark.
|
||||
/// </summary>
|
||||
public class CrushingBlow : WeaponAbility
|
||||
{
|
||||
public CrushingBlow()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
public override double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.5;
|
||||
}
|
||||
}
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060090); // You have delivered a crushing blow!
|
||||
defender.SendLocalizedMessage(1060091); // You take extra damage from the crushing attack!
|
||||
|
||||
defender.PlaySound(0x1E1);
|
||||
defender.FixedParticles(0, 1, 0, 9946, EffectLayer.Head);
|
||||
|
||||
Effects.SendMovingParticles(new Entity(Serial.Zero, new Point3D(defender.X, defender.Y, defender.Z + 50), defender.Map), new Entity(Serial.Zero, new Point3D(defender.X, defender.Y, defender.Z + 20), defender.Map), 0xFB4, 1, 0, false, false, 0, 3, 9501, 1, 0, EffectLayer.Head, 0x100);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Scripts/Abilities/DefenseMastery.cs
Normal file
100
Scripts/Abilities/DefenseMastery.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises your physical resistance for a short time while lowering your ability to inflict damage. Requires Bushido or Ninjitsu skill.
|
||||
/// </summary>
|
||||
public class DefenseMastery : WeaponAbility
|
||||
{
|
||||
private static readonly Hashtable m_Table = new Hashtable();
|
||||
public DefenseMastery()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public static bool GetMalus(Mobile targ, ref int damageMalus)
|
||||
{
|
||||
DefenseMasteryInfo info = m_Table[targ] as DefenseMasteryInfo;
|
||||
|
||||
if (info == null)
|
||||
return false;
|
||||
|
||||
damageMalus = info.m_DamageMalus;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1063353); // You perform a masterful defense!
|
||||
|
||||
attacker.FixedParticles(0x375A, 1, 17, 0x7F2, 0x3E8, 0x3, EffectLayer.Waist);
|
||||
|
||||
int modifier = (int)(30.0 * ((Math.Max(attacker.Skills[SkillName.Bushido].Value, attacker.Skills[SkillName.Ninjitsu].Value) - 50.0) / 70.0));
|
||||
|
||||
DefenseMasteryInfo info = m_Table[attacker] as DefenseMasteryInfo;
|
||||
|
||||
if (info != null)
|
||||
EndDefense((object)info);
|
||||
|
||||
ResistanceMod mod = new ResistanceMod(ResistanceType.Physical, 50 + modifier);
|
||||
attacker.AddResistanceMod(mod);
|
||||
|
||||
info = new DefenseMasteryInfo(attacker, 80 - modifier, mod);
|
||||
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(3.0), new TimerStateCallback(EndDefense), info);
|
||||
|
||||
m_Table[attacker] = info;
|
||||
|
||||
attacker.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
|
||||
private static void EndDefense(object state)
|
||||
{
|
||||
DefenseMasteryInfo info = (DefenseMasteryInfo)state;
|
||||
|
||||
if (info.m_Mod != null)
|
||||
info.m_From.RemoveResistanceMod(info.m_Mod);
|
||||
|
||||
if (info.m_Timer != null)
|
||||
info.m_Timer.Stop();
|
||||
|
||||
// No message is sent to the player.
|
||||
|
||||
m_Table.Remove(info.m_From);
|
||||
|
||||
info.m_From.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
|
||||
private class DefenseMasteryInfo
|
||||
{
|
||||
public readonly Mobile m_From;
|
||||
public readonly int m_DamageMalus;
|
||||
public readonly ResistanceMod m_Mod;
|
||||
public Timer m_Timer;
|
||||
public DefenseMasteryInfo(Mobile from, int damageMalus, ResistanceMod mod)
|
||||
{
|
||||
m_From = from;
|
||||
m_DamageMalus = damageMalus;
|
||||
m_Mod = mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Scripts/Abilities/Disarm.cs
Normal file
121
Scripts/Abilities/Disarm.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// This attack allows you to disarm your foe.
|
||||
/// Now in Age of Shadows, a successful Disarm leaves the victim unable to re-arm another weapon for several seconds.
|
||||
/// </summary>
|
||||
public class Disarm : WeaponAbility
|
||||
{
|
||||
public static readonly TimeSpan BlockEquipDuration = TimeSpan.FromSeconds(5.0);
|
||||
public Disarm()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RequiresSecondarySkill(Mobile from)
|
||||
{
|
||||
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return false;
|
||||
|
||||
return weapon.Skill != SkillName.Wrestling;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
if (IsImmune(defender))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1111827); // Your opponent is gripping their weapon too tightly to be disarmed.
|
||||
defender.SendLocalizedMessage(1111828); // You will not be caught off guard by another disarm attack for some time.
|
||||
return;
|
||||
}
|
||||
|
||||
Item toDisarm = defender.FindItemOnLayer(Layer.OneHanded);
|
||||
|
||||
if (toDisarm == null || !toDisarm.Movable)
|
||||
toDisarm = defender.FindItemOnLayer(Layer.TwoHanded);
|
||||
|
||||
Container pack = defender.Backpack;
|
||||
|
||||
if (pack == null || (toDisarm != null && !toDisarm.Movable))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent.
|
||||
}
|
||||
else if (toDisarm == null || toDisarm is BaseShield || toDisarm is Spellbook && !Core.ML)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060849); // Your target is already unarmed!
|
||||
}
|
||||
else if (this.CheckMana(attacker, true))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060092); // You disarm their weapon!
|
||||
defender.SendLocalizedMessage(1060093); // Your weapon has been disarmed!
|
||||
|
||||
defender.PlaySound(0x3B9);
|
||||
defender.FixedParticles(0x37BE, 232, 25, 9948, EffectLayer.LeftHand);
|
||||
|
||||
pack.DropItem(toDisarm);
|
||||
|
||||
BuffInfo.AddBuff(defender, new BuffInfo( BuffIcon.NoRearm, 1075637, BlockEquipDuration, defender));
|
||||
|
||||
BaseWeapon.BlockEquip(defender, BlockEquipDuration);
|
||||
|
||||
if (defender is BaseCreature && _AutoRearms.Any(t => t == defender.GetType()))
|
||||
{
|
||||
Timer.DelayCall(BlockEquipDuration + TimeSpan.FromSeconds(Utility.RandomMinMax(3, 10)), () =>
|
||||
{
|
||||
if (toDisarm != null && !toDisarm.Deleted && toDisarm.IsChildOf(defender.Backpack))
|
||||
defender.EquipItem(toDisarm);
|
||||
});
|
||||
}
|
||||
|
||||
if(Core.SA)
|
||||
AddImmunity(defender, Core.TOL && attacker.Weapon is Fists ? TimeSpan.FromSeconds(10) : TimeSpan.FromSeconds(15));
|
||||
}
|
||||
}
|
||||
|
||||
private Type[] _AutoRearms =
|
||||
{
|
||||
typeof(BritannianInfantry)
|
||||
};
|
||||
|
||||
public static List<Mobile> _Immunity;
|
||||
|
||||
public static bool IsImmune(Mobile m)
|
||||
{
|
||||
return _Immunity != null && _Immunity.Contains(m);
|
||||
}
|
||||
|
||||
public static void AddImmunity(Mobile m, TimeSpan duration)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
_Immunity = new List<Mobile>();
|
||||
|
||||
_Immunity.Add(m);
|
||||
|
||||
Timer.DelayCall<Mobile>(duration, mob =>
|
||||
{
|
||||
if (_Immunity != null && _Immunity.Contains(mob))
|
||||
_Immunity.Remove(mob);
|
||||
}, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
141
Scripts/Abilities/Dismount.cs
Normal file
141
Scripts/Abilities/Dismount.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Perfect for the foot-soldier, the Dismount special attack can unseat a mounted opponent.
|
||||
/// The fighter using this ability must be on his own two feet and not in the saddle of a steed
|
||||
/// (with one exception: players may use a lance to dismount other players while mounted).
|
||||
/// If it works, the target will be knocked off his own mount and will take some extra damage from the fall!
|
||||
/// </summary>
|
||||
public class Dismount : WeaponAbility
|
||||
{
|
||||
public static readonly TimeSpan DefenderRemountDelay = TimeSpan.FromSeconds(10.0);// TODO: Taken from bola script, needs to be verified
|
||||
public static readonly TimeSpan AttackerRemountDelay = TimeSpan.FromSeconds(3.0);
|
||||
public Dismount()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return Core.TOL ? 25 : 20;
|
||||
}
|
||||
}
|
||||
public override bool Validate(Mobile from)
|
||||
{
|
||||
if (!base.Validate(from))
|
||||
return false;
|
||||
|
||||
if ( (from.Mounted || from.Flying) && !(from.Weapon is Lance) && !(from.Weapon is GargishLance) )
|
||||
{
|
||||
from.SendLocalizedMessage(1061283); // You cannot perform that attack while mounted or flying!
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker))
|
||||
return;
|
||||
|
||||
if (defender is ChaosDragoon || defender is ChaosDragoonElite)
|
||||
return;
|
||||
|
||||
if (CheckMountedNoLance(attacker, defender)) // TODO: Should there be a message here?
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
IMount mount = defender.Mount;
|
||||
|
||||
if (mount == null && !defender.Flying && (!Core.ML || !Server.Spells.Ninjitsu.AnimalForm.UnderTransformation(defender)))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060848); // This attack only works on mounted or flying targets
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Core.ML && attacker is LesserHiryu && 0.8 >= Utility.RandomDouble())
|
||||
{
|
||||
return; //Lesser Hiryu have an 80% chance of missing this attack
|
||||
}
|
||||
|
||||
defender.PlaySound(0x140);
|
||||
defender.FixedParticles(0x3728, 10, 15, 9955, EffectLayer.Waist);
|
||||
|
||||
int delay = Core.TOL && attacker.Weapon is BaseRanged ? 8 : 10;
|
||||
|
||||
DoDismount(attacker, defender, mount, delay);
|
||||
|
||||
if (!attacker.Mounted)
|
||||
{
|
||||
AOS.Damage(defender, attacker, Utility.RandomMinMax(15, 25), 100, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DoDismount(Mobile attacker, Mobile defender, IMount mount, int delay, BlockMountType type = BlockMountType.Dazed)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060082); // The force of your attack has dislodged them from their mount!
|
||||
|
||||
if (defender is PlayerMobile)
|
||||
{
|
||||
if (Core.ML && Server.Spells.Ninjitsu.AnimalForm.UnderTransformation(defender))
|
||||
{
|
||||
defender.SendLocalizedMessage(1114066, attacker.Name); // ~1_NAME~ knocked you out of animal form!
|
||||
}
|
||||
else if (defender.Flying)
|
||||
{
|
||||
defender.SendLocalizedMessage(1113590, attacker.Name); // You have been grounded by ~1_NAME~!
|
||||
}
|
||||
else if (defender.Mounted)
|
||||
{
|
||||
defender.SendLocalizedMessage(1060083); // You fall off of your mount and take damage!
|
||||
}
|
||||
|
||||
((PlayerMobile)defender).SetMountBlock(type, TimeSpan.FromSeconds(delay), true);
|
||||
}
|
||||
else if (mount != null)
|
||||
{
|
||||
mount.Rider = null;
|
||||
}
|
||||
|
||||
if (attacker is PlayerMobile)
|
||||
{
|
||||
((PlayerMobile)attacker).SetMountBlock(BlockMountType.DismountRecovery, TimeSpan.FromSeconds(Core.TOL && attacker.Weapon is BaseRanged ? 8 : 10), false);
|
||||
}
|
||||
else if (Core.ML && attacker is BaseCreature)
|
||||
{
|
||||
BaseCreature bc = attacker as BaseCreature;
|
||||
|
||||
if (bc.ControlMaster is PlayerMobile)
|
||||
{
|
||||
PlayerMobile pm = bc.ControlMaster as PlayerMobile;
|
||||
|
||||
pm.SetMountBlock(BlockMountType.DismountRecovery, TimeSpan.FromSeconds(delay), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckMountedNoLance(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!attacker.Mounted && !attacker.Flying)
|
||||
return false;
|
||||
|
||||
if ((attacker.Weapon is Lance || attacker.Weapon is GargishLance) && (defender.Weapon is Lance || defender.Weapon is GargishLance))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Scripts/Abilities/Disrobe.cs
Normal file
53
Scripts/Abilities/Disrobe.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// This attack allows you to disrobe your foe.
|
||||
/// </summary>
|
||||
public class Disrobe : WeaponAbility
|
||||
{
|
||||
public static readonly TimeSpan BlockEquipDuration = TimeSpan.FromSeconds(5.0);
|
||||
public Disrobe()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}// Not Sure what amount of mana a creature uses.
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
Item toDisrobe = defender.FindItemOnLayer(Layer.InnerTorso);
|
||||
|
||||
if (toDisrobe == null || !toDisrobe.Movable)
|
||||
toDisrobe = defender.FindItemOnLayer(Layer.OuterTorso);
|
||||
|
||||
Container pack = defender.Backpack;
|
||||
|
||||
if (pack == null || toDisrobe == null || !toDisrobe.Movable)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent.
|
||||
}
|
||||
else if (this.CheckMana(attacker, true))
|
||||
{
|
||||
//attacker.SendLocalizedMessage( 1060092 ); // You disarm their weapon!
|
||||
defender.SendLocalizedMessage(1062002); // You can no longer wear your ~1_ARMOR~
|
||||
|
||||
defender.PlaySound(0x3B9);
|
||||
//defender.FixedParticles( 0x37BE, 232, 25, 9948, EffectLayer.InnerTorso );
|
||||
|
||||
pack.DropItem(toDisrobe);
|
||||
|
||||
BaseWeapon.BlockEquip(defender, BlockEquipDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Scripts/Abilities/DoubleShot.cs
Normal file
81
Scripts/Abilities/DoubleShot.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Send two arrows flying at your opponent if you're mounted. Requires Bushido or Ninjitsu skill.
|
||||
/// </summary>
|
||||
public class DoubleShot : WeaponAbility
|
||||
{
|
||||
public DoubleShot()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return Core.TOL ? 30 : 35;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
||||
{
|
||||
BaseWeapon wep = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (wep != null)
|
||||
wep.ProcessingMultipleHits = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
Use(attacker, defender);
|
||||
}
|
||||
|
||||
public override void OnMiss(Mobile attacker, Mobile defender)
|
||||
{
|
||||
Use(attacker, defender);
|
||||
}
|
||||
|
||||
public override bool Validate(Mobile from)
|
||||
{
|
||||
if (base.Validate(from))
|
||||
{
|
||||
if (from.Mounted)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1070770); // You can only execute this attack while mounted!
|
||||
ClearCurrentAbility(from);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Use(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true) || attacker.Weapon == null) //sanity
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1063348); // You launch two shots at once!
|
||||
defender.SendLocalizedMessage(1063349); // You're attacked with a barrage of shots!
|
||||
|
||||
defender.FixedParticles(0x37B9, 1, 19, 0x251D, EffectLayer.Waist);
|
||||
|
||||
attacker.Weapon.OnSwing(attacker, defender);
|
||||
|
||||
if (attacker.Weapon is BaseWeapon)
|
||||
((BaseWeapon)attacker.Weapon).ProcessingMultipleHits = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Scripts/Abilities/DoubleStrike.cs
Normal file
68
Scripts/Abilities/DoubleStrike.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// The highly skilled warrior can use this special attack to make two quick swings in succession.
|
||||
/// Landing both blows would be devastating!
|
||||
/// </summary>
|
||||
public class DoubleStrike : WeaponAbility
|
||||
{
|
||||
public override int BaseMana { get { return 30; } }
|
||||
public override double DamageScalar { get { return 0.9; } }
|
||||
|
||||
public override bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
||||
{
|
||||
BaseWeapon wep = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (wep != null)
|
||||
wep.InDoubleStrike = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
BaseWeapon weapon = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If no combatant, wrong map, one of us is a ghost, or cannot see, or deleted, then stop combat
|
||||
if (defender.Deleted || attacker.Deleted || defender.Map != attacker.Map || !defender.Alive ||
|
||||
!attacker.Alive || !attacker.CanSee(defender))
|
||||
{
|
||||
weapon.InDoubleStrike = false;
|
||||
attacker.Combatant = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!attacker.InRange(defender, weapon.MaxRange))
|
||||
{
|
||||
weapon.InDoubleStrike = false;
|
||||
return;
|
||||
}
|
||||
|
||||
attacker.SendLocalizedMessage(1060084); // You attack with lightning speed!
|
||||
defender.SendLocalizedMessage(1060085); // Your attacker strikes with lightning speed!
|
||||
|
||||
defender.PlaySound(0x3BB);
|
||||
defender.FixedEffect(0x37B9, 244, 25);
|
||||
|
||||
if (attacker.InLOS(defender))
|
||||
{
|
||||
attacker.RevealingAction();
|
||||
attacker.NextCombatTime = Core.TickCount + (int)weapon.OnSwing(attacker, defender).TotalMilliseconds;
|
||||
}
|
||||
|
||||
weapon.InDoubleStrike = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
137
Scripts/Abilities/DualWield.cs
Normal file
137
Scripts/Abilities/DualWield.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Attack faster as you swing with both weapons.
|
||||
/// </summary>
|
||||
public class DualWield : WeaponAbility
|
||||
{
|
||||
private static readonly Dictionary<Mobile, DualWieldTimer> m_Registry = new Dictionary<Mobile, DualWieldTimer>();
|
||||
|
||||
public DualWield()
|
||||
{
|
||||
}
|
||||
|
||||
public static Dictionary<Mobile, DualWieldTimer> Registry { get { return m_Registry; } }
|
||||
public override int BaseMana { get { return 20; } }
|
||||
|
||||
public static readonly TimeSpan Duration = TimeSpan.FromSeconds(8);
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
if (HasRegistry(attacker))
|
||||
{
|
||||
var timer = m_Registry[attacker];
|
||||
|
||||
if (timer.DualHitChance < .75)
|
||||
{
|
||||
timer.Expires += TimeSpan.FromSeconds(2);
|
||||
timer.DualHitChance += .25;
|
||||
|
||||
BuffInfo.RemoveBuff(attacker, BuffIcon.DualWield);
|
||||
BuffInfo.AddBuff(attacker, new BuffInfo(BuffIcon.DualWield, 1151294, 1151293, timer.Expires - DateTime.UtcNow, attacker, (timer.DualHitChance * 100).ToString()));
|
||||
|
||||
attacker.SendLocalizedMessage(timer.DualHitChance == .75 ? 1150283 : 1150282); // Dual wield level increased to peak! : Dual wield level increased!
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
attacker.SendLocalizedMessage(1150281); // You begin trying to strike with both your weapons at once.
|
||||
attacker.SendLocalizedMessage(1150284, true, Duration.TotalSeconds.ToString()); // Remaining Duration (seconds):
|
||||
|
||||
DualWieldTimer t = new DualWieldTimer(attacker, .25);
|
||||
BuffInfo.AddBuff(attacker, new BuffInfo(BuffIcon.DualWield, 1151294, 1151293, Duration, attacker, "25"));
|
||||
|
||||
Registry[attacker] = t;
|
||||
|
||||
attacker.FixedParticles(0x3779, 1, 15, 0x7F6, 0x3E8, 3, EffectLayer.LeftHand);
|
||||
Effects.PlaySound(attacker.Location, attacker.Map, 0x524);
|
||||
}
|
||||
|
||||
public static bool HasRegistry(Mobile attacker)
|
||||
{
|
||||
return m_Registry.ContainsKey(attacker);
|
||||
}
|
||||
|
||||
public static void RemoveFromRegistry(Mobile from)
|
||||
{
|
||||
if (m_Registry.ContainsKey(from))
|
||||
{
|
||||
from.SendLocalizedMessage(1150285); // You no longer try to strike with both weapons at the same time.
|
||||
|
||||
m_Registry[from].Stop();
|
||||
m_Registry.Remove(from);
|
||||
|
||||
if(from.Weapon is BaseWeapon)
|
||||
((BaseWeapon)from.Weapon).ProcessingMultipleHits = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from BaseWeapon, on successful hit
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
public static void DoHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (HasRegistry(attacker) && attacker.Weapon is BaseWeapon && m_Registry[attacker].DualHitChance > Utility.RandomDouble())
|
||||
{
|
||||
BaseWeapon wep = (BaseWeapon)attacker.Weapon;
|
||||
|
||||
if (!m_Registry[attacker].SecondHit)
|
||||
{
|
||||
wep.ProcessingMultipleHits = true;
|
||||
m_Registry[attacker].SecondHit = true;
|
||||
wep.OnHit(attacker, defender, .6);
|
||||
m_Registry[attacker].SecondHit = false;
|
||||
}
|
||||
else if (wep.ProcessingMultipleHits)
|
||||
{
|
||||
wep.EndDualWield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DualWieldTimer : Timer
|
||||
{
|
||||
public Mobile Owner { get; set; }
|
||||
public double DualHitChance { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
public bool SecondHit { get; set; }
|
||||
|
||||
private readonly TimeSpan Duration = TimeSpan.FromSeconds(8);
|
||||
|
||||
public DualWieldTimer(Mobile owner, double dualHitChance)
|
||||
: base(TimeSpan.FromMilliseconds(250), TimeSpan.FromMilliseconds(250))
|
||||
{
|
||||
Owner = owner;
|
||||
DualHitChance = dualHitChance;
|
||||
|
||||
Expires = DateTime.UtcNow + Duration;
|
||||
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
Start();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (DateTime.UtcNow > Expires)
|
||||
{
|
||||
RemoveFromRegistry(Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
319
Scripts/Abilities/Enhancement.cs
Normal file
319
Scripts/Abilities/Enhancement.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class EnhancementAttributes
|
||||
{
|
||||
public string Title { get; set; }
|
||||
|
||||
public AosAttributes Attributes { get; private set; }
|
||||
public AosWeaponAttributes WeaponAttributes { get; private set; }
|
||||
public AosArmorAttributes ArmorAttributes { get; private set; }
|
||||
public SAAbsorptionAttributes AbsorptionAttributes { get; private set; }
|
||||
public ExtendedWeaponAttributes ExtendedWeaponAttributes { get; private set; }
|
||||
|
||||
public EnhancementAttributes(string title)
|
||||
{
|
||||
this.Title = title;
|
||||
this.Attributes = new AosAttributes(null);
|
||||
this.WeaponAttributes = new AosWeaponAttributes(null);
|
||||
this.ArmorAttributes = new AosArmorAttributes(null);
|
||||
this.AbsorptionAttributes = new SAAbsorptionAttributes(null);
|
||||
this.ExtendedWeaponAttributes = new ExtendedWeaponAttributes(null);
|
||||
}
|
||||
}
|
||||
|
||||
public class Enhancement
|
||||
{
|
||||
public static Dictionary<Mobile, List<EnhancementAttributes>> EnhancementList = new Dictionary<Mobile, List<EnhancementAttributes>>();
|
||||
|
||||
public static bool AddMobile(Mobile m)
|
||||
{
|
||||
if (!EnhancementList.ContainsKey(m))
|
||||
{
|
||||
EnhancementList.Add(m, new List<EnhancementAttributes>());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the mobile and/or attributes from the dictionary
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <param name="title">null or default value will remove the entire entry. Add the title arg to remove only that element from the list.</param>
|
||||
/// <returns></returns>
|
||||
public static bool RemoveMobile(Mobile m, string title = null)
|
||||
{
|
||||
if (EnhancementList.ContainsKey(m))
|
||||
{
|
||||
if (title != null)
|
||||
{
|
||||
EnhancementAttributes match = EnhancementList[m].FirstOrDefault(attrs => attrs.Title == title);
|
||||
|
||||
if (match != null && EnhancementList[m].Contains(match))
|
||||
{
|
||||
if(match.Attributes.BonusStr > 0)
|
||||
m.RemoveStatMod("MagicalEnhancementStr");
|
||||
|
||||
if (match.Attributes.BonusDex > 0)
|
||||
m.RemoveStatMod("MagicalEnhancementDex");
|
||||
|
||||
if (match.Attributes.BonusInt > 0)
|
||||
m.RemoveStatMod("MagicalEnhancementInt");
|
||||
|
||||
EnhancementList[m].Remove(match);
|
||||
}
|
||||
}
|
||||
|
||||
if(EnhancementList[m].Count == 0 || title == null)
|
||||
EnhancementList.Remove(m);
|
||||
|
||||
m.CheckStatTimers();
|
||||
m.UpdateResistances();
|
||||
m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);
|
||||
|
||||
m.Items.ForEach(i => i.InvalidateProperties());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int GetValue(Mobile m, AosAttribute att)
|
||||
{
|
||||
if (m == null)
|
||||
return 0;
|
||||
|
||||
if (EnhancementList.ContainsKey(m))
|
||||
{
|
||||
int value = 0;
|
||||
EnhancementList[m].ForEach(attrs => value += attrs.Attributes[att]);
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetValue(Mobile m, AosAttribute att, int value, string title)
|
||||
{
|
||||
if (!EnhancementList.ContainsKey(m))
|
||||
AddMobile(m);
|
||||
|
||||
if (att == AosAttribute.BonusStr)
|
||||
{
|
||||
m.RemoveStatMod("MagicalEnhancementStr");
|
||||
m.AddStatMod(new StatMod(StatType.Str, "MagicalEnhancementStr", value, TimeSpan.Zero));
|
||||
}
|
||||
else if (att == AosAttribute.BonusDex)
|
||||
{
|
||||
m.RemoveStatMod("MagicalEnhancementDex");
|
||||
m.AddStatMod(new StatMod(StatType.Dex, "MagicalEnhancementDex", value, TimeSpan.Zero));
|
||||
}
|
||||
else if (att == AosAttribute.BonusInt)
|
||||
{
|
||||
m.RemoveStatMod("MagicalEnhancementInt");
|
||||
m.AddStatMod(new StatMod(StatType.Int, "MagicalEnhancementInt", value, TimeSpan.Zero));
|
||||
}
|
||||
|
||||
EnhancementAttributes match = EnhancementList[m].FirstOrDefault(attrs => attrs.Title == title);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
match.Attributes[att] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
match = new EnhancementAttributes(title);
|
||||
match.Attributes[att] = value;
|
||||
|
||||
EnhancementList[m].Add(match);
|
||||
}
|
||||
|
||||
m.CheckStatTimers();
|
||||
m.UpdateResistances();
|
||||
m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);
|
||||
}
|
||||
|
||||
public static int GetValue(Mobile m, AosWeaponAttribute att)
|
||||
{
|
||||
if (m == null)
|
||||
return 0;
|
||||
|
||||
if (EnhancementList.ContainsKey(m))
|
||||
{
|
||||
int value = 0;
|
||||
EnhancementList[m].ForEach(attrs => value += attrs.WeaponAttributes[att]);
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetValue(Mobile m, AosWeaponAttribute att, int value, string title)
|
||||
{
|
||||
if (!EnhancementList.ContainsKey(m))
|
||||
AddMobile(m);
|
||||
|
||||
EnhancementAttributes match = EnhancementList[m].FirstOrDefault(attrs => attrs.Title == title);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
match.WeaponAttributes[att] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
match = new EnhancementAttributes(title);
|
||||
match.WeaponAttributes[att] = value;
|
||||
|
||||
EnhancementList[m].Add(match);
|
||||
}
|
||||
|
||||
m.CheckStatTimers();
|
||||
m.UpdateResistances();
|
||||
m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);
|
||||
}
|
||||
|
||||
public static int GetValue(Mobile m, AosArmorAttribute att)
|
||||
{
|
||||
if (m == null)
|
||||
return 0;
|
||||
|
||||
if (EnhancementList.ContainsKey(m))
|
||||
{
|
||||
int value = 0;
|
||||
EnhancementList[m].ForEach(attrs => value += attrs.ArmorAttributes[att]);
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetValue(Mobile m, AosArmorAttribute att, int value, string title)
|
||||
{
|
||||
if (!EnhancementList.ContainsKey(m))
|
||||
AddMobile(m);
|
||||
|
||||
EnhancementAttributes match = EnhancementList[m].FirstOrDefault(attrs => attrs.Title == title);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
match.ArmorAttributes[att] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
match = new EnhancementAttributes(title);
|
||||
match.ArmorAttributes[att] = value;
|
||||
|
||||
EnhancementList[m].Add(match);
|
||||
}
|
||||
|
||||
m.CheckStatTimers();
|
||||
m.UpdateResistances();
|
||||
m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);
|
||||
}
|
||||
|
||||
public static int GetValue(Mobile m, SAAbsorptionAttribute att)
|
||||
{
|
||||
if (m == null)
|
||||
return 0;
|
||||
|
||||
if (EnhancementList.ContainsKey(m))
|
||||
{
|
||||
int value = 0;
|
||||
EnhancementList[m].ForEach(attrs => value += attrs.AbsorptionAttributes[att]);
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetValue(Mobile m, SAAbsorptionAttribute att, int value, string title)
|
||||
{
|
||||
if (!EnhancementList.ContainsKey(m))
|
||||
AddMobile(m);
|
||||
|
||||
EnhancementAttributes match = EnhancementList[m].FirstOrDefault(attrs => attrs.Title == title);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
match.AbsorptionAttributes[att] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
match = new EnhancementAttributes(title);
|
||||
match.AbsorptionAttributes[att] = value;
|
||||
|
||||
EnhancementList[m].Add(match);
|
||||
}
|
||||
|
||||
m.CheckStatTimers();
|
||||
m.UpdateResistances();
|
||||
m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);
|
||||
}
|
||||
|
||||
public static int GetValue(Mobile m, ExtendedWeaponAttribute att)
|
||||
{
|
||||
if (m == null)
|
||||
return 0;
|
||||
|
||||
if (EnhancementList.ContainsKey(m))
|
||||
{
|
||||
int value = 0;
|
||||
EnhancementList[m].ForEach(attrs => value += attrs.ExtendedWeaponAttributes[att]);
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetValue(Mobile m, ExtendedWeaponAttribute att, int value, string title)
|
||||
{
|
||||
if (!EnhancementList.ContainsKey(m))
|
||||
AddMobile(m);
|
||||
|
||||
EnhancementAttributes match = EnhancementList[m].FirstOrDefault(attrs => attrs.Title == title);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
match.ExtendedWeaponAttributes[att] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
match = new EnhancementAttributes(title);
|
||||
match.ExtendedWeaponAttributes[att] = value;
|
||||
|
||||
EnhancementList[m].Add(match);
|
||||
}
|
||||
|
||||
m.CheckStatTimers();
|
||||
m.UpdateResistances();
|
||||
m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
AOS.cs
|
||||
MagicalEnhancements.GetValue( m, attribute );
|
||||
Usage of setting total (intended use)
|
||||
MagicalEnhancements.SetValue( m, AosAttribute.Luck, 50 );
|
||||
Example of a timed stackable Enhancement (supports to an extent)
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public void Luckboon()
|
||||
{
|
||||
MagicalEnhancements.AddMobile( m );
|
||||
MagicalEnhancements.EnhancementList[m].Attributes.Luck += 200;
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 30 ), new TimerCallback( Expire ) );
|
||||
}
|
||||
|
||||
private void Expire()
|
||||
{
|
||||
if ( m_Mobile != null && MagicalEnhancements.EnhancementList.ContainsKey( m ) )
|
||||
MagicalEnhancements.EnhancementList[m].Attributes.Luck -= 200;
|
||||
}
|
||||
*/
|
||||
122
Scripts/Abilities/EnhancementTimer.cs
Normal file
122
Scripts/Abilities/EnhancementTimer.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
// Created by Peoharen
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class EnhancementTimer : Timer
|
||||
{
|
||||
private readonly ArrayList AL = new ArrayList();
|
||||
private readonly Mobile m_Mobile;
|
||||
private readonly string m_Title;
|
||||
private int m_Duration;
|
||||
|
||||
public EnhancementTimer(Mobile m, int duration, string title, params object[] args)
|
||||
: base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
|
||||
{
|
||||
if (args.Length < 1 || (args.Length % 2) != 0)
|
||||
throw new Exception("EnhancementTimer: args.length must be an even number greater than 0");
|
||||
|
||||
Enhancement.AddMobile(m);
|
||||
this.m_Mobile = m;
|
||||
this.m_Title = title;
|
||||
this.m_Duration = duration;
|
||||
|
||||
AosAttribute att;
|
||||
AosWeaponAttribute weapon;
|
||||
AosArmorAttribute armor;
|
||||
SAAbsorptionAttribute absorb;
|
||||
int number = 0;
|
||||
|
||||
for (int i = 0; i < args.Length - 1; i += 2)
|
||||
{
|
||||
if (!(args[i + 1] is int))
|
||||
throw new Exception("EnhancementTimer: The second value must be an integer");
|
||||
|
||||
number = (int)args[i + 1];
|
||||
|
||||
if (args[i] is AosAttribute)
|
||||
{
|
||||
att = (AosAttribute)args[i];
|
||||
Enhancement.SetValue(m, att, (Enhancement.GetValue(m, att) + number), this.m_Title);
|
||||
this.AL.Add(att);
|
||||
this.AL.Add(number);
|
||||
}
|
||||
else if (args[i] is AosWeaponAttribute)
|
||||
{
|
||||
weapon = (AosWeaponAttribute)args[i];
|
||||
Enhancement.SetValue(m, weapon, (Enhancement.GetValue(m, weapon) + number), this.m_Title);
|
||||
this.AL.Add(weapon);
|
||||
this.AL.Add(number);
|
||||
}
|
||||
else if (args[i] is AosArmorAttribute)
|
||||
{
|
||||
armor = (AosArmorAttribute)args[i];
|
||||
Enhancement.SetValue(m, armor, (Enhancement.GetValue(m, armor) + number), this.m_Title);
|
||||
this.AL.Add(armor);
|
||||
this.AL.Add(number);
|
||||
}
|
||||
else if (args[i] is SAAbsorptionAttribute)
|
||||
{
|
||||
absorb = (SAAbsorptionAttribute)args[i];
|
||||
Enhancement.SetValue(m, absorb, (Enhancement.GetValue(m, absorb) + number), this.m_Title);
|
||||
this.AL.Add(absorb);
|
||||
this.AL.Add(number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
if (Enhancement.EnhancementList.ContainsKey(this.m_Mobile))
|
||||
{
|
||||
AosAttribute att;
|
||||
AosWeaponAttribute weapon;
|
||||
AosArmorAttribute armor;
|
||||
SAAbsorptionAttribute absorb;
|
||||
int number = 0;
|
||||
|
||||
for (int i = 0; i < this.AL.Count - 1; i += 2)
|
||||
{
|
||||
number = (int)this.AL[i + 1];
|
||||
|
||||
if (this.AL[i] is AosAttribute)
|
||||
{
|
||||
att = (AosAttribute)this.AL[i];
|
||||
Enhancement.SetValue(this.m_Mobile, att, (Enhancement.GetValue(this.m_Mobile, att) - number), this.m_Title);
|
||||
}
|
||||
else if (this.AL[i] is AosWeaponAttribute)
|
||||
{
|
||||
weapon = (AosWeaponAttribute)this.AL[i];
|
||||
Enhancement.SetValue(this.m_Mobile, weapon, (Enhancement.GetValue(this.m_Mobile, weapon) - number), this.m_Title);
|
||||
}
|
||||
else if (this.AL[i] is AosArmorAttribute)
|
||||
{
|
||||
armor = (AosArmorAttribute)this.AL[i];
|
||||
Enhancement.SetValue(this.m_Mobile, armor, (Enhancement.GetValue(this.m_Mobile, armor) - number), this.m_Title);
|
||||
}
|
||||
else if (this.AL[i] is SAAbsorptionAttribute)
|
||||
{
|
||||
absorb = (SAAbsorptionAttribute)this.AL[i];
|
||||
Enhancement.SetValue(this.m_Mobile, absorb, (Enhancement.GetValue(this.m_Mobile, absorb) - number), this.m_Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Stop();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
this.m_Duration--;
|
||||
|
||||
if (this.m_Mobile == null)
|
||||
this.Stop();
|
||||
|
||||
if (this.m_Duration < 0)
|
||||
{
|
||||
this.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Scripts/Abilities/Feint.cs
Normal file
91
Scripts/Abilities/Feint.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Gain a defensive advantage over your primary opponent for a short time.
|
||||
/// </summary>
|
||||
public class Feint : WeaponAbility
|
||||
{
|
||||
private static Dictionary<Mobile, FeintTimer> m_Registry = new Dictionary<Mobile, FeintTimer>();
|
||||
public static Dictionary<Mobile, FeintTimer> Registry { get { return m_Registry; } }
|
||||
|
||||
public Feint()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 30; } }
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
if( !Validate( attacker ) || !CheckMana( attacker, true ) )
|
||||
return;
|
||||
|
||||
if( Registry.ContainsKey( attacker ) )
|
||||
{
|
||||
if (m_Registry[attacker] != null)
|
||||
m_Registry[attacker].Stop();
|
||||
|
||||
Registry.Remove(attacker);
|
||||
}
|
||||
|
||||
bool creature = attacker is BaseCreature;
|
||||
ClearCurrentAbility( attacker );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063360 ); // You baffle your target with a feint!
|
||||
defender.SendLocalizedMessage( 1063361 ); // You were deceived by an attacker's feint!
|
||||
|
||||
attacker.FixedParticles( 0x3728, 1, 13, 0x7F3, 0x962, 0, EffectLayer.Waist );
|
||||
attacker.PlaySound(0x525);
|
||||
|
||||
double skill = creature ? attacker.Skills[SkillName.Bushido].Value :
|
||||
Math.Max(attacker.Skills[SkillName.Ninjitsu].Value, attacker.Skills[SkillName.Bushido].Value);
|
||||
|
||||
int bonus = (int)(20.0 + 3.0 * (skill - 50.0) / 7.0);
|
||||
|
||||
FeintTimer t = new FeintTimer( attacker, defender, bonus ); //20-50 % decrease
|
||||
|
||||
t.Start();
|
||||
m_Registry[attacker] = t;
|
||||
|
||||
string args = String.Format("{0}\t{1}", defender.Name, bonus);
|
||||
BuffInfo.AddBuff(attacker, new BuffInfo(BuffIcon.Feint, 1151308, 1151307, TimeSpan.FromSeconds(6), attacker, args));
|
||||
|
||||
if (creature)
|
||||
PetTrainingHelper.OnWeaponAbilityUsed((BaseCreature)attacker, SkillName.Bushido);
|
||||
}
|
||||
|
||||
public class FeintTimer : Timer
|
||||
{
|
||||
private Mobile m_Owner;
|
||||
private Mobile m_Enemy;
|
||||
private int m_DamageReduction;
|
||||
|
||||
public Mobile Owner { get { return m_Owner; } }
|
||||
public Mobile Enemy { get { return m_Enemy; } }
|
||||
|
||||
public int DamageReduction { get { return m_DamageReduction; } }
|
||||
|
||||
public FeintTimer(Mobile owner, Mobile enemy, int DamageReduction)
|
||||
: base(TimeSpan.FromSeconds(6.0))
|
||||
{
|
||||
m_Owner = owner;
|
||||
m_Enemy = enemy;
|
||||
m_DamageReduction = DamageReduction;
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
Registry.Remove(m_Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Scripts/Abilities/Focus.cs
Normal file
119
Scripts/Abilities/Focus.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Server.Mobiles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class Focus
|
||||
{
|
||||
private static Dictionary<Mobile, FocusInfo> m_Table = new Dictionary<Mobile, FocusInfo>();
|
||||
private static int DefaultDamageBonus = -40;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.Login += OnLogin;
|
||||
}
|
||||
|
||||
public class FocusInfo
|
||||
{
|
||||
public Mobile Target { get; set; }
|
||||
public int DamageBonus { get; set; }
|
||||
|
||||
public FocusInfo(Mobile defender, int bonus)
|
||||
{
|
||||
Target = defender;
|
||||
DamageBonus = bonus;
|
||||
}
|
||||
}
|
||||
|
||||
public Focus()
|
||||
{
|
||||
}
|
||||
|
||||
public static void OnLogin(LoginEventArgs e)
|
||||
{
|
||||
var pm = e.Mobile as PlayerMobile;
|
||||
|
||||
if (pm != null)
|
||||
{
|
||||
UpdateBuff(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateBuff(Mobile from, Mobile target = null)
|
||||
{
|
||||
var item = from.FindItemOnLayer(Layer.TwoHanded);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
item = from.FindItemOnLayer(Layer.OneHanded);
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
if (m_Table.ContainsKey(from))
|
||||
{
|
||||
m_Table.Remove(from);
|
||||
BuffInfo.RemoveBuff(from, BuffIcon.RageFocusingBuff);
|
||||
}
|
||||
}
|
||||
else if (item is BaseWeapon && ((BaseWeapon)item).ExtendedWeaponAttributes.Focus > 0)
|
||||
{
|
||||
if (m_Table.ContainsKey(from))
|
||||
{
|
||||
FocusInfo info = m_Table[from];
|
||||
|
||||
BuffInfo.AddBuff(from, new BuffInfo(BuffIcon.RageFocusingBuff, 1151393, 1151394,
|
||||
String.Format("{0}\t{1}", info.Target == null ? "NONE" : info.Target.Name, info.DamageBonus)));
|
||||
}
|
||||
|
||||
m_Table[from] = new FocusInfo(target, DefaultDamageBonus);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetBonus(Mobile from, Mobile target)
|
||||
{
|
||||
if (m_Table.ContainsKey(from))
|
||||
{
|
||||
FocusInfo info = m_Table[from];
|
||||
|
||||
if (info.Target == target)
|
||||
{
|
||||
return info.DamageBonus;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void OnHit(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (m_Table.ContainsKey(attacker))
|
||||
{
|
||||
FocusInfo info = m_Table[attacker];
|
||||
|
||||
if (info.Target == null)
|
||||
{
|
||||
info.DamageBonus -= 10;
|
||||
}
|
||||
else if (info.Target == defender)
|
||||
{
|
||||
if (info.DamageBonus < -40)
|
||||
info.DamageBonus += 10;
|
||||
else
|
||||
info.DamageBonus += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info.DamageBonus >= -50)
|
||||
info.DamageBonus = DefaultDamageBonus;
|
||||
}
|
||||
|
||||
if (info.Target != defender)
|
||||
info.Target = defender;
|
||||
|
||||
UpdateBuff(attacker, defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Scripts/Abilities/ForceArrow.cs
Normal file
168
Scripts/Abilities/ForceArrow.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using Server;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Server.Spells;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class ForceArrow : WeaponAbility
|
||||
{
|
||||
public ForceArrow()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 20; } }
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1074381); // You fire an arrow of pure force.
|
||||
defender.SendLocalizedMessage(1074382); // You are struck by a force arrow!
|
||||
|
||||
if (.4 > Utility.RandomDouble())
|
||||
{
|
||||
defender.Combatant = null;
|
||||
defender.Warmode = false;
|
||||
}
|
||||
|
||||
ForceArrowInfo info = GetInfo(attacker, defender);
|
||||
|
||||
if (info == null)
|
||||
BeginForceArrow(attacker, defender);
|
||||
else
|
||||
{
|
||||
if (info.Timer != null && info.Timer.Running)
|
||||
{
|
||||
info.Timer.IncreaseExpiration();
|
||||
|
||||
BuffInfo.RemoveBuff(defender, BuffIcon.ForceArrow);
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
Spell spell = defender.Spell as Spell;
|
||||
|
||||
if (spell != null && spell.IsCasting)
|
||||
spell.Disturb(DisturbType.Hurt, false, true);
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, List<ForceArrowInfo>> m_Table = new Dictionary<Mobile, List<ForceArrowInfo>>();
|
||||
|
||||
public static void BeginForceArrow(Mobile attacker, Mobile defender)
|
||||
{
|
||||
ForceArrowInfo info = new ForceArrowInfo(attacker, defender);
|
||||
info.Timer = new ForceArrowTimer(info);
|
||||
|
||||
if (!m_Table.ContainsKey(attacker))
|
||||
m_Table[attacker] = new List<ForceArrowInfo>();
|
||||
|
||||
m_Table[attacker].Add(info);
|
||||
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString()));
|
||||
}
|
||||
|
||||
public static void EndForceArrow(ForceArrowInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
Mobile attacker = info.Attacker;
|
||||
|
||||
if (m_Table.ContainsKey(attacker) && m_Table[attacker].Contains(info))
|
||||
{
|
||||
m_Table[attacker].Remove(info);
|
||||
|
||||
if (m_Table[attacker].Count == 0)
|
||||
m_Table.Remove(attacker);
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(info.Defender, BuffIcon.ForceArrow);
|
||||
}
|
||||
|
||||
public static bool HasForceArrow(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!m_Table.ContainsKey(attacker))
|
||||
return false;
|
||||
|
||||
foreach (ForceArrowInfo info in m_Table[attacker])
|
||||
{
|
||||
if (info.Defender == defender)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ForceArrowInfo GetInfo(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!m_Table.ContainsKey(attacker))
|
||||
return null;
|
||||
|
||||
foreach (ForceArrowInfo info in m_Table[attacker])
|
||||
{
|
||||
if (info.Defender == defender)
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public class ForceArrowInfo
|
||||
{
|
||||
private Mobile m_Attacker;
|
||||
private Mobile m_Defender;
|
||||
private ForceArrowTimer m_Timer;
|
||||
private int m_DefenseChanceMalus;
|
||||
|
||||
public Mobile Attacker { get { return m_Attacker; } }
|
||||
public Mobile Defender { get { return m_Defender; } }
|
||||
public ForceArrowTimer Timer { get { return m_Timer; } set { m_Timer = value; } }
|
||||
public int DefenseChanceMalus { get { return m_DefenseChanceMalus; } set { m_DefenseChanceMalus = value; } }
|
||||
|
||||
public ForceArrowInfo(Mobile attacker, Mobile defender)
|
||||
{
|
||||
m_Attacker = attacker;
|
||||
m_Defender = defender;
|
||||
m_DefenseChanceMalus = 10;
|
||||
}
|
||||
}
|
||||
|
||||
public class ForceArrowTimer : Timer
|
||||
{
|
||||
private ForceArrowInfo m_Info;
|
||||
private DateTime m_Expires;
|
||||
|
||||
public ForceArrowTimer(ForceArrowInfo info)
|
||||
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1))
|
||||
{
|
||||
m_Info = info;
|
||||
Priority = TimerPriority.OneSecond;
|
||||
|
||||
m_Expires = DateTime.UtcNow + TimeSpan.FromSeconds(10);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Expires < DateTime.UtcNow)
|
||||
{
|
||||
Stop();
|
||||
EndForceArrow(m_Info);
|
||||
}
|
||||
}
|
||||
|
||||
public void IncreaseExpiration()
|
||||
{
|
||||
m_Expires = m_Expires + TimeSpan.FromSeconds(2);
|
||||
|
||||
m_Info.DefenseChanceMalus += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Scripts/Abilities/ForceofNature.cs
Normal file
133
Scripts/Abilities/ForceofNature.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using Server;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class ForceOfNature : WeaponAbility
|
||||
{
|
||||
public ForceOfNature()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 35; } }
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1074374); // You attack your enemy with the force of nature!
|
||||
defender.SendLocalizedMessage(1074375); // You are assaulted with great force!
|
||||
|
||||
defender.PlaySound(0x22F);
|
||||
defender.FixedParticles(0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head);
|
||||
defender.FixedParticles(0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255);
|
||||
|
||||
if (m_Table.ContainsKey(attacker))
|
||||
Remove(attacker);
|
||||
|
||||
ForceOfNatureTimer t = new ForceOfNatureTimer(attacker, defender);
|
||||
t.Start();
|
||||
|
||||
m_Table[attacker] = t;
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, ForceOfNatureTimer> m_Table = new Dictionary<Mobile, ForceOfNatureTimer>();
|
||||
|
||||
public static bool Remove(Mobile m)
|
||||
{
|
||||
ForceOfNatureTimer t;
|
||||
|
||||
m_Table.TryGetValue(m, out t);
|
||||
|
||||
if (t == null)
|
||||
return false;
|
||||
|
||||
t.Stop();
|
||||
m_Table.Remove(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void OnHit(Mobile from, Mobile target)
|
||||
{
|
||||
if (m_Table.ContainsKey(from))
|
||||
{
|
||||
ForceOfNatureTimer t = m_Table[from];
|
||||
|
||||
t.Hits++;
|
||||
t.LastHit = DateTime.Now;
|
||||
|
||||
if (t.Hits % 12 == 0)
|
||||
{
|
||||
int duration = target.Skills[SkillName.MagicResist].Value >= 90.0 ? 1 : 2;
|
||||
target.Paralyze(TimeSpan.FromSeconds(duration));
|
||||
t.Hits = 0;
|
||||
|
||||
from.SendLocalizedMessage(1004013); // You successfully stun your opponent!
|
||||
target.SendLocalizedMessage(1004014); // You have been stunned!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetBonus(Mobile from, Mobile target)
|
||||
{
|
||||
if (m_Table.ContainsKey(from))
|
||||
{
|
||||
ForceOfNatureTimer t = m_Table[from];
|
||||
|
||||
if (t.Target == target)
|
||||
{
|
||||
int bonus = Math.Max(50, from.Str - 50);
|
||||
if (bonus > 100) bonus = 100;
|
||||
return bonus;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private class ForceOfNatureTimer : Timer
|
||||
{
|
||||
private Mobile m_Target, m_From;
|
||||
|
||||
private DateTime m_LastHit;
|
||||
private int m_Tick, m_Hits;
|
||||
|
||||
public Mobile Target { get { return m_Target; } }
|
||||
public Mobile From { get { return m_From; } }
|
||||
public int Hits { get { return m_Hits; } set { m_Hits = value; } }
|
||||
public DateTime LastHit { get { return m_LastHit; } set { m_LastHit = value; } }
|
||||
|
||||
public ForceOfNatureTimer(Mobile from, Mobile target)
|
||||
: base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
|
||||
{
|
||||
m_Target = target;
|
||||
m_From = from;
|
||||
m_Tick = 0;
|
||||
m_Hits = 1;
|
||||
m_LastHit = DateTime.Now;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Tick++;
|
||||
|
||||
if (!m_From.Alive || !m_Target.Alive || m_Target.Map != m_From.Map || m_Target.GetDistanceToSqrt(m_From.Location) > 10 || m_LastHit + TimeSpan.FromSeconds(20) < DateTime.Now || m_Tick > 36)
|
||||
{
|
||||
Server.Items.ForceOfNature.Remove(m_From);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Tick == 1)
|
||||
{
|
||||
int damage = Utility.RandomMinMax(15, 35);
|
||||
|
||||
AOS.Damage(m_Target, m_From, damage, false, 0, 0, 0, 0, 0, 0, 100, false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
Scripts/Abilities/FrenziedWhirlwind.cs
Normal file
146
Scripts/Abilities/FrenziedWhirlwind.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Spells;
|
||||
using Server.Engines.PartySystem;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// A quick attack to all enemies in range of your weapon that causes damage over time. Requires Bushido or Ninjitsu skill.
|
||||
/// </summary>
|
||||
public class FrenziedWhirlwind : WeaponAbility
|
||||
{
|
||||
public FrenziedWhirlwind()
|
||||
{
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 30; } }
|
||||
|
||||
private static Dictionary<Mobile, Timer> m_Registry = new Dictionary<Mobile, Timer>();
|
||||
public static Dictionary<Mobile, Timer> Registry { get { return m_Registry; } }
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker)) //Mana check after check that there are targets
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
Map map = attacker.Map;
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
BaseWeapon weapon = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return;
|
||||
|
||||
var targets = SpellHelper.AcquireIndirectTargets(attacker, attacker.Location, attacker.Map, 2).OfType<Mobile>().ToList();
|
||||
|
||||
if (targets.Count > 0)
|
||||
{
|
||||
if (!CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
attacker.FixedEffect(0x3728, 10, 15);
|
||||
attacker.PlaySound(0x2A1);
|
||||
|
||||
if (m_Registry.ContainsKey(attacker))
|
||||
{
|
||||
RemoveFromRegistry(attacker);
|
||||
}
|
||||
|
||||
m_Registry[attacker] = new InternalTimer(attacker, targets);
|
||||
|
||||
foreach (var pm in targets.OfType<PlayerMobile>())
|
||||
{
|
||||
BuffInfo.AddBuff(pm, new BuffInfo(BuffIcon.SplinteringEffect, 1153804, 1028852, TimeSpan.FromSeconds(2.0), pm));
|
||||
}
|
||||
|
||||
if (defender is PlayerMobile && attacker is PlayerMobile)
|
||||
{
|
||||
defender.SendSpeedControl(SpeedControlType.WalkSpeed);
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.SplinteringEffect, 1153804, 1152144, TimeSpan.FromSeconds(2.0), defender));
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(2), mob => mob.SendSpeedControl(SpeedControlType.Disable), defender);
|
||||
}
|
||||
|
||||
if (attacker is BaseCreature)
|
||||
PetTrainingHelper.OnWeaponAbilityUsed((BaseCreature)attacker, SkillName.Ninjitsu);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveFromRegistry(Mobile from)
|
||||
{
|
||||
if (m_Registry.ContainsKey(from))
|
||||
{
|
||||
m_Registry[from].Stop();
|
||||
m_Registry.Remove(from);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Attacker;
|
||||
private List<Mobile> m_List;
|
||||
private long m_Start;
|
||||
|
||||
public InternalTimer(Mobile attacker, List<Mobile> list)
|
||||
: base(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(500))
|
||||
{
|
||||
m_Attacker = attacker;
|
||||
m_List = list;
|
||||
|
||||
m_Start = Core.TickCount;
|
||||
|
||||
Priority = TimerPriority.TwentyFiveMS;
|
||||
DoHit();
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Attacker.Alive)
|
||||
DoHit();
|
||||
|
||||
if (!m_Attacker.Alive || m_Start + 2000 < Core.TickCount)
|
||||
{
|
||||
ColUtility.Free(m_List);
|
||||
Server.Items.FrenziedWhirlwind.RemoveFromRegistry(m_Attacker);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoHit()
|
||||
{
|
||||
if (m_List == null)
|
||||
return;
|
||||
|
||||
foreach (Mobile m in m_List)
|
||||
{
|
||||
if (m_Attacker.InRange(m.Location, 2) && m.Alive && m.Map == m_Attacker.Map)
|
||||
{
|
||||
m_Attacker.FixedEffect(0x3728, 10, 15);
|
||||
m_Attacker.PlaySound(0x2A1);
|
||||
|
||||
int skill = m_Attacker is BaseCreature ? (int)m_Attacker.Skills[SkillName.Ninjitsu].Value :
|
||||
(int)Math.Max(m_Attacker.Skills[SkillName.Bushido].Value, m_Attacker.Skills[SkillName.Ninjitsu].Value);
|
||||
|
||||
var baseMin = (int)Math.Max(5, (skill / 50) * 5);
|
||||
AOS.Damage(m, m_Attacker, Utility.RandomMinMax(baseMin, (baseMin * 3) + 2), 100, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Scripts/Abilities/InfectiousStrike.cs
Normal file
123
Scripts/Abilities/InfectiousStrike.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// This special move represents a significant change to the use of poisons in Age of Shadows.
|
||||
/// Now, only certain weapon types — those that have Infectious Strike as an available special move — will be able to be poisoned.
|
||||
/// Targets will no longer be poisoned at random when hit by poisoned weapons.
|
||||
/// Instead, the wielder must use this ability to deliver the venom.
|
||||
/// While no skill in Poisoning is directly required to use this ability, being knowledgeable in the application and use of toxins
|
||||
/// will allow a character to use Infectious Strike at reduced mana cost and with a chance to inflict more deadly poison on his victim.
|
||||
/// With this change, weapons will no longer be corroded by poison.
|
||||
/// Level 5 poison will be possible when using this special move.
|
||||
/// </summary>
|
||||
public class InfectiousStrike : WeaponAbility
|
||||
{
|
||||
public InfectiousStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RequiresSecondarySkill(Mobile from)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return SkillName.Poisoning;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
BaseWeapon weapon = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return;
|
||||
|
||||
Poison p = weapon.Poison;
|
||||
|
||||
if (p == null || weapon.PoisonCharges <= 0)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1061141); // Your weapon must have a dose of poison to perform an infectious strike!
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
// Skill Masteries
|
||||
int noChargeChance = Server.Spells.SkillMasteries.MasteryInfo.NonPoisonConsumeChance(attacker);
|
||||
|
||||
if (noChargeChance == 0 || noChargeChance < Utility.Random(100))
|
||||
--weapon.PoisonCharges;
|
||||
else
|
||||
attacker.SendLocalizedMessage(1156095); // Your mastery of poisoning allows you to use your poison charge without consuming it.
|
||||
|
||||
// Infectious strike special move now uses poisoning skill to help determine potency
|
||||
int maxLevel = 0;
|
||||
if (p == Poison.DarkGlow)
|
||||
{
|
||||
maxLevel = 10 + (attacker.Skills[SkillName.Poisoning].Fixed / 333);
|
||||
if (maxLevel > 13)
|
||||
maxLevel = 13;
|
||||
}
|
||||
else if (p == Poison.Parasitic)
|
||||
{
|
||||
maxLevel = 14 + (attacker.Skills[SkillName.Poisoning].Fixed / 250);
|
||||
if (maxLevel > 18)
|
||||
maxLevel = 18;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxLevel = attacker.Skills[SkillName.Poisoning].Fixed / 200;
|
||||
if (maxLevel > 5)
|
||||
maxLevel = 5;
|
||||
}
|
||||
|
||||
if (maxLevel < 0)
|
||||
maxLevel = 0;
|
||||
if (p.Level > maxLevel) // If they don't have enough Poisoning Skill for the potion strength, lower it.
|
||||
p = Poison.GetPoison(maxLevel);
|
||||
|
||||
if ((attacker.Skills[SkillName.Poisoning].Value / 100.0) > Utility.RandomDouble())
|
||||
{
|
||||
if (p !=null && p.Level + 1 <= maxLevel)
|
||||
{
|
||||
int level = p.Level + 1;
|
||||
Poison newPoison = Poison.GetPoison(level);
|
||||
|
||||
if (newPoison != null)
|
||||
{
|
||||
p = newPoison;
|
||||
|
||||
attacker.SendLocalizedMessage(1060080); // Your precise strike has increased the level of the poison by 1
|
||||
defender.SendLocalizedMessage(1060081); // The poison seems extra effective!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defender.PlaySound(0xDD);
|
||||
defender.FixedParticles(0x3728, 244, 25, 9941, 1266, 0, EffectLayer.Waist);
|
||||
|
||||
if (defender.ApplyPoison(attacker, p) != ApplyPoisonResult.Immune)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1008096, true, defender.Name); // You have poisoned your target :
|
||||
defender.SendLocalizedMessage(1008097, false, attacker.Name); // : poisoned you!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Scripts/Abilities/InfusedThrow.cs
Normal file
45
Scripts/Abilities/InfusedThrow.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
// The projectile will be infused with energy by the attacker causing it to do more damage and stun or dismount the target.
|
||||
// The player infuses their throwing projectile with mystical power.
|
||||
// The infused projectile will dismount the target if possible; otherwise it will temporarily stun the target.
|
||||
// The target will be hit with chaos damage regardless of whether they were dismounted or paralyzed.
|
||||
public class InfusedThrow : WeaponAbility
|
||||
{
|
||||
public override int BaseMana { get { return 25; } }
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1149563); //The infused projectile strikes a target!
|
||||
defender.SendLocalizedMessage(1149564); //You are struck by the infused projectile and take damage!
|
||||
|
||||
AOS.Damage(defender, attacker, 15, false, 0, 0, 0, 0, 0, 100, 0, false);
|
||||
|
||||
IMount mount = defender.Mount;
|
||||
|
||||
if ((defender.Mounted || defender.Flying || Server.Spells.Ninjitsu.AnimalForm.UnderTransformation(defender)) && !attacker.Mounted && !attacker.Flying && !(defender is ChaosDragoon) && !(defender is ChaosDragoonElite))
|
||||
{
|
||||
defender.PlaySound(0x140);
|
||||
defender.FixedParticles(0x3728, 10, 15, 9955, EffectLayer.Waist);
|
||||
|
||||
Server.Items.Dismount.DoDismount(attacker, defender, mount, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
defender.FixedEffect(0x376A, 9, 32);
|
||||
defender.PlaySound(0x204);
|
||||
|
||||
TimeSpan duration = defender.Player ? TimeSpan.FromSeconds(3.0) : TimeSpan.FromSeconds(6.0);
|
||||
defender.Paralyze(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Scripts/Abilities/LightningArrow.cs
Normal file
88
Scripts/Abilities/LightningArrow.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Spells;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class LightningArrow : WeaponAbility
|
||||
{
|
||||
public LightningArrow()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ConsumeAmmo
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
Map map = attacker.Map;
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
BaseWeapon weapon = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return;
|
||||
|
||||
if (!this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
IPooledEnumerable eable = defender.GetMobilesInRange(5);
|
||||
|
||||
foreach(Mobile m in eable)
|
||||
{
|
||||
if (m != defender && m != attacker && SpellHelper.ValidIndirectTarget(attacker, m))
|
||||
{
|
||||
if (m == null || m.Deleted || m.Map != attacker.Map || !m.Alive || !attacker.CanSee(m) || !attacker.CanBeHarmful(m))
|
||||
continue;
|
||||
|
||||
if (!attacker.InRange(m, weapon.MaxRange) || !attacker.InLOS(m))
|
||||
continue;
|
||||
|
||||
targets.Add(m);
|
||||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
defender.BoltEffect(0);
|
||||
|
||||
if (targets.Count > 0)
|
||||
{
|
||||
while (targets.Count > 2)
|
||||
{
|
||||
targets.Remove(targets[Utility.Random(targets.Count)]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < targets.Count; ++i)
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
m.BoltEffect(0);
|
||||
|
||||
AOS.Damage(m, attacker, Utility.RandomMinMax(29, 40), 0, 0, 0, 0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
ColUtility.Free(targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Scripts/Abilities/MortalStrike.cs
Normal file
127
Scripts/Abilities/MortalStrike.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// The assassin's friend.
|
||||
/// A successful Mortal Strike will render its victim unable to heal any damage for several seconds.
|
||||
/// Use a gruesome follow-up to finish off your foe.
|
||||
/// </summary>
|
||||
public class MortalStrike : WeaponAbility
|
||||
{
|
||||
public static readonly TimeSpan PlayerDuration = TimeSpan.FromSeconds(6.0);
|
||||
public static readonly TimeSpan NPCDuration = TimeSpan.FromSeconds(12.0);
|
||||
|
||||
private static readonly Dictionary<Mobile, Timer> m_Table = new Dictionary<Mobile, Timer>();
|
||||
private static readonly List<Mobile> m_EffectReduction = new List<Mobile>();
|
||||
|
||||
public MortalStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
public static bool IsWounded(Mobile m)
|
||||
{
|
||||
return m_Table.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static void BeginWound(Mobile m, TimeSpan duration)
|
||||
{
|
||||
Timer t;
|
||||
|
||||
if (m_Table.ContainsKey(m))
|
||||
{
|
||||
EndWound(m, true);
|
||||
}
|
||||
|
||||
if (Core.HS && m_EffectReduction.Contains(m))
|
||||
{
|
||||
double d = duration.TotalSeconds;
|
||||
duration = TimeSpan.FromSeconds(d / 2);
|
||||
}
|
||||
|
||||
t = new InternalTimer(m, duration);
|
||||
m_Table[m] = t;
|
||||
|
||||
t.Start();
|
||||
|
||||
m.YellowHealthbar = true;
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.MortalStrike, 1075810, 1075811, duration, m));
|
||||
}
|
||||
|
||||
public static void EndWound(Mobile m, bool natural = false)
|
||||
{
|
||||
if (!IsWounded(m))
|
||||
return;
|
||||
|
||||
Timer t = m_Table[m];
|
||||
|
||||
if (t != null)
|
||||
t.Stop();
|
||||
|
||||
m_Table.Remove(m);
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.MortalStrike);
|
||||
|
||||
m.YellowHealthbar = false;
|
||||
m.SendLocalizedMessage(1060208); // You are no longer mortally wounded.
|
||||
|
||||
if (Core.HS && natural && !m_EffectReduction.Contains(m))
|
||||
{
|
||||
m_EffectReduction.Add(m);
|
||||
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(8), () =>
|
||||
{
|
||||
if (m_EffectReduction.Contains(m))
|
||||
m_EffectReduction.Remove(m);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060086); // You deliver a mortal wound!
|
||||
defender.SendLocalizedMessage(1060087); // You have been mortally wounded!
|
||||
|
||||
defender.PlaySound(0x1E1);
|
||||
defender.FixedParticles(0x37B9, 244, 25, 9944, 31, 0, EffectLayer.Waist);
|
||||
|
||||
// Do not reset timer if one is already in place.
|
||||
if (Core.HS || !IsWounded(defender))
|
||||
{
|
||||
if (Spells.SkillMasteries.ResilienceSpell.UnderEffects(defender)) //Halves time
|
||||
BeginWound(defender, defender.Player ? TimeSpan.FromSeconds(3.0) : TimeSpan.FromSeconds(6));
|
||||
else
|
||||
BeginWound(defender, defender.Player ? PlayerDuration : NPCDuration);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
public InternalTimer(Mobile m, TimeSpan duration)
|
||||
: base(duration)
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndWound(m_Mobile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Scripts/Abilities/MovingShot.cs
Normal file
57
Scripts/Abilities/MovingShot.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Available on some crossbows, this special move allows archers to fire while on the move.
|
||||
/// This shot is somewhat less accurate than normal, but the ability to fire while running is a clear advantage.
|
||||
/// </summary>
|
||||
public class MovingShot : WeaponAbility
|
||||
{
|
||||
public MovingShot()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
public override int AccuracyBonus
|
||||
{
|
||||
get
|
||||
{
|
||||
return Core.TOL ? -35 : -25;
|
||||
}
|
||||
}
|
||||
public override bool ValidatesDuringHit
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
|
||||
{
|
||||
return (this.Validate(attacker) && this.CheckMana(attacker, true));
|
||||
}
|
||||
|
||||
public override void OnMiss(Mobile attacker, Mobile defender)
|
||||
{
|
||||
//Validates in OnSwing for accuracy scalar
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060089); // You fail to execute your special move
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
//Validates in OnSwing for accuracy scalar
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060216); // Your shot was successful
|
||||
}
|
||||
}
|
||||
}
|
||||
83
Scripts/Abilities/MysticArc.cs
Normal file
83
Scripts/Abilities/MysticArc.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
// The thrown projectile will arc to a second target after hitting the primary target. Chaos energy will burst from the projectile at each target.
|
||||
// This will only hit targets that are in combat with the user.
|
||||
public class MysticArc : WeaponAbility
|
||||
{
|
||||
private readonly int m_Damage = 15;
|
||||
private Mobile m_Target;
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.CheckMana(attacker, true) && defender != null)
|
||||
return;
|
||||
|
||||
BaseThrown weapon = attacker.Weapon as BaseThrown;
|
||||
|
||||
if (weapon == null)
|
||||
return;
|
||||
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
IPooledEnumerable eable = attacker.GetMobilesInRange(weapon.MaxRange);
|
||||
|
||||
foreach (Mobile m in eable)
|
||||
{
|
||||
if (m == defender)
|
||||
continue;
|
||||
|
||||
if (m.Combatant != attacker)
|
||||
continue;
|
||||
|
||||
targets.Add(m);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
if (targets.Count > 0)
|
||||
this.m_Target = targets[Utility.Random(targets.Count)];
|
||||
|
||||
AOS.Damage(defender, attacker, this.m_Damage, 0, 0, 0, 0, 0, 100);
|
||||
|
||||
if (this.m_Target != null)
|
||||
{
|
||||
defender.MovingEffect(this.m_Target, weapon.ItemID, 18, 1, false, false);
|
||||
Timer.DelayCall(TimeSpan.FromMilliseconds(333.0), new TimerCallback(ThrowAgain));
|
||||
this.m_Mobile = attacker;
|
||||
}
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
}
|
||||
|
||||
public void ThrowAgain()
|
||||
{
|
||||
if (this.m_Target != null && this.m_Mobile != null)
|
||||
{
|
||||
BaseThrown weapon = this.m_Mobile.Weapon as BaseThrown;
|
||||
|
||||
if (weapon == null)
|
||||
return;
|
||||
|
||||
if (WeaponAbility.GetCurrentAbility(this.m_Mobile) is MysticArc)
|
||||
ClearCurrentAbility(this.m_Mobile);
|
||||
|
||||
if (weapon.CheckHit(this.m_Mobile, this.m_Target))
|
||||
{
|
||||
weapon.OnHit(this.m_Mobile, this.m_Target, 0.0);
|
||||
AOS.Damage(this.m_Target, this.m_Mobile, this.m_Damage, 0, 0, 0, 0, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Scripts/Abilities/NerveStrike.cs
Normal file
99
Scripts/Abilities/NerveStrike.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Does damage and paralyses your opponent for a short time.
|
||||
/// </summary>
|
||||
public class NerveStrike : WeaponAbility
|
||||
{
|
||||
public NerveStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
public override bool CheckSkills(Mobile from)
|
||||
{
|
||||
if (this.GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(1070768, "50"); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckSkills(from);
|
||||
}
|
||||
|
||||
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (!Core.ML && defender.Frozen)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1061923); // The target is already frozen.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
bool immune = Server.Items.ParalyzingBlow.IsImmune(defender);
|
||||
bool doEffects = false;
|
||||
|
||||
if (Core.ML)
|
||||
{
|
||||
AOS.Damage(defender, attacker, (int)(15.0 * (attacker.Skills[SkillName.Bushido].Value - 50.0) / 70.0 + Utility.Random(10)), true, 100, 0, 0, 0, 0); //0-25
|
||||
|
||||
if (!immune && ((150.0 / 7.0 + (4.0 * attacker.Skills[SkillName.Bushido].Value) / 7.0) / 100.0) > Utility.RandomDouble())
|
||||
{
|
||||
defender.Paralyze(TimeSpan.FromSeconds(2.0));
|
||||
doEffects = true;
|
||||
}
|
||||
|
||||
if(attacker is BaseCreature)
|
||||
PetTrainingHelper.OnWeaponAbilityUsed((BaseCreature)attacker, SkillName.Bushido);
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(defender, attacker, (int)(15.0 * (attacker.Skills[SkillName.Bushido].Value - 50.0) / 70.0 + 10), true, 100, 0, 0, 0, 0); //10-25
|
||||
|
||||
if(!immune)
|
||||
{
|
||||
defender.Freeze(TimeSpan.FromSeconds(2.0));
|
||||
doEffects = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!immune)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1063356); // You cripple your target with a nerve strike!
|
||||
defender.SendLocalizedMessage(1063357); // Your attacker dealt a crippling nerve strike!
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker.SendLocalizedMessage(1070804); // Your target resists paralysis.
|
||||
defender.SendLocalizedMessage(1070813); // You resist paralysis.
|
||||
}
|
||||
|
||||
if (doEffects)
|
||||
{
|
||||
attacker.PlaySound(0x204);
|
||||
defender.FixedEffect(0x376A, 9, 32);
|
||||
defender.FixedParticles(0x37C4, 1, 8, 0x13AF, 0, 0, EffectLayer.Waist);
|
||||
}
|
||||
|
||||
Server.Items.ParalyzingBlow.BeginImmunity(defender, Server.Items.ParalyzingBlow.FreezeDelayDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Scripts/Abilities/ParalyzingBlow.cs
Normal file
127
Scripts/Abilities/ParalyzingBlow.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// A successful Paralyzing Blow will leave the target stunned, unable to move, attack, or cast spells, for a few seconds.
|
||||
/// </summary>
|
||||
public class ParalyzingBlow : WeaponAbility
|
||||
{
|
||||
public static readonly TimeSpan PlayerFreezeDuration = TimeSpan.FromSeconds(3.0);
|
||||
public static readonly TimeSpan NPCFreezeDuration = TimeSpan.FromSeconds(6.0);
|
||||
public static readonly TimeSpan FreezeDelayDuration = TimeSpan.FromSeconds(8.0);
|
||||
// No longer active in pub21:
|
||||
private static readonly Hashtable m_Table = new Hashtable();
|
||||
public ParalyzingBlow()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
public static bool IsImmune(Mobile m)
|
||||
{
|
||||
return m_Table.Contains(m);
|
||||
}
|
||||
|
||||
public static void BeginImmunity(Mobile m, TimeSpan duration)
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if (t != null)
|
||||
t.Stop();
|
||||
|
||||
t = new InternalTimer(m, duration);
|
||||
m_Table[m] = t;
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
public static void EndImmunity(Mobile m)
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if (t != null)
|
||||
t.Stop();
|
||||
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
public override bool RequiresSecondarySkill(Mobile from)
|
||||
{
|
||||
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return true;
|
||||
|
||||
return weapon.Skill != SkillName.Wrestling;
|
||||
}
|
||||
|
||||
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if(defender == null)
|
||||
return false;
|
||||
|
||||
if (defender.Paralyzed)
|
||||
{
|
||||
if (attacker != null)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1061923); // The target is already frozen.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
if (IsImmune(defender)) //Intentionally going after Mana consumption
|
||||
{
|
||||
attacker.SendLocalizedMessage(1070804); // Your target resists paralysis.
|
||||
defender.SendLocalizedMessage(1070813); // You resist paralysis.
|
||||
return;
|
||||
}
|
||||
|
||||
defender.FixedEffect(0x376A, 9, 32);
|
||||
defender.PlaySound(0x204);
|
||||
|
||||
attacker.SendLocalizedMessage(1060163); // You deliver a paralyzing blow!
|
||||
defender.SendLocalizedMessage(1060164); // The attack has temporarily paralyzed you!
|
||||
|
||||
TimeSpan duration = defender.Player ? PlayerFreezeDuration : NPCFreezeDuration;
|
||||
|
||||
// Treat it as paralyze not as freeze, effect must be removed when damaged.
|
||||
defender.Paralyze(duration);
|
||||
|
||||
BeginImmunity(defender, duration + FreezeDelayDuration);
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
public InternalTimer(Mobile m, TimeSpan duration)
|
||||
: base(duration)
|
||||
{
|
||||
this.m_Mobile = m;
|
||||
this.Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndImmunity(this.m_Mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Scripts/Abilities/PsychicAttack.cs
Normal file
88
Scripts/Abilities/PsychicAttack.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class PsychicAttack : WeaponAbility
|
||||
{
|
||||
public PsychicAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 30; } }
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!Validate(attacker) || !CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1074383); // Your shot sends forth a wave of psychic energy.
|
||||
defender.SendLocalizedMessage(1074384); // Your mind is attacked by psychic force!
|
||||
|
||||
defender.FixedParticles(0x3789, 10, 25, 5032, EffectLayer.Head);
|
||||
defender.PlaySound(0x1F8);
|
||||
|
||||
if (m_Registry.ContainsKey(defender))
|
||||
{
|
||||
if (!m_Registry[defender].DoneIncrease)
|
||||
{
|
||||
m_Registry[defender].SpellDamageMalus *= 2;
|
||||
m_Registry[defender].ManaCostMalus *= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_Registry[defender] = new PsychicAttackTimer(defender);
|
||||
|
||||
BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack);
|
||||
|
||||
string args = String.Format("{0}\t{1}", m_Registry[defender].SpellDamageMalus, m_Registry[defender].ManaCostMalus);
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.PsychicAttack, 1151296, 1151297, args));
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, PsychicAttackTimer> m_Registry = new Dictionary<Mobile, PsychicAttackTimer>();
|
||||
public static Dictionary<Mobile, PsychicAttackTimer> Registry { get { return m_Registry; } }
|
||||
|
||||
public static void RemoveEffects(Mobile defender)
|
||||
{
|
||||
if (defender == null)
|
||||
return;
|
||||
|
||||
BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack);
|
||||
|
||||
if (m_Registry.ContainsKey(defender))
|
||||
m_Registry.Remove(defender);
|
||||
|
||||
defender.SendLocalizedMessage(1150292); // You recover from the effects of the psychic attack.
|
||||
}
|
||||
|
||||
public class PsychicAttackTimer : Timer
|
||||
{
|
||||
private Mobile m_Defender;
|
||||
private int m_SpellDamageMalus;
|
||||
private int m_ManaCostMalus;
|
||||
private bool m_DoneIncrease;
|
||||
|
||||
public int SpellDamageMalus { get { return m_SpellDamageMalus; } set { m_SpellDamageMalus = value; m_DoneIncrease = true; } }
|
||||
public int ManaCostMalus { get { return m_ManaCostMalus; } set { m_ManaCostMalus = value; m_DoneIncrease = true; } }
|
||||
public bool DoneIncrease { get { return m_DoneIncrease; } }
|
||||
|
||||
public PsychicAttackTimer(Mobile defender)
|
||||
: base(TimeSpan.FromSeconds(10))
|
||||
{
|
||||
m_Defender = defender;
|
||||
m_SpellDamageMalus = 15;
|
||||
m_ManaCostMalus = 15;
|
||||
m_DoneIncrease = false;
|
||||
Start();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
RemoveEffects(m_Defender);
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Scripts/Abilities/RidingSwipe.cs
Normal file
101
Scripts/Abilities/RidingSwipe.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// If you are on foot, dismounts your opponent and damage the ethereal's rider or the
|
||||
/// living mount(which must be healed before ridden again). If you are mounted, damages
|
||||
/// and stuns the mounted opponent.
|
||||
/// </summary>
|
||||
public class RidingSwipe : WeaponAbility
|
||||
{
|
||||
public RidingSwipe()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
public override bool RequiresSE
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override bool CheckSkills(Mobile from)
|
||||
{
|
||||
if (this.GetSkill(from, SkillName.Bushido) < 50.0)
|
||||
{
|
||||
from.SendLocalizedMessage(1070768, "50"); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckSkills(from);
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!defender.Mounted && !defender.Flying && (!Core.ML || !Server.Spells.Ninjitsu.AnimalForm.UnderTransformation(defender)))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060848); // This attack only works on mounted targets
|
||||
ClearCurrentAbility(attacker);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
int amount = 10 + (int)(10.0 * (attacker.Skills[SkillName.Bushido].Value - 50.0) / 70.0 + 5);
|
||||
|
||||
if (!attacker.Mounted)
|
||||
{
|
||||
BlockMountType type = BlockMountType.RidingSwipe;
|
||||
IMount mount = defender.Mount;
|
||||
|
||||
if (Core.SA)
|
||||
{
|
||||
if (defender.Flying)
|
||||
{
|
||||
type = BlockMountType.RidingSwipeFlying;
|
||||
}
|
||||
else if (mount is EtherealMount)
|
||||
{
|
||||
type = BlockMountType.RidingSwipeEthereal;
|
||||
}
|
||||
}
|
||||
|
||||
Server.Items.Dismount.DoDismount(attacker, defender, mount, 10, type);
|
||||
|
||||
if(mount is Mobile)
|
||||
AOS.Damage((Mobile)mount, attacker, amount, 100, 0, 0, 0, 0);
|
||||
|
||||
defender.PlaySound(0x140);
|
||||
defender.FixedParticles(0x3728, 10, 15, 9955, EffectLayer.Waist);
|
||||
}
|
||||
else
|
||||
{
|
||||
AOS.Damage(defender, attacker, amount, 100, 0, 0, 0, 0);
|
||||
|
||||
if (Server.Items.ParalyzingBlow.IsImmune(defender)) //Does it still do damage?
|
||||
{
|
||||
attacker.SendLocalizedMessage(1070804); // Your target resists paralysis.
|
||||
defender.SendLocalizedMessage(1070813); // You resist paralysis.
|
||||
}
|
||||
else
|
||||
{
|
||||
defender.Paralyze(TimeSpan.FromSeconds(3.0));
|
||||
Server.Items.ParalyzingBlow.BeginImmunity(defender, Server.Items.ParalyzingBlow.FreezeDelayDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
768
Scripts/Abilities/SAPropEffects.cs
Normal file
768
Scripts/Abilities/SAPropEffects.cs
Normal file
@@ -0,0 +1,768 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Ninjitsu;
|
||||
using Server.Network;
|
||||
using Server.Spells.SkillMasteries;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public enum EffectsType
|
||||
{
|
||||
BattleLust,
|
||||
SoulCharge,
|
||||
DamageEater,
|
||||
Splintering,
|
||||
Searing,
|
||||
Bane,
|
||||
BoneBreaker,
|
||||
Swarm,
|
||||
Sparks,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for complex weapon/armor properties introduced in Stagyian Abyss
|
||||
/// </summary>
|
||||
public class PropertyEffect
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
private Mobile m_Victim;
|
||||
private Item m_Owner;
|
||||
private EffectsType m_Effect;
|
||||
private TimeSpan m_Duration;
|
||||
private TimeSpan m_TickDuration;
|
||||
private Timer m_Timer;
|
||||
|
||||
public Mobile Mobile { get { return m_Mobile; } }
|
||||
public Mobile Victim { get { return m_Victim; } }
|
||||
public Item Owner { get { return m_Owner; } }
|
||||
public EffectsType Effect { get { return m_Effect; } }
|
||||
public TimeSpan Duration { get { return m_Duration; } }
|
||||
public TimeSpan TickDuration { get { return m_TickDuration; } }
|
||||
public Timer Timer { get { return m_Timer; } }
|
||||
|
||||
private static List<PropertyEffect> m_Effects = new List<PropertyEffect>();
|
||||
public static List<PropertyEffect> Effects { get { return m_Effects; } }
|
||||
|
||||
public PropertyEffect(Mobile from, Mobile victim, Item owner, EffectsType effect, TimeSpan duration, TimeSpan tickduration)
|
||||
{
|
||||
m_Mobile = from;
|
||||
m_Victim = victim;
|
||||
m_Owner = owner;
|
||||
m_Effect = effect;
|
||||
m_Duration = duration;
|
||||
m_TickDuration = tickduration;
|
||||
|
||||
m_Effects.Add(this);
|
||||
|
||||
if (m_TickDuration > TimeSpan.MinValue)
|
||||
StartTimer();
|
||||
}
|
||||
|
||||
public virtual void RemoveEffects()
|
||||
{
|
||||
StopTimer();
|
||||
|
||||
if(m_Effects.Contains(this))
|
||||
m_Effects.Remove(this);
|
||||
}
|
||||
|
||||
public void StartTimer()
|
||||
{
|
||||
if (m_Timer == null)
|
||||
{
|
||||
m_Timer = new InternalTimer(this);
|
||||
m_Timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void StopTimer()
|
||||
{
|
||||
if (m_Timer != null)
|
||||
{
|
||||
m_Timer.Stop();
|
||||
m_Timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEquipped()
|
||||
{
|
||||
if (m_Owner == null)
|
||||
return false;
|
||||
|
||||
return m_Mobile.FindItemOnLayer(m_Owner.Layer) == m_Owner;
|
||||
}
|
||||
|
||||
public virtual void OnTick()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnDamaged(int damage)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnDamage(int damage, int phys, int fire, int cold, int poison, int energy, int direct)
|
||||
{
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private PropertyEffect m_Effect;
|
||||
private DateTime m_Expires;
|
||||
|
||||
public InternalTimer(PropertyEffect effect)
|
||||
: base(effect.TickDuration, effect.TickDuration)
|
||||
{
|
||||
m_Effect = effect;
|
||||
|
||||
if (effect != null && effect.Duration > TimeSpan.MinValue)
|
||||
m_Expires = DateTime.UtcNow + effect.Duration;
|
||||
else
|
||||
m_Expires = DateTime.MinValue;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Effect.Mobile == null || (m_Effect.Mobile.Deleted || !m_Effect.Mobile.Alive || m_Effect.Mobile.IsDeadBondedPet))
|
||||
{
|
||||
m_Effect.RemoveEffects();
|
||||
}
|
||||
else if (m_Effect.Victim != null && (m_Effect.Victim.Deleted || !m_Effect.Victim.Alive || m_Effect.Mobile.IsDeadBondedPet))
|
||||
{
|
||||
m_Effect.RemoveEffects();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Effect.OnTick();
|
||||
|
||||
if (m_Expires > DateTime.MinValue && m_Expires <= DateTime.UtcNow)
|
||||
{
|
||||
m_Effect.RemoveEffects();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsUnderEffects(Mobile from, EffectsType effect)
|
||||
{
|
||||
foreach (PropertyEffect e in m_Effects)
|
||||
{
|
||||
if (e.Mobile == from && e.Effect == effect)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static T GetContext<T>(Mobile from, EffectsType type) where T : PropertyEffect
|
||||
{
|
||||
return m_Effects.FirstOrDefault(e => e.Mobile == from && e.Effect == type) as T;
|
||||
}
|
||||
|
||||
public static T GetContext<T>(Mobile from, Mobile victim, EffectsType type) where T : PropertyEffect
|
||||
{
|
||||
return m_Effects.FirstOrDefault(e => e.Mobile == from && e.Victim == victim && e.Effect == type) as T;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetContexts<T>(Mobile victim, EffectsType type) where T : PropertyEffect
|
||||
{
|
||||
foreach (PropertyEffect effect in m_Effects.OfType<T>().Where(e => e.Victim == victim))
|
||||
{
|
||||
yield return effect as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SoulChargeContext : PropertyEffect
|
||||
{
|
||||
private bool m_Active;
|
||||
|
||||
public SoulChargeContext(Mobile from, Item item)
|
||||
: base(from, null, item, EffectsType.SoulCharge, TimeSpan.FromSeconds(40), TimeSpan.FromSeconds(40))
|
||||
{
|
||||
m_Active = true;
|
||||
}
|
||||
|
||||
public override void OnDamaged(int damage)
|
||||
{
|
||||
if (m_Active && IsEquipped() && this.Mobile != null)
|
||||
{
|
||||
double mod = BaseFishPie.IsUnderEffects(this.Mobile, FishPieEffect.SoulCharge) ? .50 : .30;
|
||||
this.Mobile.Mana += (int)Math.Min(this.Mobile.ManaMax, damage * mod);
|
||||
m_Active = false;
|
||||
|
||||
Server.Effects.SendTargetParticles(this.Mobile, 0x375A, 0x1, 0xA, 0x71, 0x2, 0x1AE9, (EffectLayer)0, 0);
|
||||
|
||||
this.Mobile.SendLocalizedMessage(1113636); //The soul charge effect converts some of the damage you received into mana.
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
BaseShield shield = defender.FindItemOnLayer(Layer.TwoHanded) as BaseShield;
|
||||
|
||||
if (shield != null && shield.ArmorAttributes.SoulCharge > 0 && shield.ArmorAttributes.SoulCharge > Utility.Random(100))
|
||||
{
|
||||
SoulChargeContext sc = PropertyEffect.GetContext<SoulChargeContext>(defender, EffectsType.SoulCharge);
|
||||
|
||||
if (sc == null)
|
||||
sc = new SoulChargeContext(defender, shield);
|
||||
|
||||
sc.OnDamaged(damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum DamageType
|
||||
{
|
||||
Kinetic,
|
||||
Fire,
|
||||
Cold,
|
||||
Poison,
|
||||
Energy,
|
||||
AllTypes
|
||||
}
|
||||
|
||||
public class DamageEaterContext : PropertyEffect
|
||||
{
|
||||
private int m_Charges;
|
||||
|
||||
public DamageEaterContext(Mobile mobile)
|
||||
: base(mobile, null, null, EffectsType.DamageEater, TimeSpan.MinValue, TimeSpan.FromSeconds(10))
|
||||
{
|
||||
m_Charges = 0;
|
||||
}
|
||||
|
||||
public override void OnDamage(int damage, int phys, int fire, int cold, int poison, int energy, int direct)
|
||||
{
|
||||
if (m_Charges >= 20)
|
||||
return;
|
||||
|
||||
double pd = 0; double fd = 0;
|
||||
double cd = 0; double pod = 0;
|
||||
double ed = 0; double dd = 0;
|
||||
|
||||
double k = (double)GetValue(DamageType.Kinetic, this.Mobile) / 100;
|
||||
double f = (double)GetValue(DamageType.Fire, this.Mobile) / 100;
|
||||
double c = (double)GetValue(DamageType.Cold, this.Mobile) / 100;
|
||||
double p = (double)GetValue(DamageType.Poison, this.Mobile) / 100;
|
||||
double e = (double)GetValue(DamageType.Energy, this.Mobile) / 100;
|
||||
double a = (double)GetValue(DamageType.AllTypes, this.Mobile) / 100;
|
||||
|
||||
if (phys > 0 && (k > 0 || a > 0))
|
||||
{
|
||||
pd = damage * ((double)phys / 100);
|
||||
|
||||
if (k >= a)
|
||||
DelayHeal(Math.Min(pd * k, pd * .3));
|
||||
else
|
||||
DelayHeal(Math.Min(pd * a, pd * .18));
|
||||
|
||||
m_Charges++;
|
||||
}
|
||||
|
||||
if (fire > 0 && (f > 0 || a > 0))
|
||||
{
|
||||
fd = damage * ((double)fire / 100);
|
||||
|
||||
if (f >= a)
|
||||
DelayHeal(Math.Min(fd * f, fd * .3));
|
||||
else
|
||||
DelayHeal(Math.Min(fd * a, fd * .18));
|
||||
|
||||
m_Charges++;
|
||||
}
|
||||
|
||||
if (cold > 0 && (c > 0 || a > 0))
|
||||
{
|
||||
cd = damage * ((double)cold / 100);
|
||||
|
||||
if (c >= a)
|
||||
DelayHeal(Math.Min(cd * c, cd * .3));
|
||||
else
|
||||
DelayHeal(Math.Min(cd * a, cd * .18));
|
||||
|
||||
m_Charges++;
|
||||
}
|
||||
|
||||
if (poison > 0 && (p > 0 || a > 0))
|
||||
{
|
||||
pod = damage * ((double)poison / 100);
|
||||
|
||||
if (p >= a)
|
||||
DelayHeal(Math.Min(pod * p, pod * .3));
|
||||
else
|
||||
DelayHeal(Math.Min(pod * a, pod * .18));
|
||||
|
||||
m_Charges++;
|
||||
}
|
||||
|
||||
if (energy > 0 && (e > 0 || a > 0))
|
||||
{
|
||||
ed = damage * ((double)energy / 100);
|
||||
|
||||
if (e >= a)
|
||||
DelayHeal(Math.Min(ed * e, ed * .3));
|
||||
else
|
||||
DelayHeal(Math.Min(ed * a, ed * .18));
|
||||
|
||||
m_Charges++;
|
||||
}
|
||||
|
||||
if (direct > 0 && a > 0)
|
||||
{
|
||||
dd = damage * ((double)direct / 100);
|
||||
|
||||
DelayHeal(Math.Min(dd * a, dd * .18));
|
||||
m_Charges++;
|
||||
}
|
||||
}
|
||||
|
||||
public void DelayHeal(double toHeal)
|
||||
{
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(3), new TimerStateCallback(DoHeal), toHeal);
|
||||
}
|
||||
|
||||
public void DoHeal(object obj)
|
||||
{
|
||||
double dam = (double)obj;
|
||||
|
||||
if (dam < 0)
|
||||
return;
|
||||
|
||||
Mobile.Heal((int)dam, Mobile, false);
|
||||
Mobile.SendLocalizedMessage(1113617); // Some of the damage you received has been converted to heal you.
|
||||
Server.Effects.SendPacket(Mobile.Location, Mobile.Map, new ParticleEffect(EffectType.FixedFrom, Mobile.Serial, Serial.Zero, 0x375A, Mobile.Location, Mobile.Location, 1, 10, false, false, 33, 0, 2, 6889, 1, Mobile.Serial, 45, 0));
|
||||
m_Charges--;
|
||||
}
|
||||
|
||||
public override void OnTick()
|
||||
{
|
||||
if (m_Charges <= 0)
|
||||
RemoveEffects();
|
||||
}
|
||||
|
||||
public static bool HasValue(Mobile from)
|
||||
{
|
||||
if (GetValue(DamageType.Kinetic, from) > 0)
|
||||
return true;
|
||||
if (GetValue(DamageType.Fire, from) > 0)
|
||||
return true;
|
||||
if (GetValue(DamageType.Cold, from) > 0)
|
||||
return true;
|
||||
if (GetValue(DamageType.Poison, from) > 0)
|
||||
return true;
|
||||
if (GetValue(DamageType.Energy, from) > 0)
|
||||
return true;
|
||||
if (GetValue(DamageType.AllTypes, from) > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int GetValue(DamageType type, Mobile from)
|
||||
{
|
||||
if (from == null)
|
||||
return 0;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DamageType.Kinetic: return (int)SAAbsorptionAttributes.GetValue(from, SAAbsorptionAttribute.EaterKinetic);
|
||||
case DamageType.Fire: return (int)SAAbsorptionAttributes.GetValue(from, SAAbsorptionAttribute.EaterFire);
|
||||
case DamageType.Cold: return (int)SAAbsorptionAttributes.GetValue(from, SAAbsorptionAttribute.EaterCold);
|
||||
case DamageType.Poison: return (int)SAAbsorptionAttributes.GetValue(from, SAAbsorptionAttribute.EaterPoison);
|
||||
case DamageType.Energy: return (int)SAAbsorptionAttributes.GetValue(from, SAAbsorptionAttribute.EaterEnergy);
|
||||
case DamageType.AllTypes: return (int)SAAbsorptionAttributes.GetValue(from, SAAbsorptionAttribute.EaterDamage);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void CheckDamage(Mobile from, int damage, int phys, int fire, int cold, int pois, int ergy, int direct)
|
||||
{
|
||||
DamageEaterContext context = PropertyEffect.GetContext<DamageEaterContext>(from, EffectsType.DamageEater);
|
||||
|
||||
if (context == null && HasValue(from))
|
||||
context = new DamageEaterContext(from);
|
||||
|
||||
if (context != null)
|
||||
context.OnDamage(damage, phys, fire, cold, pois, ergy, direct);
|
||||
}
|
||||
}
|
||||
|
||||
public class SplinteringWeaponContext : PropertyEffect
|
||||
{
|
||||
public static List<Mobile> BleedImmune { get; set; } = new List<Mobile>();
|
||||
|
||||
public SplinteringWeaponContext(Mobile from, Mobile defender, Item weapon)
|
||||
: base(from, defender, weapon, EffectsType.Splintering, TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(4))
|
||||
{
|
||||
StartForceWalk(defender);
|
||||
|
||||
if (Core.EJ)
|
||||
{
|
||||
if (!(defender is PlayerMobile) || !IsBleedImmune(defender))
|
||||
{
|
||||
BleedAttack.BeginBleed(defender, from, true);
|
||||
AddBleedImmunity(defender);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BleedAttack.BeginBleed(defender, from, true);
|
||||
}
|
||||
|
||||
defender.SendLocalizedMessage(1112486); // A shard of the brittle weapon has become lodged in you!
|
||||
from.SendLocalizedMessage(1113077); // A shard of your blade breaks off and sticks in your opponent!
|
||||
|
||||
Server.Effects.PlaySound(defender.Location, defender.Map, 0x1DF);
|
||||
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.SplinteringEffect, 1154670, 1152144, TimeSpan.FromSeconds(10), defender));
|
||||
}
|
||||
|
||||
public override void OnTick()
|
||||
{
|
||||
base.OnTick();
|
||||
|
||||
BuffInfo.RemoveBuff(Victim, BuffIcon.SplinteringEffect);
|
||||
}
|
||||
|
||||
public void StartForceWalk(Mobile m)
|
||||
{
|
||||
if (m.NetState != null && m.AccessLevel < AccessLevel.GameMaster)
|
||||
m.SendSpeedControl(SpeedControlType.WalkSpeed);
|
||||
}
|
||||
|
||||
public void EndForceWalk(Mobile m)
|
||||
{
|
||||
m.SendSpeedControl(SpeedControlType.Disable);
|
||||
}
|
||||
|
||||
public override void RemoveEffects()
|
||||
{
|
||||
EndForceWalk(Victim);
|
||||
Victim.SendLocalizedMessage(1112487); // The shard is successfully removed.
|
||||
|
||||
base.RemoveEffects();
|
||||
}
|
||||
|
||||
public static bool CheckHit(Mobile attacker, Mobile defender, WeaponAbility ability, Item weapon)
|
||||
{
|
||||
if (defender == null || (Core.EJ && (ability == WeaponAbility.Disarm || ability == WeaponAbility.InfectiousStrike || SkillMasterySpell.HasSpell(attacker, typeof(SkillMasterySpell)))))
|
||||
return false;
|
||||
|
||||
SplinteringWeaponContext context = PropertyEffect.GetContext<SplinteringWeaponContext>(attacker, defender, EffectsType.Splintering);
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
new SplinteringWeaponContext(attacker, defender, weapon);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsBleedImmune(Mobile m)
|
||||
{
|
||||
return BleedImmune.Contains(m);
|
||||
}
|
||||
|
||||
public static void AddBleedImmunity(Mobile m)
|
||||
{
|
||||
if (!(m is PlayerMobile) || BleedImmune.Contains(m))
|
||||
return;
|
||||
|
||||
BleedImmune.Add(m);
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(16), () => BleedImmune.Remove(m));
|
||||
}
|
||||
}
|
||||
|
||||
public class SearingWeaponContext : PropertyEffect
|
||||
{
|
||||
public static int Damage { get { return Utility.RandomMinMax(10, 15); } }
|
||||
|
||||
public SearingWeaponContext(Mobile from, Mobile defender)
|
||||
: base(from, defender, null, EffectsType.Searing, TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(4))
|
||||
{
|
||||
from.SendLocalizedMessage(1151177); //The searing attack cauterizes the wound on impact.
|
||||
}
|
||||
|
||||
public static void CheckHit(Mobile attacker, Mobile defender)
|
||||
{
|
||||
SearingWeaponContext context = PropertyEffect.GetContext<SearingWeaponContext>(attacker, defender, EffectsType.Searing);
|
||||
|
||||
if (context == null)
|
||||
new SearingWeaponContext(attacker, defender);
|
||||
}
|
||||
|
||||
public static bool HasContext(Mobile defender)
|
||||
{
|
||||
return PropertyEffect.GetContext<SearingWeaponContext>(defender, EffectsType.Searing) != null;
|
||||
}
|
||||
}
|
||||
|
||||
public class BoneBreakerContext : PropertyEffect
|
||||
{
|
||||
public static Dictionary<Mobile, DateTime> _Immunity;
|
||||
|
||||
public BoneBreakerContext(Mobile attacker, Mobile defender, Item weapon)
|
||||
: base(attacker, defender, weapon, EffectsType.BoneBreaker, TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(1))
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnTick()
|
||||
{
|
||||
int toReduce = Victim.StamMax / 10;
|
||||
|
||||
if (Victim.Stam - toReduce >= 3)
|
||||
Victim.Stam -= toReduce;
|
||||
else
|
||||
Victim.Stam = Math.Max(1, Victim.Stam - 1);
|
||||
}
|
||||
|
||||
public override void RemoveEffects()
|
||||
{
|
||||
base.RemoveEffects();
|
||||
|
||||
AddImmunity(Victim);
|
||||
}
|
||||
|
||||
public static int CheckHit(Mobile attacker, Mobile defender)
|
||||
{
|
||||
int mana = (int)(30.0 * ((double)(AosAttributes.GetValue(attacker, AosAttribute.LowerManaCost) + BaseArmor.GetInherentLowerManaCost(attacker)) / 100.0));
|
||||
int damage = 0;
|
||||
|
||||
if (attacker.Mana >= mana)
|
||||
{
|
||||
attacker.Mana -= mana;
|
||||
damage += 50;
|
||||
|
||||
defender.SendLocalizedMessage(1157317); // The attack shatters your bones!
|
||||
}
|
||||
|
||||
if (IsImmune(defender))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1157316); // Your target is currently immune to bone breaking!
|
||||
return damage;
|
||||
}
|
||||
|
||||
if (20 > Utility.Random(100))
|
||||
{
|
||||
BoneBreakerContext context = PropertyEffect.GetContext<BoneBreakerContext>(attacker, defender, EffectsType.BoneBreaker);
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
new BoneBreakerContext(attacker, defender, null);
|
||||
defender.SendLocalizedMessage(1157363); // Your bones are broken! Stamina drain over time!
|
||||
|
||||
defender.PlaySound(0x204);
|
||||
defender.FixedEffect(0x376A, 9, 32);
|
||||
|
||||
defender.FixedEffect(0x3779, 10, 20, 1365, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
public static bool IsImmune(Mobile m)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
return false;
|
||||
|
||||
List<Mobile> list = new List<Mobile>(_Immunity.Keys);
|
||||
|
||||
foreach (Mobile mob in list)
|
||||
{
|
||||
if (_Immunity[mob] < DateTime.UtcNow)
|
||||
_Immunity.Remove(mob);
|
||||
}
|
||||
|
||||
ColUtility.Free(list);
|
||||
|
||||
return _Immunity.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static void AddImmunity(Mobile m)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
_Immunity = new Dictionary<Mobile, DateTime>();
|
||||
|
||||
_Immunity[m] = DateTime.UtcNow + TimeSpan.FromSeconds(60);
|
||||
}
|
||||
}
|
||||
|
||||
public class SwarmContext : PropertyEffect
|
||||
{
|
||||
public static Dictionary<Mobile, DateTime> _Immunity;
|
||||
|
||||
private int _ID;
|
||||
|
||||
public SwarmContext(Mobile attacker, Mobile defender, Item weapon)
|
||||
: base(attacker, defender, weapon, EffectsType.Swarm, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(5))
|
||||
{
|
||||
_ID = Utility.RandomMinMax(2331, 2339);
|
||||
|
||||
DoEffects();
|
||||
}
|
||||
|
||||
public static void CheckHit(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (IsImmune(defender))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1157322); // Your target is currently immune to swarm!
|
||||
return;
|
||||
}
|
||||
|
||||
SwarmContext context = PropertyEffect.GetContext<SwarmContext>(attacker, defender, EffectsType.Swarm);
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
context.RemoveEffects();
|
||||
}
|
||||
|
||||
context = new SwarmContext(attacker, defender, null);
|
||||
|
||||
defender.NonlocalOverheadMessage(MessageType.Regular, 0x5C, 1114447, defender.Name); // * ~1_NAME~ is stung by a swarm of insects *
|
||||
defender.LocalOverheadMessage(MessageType.Regular, 0x5C, 1071905); // * The swarm of insects bites and stings your flesh! *
|
||||
}
|
||||
|
||||
public override void OnTick()
|
||||
{
|
||||
if (Victim == null || !Victim.Alive)
|
||||
{
|
||||
RemoveEffects();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Victim.FindItemOnLayer(Layer.OneHanded) is Torch)
|
||||
{
|
||||
if (Victim.NetState != null)
|
||||
Victim.LocalOverheadMessage(MessageType.Regular, 0x61, 1071925); // * The open flame begins to scatter the swarm of insects! *
|
||||
}
|
||||
else
|
||||
{
|
||||
DoEffects();
|
||||
}
|
||||
}
|
||||
|
||||
private void DoEffects()
|
||||
{
|
||||
AOS.Damage(Victim, Mobile, 10, 0, 0, 0, 0, 0, 0, 100);
|
||||
Victim.SendLocalizedMessage(1157362); // Biting insects are attacking you!
|
||||
Server.Effects.SendTargetEffect(Victim, _ID, 40);
|
||||
|
||||
Victim.PlaySound(0x00E);
|
||||
Victim.PlaySound(0x1BC);
|
||||
}
|
||||
|
||||
public override void RemoveEffects()
|
||||
{
|
||||
base.RemoveEffects();
|
||||
|
||||
//AddImmunity(Victim);
|
||||
}
|
||||
|
||||
public static void CheckRemove(Mobile victim)
|
||||
{
|
||||
ColUtility.ForEach(PropertyEffect.GetContexts<SwarmContext>(victim, EffectsType.Swarm), context =>
|
||||
{
|
||||
context.RemoveEffects();
|
||||
});
|
||||
}
|
||||
|
||||
public static bool IsImmune(Mobile m)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
return false;
|
||||
|
||||
List<Mobile> list = new List<Mobile>(_Immunity.Keys);
|
||||
|
||||
foreach (Mobile mob in list)
|
||||
{
|
||||
if (_Immunity[mob] < DateTime.UtcNow)
|
||||
_Immunity.Remove(mob);
|
||||
}
|
||||
|
||||
ColUtility.Free(list);
|
||||
|
||||
return _Immunity.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static void AddImmunity(Mobile m)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
_Immunity = new Dictionary<Mobile, DateTime>();
|
||||
|
||||
_Immunity[m] = DateTime.UtcNow + TimeSpan.FromSeconds(60);
|
||||
}
|
||||
}
|
||||
|
||||
public class SparksContext : PropertyEffect
|
||||
{
|
||||
public static Dictionary<Mobile, DateTime> _Immunity;
|
||||
|
||||
public SparksContext(Mobile attacker, Mobile defender, Item weapon)
|
||||
: base(attacker, defender, weapon, EffectsType.Sparks, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1))
|
||||
{
|
||||
}
|
||||
|
||||
public static void CheckHit(Mobile attacker, Mobile defender)
|
||||
{
|
||||
if (IsImmune(defender))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1157324); // Your target is currently immune to sparks!
|
||||
return;
|
||||
}
|
||||
|
||||
SparksContext context = PropertyEffect.GetContext<SparksContext>(attacker, defender, EffectsType.Sparks);
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
context = new SparksContext(attacker, defender, null);
|
||||
|
||||
attacker.PlaySound(0x20A);
|
||||
defender.FixedParticles(0x3818, 1, 11, 0x13A8, 0, 0, EffectLayer.Waist);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnTick()
|
||||
{
|
||||
AOS.Damage(Victim, Mobile, Utility.RandomMinMax(20, 40), 0, 0, 0, 0, 100);
|
||||
}
|
||||
|
||||
public override void RemoveEffects()
|
||||
{
|
||||
base.RemoveEffects();
|
||||
|
||||
//AddImmunity(Victim);
|
||||
}
|
||||
|
||||
public static bool IsImmune(Mobile m)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
return false;
|
||||
|
||||
List<Mobile> list = new List<Mobile>(_Immunity.Keys);
|
||||
|
||||
foreach (Mobile mob in list)
|
||||
{
|
||||
if (_Immunity[mob] < DateTime.UtcNow)
|
||||
_Immunity.Remove(mob);
|
||||
}
|
||||
|
||||
ColUtility.Free(list);
|
||||
|
||||
return _Immunity.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static void AddImmunity(Mobile m)
|
||||
{
|
||||
if (_Immunity == null)
|
||||
_Immunity = new Dictionary<Mobile, DateTime>();
|
||||
|
||||
_Immunity[m] = DateTime.UtcNow + TimeSpan.FromSeconds(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Scripts/Abilities/SerpentArrow.cs
Normal file
80
Scripts/Abilities/SerpentArrow.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class SerpentArrow : WeaponAbility
|
||||
{
|
||||
public SerpentArrow()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return SkillName.Poisoning;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
defender.SendLocalizedMessage(1112369); // You have been poisoned by a lethal arrow!
|
||||
|
||||
int level;
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
if (attacker.InRange(defender, 2))
|
||||
{
|
||||
int total = (attacker.Skills.Poisoning.Fixed) / 2;
|
||||
|
||||
if (total >= 1000)
|
||||
level = 3;
|
||||
else if (total > 850)
|
||||
level = 2;
|
||||
else if (total > 650)
|
||||
level = 1;
|
||||
else
|
||||
level = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double total = attacker.Skills[SkillName.Poisoning].Value;
|
||||
|
||||
double dist = attacker.GetDistanceToSqrt(defender);
|
||||
|
||||
if (dist >= 3.0)
|
||||
total -= (dist - 3.0) * 10.0;
|
||||
|
||||
if (total >= 200.0 && 1 > Utility.Random(10))
|
||||
level = 3;
|
||||
else if (total > (Core.AOS ? 170.1 : 170.0))
|
||||
level = 2;
|
||||
else if (total > (Core.AOS ? 130.1 : 130.0))
|
||||
level = 1;
|
||||
else
|
||||
level = 0;
|
||||
}
|
||||
|
||||
defender.ApplyPoison(attacker, Poison.GetPoison(level));
|
||||
|
||||
defender.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
|
||||
defender.PlaySound(0x474);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Scripts/Abilities/ShadowStrike.cs
Normal file
70
Scripts/Abilities/ShadowStrike.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// This powerful ability requires secondary skills to activate.
|
||||
/// Successful use of Shadowstrike deals extra damage to the target <20> and renders the attacker invisible!
|
||||
/// Only those who are adept at the art of stealth will be able to use this ability.
|
||||
/// </summary>
|
||||
public class ShadowStrike : WeaponAbility
|
||||
{
|
||||
public ShadowStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
public override double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.25;
|
||||
}
|
||||
}
|
||||
public override bool RequiresSecondarySkill(Mobile from)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CheckSkills(Mobile from)
|
||||
{
|
||||
if (!base.CheckSkills(from))
|
||||
return false;
|
||||
|
||||
Skill skill = from.Skills[SkillName.Stealth];
|
||||
|
||||
if (skill != null && skill.Value >= 80.0)
|
||||
return true;
|
||||
|
||||
from.SendLocalizedMessage(1060183); // You lack the required stealth to perform that attack
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1060078); // You strike and hide in the shadows!
|
||||
defender.SendLocalizedMessage(1060079); // You are dazed by the attack and your attacker vanishes!
|
||||
|
||||
Effects.SendLocationParticles(EffectItem.Create(attacker.Location, attacker.Map, EffectItem.DefaultDuration), 0x376A, 8, 12, 9943);
|
||||
attacker.PlaySound(0x482);
|
||||
|
||||
defender.FixedEffect(0x37BE, 20, 25);
|
||||
|
||||
attacker.Combatant = null;
|
||||
attacker.Warmode = false;
|
||||
attacker.Hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
136
Scripts/Abilities/SlayerEntry.cs
Normal file
136
Scripts/Abilities/SlayerEntry.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class SlayerEntry
|
||||
{
|
||||
private static readonly int[] m_AosTitles = new int[]
|
||||
{
|
||||
1060479, // undead slayer
|
||||
1060470, // orc slayer
|
||||
1060480, // troll slayer
|
||||
1060468, // ogre slayer
|
||||
1060472, // repond slayer
|
||||
1060462, // dragon slayer
|
||||
1060478, // terathan slayer
|
||||
1060475, // snake slayer
|
||||
1060467, // lizardman slayer
|
||||
1060473, // reptile slayer
|
||||
1060460, // demon slayer
|
||||
1060466, // gargoyle slayer
|
||||
1017396, // Balron Damnation
|
||||
1060461, // demon slayer
|
||||
1060469, // ophidian slayer
|
||||
1060477, // spider slayer
|
||||
1060474, // scorpion slayer
|
||||
1060458, // arachnid slayer
|
||||
1060465, // fire elemental slayer
|
||||
1060481, // water elemental slayer
|
||||
1060457, // air elemental slayer
|
||||
1060471, // poison elemental slayer
|
||||
1060463, // earth elemental slayer
|
||||
1060459, // blood elemental slayer
|
||||
1060476, // snow elemental slayer
|
||||
1060464, // elemental slayer
|
||||
1070855, // fey slayer
|
||||
1156240, // dinosaur slayer
|
||||
1156241, // myrmidex slayer
|
||||
1156126, // Eodon Slayer
|
||||
1156347 // Eodon Tribe Slayer
|
||||
};
|
||||
private static readonly int[] m_OldTitles = new int[]
|
||||
{
|
||||
1017384, // Silver
|
||||
1017385, // Orc Slaying
|
||||
1017386, // Troll Slaughter
|
||||
1017387, // Ogre Thrashing
|
||||
1017388, // Repond
|
||||
1017389, // Dragon Slaying
|
||||
1017390, // Terathan
|
||||
1017391, // Snake's Bane
|
||||
1017392, // Lizardman Slaughter
|
||||
1017393, // Reptilian Death
|
||||
1017394, // Daemon Dismissal
|
||||
1017395, // Gargoyle's Foe
|
||||
1017396, // Balron Damnation
|
||||
1017397, // Exorcism
|
||||
1017398, // Ophidian
|
||||
1017399, // Spider's Death
|
||||
1017400, // Scorpion's Bane
|
||||
1017401, // Arachnid Doom
|
||||
1017402, // Flame Dousing
|
||||
1017403, // Water Dissipation
|
||||
1017404, // Vacuum
|
||||
1017405, // Elemental Health
|
||||
1017406, // Earth Shatter
|
||||
1017407, // Blood Drinking
|
||||
1017408, // Summer Wind
|
||||
1017409, // Elemental Ban
|
||||
1070855 // fey slayer
|
||||
};
|
||||
private readonly SlayerName m_Name;
|
||||
private readonly Type[] m_Types;
|
||||
private SlayerGroup m_Group;
|
||||
public SlayerEntry(SlayerName name, params Type[] types)
|
||||
{
|
||||
this.m_Name = name;
|
||||
this.m_Types = types;
|
||||
}
|
||||
|
||||
public SlayerGroup Group
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_Group;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_Group = value;
|
||||
}
|
||||
}
|
||||
public SlayerName Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_Name;
|
||||
}
|
||||
}
|
||||
public Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_Types;
|
||||
}
|
||||
}
|
||||
public int Title
|
||||
{
|
||||
get
|
||||
{
|
||||
int[] titles = (Core.AOS ? m_AosTitles : m_OldTitles);
|
||||
|
||||
return titles[(int)this.m_Name - 1];
|
||||
}
|
||||
}
|
||||
public bool Slays(Mobile m)
|
||||
{
|
||||
|
||||
if (m.SpecialSlayerMechanics)
|
||||
{
|
||||
if (m.SlayerVulnerabilities.Contains(m_Name.ToString()))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
Type t = m.GetType();
|
||||
|
||||
for (int i = 0; i < this.m_Types.Length; ++i)
|
||||
{
|
||||
if (this.m_Types[i].IsAssignableFrom(t))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
774
Scripts/Abilities/SlayerGroup.cs
Normal file
774
Scripts/Abilities/SlayerGroup.cs
Normal file
@@ -0,0 +1,774 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Engines.Shadowguard;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class SlayerGroup
|
||||
{
|
||||
private static SlayerEntry[] m_TotalEntries;
|
||||
private static SlayerGroup[] m_Groups;
|
||||
private SlayerGroup[] m_Opposition;
|
||||
private SlayerEntry m_Super;
|
||||
private SlayerEntry[] m_Entries;
|
||||
private Type[] m_FoundOn;
|
||||
|
||||
public SlayerGroup()
|
||||
{
|
||||
}
|
||||
|
||||
static SlayerGroup()
|
||||
{
|
||||
SlayerGroup humanoid = new SlayerGroup();
|
||||
SlayerGroup undead = new SlayerGroup();
|
||||
SlayerGroup elemental = new SlayerGroup();
|
||||
SlayerGroup abyss = new SlayerGroup();
|
||||
SlayerGroup arachnid = new SlayerGroup();
|
||||
SlayerGroup reptilian = new SlayerGroup();
|
||||
SlayerGroup fey = new SlayerGroup();
|
||||
SlayerGroup eodon = new SlayerGroup();
|
||||
SlayerGroup eodonTribe = new SlayerGroup();
|
||||
SlayerGroup dino = new SlayerGroup();
|
||||
SlayerGroup myrmidex = new SlayerGroup();
|
||||
|
||||
humanoid.Opposition = new SlayerGroup[]
|
||||
{
|
||||
undead
|
||||
};
|
||||
|
||||
humanoid.FoundOn = new Type[]
|
||||
{
|
||||
typeof(BoneKnight), typeof(Lich),
|
||||
typeof(LichLord)
|
||||
};
|
||||
|
||||
humanoid.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.Repond,
|
||||
|
||||
typeof(ClanCA), typeof(ClanCT),
|
||||
typeof(ClanRS), typeof(ClanRC),
|
||||
typeof(ClanSS), typeof(ClanSH),
|
||||
typeof(Barracoon), typeof(MasterTheophilus),
|
||||
typeof(Lurg), typeof(ArcticOgreLord),
|
||||
typeof(Cyclops), typeof(Ettin),
|
||||
typeof(EvilMage), typeof(EvilMageLord),
|
||||
typeof(FrostTroll), typeof(MeerCaptain),
|
||||
typeof(MeerEternal), typeof(MeerMage),
|
||||
typeof(MeerWarrior), typeof(Ogre),
|
||||
typeof(OgreLord), typeof(Orc),
|
||||
typeof(OrcBomber), typeof(OrcBrute),
|
||||
typeof(OrcCaptain), typeof(OrcChopper),
|
||||
typeof(OrcScout), typeof(OrcishLord),
|
||||
typeof(OrcishMage), typeof(Ratman),
|
||||
typeof(RatmanArcher), typeof(RatmanMage),
|
||||
typeof(SavageRider), typeof(SavageShaman),
|
||||
typeof(Savage), typeof(Titan),
|
||||
typeof(Troglodyte), typeof(Troll),
|
||||
typeof(Troglodyte), typeof(MougGuur),
|
||||
typeof(Chiikkaha), typeof(Minotaur),
|
||||
typeof(MinotaurGeneral), typeof(Medusa),
|
||||
typeof(RakktaviRenowned), typeof(TikitaviRenowned),
|
||||
typeof(VitaviRenowned), typeof(EnslavedGoblinScout),
|
||||
typeof(EnslavedGoblinKeeper), typeof(EnslavedGreenGoblin),
|
||||
typeof(EnslavedGreenGoblinAlchemist), typeof(EnslavedGoblinMage),
|
||||
typeof(EnslavedGrayGoblin), typeof(GreenGoblinScout),
|
||||
typeof(GreenGoblinAlchemist), typeof(GreenGoblin),
|
||||
typeof(GrayGoblinMage), typeof(GrayGoblinKeeper),
|
||||
typeof(GrayGoblin), typeof(GreenGoblinAlchemistRenowned),
|
||||
typeof(GrayGoblinMageRenowned), typeof(CorgulTheSoulBinder),
|
||||
typeof(PirateCrew), typeof(LizardmanWitchdoctor),
|
||||
typeof(OrcFootSoldier), typeof(RatmanAssassin),
|
||||
typeof(OgreBoneCrusher), typeof(TitanRockHunter)
|
||||
);
|
||||
|
||||
humanoid.Entries = new SlayerEntry[]
|
||||
{
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.OgreTrashing,
|
||||
|
||||
typeof(Ogre), typeof(OgreLord),
|
||||
typeof(ArcticOgreLord), typeof(OgreBoneCrusher)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.OrcSlaying,
|
||||
|
||||
typeof(Orc), typeof(OrcBomber),
|
||||
typeof(OrcBrute), typeof(OrcCaptain),
|
||||
typeof(OrcChopper), typeof(OrcScout),
|
||||
typeof(OrcishLord), typeof(OrcishMage),
|
||||
typeof(OrcFootSoldier)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.TrollSlaughter,
|
||||
|
||||
typeof(Troll), typeof(FrostTroll)
|
||||
),
|
||||
};
|
||||
|
||||
undead.Opposition = new SlayerGroup[]
|
||||
{
|
||||
humanoid
|
||||
};
|
||||
|
||||
undead.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.Silver,
|
||||
|
||||
typeof(AncientLich), typeof(AncientLichRenowned),
|
||||
typeof(Bogle), typeof(BoneKnight),
|
||||
typeof(BoneMagi), typeof(DarkGuardian),
|
||||
typeof(DarknightCreeper), typeof(FleshGolem),
|
||||
typeof(Ghoul), typeof(GoreFiend),
|
||||
typeof(HellSteed), typeof(LadyOfTheSnow),
|
||||
typeof(Lich), typeof(LichLord),
|
||||
typeof(Mummy), typeof(PestilentBandage),
|
||||
typeof(Revenant), typeof(RevenantLion),
|
||||
typeof(RottingCorpse), typeof(Shade),
|
||||
typeof(ShadowKnight), typeof(SkeletalKnight),
|
||||
typeof(SkeletalMage), typeof(SkeletalMount),
|
||||
typeof(Skeleton), typeof(Spectre),
|
||||
typeof(Wraith), typeof(Zombie),
|
||||
typeof(UnfrozenMummy), typeof(RedDeath),
|
||||
typeof(SirPatrick), typeof(LadyJennifyr),
|
||||
typeof(MasterMikael), typeof(MasterJonath),
|
||||
typeof(LadyMarai), typeof(PrimevalLich),
|
||||
typeof(Niporailem), typeof(DreamWraith),
|
||||
typeof(EffeteUndeadGargoyle), typeof(UndeadGargoyle),
|
||||
typeof(UndeadGuardian), typeof(PutridUndeadGargoyle),
|
||||
typeof(PutridUndeadGuardian), typeof(Juonar),
|
||||
typeof(Spellbinder), typeof(AngeredSpirit),
|
||||
typeof(BoneSwordSlinger), typeof(CovetousRevenant),
|
||||
typeof(DiseasedLich), typeof(VileCadaver),
|
||||
typeof(GrizzledMare), typeof(SkeletalCat)
|
||||
);
|
||||
|
||||
undead.Entries = new SlayerEntry[0];
|
||||
|
||||
fey.Opposition = new SlayerGroup[]
|
||||
{
|
||||
abyss
|
||||
};
|
||||
|
||||
fey.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.Fey,
|
||||
|
||||
typeof(Centaur), typeof(CuSidhe),
|
||||
typeof(EtherealWarrior), typeof(Kirin),
|
||||
typeof(LordOaks), typeof(Pixie),
|
||||
typeof(PixieRenowned), typeof(Silvani),
|
||||
typeof(Treefellow), typeof(Unicorn),
|
||||
typeof(Wisp), typeof(MLDryad),
|
||||
typeof(Satyr), typeof(Changeling),
|
||||
typeof(InsaneDryad), typeof(CorporealBrume),
|
||||
typeof(CrystalLatticeSeeker), typeof(LadyMelisande),
|
||||
typeof(DreadHorn), typeof(Travesty),
|
||||
typeof(ShimmeringEffusion), typeof(Guile),
|
||||
typeof(Irk), typeof(DarkWisp),
|
||||
typeof(FeralTreefellow)
|
||||
);
|
||||
|
||||
fey.Entries = new SlayerEntry[0];
|
||||
|
||||
elemental.Opposition = new SlayerGroup[]
|
||||
{
|
||||
abyss
|
||||
};
|
||||
|
||||
elemental.FoundOn = new Type[]
|
||||
{
|
||||
typeof(Balron), typeof(Daemon),
|
||||
typeof(Putrefier), typeof(FireDaemonRenowned)
|
||||
};
|
||||
|
||||
elemental.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.ElementalBan,
|
||||
|
||||
typeof(LavaElemental), typeof(ToxicElemental),
|
||||
typeof(AcidElemental), typeof(AcidElementalRenowned),
|
||||
typeof(FireElementalRenowned), typeof(AgapiteElemental),
|
||||
typeof(AirElemental), typeof(SummonedAirElemental),
|
||||
typeof(BloodElemental), typeof(BronzeElemental),
|
||||
typeof(CopperElemental), typeof(CrystalElemental),
|
||||
typeof(DullCopperElemental), typeof(EarthElemental),
|
||||
typeof(SummonedEarthElemental), typeof(Efreet),
|
||||
typeof(FireElemental), typeof(SummonedFireElemental),
|
||||
typeof(GoldenElemental), typeof(IceElemental),
|
||||
typeof(KazeKemono), typeof(PoisonElemental),
|
||||
typeof(RaiJu), typeof(SandVortex),
|
||||
typeof(ShadowIronElemental), typeof(SnowElemental),
|
||||
typeof(ValoriteElemental), typeof(VeriteElemental),
|
||||
typeof(WaterElemental), typeof(SummonedWaterElemental),
|
||||
typeof(Flurry), typeof(Mistral),
|
||||
typeof(Tempest), typeof(UnboundEnergyVortex),
|
||||
typeof(ChaosVortex), typeof(WindElemental),
|
||||
typeof(FlameElemental), typeof(QuartzElemental),
|
||||
typeof(VoidManifestation), typeof(DemonKnight),
|
||||
typeof(CovetousEarthElemental), typeof(VenomElemental),
|
||||
typeof(SearingElemental), typeof(VortexElemental),
|
||||
typeof(CovetousWaterElemental)
|
||||
);
|
||||
|
||||
elemental.Entries = new SlayerEntry[]
|
||||
{
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.BloodDrinking,
|
||||
|
||||
typeof(BloodElemental), typeof(DemonKnight)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.EarthShatter,
|
||||
|
||||
typeof(AgapiteElemental), typeof(BronzeElemental),
|
||||
typeof(CopperElemental), typeof(DullCopperElemental),
|
||||
typeof(EarthElemental), typeof(SummonedEarthElemental),
|
||||
typeof(GoldenElemental), typeof(ShadowIronElemental),
|
||||
typeof(ValoriteElemental), typeof(VeriteElemental),
|
||||
typeof(QuartzElemental), typeof(DemonKnight),
|
||||
typeof(CovetousEarthElemental)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.ElementalHealth,
|
||||
|
||||
typeof(PoisonElemental), typeof(DemonKnight),
|
||||
typeof(VenomElemental)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.FlameDousing,
|
||||
|
||||
typeof(FireElemental), typeof(FireElementalRenowned),
|
||||
typeof(SummonedFireElemental), typeof(FlameElemental),
|
||||
typeof(DemonKnight), typeof(SearingElemental)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.SummerWind,
|
||||
|
||||
typeof(SnowElemental), typeof(IceElemental),
|
||||
typeof(DemonKnight)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.Vacuum,
|
||||
|
||||
typeof(AirElemental), typeof(SummonedAirElemental),
|
||||
typeof(Flurry), typeof(Mistral),
|
||||
typeof(Tempest), typeof(UnboundEnergyVortex),
|
||||
typeof(ChaosVortex), typeof(WindElemental),
|
||||
typeof(DemonKnight), typeof(VortexElemental)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.WaterDissipation,
|
||||
|
||||
typeof(WaterElemental), typeof(SummonedWaterElemental),
|
||||
typeof(DemonKnight), typeof(CovetousWaterElemental)
|
||||
)
|
||||
};
|
||||
|
||||
abyss.Opposition = new SlayerGroup[]
|
||||
{
|
||||
elemental,
|
||||
fey
|
||||
};
|
||||
|
||||
abyss.FoundOn = new Type[]
|
||||
{
|
||||
typeof(BloodElemental)
|
||||
};
|
||||
|
||||
if (Core.AOS)
|
||||
{
|
||||
abyss.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.Exorcism,
|
||||
|
||||
typeof(DevourerRenowned), typeof(FireDaemonRenowned),
|
||||
typeof(AbysmalHorror), typeof(AbyssalInfernal),
|
||||
typeof(ArcaneDaemon), typeof(Balron),
|
||||
typeof(BoneDemon), typeof(ChaosDaemon),
|
||||
typeof(Daemon), typeof(SummonedDaemon),
|
||||
typeof(DemonKnight), typeof(Devourer),
|
||||
typeof(EnslavedGargoyle), typeof(FanDancer),
|
||||
typeof(FireGargoyle), typeof(Gargoyle),
|
||||
typeof(GargoyleDestroyer), typeof(GargoyleEnforcer),
|
||||
typeof(Gibberling), typeof(HordeMinion),
|
||||
typeof(FireDaemon), typeof(IceFiend),
|
||||
typeof(Imp), typeof(Impaler),
|
||||
typeof(Moloch), typeof(Oni),
|
||||
typeof(Ravager), typeof(Semidar),
|
||||
typeof(StoneGargoyle), typeof(Succubus),
|
||||
typeof(PatchworkSkeleton), typeof(TsukiWolf),
|
||||
typeof(Szavetra), typeof(CrystalDaemon),
|
||||
typeof(SlasherOfVeils), typeof(GargoyleShade),
|
||||
typeof(Putrefier), typeof(ChiefParoxysmus),
|
||||
typeof(Anzuanord), typeof(Ballem),
|
||||
typeof(Betballem), typeof(SkeletalLich),
|
||||
typeof(UsagralemBallem), typeof(EffetePutridGargoyle),
|
||||
typeof(EffeteUndeadGargoyle), typeof(PitFiend),
|
||||
typeof(ArchDaemon), typeof(AbyssalAbomination),
|
||||
typeof(Virtuebane), typeof(LesserOni),
|
||||
typeof(Lifestealer)
|
||||
);
|
||||
|
||||
abyss.Entries = new SlayerEntry[]
|
||||
{
|
||||
// Daemon Dismissal & Balron Damnation have been removed and moved up to super slayer on OSI.
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.GargoylesFoe,
|
||||
|
||||
typeof(EnslavedGargoyle), typeof(FireGargoyle),
|
||||
typeof(Gargoyle), typeof(GargoyleDestroyer),
|
||||
typeof(GargoyleEnforcer), typeof(StoneGargoyle),
|
||||
typeof(GargoyleShade), typeof(EffetePutridGargoyle),
|
||||
typeof(EffeteUndeadGargoyle), typeof(DaemonMongbat),
|
||||
typeof(CovetousDoppleganger), typeof(CovetousFireDaemon),
|
||||
typeof(GargoyleAssassin), typeof(LesserOni)
|
||||
),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
abyss.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.Exorcism,
|
||||
|
||||
typeof(AbysmalHorror), typeof(Balron),
|
||||
typeof(BoneDemon), typeof(ChaosDaemon),
|
||||
typeof(Daemon), typeof(SummonedDaemon),
|
||||
typeof(DemonKnight), typeof(Devourer),
|
||||
typeof(Gargoyle), typeof(FireGargoyle),
|
||||
typeof(Gibberling), typeof(HordeMinion),
|
||||
typeof(IceFiend), typeof(Imp),
|
||||
typeof(Impaler), typeof(Ravager),
|
||||
typeof(StoneGargoyle), typeof(ArcaneDaemon),
|
||||
typeof(EnslavedGargoyle), typeof(GargoyleDestroyer),
|
||||
typeof(GargoyleEnforcer), typeof(Moloch)
|
||||
);
|
||||
|
||||
abyss.Entries = new SlayerEntry[]
|
||||
{
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.DaemonDismissal,
|
||||
|
||||
typeof(Semidar), typeof(AbyssalInfernal),
|
||||
typeof(AbysmalHorror), typeof(Balron),
|
||||
typeof(BoneDemon), typeof(ChaosDaemon),
|
||||
typeof(Daemon), typeof(SummonedDaemon),
|
||||
typeof(DemonKnight), typeof(Devourer),
|
||||
typeof(Gibberling), typeof(HordeMinion),
|
||||
typeof(IceFiend), typeof(Imp),
|
||||
typeof(Impaler), typeof(Ravager),
|
||||
typeof(ArcaneDaemon), typeof(Moloch)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.GargoylesFoe,
|
||||
|
||||
typeof(FireGargoyle), typeof(Gargoyle),
|
||||
typeof(StoneGargoyle), typeof(EnslavedGargoyle),
|
||||
typeof(GargoyleDestroyer), typeof(GargoyleEnforcer)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.BalronDamnation,
|
||||
|
||||
typeof(Balron)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
arachnid.Opposition = new SlayerGroup[]
|
||||
{
|
||||
reptilian
|
||||
};
|
||||
|
||||
arachnid.FoundOn = new Type[]
|
||||
{
|
||||
typeof(AncientWyrm), typeof(GreaterDragon),
|
||||
typeof(Dragon), typeof(OphidianMatriarch),
|
||||
typeof(ShadowWyrm)
|
||||
};
|
||||
|
||||
arachnid.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.ArachnidDoom,
|
||||
|
||||
typeof(DreadSpider), typeof(FrostSpider),
|
||||
typeof(GiantBlackWidow), typeof(GiantSpider),
|
||||
typeof(Mephitis), typeof(Scorpion),
|
||||
typeof(TerathanAvenger), typeof(TerathanDrone),
|
||||
typeof(TerathanMatriarch), typeof(TerathanWarrior),
|
||||
typeof(Miasma), typeof(SpeckledScorpion),
|
||||
typeof(LadyLissith), typeof(LadySabrix),
|
||||
typeof(Virulent), typeof(Silk),
|
||||
typeof(Malefic), typeof(Navrey),
|
||||
typeof(SentinelSpider), typeof(WolfSpider),
|
||||
typeof(TrapdoorSpider), typeof(Anlorzen),
|
||||
typeof(Anlorlem)
|
||||
);
|
||||
|
||||
arachnid.Entries = new SlayerEntry[]
|
||||
{
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.ScorpionsBane,
|
||||
|
||||
typeof(Scorpion), typeof(Miasma),
|
||||
typeof(SpeckledScorpion)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.SpidersDeath,
|
||||
|
||||
typeof(DreadSpider), typeof(FrostSpider),
|
||||
typeof(GiantBlackWidow), typeof(GiantSpider),
|
||||
typeof(Mephitis), typeof(LadyLissith),
|
||||
typeof(LadySabrix), typeof(Virulent),
|
||||
typeof(Silk), typeof(Malefic),
|
||||
typeof(Navrey), typeof(SentinelSpider),
|
||||
typeof(WolfSpider), typeof(TrapdoorSpider),
|
||||
typeof(Anlorzen)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.Terathan,
|
||||
|
||||
typeof(TerathanAvenger), typeof(TerathanDrone),
|
||||
typeof(TerathanMatriarch), typeof(TerathanWarrior),
|
||||
typeof(Anlorlem)
|
||||
)
|
||||
};
|
||||
|
||||
reptilian.Opposition = new SlayerGroup[]
|
||||
{
|
||||
arachnid
|
||||
};
|
||||
|
||||
reptilian.FoundOn = new Type[]
|
||||
{
|
||||
typeof(TerathanAvenger), typeof(TerathanMatriarch)
|
||||
};
|
||||
|
||||
reptilian.Super = new SlayerEntry
|
||||
(
|
||||
SlayerName.ReptilianDeath,
|
||||
|
||||
typeof(Rikktor), typeof(Serado),
|
||||
typeof(SkeletalDragonRenowned), typeof(WyvernRenowned),
|
||||
typeof(AncientWyrm), typeof(DeepSeaSerpent),
|
||||
typeof(GreaterDragon), typeof(Dragon),
|
||||
typeof(Drake), typeof(GiantIceWorm),
|
||||
typeof(IceSerpent), typeof(GiantSerpent),
|
||||
typeof(Hiryu), typeof(IceSnake),
|
||||
typeof(JukaLord), typeof(JukaMage),
|
||||
typeof(JukaWarrior), typeof(LavaSerpent),
|
||||
typeof(LavaSnake), typeof(LesserHiryu),
|
||||
typeof(Lizardman), typeof(OphidianArchmage),
|
||||
typeof(OphidianKnight), typeof(OphidianMage),
|
||||
typeof(OphidianMatriarch), typeof(OphidianWarrior),
|
||||
typeof(Reptalon), typeof(SeaSerpent),
|
||||
typeof(Serado), typeof(SerpentineDragon),
|
||||
typeof(ShadowWyrm), typeof(SilverSerpent),
|
||||
typeof(SkeletalDragon), typeof(Snake),
|
||||
typeof(SwampDragon), typeof(WhiteWyrm),
|
||||
typeof(Wyvern), typeof(Yamandon),
|
||||
typeof(Hydra), typeof(CrystalHydra),
|
||||
typeof(CrystalSeaSerpent), typeof(Rend),
|
||||
typeof(Thrasher), typeof(Abscess),
|
||||
typeof(Grim), typeof(ChickenLizard),
|
||||
typeof(StygianDragon), typeof(FairyDragon),
|
||||
typeof(Skree), typeof(Slith),
|
||||
typeof(StoneSlith), typeof(ToxicSlith),
|
||||
typeof(Raptor), typeof(Kepetch),
|
||||
typeof(KepetchAmbusher), typeof(FrostDragon),
|
||||
typeof(ColdDrake), typeof(FrostDrake), typeof(Coil),
|
||||
typeof(SkeletalDrake), typeof(CoralSnake)
|
||||
);
|
||||
|
||||
reptilian.Entries = new SlayerEntry[]
|
||||
{
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.DragonSlaying,
|
||||
|
||||
typeof(Rikktor), typeof(SkeletalDragonRenowned),
|
||||
typeof(WyvernRenowned), typeof(AncientWyrm),
|
||||
typeof(GreaterDragon), typeof(Dragon),
|
||||
typeof(Drake), typeof(Hiryu),
|
||||
typeof(LesserHiryu), typeof(Reptalon),
|
||||
typeof(SerpentineDragon), typeof(ShadowWyrm),
|
||||
typeof(SkeletalDragon), typeof(SwampDragon),
|
||||
typeof(WhiteWyrm), typeof(Wyvern),
|
||||
typeof(Hydra), typeof(CrystalHydra),
|
||||
typeof(Rend), typeof(Abscess),
|
||||
typeof(Grim), typeof(StygianDragon),
|
||||
typeof(FairyDragon), typeof(SkeletalDrake),
|
||||
typeof(ColdDrake)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.LizardmanSlaughter,
|
||||
|
||||
typeof(Lizardman)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.Ophidian,
|
||||
|
||||
typeof(OphidianArchmage), typeof(OphidianKnight),
|
||||
typeof(OphidianMage), typeof(OphidianMatriarch),
|
||||
typeof(OphidianWarrior)
|
||||
),
|
||||
|
||||
new SlayerEntry
|
||||
(
|
||||
SlayerName.SnakesBane,
|
||||
|
||||
typeof(CrystalSeaSerpent), typeof(Coil),
|
||||
typeof(CoralSnake), typeof(DeepSeaSerpent),
|
||||
typeof(GiantIceWorm), typeof(GiantSerpent),
|
||||
typeof(IceSerpent), typeof(IceSnake),
|
||||
typeof(LavaSerpent), typeof(LavaSnake),
|
||||
typeof(SeaSerpent), typeof(Serado),
|
||||
typeof(SilverSerpent), typeof(Snake),
|
||||
typeof(Yamandon)
|
||||
)
|
||||
};
|
||||
|
||||
eodon.Opposition = new SlayerGroup[] { };
|
||||
eodon.FoundOn = new Type[] { };
|
||||
eodon.Super =
|
||||
new SlayerEntry(
|
||||
|
||||
SlayerName.Eodon,
|
||||
|
||||
typeof(Dimetrosaur), typeof(Gallusaurus),
|
||||
typeof(Archaeosaurus), typeof(Najasaurus),
|
||||
typeof(Saurosaurus), typeof(Allosaurus),
|
||||
typeof(MyrmidexLarvae), typeof(MyrmidexDrone),
|
||||
typeof(MyrmidexWarrior), typeof(DragonTurtle),
|
||||
typeof(DragonTurtleHatchling), typeof(DesertScorpion),
|
||||
typeof(TribeWarrior), typeof(TribeShaman),
|
||||
typeof(TribeChieftan), typeof(WildTiger),
|
||||
typeof(WildBlackTiger), typeof(WildWhiteTiger),
|
||||
typeof(TRex), typeof(SilverbackGorilla));
|
||||
|
||||
eodon.Entries = new SlayerEntry[] { };
|
||||
|
||||
eodonTribe.Opposition = new SlayerGroup[] { };
|
||||
eodonTribe.FoundOn = new Type[] { };
|
||||
eodonTribe.Super = new SlayerEntry(SlayerName.EodonTribe, typeof(TribeWarrior), typeof(TribeShaman), typeof(TribeChieftan));
|
||||
eodonTribe.Entries = new SlayerEntry[] { };
|
||||
|
||||
dino.Opposition = new SlayerGroup[] { fey };
|
||||
dino.FoundOn = new Type[] { };
|
||||
dino.Super =
|
||||
new SlayerEntry(
|
||||
|
||||
SlayerName.Dinosaur,
|
||||
|
||||
typeof(Dimetrosaur), typeof(Gallusaurus),
|
||||
typeof(Archaeosaurus), typeof(Najasaurus),
|
||||
typeof(Saurosaurus), typeof(Allosaurus),
|
||||
typeof(MyrmidexLarvae), typeof(MyrmidexDrone),
|
||||
typeof(TRex), typeof(MyrmidexWarrior));
|
||||
|
||||
dino.Entries = new SlayerEntry[] { };
|
||||
|
||||
myrmidex.Opposition = new SlayerGroup[] { fey };
|
||||
myrmidex.FoundOn = new Type[] { };
|
||||
myrmidex.Super = new SlayerEntry(
|
||||
|
||||
SlayerName.Myrmidex,
|
||||
|
||||
typeof(MyrmidexLarvae), typeof(MyrmidexDrone),
|
||||
typeof(MyrmidexWarrior));
|
||||
myrmidex.Entries = new SlayerEntry[] { };
|
||||
|
||||
m_Groups = new SlayerGroup[]
|
||||
{
|
||||
humanoid,
|
||||
undead,
|
||||
elemental,
|
||||
abyss,
|
||||
arachnid,
|
||||
reptilian,
|
||||
fey,
|
||||
eodon,
|
||||
eodonTribe,
|
||||
dino,
|
||||
myrmidex,
|
||||
};
|
||||
|
||||
m_TotalEntries = CompileEntries(m_Groups);
|
||||
}
|
||||
|
||||
public static SlayerEntry[] TotalEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_TotalEntries;
|
||||
}
|
||||
}
|
||||
public static SlayerGroup[] Groups
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Groups;
|
||||
}
|
||||
}
|
||||
public SlayerGroup[] Opposition
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_Opposition;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_Opposition = value;
|
||||
}
|
||||
}
|
||||
public SlayerEntry Super
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_Super;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_Super = value;
|
||||
}
|
||||
}
|
||||
public SlayerEntry[] Entries
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_Entries;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_Entries = value;
|
||||
}
|
||||
}
|
||||
public Type[] FoundOn
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_FoundOn;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_FoundOn = value;
|
||||
}
|
||||
}
|
||||
public static SlayerEntry GetEntryByName(SlayerName name)
|
||||
{
|
||||
int v = (int)name;
|
||||
|
||||
if (v >= 0 && v < m_TotalEntries.Length)
|
||||
return m_TotalEntries[v];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SlayerName GetLootSlayerType(Type type)
|
||||
{
|
||||
for (int i = 0; i < m_Groups.Length; ++i)
|
||||
{
|
||||
SlayerGroup group = m_Groups[i];
|
||||
Type[] foundOn = group.FoundOn;
|
||||
|
||||
bool inGroup = false;
|
||||
|
||||
for (int j = 0; foundOn != null && !inGroup && j < foundOn.Length; ++j)
|
||||
inGroup = (foundOn[j] == type);
|
||||
|
||||
if (inGroup)
|
||||
{
|
||||
int index = Utility.Random(1 + group.Entries.Length);
|
||||
|
||||
if (index == 0)
|
||||
return group.m_Super.Name;
|
||||
|
||||
return group.Entries[index - 1].Name;
|
||||
}
|
||||
}
|
||||
|
||||
return SlayerName.Silver;
|
||||
}
|
||||
|
||||
public bool OppositionSuperSlays(Mobile m)
|
||||
{
|
||||
for (int i = 0; i < this.Opposition.Length; i++)
|
||||
{
|
||||
if (this.Opposition[i].Super.Slays(m))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_Super.Name == SlayerName.Eodon && !m_Super.Slays(m))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static SlayerEntry[] CompileEntries(SlayerGroup[] groups)
|
||||
{
|
||||
SlayerEntry[] entries = new SlayerEntry[32];
|
||||
|
||||
for (int i = 0; i < groups.Length; ++i)
|
||||
{
|
||||
SlayerGroup g = groups[i];
|
||||
|
||||
g.Super.Group = g;
|
||||
|
||||
entries[(int)g.Super.Name] = g.Super;
|
||||
|
||||
for (int j = 0; j < g.Entries.Length; ++j)
|
||||
{
|
||||
g.Entries[j].Group = g;
|
||||
entries[(int)g.Entries[j].Name] = g.Entries[j];
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static SlayerName RandomSuperSlayerAOS(bool excludeFey = true)
|
||||
{
|
||||
int maxIndex = excludeFey ? 5 : 6;
|
||||
|
||||
return Groups[Utility.Random(maxIndex)].Super.Name;
|
||||
}
|
||||
|
||||
public static SlayerName RandomSuperSlayerTOL()
|
||||
{
|
||||
return Groups[Utility.Random(Groups.Length)].Super.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Scripts/Abilities/SlayerName.cs
Normal file
40
Scripts/Abilities/SlayerName.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public enum SlayerName
|
||||
{
|
||||
None,
|
||||
Silver,
|
||||
OrcSlaying,
|
||||
TrollSlaughter,
|
||||
OgreTrashing,
|
||||
Repond,
|
||||
DragonSlaying,
|
||||
Terathan,
|
||||
SnakesBane,
|
||||
LizardmanSlaughter,
|
||||
ReptilianDeath,
|
||||
DaemonDismissal,
|
||||
GargoylesFoe,
|
||||
BalronDamnation,
|
||||
Exorcism,
|
||||
Ophidian,
|
||||
SpidersDeath,
|
||||
ScorpionsBane,
|
||||
ArachnidDoom,
|
||||
FlameDousing,
|
||||
WaterDissipation,
|
||||
Vacuum,
|
||||
ElementalHealth,
|
||||
EarthShatter,
|
||||
BloodDrinking,
|
||||
SummerWind,
|
||||
ElementalBan,
|
||||
Fey,
|
||||
Dinosaur,
|
||||
Myrmidex,
|
||||
Eodon,
|
||||
EodonTribe
|
||||
}
|
||||
}
|
||||
119
Scripts/Abilities/TalonStrike.cs
Normal file
119
Scripts/Abilities/TalonStrike.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// Attack with increased damage with additional damage over time.
|
||||
/// </summary>
|
||||
public class TalonStrike : WeaponAbility
|
||||
{
|
||||
private static readonly Hashtable m_Registry = new Hashtable();
|
||||
public TalonStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return from.Skills[SkillName.Ninjitsu].Base > from.Skills[SkillName.Bushido].Base ? SkillName.Ninjitsu : SkillName.Bushido;
|
||||
}
|
||||
|
||||
public static Hashtable Registry
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Registry;
|
||||
}
|
||||
}
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
public override double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (Registry.Contains(defender) || !this.Validate(attacker) || !this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
attacker.SendLocalizedMessage(1063358); // You deliver a talon strike!
|
||||
defender.SendLocalizedMessage(1063359); // Your attacker delivers a talon strike!
|
||||
|
||||
defender.FixedParticles(0x373A, 1, 17, 0x26BC, 0x662, 0, EffectLayer.Waist);
|
||||
|
||||
Timer t = new InternalTimer(defender, (int)(10.0 * (attacker.Skills[SkillName.Ninjitsu].Value - 50.0) / 70.0 + 5), attacker); //5 - 15 damage
|
||||
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.TalonStrike, 1028856, 1151309, TimeSpan.FromSeconds(5.0), defender, "40"));
|
||||
|
||||
t.Start();
|
||||
|
||||
Registry.Add(defender, t);
|
||||
|
||||
if (attacker is BaseCreature)
|
||||
PetTrainingHelper.OnWeaponAbilityUsed((BaseCreature)attacker, SkillName.Ninjitsu);
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Defender;
|
||||
private readonly Mobile m_Attacker;
|
||||
private readonly double DamagePerTick;
|
||||
private double m_DamageRemaining;
|
||||
private double m_DamageToDo;
|
||||
public InternalTimer(Mobile defender, int totalDamage, Mobile attacker)
|
||||
: base(TimeSpan.Zero, TimeSpan.FromSeconds(0.25), 12)// 3 seconds at .25 seconds apart = 12. Confirm delay inbetween of .25 each.
|
||||
{
|
||||
this.m_Defender = defender;
|
||||
this.m_DamageRemaining = (double)totalDamage;
|
||||
this.Priority = TimerPriority.TwentyFiveMS;
|
||||
|
||||
this.m_Attacker = attacker;
|
||||
|
||||
this.DamagePerTick = (double)totalDamage / 12 + .01;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (!this.m_Defender.Alive || this.m_DamageRemaining <= 0)
|
||||
{
|
||||
this.Stop();
|
||||
Server.Items.TalonStrike.Registry.Remove(this.m_Defender);
|
||||
return;
|
||||
}
|
||||
|
||||
this.m_DamageRemaining -= this.DamagePerTick;
|
||||
this.m_DamageToDo += this.DamagePerTick;
|
||||
|
||||
if (this.m_DamageRemaining <= 0 && this.m_DamageToDo < 1)
|
||||
this.m_DamageToDo = 1.0; //Confirm this 'round up' at the end
|
||||
|
||||
int damage = (int)this.m_DamageToDo;
|
||||
|
||||
if (damage > 0)
|
||||
{
|
||||
//m_Defender.Damage( damage, m_Attacker, false );
|
||||
this.m_Defender.Hits -= damage; //Don't show damage, don't disrupt
|
||||
this.m_DamageToDo -= damage;
|
||||
}
|
||||
|
||||
if (!this.m_Defender.Alive || this.m_DamageRemaining <= 0)
|
||||
{
|
||||
this.Stop();
|
||||
Server.Items.TalonStrike.Registry.Remove(this.m_Defender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Scripts/Abilities/TimedResistanceMod.cs
Normal file
68
Scripts/Abilities/TimedResistanceMod.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Created by Peoharen
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class TimedResistanceMod
|
||||
{
|
||||
private static readonly Dictionary<string, ResistanceModTimer> m_Table = new Dictionary<string, ResistanceModTimer>();
|
||||
public static void AddMod(Mobile m, string name, ResistanceMod[] mods, TimeSpan duration)
|
||||
{
|
||||
string fullname = name + ":" + m.Serial.ToString();
|
||||
|
||||
if (m_Table.ContainsKey(fullname))
|
||||
{
|
||||
ResistanceModTimer timer = m_Table[fullname];
|
||||
timer.End();
|
||||
m_Table.Remove(fullname);
|
||||
}
|
||||
|
||||
ResistanceModTimer timertostart = new ResistanceModTimer(m, name, mods, duration);
|
||||
timertostart.Start();
|
||||
m_Table.Add(fullname, timertostart);
|
||||
}
|
||||
|
||||
public static void RemoveMod(Mobile m, string name)
|
||||
{
|
||||
string fullname = name + ":" + m.Serial.ToString();
|
||||
|
||||
if (m_Table.ContainsKey(fullname))
|
||||
{
|
||||
ResistanceModTimer t = m_Table[fullname];
|
||||
|
||||
if (t != null)
|
||||
t.End();
|
||||
|
||||
m_Table.Remove(fullname);
|
||||
}
|
||||
}
|
||||
|
||||
public class ResistanceModTimer : Timer
|
||||
{
|
||||
public Mobile m_Mobile;
|
||||
public ResistanceMod[] m_Mods;
|
||||
public String m_Name;
|
||||
public ResistanceModTimer(Mobile m, string name, ResistanceMod[] mods, TimeSpan duration)
|
||||
: base(duration)
|
||||
{
|
||||
this.m_Mobile = m;
|
||||
this.m_Name = name;
|
||||
this.m_Mods = mods;
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
for (int i = 0; i < this.m_Mods.Length; ++i)
|
||||
this.m_Mobile.RemoveResistanceMod(this.m_Mods[i]);
|
||||
|
||||
this.Stop();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
RemoveMod(this.m_Mobile, this.m_Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
561
Scripts/Abilities/WeaponAbility.cs
Normal file
561
Scripts/Abilities/WeaponAbility.cs
Normal file
@@ -0,0 +1,561 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Server.Network;
|
||||
using Server.Spells;
|
||||
using Server.Spells.SkillMasteries;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public abstract class WeaponAbility
|
||||
{
|
||||
public virtual int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int AccuracyBonus
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public virtual double DamageScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool RequiresSE
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return false to make this special ability consume no ammo from ranged weapons
|
||||
/// </summary>
|
||||
public virtual bool ConsumeAmmo
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnMiss(Mobile attacker, Mobile defender)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool OnBeforeSwing(Mobile attacker, Mobile defender)
|
||||
{
|
||||
// Here because you must be sure you can use the skill before calling CheckHit if the ability has a HCI bonus for example
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool RequiresSecondarySkill(Mobile from)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual double GetRequiredSkill(Mobile from)
|
||||
{
|
||||
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon != null && (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave))
|
||||
return 70.0;
|
||||
else if (weapon != null && (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave))
|
||||
return 90.0;
|
||||
|
||||
return 200.0;
|
||||
}
|
||||
|
||||
public virtual double GetRequiredSecondarySkill(Mobile from)
|
||||
{
|
||||
if (!RequiresSecondarySkill(from))
|
||||
return 0.0;
|
||||
|
||||
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon != null && (weapon.PrimaryAbility == this || weapon.PrimaryAbility == Bladeweave))
|
||||
return Core.TOL ? 30.0 : 70.0;
|
||||
else if (weapon != null && (weapon.SecondaryAbility == this || weapon.SecondaryAbility == Bladeweave))
|
||||
return Core.TOL ? 60.0 : 90.0;
|
||||
|
||||
return 200.0;
|
||||
}
|
||||
|
||||
public virtual SkillName GetSecondarySkill(Mobile from)
|
||||
{
|
||||
return SkillName.Tactics;
|
||||
}
|
||||
|
||||
public virtual int CalculateMana(Mobile from)
|
||||
{
|
||||
int mana = BaseMana;
|
||||
|
||||
double skillTotal = GetSkillTotal(from);
|
||||
|
||||
if (skillTotal >= 300.0)
|
||||
mana -= 10;
|
||||
else if (skillTotal >= 200.0)
|
||||
mana -= 5;
|
||||
|
||||
double scalar = 1.0;
|
||||
|
||||
if (!Server.Spells.Necromancy.MindRotSpell.GetMindRotScalar(from, ref scalar))
|
||||
{
|
||||
scalar = 1.0;
|
||||
}
|
||||
|
||||
if (Server.Spells.Mysticism.PurgeMagicSpell.IsUnderCurseEffects(from))
|
||||
{
|
||||
scalar += .5;
|
||||
}
|
||||
|
||||
// Lower Mana Cost = 40%
|
||||
int lmc = Math.Min(AosAttributes.GetValue(from, AosAttribute.LowerManaCost), 40);
|
||||
|
||||
lmc += BaseArmor.GetInherentLowerManaCost(from);
|
||||
|
||||
scalar -= (double)lmc / 100;
|
||||
mana = (int)(mana * scalar);
|
||||
|
||||
// Using a special move within 3 seconds of the previous special move costs double mana
|
||||
if (GetContext(from) != null)
|
||||
mana *= 2;
|
||||
|
||||
return mana;
|
||||
}
|
||||
|
||||
public virtual bool CheckWeaponSkill(Mobile from)
|
||||
{
|
||||
BaseWeapon weapon = from.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return false;
|
||||
|
||||
Skill skill = from.Skills[weapon.Skill];
|
||||
|
||||
double reqSkill = GetRequiredSkill(from);
|
||||
double reqSecondarySkill = GetRequiredSecondarySkill(from);
|
||||
SkillName secondarySkill = Core.TOL ? GetSecondarySkill(from) : SkillName.Tactics;
|
||||
|
||||
if (Core.ML && from.Skills[secondarySkill].Base < reqSecondarySkill)
|
||||
{
|
||||
int loc = GetSkillLocalization(secondarySkill);
|
||||
|
||||
if (loc == 1060184)
|
||||
{
|
||||
from.SendLocalizedMessage(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(loc, reqSecondarySkill.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skill != null && skill.Base >= reqSkill)
|
||||
return true;
|
||||
|
||||
/* <UBWS> */
|
||||
if (weapon.WeaponAttributes.UseBestSkill > 0 && (from.Skills[SkillName.Swords].Base >= reqSkill || from.Skills[SkillName.Macing].Base >= reqSkill || from.Skills[SkillName.Fencing].Base >= reqSkill))
|
||||
return true;
|
||||
/* </UBWS> */
|
||||
|
||||
if (reqSecondarySkill != 0.0 && !Core.TOL)
|
||||
{
|
||||
from.SendLocalizedMessage(1079308, reqSkill.ToString()); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendLocalizedMessage(1060182, reqSkill.ToString()); // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int GetSkillLocalization(SkillName skill)
|
||||
{
|
||||
switch (skill)
|
||||
{
|
||||
default: return Core.TOL ? 1157351 : 1079308;
|
||||
// You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack
|
||||
// You need ~1_SKILL_REQUIREMENT~ tactics skill to perform that attack
|
||||
case SkillName.Bushido:
|
||||
case SkillName.Ninjitsu: return 1063347;
|
||||
// You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack!
|
||||
case SkillName.Poisoning: return 1060184;
|
||||
// You lack the required poisoning to perform that attack
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckSkills(Mobile from)
|
||||
{
|
||||
return CheckWeaponSkill(from);
|
||||
}
|
||||
|
||||
public virtual double GetSkillTotal(Mobile from)
|
||||
{
|
||||
return GetSkill(from, SkillName.Swords) + GetSkill(from, SkillName.Macing) +
|
||||
GetSkill(from, SkillName.Fencing) + GetSkill(from, SkillName.Archery) + GetSkill(from, SkillName.Parry) +
|
||||
GetSkill(from, SkillName.Lumberjacking) + GetSkill(from, SkillName.Stealth) + GetSkill(from, SkillName.Throwing) +
|
||||
GetSkill(from, SkillName.Poisoning) + GetSkill(from, SkillName.Bushido) + GetSkill(from, SkillName.Ninjitsu);
|
||||
}
|
||||
|
||||
public virtual double GetSkill(Mobile from, SkillName skillName)
|
||||
{
|
||||
Skill skill = from.Skills[skillName];
|
||||
|
||||
if (skill == null)
|
||||
return 0.0;
|
||||
|
||||
return skill.Value;
|
||||
}
|
||||
|
||||
public virtual bool CheckMana(Mobile from, bool consume)
|
||||
{
|
||||
int mana = CalculateMana(from);
|
||||
|
||||
if (from.Mana < mana)
|
||||
{
|
||||
from.SendLocalizedMessage(1060181, mana.ToString()); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack
|
||||
return false;
|
||||
}
|
||||
|
||||
if (consume)
|
||||
{
|
||||
if (GetContext(from) == null)
|
||||
{
|
||||
Timer timer = new WeaponAbilityTimer(from);
|
||||
timer.Start();
|
||||
|
||||
AddContext(from, new WeaponAbilityContext(timer));
|
||||
}
|
||||
|
||||
if (ManaPhasingOrb.IsInManaPhase(from))
|
||||
ManaPhasingOrb.RemoveFromTable(from);
|
||||
else
|
||||
from.Mana -= mana;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool Validate(Mobile from)
|
||||
{
|
||||
if (!from.Player && (!Core.TOL || CheckMana(from, false)))
|
||||
return true;
|
||||
|
||||
NetState state = from.NetState;
|
||||
|
||||
if (state == null)
|
||||
return false;
|
||||
|
||||
if (RequiresSE && !state.SupportsExpansion(Expansion.SE))
|
||||
{
|
||||
from.SendLocalizedMessage(1063456); // You must upgrade to Samurai Empire in order to use that ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Spells.Bushido.HonorableExecution.IsUnderPenalty(from) || Spells.Ninjitsu.AnimalForm.UnderTransformation(from))
|
||||
{
|
||||
from.SendLocalizedMessage(1063024); // You cannot perform this special move right now.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Core.ML && from.Spell != null)
|
||||
{
|
||||
from.SendLocalizedMessage(1063024); // You cannot perform this special move right now.
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckSkills(from) && CheckMana(from, false);
|
||||
}
|
||||
|
||||
private static readonly WeaponAbility[] m_Abilities = new WeaponAbility[34]
|
||||
{
|
||||
null,
|
||||
new ArmorIgnore(),
|
||||
new BleedAttack(),
|
||||
new ConcussionBlow(),
|
||||
new CrushingBlow(),
|
||||
new Disarm(),
|
||||
new Dismount(),
|
||||
new DoubleStrike(),
|
||||
new InfectiousStrike(),
|
||||
new MortalStrike(),
|
||||
new MovingShot(),
|
||||
new ParalyzingBlow(),
|
||||
new ShadowStrike(),
|
||||
new WhirlwindAttack(),
|
||||
new RidingSwipe(),
|
||||
new FrenziedWhirlwind(),
|
||||
new Block(),
|
||||
new DefenseMastery(),
|
||||
new NerveStrike(),
|
||||
new TalonStrike(),
|
||||
new Feint(),
|
||||
new DualWield(),
|
||||
new DoubleShot(),
|
||||
new ArmorPierce(),
|
||||
new Bladeweave(),
|
||||
new ForceArrow(),
|
||||
new LightningArrow(),
|
||||
new PsychicAttack(),
|
||||
new SerpentArrow(),
|
||||
new ForceOfNature(),
|
||||
new InfusedThrow(),
|
||||
new MysticArc(),
|
||||
new Disrobe(),
|
||||
new ColdWind()
|
||||
};
|
||||
|
||||
public static WeaponAbility[] Abilities
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Abilities;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static Hashtable Table
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Table;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly WeaponAbility ArmorIgnore = m_Abilities[1];
|
||||
public static readonly WeaponAbility BleedAttack = m_Abilities[2];
|
||||
public static readonly WeaponAbility ConcussionBlow = m_Abilities[3];
|
||||
public static readonly WeaponAbility CrushingBlow = m_Abilities[4];
|
||||
public static readonly WeaponAbility Disarm = m_Abilities[5];
|
||||
public static readonly WeaponAbility Dismount = m_Abilities[6];
|
||||
public static readonly WeaponAbility DoubleStrike = m_Abilities[7];
|
||||
public static readonly WeaponAbility InfectiousStrike = m_Abilities[8];
|
||||
public static readonly WeaponAbility MortalStrike = m_Abilities[9];
|
||||
public static readonly WeaponAbility MovingShot = m_Abilities[10];
|
||||
public static readonly WeaponAbility ParalyzingBlow = m_Abilities[11];
|
||||
public static readonly WeaponAbility ShadowStrike = m_Abilities[12];
|
||||
public static readonly WeaponAbility WhirlwindAttack = m_Abilities[13];
|
||||
|
||||
public static readonly WeaponAbility RidingSwipe = m_Abilities[14];
|
||||
public static readonly WeaponAbility FrenziedWhirlwind = m_Abilities[15];
|
||||
public static readonly WeaponAbility Block = m_Abilities[16];
|
||||
public static readonly WeaponAbility DefenseMastery = m_Abilities[17];
|
||||
public static readonly WeaponAbility NerveStrike = m_Abilities[18];
|
||||
public static readonly WeaponAbility TalonStrike = m_Abilities[19];
|
||||
public static readonly WeaponAbility Feint = m_Abilities[20];
|
||||
public static readonly WeaponAbility DualWield = m_Abilities[21];
|
||||
public static readonly WeaponAbility DoubleShot = m_Abilities[22];
|
||||
public static readonly WeaponAbility ArmorPierce = m_Abilities[23];
|
||||
|
||||
public static readonly WeaponAbility Bladeweave = m_Abilities[24];
|
||||
public static readonly WeaponAbility ForceArrow = m_Abilities[25];
|
||||
public static readonly WeaponAbility LightningArrow = m_Abilities[26];
|
||||
public static readonly WeaponAbility PsychicAttack = m_Abilities[27];
|
||||
public static readonly WeaponAbility SerpentArrow = m_Abilities[28];
|
||||
public static readonly WeaponAbility ForceOfNature = m_Abilities[29];
|
||||
|
||||
public static readonly WeaponAbility InfusedThrow = m_Abilities[30];
|
||||
public static readonly WeaponAbility MysticArc = m_Abilities[31];
|
||||
|
||||
public static readonly WeaponAbility Disrobe = m_Abilities[32];
|
||||
public static readonly WeaponAbility ColdWind = m_Abilities[33];
|
||||
|
||||
public static bool IsWeaponAbility(Mobile m, WeaponAbility a)
|
||||
{
|
||||
if (a == null)
|
||||
return true;
|
||||
|
||||
if (!m.Player)
|
||||
return true;
|
||||
|
||||
BaseWeapon weapon = m.Weapon as BaseWeapon;
|
||||
|
||||
return (weapon != null && (weapon.PrimaryAbility == a || weapon.SecondaryAbility == a));
|
||||
}
|
||||
|
||||
public virtual bool ValidatesDuringHit
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static WeaponAbility GetCurrentAbility(Mobile m)
|
||||
{
|
||||
if (!Core.AOS)
|
||||
{
|
||||
ClearCurrentAbility(m);
|
||||
return null;
|
||||
}
|
||||
|
||||
WeaponAbility a = (WeaponAbility)m_Table[m];
|
||||
|
||||
if (!IsWeaponAbility(m, a))
|
||||
{
|
||||
ClearCurrentAbility(m);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (a != null && a.ValidatesDuringHit && !a.Validate(m))
|
||||
{
|
||||
ClearCurrentAbility(m);
|
||||
return null;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static bool SetCurrentAbility(Mobile m, WeaponAbility a)
|
||||
{
|
||||
if (!Core.AOS)
|
||||
{
|
||||
ClearCurrentAbility(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsWeaponAbility(m, a))
|
||||
{
|
||||
ClearCurrentAbility(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a != null && !a.Validate(m))
|
||||
{
|
||||
ClearCurrentAbility(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a == null)
|
||||
{
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpecialMove.ClearCurrentMove(m);
|
||||
|
||||
m_Table[m] = a;
|
||||
|
||||
SkillMasterySpell.CancelWeaponAbility(m);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void ClearCurrentAbility(Mobile m)
|
||||
{
|
||||
m_Table.Remove(m);
|
||||
|
||||
if (Core.AOS && m.NetState != null)
|
||||
m.Send(ClearWeaponAbility.Instance);
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.SetAbility += new SetAbilityEventHandler(EventSink_SetAbility);
|
||||
}
|
||||
|
||||
public WeaponAbility()
|
||||
{
|
||||
}
|
||||
|
||||
private static void EventSink_SetAbility(SetAbilityEventArgs e)
|
||||
{
|
||||
int index = e.Index;
|
||||
|
||||
if (index == 0)
|
||||
ClearCurrentAbility(e.Mobile);
|
||||
else if (index >= 1 && index < m_Abilities.Length)
|
||||
SetCurrentAbility(e.Mobile, m_Abilities[index]);
|
||||
}
|
||||
|
||||
private static readonly Hashtable m_PlayersTable = new Hashtable();
|
||||
|
||||
private static void AddContext(Mobile m, WeaponAbilityContext context)
|
||||
{
|
||||
m_PlayersTable[m] = context;
|
||||
}
|
||||
|
||||
private static void RemoveContext(Mobile m)
|
||||
{
|
||||
WeaponAbilityContext context = GetContext(m);
|
||||
|
||||
if (context != null)
|
||||
RemoveContext(m, context);
|
||||
}
|
||||
|
||||
private static void RemoveContext(Mobile m, WeaponAbilityContext context)
|
||||
{
|
||||
m_PlayersTable.Remove(m);
|
||||
|
||||
context.Timer.Stop();
|
||||
}
|
||||
|
||||
private static WeaponAbilityContext GetContext(Mobile m)
|
||||
{
|
||||
return (m_PlayersTable[m] as WeaponAbilityContext);
|
||||
}
|
||||
|
||||
private class WeaponAbilityTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public WeaponAbilityTimer(Mobile from)
|
||||
: base(TimeSpan.FromSeconds(3.0))
|
||||
{
|
||||
m_Mobile = from;
|
||||
|
||||
Priority = TimerPriority.TwentyFiveMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
RemoveContext(m_Mobile);
|
||||
}
|
||||
}
|
||||
|
||||
private class WeaponAbilityContext
|
||||
{
|
||||
private readonly Timer m_Timer;
|
||||
|
||||
public Timer Timer
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Timer;
|
||||
}
|
||||
}
|
||||
|
||||
public WeaponAbilityContext(Timer timer)
|
||||
{
|
||||
m_Timer = timer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Scripts/Abilities/WhirlwindAttack.cs
Normal file
89
Scripts/Abilities/WhirlwindAttack.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server.Spells;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
/// <summary>
|
||||
/// A godsend to a warrior surrounded, the Whirlwind Attack allows the fighter to strike at all nearby targets in one mighty spinning swing.
|
||||
/// </summary>
|
||||
public class WhirlwindAttack : WeaponAbility
|
||||
{
|
||||
public WhirlwindAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnBeforeDamage(Mobile attacker, Mobile defender)
|
||||
{
|
||||
BaseWeapon wep = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (wep != null)
|
||||
wep.ProcessingMultipleHits = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnHit(Mobile attacker, Mobile defender, int damage)
|
||||
{
|
||||
if (!this.Validate(attacker))
|
||||
return;
|
||||
|
||||
ClearCurrentAbility(attacker);
|
||||
|
||||
Map map = attacker.Map;
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
BaseWeapon weapon = attacker.Weapon as BaseWeapon;
|
||||
|
||||
if (weapon == null)
|
||||
return;
|
||||
|
||||
if (!this.CheckMana(attacker, true))
|
||||
return;
|
||||
|
||||
attacker.FixedEffect(0x3728, 10, 15);
|
||||
attacker.PlaySound(0x2A1);
|
||||
|
||||
var list = SpellHelper.AcquireIndirectTargets(attacker, attacker, attacker.Map, 1)
|
||||
.OfType<Mobile>()
|
||||
.Where(m => attacker.InRange(m, weapon.MaxRange) && m != defender).ToList();
|
||||
|
||||
int count = list.Count;
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
double bushido = attacker.Skills.Bushido.Value;
|
||||
double damageBonus = 1.0 + Math.Pow((count * bushido) / 60, 2) / 100;
|
||||
|
||||
if (damageBonus > 2.0)
|
||||
damageBonus = 2.0;
|
||||
|
||||
attacker.RevealingAction();
|
||||
|
||||
foreach(var m in list)
|
||||
{
|
||||
attacker.SendLocalizedMessage(1060161); // The whirling attack strikes a target!
|
||||
m.SendLocalizedMessage(1060162); // You are struck by the whirling attack and take damage!
|
||||
|
||||
weapon.OnHit(attacker, m, damageBonus);
|
||||
}
|
||||
}
|
||||
|
||||
ColUtility.Free(list);
|
||||
|
||||
weapon.ProcessingMultipleHits = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user