2689 lines
82 KiB
C#
2689 lines
82 KiB
C#
using System;
|
|
using Server;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Server.Items;
|
|
using Server.Network;
|
|
|
|
namespace Server.Mobiles
|
|
{
|
|
public abstract class SpecialAbility
|
|
{
|
|
public virtual int ManaCost { get { return 10; } }
|
|
public virtual int MaxRange { get { return 1; } }
|
|
public virtual double TriggerChance { get { return 0.1; } }
|
|
public virtual bool RequiresCombatant { get { return true; } }
|
|
public virtual TimeSpan CooldownDuration { get { return TimeSpan.FromSeconds(30); } }
|
|
|
|
public virtual MagicalAbility RequiredSchool { get { return MagicalAbility.None; } }
|
|
public virtual bool NaturalAbility { get { return false; } }
|
|
|
|
public virtual bool TriggerOnGotMeleeDamage { get { return false; } }
|
|
public virtual bool TriggerOnDoMeleeDamage { get { return false; } }
|
|
public virtual bool TriggerOnGotSpellDamage { get { return false; } }
|
|
public virtual bool TriggerOnDoSpellDamage { get { return false; } }
|
|
public virtual bool TriggerOnThink { get { return false; } }
|
|
public virtual bool TriggerOnApproach { get { return false; } }
|
|
|
|
public abstract void DoEffects(BaseCreature creature, Mobile defender, ref int damage);
|
|
|
|
public SpecialAbility()
|
|
{
|
|
}
|
|
|
|
public static void CheckCombatTrigger(Mobile attacker, Mobile defender, ref int damage, DamageType type)
|
|
{
|
|
if(defender == null)
|
|
return;
|
|
|
|
if (attacker is BaseCreature && !((BaseCreature)attacker).Summoned)
|
|
{
|
|
var bc = attacker as BaseCreature;
|
|
var profile = PetTrainingHelper.GetAbilityProfile(bc);
|
|
|
|
if (profile != null)
|
|
{
|
|
SpecialAbility ability = null;
|
|
|
|
var abilties = profile.EnumerateSpecialAbilities().Where(m =>
|
|
(type == DamageType.Melee && m.TriggerOnDoMeleeDamage) || (type >= DamageType.Spell && m.TriggerOnDoSpellDamage) &&
|
|
!m.IsInCooldown(attacker)).ToArray();
|
|
|
|
if (abilties != null && abilties.Length > 0)
|
|
{
|
|
ability = abilties[Utility.Random(abilties.Length)];
|
|
}
|
|
|
|
if (ability != null)
|
|
{
|
|
ability.Trigger(bc, defender, ref damage);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (defender is BaseCreature && !((BaseCreature)defender).Summoned)
|
|
{
|
|
var bc = defender as BaseCreature;
|
|
var profile = PetTrainingHelper.GetAbilityProfile(bc);
|
|
|
|
if (profile != null)
|
|
{
|
|
SpecialAbility ability = null;
|
|
|
|
var abilties = profile.EnumerateSpecialAbilities().Where(m =>
|
|
(type == DamageType.Melee && m.TriggerOnGotMeleeDamage) || (type >= DamageType.Spell && m.TriggerOnGotSpellDamage) &&
|
|
!m.IsInCooldown(defender)).ToArray();
|
|
|
|
if (abilties != null && abilties.Length > 0)
|
|
{
|
|
ability = abilties[Utility.Random(abilties.Length)];
|
|
}
|
|
|
|
if (ability != null)
|
|
{
|
|
ability.Trigger(bc, attacker, ref damage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool CheckThinkTrigger(BaseCreature bc)
|
|
{
|
|
var combatant = bc.Combatant;
|
|
var profile = PetTrainingHelper.GetAbilityProfile(bc);
|
|
|
|
if (combatant is Mobile)
|
|
{
|
|
if (profile != null)
|
|
{
|
|
SpecialAbility ability = null;
|
|
|
|
var abilties = profile.EnumerateSpecialAbilities().Where(m => m.TriggerOnThink && !m.IsInCooldown(bc)).ToArray();
|
|
|
|
if (abilties != null && abilties.Length > 0)
|
|
{
|
|
ability = abilties[Utility.Random(abilties.Length)];
|
|
}
|
|
|
|
if (ability != null)
|
|
{
|
|
int d = 0;
|
|
ability.Trigger(bc, (Mobile)combatant, ref d);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SpecialAbility ability = null;
|
|
|
|
var abilties =
|
|
profile.EnumerateSpecialAbilities().Where(
|
|
m =>
|
|
m.TriggerOnThink &&
|
|
!m.IsInCooldown(bc) &&
|
|
!m.RequiresCombatant).ToArray();
|
|
|
|
if (abilties != null && abilties.Length > 0)
|
|
{
|
|
ability = abilties[Utility.Random(abilties.Length)];
|
|
}
|
|
|
|
if (ability != null)
|
|
{
|
|
int d = 0;
|
|
return ability.Trigger(bc, bc, ref d);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool CheckApproachTrigger(BaseCreature bc, Mobile mobile, Point3D oldLocation)
|
|
{
|
|
var profile = PetTrainingHelper.GetAbilityProfile(bc);
|
|
|
|
if (profile != null)
|
|
{
|
|
SpecialAbility ability = null;
|
|
|
|
var abilties = profile.EnumerateSpecialAbilities().Where(m =>
|
|
m.TriggerOnApproach &&
|
|
bc.InRange(mobile.Location, m.MaxRange) &&
|
|
!bc.InRange(oldLocation, m.MaxRange) &&
|
|
!m.IsInCooldown(bc)).ToArray();
|
|
|
|
if (abilties != null && abilties.Length > 0)
|
|
{
|
|
ability = abilties[Utility.Random(abilties.Length)];
|
|
}
|
|
|
|
if (ability != null)
|
|
{
|
|
int d = 0;
|
|
return ability.Trigger(bc, mobile, ref d);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual bool Trigger(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (CheckMana(creature) && Validate(creature, defender) && TriggerChance >= Utility.RandomDouble())
|
|
{
|
|
creature.Mana -= ManaCost;
|
|
|
|
DoEffects(creature, defender, ref damage);
|
|
AddToCooldown(creature);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual bool Validate(BaseCreature attacker, Mobile defender)
|
|
{
|
|
if (RequiredSchool != MagicalAbility.None)
|
|
{
|
|
var profile = PetTrainingHelper.GetAbilityProfile(attacker);
|
|
|
|
if (profile == null || !profile.HasAbility(RequiredSchool))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return defender != null && defender.Alive && !defender.Deleted && !defender.IsDeadBondedPet &&
|
|
attacker.Alive && !attacker.IsDeadBondedPet && defender.InRange(attacker.Location, MaxRange) &&
|
|
defender.Map == attacker.Map && attacker.InLOS(defender) && !attacker.BardPacified && attacker.CanBeHarmful(defender);
|
|
}
|
|
|
|
public bool CheckMana(Mobile m)
|
|
{
|
|
return m.Mana >= ManaCost;
|
|
}
|
|
|
|
protected List<Mobile> _Cooldown;
|
|
|
|
public bool IsInCooldown(Mobile m)
|
|
{
|
|
return _Cooldown != null && _Cooldown.Contains(m);
|
|
}
|
|
|
|
public virtual void AddToCooldown(BaseCreature m)
|
|
{
|
|
if(CooldownDuration != TimeSpan.MinValue)
|
|
{
|
|
if (_Cooldown == null)
|
|
_Cooldown = new List<Mobile>();
|
|
|
|
_Cooldown.Add(m);
|
|
Timer.DelayCall<Mobile>(CooldownDuration, RemoveFromCooldown, m);
|
|
}
|
|
}
|
|
|
|
public void RemoveFromCooldown(Mobile m)
|
|
{
|
|
_Cooldown.Remove(m);
|
|
}
|
|
|
|
public static SpecialAbility[] Abilities { get { return _Abilities; } }
|
|
private static SpecialAbility[] _Abilities;
|
|
|
|
static SpecialAbility()
|
|
{
|
|
_Abilities = new SpecialAbility[29];
|
|
|
|
_Abilities[0] = new AngryFire();
|
|
_Abilities[1] = new ConductiveBlast();
|
|
_Abilities[2] = new DragonBreath();
|
|
_Abilities[3] = new GraspingClaw();
|
|
_Abilities[4] = new Inferno();
|
|
_Abilities[5] = new LightningForce();
|
|
_Abilities[6] = new ManaDrain();
|
|
_Abilities[7] = new RagingBreath();
|
|
_Abilities[8] = new Repel();
|
|
_Abilities[9] = new SearingWounds();
|
|
_Abilities[10] = new StealLife();
|
|
_Abilities[11] = new VenomousBite();
|
|
_Abilities[12] = new ViciousBite();
|
|
_Abilities[13] = new RuneCorruption();
|
|
_Abilities[14] = new LifeLeech();
|
|
_Abilities[15] = new StickySkin();
|
|
_Abilities[16] = new TailSwipe();
|
|
_Abilities[17] = new FlurryForce();
|
|
_Abilities[18] = new Rage();
|
|
|
|
// Non-Trainable
|
|
_Abilities[19] = new Heal();
|
|
_Abilities[20] = new HowlOfCacophony();
|
|
_Abilities[21] = new Webbing();
|
|
_Abilities[22] = new Anemia();
|
|
_Abilities[23] = new BloodDisease();
|
|
_Abilities[24] = new PoisonSpit();
|
|
_Abilities[25] = new TrueFear();
|
|
_Abilities[26] = new ColossalBlow();
|
|
_Abilities[27] = new LifeDrain();
|
|
_Abilities[28] = new ColossalRage();
|
|
}
|
|
|
|
public static SpecialAbility AngryFire
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[0];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility ConductiveBlast
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[1];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility DragonBreath
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[2];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility GraspingClaw
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[3];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility Inferno
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[4];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility LightningForce
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[5];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility ManaDrain
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[6];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility RagingBreath
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[7];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility Repel
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[8];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility SearingWounds
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[9];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility StealLife
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[10];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility RuneCorruption
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[13];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility LifeLeech
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[14];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility StickySkin
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[15];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility TailSwipe
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[16];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility VenomousBite
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[11];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility ViciousBite
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[12];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility FlurryForce
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[17];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility Rage
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[18];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility Heal
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[19];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility HowlOfCacophony
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[20];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility Webbing
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[21];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility Anemia
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[22];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility BloodDisease
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[23];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility PoisonSpit
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[24];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility TrueFear
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[25];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility ColossalBlow
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[26];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility LifeDrain
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[27];
|
|
}
|
|
}
|
|
|
|
public static SpecialAbility ColossalRage
|
|
{
|
|
get
|
|
{
|
|
return _Abilities[28];
|
|
}
|
|
}
|
|
}
|
|
|
|
public class AngryFire : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public AngryFire()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
int d = Utility.RandomMinMax(30, 40);
|
|
|
|
AOS.Damage(defender, creature, d, 60, 20, 0, 0, 20);
|
|
|
|
defender.FixedParticles(0x3709, 10, 30, 5052, EffectLayer.LeftFoot);
|
|
defender.PlaySound(0x208);
|
|
|
|
defender.SendLocalizedMessage(1070823); // The creature hits you with its Angry Fire.
|
|
}
|
|
}
|
|
|
|
public class ConductiveBlast : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
private static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public ConductiveBlast()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
ExpireTimer timer = null;
|
|
|
|
if(_Table == null)
|
|
{
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
}
|
|
|
|
if(_Table.ContainsKey(defender))
|
|
{
|
|
timer = _Table[defender];
|
|
}
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.DoExpire();
|
|
defender.SendLocalizedMessage(1070828); // The creature continues to hinder your energy resistance!
|
|
}
|
|
else
|
|
defender.SendLocalizedMessage(1070827); // The creature's attack has made you more susceptible to energy attacks!
|
|
|
|
int effect = -(defender.EnergyResistance / 2);
|
|
|
|
ResistanceMod mod = new ResistanceMod(ResistanceType.Energy, effect);
|
|
|
|
defender.FixedEffect(0x37B9, 10, 5);
|
|
defender.AddResistanceMod(mod);
|
|
|
|
timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(10.0));
|
|
timer.Start();
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
public static void RemoveFromTable(Mobile m)
|
|
{
|
|
if(_Table != null && _Table.ContainsKey(m))
|
|
{
|
|
_Table.Remove(m);
|
|
}
|
|
}
|
|
|
|
private class ExpireTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
private readonly ResistanceMod m_Mod;
|
|
|
|
public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay)
|
|
: base(delay)
|
|
{
|
|
m_Mobile = m;
|
|
m_Mod = mod;
|
|
Priority = TimerPriority.TwoFiftyMS;
|
|
}
|
|
|
|
public void DoExpire()
|
|
{
|
|
m_Mobile.RemoveResistanceMod(m_Mod);
|
|
Stop();
|
|
RemoveFromTable(m_Mobile);
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.SendLocalizedMessage(1070829); // Your resistance to energy attacks has returned.
|
|
DoExpire();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class FlurryForce : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
|
|
private static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public FlurryForce()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
ExpireTimer timer = null;
|
|
|
|
if(_Table == null)
|
|
{
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
}
|
|
|
|
if(_Table.ContainsKey(defender))
|
|
{
|
|
timer = _Table[defender];
|
|
}
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.DoExpire();
|
|
defender.SendLocalizedMessage(1070851); // The creature lands another blow in your weakened state.
|
|
}
|
|
else
|
|
defender.SendLocalizedMessage(1070850); // The creature's flurry of twigs has made you more susceptible to physical attacks!
|
|
|
|
int effect = -(defender.PhysicalResistance * 15 / 100);
|
|
|
|
ResistanceMod mod = new ResistanceMod(ResistanceType.Physical, effect);
|
|
|
|
defender.FixedEffect(0x37B9, 10, 5);
|
|
defender.AddResistanceMod(mod);
|
|
|
|
timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(10.0));
|
|
timer.Start();
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
public static void RemoveFromTable(Mobile m)
|
|
{
|
|
if(_Table != null && _Table.ContainsKey(m))
|
|
{
|
|
_Table.Remove(m);
|
|
}
|
|
}
|
|
|
|
private class ExpireTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
private readonly ResistanceMod m_Mod;
|
|
|
|
public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay)
|
|
: base(delay)
|
|
{
|
|
m_Mobile = m;
|
|
m_Mod = mod;
|
|
Priority = TimerPriority.TwoFiftyMS;
|
|
}
|
|
|
|
public void DoExpire()
|
|
{
|
|
m_Mobile.RemoveResistanceMod(m_Mod);
|
|
Stop();
|
|
RemoveFromTable(m_Mobile);
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.SendLocalizedMessage(1070829); // Your resistance to energy attacks has returned.
|
|
DoExpire();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class DragonBreath : SpecialAbility
|
|
{
|
|
public override int MaxRange { get { return 12; } }
|
|
public override bool TriggerOnThink { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public DragonBreath()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
DoBreath(creature, defender);
|
|
}
|
|
|
|
public override void AddToCooldown(BaseCreature m)
|
|
{
|
|
if (CooldownDuration != TimeSpan.MinValue)
|
|
{
|
|
if (_Cooldown == null)
|
|
_Cooldown = new List<Mobile>();
|
|
|
|
var def = DragonBreathDefinition.GetDefinition(m);
|
|
|
|
_Cooldown.Add(m);
|
|
Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(Utility.RandomMinMax(def.MinDelay, def.MaxDelay)), RemoveFromCooldown, m);
|
|
}
|
|
}
|
|
|
|
public virtual void DoBreath(BaseCreature creature, Mobile target)
|
|
{
|
|
var def = DragonBreathDefinition.GetDefinition(creature);
|
|
|
|
creature.RevealingAction();
|
|
|
|
if (creature.AIObject != null)
|
|
{
|
|
creature.AIObject.NextMove = Core.TickCount + (int)(def.StallTime * 1000);
|
|
}
|
|
|
|
creature.PlaySound(creature.GetAngerSound());
|
|
|
|
if (Core.SA)
|
|
{
|
|
creature.Animate(AnimationType.Pillage, 0);
|
|
}
|
|
else
|
|
{
|
|
creature.Animate(def.AngerAnimation, 5, 1, true, false, 0);
|
|
}
|
|
|
|
creature.Direction = creature.GetDirectionTo(target);
|
|
|
|
if (def.AttacksMultipleTargets)
|
|
{
|
|
var list = Server.Spells.SpellHelper.AcquireIndirectTargets(creature, target, creature.Map, 5).OfType<Mobile>().Where(m => m.InRange(creature.Location, MaxRange)).ToList();
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
if (list.Count == 0)
|
|
break;
|
|
|
|
var m = i == 0 ? target : list[Utility.Random(list.Count)];
|
|
|
|
list.Remove(m);
|
|
Timer.DelayCall(TimeSpan.FromSeconds(def.EffectDelay), new TimerStateCallback<BaseCreature, Mobile, DragonBreathDefinition>(BreathEffect_Callback), creature, m, def);
|
|
}
|
|
|
|
ColUtility.Free(list);
|
|
}
|
|
else
|
|
{
|
|
Timer.DelayCall(TimeSpan.FromSeconds(def.EffectDelay), new TimerStateCallback<BaseCreature, Mobile, DragonBreathDefinition>(BreathEffect_Callback), creature, target, def);
|
|
}
|
|
}
|
|
|
|
public void BreathEffect_Callback(BaseCreature creature, Mobile target, DragonBreathDefinition def)
|
|
{
|
|
creature.RevealingAction();
|
|
|
|
if (!target.Alive || !creature.CanBeHarmful(target))
|
|
{
|
|
return;
|
|
}
|
|
|
|
creature.PlaySound(def.EffectSound);
|
|
|
|
Effects.SendMovingEffect(
|
|
creature,
|
|
target,
|
|
def.EffectItemID,
|
|
def.EffectSpeed,
|
|
def.EffectDuration,
|
|
def.EffectFixedDir,
|
|
def.EffectExplodes,
|
|
def.EffectHue,
|
|
def.EffectRenderMode);
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(def.DamageDelay), new TimerStateCallback<BaseCreature, Mobile, DragonBreathDefinition>(BreathDamage_Callback), creature, target, def);
|
|
}
|
|
|
|
public void BreathDamage_Callback(BaseCreature creature, Mobile target, DragonBreathDefinition def)
|
|
{
|
|
if (target is BaseCreature && ((BaseCreature)target).BreathImmune)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (creature.CanBeHarmful(target))
|
|
{
|
|
creature.DoHarmful(target);
|
|
BreathDealDamage(creature, target, def);
|
|
}
|
|
}
|
|
|
|
public void BreathDealDamage(BaseCreature creature, Mobile target, DragonBreathDefinition def)
|
|
{
|
|
if (!Server.Spells.Bushido.Evasion.CheckSpellEvasion(target))
|
|
{
|
|
AOS.Damage(
|
|
target,
|
|
creature,
|
|
BreathComputeDamage(creature, def),
|
|
def.GetElementalDamage(creature, ElementType.Physical),
|
|
def.GetElementalDamage(creature, ElementType.Fire),
|
|
def.GetElementalDamage(creature, ElementType.Cold),
|
|
def.GetElementalDamage(creature, ElementType.Poison),
|
|
def.GetElementalDamage(creature, ElementType.Energy),
|
|
def.GetElementalDamage(creature, ElementType.Chaos),
|
|
def.GetElementalDamage(creature, ElementType.Direct));
|
|
}
|
|
}
|
|
|
|
public int BreathComputeDamage(BaseCreature creature, DragonBreathDefinition def)
|
|
{
|
|
int damage = (int)(creature.Hits * def.DamageScalar);
|
|
|
|
if (creature.IsParagon)
|
|
{
|
|
damage = (int)(damage / Paragon.HitsBuff);
|
|
}
|
|
|
|
if (damage > 200)
|
|
{
|
|
damage = 200;
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
|
|
public class DragonBreathDefinition
|
|
{
|
|
public Type[] Uses { get; private set; }
|
|
|
|
// Base damage given is: CurrentHitPoints * BreathDamageScalar
|
|
public double DamageScalar { get; private set; }
|
|
|
|
// Creature stops moving for 1.0 seconds while breathing
|
|
public double StallTime { get; private set; }
|
|
|
|
// Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played
|
|
public double EffectDelay { get; private set; }
|
|
|
|
// Damage is given 1.0 seconds after effect is sent
|
|
public double DamageDelay { get; private set; }
|
|
|
|
// Damage types
|
|
public int PhysicalDamage { get; private set; }
|
|
public int FireDamage { get; private set; }
|
|
public int ColdDamage { get; private set; }
|
|
public int PoisonDamage { get; private set; }
|
|
public int EnergyDamage { get; private set; }
|
|
public int ChaosDamage { get; private set; }
|
|
public int DirectDamage { get; private set; }
|
|
|
|
public double MinDelay { get; private set; }
|
|
public double MaxDelay { get; private set; }
|
|
|
|
// Effect details and sound
|
|
public int EffectItemID { get; private set; }
|
|
public int EffectSpeed { get; private set; }
|
|
public int EffectDuration { get; private set; }
|
|
public bool EffectExplodes { get; private set; }
|
|
public bool EffectFixedDir { get; private set; }
|
|
public int EffectHue { get; private set; }
|
|
public int EffectRenderMode { get; private set; }
|
|
|
|
public int EffectSound { get; private set; }
|
|
|
|
// Anger sound/animations
|
|
public int AngerAnimation { get; private set; }
|
|
|
|
public bool AttacksMultipleTargets { get; private set; }
|
|
|
|
public static List<DragonBreathDefinition> Definitions { get; private set; } = new List<DragonBreathDefinition>();
|
|
|
|
public static void Initialize()
|
|
{
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
Core.AOS ? 0.16 : 0.05,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 100, 0, 0, 0, 0, 0,
|
|
30.0, 45.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0,
|
|
0,
|
|
0x227,
|
|
12));
|
|
|
|
// Skeletal Dragon / Renowned
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
Core.AOS ? 0.16 : 0.05,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 0, 100, 0, 0, 0, 0,
|
|
30.0, 45.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0x480,
|
|
0,
|
|
0x227,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(SkeletalDragonRenowned), typeof(SkeletalDragon) }));
|
|
|
|
// Leviathan
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
0.05,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
70, 0, 30, 0, 0, 0, 0,
|
|
5.0, 7.5,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0x1ED,
|
|
0,
|
|
0x227,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(Leviathan) }));
|
|
|
|
// Red Death
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
0.16,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 0, 0, 0, 0, 100, 0,
|
|
5.0, 7.5,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0x1ED,
|
|
0,
|
|
0x227,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(RedDeath) }));
|
|
|
|
// Frost Dragon/Drake
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
Core.AOS ? 0.16 : 0.05,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 0, 100, 0, 0, 0, 0,
|
|
30.0, 45.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
1264,
|
|
0,
|
|
0x227,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(FrostDragon), typeof(ColdDrake) }));
|
|
|
|
// Antlion
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
Core.AOS ? 0.16 : 0.05,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 0, 0, 100, 0, 0, 0,
|
|
30.0, 45.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0x3F,
|
|
0,
|
|
0,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(AntLion) }));
|
|
|
|
// Rend
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
0.06,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 100, 0, 0, 0, 0, 0,
|
|
30.0, 45.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0,
|
|
0,
|
|
0x227,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(Rend) }));
|
|
|
|
// Crystal Sea Serpent
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
0.55,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 0, 50, 0, 50, 0, 0,
|
|
30.0, 45.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0x1ED,
|
|
0,
|
|
0x227,
|
|
12,
|
|
false,
|
|
new Type[] { typeof(CrystalSeaSerpent) }));
|
|
|
|
// Crystal Hydra
|
|
Definitions.Add(new DragonBreathDefinition(
|
|
0.13,
|
|
1.0,
|
|
1.3,
|
|
1.0,
|
|
0, 0, 100, 0, 0, 0, 0,
|
|
5.0, 7.0,
|
|
0x36D4,
|
|
5,
|
|
0,
|
|
false,
|
|
false,
|
|
0x47E,
|
|
0,
|
|
0x56D,
|
|
12,
|
|
true,
|
|
new Type[] { typeof(CrystalHydra) }));
|
|
}
|
|
|
|
public static DragonBreathDefinition GetDefinition(BaseCreature bc)
|
|
{
|
|
var def = Definitions.FirstOrDefault(d => d.Uses != null && d.Uses.Any(type => type == bc.GetType()));
|
|
|
|
if (def == null)
|
|
{
|
|
return Definitions[0]; // default
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
public DragonBreathDefinition(
|
|
double scalar,
|
|
double stallTime,
|
|
double effectDelay,
|
|
double damageDelay,
|
|
int physDamage, int fireDamage, int coldDamage, int poisonDamage, int energyDamage, int chaosDamage, int directDamage,
|
|
double minDelay, double maxDelay,
|
|
int effectID,
|
|
int effectDuration,
|
|
int effectSpeed,
|
|
bool explodes,
|
|
bool fixedDirection,
|
|
int effectHue,
|
|
int renderMode,
|
|
int effectSound,
|
|
int angerAnimation,
|
|
bool attacksMultiples = false,
|
|
Type[] uses = null)
|
|
{
|
|
DamageScalar = scalar;
|
|
StallTime = stallTime;
|
|
EffectDelay = effectDelay;
|
|
DamageDelay = damageDelay;
|
|
|
|
PhysicalDamage = physDamage;
|
|
FireDamage = fireDamage;
|
|
ColdDamage = coldDamage;
|
|
PoisonDamage = poisonDamage;
|
|
EnergyDamage = energyDamage;
|
|
ChaosDamage = chaosDamage;
|
|
DirectDamage = directDamage;
|
|
|
|
MinDelay = minDelay;
|
|
MaxDelay = maxDelay;
|
|
|
|
EffectItemID = effectID;
|
|
EffectSpeed = effectDuration;
|
|
EffectExplodes = explodes;
|
|
EffectFixedDir = fixedDirection;
|
|
EffectHue = effectHue;
|
|
EffectRenderMode = renderMode;
|
|
EffectSound = effectSound;
|
|
AngerAnimation = angerAnimation;
|
|
AttacksMultipleTargets = attacksMultiples;
|
|
|
|
Uses = uses;
|
|
}
|
|
|
|
public int GetElementalDamage(BaseCreature bc, ElementType element)
|
|
{
|
|
switch (element)
|
|
{
|
|
default:
|
|
case ElementType.Physical:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Physical ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return PhysicalDamage;
|
|
}
|
|
case ElementType.Fire:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Fire ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return FireDamage;
|
|
}
|
|
case ElementType.Cold:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Cold ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return ColdDamage;
|
|
}
|
|
case ElementType.Poison:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Poison ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return PoisonDamage;
|
|
}
|
|
case ElementType.Energy:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Energy ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return EnergyDamage;
|
|
}
|
|
case ElementType.Chaos:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Chaos ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return ChaosDamage;
|
|
}
|
|
case ElementType.Direct:
|
|
if (bc is IElementalCreature)
|
|
{
|
|
return ((IElementalCreature)bc).ElementType == ElementType.Direct ? 100 : 0;
|
|
}
|
|
else
|
|
{
|
|
return DirectDamage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class HowlOfCacophony : SpecialAbility
|
|
{
|
|
private static Dictionary<Mobile, InternalTimer> _Table;
|
|
public override int ManaCost { get { return 25; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
|
|
public HowlOfCacophony()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table != null && _Table.ContainsKey(defender))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, InternalTimer>();
|
|
|
|
_Table[defender] = new InternalTimer(defender);
|
|
|
|
defender.SendSpeedControl(SpeedControlType.WalkSpeed);
|
|
defender.SendLocalizedMessage(1072069); // // A cacophonic sound lambastes you, suppressing your ability to move.
|
|
defender.PlaySound(0x584);
|
|
|
|
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.HowlOfCacophony, 1153793, 1153820, TimeSpan.FromSeconds(30), defender, "60\t5\t5"));
|
|
}
|
|
|
|
public static bool IsUnderEffects(Mobile m)
|
|
{
|
|
return _Table != null && _Table.ContainsKey(m);
|
|
}
|
|
|
|
private class InternalTimer : Timer
|
|
{
|
|
public Mobile Defender { get; set; }
|
|
|
|
public InternalTimer(Mobile defender)
|
|
: base(TimeSpan.FromSeconds(30))
|
|
{
|
|
Defender = defender;
|
|
Start();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if (_Table != null && _Table.ContainsKey(Defender))
|
|
{
|
|
_Table.Remove(Defender);
|
|
|
|
BuffInfo.RemoveBuff(Defender, BuffIcon.HowlOfCacophony);
|
|
Defender.SendSpeedControl(SpeedControlType.Disable);
|
|
}
|
|
|
|
Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class GraspingClaw : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public GraspingClaw()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
|
|
ExpireTimer timer = null;
|
|
|
|
if (_Table.ContainsKey(defender))
|
|
timer = _Table[defender];
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.DoExpire();
|
|
defender.SendLocalizedMessage(1070837); // The creature lands another blow in your weakened state.
|
|
}
|
|
else
|
|
defender.SendLocalizedMessage(1070836); // The blow from the creature's claws has made you more susceptible to physical attacks.
|
|
|
|
int effect = -(defender.PhysicalResistance * 15 / 100);
|
|
|
|
ResistanceMod mod = new ResistanceMod(ResistanceType.Physical, effect);
|
|
|
|
defender.FixedParticles(0x373A, 10, 15, 5018, EffectLayer.Waist);
|
|
defender.AddResistanceMod(mod);
|
|
|
|
timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(5.0));
|
|
timer.Start();
|
|
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
public static void Expire(Mobile m)
|
|
{
|
|
if (_Table != null && _Table.ContainsKey(m))
|
|
{
|
|
_Table.Remove(m);
|
|
}
|
|
}
|
|
|
|
public class ExpireTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
private readonly ResistanceMod m_Mod;
|
|
|
|
public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay)
|
|
: base(delay)
|
|
{
|
|
m_Mobile = m;
|
|
m_Mod = mod;
|
|
Priority = TimerPriority.TwoFiftyMS;
|
|
}
|
|
|
|
public void DoExpire()
|
|
{
|
|
m_Mobile.RemoveResistanceMod(m_Mod);
|
|
Stop();
|
|
|
|
Expire(m_Mobile);
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.SendLocalizedMessage(1070838); // Your resistance to physical attacks has returned.
|
|
DoExpire();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Inferno : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public Inferno()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
|
|
ExpireTimer timer = null;
|
|
|
|
if (_Table.ContainsKey(defender))
|
|
timer = _Table[defender];
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.DoExpire();
|
|
}
|
|
|
|
defender.SendLocalizedMessage(1070833); // The creature fans you with fire, reducing your resistance to fire attacks.
|
|
|
|
ResistanceMod mod = new ResistanceMod(ResistanceType.Fire, -25);
|
|
|
|
Effects.SendLocationParticles(defender, 0x3709, 10, 30, 5052);
|
|
Effects.PlaySound(defender.Location, defender.Map, 0x208);
|
|
|
|
timer = new ExpireTimer(defender, mod, TimeSpan.FromSeconds(6.0));
|
|
timer.Start();
|
|
|
|
AOS.Damage(defender, creature, Utility.RandomMinMax(46, 79), 0, 100, 0, 0, 0);
|
|
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
public static void Expire(Mobile m)
|
|
{
|
|
if (_Table != null && _Table.ContainsKey(m))
|
|
{
|
|
_Table.Remove(m);
|
|
}
|
|
}
|
|
|
|
public class ExpireTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
private readonly ResistanceMod m_Mod;
|
|
|
|
public ExpireTimer(Mobile m, ResistanceMod mod, TimeSpan delay)
|
|
: base(delay)
|
|
{
|
|
m_Mobile = m;
|
|
m_Mod = mod;
|
|
Priority = TimerPriority.TwoFiftyMS;
|
|
|
|
m.AddResistanceMod(mod);
|
|
}
|
|
|
|
public void DoExpire()
|
|
{
|
|
m_Mobile.RemoveResistanceMod(m_Mod);
|
|
Stop();
|
|
|
|
Expire(m_Mobile);
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.SendLocalizedMessage(1070834); // Your resistance to fire attacks has returned.
|
|
DoExpire();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class LightningForce : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public LightningForce()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
Server.Effects.SendBoltEffect(defender, true);
|
|
AOS.Damage(defender, creature, Utility.RandomMinMax(15, 20), 0, 0, 0, 0, 100);
|
|
}
|
|
}
|
|
|
|
public class ManaDrain : SpecialAbility
|
|
{
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
|
|
public ManaDrain()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (creature.Map == null)
|
|
return;
|
|
|
|
List<Mobile> list = new List<Mobile>();
|
|
IPooledEnumerable eable = creature.GetMobilesInRange(8);
|
|
|
|
foreach (Mobile m in eable)
|
|
{
|
|
if (AreaEffect.ValidTarget(creature, m))
|
|
list.Add(m);
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
foreach (Mobile m in list)
|
|
{
|
|
creature.DoHarmful(m, false);
|
|
|
|
m.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
|
m.PlaySound(0x231);
|
|
|
|
m.SendMessage("You feel the mana drain out of you!");
|
|
|
|
int toDrain = Utility.RandomMinMax(40, 60);
|
|
|
|
creature.Mana += toDrain;
|
|
m.Mana -= toDrain;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class RagingBreath : SpecialAbility
|
|
{
|
|
public static Dictionary<Mobile, InternalTimer> _Table;
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public RagingBreath()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, InternalTimer>();
|
|
|
|
InternalTimer timer = null;
|
|
|
|
if (_Table.ContainsKey(defender))
|
|
{
|
|
timer = _Table[defender];
|
|
}
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.Stop();
|
|
defender.SendLocalizedMessage(1070841); // The creature's breath continues to burn you!
|
|
}
|
|
else
|
|
{
|
|
defender.SendLocalizedMessage(1070842); // The creature's breath is burning you!
|
|
}
|
|
|
|
int layer = 0;
|
|
|
|
if (defender is BaseCreature)
|
|
{
|
|
layer = 163;
|
|
}
|
|
else
|
|
{
|
|
layer = 45;
|
|
}
|
|
|
|
Effects.SendPacket(defender.Location, defender.Map, new ParticleEffect(EffectType.FixedFrom, defender.Serial, Serial.Zero, 0x3709, defender.Location, defender.Location, 1, 15, false, false, 2735, 0, 4, 9502, 1, defender.Serial, layer, 0));
|
|
Effects.SendPacket(defender.Location, defender.Map, new ParticleEffect(EffectType.FixedFrom, defender.Serial, Serial.Zero, 0x3709, defender.Location, defender.Location, 10, 30, false, false, 0, 0, 0, 52, 1, defender.Serial, layer, 0));
|
|
defender.PlaySound(520);
|
|
|
|
timer = new InternalTimer(creature, defender);
|
|
timer.Start();
|
|
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
public static void EndFire(Mobile m)
|
|
{
|
|
if (_Table != null && _Table.ContainsKey(m))
|
|
{
|
|
_Table[m].Stop();
|
|
_Table.Remove(m);
|
|
m.SendLocalizedMessage(1070843); // The fiery breath dissipates.
|
|
}
|
|
}
|
|
|
|
public class InternalTimer : Timer
|
|
{
|
|
public Mobile Attacker { get; set; }
|
|
public Mobile Defender { get; set; }
|
|
|
|
private int _Tick;
|
|
|
|
public InternalTimer(Mobile attacker, Mobile defender)
|
|
: base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
|
|
{
|
|
Attacker = attacker;
|
|
Defender = defender;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
_Tick++;
|
|
|
|
if (_Tick > 10 || !Defender.Alive)
|
|
{
|
|
EndFire(Defender);
|
|
}
|
|
else
|
|
{
|
|
int layer = 0;
|
|
|
|
if (Defender is BaseCreature)
|
|
{
|
|
layer = 163;
|
|
}
|
|
else
|
|
{
|
|
layer = 45;
|
|
}
|
|
|
|
Effects.SendPacket(Defender.Location, Defender.Map, new ParticleEffect(EffectType.FixedFrom, Defender.Serial, Serial.Zero, 0x3709, Defender.Location, Defender.Location, 1, 15, false, false, 2735, 0, 4, 9502, 1, Defender.Serial, layer, 0));
|
|
AOS.Damage(Defender, Attacker, 10, 0, 100, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Repel : SpecialAbility
|
|
{
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotSpellDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public Repel()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
defender.SendLocalizedMessage(1070844); // The creature repels the attack back at you.
|
|
defender.FixedEffect(0x37B9, 10, 5);
|
|
AOS.Damage(defender, creature, damage / 2, 0, 0, 0, 0, 0, 0, 100);
|
|
|
|
damage = 0;
|
|
}
|
|
}
|
|
|
|
public class SearingWounds : SpecialAbility
|
|
{
|
|
private static Dictionary<Mobile, InternalTimer> _Table;
|
|
public override int ManaCost { get { return 25; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
|
|
public SearingWounds()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if(_Table != null && _Table.ContainsKey(defender))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(_Table == null)
|
|
_Table = new Dictionary<Mobile, InternalTimer>();
|
|
|
|
_Table[defender] = new InternalTimer(defender);
|
|
|
|
defender.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
|
defender.SendLocalizedMessage(1151177); // The searing attack cauterizes the wound on impact.
|
|
}
|
|
|
|
public static bool IsUnderEffects(Mobile m)
|
|
{
|
|
return _Table != null && _Table.ContainsKey(m);
|
|
}
|
|
|
|
private class InternalTimer : Timer
|
|
{
|
|
public Mobile Defender { get; set; }
|
|
|
|
public InternalTimer(Mobile defender)
|
|
: base(TimeSpan.FromSeconds(10))
|
|
{
|
|
Defender = defender;
|
|
Start();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if(_Table != null && _Table.ContainsKey(Defender))
|
|
{
|
|
_Table.Remove(Defender);
|
|
}
|
|
|
|
Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class StealLife : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public StealLife()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
defender.FixedParticles(0x374A, 1, 15, 5054, 23, 7, EffectLayer.Head);
|
|
defender.PlaySound(0x1F9);
|
|
|
|
AOS.Damage(defender, creature, damage, 0, 0, 0, 0, 100);
|
|
|
|
new InternalTimer(creature, defender, damage);
|
|
}
|
|
|
|
private class InternalTimer : Timer
|
|
{
|
|
public BaseCreature Attacker { get; set; }
|
|
public Mobile Defender { get; set; }
|
|
public int ToHeal { get; set; }
|
|
|
|
public InternalTimer(BaseCreature creature, Mobile defender, int toHeal)
|
|
: base(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), 5)
|
|
{
|
|
Attacker = creature;
|
|
Defender = defender;
|
|
ToHeal = toHeal;
|
|
|
|
defender.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
|
defender.PlaySound(0x231);
|
|
|
|
Start();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
Defender.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
|
Attacker.Hits = Math.Min(Attacker.HitsMax, Attacker.Hits + (ToHeal / 5));
|
|
}
|
|
}
|
|
}
|
|
|
|
public class VenomousBite : SpecialAbility
|
|
{
|
|
public override int ManaCost { get { return 30; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override MagicalAbility RequiredSchool { get { return MagicalAbility.Poisoning; } }
|
|
|
|
public VenomousBite()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
IPooledEnumerable eable = creature.GetMobilesInRange(3);
|
|
List<Mobile> list = new List<Mobile>();
|
|
|
|
list.Add(defender);
|
|
|
|
foreach(Mobile m in eable)
|
|
{
|
|
if (AreaEffect.ValidTarget(creature, m))
|
|
list.Add(m);
|
|
}
|
|
|
|
eable.Free();
|
|
Poison p = creature.GetHitPoison();
|
|
|
|
if (p == null)
|
|
return;
|
|
|
|
foreach(var m in list)
|
|
{
|
|
defender.PlaySound(0xDD);
|
|
defender.FixedParticles(0x3728, 244, 25, 9941, 1266, 0, EffectLayer.Waist);
|
|
|
|
m.SendLocalizedMessage(1008097, false, creature.Name); // : poisoned you!
|
|
|
|
m.ApplyPoison(creature, p);
|
|
}
|
|
|
|
if (creature.Controlled && list.Count > 0)
|
|
{
|
|
var profile = PetTrainingHelper.GetAbilityProfile(creature);
|
|
|
|
if ((profile != null && profile.HasAbility(MagicalAbility.Poisoning)) || 0.2 > Utility.RandomDouble())
|
|
creature.CheckSkill(SkillName.Poisoning, 0, creature.Skills[SkillName.Poisoning].Cap);
|
|
}
|
|
|
|
ColUtility.Free(list);
|
|
}
|
|
}
|
|
|
|
public class ViciousBite : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 20; } }
|
|
|
|
private static Dictionary<Mobile, InternalTimer> _Table;
|
|
|
|
public ViciousBite()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if(_Table != null && _Table.ContainsKey(defender))
|
|
return;
|
|
|
|
else if (_Table == null)
|
|
_Table = new Dictionary<Mobile, InternalTimer>();
|
|
|
|
defender.PlaySound(0x1324);
|
|
defender.SendLocalizedMessage(1234567); // The creature gives you a particular vicious bite.
|
|
Effects.SendLocationParticles(EffectItem.Create(defender.Location, defender.Map, EffectItem.DefaultDuration), 0x37CC, 1, 40, 97, 3, 9917, 0);
|
|
|
|
_Table[defender] = new InternalTimer(creature, defender);
|
|
}
|
|
|
|
private class InternalTimer : Timer
|
|
{
|
|
public BaseCreature Attacker { get; set; }
|
|
public Mobile Defender { get; set; }
|
|
|
|
private int _Tick;
|
|
|
|
public InternalTimer(BaseCreature creature, Mobile defender)
|
|
: base(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(20))
|
|
{
|
|
Attacker = creature;
|
|
Defender = defender;
|
|
Start();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
_Tick++;
|
|
|
|
AOS.Damage(Defender, Attacker, _Tick * 5, 0, 0, 0, 0, 0, 0, 100);
|
|
Defender.SendLocalizedMessage(1112473); //Your vicious wound is festering!
|
|
|
|
if(_Tick >= 20 || !Defender.Alive || Defender.IsDeadBondedPet)
|
|
{
|
|
Stop();
|
|
|
|
if(_Table.ContainsKey(Defender))
|
|
_Table.Remove(Defender);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class RuneCorruption : SpecialAbility
|
|
{
|
|
public static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override int ManaCost { get { return 30; } }
|
|
|
|
public RuneCorruption()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if(_Table == null)
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
|
|
ExpireTimer timer = null;
|
|
|
|
if (_Table.ContainsKey(creature))
|
|
timer = _Table[creature];
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.DoExpire();
|
|
defender.SendLocalizedMessage(1070845); // The creature continues to corrupt your armor!
|
|
}
|
|
else
|
|
defender.SendLocalizedMessage(1070846); // The creature magically corrupts your armor!
|
|
|
|
List<ResistanceMod> mods = new List<ResistanceMod>();
|
|
|
|
int phy = 0; int fire = 0; int cold = 0; int poison = 0; int energy = 0;
|
|
|
|
if (Core.ML)
|
|
{
|
|
if (defender.PhysicalResistance > 0)
|
|
{
|
|
phy = defender.PhysicalResistance / 2;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Physical, -phy));
|
|
}
|
|
|
|
if (defender.FireResistance > 0)
|
|
{
|
|
fire = defender.FireResistance / 2;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Fire, -fire));
|
|
}
|
|
|
|
if (defender.ColdResistance > 0)
|
|
{
|
|
cold = defender.ColdResistance / 2;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Cold, -cold));
|
|
}
|
|
|
|
if (defender.PoisonResistance > 0)
|
|
{
|
|
poison = defender.PoisonResistance / 2;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Poison, -poison));
|
|
}
|
|
|
|
if (defender.EnergyResistance > 0)
|
|
{
|
|
energy = defender.EnergyResistance / 2;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Energy, -energy));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (defender.PhysicalResistance > 0)
|
|
{
|
|
phy = (defender.PhysicalResistance > 70) ? 70 : defender.PhysicalResistance;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Physical, -phy));
|
|
}
|
|
|
|
if (defender.FireResistance > 0)
|
|
{
|
|
fire = (defender.FireResistance > 70) ? 70 : defender.FireResistance;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Fire, -fire));
|
|
}
|
|
|
|
if (defender.ColdResistance > 0)
|
|
{
|
|
cold = (defender.ColdResistance > 70) ? 70 : defender.ColdResistance;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Cold, -cold));
|
|
}
|
|
|
|
if (defender.PoisonResistance > 0)
|
|
{
|
|
poison = (defender.PoisonResistance > 70) ? 70 : defender.PoisonResistance;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Poison, -poison));
|
|
}
|
|
|
|
if (defender.EnergyResistance > 0)
|
|
{
|
|
energy = (defender.EnergyResistance > 70) ? 70 : defender.EnergyResistance;
|
|
|
|
mods.Add(new ResistanceMod(ResistanceType.Energy, -energy));
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < mods.Count; ++i)
|
|
defender.AddResistanceMod(mods[i]);
|
|
|
|
defender.FixedEffect(0x37B9, 10, 5);
|
|
|
|
timer = new ExpireTimer(defender, mods, TimeSpan.FromSeconds(5.0));
|
|
timer.Start();
|
|
|
|
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.RuneBeetleCorruption, 1153796, 1153823, TimeSpan.FromSeconds(5.0), defender, String.Format("{0}\t{1}\t{2}\t{3}\t{4}", phy, cold, poison, energy, fire)));
|
|
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
public class ExpireTimer : Timer
|
|
{
|
|
private readonly Mobile m_Mobile;
|
|
private readonly List<ResistanceMod> m_Mods;
|
|
|
|
public ExpireTimer(Mobile m, List<ResistanceMod> mods, TimeSpan delay)
|
|
: base(delay)
|
|
{
|
|
m_Mobile = m;
|
|
m_Mods = mods;
|
|
Priority = TimerPriority.TwoFiftyMS;
|
|
}
|
|
|
|
public void DoExpire()
|
|
{
|
|
for (int i = 0; i < m_Mods.Count; ++i)
|
|
m_Mobile.RemoveResistanceMod(m_Mods[i]);
|
|
|
|
Stop();
|
|
|
|
if (_Table != null && _Table.ContainsKey(m_Mobile))
|
|
_Table.Remove(m_Mobile);
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Mobile.SendMessage("The corruption of your armor has worn off");
|
|
DoExpire();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class LifeLeech : SpecialAbility
|
|
{
|
|
public override int ManaCost { get { return 5; } }
|
|
public override TimeSpan CooldownDuration { get { return TimeSpan.FromSeconds(Utility.Random(10, 20)); } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
|
|
public static Dictionary<Mobile, InternalTimer> _Table;
|
|
|
|
public LifeLeech()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, InternalTimer>();
|
|
|
|
InternalTimer t = null;
|
|
|
|
if (_Table.ContainsKey(defender))
|
|
t = _Table[defender];
|
|
|
|
if (t != null)
|
|
t.Stop();
|
|
|
|
t = new InternalTimer(creature, defender);
|
|
_Table[defender] = t;
|
|
|
|
defender.SendLocalizedMessage(1070848); // You feel your life force being stolen away!
|
|
|
|
t.Start();
|
|
}
|
|
|
|
public static void DrainLife(Mobile m, Mobile from)
|
|
{
|
|
if (m.Alive)
|
|
{
|
|
int damageGiven = AOS.Damage(m, from, 5, 0, 0, 0, 0, 100);
|
|
from.Hits += damageGiven;
|
|
|
|
m.SendLocalizedMessage(1070847); // The creature continues to steal your life force!
|
|
}
|
|
else
|
|
{
|
|
EndLifeDrain(m);
|
|
}
|
|
}
|
|
|
|
public static void EndLifeDrain(Mobile m)
|
|
{
|
|
if (_Table != null && _Table.ContainsKey(m))
|
|
{
|
|
_Table[m].Stop();
|
|
_Table.Remove(m);
|
|
m.SendLocalizedMessage(1070849); // The drain on your life force is gone.
|
|
}
|
|
}
|
|
|
|
public class InternalTimer : Timer
|
|
{
|
|
private Mobile m_From;
|
|
private Mobile m_Mobile;
|
|
private int m_Count;
|
|
|
|
public InternalTimer(Mobile from, Mobile m)
|
|
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
|
|
{
|
|
m_From = from;
|
|
m_Mobile = m;
|
|
Priority = TimerPriority.TwoFiftyMS;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
DrainLife(m_Mobile, m_From);
|
|
|
|
if (Running && ++m_Count == 5)
|
|
EndLifeDrain(m_Mobile);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Webbing : SpecialAbility
|
|
{
|
|
public override int ManaCost { get { return 0; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
|
|
public Webbing()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (creature.Map == null)
|
|
return;
|
|
|
|
List<Mobile> list = new List<Mobile>();
|
|
IPooledEnumerable eable = creature.GetMobilesInRange(12);
|
|
|
|
foreach (Mobile m in eable)
|
|
{
|
|
if (AreaEffect.ValidTarget(creature, m))
|
|
list.Add(m);
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if (list.Count > 0)
|
|
{
|
|
Mobile m = list[Utility.Random(list.Count)];
|
|
|
|
creature.DoHarmful(m, false);
|
|
creature.Direction = creature.GetDirectionTo(m);
|
|
|
|
SpiderWebbing web = new SpiderWebbing(m);
|
|
Effects.SendMovingParticles(creature, m, web.ItemID, 12, 0, false, false, 0, 0, 9502, 1, 0, (EffectLayer)255, 0x100);
|
|
Timer.DelayCall(TimeSpan.FromSeconds(0.5), () => web.MoveToWorld(m.Location, m.Map));
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Anemia : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
|
|
private static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public Anemia()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
|
|
if (_Table.ContainsKey(defender))
|
|
{
|
|
defender.PublicOverheadMessage(MessageType.Regular, 0x25, 1111668); // * The creature is repulsed by your diseased blood. *
|
|
}
|
|
else
|
|
{
|
|
defender.PublicOverheadMessage(MessageType.Regular, 0x25, 1111698); // *The creature drains some of your blood to replenish its health.*
|
|
|
|
creature.Hits = Math.Min(creature.HitsMax, creature.Hits + Utility.RandomMinMax(50, 70));
|
|
}
|
|
|
|
TryInfect(creature, defender);
|
|
}
|
|
|
|
private void TryInfect(BaseCreature creature, Mobile attacker)
|
|
{
|
|
if (!_Table.ContainsKey(attacker) && creature.InRange(attacker, 1) && 0.25 > Utility.RandomDouble() && !FountainOfFortune.UnderProtection(attacker))
|
|
{
|
|
attacker.SendLocalizedMessage(1111669); // The creature's attack weakens you. You have become anemic.
|
|
|
|
Effects.SendPacket(attacker, attacker.Map, new GraphicalEffect(EffectType.FixedFrom, attacker.Serial, Serial.Zero, 0x375A, attacker.Location, attacker.Location, 9, 20, true, false));
|
|
Effects.SendTargetParticles(attacker, 0x373A, 1, 15, 0x26B9, EffectLayer.Head);
|
|
Effects.SendLocationParticles(attacker, 0x11A6, 9, 32, 0x253A);
|
|
|
|
attacker.PlaySound(0x1ED);
|
|
|
|
ExpireTimer timer = new ExpireTimer(attacker);
|
|
timer.Start();
|
|
|
|
int str = attacker.RawStr / 3;
|
|
int dex = attacker.RawDex / 3;
|
|
int Int = attacker.RawInt / 3;
|
|
|
|
attacker.AddStatMod(new StatMod(StatType.Str, "BloodWorm_Str", -str, TimeSpan.FromSeconds(60)));
|
|
attacker.AddStatMod(new StatMod(StatType.Dex, "BloodWorm_Dex", -dex, TimeSpan.FromSeconds(60)));
|
|
attacker.AddStatMod(new StatMod(StatType.Int, "BloodWorm_Int", -Int, TimeSpan.FromSeconds(60)));
|
|
|
|
// -~1_STR~ strength.<br>-~2_INT~ intelligence.<br>-~3_DEX~ dexterity.<br> Drains all stamina.
|
|
BuffInfo.AddBuff(attacker, new BuffInfo(BuffIcon.BloodwormAnemia, 1153797, 1153824, String.Format("{0}\t{1}\t{2}", str, dex, Int)));
|
|
|
|
_Table.Add(attacker, timer);
|
|
}
|
|
}
|
|
|
|
private class ExpireTimer : Timer
|
|
{
|
|
private DateTime _Expires;
|
|
private Mobile m_Victim;
|
|
|
|
public ExpireTimer(Mobile m)
|
|
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0))
|
|
{
|
|
m_Victim = m;
|
|
_Expires = DateTime.UtcNow + TimeSpan.FromMinutes(1);
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if (_Expires < DateTime.UtcNow || m_Victim.Deleted || !m_Victim.Alive || m_Victim.IsDeadBondedPet)
|
|
{
|
|
m_Victim.SendLocalizedMessage(1111670); // You recover from your anemia.
|
|
|
|
_Table.Remove(m_Victim);
|
|
|
|
BuffInfo.RemoveBuff(m_Victim, BuffIcon.BloodwormAnemia);
|
|
|
|
Stop();
|
|
}
|
|
else
|
|
{
|
|
m_Victim.Stam -= m_Victim.Stam < 2 ? 0 : Utility.RandomMinMax(2, 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class BloodDisease : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
|
|
private static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public BloodDisease()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
|
|
if (!_Table.ContainsKey(defender) && creature.InRange(defender, 1) && 0.25 > Utility.RandomDouble() && !FountainOfFortune.UnderProtection(defender))
|
|
{
|
|
// The rotworm has infected you with blood disease!!
|
|
defender.SendLocalizedMessage(1111672, "", 0x25);
|
|
|
|
defender.PlaySound(0x213);
|
|
Effects.SendTargetParticles(defender, 0x373A, 1, 15, 0x26B9, EffectLayer.Head);
|
|
|
|
ExpireTimer timer = new ExpireTimer(defender, creature);
|
|
timer.Start();
|
|
|
|
// TODO: 2nd cliloc
|
|
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.RotwormBloodDisease, 1153798, 1153798));
|
|
|
|
_Table.Add(defender, timer);
|
|
}
|
|
}
|
|
|
|
private class ExpireTimer : Timer
|
|
{
|
|
private const int MaxCount = 8;
|
|
|
|
private int m_Count;
|
|
private Mobile m_Victim;
|
|
private Mobile m_Attacker;
|
|
|
|
public ExpireTimer(Mobile victim, Mobile attacker)
|
|
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0))
|
|
{
|
|
m_Victim = victim;
|
|
m_Attacker = attacker;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if (m_Count == MaxCount || m_Victim.Deleted || !m_Victim.Alive || m_Victim.IsDeadBondedPet)
|
|
{
|
|
// You no longer feel sick.
|
|
m_Victim.SendLocalizedMessage(1111673);
|
|
|
|
_Table.Remove(m_Victim);
|
|
|
|
BuffInfo.RemoveBuff(m_Victim, BuffIcon.RotwormBloodDisease);
|
|
|
|
Stop();
|
|
}
|
|
else if (m_Count > 0)
|
|
{
|
|
AOS.Damage(m_Victim, m_Attacker, Utility.RandomMinMax(10, 20), 0, 0, 0, 100, 0);
|
|
m_Victim.Combatant = null;
|
|
}
|
|
|
|
m_Count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class StickySkin : SpecialAbility
|
|
{
|
|
public override int ManaCost { get { return 5; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
|
|
public static List<Mobile> _Table;
|
|
|
|
public StickySkin()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
//TODO: Effects/Sound
|
|
if (_Table == null)
|
|
_Table = new List<Mobile>();
|
|
|
|
defender.SendLocalizedMessage(1153752); // Your attack speed has been slowed.
|
|
|
|
_Table.Add(defender);
|
|
Timer.DelayCall(TimeSpan.FromSeconds(10), () =>
|
|
{
|
|
RemoveEffects(defender);
|
|
});
|
|
}
|
|
|
|
public static bool IsUnderEffects(Mobile m)
|
|
{
|
|
return _Table != null && _Table.Contains(m);
|
|
}
|
|
|
|
public static void RemoveEffects(Mobile m)
|
|
{
|
|
if (_Table != null && _Table.Contains(m))
|
|
{
|
|
_Table.Remove(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TailSwipe : SpecialAbility
|
|
{
|
|
public override int ManaCost { get { return 30; } }
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
|
|
public TailSwipe()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if(Utility.RandomBool())
|
|
{
|
|
defender.SendLocalizedMessage(1112554); // You're stunned as the creature's tail knocks the wind out of you.
|
|
|
|
defender.PlaySound(0x204);
|
|
defender.FixedEffect(0x376A, 6, 1);
|
|
|
|
defender.Paralyze(TimeSpan.FromSeconds(3));
|
|
}
|
|
else
|
|
{
|
|
defender.SendLocalizedMessage(1112555); // You're left confused as the creature's tail catches you right in the face!
|
|
|
|
defender.PlaySound(0x204);
|
|
defender.FixedEffect(0x376A, 6, 1);
|
|
|
|
defender.AddStatMod(new StatMod(StatType.Dex, "[TailSwipe] Dex", -20, TimeSpan.FromSeconds(5)));
|
|
defender.AddStatMod(new StatMod(StatType.Int, "[TailSwipe] Int", -20, TimeSpan.FromSeconds(5)));
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Rage : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
|
|
private static Dictionary<Mobile, ExpireTimer> _Table;
|
|
|
|
public Rage()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (_Table == null)
|
|
_Table = new Dictionary<Mobile, ExpireTimer>();
|
|
|
|
ExpireTimer timer = null;
|
|
|
|
if (_Table.ContainsKey(defender))
|
|
timer = _Table[defender];
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.DoExpire();
|
|
defender.SendLocalizedMessage(1070825); // The creature continues to rage!
|
|
}
|
|
else
|
|
defender.SendLocalizedMessage(1070826); // The creature goes into a rage, inflicting heavy damage!
|
|
|
|
timer = new ExpireTimer(defender, creature);
|
|
timer.Start();
|
|
_Table[defender] = timer;
|
|
}
|
|
|
|
private class ExpireTimer : Timer
|
|
{
|
|
private Mobile m_Mobile;
|
|
private 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();
|
|
_Table.Remove(m_Mobile);
|
|
}
|
|
|
|
public void DrainLife()
|
|
{
|
|
if (m_Mobile.Alive)
|
|
m_Mobile.Damage(2, m_From);
|
|
else
|
|
DoExpire();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
DrainLife();
|
|
|
|
if (++m_Count >= 5)
|
|
{
|
|
DoExpire();
|
|
m_Mobile.SendLocalizedMessage(1070824); // The creature's rage subsides.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Heal : SpecialAbility
|
|
{
|
|
public override bool TriggerOnThink { get { return true; } }
|
|
public override double TriggerChance { get { return 1.0; } }
|
|
public override bool RequiresCombatant { get { return false; } }
|
|
public override int ManaCost { get { return 15; } }
|
|
public override TimeSpan CooldownDuration { get { return TimeSpan.MinValue; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
|
|
public Heal()
|
|
{
|
|
}
|
|
|
|
public override bool Trigger(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (CheckMana(creature) && Validate(creature, defender) && TriggerChance >= Utility.RandomDouble())
|
|
{
|
|
if (creature.CheckHeal())
|
|
{
|
|
creature.Mana -= ManaCost;
|
|
AddToCooldown(creature);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile target, ref int damage)
|
|
{
|
|
creature.CheckHeal();
|
|
}
|
|
}
|
|
|
|
public class PoisonSpit : SpecialAbility
|
|
{
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotSpellDamage { get { return true; } }
|
|
public override int MaxRange { get { return 10; } }
|
|
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
|
|
public PoisonSpit()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile target, ref int damage)
|
|
{
|
|
Effects.SendMovingEffect(creature, target, 0x36D4, 5, 0, false, true, 63, 0);
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1), () =>
|
|
{
|
|
creature.DoHarmful(target);
|
|
target.ApplyPoison(creature, creature.HitPoison ?? Poison.Regular);
|
|
target.SendLocalizedMessage(1070821, creature.Name); // ~1_CREATURE~ spits a poisonous substance at you!
|
|
});
|
|
}
|
|
}
|
|
|
|
public class TrueFear : SpecialAbility
|
|
{
|
|
public override bool TriggerOnApproach { get { return true; } }
|
|
public override int MaxRange { get { return 8; } }
|
|
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
public override TimeSpan CooldownDuration { get { return TimeSpan.FromMinutes(Utility.RandomMinMax(1, 3)); } }
|
|
|
|
public TrueFear()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile target, ref int damage)
|
|
{
|
|
int seconds = (int)Math.Max(1, (13.0 - (target.Skills[SkillName.MagicResist].Value / 10.0)));
|
|
|
|
int number;
|
|
|
|
if (seconds <= 2)
|
|
number = 1080339; // A sense of discomfort passes through you, but it fades quickly
|
|
else if (seconds <= 4)
|
|
number = 1080340; // An unfamiliar fear washes over you, and for a moment you're unable to move
|
|
else if (seconds <= 7)
|
|
number = 1080341; // Panic grips you! You're unable to move, to think, to feel anything but fear!
|
|
else if (seconds <= 10)
|
|
number = 1080342; // Terror slices into your very being, destroying any chance of resisting ~1_name~ you might have had
|
|
else
|
|
number = 1080343; // Everything around you dissolves into darkness as ~1_name~'s burning eyes fill your vision
|
|
|
|
target.SendLocalizedMessage(number, creature.Name, 0x21);
|
|
target.Frozen = true;
|
|
|
|
BuffInfo.AddBuff(target, new BuffInfo(BuffIcon.TrueFear, 1153791, 1153827, TimeSpan.FromSeconds(seconds), target));
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(seconds), () =>
|
|
{
|
|
target.Frozen = false;
|
|
target.SendLocalizedMessage(1005603); // You can move again!
|
|
});
|
|
}
|
|
}
|
|
|
|
public class ColossalBlow : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
public override double TriggerChance { get { return 0.3; } }
|
|
public override TimeSpan CooldownDuration { get { return TimeSpan.FromSeconds(10); } }
|
|
|
|
public ColossalBlow()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
if (Core.SA)
|
|
{
|
|
defender.Animate(AnimationType.Die, 0);
|
|
}
|
|
else
|
|
{
|
|
defender.Animate(21, 6, 1, true, false, 0);
|
|
}
|
|
|
|
creature.PlaySound(0xEE);
|
|
defender.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1070696); // You have been stunned by a colossal blow!
|
|
|
|
BaseWeapon weapon = creature.Weapon as BaseWeapon;
|
|
|
|
if (weapon != null)
|
|
weapon.OnHit(creature, defender);
|
|
|
|
if (defender.Alive)
|
|
{
|
|
defender.Frozen = true;
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(5.0), victim =>
|
|
{
|
|
victim.Frozen = false;
|
|
victim.Combatant = null;
|
|
victim.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1070695); // You recover your senses.
|
|
|
|
}, defender);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class LifeDrain : SpecialAbility
|
|
{
|
|
public override bool TriggerOnDoMeleeDamage { get { return true; } }
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override TimeSpan CooldownDuration { get { return TimeSpan.FromSeconds(1); } }
|
|
|
|
public LifeDrain()
|
|
{
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
foreach (Mobile m in AreaEffect.FindValidTargets(creature, 2).OfType<Mobile>())
|
|
{
|
|
if (!CanDrainLife(creature, defender))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
creature.DoHarmful(defender);
|
|
|
|
defender.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
|
|
defender.PlaySound(0x231);
|
|
|
|
defender.SendMessage("You feel the life drain out of you!");
|
|
|
|
int toDrain = GetDrainAmount(creature, defender);
|
|
|
|
if (defender is PlayerMobile)
|
|
{
|
|
toDrain = (int)LifeShieldLotion.HandleLifeDrain((PlayerMobile)defender, toDrain);
|
|
}
|
|
|
|
creature.Hits += toDrain;
|
|
AOS.Damage(m, creature, toDrain, 0, 0, 0, 0, 0, 0, 100);
|
|
|
|
creature.OnDrainLife(defender);
|
|
}
|
|
}
|
|
|
|
public bool CanDrainLife(BaseCreature bc, Mobile victim)
|
|
{
|
|
if (bc is Succubus && victim.Female)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public int GetDrainAmount(BaseCreature bc, Mobile victim)
|
|
{
|
|
int amount = Utility.RandomMinMax(10, 40);
|
|
|
|
if (bc is Semidar && !victim.Female)
|
|
{
|
|
amount *= 2;
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
}
|
|
|
|
public class ColossalRage : SpecialAbility
|
|
{
|
|
public override bool TriggerOnGotMeleeDamage { get { return true; } }
|
|
public override bool NaturalAbility { get { return true; } }
|
|
public override int ManaCost { get { return 0; } }
|
|
public override double TriggerChance { get { return .5; } }
|
|
public override TimeSpan CooldownDuration { get { return TimeSpan.FromSeconds(15); } }
|
|
|
|
public ColossalRage()
|
|
{
|
|
}
|
|
|
|
public override bool Validate(BaseCreature attacker, Mobile defender)
|
|
{
|
|
if (defender.Hits < ((double)defender.HitsMax * .33))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return base.Validate(attacker, defender);
|
|
}
|
|
|
|
public override void DoEffects(BaseCreature creature, Mobile defender, ref int damage)
|
|
{
|
|
AddRage(creature);
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(.25), () =>
|
|
{
|
|
creature.PublicOverheadMessage(MessageType.Regular, 0x20, 1113587); // The creature goes into a frenzied rage!
|
|
});
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(10), () =>
|
|
{
|
|
RemoveRage(creature);
|
|
});
|
|
}
|
|
|
|
public static void AddRage(BaseCreature bc)
|
|
{
|
|
if (InRage == null)
|
|
{
|
|
InRage = new List<BaseCreature>();
|
|
}
|
|
|
|
InRage.Add(bc);
|
|
bc.HueMod = 1157;
|
|
bc.Stam = bc.StamMax;
|
|
}
|
|
|
|
public static void RemoveRage(BaseCreature bc)
|
|
{
|
|
if (InRage != null && InRage.Contains(bc))
|
|
{
|
|
bc.HueMod = -1;
|
|
InRage.Remove(bc);
|
|
|
|
if (InRage.Count == 0)
|
|
{
|
|
InRage = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool HasRage(BaseCreature bc)
|
|
{
|
|
return InRage != null && InRage.Contains(bc);
|
|
}
|
|
|
|
public static List<BaseCreature> InRage { get; set; }
|
|
}
|
|
}
|