Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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++;
}
}
}
}

View 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);
}
}
}

View 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
View 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;
}
}
}
}

View 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.
}
}
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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
View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}
}
}

View 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;
}
*/

View 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();
}
}
}
}

View 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
View 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);
}
}
}
}

View 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;
}
}
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}
}
}
}

View 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!
}
}
}
}

View 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);
}
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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
}
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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();
}
}
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}