#W# Source and Scripts added. Added Scripts/Settings.cs to expose some basic tweak able settings.
This commit is contained in:
parent
b51c58f514
commit
3045c83799
3512 changed files with 627673 additions and 0 deletions
15
Scripts/Spells/Base/DisturbType.cs
Normal file
15
Scripts/Spells/Base/DisturbType.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Server;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public enum DisturbType
|
||||
{
|
||||
Unspecified,
|
||||
EquipRequest,
|
||||
UseRequest,
|
||||
Hurt,
|
||||
Kill,
|
||||
NewCast
|
||||
}
|
||||
}
|
||||
116
Scripts/Spells/Base/MagerySpell.cs
Normal file
116
Scripts/Spells/Base/MagerySpell.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public abstract class MagerySpell : Spell
|
||||
{
|
||||
public MagerySpell( Mobile caster, Item scroll, SpellInfo info )
|
||||
: base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public abstract SpellCircle Circle { get; }
|
||||
|
||||
public override bool ConsumeReagents()
|
||||
{
|
||||
if( base.ConsumeReagents() )
|
||||
return true;
|
||||
|
||||
if( ArcaneGem.ConsumeCharges( Caster, (Core.SE ? 1 : 1 + (int)Circle) ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private const double ChanceOffset = 20.0, ChanceLength = 100.0 / 7.0;
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
int circle = (int)Circle;
|
||||
|
||||
if( Scroll != null )
|
||||
circle -= 2;
|
||||
|
||||
double avg = ChanceLength * circle;
|
||||
|
||||
min = avg - ChanceOffset;
|
||||
max = avg + ChanceOffset;
|
||||
}
|
||||
|
||||
private static int[] m_ManaTable = new int[] { 4, 6, 9, 11, 14, 20, 40, 50 };
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
if( Scroll is BaseWand )
|
||||
return 0;
|
||||
|
||||
return m_ManaTable[(int)Circle];
|
||||
}
|
||||
|
||||
public override double GetResistSkill( Mobile m )
|
||||
{
|
||||
int maxSkill = (1 + (int)Circle) * 10;
|
||||
maxSkill += (1 + ((int)Circle / 6)) * 25;
|
||||
|
||||
if( m.Skills[SkillName.MagicResist].Value < maxSkill )
|
||||
m.CheckSkill( SkillName.MagicResist, 0.0, m.Skills[SkillName.MagicResist].Cap );
|
||||
|
||||
return m.Skills[SkillName.MagicResist].Value;
|
||||
}
|
||||
|
||||
public virtual bool CheckResisted( Mobile target )
|
||||
{
|
||||
double n = GetResistPercent( target );
|
||||
|
||||
n /= 100.0;
|
||||
|
||||
if( n <= 0.0 )
|
||||
return false;
|
||||
|
||||
if( n >= 1.0 )
|
||||
return true;
|
||||
|
||||
int maxSkill = (1 + (int)Circle) * 10;
|
||||
maxSkill += (1 + ((int)Circle / 6)) * 25;
|
||||
|
||||
if( target.Skills[SkillName.MagicResist].Value < maxSkill )
|
||||
target.CheckSkill( SkillName.MagicResist, 0.0, target.Skills[SkillName.MagicResist].Cap );
|
||||
|
||||
return (n >= Utility.RandomDouble());
|
||||
}
|
||||
|
||||
public virtual double GetResistPercentForCircle( Mobile target, SpellCircle circle )
|
||||
{
|
||||
double firstPercent = target.Skills[SkillName.MagicResist].Value / 5.0;
|
||||
double secondPercent = target.Skills[SkillName.MagicResist].Value - (((Caster.Skills[CastSkill].Value - 20.0) / 5.0) + (1 + (int)circle) * 5.0);
|
||||
|
||||
return (firstPercent > secondPercent ? firstPercent : secondPercent) / 2.0; // Seems should be about half of what stratics says.
|
||||
}
|
||||
|
||||
public virtual double GetResistPercent( Mobile target )
|
||||
{
|
||||
return GetResistPercentForCircle( target, Circle );
|
||||
}
|
||||
|
||||
public override TimeSpan GetCastDelay()
|
||||
{
|
||||
if( !Core.ML && Scroll is BaseWand )
|
||||
return TimeSpan.Zero;
|
||||
|
||||
if( !Core.AOS )
|
||||
return TimeSpan.FromSeconds( 0.5 + (0.25 * (int)Circle) );
|
||||
|
||||
return base.GetCastDelay();
|
||||
}
|
||||
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds( (3 + (int)Circle) * CastDelaySecondsPerTick );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
362
Scripts/Spells/Base/SpecialMove.cs
Normal file
362
Scripts/Spells/Base/SpecialMove.cs
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Spells.Ninjitsu;
|
||||
using Server.Spells.Bushido;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public abstract class SpecialMove
|
||||
{
|
||||
public virtual int BaseMana{ get{ return 0; } }
|
||||
|
||||
public virtual SkillName MoveSkill{ get{ return SkillName.Bushido; } }
|
||||
public virtual double RequiredSkill{ get{ return 0.0; } }
|
||||
|
||||
public virtual TextDefinition AbilityMessage{ get{ return 0; } }
|
||||
|
||||
public virtual bool BlockedByAnimalForm{ get{ return true; } }
|
||||
public virtual bool DelayedContext{ get{ return false; } }
|
||||
|
||||
public virtual int GetAccuracyBonus( Mobile attacker )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// Called before swinging, to make sure the accuracy scalar is to be computed.
|
||||
public virtual bool OnBeforeSwing( Mobile attacker, Mobile defender )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called when a hit connects, but before damage is calculated.
|
||||
public virtual bool OnBeforeDamage( Mobile attacker, Mobile defender )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called as soon as the ability is used.
|
||||
public virtual void OnUse( Mobile from )
|
||||
{
|
||||
}
|
||||
|
||||
// Called when a hit connects, at the end of the weapon.OnHit() method.
|
||||
public virtual void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
}
|
||||
|
||||
// Called when a hit misses.
|
||||
public virtual void OnMiss( Mobile attacker, Mobile defender )
|
||||
{
|
||||
}
|
||||
|
||||
// Called when the move is cleared.
|
||||
public virtual void OnClearMove( Mobile from )
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool IgnoreArmor( Mobile attacker )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual double GetPropertyBonus( Mobile attacker )
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
public virtual bool CheckSkills( Mobile m )
|
||||
{
|
||||
if ( m.Skills[MoveSkill].Value < RequiredSkill )
|
||||
{
|
||||
string args = String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), MoveSkill.ToString() );
|
||||
m.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual int ScaleMana( Mobile m, int mana )
|
||||
{
|
||||
double scalar = 1.0;
|
||||
|
||||
if ( !Server.Spells.Necromancy.MindRotSpell.GetMindRotScalar( m, ref scalar ) )
|
||||
scalar = 1.0;
|
||||
|
||||
// Lower Mana Cost = 40%
|
||||
int lmc = Math.Min( AosAttributes.GetValue( m, AosAttribute.LowerManaCost ), 40 );
|
||||
|
||||
scalar -= (double)lmc / 100;
|
||||
|
||||
int total = (int)(mana * scalar);
|
||||
|
||||
if ( m.Skills[MoveSkill].Value < 50.0 && GetContext( m ) != null )
|
||||
total *= 2;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public virtual bool CheckMana( Mobile from, bool consume )
|
||||
{
|
||||
int mana = ScaleMana( from, BaseMana );
|
||||
|
||||
if ( from.Mana < mana )
|
||||
{
|
||||
from.SendLocalizedMessage( 1060181, mana.ToString() ); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( consume )
|
||||
{
|
||||
if ( !DelayedContext )
|
||||
SetContext( from );
|
||||
|
||||
from.Mana -= mana;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void SetContext( Mobile from )
|
||||
{
|
||||
if ( GetContext( from ) == null )
|
||||
{
|
||||
if ( DelayedContext || from.Skills[MoveSkill].Value < 50.0 )
|
||||
{
|
||||
Timer timer = new SpecialMoveTimer( from );
|
||||
timer.Start();
|
||||
|
||||
AddContext( from, new SpecialMoveContext( timer, this.GetType() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool Validate( Mobile from )
|
||||
{
|
||||
if ( !from.Player )
|
||||
return true;
|
||||
|
||||
if ( Bushido.HonorableExecution.IsUnderPenalty( from ) )
|
||||
{
|
||||
from.SendLocalizedMessage( 1063024 ); // You cannot perform this special move right now.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( Ninjitsu.AnimalForm.UnderTransformation( from ) )
|
||||
{
|
||||
from.SendLocalizedMessage( 1063024 ); // You cannot perform this special move right now.
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Dueling
|
||||
string option = null;
|
||||
|
||||
if ( this is Backstab )
|
||||
option = "Backstab";
|
||||
else if ( this is DeathStrike )
|
||||
option = "Death Strike";
|
||||
else if ( this is FocusAttack )
|
||||
option = "Focus Attack";
|
||||
else if ( this is KiAttack )
|
||||
option = "Ki Attack";
|
||||
else if ( this is SurpriseAttack )
|
||||
option = "Surprise Attack";
|
||||
else if ( this is HonorableExecution )
|
||||
option = "Honorable Execution";
|
||||
else if ( this is LightningStrike )
|
||||
option = "Lightning Strike";
|
||||
else if ( this is MomentumStrike )
|
||||
option = "Momentum Strike";
|
||||
|
||||
if ( option != null && !Engines.ConPVP.DuelContext.AllowSpecialMove( from, option, this ) )
|
||||
return false;
|
||||
#endregion
|
||||
|
||||
return CheckSkills( from ) && CheckMana( from, false );
|
||||
}
|
||||
|
||||
public virtual void CheckGain( Mobile m )
|
||||
{
|
||||
m.CheckSkill( MoveSkill, RequiredSkill, RequiredSkill + 37.5 );
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, SpecialMove> m_Table = new Dictionary<Mobile, SpecialMove>();
|
||||
|
||||
public static Dictionary<Mobile, SpecialMove> Table{ get{ return m_Table; } }
|
||||
|
||||
public static void ClearAllMoves( Mobile m )
|
||||
{
|
||||
foreach ( KeyValuePair<Int32, SpecialMove> kvp in SpellRegistry.SpecialMoves )
|
||||
{
|
||||
int moveID = kvp.Key;
|
||||
|
||||
if ( moveID != -1 )
|
||||
m.Send( new ToggleSpecialAbility( moveID + 1, false ) );
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool ValidatesDuringHit{ get { return true; } }
|
||||
|
||||
public static SpecialMove GetCurrentMove( Mobile m )
|
||||
{
|
||||
if ( m == null )
|
||||
return null;
|
||||
|
||||
if ( !Core.SE )
|
||||
{
|
||||
ClearCurrentMove( m );
|
||||
return null;
|
||||
}
|
||||
|
||||
SpecialMove move = null;
|
||||
m_Table.TryGetValue( m, out move );
|
||||
|
||||
if ( move != null && move.ValidatesDuringHit && !move.Validate( m ) )
|
||||
{
|
||||
ClearCurrentMove( m );
|
||||
return null;
|
||||
}
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
public static bool SetCurrentMove( Mobile m, SpecialMove move )
|
||||
{
|
||||
if ( !Core.SE )
|
||||
{
|
||||
ClearCurrentMove( m );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( move != null && !move.Validate( m ) )
|
||||
{
|
||||
ClearCurrentMove( m );
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sameMove = ( move == GetCurrentMove( m ) );
|
||||
|
||||
ClearCurrentMove( m );
|
||||
|
||||
if ( sameMove )
|
||||
return true;
|
||||
|
||||
if ( move != null )
|
||||
{
|
||||
WeaponAbility.ClearCurrentAbility( m );
|
||||
|
||||
m_Table[m] = move;
|
||||
|
||||
move.OnUse( m );
|
||||
|
||||
int moveID = SpellRegistry.GetRegistryNumber( move );
|
||||
|
||||
if ( moveID > 0 )
|
||||
m.Send( new ToggleSpecialAbility( moveID + 1, true ) );
|
||||
|
||||
TextDefinition.SendMessageTo( m, move.AbilityMessage );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void ClearCurrentMove( Mobile m )
|
||||
{
|
||||
SpecialMove move = null;
|
||||
m_Table.TryGetValue( m, out move );
|
||||
|
||||
if ( move != null )
|
||||
{
|
||||
move.OnClearMove( m );
|
||||
|
||||
int moveID = SpellRegistry.GetRegistryNumber( move );
|
||||
|
||||
if ( moveID > 0 )
|
||||
m.Send( new ToggleSpecialAbility( moveID + 1, false ) );
|
||||
}
|
||||
|
||||
m_Table.Remove( m );
|
||||
}
|
||||
|
||||
public SpecialMove()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
private static Dictionary<Mobile, SpecialMoveContext> m_PlayersTable = new Dictionary<Mobile, SpecialMoveContext>();
|
||||
|
||||
private static void AddContext( Mobile m, SpecialMoveContext context )
|
||||
{
|
||||
m_PlayersTable[m] = context;
|
||||
}
|
||||
|
||||
private static void RemoveContext( Mobile m )
|
||||
{
|
||||
SpecialMoveContext context = GetContext( m );
|
||||
|
||||
if ( context != null )
|
||||
{
|
||||
m_PlayersTable.Remove( m );
|
||||
|
||||
context.Timer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static SpecialMoveContext GetContext( Mobile m )
|
||||
{
|
||||
return ( m_PlayersTable.ContainsKey( m ) ? m_PlayersTable[m] : null );
|
||||
}
|
||||
|
||||
public static bool GetContext( Mobile m, Type type )
|
||||
{
|
||||
SpecialMoveContext context = null;
|
||||
m_PlayersTable.TryGetValue( m, out context );
|
||||
|
||||
if ( context == null )
|
||||
return false;
|
||||
|
||||
return ( context.Type == type );
|
||||
}
|
||||
|
||||
private class SpecialMoveTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public SpecialMoveTimer( Mobile from ) : base ( TimeSpan.FromSeconds( 3.0 ) )
|
||||
{
|
||||
m_Mobile = from;
|
||||
|
||||
Priority = TimerPriority.TwentyFiveMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
RemoveContext( m_Mobile );
|
||||
}
|
||||
}
|
||||
|
||||
public class SpecialMoveContext
|
||||
{
|
||||
private Timer m_Timer;
|
||||
private Type m_Type;
|
||||
|
||||
public Timer Timer{ get{ return m_Timer; } }
|
||||
public Type Type{ get{ return m_Type; } }
|
||||
|
||||
public SpecialMoveContext( Timer timer, Type type )
|
||||
{
|
||||
m_Timer = timer;
|
||||
m_Type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
934
Scripts/Spells/Base/Spell.cs
Normal file
934
Scripts/Spells/Base/Spell.cs
Normal file
|
|
@ -0,0 +1,934 @@
|
|||
using System;
|
||||
using Server.Items;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells.Second;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Spells.Ninjitsu;
|
||||
using System.Collections.Generic;
|
||||
using Server.Spells.Spellweaving;
|
||||
using Server.Spells.Bushido;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public abstract class Spell : ISpell
|
||||
{
|
||||
private Mobile m_Caster;
|
||||
private Item m_Scroll;
|
||||
private SpellInfo m_Info;
|
||||
private SpellState m_State;
|
||||
private long m_StartCastTime;
|
||||
|
||||
public SpellState State{ get{ return m_State; } set{ m_State = value; } }
|
||||
public Mobile Caster{ get{ return m_Caster; } }
|
||||
public SpellInfo Info{ get{ return m_Info; } }
|
||||
public string Name{ get{ return m_Info.Name; } }
|
||||
public string Mantra{ get{ return m_Info.Mantra; } }
|
||||
public Type[] Reagents{ get{ return m_Info.Reagents; } }
|
||||
public Item Scroll{ get{ return m_Scroll; } }
|
||||
public long StartCastTime { get { return m_StartCastTime; } }
|
||||
|
||||
private static TimeSpan NextSpellDelay = TimeSpan.FromSeconds( 0.75 );
|
||||
private static TimeSpan AnimateDelay = TimeSpan.FromSeconds( 1.5 );
|
||||
|
||||
public virtual SkillName CastSkill{ get{ return SkillName.Magery; } }
|
||||
public virtual SkillName DamageSkill{ get{ return SkillName.EvalInt; } }
|
||||
|
||||
public virtual bool RevealOnCast{ get{ return true; } }
|
||||
public virtual bool ClearHandsOnCast{ get{ return true; } }
|
||||
public virtual bool ShowHandMovement{ get{ return true; } }
|
||||
|
||||
public virtual bool DelayedDamage{ get{ return false; } }
|
||||
|
||||
public virtual bool DelayedDamageStacking { get { return true; } }
|
||||
//In reality, it's ANY delayed Damage spell Post-AoS that can't stack, but, only
|
||||
//Expo & Magic Arrow have enough delay and a short enough cast time to bring up
|
||||
//the possibility of stacking 'em. Note that a MA & an Explosion will stack, but
|
||||
//of course, two MA's won't.
|
||||
|
||||
private static Dictionary<Type, DelayedDamageContextWrapper> m_ContextTable = new Dictionary<Type, DelayedDamageContextWrapper>();
|
||||
|
||||
private class DelayedDamageContextWrapper
|
||||
{
|
||||
private Dictionary<Mobile, Timer> m_Contexts = new Dictionary<Mobile, Timer>();
|
||||
|
||||
public void Add( Mobile m, Timer t )
|
||||
{
|
||||
Timer oldTimer;
|
||||
if( m_Contexts.TryGetValue( m, out oldTimer ) )
|
||||
{
|
||||
oldTimer.Stop();
|
||||
m_Contexts.Remove( m );
|
||||
}
|
||||
|
||||
m_Contexts.Add( m, t );
|
||||
}
|
||||
|
||||
public void Remove( Mobile m )
|
||||
{
|
||||
m_Contexts.Remove( m );
|
||||
}
|
||||
}
|
||||
|
||||
public void StartDelayedDamageContext( Mobile m, Timer t )
|
||||
{
|
||||
if( DelayedDamageStacking )
|
||||
return; //Sanity
|
||||
|
||||
DelayedDamageContextWrapper contexts;
|
||||
|
||||
if( !m_ContextTable.TryGetValue( GetType(), out contexts ) )
|
||||
{
|
||||
contexts = new DelayedDamageContextWrapper();
|
||||
m_ContextTable.Add( GetType(), contexts );
|
||||
}
|
||||
|
||||
contexts.Add( m, t );
|
||||
}
|
||||
|
||||
public void RemoveDelayedDamageContext( Mobile m )
|
||||
{
|
||||
DelayedDamageContextWrapper contexts;
|
||||
|
||||
if( !m_ContextTable.TryGetValue( GetType(), out contexts ) )
|
||||
return;
|
||||
|
||||
contexts.Remove( m );
|
||||
}
|
||||
|
||||
public void HarmfulSpell( Mobile m )
|
||||
{
|
||||
if ( m is BaseCreature )
|
||||
((BaseCreature)m).OnHarmfulSpell( m_Caster );
|
||||
}
|
||||
|
||||
public Spell( Mobile caster, Item scroll, SpellInfo info )
|
||||
{
|
||||
m_Caster = caster;
|
||||
m_Scroll = scroll;
|
||||
m_Info = info;
|
||||
}
|
||||
|
||||
public virtual int GetNewAosDamage( int bonus, int dice, int sides, Mobile singleTarget )
|
||||
{
|
||||
if( singleTarget != null )
|
||||
{
|
||||
return GetNewAosDamage( bonus, dice, sides, (Caster.Player && singleTarget.Player), GetDamageScalar( singleTarget ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetNewAosDamage( bonus, dice, sides, false );
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int GetNewAosDamage( int bonus, int dice, int sides, bool playerVsPlayer )
|
||||
{
|
||||
return GetNewAosDamage( bonus, dice, sides, playerVsPlayer, 1.0 );
|
||||
}
|
||||
|
||||
public virtual int GetNewAosDamage( int bonus, int dice, int sides, bool playerVsPlayer, double scalar )
|
||||
{
|
||||
int damage = Utility.Dice( dice, sides, bonus ) * 100;
|
||||
int damageBonus = 0;
|
||||
|
||||
int inscribeSkill = GetInscribeFixed( m_Caster );
|
||||
int inscribeBonus = (inscribeSkill + (1000 * (inscribeSkill / 1000))) / 200;
|
||||
damageBonus += inscribeBonus;
|
||||
|
||||
int intBonus = Caster.Int / 10;
|
||||
damageBonus += intBonus;
|
||||
|
||||
int sdiBonus = AosAttributes.GetValue( m_Caster, AosAttribute.SpellDamage );
|
||||
// PvP spell damage increase cap of 15% from an item’s magic property
|
||||
if ( playerVsPlayer && sdiBonus > 15 )
|
||||
sdiBonus = 15;
|
||||
|
||||
damageBonus += sdiBonus;
|
||||
|
||||
TransformContext context = TransformationSpellHelper.GetContext( Caster );
|
||||
|
||||
if( context != null && context.Spell is ReaperFormSpell )
|
||||
damageBonus += ((ReaperFormSpell)context.Spell).SpellDamageBonus;
|
||||
|
||||
damage = AOS.Scale( damage, 100 + damageBonus );
|
||||
|
||||
int evalSkill = GetDamageFixed( m_Caster );
|
||||
int evalScale = 30 + ((9 * evalSkill) / 100);
|
||||
|
||||
damage = AOS.Scale( damage, evalScale );
|
||||
|
||||
damage = AOS.Scale( damage, (int)(scalar*100) );
|
||||
|
||||
return damage / 100;
|
||||
}
|
||||
|
||||
public virtual bool IsCasting{ get{ return m_State == SpellState.Casting; } }
|
||||
|
||||
public virtual void OnCasterHurt()
|
||||
{
|
||||
//Confirm: Monsters and pets cannot be disturbed.
|
||||
if ( !Caster.Player )
|
||||
return;
|
||||
|
||||
if ( IsCasting )
|
||||
{
|
||||
object o = ProtectionSpell.Registry[m_Caster];
|
||||
bool disturb = true;
|
||||
|
||||
if ( o != null && o is double )
|
||||
{
|
||||
if ( ((double)o) > Utility.RandomDouble()*100.0 )
|
||||
disturb = false;
|
||||
}
|
||||
|
||||
if ( disturb )
|
||||
Disturb( DisturbType.Hurt, false, true );
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnCasterKilled()
|
||||
{
|
||||
Disturb( DisturbType.Kill );
|
||||
}
|
||||
|
||||
public virtual void OnConnectionChanged()
|
||||
{
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public virtual bool OnCasterMoving( Direction d )
|
||||
{
|
||||
if ( IsCasting && BlocksMovement )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 500111 ); // You are frozen and can not move.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool OnCasterEquiping( Item item )
|
||||
{
|
||||
if ( IsCasting )
|
||||
Disturb( DisturbType.EquipRequest );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool OnCasterUsingObject( object o )
|
||||
{
|
||||
if ( m_State == SpellState.Sequencing )
|
||||
Disturb( DisturbType.UseRequest );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool OnCastInTown( Region r )
|
||||
{
|
||||
return m_Info.AllowTown;
|
||||
}
|
||||
|
||||
public virtual bool ConsumeReagents()
|
||||
{
|
||||
if ( m_Scroll != null || !m_Caster.Player )
|
||||
return true;
|
||||
|
||||
if ( AosAttributes.GetValue( m_Caster, AosAttribute.LowerRegCost ) > Utility.Random( 100 ) )
|
||||
return true;
|
||||
|
||||
if ( Engines.ConPVP.DuelContext.IsFreeConsume( m_Caster ) )
|
||||
return true;
|
||||
|
||||
Container pack = m_Caster.Backpack;
|
||||
|
||||
if ( pack == null )
|
||||
return false;
|
||||
|
||||
if ( pack.ConsumeTotal( m_Info.Reagents, m_Info.Amounts ) == -1 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual double GetInscribeSkill( Mobile m )
|
||||
{
|
||||
// There is no chance to gain
|
||||
// m.CheckSkill( SkillName.Inscribe, 0.0, 120.0 );
|
||||
|
||||
return m.Skills[SkillName.Inscribe].Value;
|
||||
}
|
||||
|
||||
public virtual int GetInscribeFixed( Mobile m )
|
||||
{
|
||||
// There is no chance to gain
|
||||
// m.CheckSkill( SkillName.Inscribe, 0.0, 120.0 );
|
||||
|
||||
return m.Skills[SkillName.Inscribe].Fixed;
|
||||
}
|
||||
|
||||
public virtual int GetDamageFixed( Mobile m )
|
||||
{
|
||||
//m.CheckSkill( DamageSkill, 0.0, m.Skills[DamageSkill].Cap );
|
||||
|
||||
return m.Skills[DamageSkill].Fixed;
|
||||
}
|
||||
|
||||
public virtual double GetDamageSkill( Mobile m )
|
||||
{
|
||||
//m.CheckSkill( DamageSkill, 0.0, m.Skills[DamageSkill].Cap );
|
||||
|
||||
return m.Skills[DamageSkill].Value;
|
||||
}
|
||||
|
||||
public virtual double GetResistSkill( Mobile m )
|
||||
{
|
||||
return m.Skills[SkillName.MagicResist].Value;
|
||||
}
|
||||
|
||||
public virtual double GetDamageScalar( Mobile target )
|
||||
{
|
||||
double scalar = 1.0;
|
||||
|
||||
if( !Core.AOS ) //EvalInt stuff for AoS is handled elsewhere
|
||||
{
|
||||
double casterEI = m_Caster.Skills[DamageSkill].Value;
|
||||
double targetRS = target.Skills[SkillName.MagicResist].Value;
|
||||
|
||||
/*
|
||||
if( Core.AOS )
|
||||
targetRS = 0;
|
||||
*/
|
||||
|
||||
//m_Caster.CheckSkill( DamageSkill, 0.0, 120.0 );
|
||||
|
||||
if( casterEI > targetRS )
|
||||
scalar = (1.0 + ((casterEI - targetRS) / 500.0));
|
||||
else
|
||||
scalar = (1.0 + ((casterEI - targetRS) / 200.0));
|
||||
|
||||
// magery damage bonus, -25% at 0 skill, +0% at 100 skill, +5% at 120 skill
|
||||
scalar += (m_Caster.Skills[CastSkill].Value - 100.0) / 400.0;
|
||||
|
||||
if( !target.Player && !target.Body.IsHuman /*&& !Core.AOS*/ )
|
||||
scalar *= 2.0; // Double magery damage to monsters/animals if not AOS
|
||||
}
|
||||
|
||||
if ( target is BaseCreature )
|
||||
((BaseCreature)target).AlterDamageScalarFrom( m_Caster, ref scalar );
|
||||
|
||||
if ( m_Caster is BaseCreature )
|
||||
((BaseCreature)m_Caster).AlterDamageScalarTo( target, ref scalar );
|
||||
|
||||
if( Core.SE )
|
||||
scalar *= GetSlayerDamageScalar( target );
|
||||
|
||||
target.Region.SpellDamageScalar( m_Caster, target, ref scalar );
|
||||
|
||||
if( Evasion.CheckSpellEvasion( target ) ) //Only single target spells an be evaded
|
||||
scalar = 0;
|
||||
|
||||
return scalar;
|
||||
}
|
||||
|
||||
public virtual double GetSlayerDamageScalar( Mobile defender )
|
||||
{
|
||||
Spellbook atkBook = Spellbook.FindEquippedSpellbook( m_Caster );
|
||||
|
||||
double scalar = 1.0;
|
||||
if( atkBook != null )
|
||||
{
|
||||
SlayerEntry atkSlayer = SlayerGroup.GetEntryByName( atkBook.Slayer );
|
||||
SlayerEntry atkSlayer2 = SlayerGroup.GetEntryByName( atkBook.Slayer2 );
|
||||
|
||||
if( atkSlayer != null && atkSlayer.Slays( defender ) || atkSlayer2 != null && atkSlayer2.Slays( defender ) )
|
||||
{
|
||||
defender.FixedEffect( 0x37B9, 10, 5 ); //TODO: Confirm this displays on OSIs
|
||||
scalar = 2.0;
|
||||
}
|
||||
|
||||
|
||||
TransformContext context = TransformationSpellHelper.GetContext( defender );
|
||||
|
||||
if( (atkBook.Slayer == SlayerName.Silver || atkBook.Slayer2 == SlayerName.Silver) && context != null && context.Type != typeof( HorrificBeastSpell ) )
|
||||
scalar +=.25; // Every necromancer transformation other than horrific beast take an additional 25% damage
|
||||
|
||||
if( scalar != 1.0 )
|
||||
return scalar;
|
||||
}
|
||||
|
||||
ISlayer defISlayer = Spellbook.FindEquippedSpellbook( defender );
|
||||
|
||||
if( defISlayer == null )
|
||||
defISlayer = defender.Weapon as ISlayer;
|
||||
|
||||
if( defISlayer != null )
|
||||
{
|
||||
SlayerEntry defSlayer = SlayerGroup.GetEntryByName( defISlayer.Slayer );
|
||||
SlayerEntry defSlayer2 = SlayerGroup.GetEntryByName( defISlayer.Slayer2 );
|
||||
|
||||
if( defSlayer != null && defSlayer.Group.OppositionSuperSlays( m_Caster ) || defSlayer2 != null && defSlayer2.Group.OppositionSuperSlays( m_Caster ) )
|
||||
scalar = 2.0;
|
||||
}
|
||||
|
||||
return scalar;
|
||||
}
|
||||
|
||||
public virtual void DoFizzle()
|
||||
{
|
||||
m_Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502632 ); // The spell fizzles.
|
||||
|
||||
if ( m_Caster.Player )
|
||||
{
|
||||
if ( Core.AOS )
|
||||
m_Caster.FixedParticles( 0x3735, 1, 30, 9503, EffectLayer.Waist );
|
||||
else
|
||||
m_Caster.FixedEffect( 0x3735, 6, 30 );
|
||||
|
||||
m_Caster.PlaySound( 0x5C );
|
||||
}
|
||||
}
|
||||
|
||||
private CastTimer m_CastTimer;
|
||||
private AnimTimer m_AnimTimer;
|
||||
|
||||
public void Disturb( DisturbType type )
|
||||
{
|
||||
Disturb( type, true, false );
|
||||
}
|
||||
|
||||
public virtual bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable )
|
||||
{
|
||||
if ( resistable && m_Scroll is BaseWand )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Disturb( DisturbType type, bool firstCircle, bool resistable )
|
||||
{
|
||||
if ( !CheckDisturb( type, firstCircle, resistable ) )
|
||||
return;
|
||||
|
||||
if ( m_State == SpellState.Casting )
|
||||
{
|
||||
if( !firstCircle && !Core.AOS && this is MagerySpell && ((MagerySpell)this).Circle == SpellCircle.First )
|
||||
return;
|
||||
|
||||
m_State = SpellState.None;
|
||||
m_Caster.Spell = null;
|
||||
|
||||
OnDisturb( type, true );
|
||||
|
||||
if ( m_CastTimer != null )
|
||||
m_CastTimer.Stop();
|
||||
|
||||
if ( m_AnimTimer != null )
|
||||
m_AnimTimer.Stop();
|
||||
|
||||
if ( Core.AOS && m_Caster.Player && type == DisturbType.Hurt )
|
||||
DoHurtFizzle();
|
||||
|
||||
m_Caster.NextSpellTime = Core.TickCount + (int)GetDisturbRecovery().TotalMilliseconds;
|
||||
}
|
||||
else if ( m_State == SpellState.Sequencing )
|
||||
{
|
||||
if( !firstCircle && !Core.AOS && this is MagerySpell && ((MagerySpell)this).Circle == SpellCircle.First )
|
||||
return;
|
||||
|
||||
m_State = SpellState.None;
|
||||
m_Caster.Spell = null;
|
||||
|
||||
OnDisturb( type, false );
|
||||
|
||||
Targeting.Target.Cancel( m_Caster );
|
||||
|
||||
if ( Core.AOS && m_Caster.Player && type == DisturbType.Hurt )
|
||||
DoHurtFizzle();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void DoHurtFizzle()
|
||||
{
|
||||
m_Caster.FixedEffect( 0x3735, 6, 30 );
|
||||
m_Caster.PlaySound( 0x5C );
|
||||
}
|
||||
|
||||
public virtual void OnDisturb( DisturbType type, bool message )
|
||||
{
|
||||
if ( message )
|
||||
m_Caster.SendLocalizedMessage( 500641 ); // Your concentration is disturbed, thus ruining thy spell.
|
||||
}
|
||||
|
||||
public virtual bool CheckCast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void SayMantra()
|
||||
{
|
||||
if ( m_Scroll is BaseWand )
|
||||
return;
|
||||
|
||||
if ( m_Info.Mantra != null && m_Info.Mantra.Length > 0 && m_Caster.Player )
|
||||
m_Caster.PublicOverheadMessage( MessageType.Spell, m_Caster.SpeechHue, true, m_Info.Mantra, false );
|
||||
}
|
||||
|
||||
public virtual bool BlockedByHorrificBeast{ get{ return true; } }
|
||||
public virtual bool BlockedByAnimalForm{ get{ return true; } }
|
||||
public virtual bool BlocksMovement{ get{ return true; } }
|
||||
|
||||
public virtual bool CheckNextSpellTime{ get{ return !(m_Scroll is BaseWand); } }
|
||||
|
||||
public bool Cast()
|
||||
{
|
||||
m_StartCastTime = Core.TickCount;
|
||||
|
||||
if ( Core.AOS && m_Caster.Spell is Spell && ((Spell)m_Caster.Spell).State == SpellState.Sequencing )
|
||||
((Spell)m_Caster.Spell).Disturb( DisturbType.NewCast );
|
||||
|
||||
if ( !m_Caster.CheckAlive() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ( m_Scroll is BaseWand && m_Caster.Spell != null && m_Caster.Spell.IsCasting )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 502643 ); // You can not cast a spell while frozen.
|
||||
}
|
||||
else if ( m_Caster.Spell != null && m_Caster.Spell.IsCasting )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 502642 ); // You are already casting a spell.
|
||||
}
|
||||
else if ( BlockedByHorrificBeast && TransformationSpellHelper.UnderTransformation( m_Caster, typeof( HorrificBeastSpell ) ) || ( BlockedByAnimalForm && AnimalForm.UnderTransformation( m_Caster ) ))
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
|
||||
}
|
||||
else if ( !(m_Scroll is BaseWand) && (m_Caster.Paralyzed || m_Caster.Frozen) )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 502643 ); // You can not cast a spell while frozen.
|
||||
}
|
||||
else if (CheckNextSpellTime && Core.TickCount - m_Caster.NextSpellTime < 0)
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 502644 ); // You have not yet recovered from casting a spell.
|
||||
}
|
||||
else if ( m_Caster is PlayerMobile && ( (PlayerMobile) m_Caster ).PeacedUntil > DateTime.UtcNow )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 1072060 ); // You cannot cast a spell while calmed.
|
||||
}
|
||||
#region Dueling
|
||||
else if ( m_Caster is PlayerMobile && ((PlayerMobile)m_Caster).DuelContext != null && !((PlayerMobile)m_Caster).DuelContext.AllowSpellCast( m_Caster, this ) )
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
else if ( m_Caster.Mana >= ScaleMana( GetMana() ) )
|
||||
{
|
||||
if ( m_Caster.Spell == null && m_Caster.CheckSpellCast( this ) && CheckCast() && m_Caster.Region.OnBeginSpellCast( m_Caster, this ) )
|
||||
{
|
||||
m_State = SpellState.Casting;
|
||||
m_Caster.Spell = this;
|
||||
|
||||
if ( !( m_Scroll is BaseWand ) && RevealOnCast )
|
||||
m_Caster.RevealingAction();
|
||||
|
||||
SayMantra();
|
||||
|
||||
TimeSpan castDelay = this.GetCastDelay();
|
||||
|
||||
if ( ShowHandMovement && ( m_Caster.Body.IsHuman || ( m_Caster.Player && m_Caster.Body.IsMonster ) ) )
|
||||
{
|
||||
int count = (int)Math.Ceiling( castDelay.TotalSeconds / AnimateDelay.TotalSeconds );
|
||||
|
||||
if ( count != 0 )
|
||||
{
|
||||
m_AnimTimer = new AnimTimer( this, count );
|
||||
m_AnimTimer.Start();
|
||||
}
|
||||
|
||||
if ( m_Info.LeftHandEffect > 0 )
|
||||
Caster.FixedParticles( 0, 10, 5, m_Info.LeftHandEffect, EffectLayer.LeftHand );
|
||||
|
||||
if ( m_Info.RightHandEffect > 0 )
|
||||
Caster.FixedParticles( 0, 10, 5, m_Info.RightHandEffect, EffectLayer.RightHand );
|
||||
}
|
||||
|
||||
if ( ClearHandsOnCast )
|
||||
m_Caster.ClearHands();
|
||||
|
||||
if ( Core.ML )
|
||||
WeaponAbility.ClearCurrentAbility( m_Caster );
|
||||
|
||||
m_CastTimer = new CastTimer( this, castDelay );
|
||||
//m_CastTimer.Start();
|
||||
|
||||
OnBeginCast();
|
||||
|
||||
if ( castDelay > TimeSpan.Zero ) {
|
||||
m_CastTimer.Start();
|
||||
} else {
|
||||
m_CastTimer.Tick();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 502625 ); // Insufficient mana
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void OnCast();
|
||||
|
||||
public virtual void OnBeginCast()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
min = max = 0; //Intended but not required for overriding.
|
||||
}
|
||||
|
||||
public virtual bool CheckFizzle()
|
||||
{
|
||||
if ( m_Scroll is BaseWand )
|
||||
return true;
|
||||
|
||||
double minSkill, maxSkill;
|
||||
|
||||
GetCastSkills( out minSkill, out maxSkill );
|
||||
|
||||
if ( DamageSkill != CastSkill )
|
||||
Caster.CheckSkill( DamageSkill, 0.0, Caster.Skills[ DamageSkill ].Cap );
|
||||
|
||||
return Caster.CheckSkill( CastSkill, minSkill, maxSkill );
|
||||
}
|
||||
|
||||
public abstract int GetMana();
|
||||
|
||||
public virtual int ScaleMana( int mana )
|
||||
{
|
||||
double scalar = 1.0;
|
||||
|
||||
if ( !Necromancy.MindRotSpell.GetMindRotScalar( Caster, ref scalar ) )
|
||||
scalar = 1.0;
|
||||
|
||||
// Lower Mana Cost = 40%
|
||||
int lmc = AosAttributes.GetValue( m_Caster, AosAttribute.LowerManaCost );
|
||||
if ( lmc > 40 )
|
||||
lmc = 40;
|
||||
|
||||
scalar -= (double)lmc / 100;
|
||||
|
||||
return (int)(mana * scalar);
|
||||
}
|
||||
|
||||
public virtual TimeSpan GetDisturbRecovery()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
return TimeSpan.Zero;
|
||||
|
||||
double delay = 1.0 - Math.Sqrt((Core.TickCount - m_StartCastTime) / 1000.0 / GetCastDelay().TotalSeconds);
|
||||
|
||||
if ( delay < 0.2 )
|
||||
delay = 0.2;
|
||||
|
||||
return TimeSpan.FromSeconds( delay );
|
||||
}
|
||||
|
||||
public virtual int CastRecoveryBase{ get{ return 6; } }
|
||||
public virtual int CastRecoveryFastScalar{ get{ return 1; } }
|
||||
public virtual int CastRecoveryPerSecond{ get{ return 4; } }
|
||||
public virtual int CastRecoveryMinimum{ get{ return 0; } }
|
||||
|
||||
public virtual TimeSpan GetCastRecovery()
|
||||
{
|
||||
if ( !Core.AOS )
|
||||
return NextSpellDelay;
|
||||
|
||||
int fcr = AosAttributes.GetValue( m_Caster, AosAttribute.CastRecovery );
|
||||
|
||||
fcr -= ThunderstormSpell.GetCastRecoveryMalus( m_Caster );
|
||||
|
||||
int fcrDelay = -(CastRecoveryFastScalar * fcr);
|
||||
|
||||
int delay = CastRecoveryBase + fcrDelay;
|
||||
|
||||
if ( delay < CastRecoveryMinimum )
|
||||
delay = CastRecoveryMinimum;
|
||||
|
||||
return TimeSpan.FromSeconds( (double)delay / CastRecoveryPerSecond );
|
||||
}
|
||||
|
||||
public abstract TimeSpan CastDelayBase { get; }
|
||||
|
||||
public virtual double CastDelayFastScalar { get { return 1; } }
|
||||
public virtual double CastDelaySecondsPerTick { get { return 0.25; } }
|
||||
public virtual TimeSpan CastDelayMinimum { get { return TimeSpan.FromSeconds( 0.25 ); } }
|
||||
|
||||
//public virtual int CastDelayBase{ get{ return 3; } }
|
||||
//public virtual int CastDelayFastScalar{ get{ return 1; } }
|
||||
//public virtual int CastDelayPerSecond{ get{ return 4; } }
|
||||
//public virtual int CastDelayMinimum{ get{ return 1; } }
|
||||
|
||||
public virtual TimeSpan GetCastDelay()
|
||||
{
|
||||
if ( m_Scroll is BaseWand )
|
||||
return Core.ML ? CastDelayBase : TimeSpan.Zero; // TODO: Should FC apply to wands?
|
||||
|
||||
// Faster casting cap of 2 (if not using the protection spell)
|
||||
// Faster casting cap of 0 (if using the protection spell)
|
||||
// Paladin spells are subject to a faster casting cap of 4
|
||||
// Paladins with magery of 70.0 or above are subject to a faster casting cap of 2
|
||||
int fcMax = 4;
|
||||
|
||||
if ( CastSkill == SkillName.Magery || CastSkill == SkillName.Necromancy || ( CastSkill == SkillName.Chivalry && m_Caster.Skills[SkillName.Magery].Value >= 70.0 ) )
|
||||
fcMax = 2;
|
||||
|
||||
int fc = AosAttributes.GetValue( m_Caster, AosAttribute.CastSpeed );
|
||||
|
||||
if ( fc > fcMax )
|
||||
fc = fcMax;
|
||||
|
||||
if ( ProtectionSpell.Registry.Contains( m_Caster ) )
|
||||
fc -= 2;
|
||||
|
||||
if( EssenceOfWindSpell.IsDebuffed( m_Caster ) )
|
||||
fc -= EssenceOfWindSpell.GetFCMalus( m_Caster );
|
||||
|
||||
TimeSpan baseDelay = CastDelayBase;
|
||||
|
||||
TimeSpan fcDelay = TimeSpan.FromSeconds( -(CastDelayFastScalar * fc * CastDelaySecondsPerTick) );
|
||||
|
||||
//int delay = CastDelayBase + circleDelay + fcDelay;
|
||||
TimeSpan delay = baseDelay + fcDelay;
|
||||
|
||||
if ( delay < CastDelayMinimum )
|
||||
delay = CastDelayMinimum;
|
||||
|
||||
//return TimeSpan.FromSeconds( (double)delay / CastDelayPerSecond );
|
||||
return delay;
|
||||
}
|
||||
|
||||
public virtual void FinishSequence()
|
||||
{
|
||||
m_State = SpellState.None;
|
||||
|
||||
if ( m_Caster.Spell == this )
|
||||
m_Caster.Spell = null;
|
||||
}
|
||||
|
||||
public virtual int ComputeKarmaAward()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual bool CheckSequence()
|
||||
{
|
||||
int mana = ScaleMana( GetMana() );
|
||||
|
||||
if ( m_Caster.Deleted || !m_Caster.Alive || m_Caster.Spell != this || m_State != SpellState.Sequencing )
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
else if ( m_Scroll != null && !(m_Scroll is Runebook) && (m_Scroll.Amount <= 0 || m_Scroll.Deleted || m_Scroll.RootParent != m_Caster || (m_Scroll is BaseWand && (((BaseWand)m_Scroll).Charges <= 0 || m_Scroll.Parent != m_Caster))) )
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
else if ( !ConsumeReagents() )
|
||||
{
|
||||
m_Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 502630 ); // More reagents are needed for this spell.
|
||||
}
|
||||
else if ( m_Caster.Mana < mana )
|
||||
{
|
||||
m_Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 502625 ); // Insufficient mana for this spell.
|
||||
}
|
||||
else if ( Core.AOS && (m_Caster.Frozen || m_Caster.Paralyzed) )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 502646 ); // You cannot cast a spell while frozen.
|
||||
DoFizzle();
|
||||
}
|
||||
else if ( m_Caster is PlayerMobile && ((PlayerMobile) m_Caster).PeacedUntil > DateTime.UtcNow )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 1072060 ); // You cannot cast a spell while calmed.
|
||||
DoFizzle();
|
||||
}
|
||||
else if ( CheckFizzle() )
|
||||
{
|
||||
m_Caster.Mana -= mana;
|
||||
|
||||
if ( m_Scroll is SpellScroll )
|
||||
m_Scroll.Consume();
|
||||
else if ( m_Scroll is BaseWand )
|
||||
{
|
||||
((BaseWand)m_Scroll).ConsumeCharge( m_Caster );
|
||||
m_Caster.RevealingAction();
|
||||
}
|
||||
|
||||
if ( m_Scroll is BaseWand )
|
||||
{
|
||||
bool m = m_Scroll.Movable;
|
||||
|
||||
m_Scroll.Movable = false;
|
||||
|
||||
if ( ClearHandsOnCast )
|
||||
m_Caster.ClearHands();
|
||||
|
||||
m_Scroll.Movable = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ClearHandsOnCast )
|
||||
m_Caster.ClearHands();
|
||||
}
|
||||
|
||||
int karma = ComputeKarmaAward();
|
||||
|
||||
if ( karma != 0 )
|
||||
Misc.Titles.AwardKarma( Caster, karma, true );
|
||||
|
||||
if( TransformationSpellHelper.UnderTransformation( m_Caster, typeof( VampiricEmbraceSpell ) ) )
|
||||
{
|
||||
bool garlic = false;
|
||||
|
||||
for ( int i = 0; !garlic && i < m_Info.Reagents.Length; ++i )
|
||||
garlic = ( m_Info.Reagents[i] == Reagent.Garlic );
|
||||
|
||||
if ( garlic )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 1061651 ); // The garlic burns you!
|
||||
AOS.Damage( m_Caster, Utility.RandomMinMax( 17, 23 ), 100, 0, 0, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CheckBSequence( Mobile target )
|
||||
{
|
||||
return CheckBSequence( target, false );
|
||||
}
|
||||
|
||||
public bool CheckBSequence( Mobile target, bool allowDead )
|
||||
{
|
||||
if ( !target.Alive && !allowDead )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 501857 ); // This spell won't work on that!
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.CanBeBeneficial( target, true, allowDead ) && CheckSequence() )
|
||||
{
|
||||
Caster.DoBeneficial( target );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckHSequence( Mobile target )
|
||||
{
|
||||
if ( !target.Alive )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 501857 ); // This spell won't work on that!
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.CanBeHarmful( target ) && CheckSequence() )
|
||||
{
|
||||
Caster.DoHarmful( target );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class AnimTimer : Timer
|
||||
{
|
||||
private Spell m_Spell;
|
||||
|
||||
public AnimTimer( Spell spell, int count ) : base( TimeSpan.Zero, AnimateDelay, count )
|
||||
{
|
||||
m_Spell = spell;
|
||||
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Spell.State != SpellState.Casting || m_Spell.m_Caster.Spell != m_Spell )
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !m_Spell.Caster.Mounted && m_Spell.m_Info.Action >= 0 )
|
||||
{
|
||||
if ( m_Spell.Caster.Body.IsHuman )
|
||||
m_Spell.Caster.Animate( m_Spell.m_Info.Action, 7, 1, true, false, 0 );
|
||||
else if ( m_Spell.Caster.Player && m_Spell.Caster.Body.IsMonster )
|
||||
m_Spell.Caster.Animate( 12, 7, 1, true, false, 0 );
|
||||
}
|
||||
|
||||
if ( !Running )
|
||||
m_Spell.m_AnimTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class CastTimer : Timer
|
||||
{
|
||||
private Spell m_Spell;
|
||||
|
||||
public CastTimer( Spell spell, TimeSpan castDelay ) : base( castDelay )
|
||||
{
|
||||
m_Spell = spell;
|
||||
|
||||
Priority = TimerPriority.TwentyFiveMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Spell == null || m_Spell.m_Caster == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if ( m_Spell.m_State == SpellState.Casting && m_Spell.m_Caster.Spell == m_Spell )
|
||||
{
|
||||
m_Spell.m_State = SpellState.Sequencing;
|
||||
m_Spell.m_CastTimer = null;
|
||||
m_Spell.m_Caster.OnSpellCast( m_Spell );
|
||||
if ( m_Spell.m_Caster.Region != null )
|
||||
m_Spell.m_Caster.Region.OnSpellCast( m_Spell.m_Caster, m_Spell );
|
||||
m_Spell.m_Caster.NextSpellTime = Core.TickCount + (int)m_Spell.GetCastRecovery().TotalMilliseconds; // Spell.NextSpellDelay;
|
||||
|
||||
Target originalTarget = m_Spell.m_Caster.Target;
|
||||
|
||||
m_Spell.OnCast();
|
||||
|
||||
if ( m_Spell.m_Caster.Player && m_Spell.m_Caster.Target != originalTarget && m_Spell.Caster.Target != null )
|
||||
m_Spell.m_Caster.Target.BeginTimeout( m_Spell.m_Caster, TimeSpan.FromSeconds( 30.0 ) );
|
||||
|
||||
m_Spell.m_CastTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
OnTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Scripts/Spells/Base/SpellCircle.cs
Normal file
16
Scripts/Spells/Base/SpellCircle.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public enum SpellCircle
|
||||
{
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
Fifth,
|
||||
Sixth,
|
||||
Seventh,
|
||||
Eighth
|
||||
}
|
||||
}
|
||||
1451
Scripts/Spells/Base/SpellHelper.cs
Normal file
1451
Scripts/Spells/Base/SpellHelper.cs
Normal file
File diff suppressed because it is too large
Load diff
66
Scripts/Spells/Base/SpellInfo.cs
Normal file
66
Scripts/Spells/Base/SpellInfo.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using Server;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class SpellInfo
|
||||
{
|
||||
private string m_Name;
|
||||
private string m_Mantra;
|
||||
private Type[] m_Reagents;
|
||||
private int[] m_Amounts;
|
||||
private int m_Action;
|
||||
private bool m_AllowTown;
|
||||
private int m_LeftHandEffect, m_RightHandEffect;
|
||||
|
||||
public SpellInfo( string name, string mantra, params Type[] regs ) : this( name, mantra, 16, 0, 0, true, regs )
|
||||
{
|
||||
}
|
||||
|
||||
public SpellInfo( string name, string mantra, bool allowTown, params Type[] regs ) : this( name, mantra, 16, 0, 0, allowTown, regs )
|
||||
{
|
||||
}
|
||||
|
||||
public SpellInfo( string name, string mantra, int action, params Type[] regs ) : this( name, mantra, action, 0, 0, true, regs )
|
||||
{
|
||||
}
|
||||
|
||||
public SpellInfo( string name, string mantra, int action, bool allowTown, params Type[] regs ) : this( name, mantra, action, 0, 0, allowTown, regs )
|
||||
{
|
||||
}
|
||||
|
||||
public SpellInfo( string name, string mantra, int action, int handEffect, params Type[] regs ) : this( name, mantra, action, handEffect, handEffect, true, regs )
|
||||
{
|
||||
}
|
||||
|
||||
public SpellInfo( string name, string mantra, int action, int handEffect, bool allowTown, params Type[] regs ) : this( name, mantra, action, handEffect, handEffect, allowTown, regs )
|
||||
{
|
||||
}
|
||||
|
||||
public SpellInfo( string name, string mantra, int action, int leftHandEffect, int rightHandEffect, bool allowTown, params Type[] regs )
|
||||
{
|
||||
m_Name = name;
|
||||
m_Mantra = mantra;
|
||||
m_Action = action;
|
||||
m_Reagents = regs;
|
||||
m_AllowTown = allowTown;
|
||||
|
||||
m_LeftHandEffect = leftHandEffect;
|
||||
m_RightHandEffect = rightHandEffect;
|
||||
|
||||
m_Amounts = new int[regs.Length];
|
||||
|
||||
for ( int i = 0; i < regs.Length; ++i )
|
||||
m_Amounts[i] = 1;
|
||||
}
|
||||
|
||||
public int Action{ get{ return m_Action; } set{ m_Action = value; } }
|
||||
public bool AllowTown{ get{ return m_AllowTown; } set{ m_AllowTown = value; } }
|
||||
public int[] Amounts{ get{ return m_Amounts; } set{ m_Amounts = value; } }
|
||||
public string Mantra{ get{ return m_Mantra; } set{ m_Mantra = value; } }
|
||||
public string Name{ get{ return m_Name; } set{ m_Name = value; } }
|
||||
public Type[] Reagents{ get{ return m_Reagents; } set{ m_Reagents = value; } }
|
||||
public int LeftHandEffect{ get{ return m_LeftHandEffect; } set{ m_LeftHandEffect = value; } }
|
||||
public int RightHandEffect{ get{ return m_RightHandEffect; } set{ m_RightHandEffect = value; } }
|
||||
}
|
||||
}
|
||||
177
Scripts/Spells/Base/SpellRegistry.cs
Normal file
177
Scripts/Spells/Base/SpellRegistry.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Server.Spells.Bushido;
|
||||
using Server.Spells.Chivalry;
|
||||
using Server.Items;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Spells.Ninjitsu;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class SpellRegistry
|
||||
{
|
||||
private static Type[] m_Types = new Type[700];
|
||||
private static int m_Count;
|
||||
|
||||
public static Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
m_Count = -1;
|
||||
return m_Types;
|
||||
}
|
||||
}
|
||||
|
||||
//What IS this used for anyways.
|
||||
public static int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( m_Count == -1 )
|
||||
{
|
||||
m_Count = 0;
|
||||
|
||||
for ( int i = 0; i < m_Types.Length; ++i )
|
||||
if ( m_Types[i] != null )
|
||||
++m_Count;
|
||||
}
|
||||
|
||||
return m_Count;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<Type, Int32> m_IDsFromTypes = new Dictionary<Type, Int32>( m_Types.Length );
|
||||
|
||||
private static Dictionary<Int32, SpecialMove> m_SpecialMoves = new Dictionary<Int32, SpecialMove>();
|
||||
public static Dictionary<Int32, SpecialMove> SpecialMoves { get { return m_SpecialMoves; } }
|
||||
|
||||
public static int GetRegistryNumber( ISpell s )
|
||||
{
|
||||
return GetRegistryNumber( s.GetType() );
|
||||
}
|
||||
|
||||
public static int GetRegistryNumber( SpecialMove s )
|
||||
{
|
||||
return GetRegistryNumber( s.GetType() );
|
||||
}
|
||||
|
||||
public static int GetRegistryNumber( Type type )
|
||||
{
|
||||
if( m_IDsFromTypes.ContainsKey( type ) )
|
||||
return m_IDsFromTypes[type];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void Register( int spellID, Type type )
|
||||
{
|
||||
if ( spellID < 0 || spellID >= m_Types.Length )
|
||||
return;
|
||||
|
||||
if ( m_Types[spellID] == null )
|
||||
++m_Count;
|
||||
|
||||
m_Types[spellID] = type;
|
||||
|
||||
if( !m_IDsFromTypes.ContainsKey( type ) )
|
||||
m_IDsFromTypes.Add( type, spellID );
|
||||
|
||||
if( type.IsSubclassOf( typeof( SpecialMove ) ) )
|
||||
{
|
||||
SpecialMove spm = null;
|
||||
|
||||
try
|
||||
{
|
||||
spm = Activator.CreateInstance( type ) as SpecialMove;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if( spm != null )
|
||||
m_SpecialMoves.Add( spellID, spm );
|
||||
}
|
||||
}
|
||||
|
||||
public static SpecialMove GetSpecialMove( int spellID )
|
||||
{
|
||||
if ( spellID < 0 || spellID >= m_Types.Length )
|
||||
return null;
|
||||
|
||||
Type t = m_Types[spellID];
|
||||
|
||||
if ( t == null || !t.IsSubclassOf( typeof( SpecialMove ) ) || !m_SpecialMoves.ContainsKey( spellID ) )
|
||||
return null;
|
||||
|
||||
return m_SpecialMoves[spellID];
|
||||
}
|
||||
|
||||
private static object[] m_Params = new object[2];
|
||||
|
||||
public static Spell NewSpell( int spellID, Mobile caster, Item scroll )
|
||||
{
|
||||
if ( spellID < 0 || spellID >= m_Types.Length )
|
||||
return null;
|
||||
|
||||
Type t = m_Types[spellID];
|
||||
|
||||
if( t != null && !t.IsSubclassOf( typeof( SpecialMove ) ) )
|
||||
{
|
||||
m_Params[0] = caster;
|
||||
m_Params[1] = scroll;
|
||||
|
||||
try
|
||||
{
|
||||
return (Spell)Activator.CreateInstance( t, m_Params );
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string[] m_CircleNames = new string[]
|
||||
{
|
||||
"First",
|
||||
"Second",
|
||||
"Third",
|
||||
"Fourth",
|
||||
"Fifth",
|
||||
"Sixth",
|
||||
"Seventh",
|
||||
"Eighth",
|
||||
"Necromancy",
|
||||
"Chivalry",
|
||||
"Bushido",
|
||||
"Ninjitsu",
|
||||
"Spellweaving"
|
||||
};
|
||||
|
||||
public static Spell NewSpell( string name, Mobile caster, Item scroll )
|
||||
{
|
||||
for ( int i = 0; i < m_CircleNames.Length; ++i )
|
||||
{
|
||||
Type t = ScriptCompiler.FindTypeByFullName( String.Format( "Server.Spells.{0}.{1}", m_CircleNames[i], name ) );
|
||||
|
||||
if ( t != null && !t.IsSubclassOf( typeof( SpecialMove ) ) )
|
||||
{
|
||||
m_Params[0] = caster;
|
||||
m_Params[1] = scroll;
|
||||
|
||||
try
|
||||
{
|
||||
return (Spell)Activator.CreateInstance( t, m_Params );
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Scripts/Spells/Base/SpellState.cs
Normal file
11
Scripts/Spells/Base/SpellState.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public enum SpellState
|
||||
{
|
||||
None = 0,
|
||||
Casting = 1, // We are in the process of casting (that is, waiting GetCastTime() and doing animations). Spell casting may be interupted in this state.
|
||||
Sequencing = 2 // Casting completed, but the full spell sequence isn't. Usually waiting for a target response. Some actions are restricted in this state (using skills for example).
|
||||
}
|
||||
}
|
||||
159
Scripts/Spells/Bushido/Confidence.cs
Normal file
159
Scripts/Spells/Bushido/Confidence.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public class Confidence : SamuraiSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Confidence", null,
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.25 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 25.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
|
||||
public Confidence( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 );
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063115 ); // You exude confidence.
|
||||
|
||||
Caster.FixedParticles( 0x375A, 1, 17, 0x7DA, 0x960, 0x3, EffectLayer.Waist );
|
||||
Caster.PlaySound( 0x51A );
|
||||
|
||||
OnCastSuccessful( Caster );
|
||||
|
||||
BeginConfidence( Caster );
|
||||
BeginRegenerating( Caster );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool IsConfident( Mobile m )
|
||||
{
|
||||
return m_Table.Contains( m );
|
||||
}
|
||||
|
||||
public static void BeginConfidence( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
t = new InternalTimer( m );
|
||||
|
||||
m_Table[m] = t;
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
public static void EndConfidence( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
m_Table.Remove( m );
|
||||
|
||||
OnEffectEnd( m, typeof( Confidence ) );
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public InternalTimer( Mobile m ) : base( TimeSpan.FromSeconds( 15.0 ) )
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndConfidence( m_Mobile );
|
||||
m_Mobile.SendLocalizedMessage( 1063116 ); // Your confidence wanes.
|
||||
}
|
||||
}
|
||||
|
||||
private static Hashtable m_RegenTable = new Hashtable();
|
||||
|
||||
public static bool IsRegenerating( Mobile m )
|
||||
{
|
||||
return m_RegenTable.Contains( m );
|
||||
}
|
||||
|
||||
public static void BeginRegenerating( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_RegenTable[m];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
t = new RegenTimer( m );
|
||||
|
||||
m_RegenTable[m] = t;
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
public static void StopRegenerating( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_RegenTable[m];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
m_RegenTable.Remove( m );
|
||||
}
|
||||
|
||||
private class RegenTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
private int m_Ticks;
|
||||
private int m_Hits;
|
||||
|
||||
public RegenTimer( Mobile m ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) )
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Hits = 15 + (m.Skills.Bushido.Fixed * m.Skills.Bushido.Fixed / 57600);
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
++m_Ticks;
|
||||
|
||||
if ( m_Ticks >= 5 )
|
||||
{
|
||||
m_Mobile.Hits += (m_Hits - (m_Hits * 4 / 5));
|
||||
StopRegenerating( m_Mobile );
|
||||
}
|
||||
|
||||
m_Mobile.Hits += (m_Hits / 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
Scripts/Spells/Bushido/CounterAttack.cs
Normal file
116
Scripts/Spells/Bushido/CounterAttack.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public class CounterAttack : SamuraiSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"CounterAttack", null,
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.25 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 40.0; } }
|
||||
public override int RequiredMana{ get{ return 5; } }
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseShield != null )
|
||||
return true;
|
||||
|
||||
if ( Caster.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon != null )
|
||||
return true;
|
||||
|
||||
if ( Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon != null )
|
||||
return true;
|
||||
|
||||
Caster.SendLocalizedMessage( 1062944 ); // You must have a weapon or a shield equipped to use this ability!
|
||||
return false;
|
||||
}
|
||||
|
||||
public CounterAttack( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 );
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063118 ); // You prepare to respond immediately to the next blocked blow.
|
||||
|
||||
OnCastSuccessful( Caster );
|
||||
|
||||
StartCountering( Caster );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool IsCountering( Mobile m )
|
||||
{
|
||||
return m_Table.Contains( m );
|
||||
}
|
||||
|
||||
public static void StartCountering( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
t = new InternalTimer( m );
|
||||
|
||||
m_Table[m] = t;
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
public static void StopCountering( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
m_Table.Remove( m );
|
||||
|
||||
OnEffectEnd( m, typeof( CounterAttack ) );
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public InternalTimer( Mobile m ) : base( TimeSpan.FromSeconds( 30.0 ) )
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
StopCountering( m_Mobile );
|
||||
m_Mobile.SendLocalizedMessage( 1063119 ); // You return to your normal stance.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
228
Scripts/Spells/Bushido/Evasion.cs
Normal file
228
Scripts/Spells/Bushido/Evasion.cs
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public class Evasion : SamuraiSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Evasion", null,
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.25 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 60.0; } }
|
||||
public override int RequiredMana { get { return 10; } }
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if( VerifyCast( Caster, true ) )
|
||||
return base.CheckCast();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool VerifyCast( Mobile Caster, bool messages )
|
||||
{
|
||||
if( Caster == null ) // Sanity
|
||||
return false;
|
||||
|
||||
BaseWeapon weap = Caster.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon;
|
||||
|
||||
if( weap == null )
|
||||
weap = Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon;
|
||||
|
||||
if ( weap != null ) {
|
||||
if ( Core.ML && Caster.Skills[weap.Skill].Base < 50 ) {
|
||||
if ( messages ) {
|
||||
Caster.SendLocalizedMessage( 1076206 ); // Your skill with your equipped weapon must be 50 or higher to use Evasion.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if ( !( Caster.FindItemOnLayer( Layer.TwoHanded ) is BaseShield ) ) {
|
||||
if ( messages ) {
|
||||
Caster.SendLocalizedMessage( 1062944 ); // You must have a weapon or a shield equipped to use this ability!
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !Caster.CanBeginAction( typeof( Evasion ) ) ) {
|
||||
if ( messages ) {
|
||||
Caster.SendLocalizedMessage( 501789 ); // You must wait before trying again.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CheckSpellEvasion( Mobile defender )
|
||||
{
|
||||
BaseWeapon weap = defender.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon;
|
||||
|
||||
if ( weap == null )
|
||||
weap = defender.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon;
|
||||
|
||||
if ( Core.ML ) {
|
||||
if ( defender.Spell != null && defender.Spell.IsCasting ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( weap != null ) {
|
||||
if ( defender.Skills[weap.Skill].Base < 50 ) {
|
||||
return false;
|
||||
}
|
||||
} else if ( !( defender.FindItemOnLayer( Layer.TwoHanded ) is BaseShield ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsEvading( defender ) && BaseWeapon.CheckParry( defender ) ) {
|
||||
defender.Emote( "*evades*" ); // Yes. Eew. Blame OSI.
|
||||
defender.FixedEffect( 0x37B9, 10, 16 );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Evasion( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 );
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if( CheckSequence() )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063120 ); // You feel that you might be able to deflect any attack!
|
||||
Caster.FixedParticles( 0x376A, 1, 20, 0x7F5, 0x960, 3, EffectLayer.Waist );
|
||||
Caster.PlaySound( 0x51B );
|
||||
|
||||
OnCastSuccessful( Caster );
|
||||
|
||||
BeginEvasion( Caster );
|
||||
|
||||
Caster.BeginAction( typeof( Evasion ) );
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 20.0 ), delegate { Caster.EndAction( typeof( Evasion ) ); } );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool IsEvading( Mobile m )
|
||||
{
|
||||
return m_Table.Contains( m );
|
||||
}
|
||||
|
||||
public static TimeSpan GetEvadeDuration( Mobile m )
|
||||
{
|
||||
|
||||
/* Evasion duration now scales with Bushido skill
|
||||
*
|
||||
* If the player has higher than GM Bushido, and GM Tactics and Anatomy, they get a 1 second bonus
|
||||
* Evasion duration range:
|
||||
* o 3-6 seconds w/o tactics/anatomy
|
||||
* o 6-7 seconds w/ GM+ Bushido and GM tactics/anatomy
|
||||
*/
|
||||
|
||||
if( !Core.ML )
|
||||
return TimeSpan.FromSeconds( 8.0 );
|
||||
|
||||
double seconds = 3;
|
||||
|
||||
if( m.Skills.Bushido.Value > 60 )
|
||||
seconds += (m.Skills.Bushido.Value - 60) / 20;
|
||||
|
||||
if( m.Skills.Anatomy.Value >= 100.0 && m.Skills.Tactics.Value >= 100.0 && m.Skills.Bushido.Value > 100.0 ) //Bushido being HIGHER than 100 for bonus is intended
|
||||
seconds++;
|
||||
|
||||
return TimeSpan.FromSeconds( (int)seconds );
|
||||
}
|
||||
|
||||
public static double GetParryScalar( Mobile m )
|
||||
{
|
||||
/* Evasion modifier to parry now scales with Bushido skill
|
||||
*
|
||||
* If the player has higher than GM Bushido, and at least GM Tactics and Anatomy, they get a bonus to their evasion modifier (10% bonus to the evasion modifier to parry NOT 10% to the final parry chance)
|
||||
*
|
||||
* Bonus modifier to parry range: (these are the ranges for the evasion modifier)
|
||||
* o 16-40% bonus w/o tactics/anatomy
|
||||
* o 42-50% bonus w/ GM+ bushido and GM tactics/anatomy
|
||||
*/
|
||||
|
||||
if( !Core.ML )
|
||||
return 1.5;
|
||||
|
||||
double bonus = 0;
|
||||
|
||||
if( m.Skills.Bushido.Value >= 60 )
|
||||
bonus += ( ( ( m.Skills.Bushido.Value - 60 ) * .004 ) + 0.16 );
|
||||
|
||||
if( m.Skills.Anatomy.Value >= 100 && m.Skills.Tactics.Value >= 100 && m.Skills.Bushido.Value > 100 ) //Bushido being HIGHER than 100 for bonus is intended
|
||||
bonus += 0.10;
|
||||
|
||||
return 1.0 + bonus;
|
||||
}
|
||||
|
||||
public static void BeginEvasion( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if( t != null )
|
||||
t.Stop();
|
||||
|
||||
t = new InternalTimer( m, GetEvadeDuration( m ) );
|
||||
|
||||
m_Table[m] = t;
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
public static void EndEvasion( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if( t != null )
|
||||
t.Stop();
|
||||
|
||||
m_Table.Remove( m );
|
||||
|
||||
OnEffectEnd( m, typeof( Evasion ) );
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public InternalTimer( Mobile m, TimeSpan delay )
|
||||
: base( delay )
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
EndEvasion( m_Mobile );
|
||||
m_Mobile.SendLocalizedMessage( 1063121 ); // You no longer feel that you could deflect any attack.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
189
Scripts/Spells/Bushido/HonorableExecution.cs
Normal file
189
Scripts/Spells/Bushido/HonorableExecution.cs
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public class HonorableExecution : SamuraiMove
|
||||
{
|
||||
public HonorableExecution()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 0; } }
|
||||
public override double RequiredSkill{ get{ return 25.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063122 ); } } // You better kill your enemy with your next hit or you'll be rather sorry...
|
||||
|
||||
public override double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
double bushido = attacker.Skills[SkillName.Bushido].Value;
|
||||
|
||||
// TODO: 20 -> Perfection
|
||||
return 1.0 + (bushido * 20) / 10000;
|
||||
}
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
if ( !Validate( attacker ) || !CheckMana( attacker, true ) )
|
||||
return;
|
||||
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
HonorableExecutionInfo info = m_Table[attacker] as HonorableExecutionInfo;
|
||||
|
||||
if ( info != null )
|
||||
{
|
||||
info.Clear();
|
||||
|
||||
if ( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
}
|
||||
|
||||
if ( !defender.Alive )
|
||||
{
|
||||
attacker.FixedParticles( 0x373A, 1, 17, 0x7E2, EffectLayer.Waist );
|
||||
|
||||
double bushido = attacker.Skills[SkillName.Bushido].Value;
|
||||
|
||||
attacker.Hits += 20 + (int)((bushido * bushido) / 480.0);
|
||||
|
||||
int swingBonus = Math.Max( 1, (int)((bushido * bushido) / 720.0) );
|
||||
|
||||
info = new HonorableExecutionInfo( attacker, swingBonus );
|
||||
info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 20.0 ), new TimerStateCallback( EndEffect ), info );
|
||||
|
||||
m_Table[attacker] = info;
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayList mods = new ArrayList();
|
||||
|
||||
mods.Add( new ResistanceMod( ResistanceType.Physical, -40 ) );
|
||||
mods.Add( new ResistanceMod( ResistanceType.Fire, -40 ) );
|
||||
mods.Add( new ResistanceMod( ResistanceType.Cold, -40 ) );
|
||||
mods.Add( new ResistanceMod( ResistanceType.Poison, -40 ) );
|
||||
mods.Add( new ResistanceMod( ResistanceType.Energy, -40 ) );
|
||||
|
||||
double resSpells = attacker.Skills[SkillName.MagicResist].Value;
|
||||
|
||||
if ( resSpells > 0.0 )
|
||||
mods.Add( new DefaultSkillMod( SkillName.MagicResist, true, -resSpells ) );
|
||||
|
||||
info = new HonorableExecutionInfo( attacker, mods );
|
||||
info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 7.0 ), new TimerStateCallback( EndEffect ), info );
|
||||
|
||||
m_Table[attacker] = info;
|
||||
}
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static int GetSwingBonus( Mobile target )
|
||||
{
|
||||
HonorableExecutionInfo info = m_Table[target] as HonorableExecutionInfo;
|
||||
|
||||
if ( info == null )
|
||||
return 0;
|
||||
|
||||
return info.m_SwingBonus;
|
||||
}
|
||||
|
||||
public static bool IsUnderPenalty( Mobile target )
|
||||
{
|
||||
HonorableExecutionInfo info = m_Table[target] as HonorableExecutionInfo;
|
||||
|
||||
if ( info == null )
|
||||
return false;
|
||||
|
||||
return info.m_Penalty;
|
||||
}
|
||||
|
||||
public static void RemovePenalty( Mobile target )
|
||||
{
|
||||
HonorableExecutionInfo info = m_Table[target] as HonorableExecutionInfo;
|
||||
|
||||
if ( info == null || !info.m_Penalty )
|
||||
return;
|
||||
|
||||
info.Clear();
|
||||
|
||||
if ( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
m_Table.Remove( target );
|
||||
}
|
||||
|
||||
private class HonorableExecutionInfo
|
||||
{
|
||||
public Mobile m_Mobile;
|
||||
public int m_SwingBonus;
|
||||
public ArrayList m_Mods;
|
||||
public bool m_Penalty;
|
||||
public Timer m_Timer;
|
||||
|
||||
public HonorableExecutionInfo( Mobile from, int swingBonus ) : this( from, swingBonus, null, false )
|
||||
{
|
||||
}
|
||||
|
||||
public HonorableExecutionInfo( Mobile from, ArrayList mods ) : this( from, 0, mods, true )
|
||||
{
|
||||
}
|
||||
|
||||
public HonorableExecutionInfo( Mobile from, int swingBonus, ArrayList mods, bool penalty )
|
||||
{
|
||||
m_Mobile = from;
|
||||
m_SwingBonus = swingBonus;
|
||||
m_Mods = mods;
|
||||
m_Penalty = penalty;
|
||||
|
||||
Apply();
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if ( m_Mods == null )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < m_Mods.Count; ++i )
|
||||
{
|
||||
object mod = m_Mods[i];
|
||||
|
||||
if ( mod is ResistanceMod )
|
||||
m_Mobile.AddResistanceMod( (ResistanceMod) mod );
|
||||
else if ( mod is SkillMod )
|
||||
m_Mobile.AddSkillMod( (SkillMod) mod );
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if ( m_Mods == null )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < m_Mods.Count; ++i )
|
||||
{
|
||||
object mod = m_Mods[i];
|
||||
|
||||
if ( mod is ResistanceMod )
|
||||
m_Mobile.RemoveResistanceMod( (ResistanceMod) mod );
|
||||
else if ( mod is SkillMod )
|
||||
m_Mobile.RemoveSkillMod( (SkillMod) mod );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EndEffect( object state )
|
||||
{
|
||||
HonorableExecutionInfo info = (HonorableExecutionInfo)state;
|
||||
|
||||
RemovePenalty( info.m_Mobile );
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Scripts/Spells/Bushido/LightningStrike.cs
Normal file
75
Scripts/Spells/Bushido/LightningStrike.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public class LightningStrike : SamuraiMove
|
||||
{
|
||||
public LightningStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 5; } }
|
||||
public override double RequiredSkill{ get{ return 50.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063167 ); } } // You prepare to strike quickly.
|
||||
|
||||
public override bool DelayedContext{ get{ return true; } }
|
||||
|
||||
public override int GetAccuracyBonus( Mobile attacker )
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
|
||||
public override bool Validate(Mobile from)
|
||||
{
|
||||
bool isValid=base.Validate(from);
|
||||
if (isValid)
|
||||
{
|
||||
PlayerMobile ThePlayer = from as PlayerMobile;
|
||||
ThePlayer.ExecutesLightningStrike = BaseMana;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public override bool IgnoreArmor( Mobile attacker )
|
||||
{
|
||||
double bushido = attacker.Skills[SkillName.Bushido].Value;
|
||||
double criticalChance = (bushido * bushido) / 72000.0;
|
||||
return ( criticalChance >= Utility.RandomDouble() );
|
||||
}
|
||||
|
||||
public override bool OnBeforeSwing( Mobile attacker, Mobile defender )
|
||||
{
|
||||
/* no mana drain before actual hit */
|
||||
bool enoughMana = CheckMana(attacker, false);
|
||||
return Validate(attacker);
|
||||
}
|
||||
|
||||
public override bool ValidatesDuringHit { get { return false; } }
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
ClearCurrentMove(attacker);
|
||||
if (CheckMana(attacker, true))
|
||||
{
|
||||
attacker.SendLocalizedMessage(1063168); // You attack with lightning precision!
|
||||
defender.SendLocalizedMessage(1063169); // Your opponent's quick strike causes extra damage!
|
||||
defender.FixedParticles(0x3818, 1, 11, 0x13A8, 0, 0, EffectLayer.Waist);
|
||||
defender.PlaySound(0x51D);
|
||||
CheckGain(attacker);
|
||||
SetContext(attacker);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnClearMove( Mobile attacker )
|
||||
{
|
||||
PlayerMobile ThePlayer = attacker as PlayerMobile; // this can be deletet if the PlayerMobile parts are moved to Server.Mobile
|
||||
ThePlayer.ExecutesLightningStrike = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
Scripts/Spells/Bushido/MomentumStrike.cs
Normal file
77
Scripts/Spells/Bushido/MomentumStrike.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public class MomentumStrike : SamuraiMove
|
||||
{
|
||||
public MomentumStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 10; } }
|
||||
public override double RequiredSkill{ get{ return 70.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1070757 ); } } // You prepare to strike two enemies with one blow.
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
if ( !Validate( attacker ) || !CheckMana( attacker, false ) )
|
||||
return;
|
||||
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
BaseWeapon weapon = attacker.Weapon as BaseWeapon;
|
||||
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
foreach ( Mobile m in attacker.GetMobilesInRange( weapon.MaxRange ) )
|
||||
{
|
||||
if ( m == defender )
|
||||
continue;
|
||||
|
||||
if ( m.Combatant != attacker )
|
||||
continue;
|
||||
|
||||
targets.Add( m );
|
||||
}
|
||||
|
||||
if ( targets.Count > 0 )
|
||||
{
|
||||
if ( !CheckMana( attacker, true ) )
|
||||
return;
|
||||
|
||||
Mobile target = targets[Utility.Random( targets.Count )];
|
||||
|
||||
double damageBonus = attacker.Skills[SkillName.Bushido].Value / 100.0;
|
||||
|
||||
if ( !defender.Alive )
|
||||
damageBonus *= 1.5;
|
||||
|
||||
attacker.SendLocalizedMessage( 1063171 ); // You transfer the momentum of your weapon into another enemy!
|
||||
target.SendLocalizedMessage( 1063172 ); // You were hit by the momentum of a Samurai's weapon!
|
||||
|
||||
target.FixedParticles( 0x37B9, 1, 4, 0x251D, 0, 0, EffectLayer.Waist );
|
||||
|
||||
attacker.PlaySound( 0x510 );
|
||||
|
||||
weapon.OnSwing( attacker, target, damageBonus );
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker.SendLocalizedMessage( 1063123 ); // There are no valid targets to attack!
|
||||
}
|
||||
}
|
||||
|
||||
public override void CheckGain( Mobile m )
|
||||
{
|
||||
m.CheckSkill( MoveSkill, RequiredSkill, 120.0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Scripts/Spells/Bushido/SamuraiMove.cs
Normal file
15
Scripts/Spells/Bushido/SamuraiMove.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Server;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class SamuraiMove : SpecialMove
|
||||
{
|
||||
public override SkillName MoveSkill{ get{ return SkillName.Bushido; } }
|
||||
|
||||
public override void CheckGain( Mobile m )
|
||||
{
|
||||
m.CheckSkill( MoveSkill, RequiredSkill - 12.5, RequiredSkill + 37.5 ); //Per five on friday 02/16/07
|
||||
}
|
||||
}
|
||||
}
|
||||
129
Scripts/Spells/Bushido/SamuraiSpell.cs
Normal file
129
Scripts/Spells/Bushido/SamuraiSpell.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Spells;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Bushido
|
||||
{
|
||||
public abstract class SamuraiSpell : Spell
|
||||
{
|
||||
public abstract double RequiredSkill{ get; }
|
||||
public abstract int RequiredMana{ get; }
|
||||
|
||||
public override SkillName CastSkill{ get{ return SkillName.Bushido; } }
|
||||
public override SkillName DamageSkill{ get{ return SkillName.Bushido; } }
|
||||
|
||||
public override bool ClearHandsOnCast{ get{ return false; } }
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
public override bool ShowHandMovement{ get{ return false; } }
|
||||
|
||||
//public override int CastDelayBase{ get{ return 1; } }
|
||||
public override double CastDelayFastScalar{ get{ return 0; } }
|
||||
|
||||
public override int CastRecoveryBase{ get{ return 7; } }
|
||||
|
||||
public SamuraiSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public static bool CheckExpansion( Mobile from )
|
||||
{
|
||||
if ( !( from is PlayerMobile ) )
|
||||
return true;
|
||||
|
||||
if ( from.NetState == null )
|
||||
return false;
|
||||
|
||||
return from.NetState.SupportsExpansion( Expansion.SE );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
int mana = ScaleMana ( RequiredMana );
|
||||
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( !CheckExpansion( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063456 ); // You must upgrade to Samurai Empire in order to use that ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( Caster.Skills[CastSkill].Value < RequiredSkill )
|
||||
{
|
||||
string args = String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), CastSkill.ToString() );
|
||||
Caster.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CheckFizzle()
|
||||
{
|
||||
int mana = ScaleMana( RequiredMana );
|
||||
|
||||
if ( Caster.Skills[CastSkill].Value < RequiredSkill )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1070768, RequiredSkill.ToString( "F1" ) ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack!
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !base.CheckFizzle() )
|
||||
return false;
|
||||
|
||||
Caster.Mana -= mana;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
min = RequiredSkill - 12.5; //per 5 on friday, 2/16/07
|
||||
max = RequiredSkill + 37.5;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual void OnCastSuccessful( Mobile caster )
|
||||
{
|
||||
if ( Evasion.IsEvading( caster ) )
|
||||
Evasion.EndEvasion( caster );
|
||||
|
||||
if ( Confidence.IsConfident( caster ) )
|
||||
Confidence.EndConfidence( caster );
|
||||
|
||||
if ( CounterAttack.IsCountering( caster ) )
|
||||
CounterAttack.StopCountering( caster );
|
||||
|
||||
int spellID = SpellRegistry.GetRegistryNumber( this );
|
||||
|
||||
if ( spellID > 0 )
|
||||
caster.Send( new ToggleSpecialAbility( spellID + 1, true ) );
|
||||
}
|
||||
|
||||
public static void OnEffectEnd( Mobile caster, Type type )
|
||||
{
|
||||
int spellID = SpellRegistry.GetRegistryNumber( type );
|
||||
|
||||
if ( spellID > 0 )
|
||||
caster.Send( new ToggleSpecialAbility( spellID + 1, false ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Scripts/Spells/Chivalry/CleanseByFire.cs
Normal file
127
Scripts/Spells/Chivalry/CleanseByFire.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class CleanseByFireSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Cleanse By Fire", "Expor Flamus",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 5.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060718; } } // Expor Flamus
|
||||
|
||||
public CleanseByFireSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !m.Poisoned )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060176 ); // That creature is not poisoned!
|
||||
}
|
||||
else if ( CheckBSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Cures the target of poisons, but causes the caster to be burned by fire damage for 13-55 hit points.
|
||||
* The amount of fire damage is lessened if the caster has high Karma.
|
||||
*/
|
||||
|
||||
Poison p = m.Poison;
|
||||
|
||||
if ( p != null )
|
||||
{
|
||||
// Cleanse by fire is now difficulty based
|
||||
int chanceToCure = 10000 + (int)(Caster.Skills[SkillName.Chivalry].Value * 75) - ((p.Level + 1) * 2000);
|
||||
chanceToCure /= 100;
|
||||
|
||||
if ( chanceToCure > Utility.Random( 100 ) )
|
||||
{
|
||||
if ( m.CurePoison( Caster ) )
|
||||
{
|
||||
if ( Caster != m )
|
||||
Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons!
|
||||
|
||||
m.SendLocalizedMessage( 1010059 ); // You have been cured of all poisons.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage( 1010060 ); // You have failed to cure your target!
|
||||
}
|
||||
}
|
||||
|
||||
m.PlaySound( 0x1E0 );
|
||||
m.FixedParticles( 0x373A, 1, 15, 5012, 3, 2, EffectLayer.Waist );
|
||||
|
||||
IEntity from = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z - 5 ), m.Map );
|
||||
IEntity to = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z + 45 ), m.Map );
|
||||
Effects.SendMovingParticles( from, to, 0x374B, 1, 0, false, false, 63, 2, 9501, 1, 0, EffectLayer.Head, 0x100 );
|
||||
|
||||
Caster.PlaySound( 0x208 );
|
||||
Caster.FixedParticles( 0x3709, 1, 30, 9934, 0, 7, EffectLayer.Waist );
|
||||
|
||||
int damage = 50 - ComputePowerValue( 4 );
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( damage < 13 )
|
||||
damage = 13;
|
||||
else if ( damage > 55 )
|
||||
damage = 55;
|
||||
|
||||
AOS.Damage( Caster, Caster, damage, 0, 100, 0, 0, 0, true );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private CleanseByFireSpell m_Owner;
|
||||
|
||||
public InternalTarget( CleanseByFireSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Scripts/Spells/Chivalry/CloseWounds.cs
Normal file
121
Scripts/Spells/Chivalry/CloseWounds.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class CloseWoundsSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Close Wounds", "Obsu Vulni",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 0.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060719; } } // Obsu Vulni
|
||||
|
||||
public CloseWoundsSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.InRange( m, 2 ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060178 ); // You are too far away to perform that action!
|
||||
}
|
||||
else if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061654 ); // You cannot heal that which is not alive.
|
||||
}
|
||||
else if ( m.IsDeadBondedPet )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060177 ); // You cannot heal a creature that is already dead!
|
||||
}
|
||||
else if ( m.Hits >= m.HitsMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500955 ); // That being is not damaged!
|
||||
}
|
||||
else if ( m.Poisoned || Server.Items.MortalStrike.IsWounded( m ) )
|
||||
{
|
||||
Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, (Caster == m) ? 1005000 : 1010398 );
|
||||
}
|
||||
else if ( CheckBSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Heals the target for 7 to 39 points of damage.
|
||||
* The caster's Karma affects the amount of damage healed.
|
||||
*/
|
||||
|
||||
int toHeal = ComputePowerValue( 6 ) + Utility.RandomMinMax( 0, 2 );
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( toHeal < 7 )
|
||||
toHeal = 7;
|
||||
else if ( toHeal > 39 )
|
||||
toHeal = 39;
|
||||
|
||||
if ( (m.Hits + toHeal) > m.HitsMax )
|
||||
toHeal = m.HitsMax - m.Hits;
|
||||
|
||||
//m.Hits += toHeal; //Was previosuly due to the message
|
||||
//m.Heal( toHeal, Caster, false );
|
||||
SpellHelper.Heal( toHeal, m, Caster, false );
|
||||
|
||||
m.SendLocalizedMessage( 1060203, toHeal.ToString() ); // You have had ~1_HEALED_AMOUNT~ hit points of damage healed.
|
||||
|
||||
m.PlaySound( 0x202 );
|
||||
m.FixedParticles( 0x376A, 1, 62, 9923, 3, 3, EffectLayer.Waist );
|
||||
m.FixedParticles( 0x3779, 1, 46, 9502, 5, 3, EffectLayer.Waist );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private CloseWoundsSpell m_Owner;
|
||||
|
||||
public InternalTarget( CloseWoundsSpell owner ) : base( 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Scripts/Spells/Chivalry/ConsecrateWeapon.cs
Normal file
107
Scripts/Spells/Chivalry/ConsecrateWeapon.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class ConsecrateWeaponSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Consecrate Weapon", "Consecrus Arma",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 15.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060720; } } // Consecrus Arma
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
public ConsecrateWeaponSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
BaseWeapon weapon = Caster.Weapon as BaseWeapon;
|
||||
|
||||
if ( weapon == null || weapon is Fists )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501078 ); // You must be holding a weapon.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
/* Temporarily enchants the weapon the caster is currently wielding.
|
||||
* The type of damage the weapon inflicts when hitting a target will
|
||||
* be converted to the target's worst Resistance type.
|
||||
* Duration of the effect is affected by the caster's Karma and lasts for 3 to 11 seconds.
|
||||
*/
|
||||
|
||||
int itemID, soundID;
|
||||
|
||||
switch ( weapon.Skill )
|
||||
{
|
||||
case SkillName.Macing: itemID = 0xFB4; soundID = 0x232; break;
|
||||
case SkillName.Archery: itemID = 0x13B1; soundID = 0x145; break;
|
||||
default: itemID = 0xF5F; soundID = 0x56; break;
|
||||
}
|
||||
|
||||
Caster.PlaySound( 0x20C );
|
||||
Caster.PlaySound( soundID );
|
||||
Caster.FixedParticles( 0x3779, 1, 30, 9964, 3, 3, EffectLayer.Waist );
|
||||
|
||||
IEntity from = new Entity( Serial.Zero, new Point3D( Caster.X, Caster.Y, Caster.Z ), Caster.Map );
|
||||
IEntity to = new Entity( Serial.Zero, new Point3D( Caster.X, Caster.Y, Caster.Z + 50 ), Caster.Map );
|
||||
Effects.SendMovingParticles( from, to, itemID, 1, 0, false, false, 33, 3, 9501, 1, 0, EffectLayer.Head, 0x100 );
|
||||
|
||||
double seconds = ComputePowerValue( 20 );
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( seconds < 3.0 )
|
||||
seconds = 3.0;
|
||||
else if ( seconds > 11.0 )
|
||||
seconds = 11.0;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( seconds );
|
||||
|
||||
Timer t = (Timer)m_Table[weapon];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
weapon.Consecrated = true;
|
||||
|
||||
m_Table[weapon] = t = new ExpireTimer( weapon, duration );
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private BaseWeapon m_Weapon;
|
||||
|
||||
public ExpireTimer( BaseWeapon weapon, TimeSpan delay ) : base( delay )
|
||||
{
|
||||
m_Weapon = weapon;
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Weapon.Consecrated = false;
|
||||
Effects.PlaySound( m_Weapon.GetWorldLocation(), m_Weapon.Map, 0x1F8 );
|
||||
m_Table.Remove( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Scripts/Spells/Chivalry/DispelEvil.cs
Normal file
119
Scripts/Spells/Chivalry/DispelEvil.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells.Necromancy;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class DispelEvilSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Dispel Evil", "Dispiro Malas",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.25 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 35.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060721; } } // Dispiro Malas
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
public DispelEvilSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return false; } }
|
||||
|
||||
public override void SendCastEffect()
|
||||
{
|
||||
Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 ); // At player
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
foreach ( Mobile m in Caster.GetMobilesInRange( 8 ) )
|
||||
{
|
||||
if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) )
|
||||
targets.Add( m );
|
||||
}
|
||||
|
||||
Caster.PlaySound( 0xF5 );
|
||||
Caster.PlaySound( 0x299 );
|
||||
Caster.FixedParticles( 0x37C4, 1, 25, 9922, 14, 3, EffectLayer.Head );
|
||||
|
||||
int dispelSkill = ComputePowerValue( 2 );
|
||||
|
||||
double chiv = Caster.Skills.Chivalry.Value;
|
||||
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
BaseCreature bc = m as BaseCreature;
|
||||
|
||||
if ( bc != null )
|
||||
{
|
||||
bool dispellable = bc.Summoned && !bc.IsAnimatedDead;
|
||||
|
||||
if ( dispellable )
|
||||
{
|
||||
double dispelChance = (50.0 + ((100 * (chiv - bc.DispelDifficulty)) / (bc.DispelFocus*2))) / 100;
|
||||
dispelChance *= dispelSkill / 100.0;
|
||||
|
||||
if ( dispelChance > Utility.RandomDouble() )
|
||||
{
|
||||
Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x3728, 8, 20, 5042 );
|
||||
Effects.PlaySound( m, m.Map, 0x201 );
|
||||
|
||||
m.Delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool evil = !bc.Controlled && bc.Karma < 0;
|
||||
|
||||
if ( evil )
|
||||
{
|
||||
// TODO: Is this right?
|
||||
double fleeChance = (100 - Math.Sqrt( m.Fame / 2 )) * chiv * dispelSkill;
|
||||
fleeChance /= 1000000;
|
||||
|
||||
if ( fleeChance > Utility.RandomDouble() )
|
||||
{
|
||||
// guide says 2 seconds, it's longer
|
||||
bc.BeginFlee( TimeSpan.FromSeconds( 30.0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransformContext context = TransformationSpellHelper.GetContext( m );
|
||||
if( context != null && context.Spell is NecromancerSpell ) //Trees are not evil! TODO: OSI confirm?
|
||||
{
|
||||
// transformed ..
|
||||
|
||||
double drainChance = 0.5 * (Caster.Skills.Chivalry.Value / Math.Max( m.Skills.Necromancy.Value, 1 ));
|
||||
|
||||
if ( drainChance > Utility.RandomDouble() )
|
||||
{
|
||||
int drain = (5 * dispelSkill) / 100;
|
||||
|
||||
m.Stam -= drain;
|
||||
m.Mana -= drain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Scripts/Spells/Chivalry/DivineFury.cs
Normal file
79
Scripts/Spells/Chivalry/DivineFury.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class DivineFurySpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Divine Fury", "Divinum Furis",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 25.0; } }
|
||||
public override int RequiredMana{ get{ return 15; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060722; } } // Divinum Furis
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
public DivineFurySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Caster.PlaySound( 0x20F );
|
||||
Caster.PlaySound( Caster.Female ? 0x338 : 0x44A );
|
||||
Caster.FixedParticles( 0x376A, 1, 31, 9961, 1160, 0, EffectLayer.Waist );
|
||||
Caster.FixedParticles( 0x37C4, 1, 31, 9502, 43, 2, EffectLayer.Waist );
|
||||
|
||||
Caster.Stam = Caster.StamMax;
|
||||
|
||||
Timer t = (Timer)m_Table[Caster];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
int delay = ComputePowerValue( 10 );
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( delay < 7 )
|
||||
delay = 7;
|
||||
else if ( delay > 24 )
|
||||
delay = 24;
|
||||
|
||||
m_Table[Caster] = t = Timer.DelayCall( TimeSpan.FromSeconds( delay ), new TimerStateCallback( Expire_Callback ), Caster );
|
||||
Caster.Delta( MobileDelta.WeaponDamage );
|
||||
|
||||
BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.DivineFury, 1060589, 1075634, TimeSpan.FromSeconds(delay), Caster));
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool UnderEffect( Mobile m )
|
||||
{
|
||||
return m_Table.Contains( m );
|
||||
}
|
||||
|
||||
private static void Expire_Callback( object state )
|
||||
{
|
||||
Mobile m = (Mobile)state;
|
||||
|
||||
m_Table.Remove( m );
|
||||
|
||||
m.Delta( MobileDelta.WeaponDamage );
|
||||
m.PlaySound( 0xF8 );
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Scripts/Spells/Chivalry/EnemyOfOne.cs
Normal file
84
Scripts/Spells/Chivalry/EnemyOfOne.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class EnemyOfOneSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Enemy of One", "Forul Solum",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 45.0; } }
|
||||
public override int RequiredMana{ get{ return 20; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060723; } } // Forul Solum
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
public EnemyOfOneSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Caster.PlaySound( 0x0F5 );
|
||||
Caster.PlaySound( 0x1ED );
|
||||
Caster.FixedParticles( 0x375A, 1, 30, 9966, 33, 2, EffectLayer.Head );
|
||||
Caster.FixedParticles( 0x37B9, 1, 30, 9502, 43, 3, EffectLayer.Head );
|
||||
|
||||
Timer t = (Timer)m_Table[Caster];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
double delay = (double)ComputePowerValue( 1 ) / 60;
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( delay < 1.5 )
|
||||
delay = 1.5;
|
||||
else if ( delay > 3.5 )
|
||||
delay = 3.5;
|
||||
|
||||
m_Table[Caster] = Timer.DelayCall( TimeSpan.FromMinutes( delay ), new TimerStateCallback( Expire_Callback ), Caster );
|
||||
|
||||
if ( Caster is PlayerMobile )
|
||||
{
|
||||
((PlayerMobile)Caster).EnemyOfOneType = null;
|
||||
((PlayerMobile)Caster).WaitingForEnemy = true;
|
||||
|
||||
BuffInfo.AddBuff ( Caster, new BuffInfo ( BuffIcon.EnemyOfOne, 1075653, 1044111, TimeSpan.FromMinutes ( delay ), Caster ) );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
private static void Expire_Callback( object state )
|
||||
{
|
||||
Mobile m = (Mobile)state;
|
||||
|
||||
m_Table.Remove( m );
|
||||
|
||||
m.PlaySound( 0x1F8 );
|
||||
|
||||
if ( m is PlayerMobile )
|
||||
{
|
||||
((PlayerMobile)m).EnemyOfOneType = null;
|
||||
((PlayerMobile)m).WaitingForEnemy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Scripts/Spells/Chivalry/HolyLight.cs
Normal file
67
Scripts/Spells/Chivalry/HolyLight.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class HolyLightSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Holy Light", "Augus Luminos",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.75 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 55.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060724; } } // Augus Luminos
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
public HolyLightSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return false; } }
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
foreach ( Mobile m in Caster.GetMobilesInRange( 3 ) )
|
||||
if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) && ( !Core.AOS || Caster.InLOS( m ) ) )
|
||||
targets.Add( m );
|
||||
|
||||
Caster.PlaySound( 0x212 );
|
||||
Caster.PlaySound( 0x206 );
|
||||
|
||||
Effects.SendLocationParticles( EffectItem.Create( Caster.Location, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 1, 29, 0x47D, 2, 9962, 0 );
|
||||
Effects.SendLocationParticles( EffectItem.Create( new Point3D( Caster.X, Caster.Y, Caster.Z - 7 ), Caster.Map, EffectItem.DefaultDuration ), 0x37C4, 1, 29, 0x47D, 2, 9502, 0 );
|
||||
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
int damage = ComputePowerValue( 10 ) + Utility.RandomMinMax( 0, 2 );
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( damage < 8 )
|
||||
damage = 8;
|
||||
else if ( damage > 24 )
|
||||
damage = 24;
|
||||
|
||||
Caster.DoHarmful( m );
|
||||
SpellHelper.Damage( this, m, damage, 0, 0, 0, 0, 100 );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Scripts/Spells/Chivalry/NobleSacrifice.cs
Normal file
174
Scripts/Spells/Chivalry/NobleSacrifice.cs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Gumps;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Regions;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class NobleSacrificeSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Noble Sacrifice", "Dium Prostra",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 65.0; } }
|
||||
public override int RequiredMana{ get{ return 20; } }
|
||||
public override int RequiredTithing{ get{ return 30; } }
|
||||
public override int MantraNumber{ get{ return 1060725; } } // Dium Prostra
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
public NobleSacrificeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
foreach ( Mobile m in Caster.GetMobilesInRange( 3 ) ) // TODO: Validate range
|
||||
{
|
||||
if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead )
|
||||
continue;
|
||||
|
||||
if ( Caster != m && m.InLOS( Caster ) && Caster.CanBeBeneficial( m, false, true ) && !(m is Golem) )
|
||||
targets.Add( m );
|
||||
}
|
||||
|
||||
Caster.PlaySound( 0x244 );
|
||||
Caster.FixedParticles( 0x3709, 1, 30, 9965, 5, 7, EffectLayer.Waist );
|
||||
Caster.FixedParticles( 0x376A, 1, 30, 9502, 5, 3, EffectLayer.Waist );
|
||||
|
||||
/* Attempts to Resurrect, Cure and Heal all targets in a radius around the caster.
|
||||
* If any target is successfully assisted, the Paladin's current
|
||||
* Hit Points, Mana and Stamina are set to 1.
|
||||
* Amount of damage healed is affected by the Caster's Karma, from 8 to 24 hit points.
|
||||
*/
|
||||
|
||||
bool sacrifice = false;
|
||||
|
||||
// TODO: Is there really a resurrection chance?
|
||||
double resChance = 0.1 + (0.9 * ((double)Caster.Karma / 10000));
|
||||
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
if ( !m.Alive )
|
||||
{
|
||||
if ( m.Region != null && m.Region.IsPartOf( "Khaldun" ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1010395 ); // The veil of death in this area is too strong and resists thy efforts to restore life.
|
||||
}
|
||||
else if( resChance > Utility.RandomDouble() )
|
||||
{
|
||||
m.FixedParticles( 0x375A, 1, 15, 5005, 5, 3, EffectLayer.Head );
|
||||
m.CloseGump( typeof( ResurrectGump ) );
|
||||
m.SendGump( new ResurrectGump( m, Caster ) );
|
||||
sacrifice = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool sendEffect = false;
|
||||
|
||||
if ( m.Poisoned && m.CurePoison( Caster ) )
|
||||
{
|
||||
Caster.DoBeneficial( m );
|
||||
|
||||
if ( Caster != m )
|
||||
Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons!
|
||||
|
||||
m.SendLocalizedMessage( 1010059 ); // You have been cured of all poisons.
|
||||
sendEffect = true;
|
||||
sacrifice = true;
|
||||
}
|
||||
|
||||
if ( m.Hits < m.HitsMax )
|
||||
{
|
||||
int toHeal = ComputePowerValue( 10 ) + Utility.RandomMinMax( 0, 2 );
|
||||
|
||||
// TODO: Should caps be applied?
|
||||
if ( toHeal < 8 )
|
||||
toHeal = 8;
|
||||
else if ( toHeal > 24 )
|
||||
toHeal = 24;
|
||||
|
||||
Caster.DoBeneficial( m );
|
||||
m.Heal( toHeal, Caster );
|
||||
sendEffect = true;
|
||||
}
|
||||
|
||||
StatMod mod;
|
||||
|
||||
mod = m.GetStatMod( "[Magic] Str Offset" );
|
||||
if ( mod != null && mod.Offset < 0 )
|
||||
{
|
||||
m.RemoveStatMod( "[Magic] Str Offset" );
|
||||
sendEffect = true;
|
||||
}
|
||||
|
||||
mod = m.GetStatMod( "[Magic] Dex Offset" );
|
||||
if ( mod != null && mod.Offset < 0 )
|
||||
{
|
||||
m.RemoveStatMod( "[Magic] Dex Offset" );
|
||||
sendEffect = true;
|
||||
}
|
||||
|
||||
mod = m.GetStatMod( "[Magic] Int Offset" );
|
||||
if ( mod != null && mod.Offset < 0 )
|
||||
{
|
||||
m.RemoveStatMod( "[Magic] Int Offset" );
|
||||
sendEffect = true;
|
||||
}
|
||||
|
||||
if ( m.Paralyzed )
|
||||
{
|
||||
m.Paralyzed = false;
|
||||
sendEffect = true;
|
||||
}
|
||||
|
||||
if ( EvilOmenSpell.TryEndEffect( m ) )
|
||||
sendEffect = true;
|
||||
|
||||
if ( StrangleSpell.RemoveCurse( m ) )
|
||||
sendEffect = true;
|
||||
|
||||
if ( CorpseSkinSpell.RemoveCurse( m ) )
|
||||
sendEffect = true;
|
||||
|
||||
// TODO: Should this remove blood oath? Pain spike?
|
||||
|
||||
if ( sendEffect )
|
||||
{
|
||||
m.FixedParticles( 0x375A, 1, 15, 5005, 5, 3, EffectLayer.Head );
|
||||
sacrifice = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( sacrifice )
|
||||
{
|
||||
Caster.PlaySound( Caster.Body.IsFemale ? 0x150 : 0x423 );
|
||||
Caster.Hits = 1;
|
||||
Caster.Stam = 1;
|
||||
Caster.Mana = 1;
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
141
Scripts/Spells/Chivalry/PaladinSpell.cs
Normal file
141
Scripts/Spells/Chivalry/PaladinSpell.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Spells;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public abstract class PaladinSpell : Spell
|
||||
{
|
||||
public abstract double RequiredSkill{ get; }
|
||||
public abstract int RequiredMana{ get; }
|
||||
public abstract int RequiredTithing{ get; }
|
||||
public abstract int MantraNumber{ get; }
|
||||
|
||||
public override SkillName CastSkill{ get{ return SkillName.Chivalry; } }
|
||||
public override SkillName DamageSkill{ get{ return SkillName.Chivalry; } }
|
||||
|
||||
public override bool ClearHandsOnCast{ get{ return false; } }
|
||||
|
||||
//public override int CastDelayBase{ get{ return 1; } }
|
||||
|
||||
public override int CastRecoveryBase{ get{ return 7; } }
|
||||
|
||||
public PaladinSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
int mana = ScaleMana( RequiredMana );
|
||||
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( Caster.TithingPoints < RequiredTithing )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060173, RequiredTithing.ToString() ); // You must have at least ~1_TITHE_REQUIREMENT~ Tithing Points to use this ability,
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CheckFizzle()
|
||||
{
|
||||
int requiredTithing = this.RequiredTithing;
|
||||
|
||||
if ( AosAttributes.GetValue( Caster, AosAttribute.LowerRegCost ) > Utility.Random( 100 ) )
|
||||
requiredTithing = 0;
|
||||
|
||||
int mana = ScaleMana( RequiredMana );
|
||||
|
||||
if ( Caster.TithingPoints < requiredTithing )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060173, RequiredTithing.ToString() ); // You must have at least ~1_TITHE_REQUIREMENT~ Tithing Points to use this ability,
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
Caster.TithingPoints -= requiredTithing;
|
||||
|
||||
if ( !base.CheckFizzle() )
|
||||
return false;
|
||||
|
||||
Caster.Mana -= mana;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SayMantra()
|
||||
{
|
||||
Caster.PublicOverheadMessage( MessageType.Regular, 0x3B2, MantraNumber, "", false );
|
||||
}
|
||||
|
||||
public override void DoFizzle()
|
||||
{
|
||||
Caster.PlaySound( 0x1D6 );
|
||||
Caster.NextSpellTime = Core.TickCount;
|
||||
}
|
||||
|
||||
public override void DoHurtFizzle()
|
||||
{
|
||||
Caster.PlaySound( 0x1D6 );
|
||||
}
|
||||
|
||||
public override void OnDisturb( DisturbType type, bool message )
|
||||
{
|
||||
base.OnDisturb( type, message );
|
||||
|
||||
if ( message )
|
||||
Caster.PlaySound( 0x1D6 );
|
||||
}
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
SendCastEffect();
|
||||
}
|
||||
|
||||
public virtual void SendCastEffect()
|
||||
{
|
||||
Caster.FixedEffect( 0x37C4, 10, (int)( GetCastDelay().TotalSeconds * 28 ), 4, 3 );
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
min = RequiredSkill;
|
||||
max = RequiredSkill + 50.0;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int ComputePowerValue( int div )
|
||||
{
|
||||
return ComputePowerValue( Caster, div );
|
||||
}
|
||||
|
||||
public static int ComputePowerValue( Mobile from, int div )
|
||||
{
|
||||
if ( from == null )
|
||||
return 0;
|
||||
|
||||
int v = (int) Math.Sqrt( from.Karma + 20000 + (from.Skills.Chivalry.Fixed * 10) );
|
||||
|
||||
return v / div;
|
||||
}
|
||||
}
|
||||
}
|
||||
143
Scripts/Spells/Chivalry/RemoveCurse.cs
Normal file
143
Scripts/Spells/Chivalry/RemoveCurse.cs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Spells.Fourth;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class RemoveCurseSpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Remove Curse", "Extermo Vomica",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 5.0; } }
|
||||
public override int RequiredMana{ get{ return 20; } }
|
||||
public override int RequiredTithing{ get{ return 10; } }
|
||||
public override int MantraNumber{ get{ return 1060726; } } // Extermo Vomica
|
||||
|
||||
public RemoveCurseSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( CheckBSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Attempts to remove all Curse effects from Target.
|
||||
* Curses include Mage spells such as Clumsy, Weaken, Feeblemind and Paralyze
|
||||
* as well as all Necromancer curses.
|
||||
* Chance of removing curse is affected by Caster's Karma.
|
||||
*/
|
||||
|
||||
int chance = 0;
|
||||
|
||||
if ( Caster.Karma < -5000 )
|
||||
chance = 0;
|
||||
else if ( Caster.Karma < 0 )
|
||||
chance = (int) Math.Sqrt( 20000 + Caster.Karma ) - 122;
|
||||
else if ( Caster.Karma < 5625 )
|
||||
chance = (int) Math.Sqrt( Caster.Karma ) + 25;
|
||||
else
|
||||
chance = 100;
|
||||
|
||||
if ( chance > Utility.Random( 100 ) )
|
||||
{
|
||||
m.PlaySound( 0xF6 );
|
||||
m.PlaySound( 0x1F7 );
|
||||
m.FixedParticles( 0x3709, 1, 30, 9963, 13, 3, EffectLayer.Head );
|
||||
|
||||
IEntity from = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z - 10 ), Caster.Map );
|
||||
IEntity to = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z + 50 ), Caster.Map );
|
||||
Effects.SendMovingParticles( from, to, 0x2255, 1, 0, false, false, 13, 3, 9501, 1, 0, EffectLayer.Head, 0x100 );
|
||||
|
||||
StatMod mod;
|
||||
|
||||
mod = m.GetStatMod( "[Magic] Str Offset" );
|
||||
if ( mod != null && mod.Offset < 0 )
|
||||
m.RemoveStatMod( "[Magic] Str Offset" );
|
||||
|
||||
mod = m.GetStatMod( "[Magic] Dex Offset" );
|
||||
if ( mod != null && mod.Offset < 0 )
|
||||
m.RemoveStatMod( "[Magic] Dex Offset" );
|
||||
|
||||
mod = m.GetStatMod( "[Magic] Int Offset" );
|
||||
if ( mod != null && mod.Offset < 0 )
|
||||
m.RemoveStatMod( "[Magic] Int Offset" );
|
||||
|
||||
m.Paralyzed = false;
|
||||
|
||||
EvilOmenSpell.TryEndEffect( m );
|
||||
StrangleSpell.RemoveCurse( m );
|
||||
CorpseSkinSpell.RemoveCurse( m );
|
||||
CurseSpell.RemoveEffect( m );
|
||||
MortalStrike.EndWound( m );
|
||||
if (Core.ML) { BloodOathSpell.RemoveCurse ( m ); }
|
||||
MindRotSpell.ClearMindRotScalar ( m );
|
||||
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.Clumsy );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.FeebleMind );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.Weaken );
|
||||
BuffInfo.RemoveBuff ( m, BuffIcon.Curse );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.MassCurse );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.MortalStrike );
|
||||
BuffInfo.RemoveBuff ( m, BuffIcon.Mindrot );
|
||||
|
||||
// TODO: Should this remove blood oath? Pain spike?
|
||||
}
|
||||
else
|
||||
{
|
||||
m.PlaySound( 0x1DF );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private RemoveCurseSpell m_Owner;
|
||||
|
||||
public InternalTarget( RemoveCurseSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
202
Scripts/Spells/Chivalry/SacredJourney.cs
Normal file
202
Scripts/Spells/Chivalry/SacredJourney.cs
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Multis;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
{
|
||||
public class SacredJourneySpell : PaladinSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Sacred Journey", "Sanctum Viatas",
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 15.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
public override int RequiredTithing{ get{ return 15; } }
|
||||
public override int MantraNumber{ get{ return 1060727; } } // Sanctum Viatas
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
private RunebookEntry m_Entry;
|
||||
private Runebook m_Book;
|
||||
|
||||
public SacredJourneySpell( Mobile caster, Item scroll ) : this( caster, scroll, null, null )
|
||||
{
|
||||
}
|
||||
|
||||
public SacredJourneySpell( Mobile caster, Item scroll, RunebookEntry entry, Runebook book ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
m_Entry = entry;
|
||||
m_Book = book;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( m_Entry == null )
|
||||
Caster.Target = new InternalTarget( this );
|
||||
else
|
||||
Effect( m_Entry.Location, m_Entry.Map, true );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Criminal )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily.
|
||||
return false;
|
||||
}
|
||||
else if ( SpellHelper.CheckCombat( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061282 ); // You cannot use the Sacred Journey ability to flee from combat.
|
||||
return false;
|
||||
}
|
||||
else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move.
|
||||
return false;
|
||||
}
|
||||
|
||||
return SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom );
|
||||
}
|
||||
|
||||
public void Effect( Point3D loc, Map map, bool checkMulti )
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
}
|
||||
else if ( map == null || (!Core.AOS && Caster.Map != map) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005569 ); // You can not recall to another facet.
|
||||
}
|
||||
else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom ) )
|
||||
{
|
||||
}
|
||||
else if ( !SpellHelper.CheckTravel( Caster, map, loc, TravelCheckType.RecallTo ) )
|
||||
{
|
||||
}
|
||||
else if ( map == Map.Felucca && Caster is PlayerMobile && ((PlayerMobile)Caster).Young )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young.
|
||||
}
|
||||
else if ( Caster.Kills >= 5 && map != Map.Felucca )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there.
|
||||
}
|
||||
else if ( Caster.Criminal )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily.
|
||||
}
|
||||
else if ( SpellHelper.CheckCombat( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061282 ); // You cannot use the Sacred Journey ability to flee from combat.
|
||||
}
|
||||
else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move.
|
||||
}
|
||||
else if ( !map.CanSpawnMobile( loc.X, loc.Y, loc.Z ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( (checkMulti && SpellHelper.CheckMulti( loc, map )) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( m_Book != null && m_Book.CurCharges <= 0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502412 ); // There are no charges left on that item.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
BaseCreature.TeleportPets( Caster, loc, map, true );
|
||||
|
||||
if ( m_Book != null )
|
||||
--m_Book.CurCharges;
|
||||
|
||||
Effects.SendLocationParticles( EffectItem.Create( Caster.Location, Caster.Map, EffectItem.DefaultDuration ), 0, 0, 0, 5033 );
|
||||
|
||||
Caster.PlaySound( 0x1FC );
|
||||
Caster.MoveToWorld( loc, map );
|
||||
Caster.PlaySound( 0x1FC );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private SacredJourneySpell m_Owner;
|
||||
|
||||
public InternalTarget( SacredJourneySpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is RecallRune )
|
||||
{
|
||||
RecallRune rune = (RecallRune)o;
|
||||
|
||||
if ( rune.Marked )
|
||||
m_Owner.Effect( rune.Target, rune.TargetMap, true );
|
||||
else
|
||||
from.SendLocalizedMessage( 501805 ); // That rune is not yet marked.
|
||||
}
|
||||
else if ( o is Runebook )
|
||||
{
|
||||
RunebookEntry e = ((Runebook)o).Default;
|
||||
|
||||
if ( e != null )
|
||||
m_Owner.Effect( e.Location, e.Map, true );
|
||||
else
|
||||
from.SendLocalizedMessage( 502354 ); // Target is not marked.
|
||||
}
|
||||
else if ( o is Key && ((Key)o).KeyValue != 0 && ((Key)o).Link is BaseBoat )
|
||||
{
|
||||
BaseBoat boat = ((Key)o).Link as BaseBoat;
|
||||
|
||||
if ( !boat.Deleted && boat.CheckKey( ((Key)o).KeyValue ) )
|
||||
m_Owner.Effect( boat.GetMarkedLocation(), boat.Map, false );
|
||||
else
|
||||
from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object.
|
||||
}
|
||||
else if ( o is HouseRaffleDeed && ((HouseRaffleDeed)o).ValidLocation() )
|
||||
{
|
||||
HouseRaffleDeed deed = (HouseRaffleDeed)o;
|
||||
|
||||
m_Owner.Effect( deed.PlotLocation, deed.PlotFacet, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object.
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNonlocalTarget( Mobile from, object o )
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Scripts/Spells/Eighth/AirElemental.cs
Normal file
55
Scripts/Spells/Eighth/AirElemental.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class AirElementalSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Air Elemental", "Kal Vas Xen Hur",
|
||||
269,
|
||||
9010,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public AirElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + 2) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 );
|
||||
|
||||
if ( Core.AOS )
|
||||
SpellHelper.Summon( new SummonedAirElemental(), Caster, 0x217, duration, false, false );
|
||||
else
|
||||
SpellHelper.Summon( new AirElemental(), Caster, 0x217, duration, false, false );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Scripts/Spells/Eighth/EarthElemental.cs
Normal file
55
Scripts/Spells/Eighth/EarthElemental.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class EarthElementalSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Earth Elemental", "Kal Vas Xen Ylem",
|
||||
269,
|
||||
9020,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public EarthElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + 2) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 );
|
||||
|
||||
if ( Core.AOS )
|
||||
SpellHelper.Summon( new SummonedEarthElemental(), Caster, 0x217, duration, false, false );
|
||||
else
|
||||
SpellHelper.Summon( new EarthElemental(), Caster, 0x217, duration, false, false );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Scripts/Spells/Eighth/Earthquake.cs
Normal file
78
Scripts/Spells/Eighth/Earthquake.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class EarthquakeSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Earthquake", "In Vas Por",
|
||||
233,
|
||||
9012,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.Ginseng,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public EarthquakeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return !Core.AOS; } }
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( SpellHelper.CheckTown( Caster, Caster ) && CheckSequence() )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
Map map = Caster.Map;
|
||||
|
||||
if ( map != null )
|
||||
foreach ( Mobile m in Caster.GetMobilesInRange( 1 + (int)(Caster.Skills[SkillName.Magery].Value / 15.0) ) )
|
||||
if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) && (!Core.AOS || Caster.InLOS( m )) )
|
||||
targets.Add( m );
|
||||
|
||||
Caster.PlaySound( 0x220 );
|
||||
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
int damage;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
damage = m.Hits / 2;
|
||||
|
||||
if ( !m.Player )
|
||||
damage = Math.Max( Math.Min( damage, 100 ), 15 );
|
||||
damage += Utility.RandomMinMax( 0, 15 );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = (m.Hits * 6) / 10;
|
||||
|
||||
if ( !m.Player && damage < 10 )
|
||||
damage = 10;
|
||||
else if ( damage > 75 )
|
||||
damage = 75;
|
||||
}
|
||||
|
||||
Caster.DoHarmful( m );
|
||||
SpellHelper.Damage( TimeSpan.Zero, m, Caster, damage, 100, 0, 0, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Scripts/Spells/Eighth/EnergyVortex.cs
Normal file
101
Scripts/Spells/Eighth/EnergyVortex.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class EnergyVortexSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Energy Vortex", "Vas Corp Por",
|
||||
260,
|
||||
9032,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public EnergyVortexSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + (Core.SE ? 2 : 1)) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
Map map = Caster.Map;
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
TimeSpan duration;
|
||||
|
||||
if ( Core.AOS )
|
||||
duration = TimeSpan.FromSeconds( 90.0 );
|
||||
else
|
||||
duration = TimeSpan.FromSeconds( Utility.Random( 80, 40 ) );
|
||||
|
||||
BaseCreature.Summon( new EnergyVortex(), false, Caster, new Point3D( p ), 0x212, duration );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private EnergyVortexSpell m_Owner;
|
||||
|
||||
public InternalTarget( EnergyVortexSpell owner ) : base( Core.ML ? 10 : 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is IPoint3D )
|
||||
m_Owner.Target( (IPoint3D)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetOutOfLOS( Mobile from, object o )
|
||||
{
|
||||
from.SendLocalizedMessage( 501943 ); // Target cannot be seen. Try again.
|
||||
from.Target = new InternalTarget( m_Owner );
|
||||
from.Target.BeginTimeout( from, TimeoutTime - DateTime.UtcNow );
|
||||
m_Owner = null;
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
if ( m_Owner != null )
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Scripts/Spells/Eighth/FireElemental.cs
Normal file
56
Scripts/Spells/Eighth/FireElemental.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class FireElementalSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Fire Elemental", "Kal Vas Xen Flam",
|
||||
269,
|
||||
9050,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public FireElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + 4) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 );
|
||||
|
||||
if ( Core.AOS )
|
||||
SpellHelper.Summon( new SummonedFireElemental(), Caster, 0x217, duration, false, false );
|
||||
else
|
||||
SpellHelper.Summon( new FireElemental(), Caster, 0x217, duration, false, false );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Scripts/Spells/Eighth/Resurrection.cs
Normal file
113
Scripts/Spells/Eighth/Resurrection.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Gumps;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class ResurrectionSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Resurrection", "An Corp",
|
||||
245,
|
||||
9062,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.Garlic,
|
||||
Reagent.Ginseng
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public ResurrectionSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( m == Caster )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501039 ); // Thou can not resurrect thyself.
|
||||
}
|
||||
else if ( !Caster.Alive )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501040 ); // The resurrecter must be alive.
|
||||
}
|
||||
else if ( m.Alive )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501041 ); // Target is not dead.
|
||||
}
|
||||
else if ( !Caster.InRange( m, 1 ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501042 ); // Target is not close enough.
|
||||
}
|
||||
else if ( !m.Player )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501043 ); // Target is not a being.
|
||||
}
|
||||
else if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501042 ); // Target can not be resurrected at that location.
|
||||
m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there!
|
||||
}
|
||||
else if ( m.Region != null && m.Region.IsPartOf( "Khaldun" ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1010395 ); // The veil of death in this area is too strong and resists thy efforts to restore life.
|
||||
}
|
||||
else if ( CheckBSequence( m, true ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
m.PlaySound( 0x214 );
|
||||
m.FixedEffect( 0x376A, 10, 16 );
|
||||
|
||||
m.CloseGump( typeof( ResurrectGump ) );
|
||||
m.SendGump( new ResurrectGump( m, Caster ) );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private ResurrectionSpell m_Owner;
|
||||
|
||||
public InternalTarget( ResurrectionSpell owner ) : base( 1, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Scripts/Spells/Eighth/SummonDaemon.cs
Normal file
62
Scripts/Spells/Eighth/SummonDaemon.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using Server.Misc;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class SummonDaemonSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Summon Daemon", "Kal Vas Xen Corp",
|
||||
269,
|
||||
9050,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public SummonDaemonSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + (Core.SE ? 4 : 5)) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 );
|
||||
|
||||
if ( Core.AOS ) /* Why two diff daemons? TODO: solve this */
|
||||
{
|
||||
BaseCreature m_Daemon = new SummonedDaemon();
|
||||
SpellHelper.Summon( m_Daemon, Caster, 0x216, duration, false, false );
|
||||
m_Daemon.FixedParticles(0x3728, 8, 20, 5042, EffectLayer.Head );
|
||||
}
|
||||
else
|
||||
SpellHelper.Summon( new Daemon(), Caster, 0x216, duration, false, false );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Scripts/Spells/Eighth/WaterElemental.cs
Normal file
55
Scripts/Spells/Eighth/WaterElemental.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Eighth
|
||||
{
|
||||
public class WaterElementalSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Water Elemental", "Kal Vas Xen An Flam",
|
||||
269,
|
||||
9070,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Eighth; } }
|
||||
|
||||
public WaterElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + 3) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 );
|
||||
|
||||
if ( Core.AOS )
|
||||
SpellHelper.Summon( new SummonedWaterElemental(), Caster, 0x217, duration, false, false );
|
||||
else
|
||||
SpellHelper.Summon( new WaterElemental(), Caster, 0x217, duration, false, false );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
108
Scripts/Spells/Fifth/BladeSpirits.cs
Normal file
108
Scripts/Spells/Fifth/BladeSpirits.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class BladeSpiritsSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Blade Spirits", "In Jux Hur Ylem",
|
||||
266,
|
||||
9040,
|
||||
false,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public BladeSpiritsSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override TimeSpan GetCastDelay()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
return TimeSpan.FromTicks( base.GetCastDelay().Ticks * ((Core.SE) ? 3 : 5) );
|
||||
|
||||
return base.GetCastDelay() + TimeSpan.FromSeconds( 6.0 );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if( (Caster.Followers + (Core.SE ? 2 : 1)) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
Map map = Caster.Map;
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
TimeSpan duration;
|
||||
|
||||
if ( Core.AOS )
|
||||
duration = TimeSpan.FromSeconds( 120 );
|
||||
else
|
||||
duration = TimeSpan.FromSeconds( Utility.Random( 80, 40 ) );
|
||||
|
||||
BaseCreature.Summon( new BladeSpirits(), false, Caster, new Point3D( p ), 0x212, duration );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private BladeSpiritsSpell m_Owner;
|
||||
|
||||
public InternalTarget( BladeSpiritsSpell owner ) : base( Core.ML ? 10 : 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is IPoint3D )
|
||||
m_Owner.Target( (IPoint3D)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetOutOfLOS( Mobile from, object o )
|
||||
{
|
||||
from.SendLocalizedMessage( 501943 ); // Target cannot be seen. Try again.
|
||||
from.Target = new InternalTarget( m_Owner );
|
||||
from.Target.BeginTimeout( from, TimeoutTime - DateTime.UtcNow );
|
||||
m_Owner = null;
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
if ( m_Owner != null )
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Scripts/Spells/Fifth/DispelField.cs
Normal file
88
Scripts/Spells/Fifth/DispelField.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Misc;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class DispelFieldSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Dispel Field", "An Grav",
|
||||
206,
|
||||
9002,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.SpidersSilk,
|
||||
Reagent.SulfurousAsh,
|
||||
Reagent.Garlic
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public DispelFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Item item )
|
||||
{
|
||||
Type t = item.GetType();
|
||||
|
||||
if ( !Caster.CanSee( item ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( !t.IsDefined( typeof( DispellableFieldAttribute ), false ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005049 ); // That cannot be dispelled.
|
||||
}
|
||||
else if ( item is Moongate && !((Moongate)item).Dispellable )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005047 ); // That magic is too chaotic
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, item );
|
||||
|
||||
Effects.SendLocationParticles( EffectItem.Create( item.Location, item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 20, 5042 );
|
||||
Effects.PlaySound( item.GetWorldLocation(), item.Map, 0x201 );
|
||||
|
||||
item.Delete();
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private DispelFieldSpell m_Owner;
|
||||
|
||||
public InternalTarget( DispelFieldSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Item )
|
||||
{
|
||||
m_Owner.Target( (Item)o );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Owner.Caster.SendLocalizedMessage( 1005049 ); // That cannot be dispelled.
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
194
Scripts/Spells/Fifth/Incognito.cs
Normal file
194
Scripts/Spells/Fifth/Incognito.cs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Misc;
|
||||
using Server.Items;
|
||||
using Server.Gumps;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Seventh;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class IncognitoSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Incognito", "Kal In Ex",
|
||||
206,
|
||||
9002,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.Garlic,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public IncognitoSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1010445 ); // You cannot incognito if you have a sigil
|
||||
return false;
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.BodyMod == 183 || Caster.BodyMod == 184 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1042402 ); // You cannot use incognito while wearing body paint
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1010445 ); // You cannot incognito if you have a sigil
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
}
|
||||
else if ( Caster.BodyMod == 183 || Caster.BodyMod == 184 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1042402 ); // You cannot use incognito while wearing body paint
|
||||
}
|
||||
else if ( DisguiseTimers.IsDisguised( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061631 ); // You can't do that while disguised.
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) || Caster.IsBodyMod )
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
if ( Caster.BeginAction( typeof( IncognitoSpell ) ) )
|
||||
{
|
||||
DisguiseTimers.StopTimer( Caster );
|
||||
|
||||
Caster.HueMod = Caster.Race.RandomSkinHue();
|
||||
Caster.NameMod = Caster.Female ? NameList.RandomName( "female" ) : NameList.RandomName( "male" );
|
||||
|
||||
PlayerMobile pm = Caster as PlayerMobile;
|
||||
|
||||
if ( pm != null && pm.Race != null )
|
||||
{
|
||||
pm.SetHairMods( pm.Race.RandomHair( pm.Female ), pm.Race.RandomFacialHair( pm.Female ) );
|
||||
pm.HairHue = pm.Race.RandomHairHue();
|
||||
pm.FacialHairHue = pm.Race.RandomHairHue();
|
||||
}
|
||||
|
||||
Caster.FixedParticles( 0x373A, 10, 15, 5036, EffectLayer.Head );
|
||||
Caster.PlaySound( 0x3BD );
|
||||
|
||||
BaseArmor.ValidateMobile( Caster );
|
||||
BaseClothing.ValidateMobile( Caster );
|
||||
|
||||
StopTimer( Caster );
|
||||
|
||||
|
||||
int timeVal = ((6 * Caster.Skills.Magery.Fixed) / 50) + 1;
|
||||
|
||||
if( timeVal > 144 )
|
||||
timeVal = 144;
|
||||
|
||||
TimeSpan length = TimeSpan.FromSeconds( timeVal );
|
||||
|
||||
|
||||
Timer t = new InternalTimer( Caster, length );
|
||||
|
||||
m_Timers[Caster] = t;
|
||||
|
||||
t.Start();
|
||||
|
||||
BuffInfo.AddBuff( Caster, new BuffInfo( BuffIcon.Incognito, 1075819, length, Caster ) );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1079022 ); // You're already incognitoed!
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Timers = new Hashtable();
|
||||
|
||||
public static bool StopTimer( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Timers[m];
|
||||
|
||||
if ( t != null )
|
||||
{
|
||||
t.Stop();
|
||||
m_Timers.Remove( m );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.Incognito );
|
||||
}
|
||||
|
||||
return ( t != null );
|
||||
}
|
||||
|
||||
private static int[] m_HairIDs = new int[]
|
||||
{
|
||||
0x2044, 0x2045, 0x2046,
|
||||
0x203C, 0x203B, 0x203D,
|
||||
0x2047, 0x2048, 0x2049,
|
||||
0x204A, 0x0000
|
||||
};
|
||||
|
||||
private static int[] m_BeardIDs = new int[]
|
||||
{
|
||||
0x203E, 0x203F, 0x2040,
|
||||
0x2041, 0x204B, 0x204C,
|
||||
0x204D, 0x0000
|
||||
};
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Owner;
|
||||
|
||||
public InternalTimer( Mobile owner, TimeSpan length ) : base( length )
|
||||
{
|
||||
m_Owner = owner;
|
||||
|
||||
/*
|
||||
int val = ((6 * owner.Skills.Magery.Fixed) / 50) + 1;
|
||||
|
||||
if ( val > 144 )
|
||||
val = 144;
|
||||
|
||||
Delay = TimeSpan.FromSeconds( val );
|
||||
* */
|
||||
Priority = TimerPriority.OneSecond;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( !m_Owner.CanBeginAction( typeof( IncognitoSpell ) ) )
|
||||
{
|
||||
if ( m_Owner is PlayerMobile )
|
||||
((PlayerMobile)m_Owner).SetHairMods( -1, -1 );
|
||||
|
||||
m_Owner.BodyMod = 0;
|
||||
m_Owner.HueMod = -1;
|
||||
m_Owner.NameMod = null;
|
||||
m_Owner.EndAction( typeof( IncognitoSpell ) );
|
||||
|
||||
BaseArmor.ValidateMobile( m_Owner );
|
||||
BaseClothing.ValidateMobile( m_Owner );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Scripts/Spells/Fifth/MagicReflect.cs
Normal file
155
Scripts/Spells/Fifth/MagicReflect.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class MagicReflectSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Magic Reflection", "In Jux Sanct",
|
||||
242,
|
||||
9012,
|
||||
Reagent.Garlic,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public MagicReflectSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
return true;
|
||||
|
||||
if ( Caster.MagicDamageAbsorb > 0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
return false;
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
{
|
||||
/* The magic reflection spell decreases the caster's physical resistance, while increasing the caster's elemental resistances.
|
||||
* Physical decrease = 25 - (Inscription/20).
|
||||
* Elemental resistance = +10 (-20 physical, +10 elemental at GM Inscription)
|
||||
* The magic reflection spell has an indefinite duration, becoming active when cast, and deactivated when re-cast.
|
||||
* Reactive Armor, Protection, and Magic Reflection will stay on—even after logging out, even after dying—until you “turn them off” by casting them again.
|
||||
*/
|
||||
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Mobile targ = Caster;
|
||||
|
||||
ResistanceMod[] mods = (ResistanceMod[])m_Table[targ];
|
||||
|
||||
if ( mods == null )
|
||||
{
|
||||
targ.PlaySound( 0x1E9 );
|
||||
targ.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist );
|
||||
|
||||
int physiMod = -25 + (int)(targ.Skills[SkillName.Inscribe].Value / 20);
|
||||
int otherMod = 10;
|
||||
|
||||
mods = new ResistanceMod[5]
|
||||
{
|
||||
new ResistanceMod( ResistanceType.Physical, physiMod ),
|
||||
new ResistanceMod( ResistanceType.Fire, otherMod ),
|
||||
new ResistanceMod( ResistanceType.Cold, otherMod ),
|
||||
new ResistanceMod( ResistanceType.Poison, otherMod ),
|
||||
new ResistanceMod( ResistanceType.Energy, otherMod )
|
||||
};
|
||||
|
||||
m_Table[targ] = mods;
|
||||
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
targ.AddResistanceMod( mods[i] );
|
||||
|
||||
string buffFormat = String.Format( "{0}\t+{1}\t+{1}\t+{1}\t+{1}", physiMod, otherMod );
|
||||
|
||||
BuffInfo.AddBuff( targ, new BuffInfo( BuffIcon.MagicReflection, 1075817, buffFormat, true ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.PlaySound( 0x1ED );
|
||||
targ.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist );
|
||||
|
||||
m_Table.Remove( targ );
|
||||
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
targ.RemoveResistanceMod( mods[i] );
|
||||
|
||||
BuffInfo.RemoveBuff( targ, BuffIcon.MagicReflection );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Caster.MagicDamageAbsorb > 0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
if ( Caster.BeginAction( typeof( DefensiveSpell ) ) )
|
||||
{
|
||||
int value = (int)(Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Inscribe].Value);
|
||||
value = (int)(8 + (value/200)*7.0);//absorb from 8 to 15 "circles"
|
||||
|
||||
Caster.MagicDamageAbsorb = value;
|
||||
|
||||
Caster.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist );
|
||||
Caster.PlaySound( 0x1E9 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time.
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndReflect( Mobile m )
|
||||
{
|
||||
if ( m_Table.Contains( m ) )
|
||||
{
|
||||
ResistanceMod[] mods = (ResistanceMod[]) m_Table[ m ];
|
||||
|
||||
if ( mods != null )
|
||||
{
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
m.RemoveResistanceMod( mods[ i ] );
|
||||
}
|
||||
|
||||
m_Table.Remove( m );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.MagicReflection );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Scripts/Spells/Fifth/MindBlast.cs
Normal file
155
Scripts/Spells/Fifth/MindBlast.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class MindBlastSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Mind Blast", "Por Corp Wis",
|
||||
218,
|
||||
Core.AOS ? 9002 : 9032,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.Nightshade,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public MindBlastSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
if ( Core.AOS )
|
||||
m_Info.LeftHandEffect = m_Info.RightHandEffect = 9002;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
private void AosDelay_Callback( object state )
|
||||
{
|
||||
object[] states = (object[])state;
|
||||
Mobile caster = (Mobile)states[0];
|
||||
Mobile target = (Mobile)states[1];
|
||||
Mobile defender = (Mobile)states[2];
|
||||
int damage = (int)states[3];
|
||||
|
||||
if ( caster.HarmfulCheck( defender ) )
|
||||
{
|
||||
SpellHelper.Damage( this, target, Utility.RandomMinMax( damage, damage + 4 ), 0, 0, 100, 0, 0 );
|
||||
|
||||
target.FixedParticles( 0x374A, 10, 15, 5038, 1181, 2, EffectLayer.Head );
|
||||
target.PlaySound( 0x213 );
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return !Core.AOS; } }
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( Core.AOS )
|
||||
{
|
||||
if ( Caster.CanBeHarmful( m ) && CheckSequence() )
|
||||
{
|
||||
Mobile from = Caster, target = m;
|
||||
|
||||
SpellHelper.Turn( from, target );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, ref from, ref target );
|
||||
|
||||
int damage = (int)((Caster.Skills[SkillName.Magery].Value + Caster.Int) / 5);
|
||||
|
||||
if ( damage > 60 )
|
||||
damage = 60;
|
||||
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ),
|
||||
new TimerStateCallback( AosDelay_Callback ),
|
||||
new object[]{ Caster, target, m, damage } );
|
||||
}
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
Mobile from = Caster, target = m;
|
||||
|
||||
SpellHelper.Turn( from, target );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, ref from, ref target );
|
||||
|
||||
// Algorithm: (highestStat - lowestStat) / 2 [- 50% if resisted]
|
||||
|
||||
int highestStat = target.Str, lowestStat = target.Str;
|
||||
|
||||
if ( target.Dex > highestStat )
|
||||
highestStat = target.Dex;
|
||||
|
||||
if ( target.Dex < lowestStat )
|
||||
lowestStat = target.Dex;
|
||||
|
||||
if ( target.Int > highestStat )
|
||||
highestStat = target.Int;
|
||||
|
||||
if ( target.Int < lowestStat )
|
||||
lowestStat = target.Int;
|
||||
|
||||
if ( highestStat > 150 )
|
||||
highestStat = 150;
|
||||
|
||||
if ( lowestStat > 150 )
|
||||
lowestStat = 150;
|
||||
|
||||
double damage = GetDamageScalar(m)*(highestStat - lowestStat) / 2; // Many users prefer 3 or 4
|
||||
|
||||
if ( damage > 45 )
|
||||
damage = 45;
|
||||
|
||||
if ( CheckResisted( target ) )
|
||||
{
|
||||
damage /= 2;
|
||||
target.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
from.FixedParticles( 0x374A, 10, 15, 2038, EffectLayer.Head );
|
||||
|
||||
target.FixedParticles( 0x374A, 10, 15, 5038, EffectLayer.Head );
|
||||
target.PlaySound( 0x213 );
|
||||
|
||||
SpellHelper.Damage( this, target, damage, 0, 0, 100, 0, 0 );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public override double GetSlayerDamageScalar( Mobile target )
|
||||
{
|
||||
return 1.0; //This spell isn't affected by slayer spellbooks
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private MindBlastSpell m_Owner;
|
||||
|
||||
public InternalTarget( MindBlastSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
112
Scripts/Spells/Fifth/Paralyze.cs
Normal file
112
Scripts/Spells/Fifth/Paralyze.cs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Spells.Chivalry;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class ParalyzeSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Paralyze", "An Ex Por",
|
||||
218,
|
||||
9012,
|
||||
Reagent.Garlic,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public ParalyzeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( Core.AOS && (m.Frozen || m.Paralyzed || (m.Spell != null && m.Spell.IsCasting && !(m.Spell is PaladinSpell))) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061923 ); // The target is already frozen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
double duration;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
int secs = (int)((GetDamageSkill( Caster ) / 10) - (GetResistSkill( m ) / 10));
|
||||
|
||||
if( !Core.SE )
|
||||
secs += 2;
|
||||
|
||||
if ( !m.Player )
|
||||
secs *= 3;
|
||||
|
||||
if ( secs < 0 )
|
||||
secs = 0;
|
||||
|
||||
duration = secs;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Algorithm: ((20% of magery) + 7) seconds [- 50% if resisted]
|
||||
|
||||
duration = 7.0 + (Caster.Skills[SkillName.Magery].Value * 0.2);
|
||||
|
||||
if ( CheckResisted( m ) )
|
||||
duration *= 0.75;
|
||||
}
|
||||
|
||||
if ( m is PlagueBeastLord )
|
||||
{
|
||||
( (PlagueBeastLord) m ).OnParalyzed( Caster );
|
||||
duration = 120;
|
||||
}
|
||||
|
||||
m.Paralyze( TimeSpan.FromSeconds( duration ) );
|
||||
|
||||
m.PlaySound( 0x204 );
|
||||
m.FixedEffect( 0x376A, 6, 1 );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private ParalyzeSpell m_Owner;
|
||||
|
||||
public InternalTarget( ParalyzeSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
301
Scripts/Spells/Fifth/PoisonField.cs
Normal file
301
Scripts/Spells/Fifth/PoisonField.cs
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Misc;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class PoisonFieldSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Poison Field", "In Nox Grav",
|
||||
230,
|
||||
9052,
|
||||
false,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.Nightshade,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public PoisonFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( !Caster.CanSee( p ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, p );
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
int dx = Caster.Location.X - p.X;
|
||||
int dy = Caster.Location.Y - p.Y;
|
||||
int rx = (dx - dy) * 44;
|
||||
int ry = (dx + dy) * 44;
|
||||
|
||||
bool eastToWest;
|
||||
|
||||
if ( rx >= 0 && ry >= 0 )
|
||||
{
|
||||
eastToWest = false;
|
||||
}
|
||||
else if ( rx >= 0 )
|
||||
{
|
||||
eastToWest = true;
|
||||
}
|
||||
else if ( ry >= 0 )
|
||||
{
|
||||
eastToWest = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
eastToWest = false;
|
||||
}
|
||||
|
||||
Effects.PlaySound( p, Caster.Map, 0x20B );
|
||||
|
||||
int itemID = eastToWest ? 0x3915 : 0x3922;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( 3 + (Caster.Skills.Magery.Fixed / 25) );
|
||||
|
||||
for ( int i = -2; i <= 2; ++i )
|
||||
{
|
||||
Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z );
|
||||
|
||||
new InternalItem( itemID, loc, Caster, Caster.Map, duration, i );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
[DispellableField]
|
||||
public class InternalItem : Item
|
||||
{
|
||||
private Timer m_Timer;
|
||||
private DateTime m_End;
|
||||
private Mobile m_Caster;
|
||||
|
||||
public override bool BlocksFit{ get{ return true; } }
|
||||
|
||||
public InternalItem( int itemID, Point3D loc, Mobile caster, Map map, TimeSpan duration, int val ) : base( itemID )
|
||||
{
|
||||
bool canFit = SpellHelper.AdjustField( ref loc, map, 12, false );
|
||||
|
||||
Visible = false;
|
||||
Movable = false;
|
||||
Light = LightType.Circle300;
|
||||
|
||||
MoveToWorld( loc, map );
|
||||
|
||||
m_Caster = caster;
|
||||
|
||||
m_End = DateTime.UtcNow + duration;
|
||||
|
||||
m_Timer = new InternalTimer( this, TimeSpan.FromSeconds( Math.Abs( val ) * 0.2 ), caster.InLOS( this ), canFit );
|
||||
m_Timer.Start();
|
||||
}
|
||||
|
||||
public override void OnAfterDelete()
|
||||
{
|
||||
base.OnAfterDelete();
|
||||
|
||||
if ( m_Timer != null )
|
||||
m_Timer.Stop();
|
||||
}
|
||||
|
||||
public InternalItem( Serial serial ) : base( serial )
|
||||
{
|
||||
}
|
||||
|
||||
public override void Serialize( GenericWriter writer )
|
||||
{
|
||||
base.Serialize( writer );
|
||||
|
||||
writer.Write( (int) 1 ); // version
|
||||
|
||||
writer.Write( m_Caster );
|
||||
writer.WriteDeltaTime( m_End );
|
||||
}
|
||||
|
||||
public override void Deserialize( GenericReader reader )
|
||||
{
|
||||
base.Deserialize( reader );
|
||||
|
||||
int version = reader.ReadInt();
|
||||
|
||||
switch ( version )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
m_Caster = reader.ReadMobile();
|
||||
|
||||
goto case 0;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
m_End = reader.ReadDeltaTime();
|
||||
|
||||
m_Timer = new InternalTimer( this, TimeSpan.Zero, true, true );
|
||||
m_Timer.Start();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyPoisonTo( Mobile m )
|
||||
{
|
||||
if ( m_Caster == null )
|
||||
return;
|
||||
|
||||
Poison p;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
int total = (m_Caster.Skills.Magery.Fixed + m_Caster.Skills.Poisoning.Fixed) / 2;
|
||||
|
||||
if ( total >= 1000 )
|
||||
p = Poison.Deadly;
|
||||
else if ( total > 850 )
|
||||
p = Poison.Greater;
|
||||
else if ( total > 650 )
|
||||
p = Poison.Regular;
|
||||
else
|
||||
p = Poison.Lesser;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = Poison.Regular;
|
||||
}
|
||||
|
||||
if ( m.ApplyPoison( m_Caster, p ) == ApplyPoisonResult.Poisoned )
|
||||
if ( SpellHelper.CanRevealCaster( m ) )
|
||||
m_Caster.RevealingAction();
|
||||
|
||||
if ( m is BaseCreature )
|
||||
( (BaseCreature) m ).OnHarmfulSpell( m_Caster );
|
||||
}
|
||||
|
||||
public override bool OnMoveOver( Mobile m )
|
||||
{
|
||||
if ( Visible && m_Caster != null && (!Core.AOS || m != m_Caster) && SpellHelper.ValidIndirectTarget( m_Caster, m ) && m_Caster.CanBeHarmful( m, false ) )
|
||||
{
|
||||
m_Caster.DoHarmful( m );
|
||||
|
||||
ApplyPoisonTo( m );
|
||||
m.PlaySound( 0x474 );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private InternalItem m_Item;
|
||||
private bool m_InLOS, m_CanFit;
|
||||
|
||||
private static Queue m_Queue = new Queue();
|
||||
|
||||
public InternalTimer( InternalItem item, TimeSpan delay, bool inLOS, bool canFit ) : base( delay, TimeSpan.FromSeconds( 1.5 ) )
|
||||
{
|
||||
m_Item = item;
|
||||
m_InLOS = inLOS;
|
||||
m_CanFit = canFit;
|
||||
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Item.Deleted )
|
||||
return;
|
||||
|
||||
if ( !m_Item.Visible )
|
||||
{
|
||||
if ( m_InLOS && m_CanFit )
|
||||
m_Item.Visible = true;
|
||||
else
|
||||
m_Item.Delete();
|
||||
|
||||
if ( !m_Item.Deleted )
|
||||
{
|
||||
m_Item.ProcessDelta();
|
||||
Effects.SendLocationParticles( EffectItem.Create( m_Item.Location, m_Item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5040 );
|
||||
}
|
||||
}
|
||||
else if ( DateTime.UtcNow > m_Item.m_End )
|
||||
{
|
||||
m_Item.Delete();
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
Map map = m_Item.Map;
|
||||
Mobile caster = m_Item.m_Caster;
|
||||
|
||||
if ( map != null && caster != null )
|
||||
{
|
||||
bool eastToWest = ( m_Item.ItemID == 0x3915 );
|
||||
IPooledEnumerable eable = map.GetMobilesInBounds( new Rectangle2D( m_Item.X - (eastToWest ? 0 : 1), m_Item.Y - (eastToWest ? 1 : 0), (eastToWest ? 1 : 2), (eastToWest ? 2 : 1) ) );
|
||||
|
||||
foreach ( Mobile m in eable )
|
||||
{
|
||||
if ( (m.Z + 16) > m_Item.Z && (m_Item.Z + 12) > m.Z && (!Core.AOS || m != caster) && SpellHelper.ValidIndirectTarget( caster, m ) && caster.CanBeHarmful( m, false ) )
|
||||
m_Queue.Enqueue( m );
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
while ( m_Queue.Count > 0 )
|
||||
{
|
||||
Mobile m = (Mobile)m_Queue.Dequeue();
|
||||
|
||||
caster.DoHarmful( m );
|
||||
|
||||
m_Item.ApplyPoisonTo( m );
|
||||
m.PlaySound( 0x474 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private PoisonFieldSpell m_Owner;
|
||||
|
||||
public InternalTarget( PoisonFieldSpell owner ) : base( Core.ML ? 10 : 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is IPoint3D )
|
||||
m_Owner.Target( (IPoint3D)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Scripts/Spells/Fifth/SummonCreature.cs
Normal file
98
Scripts/Spells/Fifth/SummonCreature.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Fifth
|
||||
{
|
||||
public class SummonCreatureSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Summon Creature", "Kal Xen",
|
||||
16,
|
||||
false,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fifth; } }
|
||||
|
||||
public SummonCreatureSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
// NOTE: Creature list based on 1hr of summon/release on OSI.
|
||||
|
||||
private static Type[] m_Types = new Type[]
|
||||
{
|
||||
typeof( PolarBear ),
|
||||
typeof( GrizzlyBear ),
|
||||
typeof( BlackBear ),
|
||||
typeof( Horse ),
|
||||
typeof( Walrus ),
|
||||
typeof( Chicken ),
|
||||
typeof( Scorpion ),
|
||||
typeof( GiantSerpent ),
|
||||
typeof( Llama ),
|
||||
typeof( Alligator ),
|
||||
typeof( GreyWolf ),
|
||||
typeof( Slime ),
|
||||
typeof( Eagle ),
|
||||
typeof( Gorilla ),
|
||||
typeof( SnowLeopard ),
|
||||
typeof( Pig ),
|
||||
typeof( Hind ),
|
||||
typeof( Rabbit )
|
||||
};
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + 2) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
try
|
||||
{
|
||||
BaseCreature creature = (BaseCreature)Activator.CreateInstance( m_Types[Utility.Random( m_Types.Length )] );
|
||||
|
||||
//creature.ControlSlots = 2;
|
||||
|
||||
TimeSpan duration;
|
||||
|
||||
if ( Core.AOS )
|
||||
duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 );
|
||||
else
|
||||
duration = TimeSpan.FromSeconds( 4.0 * Caster.Skills[SkillName.Magery].Value );
|
||||
|
||||
SpellHelper.Summon( creature, Caster, 0x215, duration, false, false );
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public override TimeSpan GetCastDelay()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
return TimeSpan.FromTicks( base.GetCastDelay().Ticks * 5 );
|
||||
|
||||
return base.GetCastDelay() + TimeSpan.FromSeconds( 6.0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Scripts/Spells/First/Clumsy.cs
Normal file
84
Scripts/Spells/First/Clumsy.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class ClumsySpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Clumsy", "Uus Jux",
|
||||
212,
|
||||
9031,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public ClumsySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
SpellHelper.AddStatCurse( Caster, m, StatType.Dex );
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.Paralyzed = false;
|
||||
|
||||
m.FixedParticles( 0x3779, 10, 15, 5002, EffectLayer.Head );
|
||||
m.PlaySound( 0x1DF );
|
||||
|
||||
int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, true )*100);
|
||||
TimeSpan length = SpellHelper.GetDuration( Caster, m );
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Clumsy, 1075831, length, m, percentage.ToString() ) );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private ClumsySpell m_Owner;
|
||||
|
||||
public InternalTarget( ClumsySpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
Scripts/Spells/First/CreateFood.cs
Normal file
90
Scripts/Spells/First/CreateFood.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class CreateFoodSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Create Food", "In Mani Ylem",
|
||||
224,
|
||||
9011,
|
||||
Reagent.Garlic,
|
||||
Reagent.Ginseng,
|
||||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public CreateFoodSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
private static FoodInfo[] m_Food = new FoodInfo[]
|
||||
{
|
||||
new FoodInfo( typeof( Grapes ), "a grape bunch" ),
|
||||
new FoodInfo( typeof( Ham ), "a ham" ),
|
||||
new FoodInfo( typeof( CheeseWedge ), "a wedge of cheese" ),
|
||||
new FoodInfo( typeof( Muffins ), "muffins" ),
|
||||
new FoodInfo( typeof( FishSteak ), "a fish steak" ),
|
||||
new FoodInfo( typeof( Ribs ), "cut of ribs" ),
|
||||
new FoodInfo( typeof( CookedBird ), "a cooked bird" ),
|
||||
new FoodInfo( typeof( Sausage ), "sausage" ),
|
||||
new FoodInfo( typeof( Apple ), "an apple" ),
|
||||
new FoodInfo( typeof( Peach ), "a peach" )
|
||||
};
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
FoodInfo foodInfo = m_Food[Utility.Random( m_Food.Length )];
|
||||
Item food = foodInfo.Create();
|
||||
|
||||
if ( food != null )
|
||||
{
|
||||
Caster.AddToBackpack( food );
|
||||
|
||||
// You magically create food in your backpack:
|
||||
Caster.SendLocalizedMessage( 1042695, true, " " + foodInfo.Name );
|
||||
|
||||
Caster.FixedParticles( 0, 10, 5, 2003, EffectLayer.RightHand );
|
||||
Caster.PlaySound( 0x1E2 );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
|
||||
public class FoodInfo
|
||||
{
|
||||
private Type m_Type;
|
||||
private string m_Name;
|
||||
|
||||
public Type Type{ get{ return m_Type; } set{ m_Type = value; } }
|
||||
public string Name{ get{ return m_Name; } set{ m_Name = value; } }
|
||||
|
||||
public FoodInfo( Type type, string name )
|
||||
{
|
||||
m_Type = type;
|
||||
m_Name = name;
|
||||
}
|
||||
|
||||
public Item Create()
|
||||
{
|
||||
Item item;
|
||||
|
||||
try
|
||||
{
|
||||
item = (Item)Activator.CreateInstance( m_Type );
|
||||
}
|
||||
catch
|
||||
{
|
||||
item = null;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Scripts/Spells/First/Feeblemind.cs
Normal file
84
Scripts/Spells/First/Feeblemind.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class FeeblemindSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Feeblemind", "Rel Wis",
|
||||
212,
|
||||
9031,
|
||||
Reagent.Ginseng,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public FeeblemindSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
SpellHelper.AddStatCurse( Caster, m, StatType.Int );
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.Paralyzed = false;
|
||||
|
||||
m.FixedParticles( 0x3779, 10, 15, 5004, EffectLayer.Head );
|
||||
m.PlaySound( 0x1E4 );
|
||||
|
||||
int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, true )*100);
|
||||
TimeSpan length = SpellHelper.GetDuration( Caster, m );
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.FeebleMind, 1075833, length, m, percentage.ToString() ) );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private FeeblemindSpell m_Owner;
|
||||
|
||||
public InternalTarget( FeeblemindSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
Scripts/Spells/First/Heal.cs
Normal file
117
Scripts/Spells/First/Heal.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class HealSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Heal", "In Mani",
|
||||
224,
|
||||
9061,
|
||||
Reagent.Garlic,
|
||||
Reagent.Ginseng,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public HealSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( m.IsDeadBondedPet )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060177 ); // You cannot heal a creature that is already dead!
|
||||
}
|
||||
else if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061654 ); // You cannot heal that which is not alive.
|
||||
}
|
||||
else if ( m is Golem )
|
||||
{
|
||||
Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500951 ); // You cannot heal that.
|
||||
}
|
||||
else if ( m.Poisoned || Server.Items.MortalStrike.IsWounded( m ) )
|
||||
{
|
||||
Caster.LocalOverheadMessage( MessageType.Regular, 0x22, (Caster == m) ? 1005000 : 1010398 );
|
||||
}
|
||||
else if ( CheckBSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
int toHeal;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
toHeal = Caster.Skills.Magery.Fixed / 120;
|
||||
toHeal += Utility.RandomMinMax( 1, 4 );
|
||||
|
||||
if( Core.SE && Caster != m )
|
||||
toHeal = (int)(toHeal * 1.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
toHeal = (int)(Caster.Skills[SkillName.Magery].Value * 0.1);
|
||||
toHeal += Utility.Random( 1, 5 );
|
||||
}
|
||||
|
||||
//m.Heal( toHeal, Caster );
|
||||
SpellHelper.Heal( toHeal, m, Caster );
|
||||
|
||||
m.FixedParticles( 0x376A, 9, 32, 5005, EffectLayer.Waist );
|
||||
m.PlaySound( 0x1F2 );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private HealSpell m_Owner;
|
||||
|
||||
public InternalTarget( HealSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Scripts/Spells/First/MagicArrow.cs
Normal file
97
Scripts/Spells/First/MagicArrow.cs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class MagicArrowSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Magic Arrow", "In Por Ylem",
|
||||
212,
|
||||
9041,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public MagicArrowSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool DelayedDamageStacking { get { return !Core.AOS; } }
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return true; } }
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
Mobile source = Caster;
|
||||
|
||||
SpellHelper.Turn( source, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, ref source, ref m );
|
||||
|
||||
double damage;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
damage = GetNewAosDamage( 10, 1, 4, m );
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = Utility.Random( 4, 4 );
|
||||
|
||||
if ( CheckResisted( m ) )
|
||||
{
|
||||
damage *= 0.75;
|
||||
|
||||
m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
damage *= GetDamageScalar( m );
|
||||
}
|
||||
|
||||
source.MovingParticles( m, 0x36E4, 5, 0, false, false, 3006, 0, 0 );
|
||||
source.PlaySound( 0x1E5 );
|
||||
|
||||
SpellHelper.Damage( this, m, damage, 0, 100, 0, 0, 0 );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private MagicArrowSpell m_Owner;
|
||||
|
||||
public InternalTarget( MagicArrowSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Scripts/Spells/First/NightSight.cs
Normal file
76
Scripts/Spells/First/NightSight.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class NightSightSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Night Sight", "In Lor",
|
||||
236,
|
||||
9031,
|
||||
Reagent.SulfurousAsh,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public NightSightSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new NightSightTarget( this );
|
||||
}
|
||||
|
||||
private class NightSightTarget : Target
|
||||
{
|
||||
private Spell m_Spell;
|
||||
|
||||
public NightSightTarget( Spell spell ) : base( 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Spell = spell;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object targeted )
|
||||
{
|
||||
if ( targeted is Mobile && m_Spell.CheckBSequence( (Mobile) targeted ) )
|
||||
{
|
||||
Mobile targ = (Mobile)targeted;
|
||||
|
||||
SpellHelper.Turn( m_Spell.Caster, targ );
|
||||
|
||||
if ( targ.BeginAction( typeof( LightCycle ) ) )
|
||||
{
|
||||
new LightCycle.NightSightTimer( targ ).Start();
|
||||
int level = (int)( LightCycle.DungeonLevel * ( (Core.AOS ? targ.Skills[SkillName.Magery].Value : from.Skills[SkillName.Magery].Value )/ 100 ) );
|
||||
|
||||
if ( level < 0 )
|
||||
level = 0;
|
||||
|
||||
targ.LightLevel = level;
|
||||
|
||||
targ.FixedParticles( 0x376A, 9, 32, 5007, EffectLayer.Waist );
|
||||
targ.PlaySound( 0x1E3 );
|
||||
|
||||
BuffInfo.AddBuff( targ, new BuffInfo( BuffIcon.NightSight, 1075643 ) ); //Night Sight/You ignore lighting effects
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendMessage( "{0} already have nightsight.", from == targ ? "You" : "They" );
|
||||
}
|
||||
}
|
||||
|
||||
m_Spell.FinishSequence();
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Spell.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
Scripts/Spells/First/ReactiveArmor.cs
Normal file
158
Scripts/Spells/First/ReactiveArmor.cs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class ReactiveArmorSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Reactive Armor", "Flam Sanct",
|
||||
236,
|
||||
9011,
|
||||
Reagent.Garlic,
|
||||
Reagent.SpidersSilk,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public ReactiveArmorSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
return true;
|
||||
|
||||
if ( Caster.MeleeDamageAbsorb > 0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
return false;
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( Core.AOS )
|
||||
{
|
||||
/* The reactive armor spell increases the caster's physical resistance, while lowering the caster's elemental resistances.
|
||||
* 15 + (Inscription/20) Physcial bonus
|
||||
* -5 Elemental
|
||||
* The reactive armor spell has an indefinite duration, becoming active when cast, and deactivated when re-cast.
|
||||
* Reactive Armor, Protection, and Magic Reflection will stay on—even after logging out, even after dying—until you “turn them off” by casting them again.
|
||||
* (+20 physical -5 elemental at 100 Inscription)
|
||||
*/
|
||||
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Mobile targ = Caster;
|
||||
|
||||
ResistanceMod[] mods = (ResistanceMod[])m_Table[targ];
|
||||
|
||||
if ( mods == null )
|
||||
{
|
||||
targ.PlaySound( 0x1E9 );
|
||||
targ.FixedParticles( 0x376A, 9, 32, 5008, EffectLayer.Waist );
|
||||
|
||||
mods = new ResistanceMod[5]
|
||||
{
|
||||
new ResistanceMod( ResistanceType.Physical, 15 + (int)(targ.Skills[SkillName.Inscribe].Value / 20) ),
|
||||
new ResistanceMod( ResistanceType.Fire, -5 ),
|
||||
new ResistanceMod( ResistanceType.Cold, -5 ),
|
||||
new ResistanceMod( ResistanceType.Poison, -5 ),
|
||||
new ResistanceMod( ResistanceType.Energy, -5 )
|
||||
};
|
||||
|
||||
m_Table[targ] = mods;
|
||||
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
targ.AddResistanceMod( mods[i] );
|
||||
|
||||
int physresist = 15 + (int)(targ.Skills[SkillName.Inscribe].Value / 20);
|
||||
string args = String.Format("{0}\t{1}\t{2}\t{3}\t{4}", physresist, 5, 5, 5, 5);
|
||||
|
||||
BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.ReactiveArmor, 1075812, 1075813, args.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.PlaySound( 0x1ED );
|
||||
targ.FixedParticles( 0x376A, 9, 32, 5008, EffectLayer.Waist );
|
||||
|
||||
m_Table.Remove( targ );
|
||||
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
targ.RemoveResistanceMod( mods[i] );
|
||||
|
||||
BuffInfo.RemoveBuff(Caster, BuffIcon.ReactiveArmor);
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Caster.MeleeDamageAbsorb > 0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
if ( Caster.BeginAction( typeof( DefensiveSpell ) ) )
|
||||
{
|
||||
int value = (int)(Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Meditation].Value + Caster.Skills[SkillName.Inscribe].Value);
|
||||
value /= 3;
|
||||
|
||||
if ( value < 0 )
|
||||
value = 1;
|
||||
else if ( value > 75 )
|
||||
value = 75;
|
||||
|
||||
Caster.MeleeDamageAbsorb = value;
|
||||
|
||||
Caster.FixedParticles( 0x376A, 9, 32, 5008, EffectLayer.Waist );
|
||||
Caster.PlaySound( 0x1F2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time.
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndArmor( Mobile m )
|
||||
{
|
||||
if ( m_Table.Contains( m ) )
|
||||
{
|
||||
ResistanceMod[] mods = (ResistanceMod[]) m_Table[ m ];
|
||||
|
||||
if ( mods != null )
|
||||
{
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
m.RemoveResistanceMod( mods[ i ] );
|
||||
}
|
||||
|
||||
m_Table.Remove( m );
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.ReactiveArmor );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Scripts/Spells/First/Weaken.cs
Normal file
84
Scripts/Spells/First/Weaken.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.First
|
||||
{
|
||||
public class WeakenSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Weaken", "Des Mani",
|
||||
212,
|
||||
9031,
|
||||
Reagent.Garlic,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.First; } }
|
||||
|
||||
public WeakenSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
SpellHelper.AddStatCurse( Caster, m, StatType.Str );
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.Paralyzed = false;
|
||||
|
||||
m.FixedParticles( 0x3779, 10, 15, 5009, EffectLayer.Waist );
|
||||
m.PlaySound( 0x1E6 );
|
||||
|
||||
int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, true )*100);
|
||||
TimeSpan length = SpellHelper.GetDuration( Caster, m );
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Weaken, 1075837, length, m, percentage.ToString() ) );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private WeakenSpell m_Owner;
|
||||
|
||||
public InternalTarget( WeakenSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
Scripts/Spells/Fourth/ArchCure.cs
Normal file
190
Scripts/Spells/Fourth/ArchCure.cs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class ArchCureSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Arch Cure", "Vas An Nox",
|
||||
215,
|
||||
9061,
|
||||
Reagent.Garlic,
|
||||
Reagent.Ginseng,
|
||||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public ArchCureSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
// Arch cure is now 1/4th of a second faster
|
||||
public override TimeSpan CastDelayBase{ get{ return base.CastDelayBase - TimeSpan.FromSeconds( 0.25 ); } }
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( !Caster.CanSee( p ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, p );
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
Map map = Caster.Map;
|
||||
Mobile directTarget = p as Mobile;
|
||||
|
||||
if ( map != null )
|
||||
{
|
||||
bool feluccaRules = ( map.Rules == MapRules.FeluccaRules );
|
||||
|
||||
// You can target any living mobile directly, beneficial checks apply
|
||||
if ( directTarget != null && Caster.CanBeBeneficial( directTarget, false ) )
|
||||
targets.Add( directTarget );
|
||||
|
||||
IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 2 );
|
||||
|
||||
foreach ( Mobile m in eable )
|
||||
{
|
||||
if ( m == directTarget )
|
||||
continue;
|
||||
|
||||
if ( AreaCanTarget( m, feluccaRules ) )
|
||||
targets.Add( m );
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
Effects.PlaySound( p, Caster.Map, 0x299 );
|
||||
|
||||
if ( targets.Count > 0 )
|
||||
{
|
||||
int cured = 0;
|
||||
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
Caster.DoBeneficial( m );
|
||||
|
||||
Poison poison = m.Poison;
|
||||
|
||||
if ( poison != null )
|
||||
{
|
||||
int chanceToCure = 10000 + (int)(Caster.Skills[SkillName.Magery].Value * 75) - ((poison.Level + 1) * 1750);
|
||||
chanceToCure /= 100;
|
||||
chanceToCure -= 1;
|
||||
|
||||
if ( chanceToCure > Utility.Random( 100 ) && m.CurePoison( Caster ) )
|
||||
++cured;
|
||||
}
|
||||
|
||||
m.FixedParticles( 0x373A, 10, 15, 5012, EffectLayer.Waist );
|
||||
m.PlaySound( 0x1E0 );
|
||||
}
|
||||
|
||||
if ( cured > 0 )
|
||||
Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons!
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private bool AreaCanTarget( Mobile target, bool feluccaRules )
|
||||
{
|
||||
/* Arch cure area effect won't cure aggressors, victims, murderers, criminals or monsters.
|
||||
* In Felucca, it will also not cure summons and pets.
|
||||
* For red players it will only cure themselves and guild members.
|
||||
*/
|
||||
|
||||
if ( !Caster.CanBeBeneficial( target, false ) )
|
||||
return false;
|
||||
|
||||
if ( Core.AOS && target != Caster )
|
||||
{
|
||||
if ( IsAggressor( target ) || IsAggressed( target ) )
|
||||
return false;
|
||||
|
||||
if ( ( !IsInnocentTo( Caster, target ) || !IsInnocentTo( target, Caster ) ) && !IsAllyTo( Caster, target ) )
|
||||
return false;
|
||||
|
||||
if ( feluccaRules && !( target is PlayerMobile ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsAggressor( Mobile m )
|
||||
{
|
||||
foreach ( AggressorInfo info in Caster.Aggressors )
|
||||
{
|
||||
if ( m == info.Attacker && !info.Expired )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsAggressed( Mobile m )
|
||||
{
|
||||
foreach ( AggressorInfo info in Caster.Aggressed )
|
||||
{
|
||||
if ( m == info.Defender && !info.Expired )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsInnocentTo( Mobile from, Mobile to )
|
||||
{
|
||||
return ( Notoriety.Compute( from, (Mobile)to ) == Notoriety.Innocent );
|
||||
}
|
||||
|
||||
private static bool IsAllyTo( Mobile from, Mobile to )
|
||||
{
|
||||
return ( Notoriety.Compute( from, (Mobile)to ) == Notoriety.Ally );
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private ArchCureSpell m_Owner;
|
||||
|
||||
public InternalTarget( ArchCureSpell owner ) : base( Core.ML ? 10 : 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
IPoint3D p = o as IPoint3D;
|
||||
|
||||
if ( p != null )
|
||||
m_Owner.Target( p );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
171
Scripts/Spells/Fourth/ArchProtection.cs
Normal file
171
Scripts/Spells/Fourth/ArchProtection.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Engines.PartySystem;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class ArchProtectionSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Arch Protection", "Vas Uus Sanct",
|
||||
Core.AOS ? 239 : 215,
|
||||
9011,
|
||||
Reagent.Garlic,
|
||||
Reagent.Ginseng,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public ArchProtectionSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( !Caster.CanSee( p ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, p );
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
Map map = Caster.Map;
|
||||
|
||||
if ( map != null )
|
||||
{
|
||||
IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), Core.AOS ? 2 : 3 );
|
||||
|
||||
foreach ( Mobile m in eable )
|
||||
{
|
||||
if ( Caster.CanBeBeneficial( m, false ) )
|
||||
targets.Add( m );
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
}
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
Party party = Party.Get( Caster );
|
||||
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
if ( m == Caster || ( party != null && party.Contains( m ) ) )
|
||||
{
|
||||
Caster.DoBeneficial( m );
|
||||
Spells.Second.ProtectionSpell.Toggle( Caster, m );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Effects.PlaySound( p, Caster.Map, 0x299 );
|
||||
|
||||
int val = (int)(Caster.Skills[SkillName.Magery].Value/10.0 + 1);
|
||||
|
||||
if ( targets.Count > 0 )
|
||||
{
|
||||
for ( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
if ( m.BeginAction( typeof( ArchProtectionSpell ) ) )
|
||||
{
|
||||
Caster.DoBeneficial( m );
|
||||
m.VirtualArmorMod += val;
|
||||
|
||||
AddEntry( m, val );
|
||||
new InternalTimer( m, Caster ).Start();
|
||||
|
||||
m.FixedParticles( 0x375A, 9, 20, 5027, EffectLayer.Waist );
|
||||
m.PlaySound( 0x1F7 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, Int32> _Table = new Dictionary<Mobile, Int32>();
|
||||
|
||||
private static void AddEntry( Mobile m, Int32 v )
|
||||
{
|
||||
_Table[m] = v;
|
||||
}
|
||||
|
||||
public static void RemoveEntry( Mobile m )
|
||||
{
|
||||
if ( _Table.ContainsKey( m ) ) {
|
||||
int v = _Table[m];
|
||||
_Table.Remove( m );
|
||||
m.EndAction( typeof( ArchProtectionSpell ) );
|
||||
m.VirtualArmorMod -= v;
|
||||
if ( m.VirtualArmorMod < 0 )
|
||||
m.VirtualArmorMod = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Owner;
|
||||
|
||||
public InternalTimer( Mobile target, Mobile caster ) : base( TimeSpan.FromSeconds( 0 ) )
|
||||
{
|
||||
double time = caster.Skills[SkillName.Magery].Value * 1.2;
|
||||
if ( time > 144 )
|
||||
time = 144;
|
||||
Delay = TimeSpan.FromSeconds( time );
|
||||
Priority = TimerPriority.OneSecond;
|
||||
|
||||
m_Owner = target;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
ArchProtectionSpell.RemoveEntry( m_Owner );
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private ArchProtectionSpell m_Owner;
|
||||
|
||||
public InternalTarget( ArchProtectionSpell owner ) : base( Core.ML ? 10 : 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
IPoint3D p = o as IPoint3D;
|
||||
|
||||
if ( p != null )
|
||||
m_Owner.Target( p );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Scripts/Spells/Fourth/Curse.cs
Normal file
113
Scripts/Spells/Fourth/Curse.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class CurseSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Curse", "Des Sanct",
|
||||
227,
|
||||
9031,
|
||||
Reagent.Nightshade,
|
||||
Reagent.Garlic,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public CurseSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
private static Hashtable m_UnderEffect = new Hashtable();
|
||||
|
||||
public static void RemoveEffect( object state )
|
||||
{
|
||||
Mobile m = (Mobile)state;
|
||||
|
||||
m_UnderEffect.Remove( m );
|
||||
|
||||
m.UpdateResistances();
|
||||
}
|
||||
|
||||
public static bool UnderEffect( Mobile m )
|
||||
{
|
||||
return m_UnderEffect.Contains( m );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
SpellHelper.AddStatCurse( Caster, m, StatType.Str ); SpellHelper.DisableSkillCheck = true;
|
||||
SpellHelper.AddStatCurse( Caster, m, StatType.Dex );
|
||||
SpellHelper.AddStatCurse( Caster, m, StatType.Int ); SpellHelper.DisableSkillCheck = false;
|
||||
|
||||
Timer t = (Timer)m_UnderEffect[m];
|
||||
|
||||
if ( Caster.Player && m.Player /*&& Caster != m */ && t == null ) //On OSI you CAN curse yourself and get this effect.
|
||||
{
|
||||
TimeSpan duration = SpellHelper.GetDuration( Caster, m );
|
||||
m_UnderEffect[m] = t = Timer.DelayCall( duration, new TimerStateCallback( RemoveEffect ), m );
|
||||
m.UpdateResistances();
|
||||
}
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.Paralyzed = false;
|
||||
|
||||
m.FixedParticles( 0x374A, 10, 15, 5028, EffectLayer.Waist );
|
||||
m.PlaySound( 0x1E1 );
|
||||
|
||||
int percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100);
|
||||
TimeSpan length = SpellHelper.GetDuration(Caster, m);
|
||||
|
||||
string args = String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", percentage, percentage, percentage, 10, 10, 10, 10);
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Curse, 1075835, 1075836, length, m, args.ToString() ) );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private CurseSpell m_Owner;
|
||||
|
||||
public InternalTarget( CurseSpell owner ) : base( Core.ML? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
315
Scripts/Spells/Fourth/FireField.cs
Normal file
315
Scripts/Spells/Fourth/FireField.cs
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Misc;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class FireFieldSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Fire Field", "In Flam Grav",
|
||||
215,
|
||||
9041,
|
||||
false,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.SpidersSilk,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public FireFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( !Caster.CanSee( p ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, p );
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
int dx = Caster.Location.X - p.X;
|
||||
int dy = Caster.Location.Y - p.Y;
|
||||
int rx = (dx - dy) * 44;
|
||||
int ry = (dx + dy) * 44;
|
||||
|
||||
bool eastToWest;
|
||||
|
||||
if ( rx >= 0 && ry >= 0 )
|
||||
{
|
||||
eastToWest = false;
|
||||
}
|
||||
else if ( rx >= 0 )
|
||||
{
|
||||
eastToWest = true;
|
||||
}
|
||||
else if ( ry >= 0 )
|
||||
{
|
||||
eastToWest = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
eastToWest = false;
|
||||
}
|
||||
|
||||
Effects.PlaySound( p, Caster.Map, 0x20C );
|
||||
|
||||
int itemID = eastToWest ? 0x398C : 0x3996;
|
||||
|
||||
TimeSpan duration;
|
||||
|
||||
if ( Core.AOS )
|
||||
duration = TimeSpan.FromSeconds( (15 + (Caster.Skills.Magery.Fixed / 5)) / 4 );
|
||||
else
|
||||
duration = TimeSpan.FromSeconds( 4.0 + (Caster.Skills[SkillName.Magery].Value * 0.5) );
|
||||
|
||||
for ( int i = -2; i <= 2; ++i )
|
||||
{
|
||||
Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z );
|
||||
|
||||
new FireFieldItem( itemID, loc, Caster, Caster.Map, duration, i );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
[DispellableField]
|
||||
public class FireFieldItem : Item
|
||||
{
|
||||
private Timer m_Timer;
|
||||
private DateTime m_End;
|
||||
private Mobile m_Caster;
|
||||
private int m_Damage;
|
||||
|
||||
public override bool BlocksFit{ get{ return true; } }
|
||||
|
||||
public FireFieldItem( int itemID, Point3D loc, Mobile caster, Map map, TimeSpan duration, int val )
|
||||
: this( itemID, loc, caster, map, duration, val, 2 )
|
||||
{
|
||||
}
|
||||
|
||||
public FireFieldItem( int itemID, Point3D loc, Mobile caster, Map map, TimeSpan duration, int val, int damage ) : base( itemID )
|
||||
{
|
||||
bool canFit = SpellHelper.AdjustField( ref loc, map, 12, false );
|
||||
|
||||
Visible = false;
|
||||
Movable = false;
|
||||
Light = LightType.Circle300;
|
||||
|
||||
MoveToWorld( loc, map );
|
||||
|
||||
m_Caster = caster;
|
||||
|
||||
m_Damage = damage;
|
||||
|
||||
m_End = DateTime.UtcNow + duration;
|
||||
|
||||
m_Timer = new InternalTimer( this, TimeSpan.FromSeconds( Math.Abs( val ) * 0.2 ), caster.InLOS( this ), canFit );
|
||||
m_Timer.Start();
|
||||
}
|
||||
|
||||
public override void OnAfterDelete()
|
||||
{
|
||||
base.OnAfterDelete();
|
||||
|
||||
if ( m_Timer != null )
|
||||
m_Timer.Stop();
|
||||
}
|
||||
|
||||
public FireFieldItem( Serial serial ) : base( serial )
|
||||
{
|
||||
}
|
||||
|
||||
public override void Serialize( GenericWriter writer )
|
||||
{
|
||||
base.Serialize( writer );
|
||||
|
||||
writer.Write( (int) 2 ); // version
|
||||
|
||||
writer.Write( m_Damage );
|
||||
writer.Write( m_Caster );
|
||||
writer.WriteDeltaTime( m_End );
|
||||
}
|
||||
|
||||
public override void Deserialize( GenericReader reader )
|
||||
{
|
||||
base.Deserialize( reader );
|
||||
|
||||
int version = reader.ReadInt();
|
||||
|
||||
switch ( version )
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
m_Damage = reader.ReadInt();
|
||||
goto case 1;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
m_Caster = reader.ReadMobile();
|
||||
|
||||
goto case 0;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
m_End = reader.ReadDeltaTime();
|
||||
|
||||
m_Timer = new InternalTimer( this, TimeSpan.Zero, true, true );
|
||||
m_Timer.Start();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( version < 2 )
|
||||
m_Damage = 2;
|
||||
}
|
||||
|
||||
public override bool OnMoveOver( Mobile m )
|
||||
{
|
||||
if ( Visible && m_Caster != null && (!Core.AOS || m != m_Caster) && SpellHelper.ValidIndirectTarget( m_Caster, m ) && m_Caster.CanBeHarmful( m, false ) )
|
||||
{
|
||||
if ( SpellHelper.CanRevealCaster( m ) )
|
||||
m_Caster.RevealingAction();
|
||||
|
||||
m_Caster.DoHarmful( m );
|
||||
|
||||
int damage = m_Damage;
|
||||
|
||||
if ( !Core.AOS && m.CheckSkill( SkillName.MagicResist, 0.0, 30.0 ) )
|
||||
{
|
||||
damage = 1;
|
||||
|
||||
m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
AOS.Damage( m, m_Caster, damage, 0, 100, 0, 0, 0 );
|
||||
m.PlaySound( 0x208 );
|
||||
|
||||
if ( m is BaseCreature )
|
||||
((BaseCreature) m).OnHarmfulSpell( m_Caster );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private FireFieldItem m_Item;
|
||||
private bool m_InLOS, m_CanFit;
|
||||
|
||||
private static Queue m_Queue = new Queue();
|
||||
|
||||
public InternalTimer( FireFieldItem item, TimeSpan delay, bool inLOS, bool canFit ) : base( delay, TimeSpan.FromSeconds( 1.0 ) )
|
||||
{
|
||||
m_Item = item;
|
||||
m_InLOS = inLOS;
|
||||
m_CanFit = canFit;
|
||||
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Item.Deleted )
|
||||
return;
|
||||
|
||||
if ( !m_Item.Visible )
|
||||
{
|
||||
if ( m_InLOS && m_CanFit )
|
||||
m_Item.Visible = true;
|
||||
else
|
||||
m_Item.Delete();
|
||||
|
||||
if ( !m_Item.Deleted )
|
||||
{
|
||||
m_Item.ProcessDelta();
|
||||
Effects.SendLocationParticles( EffectItem.Create( m_Item.Location, m_Item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5029 );
|
||||
}
|
||||
}
|
||||
else if ( DateTime.UtcNow > m_Item.m_End )
|
||||
{
|
||||
m_Item.Delete();
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
Map map = m_Item.Map;
|
||||
Mobile caster = m_Item.m_Caster;
|
||||
|
||||
if ( map != null && caster != null )
|
||||
{
|
||||
foreach ( Mobile m in m_Item.GetMobilesInRange( 0 ) )
|
||||
{
|
||||
if ( (m.Z + 16) > m_Item.Z && (m_Item.Z + 12) > m.Z && (!Core.AOS || m != caster) && SpellHelper.ValidIndirectTarget( caster, m ) && caster.CanBeHarmful( m, false ) )
|
||||
m_Queue.Enqueue( m );
|
||||
}
|
||||
|
||||
while ( m_Queue.Count > 0 )
|
||||
{
|
||||
Mobile m = (Mobile)m_Queue.Dequeue();
|
||||
|
||||
if ( SpellHelper.CanRevealCaster( m ) )
|
||||
caster.RevealingAction();
|
||||
|
||||
caster.DoHarmful( m );
|
||||
|
||||
int damage = m_Item.m_Damage;
|
||||
|
||||
if ( !Core.AOS && m.CheckSkill( SkillName.MagicResist, 0.0, 30.0 ) )
|
||||
{
|
||||
damage = 1;
|
||||
|
||||
m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
AOS.Damage( m, caster, damage, 0, 100, 0, 0, 0 );
|
||||
m.PlaySound( 0x208 );
|
||||
|
||||
if ( m is BaseCreature )
|
||||
((BaseCreature) m).OnHarmfulSpell( caster );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private FireFieldSpell m_Owner;
|
||||
|
||||
public InternalTarget( FireFieldSpell owner ) : base( Core.ML ? 10 : 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is IPoint3D )
|
||||
m_Owner.Target( (IPoint3D)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Scripts/Spells/Fourth/GreaterHeal.cs
Normal file
107
Scripts/Spells/Fourth/GreaterHeal.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class GreaterHealSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Greater Heal", "In Vas Mani",
|
||||
204,
|
||||
9061,
|
||||
Reagent.Garlic,
|
||||
Reagent.Ginseng,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public GreaterHealSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061654 ); // You cannot heal that which is not alive.
|
||||
}
|
||||
else if ( m.IsDeadBondedPet )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060177 ); // You cannot heal a creature that is already dead!
|
||||
}
|
||||
else if ( m is Golem )
|
||||
{
|
||||
Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500951 ); // You cannot heal that.
|
||||
}
|
||||
else if ( m.Poisoned || Server.Items.MortalStrike.IsWounded( m ) )
|
||||
{
|
||||
Caster.LocalOverheadMessage( MessageType.Regular, 0x22, (Caster == m) ? 1005000 : 1010398 );
|
||||
}
|
||||
else if ( CheckBSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
// Algorithm: (40% of magery) + (1-10)
|
||||
|
||||
int toHeal = (int)(Caster.Skills[SkillName.Magery].Value * 0.4);
|
||||
toHeal += Utility.Random( 1, 10 );
|
||||
|
||||
//m.Heal( toHeal, Caster );
|
||||
SpellHelper.Heal( toHeal, m, Caster );
|
||||
|
||||
m.FixedParticles( 0x376A, 9, 32, 5030, EffectLayer.Waist );
|
||||
m.PlaySound( 0x202 );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private GreaterHealSpell m_Owner;
|
||||
|
||||
public InternalTarget( GreaterHealSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Scripts/Spells/Fourth/Lightning.cs
Normal file
91
Scripts/Spells/Fourth/Lightning.cs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class LightningSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Lightning", "Por Ort Grav",
|
||||
239,
|
||||
9021,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public LightningSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return false; } }
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
double damage;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
damage = GetNewAosDamage( 23, 1, 4, m );
|
||||
}
|
||||
else
|
||||
{
|
||||
damage = Utility.Random( 12, 9 );
|
||||
|
||||
if ( CheckResisted( m ) )
|
||||
{
|
||||
damage *= 0.75;
|
||||
|
||||
m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy.
|
||||
}
|
||||
|
||||
damage *= GetDamageScalar( m );
|
||||
}
|
||||
|
||||
m.BoltEffect( 0 );
|
||||
|
||||
SpellHelper.Damage( this, m, damage, 0, 0, 0, 0, 100 );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private LightningSpell m_Owner;
|
||||
|
||||
public InternalTarget( LightningSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Scripts/Spells/Fourth/ManaDrain.cs
Normal file
134
Scripts/Spells/Fourth/ManaDrain.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class ManaDrainSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Mana Drain", "Ort Rel",
|
||||
215,
|
||||
9031,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.SpidersSilk
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
public ManaDrainSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, Timer> m_Table = new Dictionary<Mobile, Timer>();
|
||||
|
||||
private void AosDelay_Callback( object state )
|
||||
{
|
||||
object[] states = (object[])state;
|
||||
|
||||
Mobile m = (Mobile)states[0];
|
||||
int mana = (int)states[1];
|
||||
|
||||
if ( m.Alive && !m.IsDeadBondedPet )
|
||||
{
|
||||
m.Mana += mana;
|
||||
|
||||
m.FixedEffect( 0x3779, 10, 25 );
|
||||
m.PlaySound( 0x28E );
|
||||
}
|
||||
|
||||
m_Table.Remove( m );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m );
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.Paralyzed = false;
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
int toDrain = 40 + (int)(GetDamageSkill( Caster ) - GetResistSkill( m ));
|
||||
|
||||
if ( toDrain < 0 )
|
||||
toDrain = 0;
|
||||
else if ( toDrain > m.Mana )
|
||||
toDrain = m.Mana;
|
||||
|
||||
if ( m_Table.ContainsKey( m ) )
|
||||
toDrain = 0;
|
||||
|
||||
m.FixedParticles( 0x3789, 10, 25, 5032, EffectLayer.Head );
|
||||
m.PlaySound( 0x1F8 );
|
||||
|
||||
if ( toDrain > 0 )
|
||||
{
|
||||
m.Mana -= toDrain;
|
||||
|
||||
m_Table[m] = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( AosDelay_Callback ), new object[]{ m, toDrain } );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( CheckResisted( m ) )
|
||||
m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy.
|
||||
else if ( m.Mana >= 100 )
|
||||
m.Mana -= Utility.Random( 1, 100 );
|
||||
else
|
||||
m.Mana -= Utility.Random( 1, m.Mana );
|
||||
|
||||
m.FixedParticles( 0x374A, 10, 15, 5032, EffectLayer.Head );
|
||||
m.PlaySound( 0x1F8 );
|
||||
}
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public override double GetResistPercent( Mobile target )
|
||||
{
|
||||
return 99.0;
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private ManaDrainSpell m_Owner;
|
||||
|
||||
public InternalTarget( ManaDrainSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
207
Scripts/Spells/Fourth/Recall.cs
Normal file
207
Scripts/Spells/Fourth/Recall.cs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using Server.Items;
|
||||
using Server.Multis;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
using Server.Regions;
|
||||
using Server.Spells.Necromancy;
|
||||
|
||||
namespace Server.Spells.Fourth
|
||||
{
|
||||
public class RecallSpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Recall", "Kal Ort Por",
|
||||
239,
|
||||
9031,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Fourth; } }
|
||||
|
||||
private RunebookEntry m_Entry;
|
||||
private Runebook m_Book;
|
||||
|
||||
public RecallSpell( Mobile caster, Item scroll ) : this( caster, scroll, null, null )
|
||||
{
|
||||
}
|
||||
|
||||
public RecallSpell( Mobile caster, Item scroll, RunebookEntry entry, Runebook book ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
m_Entry = entry;
|
||||
m_Book = book;
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
if ( TransformationSpellHelper.UnderTransformation( Caster, typeof( WraithFormSpell ) ) )
|
||||
min = max = 0;
|
||||
else if( Core.SE && m_Book != null ) //recall using Runebook charge
|
||||
min = max = 0;
|
||||
else
|
||||
base.GetCastSkills( out min, out max );
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( m_Entry == null )
|
||||
Caster.Target = new InternalTarget( this );
|
||||
else
|
||||
Effect( m_Entry.Location, m_Entry.Map, true );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Criminal )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily.
|
||||
return false;
|
||||
}
|
||||
else if ( SpellHelper.CheckCombat( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle??
|
||||
return false;
|
||||
}
|
||||
else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move.
|
||||
return false;
|
||||
}
|
||||
|
||||
return SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom );
|
||||
}
|
||||
|
||||
public void Effect( Point3D loc, Map map, bool checkMulti )
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
}
|
||||
else if ( map == null || (!Core.AOS && Caster.Map != map) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005569 ); // You can not recall to another facet.
|
||||
}
|
||||
else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom ) )
|
||||
{
|
||||
}
|
||||
else if ( !SpellHelper.CheckTravel( Caster, map, loc, TravelCheckType.RecallTo ) )
|
||||
{
|
||||
}
|
||||
else if ( map == Map.Felucca && Caster is PlayerMobile && ((PlayerMobile)Caster).Young )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young.
|
||||
}
|
||||
else if ( Caster.Kills >= 5 && map != Map.Felucca )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there.
|
||||
}
|
||||
else if ( Caster.Criminal )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily.
|
||||
}
|
||||
else if ( SpellHelper.CheckCombat( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle??
|
||||
}
|
||||
else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move.
|
||||
}
|
||||
else if ( !map.CanSpawnMobile( loc.X, loc.Y, loc.Z ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( (checkMulti && SpellHelper.CheckMulti( loc, map )) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( m_Book != null && m_Book.CurCharges <= 0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502412 ); // There are no charges left on that item.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
BaseCreature.TeleportPets( Caster, loc, map, true );
|
||||
|
||||
if ( m_Book != null )
|
||||
--m_Book.CurCharges;
|
||||
|
||||
Caster.PlaySound( 0x1FC );
|
||||
Caster.MoveToWorld( loc, map );
|
||||
Caster.PlaySound( 0x1FC );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private RecallSpell m_Owner;
|
||||
|
||||
public InternalTarget( RecallSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
|
||||
owner.Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501029 ); // Select Marked item.
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is RecallRune )
|
||||
{
|
||||
RecallRune rune = (RecallRune)o;
|
||||
|
||||
if ( rune.Marked )
|
||||
m_Owner.Effect( rune.Target, rune.TargetMap, true );
|
||||
else
|
||||
from.SendLocalizedMessage( 501805 ); // That rune is not yet marked.
|
||||
}
|
||||
else if ( o is Runebook )
|
||||
{
|
||||
RunebookEntry e = ((Runebook)o).Default;
|
||||
|
||||
if ( e != null )
|
||||
m_Owner.Effect( e.Location, e.Map, true );
|
||||
else
|
||||
from.SendLocalizedMessage( 502354 ); // Target is not marked.
|
||||
}
|
||||
else if ( o is Key && ((Key)o).KeyValue != 0 && ((Key)o).Link is BaseBoat )
|
||||
{
|
||||
BaseBoat boat = ((Key)o).Link as BaseBoat;
|
||||
|
||||
if ( !boat.Deleted && boat.CheckKey( ((Key)o).KeyValue ) )
|
||||
m_Owner.Effect( boat.GetMarkedLocation(), boat.Map, false );
|
||||
else
|
||||
from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object.
|
||||
}
|
||||
else if ( o is HouseRaffleDeed && ((HouseRaffleDeed)o).ValidLocation() )
|
||||
{
|
||||
HouseRaffleDeed deed = (HouseRaffleDeed)o;
|
||||
|
||||
m_Owner.Effect( deed.PlotLocation, deed.PlotFacet, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object.
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNonlocalTarget( Mobile from, object o )
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Scripts/Spells/Gargoyle/SpellDefinitions/FlySpell.cs
Normal file
101
Scripts/Spells/Gargoyle/SpellDefinitions/FlySpell.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class FlySpell : Spell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo("Gargoyle Flight", null, -1, 9002);
|
||||
private bool m_Stop;
|
||||
public FlySpell(Mobile caster)
|
||||
: base(caster, null, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool ClearHandsOnCast
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public override bool RevealOnCast
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public override double CastDelayFastScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(.25);
|
||||
}
|
||||
}
|
||||
public override TimeSpan GetCastRecovery()
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override bool ConsumeReagents()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CheckFizzle()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
this.m_Stop = true;
|
||||
this.Disturb(DisturbType.Hurt, false, false);
|
||||
}
|
||||
|
||||
public override bool CheckDisturb(DisturbType type, bool checkFirst, bool resistable)
|
||||
{
|
||||
if (type == DisturbType.EquipRequest || type == DisturbType.UseRequest/* || type == DisturbType.Hurt*/)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void DoHurtFizzle()
|
||||
{
|
||||
}
|
||||
|
||||
public override void DoFizzle()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnDisturb(DisturbType type, bool message)
|
||||
{
|
||||
if (message && !this.m_Stop)
|
||||
this.Caster.SendLocalizedMessage(1113192); // You have been disrupted while attempting to fly!
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
this.Caster.Flying = false;
|
||||
BuffInfo.RemoveBuff(this.Caster, BuffIcon.Fly);
|
||||
this.Caster.Animate(60, 10, 1, true, false, 0);
|
||||
this.Caster.SendLocalizedMessage(1112567); // You are flying.
|
||||
this.Caster.Flying = true;
|
||||
BuffInfo.AddBuff(this.Caster, new BuffInfo(BuffIcon.Fly, 1112567));
|
||||
this.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
194
Scripts/Spells/Initializer.cs
Normal file
194
Scripts/Spells/Initializer.cs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
using System;
|
||||
using Server;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class Initializer
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
// First circle
|
||||
Register( 00, typeof( First.ClumsySpell ) );
|
||||
Register( 01, typeof( First.CreateFoodSpell ) );
|
||||
Register( 02, typeof( First.FeeblemindSpell ) );
|
||||
Register( 03, typeof( First.HealSpell ) );
|
||||
Register( 04, typeof( First.MagicArrowSpell ) );
|
||||
Register( 05, typeof( First.NightSightSpell ) );
|
||||
Register( 06, typeof( First.ReactiveArmorSpell ) );
|
||||
Register( 07, typeof( First.WeakenSpell ) );
|
||||
|
||||
// Second circle
|
||||
Register( 08, typeof( Second.AgilitySpell ) );
|
||||
Register( 09, typeof( Second.CunningSpell ) );
|
||||
Register( 10, typeof( Second.CureSpell ) );
|
||||
Register( 11, typeof( Second.HarmSpell ) );
|
||||
Register( 12, typeof( Second.MagicTrapSpell ) );
|
||||
Register( 13, typeof( Second.RemoveTrapSpell ) );
|
||||
Register( 14, typeof( Second.ProtectionSpell ) );
|
||||
Register( 15, typeof( Second.StrengthSpell ) );
|
||||
|
||||
// Third circle
|
||||
Register( 16, typeof( Third.BlessSpell ) );
|
||||
Register( 17, typeof( Third.FireballSpell ) );
|
||||
Register( 18, typeof( Third.MagicLockSpell ) );
|
||||
Register( 19, typeof( Third.PoisonSpell ) );
|
||||
Register( 20, typeof( Third.TelekinesisSpell ) );
|
||||
Register( 21, typeof( Third.TeleportSpell ) );
|
||||
Register( 22, typeof( Third.UnlockSpell ) );
|
||||
Register( 23, typeof( Third.WallOfStoneSpell ) );
|
||||
|
||||
// Fourth circle
|
||||
Register( 24, typeof( Fourth.ArchCureSpell ) );
|
||||
Register( 25, typeof( Fourth.ArchProtectionSpell ) );
|
||||
Register( 26, typeof( Fourth.CurseSpell ) );
|
||||
Register( 27, typeof( Fourth.FireFieldSpell ) );
|
||||
Register( 28, typeof( Fourth.GreaterHealSpell ) );
|
||||
Register( 29, typeof( Fourth.LightningSpell ) );
|
||||
Register( 30, typeof( Fourth.ManaDrainSpell ) );
|
||||
Register( 31, typeof( Fourth.RecallSpell ) );
|
||||
|
||||
// Fifth circle
|
||||
Register( 32, typeof( Fifth.BladeSpiritsSpell ) );
|
||||
Register( 33, typeof( Fifth.DispelFieldSpell ) );
|
||||
Register( 34, typeof( Fifth.IncognitoSpell ) );
|
||||
Register( 35, typeof( Fifth.MagicReflectSpell ) );
|
||||
Register( 36, typeof( Fifth.MindBlastSpell ) );
|
||||
Register( 37, typeof( Fifth.ParalyzeSpell ) );
|
||||
Register( 38, typeof( Fifth.PoisonFieldSpell ) );
|
||||
Register( 39, typeof( Fifth.SummonCreatureSpell ) );
|
||||
|
||||
// Sixth circle
|
||||
Register( 40, typeof( Sixth.DispelSpell ) );
|
||||
Register( 41, typeof( Sixth.EnergyBoltSpell ) );
|
||||
Register( 42, typeof( Sixth.ExplosionSpell ) );
|
||||
Register( 43, typeof( Sixth.InvisibilitySpell ) );
|
||||
Register( 44, typeof( Sixth.MarkSpell ) );
|
||||
Register( 45, typeof( Sixth.MassCurseSpell ) );
|
||||
Register( 46, typeof( Sixth.ParalyzeFieldSpell ) );
|
||||
Register( 47, typeof( Sixth.RevealSpell ) );
|
||||
|
||||
// Seventh circle
|
||||
Register( 48, typeof( Seventh.ChainLightningSpell ) );
|
||||
Register( 49, typeof( Seventh.EnergyFieldSpell ) );
|
||||
Register( 50, typeof( Seventh.FlameStrikeSpell ) );
|
||||
Register( 51, typeof( Seventh.GateTravelSpell ) );
|
||||
Register( 52, typeof( Seventh.ManaVampireSpell ) );
|
||||
Register( 53, typeof( Seventh.MassDispelSpell ) );
|
||||
Register( 54, typeof( Seventh.MeteorSwarmSpell ) );
|
||||
Register( 55, typeof( Seventh.PolymorphSpell ) );
|
||||
|
||||
// Eighth circle
|
||||
Register( 56, typeof( Eighth.EarthquakeSpell ) );
|
||||
Register( 57, typeof( Eighth.EnergyVortexSpell ) );
|
||||
Register( 58, typeof( Eighth.ResurrectionSpell ) );
|
||||
Register( 59, typeof( Eighth.AirElementalSpell ) );
|
||||
Register( 60, typeof( Eighth.SummonDaemonSpell ) );
|
||||
Register( 61, typeof( Eighth.EarthElementalSpell ) );
|
||||
Register( 62, typeof( Eighth.FireElementalSpell ) );
|
||||
Register( 63, typeof( Eighth.WaterElementalSpell ) );
|
||||
|
||||
if ( Core.AOS )
|
||||
{
|
||||
// Necromancy spells
|
||||
Register( 100, typeof( Necromancy.AnimateDeadSpell ) );
|
||||
Register( 101, typeof( Necromancy.BloodOathSpell ) );
|
||||
Register( 102, typeof( Necromancy.CorpseSkinSpell ) );
|
||||
Register( 103, typeof( Necromancy.CurseWeaponSpell ) );
|
||||
Register( 104, typeof( Necromancy.EvilOmenSpell ) );
|
||||
Register( 105, typeof( Necromancy.HorrificBeastSpell ) );
|
||||
Register( 106, typeof( Necromancy.LichFormSpell ) );
|
||||
Register( 107, typeof( Necromancy.MindRotSpell ) );
|
||||
Register( 108, typeof( Necromancy.PainSpikeSpell ) );
|
||||
Register( 109, typeof( Necromancy.PoisonStrikeSpell ) );
|
||||
Register( 110, typeof( Necromancy.StrangleSpell ) );
|
||||
Register( 111, typeof( Necromancy.SummonFamiliarSpell ) );
|
||||
Register( 112, typeof( Necromancy.VampiricEmbraceSpell ) );
|
||||
Register( 113, typeof( Necromancy.VengefulSpiritSpell ) );
|
||||
Register( 114, typeof( Necromancy.WitherSpell ) );
|
||||
Register( 115, typeof( Necromancy.WraithFormSpell ) );
|
||||
|
||||
if( Core.SE )
|
||||
Register( 116, typeof( Necromancy.ExorcismSpell ) );
|
||||
|
||||
// Paladin abilities
|
||||
Register( 200, typeof( Chivalry.CleanseByFireSpell ) );
|
||||
Register( 201, typeof( Chivalry.CloseWoundsSpell ) );
|
||||
Register( 202, typeof( Chivalry.ConsecrateWeaponSpell ) );
|
||||
Register( 203, typeof( Chivalry.DispelEvilSpell ) );
|
||||
Register( 204, typeof( Chivalry.DivineFurySpell ) );
|
||||
Register( 205, typeof( Chivalry.EnemyOfOneSpell ) );
|
||||
Register( 206, typeof( Chivalry.HolyLightSpell ) );
|
||||
Register( 207, typeof( Chivalry.NobleSacrificeSpell ) );
|
||||
Register( 208, typeof( Chivalry.RemoveCurseSpell ) );
|
||||
Register( 209, typeof( Chivalry.SacredJourneySpell ) );
|
||||
|
||||
if ( Core.SE )
|
||||
{
|
||||
// Samurai abilities
|
||||
Register( 400, typeof( Bushido.HonorableExecution ) );
|
||||
Register( 401, typeof( Bushido.Confidence ) );
|
||||
Register( 402, typeof( Bushido.Evasion ) );
|
||||
Register( 403, typeof( Bushido.CounterAttack ) );
|
||||
Register( 404, typeof( Bushido.LightningStrike ) );
|
||||
Register( 405, typeof( Bushido.MomentumStrike ) );
|
||||
|
||||
// Ninja abilities
|
||||
Register( 500, typeof( Ninjitsu.FocusAttack ) );
|
||||
Register( 501, typeof( Ninjitsu.DeathStrike ) );
|
||||
Register( 502, typeof( Ninjitsu.AnimalForm ) );
|
||||
Register( 503, typeof( Ninjitsu.KiAttack ) );
|
||||
Register( 504, typeof( Ninjitsu.SurpriseAttack ) );
|
||||
Register( 505, typeof( Ninjitsu.Backstab ) );
|
||||
Register( 506, typeof( Ninjitsu.Shadowjump ) );
|
||||
Register( 507, typeof( Ninjitsu.MirrorImage ) );
|
||||
}
|
||||
|
||||
if ( Core.ML )
|
||||
{
|
||||
Register( 600, typeof( Spellweaving.ArcaneCircleSpell ) );
|
||||
Register( 601, typeof( Spellweaving.GiftOfRenewalSpell ) );
|
||||
Register( 602, typeof( Spellweaving.ImmolatingWeaponSpell ) );
|
||||
Register( 603, typeof( Spellweaving.AttuneWeaponSpell ) );
|
||||
Register( 604, typeof( Spellweaving.ThunderstormSpell ) );
|
||||
Register( 605, typeof( Spellweaving.NatureFurySpell ) );
|
||||
Register( 606, typeof( Spellweaving.SummonFeySpell ) );
|
||||
Register( 607, typeof( Spellweaving.SummonFiendSpell ) );
|
||||
Register( 608, typeof( Spellweaving.ReaperFormSpell ) );
|
||||
//Register( 609, typeof( Spellweaving.WildfireSpell ) );
|
||||
Register( 610, typeof( Spellweaving.EssenceOfWindSpell ) );
|
||||
//Register( 611, typeof( Spellweaving.DryadAllureSpell ) );
|
||||
Register( 612, typeof( Spellweaving.EtherealVoyageSpell ) );
|
||||
Register( 613, typeof( Spellweaving.WordOfDeathSpell ) );
|
||||
Register( 614, typeof( Spellweaving.GiftOfLifeSpell ) );
|
||||
//Register( 615, typeof( Spellweaving.ArcaneEmpowermentSpell ) );
|
||||
}
|
||||
|
||||
if ( Core.SA )
|
||||
{
|
||||
// Mysticism spells
|
||||
//Register( 677, typeof( Mysticism.NetherBoltSpell ) );
|
||||
//Register( 678, typeof( Mysticism.HealingStoneSpell ) );
|
||||
//Register( 679, typeof( Mysticism.PurgeMagicSpell ) );
|
||||
//Register( 680, typeof( Mysticism.EnchantSpell ) );
|
||||
//Register( 681, typeof( Mysticism.SleepSpell ) );
|
||||
Register( 682, typeof( Mysticism.EagleStrikeSpell ) );
|
||||
Register( 683, typeof( Mysticism.AnimatedWeaponSpell ) );
|
||||
Register( 684, typeof( Mysticism.StoneFormSpell ) );
|
||||
//Register( 685, typeof( Mysticism.SpellTriggerSpell ) );
|
||||
//Register( 686, typeof( Mysticism.MassSleepSpell ) );
|
||||
//Register( 687, typeof( Mysticism.CleansingWindsSpell ) );
|
||||
//Register( 688, typeof( Mysticism.BombardSpell ) );
|
||||
Register( 689, typeof( Mysticism.SpellPlagueSpell ) );
|
||||
Register( 690, typeof( Mysticism.HailStormSpell ) );
|
||||
Register( 691, typeof( Mysticism.NetherCycloneSpell ) );
|
||||
//Register( 692, typeof( Mysticism.RisingColossusSpell ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Register( int spellId, Type type )
|
||||
{
|
||||
SpellRegistry.Register( spellId, type );
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Scripts/Spells/Mysticism/AnimatedWeaponSpell.cs
Normal file
89
Scripts/Spells/Mysticism/AnimatedWeaponSpell.cs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public class AnimatedWeaponSpell : MysticSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Animated Weapon", "In Jux Por Ylem",
|
||||
-1,
|
||||
9002,
|
||||
Reagent.Bone,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.Nightshade
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 33.0; } }
|
||||
public override int RequiredMana { get { return 11; } }
|
||||
|
||||
public AnimatedWeaponSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( ( Caster.Followers + 4 ) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return;
|
||||
}
|
||||
|
||||
var map = Caster.Map;
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
if ( map == null || ( Caster.Player && !map.CanSpawnMobile( p.X, p.Y, p.Z ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
|
||||
}
|
||||
else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
var level = (int) ( ( GetBaseSkill( Caster ) + GetBoostSkill( Caster ) ) / 2.0 );
|
||||
|
||||
var duration = TimeSpan.FromSeconds( 10 + level );
|
||||
|
||||
var summon = new AnimatedWeapon( Caster, level );
|
||||
BaseCreature.Summon( summon, false, Caster, new Point3D( p ), 0x212, duration );
|
||||
|
||||
summon.PlaySound( 0x64A );
|
||||
|
||||
Effects.SendTargetParticles( summon, 0x3728, 10, 10, 0x13AA, (EffectLayer) 255 );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private AnimatedWeaponSpell m_Owner;
|
||||
|
||||
public InternalTarget( AnimatedWeaponSpell owner )
|
||||
: base( 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is IPoint3D )
|
||||
m_Owner.Target( (IPoint3D) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Scripts/Spells/Mysticism/EagleStrikeSpell.cs
Normal file
88
Scripts/Spells/Mysticism/EagleStrikeSpell.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public class EagleStrikeSpell : MysticSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Eagle Strike", "Kal Por Xen",
|
||||
-1,
|
||||
9002,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.Bone,
|
||||
Reagent.SpidersSilk,
|
||||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.25 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 20.0; } }
|
||||
public override int RequiredMana { get { return 9; } }
|
||||
|
||||
public EagleStrikeSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( CheckHSequence( m ) )
|
||||
{
|
||||
/* Conjures a magical eagle that assaults the Target with
|
||||
* its talons, dealing energy damage.
|
||||
*/
|
||||
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.CheckReflect( 2, Caster, ref m );
|
||||
|
||||
Caster.MovingParticles( m, 0x407A, 7, 0, false, true, 0, 0, 0xBBE, 0xFA6, 0xFFFF, 0 );
|
||||
Caster.PlaySound( 0x2EE );
|
||||
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), Damage, m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private void Damage( Mobile to )
|
||||
{
|
||||
if ( to == null )
|
||||
return;
|
||||
|
||||
double damage = GetNewAosDamage( 19, 1, 5, to );
|
||||
|
||||
SpellHelper.Damage( this, to, damage, 0, 0, 0, 0, 100 );
|
||||
|
||||
to.PlaySound( 0x64D );
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private EagleStrikeSpell m_Owner;
|
||||
|
||||
public InternalTarget( EagleStrikeSpell owner )
|
||||
: base( 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
144
Scripts/Spells/Mysticism/HailStormSpell.cs
Normal file
144
Scripts/Spells/Mysticism/HailStormSpell.cs
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public class HailStormSpell : MysticSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Hail Storm", "Kal Des Ylem",
|
||||
-1,
|
||||
9002,
|
||||
Reagent.DragonsBlood,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.BlackPearl,
|
||||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.25 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 70.0; } }
|
||||
public override int RequiredMana { get { return 40; } }
|
||||
|
||||
public HailStormSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
/* Summons a storm of hailstones that strikes all Targets
|
||||
* within a radius around the Target's Location, dealing
|
||||
* cold damage.
|
||||
*/
|
||||
|
||||
SpellHelper.Turn( Caster, p );
|
||||
|
||||
if ( p is Item )
|
||||
p = ( (Item) p ).GetWorldLocation();
|
||||
|
||||
var targets = new List<Mobile>();
|
||||
|
||||
var map = Caster.Map;
|
||||
|
||||
var pvp = false;
|
||||
|
||||
if ( map != null )
|
||||
{
|
||||
PlayEffect( p, Caster.Map );
|
||||
|
||||
foreach ( var m in map.GetMobilesInRange( new Point3D( p ), 2 ) )
|
||||
{
|
||||
if ( m == Caster )
|
||||
continue;
|
||||
|
||||
if ( SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) && Caster.CanSee( m ) )
|
||||
{
|
||||
if ( !Caster.InLOS( m ) )
|
||||
continue;
|
||||
|
||||
targets.Add( m );
|
||||
|
||||
if ( m.Player )
|
||||
pvp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double damage = GetNewAosDamage( 51, 1, 5, pvp );
|
||||
|
||||
foreach ( var m in targets )
|
||||
{
|
||||
Caster.DoHarmful( m );
|
||||
SpellHelper.Damage( this, m, damage, 0, 0, 100, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static void PlayEffect( IPoint3D p, Map map )
|
||||
{
|
||||
Effects.PlaySound( p, map, 0x64F );
|
||||
|
||||
PlaySingleEffect( p, map, -1, 1, -1, 1 );
|
||||
PlaySingleEffect( p, map, -2, 0, -3, -1 );
|
||||
PlaySingleEffect( p, map, -3, -1, -1, 1 );
|
||||
PlaySingleEffect( p, map, 1, 3, -1, 1 );
|
||||
PlaySingleEffect( p, map, -1, 1, 1, 3 );
|
||||
}
|
||||
|
||||
private static void PlaySingleEffect( IPoint3D p, Map map, int a, int b, int c, int d )
|
||||
{
|
||||
int x = p.X, y = p.Y, z = p.Z + 18;
|
||||
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + c, z ), new Point3D( x + a, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + c, z ), new Point3D( x + b, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + d, z ), new Point3D( x + b, y + d, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + d, z ), new Point3D( x + a, y + d, z ) );
|
||||
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + c, z ), new Point3D( x + a, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + d, z ), new Point3D( x + b, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + d, z ), new Point3D( x + b, y + d, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + c, z ), new Point3D( x + a, y + d, z ) );
|
||||
}
|
||||
|
||||
private static void SendEffectPacket( IPoint3D p, Map map, Point3D orig, Point3D dest )
|
||||
{
|
||||
Effects.SendPacket( p, map, new HuedEffect( EffectType.Moving, Serial.Zero, Serial.Zero, 0x36D4, orig, dest, 0, 0, false, false, 0x63, 0x4 ) );
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private HailStormSpell m_Owner;
|
||||
|
||||
public InternalTarget( HailStormSpell owner )
|
||||
: base( 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
var p = o as IPoint3D;
|
||||
|
||||
if ( p != null )
|
||||
m_Owner.Target( p );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Scripts/Spells/Mysticism/MysticSpell.cs
Normal file
92
Scripts/Spells/Mysticism/MysticSpell.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using Server;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public abstract class MysticSpell : Spell
|
||||
{
|
||||
public abstract double RequiredSkill { get; }
|
||||
public abstract int RequiredMana { get; }
|
||||
|
||||
public override SkillName CastSkill { get { return SkillName.Mysticism; } }
|
||||
|
||||
/*
|
||||
* As per OSI Publish 64:
|
||||
* Imbuing is not the only skill associated with Mysticism now.
|
||||
* Players can use EITHER their Focus skill or Imbuing skill.
|
||||
* Evaluate Intelligence no longer has any effect on a Mystic’s spell power.
|
||||
*/
|
||||
public override double GetDamageSkill( Mobile m )
|
||||
{
|
||||
return Math.Max( m.Skills[SkillName.Imbuing].Value, m.Skills[SkillName.Focus].Value );
|
||||
}
|
||||
|
||||
public override int GetDamageFixed( Mobile m )
|
||||
{
|
||||
return Math.Max( m.Skills[SkillName.Imbuing].Fixed, m.Skills[SkillName.Focus].Fixed );
|
||||
}
|
||||
|
||||
public MysticSpell( Mobile caster, Item scroll, SpellInfo info )
|
||||
: base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
// As per Mysticism page at the UO Herald Playguide
|
||||
// This means that we have 25% success chance at min Required Skill
|
||||
|
||||
min = RequiredSkill - 12.5;
|
||||
max = RequiredSkill + 37.5;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return RequiredMana;
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
int mana = ScaleMana( RequiredMana );
|
||||
|
||||
if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( Caster.Skills[CastSkill].Value < RequiredSkill )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063013, String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), CastSkill.ToString() ) ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
SendCastEffect();
|
||||
}
|
||||
|
||||
public virtual void SendCastEffect()
|
||||
{
|
||||
Caster.FixedEffect( 0x37C4, 10, (int) ( GetCastDelay().TotalSeconds * 28 ), 0x66C, 3 );
|
||||
}
|
||||
|
||||
public static double GetBaseSkill( Mobile m )
|
||||
{
|
||||
return m.Skills[SkillName.Mysticism].Value;
|
||||
}
|
||||
|
||||
public static double GetBoostSkill( Mobile m )
|
||||
{
|
||||
return Math.Max( m.Skills[SkillName.Imbuing].Value, m.Skills[SkillName.Focus].Value );
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Scripts/Spells/Mysticism/NetherCycloneSpell.cs
Normal file
157
Scripts/Spells/Mysticism/NetherCycloneSpell.cs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public class NetherCycloneSpell : MysticSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Nether Cyclone", "Grav Hur",
|
||||
-1,
|
||||
9002,
|
||||
Reagent.MandrakeRoot,
|
||||
Reagent.Nightshade,
|
||||
Reagent.SulfurousAsh,
|
||||
Reagent.Bloodmoss
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.5 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 83.0; } }
|
||||
public override int RequiredMana { get { return 50; } }
|
||||
|
||||
public NetherCycloneSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
|
||||
{
|
||||
/* Summons a gale of lethal winds that strikes all Targets within a radius around
|
||||
* the Target's Location, dealing chaos damage. In addition to inflicting damage,
|
||||
* each Target of the Nether Cyclone temporarily loses a percentage of mana and
|
||||
* stamina. The effectiveness of the Nether Cyclone is determined by a comparison
|
||||
* between the Caster's Mysticism and either Focus or Imbuing (whichever is greater)
|
||||
* skills and the Resisting Spells skill of the Target.
|
||||
*/
|
||||
|
||||
SpellHelper.Turn( Caster, p );
|
||||
|
||||
if ( p is Item )
|
||||
p = ( (Item) p ).GetWorldLocation();
|
||||
|
||||
var targets = new List<Mobile>();
|
||||
|
||||
var map = Caster.Map;
|
||||
|
||||
var pvp = false;
|
||||
|
||||
if ( map != null )
|
||||
{
|
||||
PlayEffect( p, Caster.Map );
|
||||
|
||||
foreach ( var m in map.GetMobilesInRange( new Point3D( p ), 2 ) )
|
||||
{
|
||||
if ( m == Caster )
|
||||
continue;
|
||||
|
||||
if ( SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) && Caster.CanSee( m ) )
|
||||
{
|
||||
if ( !Caster.InLOS( m ) )
|
||||
continue;
|
||||
|
||||
targets.Add( m );
|
||||
|
||||
if ( m.Player )
|
||||
pvp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var damage = GetNewAosDamage( 51, 1, 5, pvp );
|
||||
var reduction = ( GetBaseSkill( Caster ) + GetBoostSkill( Caster ) ) / 1200.0;
|
||||
|
||||
foreach ( var m in targets )
|
||||
{
|
||||
Caster.DoHarmful( m );
|
||||
|
||||
var types = new int[4];
|
||||
types[Utility.Random( types.Length )] = 100;
|
||||
|
||||
SpellHelper.Damage( this, m, damage, 0, types[0], types[1], types[2], types[3] );
|
||||
|
||||
var resistedReduction = reduction - ( m.Skills[SkillName.MagicResist].Value / 800.0 );
|
||||
|
||||
m.Stam -= (int) ( m.StamMax * resistedReduction );
|
||||
m.Mana -= (int) ( m.ManaMax * resistedReduction );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static void PlayEffect( IPoint3D p, Map map )
|
||||
{
|
||||
Effects.PlaySound( p, map, 0x64F );
|
||||
|
||||
PlaySingleEffect( p, map, -1, 1, -1, 1 );
|
||||
PlaySingleEffect( p, map, -2, 0, -3, -1 );
|
||||
PlaySingleEffect( p, map, -3, -1, -1, 1 );
|
||||
PlaySingleEffect( p, map, 1, 3, -1, 1 );
|
||||
PlaySingleEffect( p, map, -1, 1, 1, 3 );
|
||||
}
|
||||
|
||||
private static void PlaySingleEffect( IPoint3D p, Map map, int a, int b, int c, int d )
|
||||
{
|
||||
int x = p.X, y = p.Y, z = p.Z + 18;
|
||||
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + c, z ), new Point3D( x + a, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + c, z ), new Point3D( x + b, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + d, z ), new Point3D( x + b, y + d, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + d, z ), new Point3D( x + a, y + d, z ) );
|
||||
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + c, z ), new Point3D( x + a, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + b, y + d, z ), new Point3D( x + b, y + c, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + d, z ), new Point3D( x + b, y + d, z ) );
|
||||
SendEffectPacket( p, map, new Point3D( x + a, y + c, z ), new Point3D( x + a, y + d, z ) );
|
||||
}
|
||||
|
||||
private static void SendEffectPacket( IPoint3D p, Map map, Point3D orig, Point3D dest )
|
||||
{
|
||||
Effects.SendPacket( p, map, new HuedEffect( EffectType.Moving, Serial.Zero, Serial.Zero, 0x375A, orig, dest, 0, 0, false, false, 0x49A, 0x4 ) );
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private NetherCycloneSpell m_Owner;
|
||||
|
||||
public InternalTarget( NetherCycloneSpell owner )
|
||||
: base( 12, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
var p = o as IPoint3D;
|
||||
|
||||
if ( p != null )
|
||||
m_Owner.Target( p );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
235
Scripts/Spells/Mysticism/SpellPlagueSpell.cs
Normal file
235
Scripts/Spells/Mysticism/SpellPlagueSpell.cs
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public class SpellPlagueSpell : MysticSpell
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.PlayerDeath += new PlayerDeathEventHandler( OnPlayerDeath );
|
||||
}
|
||||
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Spell Plague", "Vas Rel Jux Ort",
|
||||
-1,
|
||||
9002,
|
||||
Reagent.DaemonBone,
|
||||
Reagent.DragonsBlood,
|
||||
Reagent.Nightshade,
|
||||
Reagent.SulfurousAsh
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.25 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 70.0; } }
|
||||
public override int RequiredMana { get { return 40; } }
|
||||
|
||||
public SpellPlagueSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile targeted )
|
||||
{
|
||||
if ( !Caster.CanSee( targeted ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckHSequence( targeted ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, targeted );
|
||||
|
||||
SpellHelper.CheckReflect( 6, Caster, ref targeted );
|
||||
|
||||
/* The target is hit with an explosion of chaos damage and then inflicted
|
||||
* with the spell plague curse. Each time the target is damaged while under
|
||||
* the effect of the spell plague, they may suffer an explosion of chaos
|
||||
* damage. The initial chance to trigger the explosion starts at 90% and
|
||||
* reduces by 30% every time an explosion occurs. Once the target is
|
||||
* afflicted by 3 explosions or 8 seconds have passed, that spell plague
|
||||
* is removed from the target. Spell Plague will stack with other spell
|
||||
* plagues so that they are applied one after the other.
|
||||
*/
|
||||
|
||||
VisualEffect( targeted );
|
||||
|
||||
var damage = GetNewAosDamage( 33, 1, 5, targeted );
|
||||
|
||||
var types = new int[4];
|
||||
types[Utility.Random( types.Length )] = 100;
|
||||
|
||||
SpellHelper.Damage( this, targeted, damage, 0, types[0], types[1], types[2], types[3] );
|
||||
|
||||
var context = new SpellPlagueContext( this, targeted );
|
||||
|
||||
if ( m_Table.ContainsKey( targeted ) )
|
||||
{
|
||||
var oldContext = m_Table[targeted];
|
||||
oldContext.SetNext( context );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Table[targeted] = context;
|
||||
context.Start();
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public static bool UnderEffect( Mobile m )
|
||||
{
|
||||
return m_Table.ContainsKey( m );
|
||||
}
|
||||
|
||||
public static void RemoveEffect( Mobile m )
|
||||
{
|
||||
if ( !m_Table.ContainsKey( m ) )
|
||||
return;
|
||||
|
||||
var context = m_Table[m];
|
||||
|
||||
context.EndPlague( false );
|
||||
}
|
||||
|
||||
public static void CheckPlague( Mobile m )
|
||||
{
|
||||
if ( !m_Table.ContainsKey( m ) )
|
||||
return;
|
||||
|
||||
var context = m_Table[m];
|
||||
|
||||
context.OnDamage();
|
||||
}
|
||||
|
||||
private static void OnPlayerDeath( PlayerDeathEventArgs e )
|
||||
{
|
||||
RemoveEffect( e.Mobile );
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, SpellPlagueContext> m_Table = new Dictionary<Mobile, SpellPlagueContext>();
|
||||
|
||||
protected void VisualEffect( Mobile to )
|
||||
{
|
||||
to.PlaySound( 0x658 );
|
||||
|
||||
to.FixedParticles( 0x3728, 1, 13, 0x26B8, 0x47E, 7, EffectLayer.Head, 0 );
|
||||
to.FixedParticles( 0x3779, 1, 15, 0x251E, 0x43, 7, EffectLayer.Head, 0 );
|
||||
}
|
||||
|
||||
private class SpellPlagueContext
|
||||
{
|
||||
private SpellPlagueSpell m_Owner;
|
||||
private Mobile m_Target;
|
||||
private DateTime m_LastExploded;
|
||||
private int m_Explosions;
|
||||
private Timer m_Timer;
|
||||
private SpellPlagueContext m_Next;
|
||||
|
||||
public SpellPlagueContext( SpellPlagueSpell owner, Mobile target )
|
||||
{
|
||||
m_Owner = owner;
|
||||
m_Target = target;
|
||||
}
|
||||
|
||||
public void SetNext( SpellPlagueContext context )
|
||||
{
|
||||
if ( m_Next == null )
|
||||
m_Next = context;
|
||||
else
|
||||
m_Next.SetNext( context );
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 8.0 ), new TimerCallback( EndPlague ) );
|
||||
m_Timer.Start();
|
||||
|
||||
BuffInfo.AddBuff( m_Target, new BuffInfo( BuffIcon.SpellPlague, 1031690, 1080167, TimeSpan.FromSeconds( 8.5 ), m_Target ) );
|
||||
}
|
||||
|
||||
public void OnDamage()
|
||||
{
|
||||
if ( DateTime.UtcNow > ( m_LastExploded + TimeSpan.FromSeconds( 2.0 ) ) )
|
||||
{
|
||||
var exploChance = 90 - ( m_Explosions * 30 );
|
||||
|
||||
var resist = m_Target.Skills[SkillName.MagicResist].Value;
|
||||
|
||||
if ( resist >= 70 )
|
||||
exploChance -= (int) ( ( resist - 70.0 ) * 3.0 / 10.0 );
|
||||
|
||||
if ( exploChance > Utility.Random( 100 ) )
|
||||
{
|
||||
m_Owner.VisualEffect( m_Target );
|
||||
|
||||
var damage = m_Owner.GetNewAosDamage( 15 + ( m_Explosions * 3 ), 1, 5, m_Target );
|
||||
|
||||
m_Explosions++;
|
||||
m_LastExploded = DateTime.UtcNow;
|
||||
|
||||
var types = new int[4];
|
||||
types[Utility.Random( types.Length )] = 100;
|
||||
|
||||
SpellHelper.Damage( m_Owner, m_Target, damage, 0, types[0], types[1], types[2], types[3] );
|
||||
|
||||
if ( m_Explosions >= 3 )
|
||||
EndPlague();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EndPlague()
|
||||
{
|
||||
EndPlague( true );
|
||||
}
|
||||
|
||||
public void EndPlague( bool restart )
|
||||
{
|
||||
if ( m_Timer != null )
|
||||
m_Timer.Stop();
|
||||
|
||||
if ( restart && m_Next != null )
|
||||
{
|
||||
m_Table[m_Target] = m_Next;
|
||||
m_Next.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Table.Remove( m_Target );
|
||||
|
||||
BuffInfo.RemoveBuff( m_Target, BuffIcon.SpellPlague );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private SpellPlagueSpell m_Owner;
|
||||
|
||||
public InternalTarget( SpellPlagueSpell owner )
|
||||
: base( 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
161
Scripts/Spells/Mysticism/StoneFormSpell.cs
Normal file
161
Scripts/Spells/Mysticism/StoneFormSpell.cs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Server.Spells.Fifth;
|
||||
using Server.Spells.Seventh;
|
||||
|
||||
namespace Server.Spells.Mysticism
|
||||
{
|
||||
public class StoneFormSpell : MysticSpell
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.PlayerDeath += OnPlayerDeath;
|
||||
}
|
||||
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Stone Form", "In Rel Ylem",
|
||||
-1,
|
||||
9002,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.FertileDirt,
|
||||
Reagent.Garlic
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 33.0; } }
|
||||
public override int RequiredMana { get { return 11; } }
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool UnderEffect( Mobile m )
|
||||
{
|
||||
return m_Table.Contains( m );
|
||||
}
|
||||
|
||||
public StoneFormSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
return false;
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed.
|
||||
return false;
|
||||
}
|
||||
else if ( Ninjitsu.AnimalForm.UnderTransformation( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063218 ); // You cannot use that ability in this form.
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Flying )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1113415 ); // You cannot use this ability while flying.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed.
|
||||
}
|
||||
else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) || ( Caster.IsBodyMod && !UnderEffect( Caster ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063218 ); // You cannot use that ability in this form.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
if ( UnderEffect( Caster ) )
|
||||
{
|
||||
RemoveEffects( Caster );
|
||||
|
||||
Caster.PlaySound( 0xFA );
|
||||
Caster.Delta( MobileDelta.Resistances );
|
||||
}
|
||||
else
|
||||
{
|
||||
var mount = Caster.Mount;
|
||||
|
||||
if ( mount != null )
|
||||
mount.Rider = null;
|
||||
|
||||
Caster.BodyMod = 0x2C1;
|
||||
Caster.HueMod = 0;
|
||||
|
||||
var offset = (int) ( ( GetBaseSkill( Caster ) + GetBoostSkill( Caster ) ) / 24.0 );
|
||||
|
||||
var mods = new List<ResistanceMod>
|
||||
{
|
||||
new ResistanceMod( ResistanceType.Physical, offset ),
|
||||
new ResistanceMod( ResistanceType.Fire, offset ),
|
||||
new ResistanceMod( ResistanceType.Cold, offset ),
|
||||
new ResistanceMod( ResistanceType.Poison, offset ),
|
||||
new ResistanceMod( ResistanceType.Energy, offset )
|
||||
};
|
||||
|
||||
foreach ( var mod in mods )
|
||||
Caster.AddResistanceMod( mod );
|
||||
|
||||
m_Table[Caster] = mods;
|
||||
|
||||
Caster.PlaySound( 0x65A );
|
||||
Caster.Delta( MobileDelta.Resistances );
|
||||
|
||||
BuffInfo.AddBuff( Caster, new BuffInfo( BuffIcon.StoneForm, 1080145, 1080146,
|
||||
string.Format( "-10\t-2\t{0}\t{1}\t{2}", offset, GetResistCapBonus( Caster ), GetDIBonus( Caster ) ), false ) );
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public static int GetDIBonus( Mobile m )
|
||||
{
|
||||
return (int) ( ( GetBaseSkill( m ) + GetBoostSkill( m ) ) / 12.0 );
|
||||
}
|
||||
|
||||
public static int GetResistCapBonus( Mobile m )
|
||||
{
|
||||
return (int) ( ( GetBaseSkill( m ) + GetBoostSkill( m ) ) / 48.0 );
|
||||
}
|
||||
|
||||
public static void RemoveEffects( Mobile m )
|
||||
{
|
||||
var mods = (List<ResistanceMod>) m_Table[m];
|
||||
|
||||
foreach ( var mod in mods )
|
||||
m.RemoveResistanceMod( mod );
|
||||
|
||||
m.BodyMod = 0;
|
||||
m.HueMod = -1;
|
||||
|
||||
m_Table.Remove( m );
|
||||
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.StoneForm );
|
||||
}
|
||||
|
||||
private static void OnPlayerDeath( PlayerDeathEventArgs e )
|
||||
{
|
||||
var m = e.Mobile;
|
||||
|
||||
if ( UnderEffect( m ) )
|
||||
RemoveEffects( m );
|
||||
}
|
||||
}
|
||||
}
|
||||
421
Scripts/Spells/Necromancy/AnimateDeadSpell.cs
Normal file
421
Scripts/Spells/Necromancy/AnimateDeadSpell.cs
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
using Server.Items;
|
||||
using Server.Engines.Quests;
|
||||
using Server.Engines.Quests.Necro;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class AnimateDeadSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Animate Dead", "Uus Corp",
|
||||
203,
|
||||
9031,
|
||||
Reagent.GraveDust,
|
||||
Reagent.DaemonBlood
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 40.0; } }
|
||||
public override int RequiredMana{ get{ return 23; } }
|
||||
|
||||
public AnimateDeadSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
Caster.SendLocalizedMessage( 1061083 ); // Animate what corpse?
|
||||
}
|
||||
|
||||
private class CreatureGroup
|
||||
{
|
||||
public Type[] m_Types;
|
||||
public SummonEntry[] m_Entries;
|
||||
|
||||
public CreatureGroup( Type[] types, SummonEntry[] entries )
|
||||
{
|
||||
m_Types = types;
|
||||
m_Entries = entries;
|
||||
}
|
||||
}
|
||||
|
||||
private class SummonEntry
|
||||
{
|
||||
public Type[] m_ToSummon;
|
||||
public int m_Requirement;
|
||||
|
||||
public SummonEntry( int requirement, params Type[] toSummon )
|
||||
{
|
||||
m_ToSummon = toSummon;
|
||||
m_Requirement = requirement;
|
||||
}
|
||||
}
|
||||
|
||||
private static CreatureGroup FindGroup( Type type )
|
||||
{
|
||||
for ( int i = 0; i < m_Groups.Length; ++i )
|
||||
{
|
||||
CreatureGroup group = m_Groups[i];
|
||||
Type[] types = group.m_Types;
|
||||
|
||||
bool contains = ( types.Length == 0 );
|
||||
|
||||
for ( int j = 0; !contains && j < types.Length; ++j )
|
||||
contains = types[j].IsAssignableFrom( type );
|
||||
|
||||
if ( contains )
|
||||
return group;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static CreatureGroup[] m_Groups = new CreatureGroup[]
|
||||
{
|
||||
// Undead group--empty
|
||||
new CreatureGroup( SlayerGroup.GetEntryByName( SlayerName.Silver ).Types, new SummonEntry[0] ),
|
||||
// Insects
|
||||
new CreatureGroup( new Type[]
|
||||
{
|
||||
typeof( DreadSpider ), typeof( FrostSpider ), typeof( GiantSpider ), typeof( GiantBlackWidow ),
|
||||
typeof( BlackSolenInfiltratorQueen ), typeof( BlackSolenInfiltratorWarrior ),
|
||||
typeof( BlackSolenQueen ), typeof( BlackSolenWarrior ), typeof( BlackSolenWorker ),
|
||||
typeof( RedSolenInfiltratorQueen ), typeof( RedSolenInfiltratorWarrior ),
|
||||
typeof( RedSolenQueen ), typeof( RedSolenWarrior ), typeof( RedSolenWorker ),
|
||||
typeof( TerathanAvenger ), typeof( TerathanDrone ), typeof( TerathanMatriarch ),
|
||||
typeof( TerathanWarrior )
|
||||
// TODO: Giant beetle? Ant lion? Ophidians?
|
||||
},
|
||||
new SummonEntry[]
|
||||
{
|
||||
new SummonEntry( 0, typeof( MoundOfMaggots ) )
|
||||
} ),
|
||||
// Mounts
|
||||
new CreatureGroup( new Type[]
|
||||
{
|
||||
typeof( Horse ), typeof( Nightmare ), typeof( FireSteed ),
|
||||
typeof( Kirin ), typeof( Unicorn )
|
||||
}, new SummonEntry[]
|
||||
{
|
||||
new SummonEntry( 10000, typeof( HellSteed ) ),
|
||||
new SummonEntry( 0, typeof( SkeletalMount ) )
|
||||
} ),
|
||||
// Elementals
|
||||
new CreatureGroup( new Type[]
|
||||
{
|
||||
typeof( BloodElemental ), typeof( EarthElemental ), typeof( SummonedEarthElemental ),
|
||||
typeof( AgapiteElemental ), typeof( BronzeElemental ), typeof( CopperElemental ),
|
||||
typeof( DullCopperElemental ), typeof( GoldenElemental ), typeof( ShadowIronElemental ),
|
||||
typeof( ValoriteElemental ), typeof( VeriteElemental ), typeof( PoisonElemental ),
|
||||
typeof( FireElemental ), typeof( SummonedFireElemental ), typeof( SnowElemental ),
|
||||
typeof( AirElemental ), typeof( SummonedAirElemental ), typeof( WaterElemental ),
|
||||
typeof( SummonedAirElemental ), typeof ( AcidElemental )
|
||||
}, new SummonEntry[]
|
||||
{
|
||||
new SummonEntry( 5000, typeof( WailingBanshee ) ),
|
||||
new SummonEntry( 0, typeof( Wraith ) )
|
||||
} ),
|
||||
// Dragons
|
||||
new CreatureGroup( new Type[]
|
||||
{
|
||||
typeof( AncientWyrm ), typeof( Dragon ), typeof( GreaterDragon ), typeof( SerpentineDragon ),
|
||||
typeof( ShadowWyrm ), typeof( SkeletalDragon ), typeof( WhiteWyrm ),
|
||||
typeof( Drake ), typeof( Wyvern ), typeof( LesserHiryu ), typeof( Hiryu )
|
||||
}, new SummonEntry[]
|
||||
{
|
||||
new SummonEntry( 18000, typeof( SkeletalDragon ) ),
|
||||
new SummonEntry( 10000, typeof( FleshGolem ) ),
|
||||
new SummonEntry( 5000, typeof( Lich ) ),
|
||||
new SummonEntry( 3000, typeof( SkeletalKnight ), typeof( BoneKnight ) ),
|
||||
new SummonEntry( 2000, typeof( Mummy ) ),
|
||||
new SummonEntry( 1000, typeof( SkeletalMage ), typeof( BoneMagi ) ),
|
||||
new SummonEntry( 0, typeof( PatchworkSkeleton ) )
|
||||
} ),
|
||||
// Default group
|
||||
new CreatureGroup( new Type[0], new SummonEntry[]
|
||||
{
|
||||
new SummonEntry( 18000, typeof( LichLord ) ),
|
||||
new SummonEntry( 10000, typeof( FleshGolem ) ),
|
||||
new SummonEntry( 5000, typeof( Lich ) ),
|
||||
new SummonEntry( 3000, typeof( SkeletalKnight ), typeof( BoneKnight ) ),
|
||||
new SummonEntry( 2000, typeof( Mummy ) ),
|
||||
new SummonEntry( 1000, typeof( SkeletalMage ), typeof( BoneMagi ) ),
|
||||
new SummonEntry( 0, typeof( PatchworkSkeleton ) )
|
||||
} ),
|
||||
};
|
||||
|
||||
public void Target( object obj )
|
||||
{
|
||||
MaabusCoffinComponent comp = obj as MaabusCoffinComponent;
|
||||
|
||||
if ( comp != null )
|
||||
{
|
||||
MaabusCoffin addon = comp.Addon as MaabusCoffin;
|
||||
|
||||
if ( addon != null )
|
||||
{
|
||||
PlayerMobile pm = Caster as PlayerMobile;
|
||||
|
||||
if ( pm != null )
|
||||
{
|
||||
QuestSystem qs = pm.Quest;
|
||||
|
||||
if ( qs is DarkTidesQuest )
|
||||
{
|
||||
QuestObjective objective = qs.FindObjective( typeof( AnimateMaabusCorpseObjective ) );
|
||||
|
||||
if ( objective != null && !objective.Completed )
|
||||
{
|
||||
addon.Awake( Caster );
|
||||
objective.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Corpse c = obj as Corpse;
|
||||
|
||||
if( c == null )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061084 ); // You cannot animate that.
|
||||
}
|
||||
else
|
||||
{
|
||||
Type type = null;
|
||||
|
||||
if( c.Owner != null )
|
||||
{
|
||||
type = c.Owner.GetType();
|
||||
}
|
||||
|
||||
if( c.ItemID != 0x2006 || c.Animated || type == typeof( PlayerMobile ) || type == null || ( c.Owner != null && c.Owner.Fame < 100 ) || ( ( c.Owner != null ) && ( c.Owner is BaseCreature ) && ( ( ( BaseCreature )c.Owner ).Summoned || ( ( BaseCreature )c.Owner ).IsBonded ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061085 ); // There's not enough life force there to animate.
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatureGroup group = FindGroup( type );
|
||||
|
||||
if( group != null )
|
||||
{
|
||||
if( group.m_Entries.Length == 0 || type == typeof( DemonKnight ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061086 ); // You cannot animate undead remains.
|
||||
}
|
||||
else if( CheckSequence() )
|
||||
{
|
||||
Point3D p = c.GetWorldLocation();
|
||||
Map map = c.Map;
|
||||
|
||||
if( map != null )
|
||||
{
|
||||
Effects.PlaySound( p, map, 0x1FB );
|
||||
Effects.SendLocationParticles( EffectItem.Create( p, map, EffectItem.DefaultDuration ), 0x3789, 1, 40, 0x3F, 3, 9907, 0 );
|
||||
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( SummonDelay_Callback ), new object[] { Caster, c, p, map, group } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, List<Mobile>> m_Table = new Dictionary<Mobile, List<Mobile>>();
|
||||
|
||||
public static void Unregister( Mobile master, Mobile summoned )
|
||||
{
|
||||
if ( master == null )
|
||||
return;
|
||||
|
||||
List<Mobile> list = null;
|
||||
m_Table.TryGetValue( master, out list );
|
||||
|
||||
if ( list == null )
|
||||
return;
|
||||
|
||||
list.Remove( summoned );
|
||||
|
||||
if ( list.Count == 0 )
|
||||
m_Table.Remove( master );
|
||||
}
|
||||
|
||||
public static void Register( Mobile master, Mobile summoned )
|
||||
{
|
||||
if ( master == null )
|
||||
return;
|
||||
|
||||
List<Mobile> list = null;
|
||||
m_Table.TryGetValue( master, out list );
|
||||
|
||||
if ( list == null )
|
||||
m_Table[master] = list = new List<Mobile>();
|
||||
|
||||
for ( int i = list.Count - 1; i >= 0; --i )
|
||||
{
|
||||
if ( i >= list.Count )
|
||||
continue;
|
||||
|
||||
Mobile mob = list[i];
|
||||
|
||||
if ( mob.Deleted )
|
||||
list.RemoveAt( i-- );
|
||||
}
|
||||
|
||||
list.Add( summoned );
|
||||
|
||||
if ( list.Count > 3 )
|
||||
Timer.DelayCall( TimeSpan.Zero, new TimerCallback( list[0].Kill ) );
|
||||
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( Summoned_Damage ), summoned );
|
||||
}
|
||||
|
||||
private static void Summoned_Damage( object state )
|
||||
{
|
||||
Mobile mob = (Mobile)state;
|
||||
|
||||
if ( mob.Hits > 0 )
|
||||
--mob.Hits;
|
||||
else
|
||||
mob.Kill();
|
||||
}
|
||||
|
||||
private static void SummonDelay_Callback( object state )
|
||||
{
|
||||
object[] states = (object[])state;
|
||||
|
||||
Mobile caster = (Mobile)states[0];
|
||||
Corpse corpse = (Corpse)states[1];
|
||||
Point3D loc = (Point3D)states[2];
|
||||
Map map = (Map)states[3];
|
||||
CreatureGroup group = (CreatureGroup)states[4];
|
||||
|
||||
if ( corpse.Animated )
|
||||
return;
|
||||
|
||||
Mobile owner = corpse.Owner;
|
||||
|
||||
if ( owner == null )
|
||||
return;
|
||||
|
||||
double necromancy = caster.Skills[SkillName.Necromancy].Value;
|
||||
double spiritSpeak = caster.Skills[SkillName.SpiritSpeak].Value;
|
||||
|
||||
int casterAbility = 0;
|
||||
|
||||
casterAbility += (int)(necromancy * 30);
|
||||
casterAbility += (int)(spiritSpeak * 70);
|
||||
casterAbility /= 10;
|
||||
casterAbility *= 18;
|
||||
|
||||
if ( casterAbility > owner.Fame )
|
||||
casterAbility = owner.Fame;
|
||||
|
||||
if ( casterAbility < 0 )
|
||||
casterAbility = 0;
|
||||
|
||||
Type toSummon = null;
|
||||
SummonEntry[] entries = group.m_Entries;
|
||||
|
||||
for ( int i = 0; toSummon == null && i < entries.Length; ++i )
|
||||
{
|
||||
SummonEntry entry = entries[i];
|
||||
|
||||
if ( casterAbility < entry.m_Requirement )
|
||||
continue;
|
||||
|
||||
Type[] animates = entry.m_ToSummon;
|
||||
|
||||
if ( animates.Length >= 0 )
|
||||
toSummon = animates[Utility.Random( animates.Length )];
|
||||
}
|
||||
|
||||
if ( toSummon == null )
|
||||
return;
|
||||
|
||||
Mobile summoned = null;
|
||||
|
||||
try{ summoned = Activator.CreateInstance( toSummon ) as Mobile; }
|
||||
catch{}
|
||||
|
||||
if ( summoned == null )
|
||||
return;
|
||||
|
||||
if ( summoned is BaseCreature )
|
||||
{
|
||||
BaseCreature bc = (BaseCreature)summoned;
|
||||
|
||||
// to be sure
|
||||
bc.Tamable = false;
|
||||
|
||||
if ( bc is BaseMount )
|
||||
bc.ControlSlots = 1;
|
||||
else
|
||||
bc.ControlSlots = 0;
|
||||
|
||||
Effects.PlaySound( loc, map, bc.GetAngerSound() );
|
||||
|
||||
BaseCreature.Summon( (BaseCreature)summoned, false, caster, loc, 0x28, TimeSpan.FromDays( 1.0 ) );
|
||||
}
|
||||
|
||||
if ( summoned is SkeletalDragon )
|
||||
Scale( (SkeletalDragon)summoned, 50 ); // lose 50% hp and strength
|
||||
|
||||
summoned.Fame = 0;
|
||||
summoned.Karma = -1500;
|
||||
|
||||
summoned.MoveToWorld( loc, map );
|
||||
|
||||
corpse.Hue = 1109;
|
||||
corpse.Animated = true;
|
||||
|
||||
Register( caster, summoned );
|
||||
}
|
||||
|
||||
public static void Scale( BaseCreature bc, int scalar )
|
||||
{
|
||||
int toScale;
|
||||
|
||||
toScale = bc.RawStr;
|
||||
bc.RawStr = AOS.Scale( toScale, scalar );
|
||||
|
||||
toScale = bc.HitsMaxSeed;
|
||||
|
||||
if ( toScale > 0 )
|
||||
bc.HitsMaxSeed = AOS.Scale( toScale, scalar );
|
||||
|
||||
bc.Hits = bc.Hits; // refresh hits
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private AnimateDeadSpell m_Owner;
|
||||
|
||||
public InternalTarget( AnimateDeadSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
m_Owner.Target( o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
Scripts/Spells/Necromancy/BloodOathSpell.cs
Normal file
190
Scripts/Spells/Necromancy/BloodOathSpell.cs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class BloodOathSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Blood Oath", "In Jux Mani Xen",
|
||||
203,
|
||||
9031,
|
||||
Reagent.DaemonBlood
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 20.0; } }
|
||||
public override int RequiredMana{ get{ return 13; } }
|
||||
|
||||
public BloodOathSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( Caster == m || !(m is PlayerMobile || m is BaseCreature) ) // only PlayerMobile and BaseCreature implement blood oath checking
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060508 ); // You can't curse that.
|
||||
}
|
||||
else if ( m_OathTable.Contains( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061607 ); // You are already bonded in a Blood Oath.
|
||||
}
|
||||
else if ( m_OathTable.Contains( m ) )
|
||||
{
|
||||
if ( m.Player )
|
||||
Caster.SendLocalizedMessage( 1061608 ); // That player is already bonded in a Blood Oath.
|
||||
else
|
||||
Caster.SendLocalizedMessage( 1061609 ); // That creature is already bonded in a Blood Oath.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Temporarily creates a dark pact between the caster and the target.
|
||||
* Any damage dealt by the target to the caster is increased, but the target receives the same amount of damage.
|
||||
* The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 80 ) + 8 seconds.
|
||||
*
|
||||
* NOTE: The above algorithm must be fixed point, it should be:
|
||||
* ((ss-rm)/8)+8
|
||||
*/
|
||||
|
||||
ExpireTimer timer = (ExpireTimer)m_Table[m];
|
||||
if ( timer != null )
|
||||
timer.DoExpire();
|
||||
|
||||
m_OathTable[Caster] = Caster;
|
||||
m_OathTable[m] = Caster;
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
Caster.PlaySound( 0x175 );
|
||||
|
||||
Caster.FixedParticles( 0x375A, 1, 17, 9919, 33, 7, EffectLayer.Waist );
|
||||
Caster.FixedParticles( 0x3728, 1, 13, 9502, 33, 7, (EffectLayer)255 );
|
||||
|
||||
m.FixedParticles( 0x375A, 1, 17, 9919, 33, 7, EffectLayer.Waist );
|
||||
m.FixedParticles( 0x3728, 1, 13, 9502, 33, 7, (EffectLayer)255 );
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( ((GetDamageSkill( Caster ) - GetResistSkill( m )) / 8) + 8 );
|
||||
m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); //Skill check for gain
|
||||
|
||||
timer = new ExpireTimer ( Caster, m, duration );
|
||||
timer.Start ();
|
||||
|
||||
BuffInfo.AddBuff ( Caster, new BuffInfo ( BuffIcon.BloodOathCaster, 1075659, duration, Caster, m.Name.ToString () ) );
|
||||
BuffInfo.AddBuff ( m, new BuffInfo ( BuffIcon.BloodOathCurse, 1075661, duration, m, Caster.Name.ToString () ) );
|
||||
|
||||
m_Table[m] = timer;
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public static bool RemoveCurse( Mobile m )
|
||||
{
|
||||
ExpireTimer t = (ExpireTimer)m_Table[m];
|
||||
|
||||
if ( t == null )
|
||||
return false;
|
||||
|
||||
t.DoExpire();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Hashtable m_OathTable = new Hashtable();
|
||||
private static Hashtable m_Table = new Hashtable ();
|
||||
|
||||
public static Mobile GetBloodOath( Mobile m )
|
||||
{
|
||||
if ( m == null )
|
||||
return null;
|
||||
|
||||
Mobile oath = (Mobile)m_OathTable[m];
|
||||
|
||||
if ( oath == m )
|
||||
oath = null;
|
||||
|
||||
return oath;
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private Mobile m_Caster;
|
||||
private Mobile m_Target;
|
||||
private DateTime m_End;
|
||||
|
||||
public ExpireTimer( Mobile caster, Mobile target, TimeSpan delay ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) )
|
||||
{
|
||||
m_Caster = caster;
|
||||
m_Target = target;
|
||||
m_End = DateTime.UtcNow + delay;
|
||||
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Caster.Deleted || m_Target.Deleted || !m_Caster.Alive || !m_Target.Alive || DateTime.UtcNow >= m_End )
|
||||
{
|
||||
DoExpire ();
|
||||
}
|
||||
}
|
||||
public void DoExpire()
|
||||
{
|
||||
if( m_OathTable.Contains( m_Caster ) )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 1061620 ); // Your Blood Oath has been broken.
|
||||
m_OathTable.Remove ( m_Caster );
|
||||
}
|
||||
|
||||
if( m_OathTable.Contains( m_Target ) )
|
||||
{
|
||||
m_Target.SendLocalizedMessage( 1061620 ); // Your Blood Oath has been broken.
|
||||
m_OathTable.Remove ( m_Target );
|
||||
}
|
||||
|
||||
Stop ();
|
||||
|
||||
BuffInfo.RemoveBuff ( m_Caster, BuffIcon.BloodOathCaster );
|
||||
BuffInfo.RemoveBuff ( m_Target, BuffIcon.BloodOathCurse );
|
||||
|
||||
m_Table.Remove ( m_Caster );
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private BloodOathSpell m_Owner;
|
||||
|
||||
public InternalTarget( BloodOathSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
else
|
||||
from.SendLocalizedMessage( 1060508 ); // You can't curse that.
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Scripts/Spells/Necromancy/CorpseSkin.cs
Normal file
157
Scripts/Spells/Necromancy/CorpseSkin.cs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class CorpseSkinSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Corpse Skin", "In Agle Corp Ylem",
|
||||
203,
|
||||
9051,
|
||||
Reagent.BatWing,
|
||||
Reagent.GraveDust
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 20.0; } }
|
||||
public override int RequiredMana{ get{ return 11; } }
|
||||
|
||||
public CorpseSkinSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Transmogrifies the flesh of the target creature or player to resemble rotted corpse flesh,
|
||||
* making them more vulnerable to Fire and Poison damage,
|
||||
* but increasing their resistance to Physical and Cold damage.
|
||||
*
|
||||
* The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 25 ) + 40 seconds.
|
||||
*
|
||||
* NOTE: Algorithm above is fixed point, should be:
|
||||
* ((ss-mr)/2.5) + 40
|
||||
*
|
||||
* NOTE: Resistance is not checked if targeting yourself
|
||||
*/
|
||||
|
||||
ExpireTimer timer = (ExpireTimer)m_Table[m];
|
||||
|
||||
if ( timer != null )
|
||||
timer.DoExpire();
|
||||
else
|
||||
m.SendLocalizedMessage( 1061689 ); // Your skin turns dry and corpselike.
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.FixedParticles( 0x373A, 1, 15, 9913, 67, 7, EffectLayer.Head );
|
||||
m.PlaySound( 0x1BB );
|
||||
|
||||
double ss = GetDamageSkill( Caster );
|
||||
double mr = ( Caster == m ? 0.0 : GetResistSkill( m ) );
|
||||
m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); //Skill check for gain
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( ((ss - mr) / 2.5) + 40.0 );
|
||||
|
||||
ResistanceMod[] mods = new ResistanceMod[4]
|
||||
{
|
||||
new ResistanceMod( ResistanceType.Fire, -15 ),
|
||||
new ResistanceMod( ResistanceType.Poison, -15 ),
|
||||
new ResistanceMod( ResistanceType.Cold, +10 ),
|
||||
new ResistanceMod( ResistanceType.Physical, +10 )
|
||||
};
|
||||
|
||||
timer = new ExpireTimer( m, mods, duration );
|
||||
timer.Start();
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.CorpseSkin, 1075663, duration, m ) );
|
||||
|
||||
m_Table[m] = timer;
|
||||
|
||||
for ( int i = 0; i < mods.Length; ++i )
|
||||
m.AddResistanceMod( mods[i] );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool RemoveCurse( Mobile m )
|
||||
{
|
||||
ExpireTimer t = (ExpireTimer)m_Table[m];
|
||||
|
||||
if ( t == null )
|
||||
return false;
|
||||
|
||||
m.SendLocalizedMessage( 1061688 ); // Your skin returns to normal.
|
||||
t.DoExpire();
|
||||
return true;
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
private ResistanceMod[] m_Mods;
|
||||
|
||||
public ExpireTimer( Mobile m, ResistanceMod[] mods, TimeSpan delay ) : base( delay )
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Mods = mods;
|
||||
}
|
||||
|
||||
public void DoExpire()
|
||||
{
|
||||
for ( int i = 0; i < m_Mods.Length; ++i )
|
||||
m_Mobile.RemoveResistanceMod( m_Mods[i] );
|
||||
|
||||
Stop();
|
||||
BuffInfo.RemoveBuff( m_Mobile, BuffIcon.CorpseSkin );
|
||||
m_Table.Remove( m_Mobile );
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile.SendLocalizedMessage( 1061688 ); // Your skin returns to normal.
|
||||
DoExpire();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private CorpseSkinSpell m_Owner;
|
||||
|
||||
public InternalTarget( CorpseSkinSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Scripts/Spells/Necromancy/CurseWeapon.cs
Normal file
106
Scripts/Spells/Necromancy/CurseWeapon.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class CurseWeaponSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Curse Weapon", "An Sanct Gra Char",
|
||||
203,
|
||||
9031,
|
||||
Reagent.PigIron
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.75 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 0.0; } }
|
||||
public override int RequiredMana{ get{ return 7; } }
|
||||
|
||||
public CurseWeaponSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
BaseWeapon weapon = Caster.Weapon as BaseWeapon;
|
||||
|
||||
if ( weapon == null || weapon is Fists )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 501078 ); // You must be holding a weapon.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
/* Temporarily imbues a weapon with a life draining effect.
|
||||
* Half the damage that the weapon inflicts is added to the necromancer's health.
|
||||
* The effects lasts for (Spirit Speak skill level / 34) + 1 seconds.
|
||||
*
|
||||
* NOTE: Above algorithm is fixed point, should be :
|
||||
* (Spirit Speak skill level / 3.4) + 1
|
||||
*
|
||||
* TODO: What happens if you curse a weapon then give it to someone else? Should they get the drain effect?
|
||||
*/
|
||||
|
||||
Caster.PlaySound( 0x387 );
|
||||
Caster.FixedParticles( 0x3779, 1, 15, 9905, 32, 2, EffectLayer.Head );
|
||||
Caster.FixedParticles( 0x37B9, 1, 14, 9502, 32, 5, (EffectLayer)255 );
|
||||
new SoundEffectTimer( Caster ).Start();
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (Caster.Skills[SkillName.SpiritSpeak].Value / 3.4) + 1.0 );
|
||||
|
||||
|
||||
Timer t = (Timer)m_Table[weapon];
|
||||
|
||||
if ( t != null )
|
||||
t.Stop();
|
||||
|
||||
weapon.Cursed = true;
|
||||
|
||||
m_Table[weapon] = t = new ExpireTimer( weapon, duration );
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private BaseWeapon m_Weapon;
|
||||
|
||||
public ExpireTimer( BaseWeapon weapon, TimeSpan delay ) : base( delay )
|
||||
{
|
||||
m_Weapon = weapon;
|
||||
Priority = TimerPriority.OneSecond;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Weapon.Cursed = false;
|
||||
Effects.PlaySound( m_Weapon.GetWorldLocation(), m_Weapon.Map, 0xFA );
|
||||
m_Table.Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
private class SoundEffectTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
|
||||
public SoundEffectTimer( Mobile m ) : base( TimeSpan.FromSeconds( 0.75 ) )
|
||||
{
|
||||
m_Mobile = m;
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile.PlaySound( 0xFA );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Scripts/Spells/Necromancy/EvilOmen.cs
Normal file
133
Scripts/Spells/Necromancy/EvilOmen.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class EvilOmenSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Evil Omen", "Pas Tym An Sanct",
|
||||
203,
|
||||
9031,
|
||||
Reagent.BatWing,
|
||||
Reagent.NoxCrystal
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(0.75); } }
|
||||
|
||||
public override double RequiredSkill { get { return 20.0; } }
|
||||
public override int RequiredMana { get { return 11; } }
|
||||
|
||||
public EvilOmenSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget(this);
|
||||
}
|
||||
|
||||
public void Target(Mobile m)
|
||||
{
|
||||
if (!(m is BaseCreature || m is PlayerMobile))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1060508); // You can't curse that.
|
||||
}
|
||||
else if (CheckHSequence(m))
|
||||
{
|
||||
SpellHelper.Turn(Caster, m);
|
||||
|
||||
/* Curses the target so that the next harmful event that affects them is magnified.
|
||||
* Damage to the target's hit points is increased 25%,
|
||||
* the poison level of the attack will be 1 higher
|
||||
* and the Resist Magic skill of the target will be fixed on 50.
|
||||
*
|
||||
* The effect lasts for one harmful event only.
|
||||
*/
|
||||
|
||||
if (m.Spell != null)
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.PlaySound(0xFC);
|
||||
m.FixedParticles(0x3728, 1, 13, 9912, 1150, 7, EffectLayer.Head);
|
||||
m.FixedParticles(0x3779, 1, 15, 9502, 67, 7, EffectLayer.Head);
|
||||
|
||||
if (!m_Table.Contains(m))
|
||||
{
|
||||
SkillMod mod = new DefaultSkillMod(SkillName.MagicResist, false, 50.0);
|
||||
|
||||
if (m.Skills[SkillName.MagicResist].Base > 50.0)
|
||||
m.AddSkillMod(mod);
|
||||
|
||||
m_Table[m] = mod;
|
||||
}
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds((Caster.Skills[SkillName.SpiritSpeak].Value / 12) + 1.0);
|
||||
|
||||
Timer.DelayCall(duration, new TimerStateCallback(EffectExpire_Callback), m);
|
||||
|
||||
HarmfulSpell( m );
|
||||
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.EvilOmen, 1075647, 1075648, duration, m));
|
||||
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
private static void EffectExpire_Callback(object state)
|
||||
{
|
||||
TryEndEffect((Mobile)state);
|
||||
}
|
||||
|
||||
/*
|
||||
* The naming here was confusing. Its a 1-off effect spell.
|
||||
* So, we dont actually "checkeffect"; we endeffect with bool
|
||||
* return to determine external behaviors.
|
||||
*
|
||||
* -refactored.
|
||||
*/
|
||||
|
||||
public static bool TryEndEffect(Mobile m)
|
||||
{
|
||||
SkillMod mod = (SkillMod)m_Table[m];
|
||||
|
||||
if (mod == null)
|
||||
return false;
|
||||
|
||||
m_Table.Remove(m);
|
||||
mod.Remove();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private EvilOmenSpell m_Owner;
|
||||
|
||||
public InternalTarget(EvilOmenSpell owner)
|
||||
: base(Core.ML ? 10 : 12, false, TargetFlags.Harmful)
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (o is Mobile)
|
||||
m_Owner.Target((Mobile)o);
|
||||
else
|
||||
from.SendLocalizedMessage(1060508); // You can't curse that.
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
201
Scripts/Spells/Necromancy/Exorcism.cs
Normal file
201
Scripts/Spells/Necromancy/Exorcism.cs
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Engines.CannedEvil;
|
||||
using Server.Guilds;
|
||||
using Server.Factions;
|
||||
using Server.Engines.PartySystem;
|
||||
using Server.Regions;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class ExorcismSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Exorcism", "Ort Corp Grav",
|
||||
203,
|
||||
9031,
|
||||
Reagent.NoxCrystal,
|
||||
Reagent.GraveDust
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 80.0; } }
|
||||
public override int RequiredMana { get { return 40; } }
|
||||
|
||||
public ExorcismSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if( Caster.Skills.SpiritSpeak.Value < 100.0 )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1072112 ); // You must have GM Spirit Speak to use this spell
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
|
||||
public override bool DelayedDamage { get { return false; } }
|
||||
|
||||
private static readonly int Range = (Core.ML ? 48 : 18);
|
||||
|
||||
public override int ComputeKarmaAward()
|
||||
{
|
||||
return 0; //no karma lost from this spell!
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
ChampionSpawnRegion r = Caster.Region.GetRegion( typeof( ChampionSpawnRegion ) ) as ChampionSpawnRegion;
|
||||
|
||||
if( r == null || !Caster.InRange( r.ChampionSpawn, Range ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1072111 ); // You are not in a valid exorcism region.
|
||||
}
|
||||
else if( CheckSequence() )
|
||||
{
|
||||
Map map = Caster.Map;
|
||||
|
||||
if( map != null )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
foreach( Mobile m in r.ChampionSpawn.GetMobilesInRange( Range ) )
|
||||
if( IsValidTarget( m ) )
|
||||
targets.Add( m );
|
||||
|
||||
for( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[i];
|
||||
|
||||
//Suprisingly, no sparkle type effects
|
||||
|
||||
m.Location = GetNearestShrine( m );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private bool IsValidTarget( Mobile m )
|
||||
{
|
||||
if( !m.Player || m.Alive )
|
||||
return false;
|
||||
|
||||
Corpse c = m.Corpse as Corpse;
|
||||
Map map = m.Map;
|
||||
|
||||
if( c != null && !c.Deleted && map != null && c.Map == map )
|
||||
{
|
||||
if( SpellHelper.IsAnyT2A( map, c.Location ) && SpellHelper.IsAnyT2A( map, m.Location ) )
|
||||
return false; //Same Map, both in T2A, ie, same 'sub server'.
|
||||
|
||||
if( m.Region.IsPartOf( typeof( DungeonRegion ) ) == Region.Find( c.Location, map ).IsPartOf( typeof( DungeonRegion ) ) )
|
||||
return false; //Same Map, both in Dungeon region OR They're both NOT in a dungeon region.
|
||||
|
||||
//Just an approximation cause RunUO doens't divide up the world the same way OSI does ;p
|
||||
|
||||
}
|
||||
|
||||
Party p = Party.Get( m );
|
||||
|
||||
if( p != null && p.Contains( Caster ) )
|
||||
return false;
|
||||
|
||||
if( m.Guild != null && Caster.Guild != null )
|
||||
{
|
||||
Guild mGuild = m.Guild as Guild;
|
||||
Guild cGuild = Caster.Guild as Guild;
|
||||
|
||||
if( mGuild.IsAlly( cGuild ) )
|
||||
return false;
|
||||
|
||||
if( mGuild == cGuild )
|
||||
return false;
|
||||
}
|
||||
|
||||
Faction f = Faction.Find( m );
|
||||
|
||||
if( Faction.Facet == m.Map && f != null && f == Faction.Find( Caster ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Point3D GetNearestShrine( Mobile m )
|
||||
{
|
||||
Map map = m.Map;
|
||||
|
||||
Point3D[] locList;
|
||||
|
||||
|
||||
if( map == Map.Felucca || map == Map.Trammel )
|
||||
locList = m_BritanniaLocs;
|
||||
else if( map == Map.Ilshenar )
|
||||
locList = m_IllshLocs;
|
||||
else if( map == Map.Tokuno )
|
||||
locList = m_TokunoLocs;
|
||||
else if( map == Map.Malas )
|
||||
locList = m_MalasLocs;
|
||||
else
|
||||
locList = new Point3D[0];
|
||||
|
||||
Point3D closest = Point3D.Zero;
|
||||
double minDist = double.MaxValue;
|
||||
|
||||
for( int i = 0; i < locList.Length; i++ )
|
||||
{
|
||||
Point3D p = locList[i];
|
||||
|
||||
double dist = m.GetDistanceToSqrt( p );
|
||||
if( minDist > dist )
|
||||
{
|
||||
closest = p;
|
||||
minDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
private static readonly Point3D[] m_BritanniaLocs = new Point3D[]
|
||||
{
|
||||
new Point3D( 1470, 843, 0 ),
|
||||
new Point3D( 1857, 865, -1 ),
|
||||
new Point3D( 4220, 563, 36 ),
|
||||
new Point3D( 1732, 3528, 0 ),
|
||||
new Point3D( 1300, 644, 8 ),
|
||||
new Point3D( 3355, 302, 9 ),
|
||||
new Point3D( 1606, 2490, 5 ),
|
||||
new Point3D( 2500, 3931, 3 ),
|
||||
new Point3D( 4264, 3707, 0 )
|
||||
};
|
||||
private static readonly Point3D[] m_IllshLocs = new Point3D[]
|
||||
{
|
||||
new Point3D( 1222, 474, -17 ),
|
||||
new Point3D( 718, 1360, -60),
|
||||
new Point3D( 297, 1014, -19 ),
|
||||
new Point3D( 986, 1006, -36 ),
|
||||
new Point3D( 1180, 1288, -30 ),
|
||||
new Point3D( 1538, 1341, -3 ),
|
||||
new Point3D( 528, 223, -38 )
|
||||
};
|
||||
private static readonly Point3D[] m_MalasLocs = new Point3D[]
|
||||
{
|
||||
new Point3D ( 976, 517, -30 )
|
||||
};
|
||||
private static readonly Point3D[] m_TokunoLocs = new Point3D[]
|
||||
{
|
||||
new Point3D( 710, 1162, 25 ),
|
||||
new Point3D( 1034, 515, 18 ),
|
||||
new Point3D( 295, 712, 55 )
|
||||
};
|
||||
}
|
||||
}
|
||||
44
Scripts/Spells/Necromancy/HorrificBeast.cs
Normal file
44
Scripts/Spells/Necromancy/HorrificBeast.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class HorrificBeastSpell : TransformationSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Horrific Beast", "Rel Xen Vas Bal",
|
||||
203,
|
||||
9031,
|
||||
Reagent.BatWing,
|
||||
Reagent.DaemonBlood
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 40.0; } }
|
||||
public override int RequiredMana{ get{ return 11; } }
|
||||
|
||||
public override int Body{ get{ return 746; } }
|
||||
|
||||
public HorrificBeastSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void DoEffect( Mobile m )
|
||||
{
|
||||
m.PlaySound( 0x165 );
|
||||
m.FixedParticles( 0x3728, 1, 13, 9918, 92, 3, EffectLayer.Head );
|
||||
|
||||
m.Delta( MobileDelta.WeaponDamage );
|
||||
m.CheckStatTimers();
|
||||
}
|
||||
|
||||
public override void RemoveEffect( Mobile m )
|
||||
{
|
||||
m.Delta( MobileDelta.WeaponDamage );
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Scripts/Spells/Necromancy/LichForm.cs
Normal file
48
Scripts/Spells/Necromancy/LichForm.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class LichFormSpell : TransformationSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Lich Form", "Rel Xen Corp Ort",
|
||||
203,
|
||||
9031,
|
||||
Reagent.GraveDust,
|
||||
Reagent.DaemonBlood,
|
||||
Reagent.NoxCrystal
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 70.0; } }
|
||||
public override int RequiredMana{ get{ return 23; } }
|
||||
|
||||
public override int Body{ get{ return 749; } }
|
||||
|
||||
public override int FireResistOffset{ get{ return -10; } }
|
||||
public override int ColdResistOffset{ get{ return +10; } }
|
||||
public override int PoisResistOffset{ get{ return +10; } }
|
||||
|
||||
public override double TickRate{ get{ return 2.5; } }
|
||||
|
||||
public LichFormSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void DoEffect( Mobile m )
|
||||
{
|
||||
m.PlaySound( 0x19C );
|
||||
m.FixedParticles( 0x3709, 1, 30, 9904, 1108, 6, EffectLayer.RightFoot );
|
||||
}
|
||||
|
||||
public override void OnTick( Mobile m )
|
||||
{
|
||||
--m.Hits;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Scripts/Spells/Necromancy/MindRot.cs
Normal file
182
Scripts/Spells/Necromancy/MindRot.cs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class MindRotSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Mind Rot", "Wis An Ben",
|
||||
203,
|
||||
9031,
|
||||
Reagent.BatWing,
|
||||
Reagent.PigIron,
|
||||
Reagent.DaemonBlood
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 30.0; } }
|
||||
public override int RequiredMana{ get{ return 17; } }
|
||||
|
||||
public MindRotSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( HasMindRotScalar( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Attempts to place a curse on the Target that increases the mana cost of any spells they cast,
|
||||
* for a duration based off a comparison between the Caster's Spirit Speak skill and the Target's Resisting Spells skill.
|
||||
* The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 50 ) + 20 seconds.
|
||||
*/
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.PlaySound( 0x1FB );
|
||||
m.PlaySound( 0x258 );
|
||||
m.FixedParticles( 0x373A, 1, 17, 9903, 15, 4, EffectLayer.Head );
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( (((GetDamageSkill( Caster ) - GetResistSkill( m )) / 5.0) + 20.0) * (m.Player ? 1.0 : 2.0 ) );
|
||||
m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); //Skill check for gain
|
||||
|
||||
if ( m.Player )
|
||||
SetMindRotScalar( Caster, m, 1.25, duration );
|
||||
else
|
||||
SetMindRotScalar( Caster, m, 2.00, duration );
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static void ClearMindRotScalar( Mobile m )
|
||||
{
|
||||
if (!m_Table.ContainsKey(m))
|
||||
return;
|
||||
|
||||
BuffInfo.RemoveBuff( m, BuffIcon.Mindrot );
|
||||
MRBucket tmpB = (MRBucket)m_Table[m];
|
||||
MRExpireTimer tmpT = (MRExpireTimer)tmpB.m_MRExpireTimer;
|
||||
tmpT.Stop();
|
||||
m_Table.Remove(m);
|
||||
m.SendLocalizedMessage(1060872); // Your mind feels normal again.
|
||||
}
|
||||
|
||||
public static bool HasMindRotScalar( Mobile m )
|
||||
{
|
||||
return m_Table.ContainsKey(m);
|
||||
}
|
||||
|
||||
public static bool GetMindRotScalar( Mobile m, ref double scalar )
|
||||
{
|
||||
if (!m_Table.ContainsKey(m))
|
||||
return false;
|
||||
|
||||
MRBucket tmpB = (MRBucket)m_Table[m];
|
||||
scalar = tmpB.m_Scalar;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SetMindRotScalar( Mobile caster, Mobile target, double scalar, TimeSpan duration )
|
||||
{
|
||||
if (!m_Table.ContainsKey(target))
|
||||
{
|
||||
m_Table.Add(target, new MRBucket(scalar, new MRExpireTimer(caster, target, duration)));
|
||||
BuffInfo.AddBuff(target, new BuffInfo(BuffIcon.Mindrot, 1075665, duration, target));
|
||||
MRBucket tmpB = (MRBucket)m_Table[target];
|
||||
MRExpireTimer tmpT = (MRExpireTimer)tmpB.m_MRExpireTimer;
|
||||
tmpT.Start();
|
||||
target.SendLocalizedMessage(1074384);
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private MindRotSpell m_Owner;
|
||||
|
||||
public InternalTarget( MindRotSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
else
|
||||
from.SendLocalizedMessage( 1060508 ); // You can't curse that.
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MRExpireTimer : Timer
|
||||
{
|
||||
private Mobile m_Caster;
|
||||
private Mobile m_Target;
|
||||
private DateTime m_End;
|
||||
|
||||
public MRExpireTimer( Mobile caster, Mobile target, TimeSpan delay ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) )
|
||||
{
|
||||
m_Caster = caster;
|
||||
m_Target = target;
|
||||
m_End = DateTime.UtcNow + delay;
|
||||
Priority = TimerPriority.TwoFiftyMS;
|
||||
}
|
||||
|
||||
public void RenewDelay(TimeSpan delay)
|
||||
{
|
||||
m_End = DateTime.UtcNow + delay;
|
||||
}
|
||||
|
||||
public void Halt()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Target.Deleted || !m_Target.Alive || DateTime.UtcNow >= m_End )
|
||||
{
|
||||
MindRotSpell.ClearMindRotScalar( m_Target );
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MRBucket
|
||||
{
|
||||
public MRBucket(double theScalar, MRExpireTimer theTimer)
|
||||
{
|
||||
m_Scalar = theScalar;
|
||||
m_MRExpireTimer = theTimer;
|
||||
}
|
||||
|
||||
public double m_Scalar;
|
||||
public MRExpireTimer m_MRExpireTimer;
|
||||
}
|
||||
}
|
||||
59
Scripts/Spells/Necromancy/NecromancerSpell.cs
Normal file
59
Scripts/Spells/Necromancy/NecromancerSpell.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public abstract class NecromancerSpell : Spell
|
||||
{
|
||||
public abstract double RequiredSkill{ get; }
|
||||
public abstract int RequiredMana{ get; }
|
||||
|
||||
public override SkillName CastSkill{ get{ return SkillName.Necromancy; } }
|
||||
public override SkillName DamageSkill{ get{ return SkillName.SpiritSpeak; } }
|
||||
|
||||
//public override int CastDelayBase{ get{ return base.CastDelayBase; } } // Reference, 3
|
||||
|
||||
public override bool ClearHandsOnCast{ get{ return false; } }
|
||||
|
||||
public override double CastDelayFastScalar{ get{ return (Core.SE? base.CastDelayFastScalar : 0); } } // Necromancer spells are not affected by fast cast items, though they are by fast cast recovery
|
||||
|
||||
public NecromancerSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public override int ComputeKarmaAward()
|
||||
{
|
||||
//TODO: Verify this formula being that Necro spells don't HAVE a circle.
|
||||
//int karma = -(70 + (10 * (int)Circle));
|
||||
int karma = -(40 + (int)(10 * (CastDelayBase.TotalSeconds / CastDelaySecondsPerTick)));
|
||||
|
||||
if ( Core.ML ) // Pub 36: "Added a new property called Increased Karma Loss which grants higher karma loss for casting necromancy spells."
|
||||
karma += AOS.Scale( karma, AosAttributes.GetValue( Caster, AosAttribute.IncreasedKarmaLoss ) );
|
||||
|
||||
return karma;
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
min = RequiredSkill;
|
||||
max = Scroll != null ? min : RequiredSkill + 40.0;
|
||||
}
|
||||
|
||||
public override bool ConsumeReagents()
|
||||
{
|
||||
if( base.ConsumeReagents() )
|
||||
return true;
|
||||
|
||||
if( ArcaneGem.ConsumeCharges( Caster, 1 ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return RequiredMana;
|
||||
}
|
||||
}
|
||||
}
|
||||
140
Scripts/Spells/Necromancy/PainSpike.cs
Normal file
140
Scripts/Spells/Necromancy/PainSpike.cs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class PainSpikeSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Pain Spike", "In Sar",
|
||||
203,
|
||||
9031,
|
||||
Reagent.GraveDust,
|
||||
Reagent.PigIron
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 20.0; } }
|
||||
public override int RequiredMana{ get{ return 5; } }
|
||||
|
||||
public PainSpikeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public override bool DelayedDamage{ get{ return false; } }
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
//SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); //Irrelevent asfter AoS
|
||||
|
||||
/* Temporarily causes intense physical pain to the target, dealing direct damage.
|
||||
* After 10 seconds the spell wears off, and if the target is still alive,
|
||||
* some of the Hit Points lost through Pain Spike are restored.
|
||||
*/
|
||||
|
||||
m.FixedParticles( 0x37C4, 1, 8, 9916, 39, 3, EffectLayer.Head );
|
||||
m.FixedParticles( 0x37C4, 1, 8, 9502, 39, 4, EffectLayer.Head );
|
||||
m.PlaySound( 0x210 );
|
||||
|
||||
double damage = ((GetDamageSkill( Caster ) - GetResistSkill( m )) / 10) + (m.Player ? 18 : 30);
|
||||
m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); //Skill check for gain
|
||||
|
||||
if ( damage < 1 )
|
||||
damage = 1;
|
||||
|
||||
TimeSpan buffTime = TimeSpan.FromSeconds( 10.0 );
|
||||
|
||||
if( m_Table.Contains( m ) )
|
||||
{
|
||||
damage = Utility.RandomMinMax( 3, 7 );
|
||||
Timer t = m_Table[m] as Timer;
|
||||
|
||||
if( t != null )
|
||||
{
|
||||
t.Delay += TimeSpan.FromSeconds( 2.0 );
|
||||
|
||||
buffTime = t.Next - DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new InternalTimer( m, damage ).Start();
|
||||
}
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.PainSpike, 1075667, buffTime, m, Convert.ToString( (int)damage ) ) );
|
||||
|
||||
Misc.WeightOverloading.DFA = Misc.DFAlgorithm.PainSpike;
|
||||
m.Damage( (int) damage, Caster );
|
||||
SpellHelper.DoLeech( (int)damage, Caster, m );
|
||||
Misc.WeightOverloading.DFA = Misc.DFAlgorithm.Standard;
|
||||
|
||||
//SpellHelper.Damage( this, m, damage, 100, 0, 0, 0, 0, Misc.DFAlgorithm.PainSpike );
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
private int m_ToRestore;
|
||||
|
||||
public InternalTimer( Mobile m, double toRestore ) : base( TimeSpan.FromSeconds( 10.0 ) )
|
||||
{
|
||||
Priority = TimerPriority.OneSecond;
|
||||
|
||||
m_Mobile = m;
|
||||
m_ToRestore = (int)toRestore;
|
||||
|
||||
m_Table[m] = this;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Table.Remove( m_Mobile );
|
||||
|
||||
if ( m_Mobile.Alive && !m_Mobile.IsDeadBondedPet )
|
||||
m_Mobile.Hits += m_ToRestore;
|
||||
|
||||
BuffInfo.RemoveBuff( m_Mobile, BuffIcon.PainSpike );
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private PainSpikeSpell m_Owner;
|
||||
|
||||
public InternalTarget( PainSpikeSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
Scripts/Spells/Necromancy/PoisonStrike.cs
Normal file
118
Scripts/Spells/Necromancy/PoisonStrike.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class PoisonStrikeSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Poison Strike", "In Vas Nox",
|
||||
203,
|
||||
9031,
|
||||
Reagent.NoxCrystal
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( (Core.ML ? 1.75 : 1.5) ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 50.0; } }
|
||||
public override int RequiredMana { get { return 17; } }
|
||||
|
||||
public PoisonStrikeSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public override bool DelayedDamage { get { return false; } }
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Creates a blast of poisonous energy centered on the target.
|
||||
* The main target is inflicted with a large amount of Poison damage, and all valid targets in a radius of 2 tiles around the main target are inflicted with a lesser effect.
|
||||
* One tile from main target receives 50% damage, two tiles from target receives 33% damage.
|
||||
*/
|
||||
|
||||
//CheckResisted( m ); // Check magic resist for skill, but do not use return value //reports from OSI: Necro spells don't give Resist gain
|
||||
|
||||
Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x36B0, 1, 14, 63, 7, 9915, 0 );
|
||||
Effects.PlaySound( m.Location, m.Map, 0x229 );
|
||||
|
||||
double damage = Utility.RandomMinMax( (Core.ML ? 32 : 36), 40 ) * ((300 + (GetDamageSkill( Caster ) * 9)) / 1000);
|
||||
|
||||
double sdiBonus = (double)AosAttributes.GetValue( Caster, AosAttribute.SpellDamage )/100;
|
||||
double pvmDamage = damage * (1 + sdiBonus);
|
||||
|
||||
if ( Core.ML && sdiBonus > 0.15 )
|
||||
sdiBonus = 0.15;
|
||||
double pvpDamage = damage * (1 + sdiBonus);
|
||||
|
||||
Map map = m.Map;
|
||||
|
||||
if( map != null )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
if ( Caster.CanBeHarmful(m, false ) )
|
||||
targets.Add( m );
|
||||
|
||||
foreach( Mobile targ in m.GetMobilesInRange( 2 ) )
|
||||
if(!(Caster is BaseCreature && targ is BaseCreature ))
|
||||
if( ( targ != Caster && m != targ ) && ( SpellHelper.ValidIndirectTarget( Caster, targ ) && Caster.CanBeHarmful( targ, false) ) )
|
||||
targets.Add( targ );
|
||||
|
||||
for( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile targ = targets[i];
|
||||
int num;
|
||||
|
||||
if( targ.InRange( m.Location, 0 ) )
|
||||
num = 1;
|
||||
else if( targ.InRange( m.Location, 1 ) )
|
||||
num = 2;
|
||||
else
|
||||
num = 3;
|
||||
|
||||
Caster.DoHarmful( targ );
|
||||
SpellHelper.Damage( this, targ, ((m.Player && Caster.Player) ? pvpDamage : pvmDamage) / num, 0, 0, 0, 100, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private PoisonStrikeSpell m_Owner;
|
||||
|
||||
public InternalTarget( PoisonStrikeSpell owner )
|
||||
: base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if( o is Mobile )
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
241
Scripts/Spells/Necromancy/Strangle.cs
Normal file
241
Scripts/Spells/Necromancy/Strangle.cs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class StrangleSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Strangle", "In Bal Nox",
|
||||
209,
|
||||
9031,
|
||||
Reagent.DaemonBlood,
|
||||
Reagent.NoxCrystal
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 65.0; } }
|
||||
public override int RequiredMana{ get{ return 29; } }
|
||||
|
||||
public StrangleSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
//SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); //Irrelevent after AoS
|
||||
|
||||
/* Temporarily chokes off the air suply of the target with poisonous fumes.
|
||||
* The target is inflicted with poison damage over time.
|
||||
* The amount of damage dealt each "hit" is based off of the caster's Spirit Speak skill and the Target's current Stamina.
|
||||
* The less Stamina the target has, the more damage is done by Strangle.
|
||||
* Duration of the effect is Spirit Speak skill level / 10 rounds, with a minimum number of 4 rounds.
|
||||
* The first round of damage is dealt after 5 seconds, and every next round after that comes 1 second sooner than the one before, until there is only 1 second between rounds.
|
||||
* The base damage of the effect lies between (Spirit Speak skill level / 10) - 2 and (Spirit Speak skill level / 10) + 1.
|
||||
* Base damage is multiplied by the following formula: (3 - (target's current Stamina / target's maximum Stamina) * 2).
|
||||
* Example:
|
||||
* For a target at full Stamina the damage multiplier is 1,
|
||||
* for a target at 50% Stamina the damage multiplier is 2 and
|
||||
* for a target at 20% Stamina the damage multiplier is 2.6
|
||||
*/
|
||||
|
||||
if ( m.Spell != null )
|
||||
m.Spell.OnCasterHurt();
|
||||
|
||||
m.PlaySound( 0x22F );
|
||||
m.FixedParticles( 0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head );
|
||||
m.FixedParticles( 0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255 );
|
||||
|
||||
if ( !m_Table.Contains( m ) )
|
||||
{
|
||||
Timer t = new InternalTimer( m, Caster );
|
||||
t.Start();
|
||||
|
||||
m_Table[m] = t;
|
||||
}
|
||||
|
||||
HarmfulSpell( m );
|
||||
}
|
||||
|
||||
//Calculations for the buff bar
|
||||
double spiritlevel = Caster.Skills[SkillName.SpiritSpeak].Value / 10;
|
||||
if (spiritlevel < 4)
|
||||
spiritlevel = 4;
|
||||
int d_MinDamage = 4;
|
||||
int d_MaxDamage = ((int)spiritlevel + 1) * 3;
|
||||
string args = String.Format("{0}\t{1}", d_MinDamage, d_MaxDamage);
|
||||
|
||||
int i_Count = (int)spiritlevel;
|
||||
int i_MaxCount = i_Count;
|
||||
int i_HitDelay = 5;
|
||||
int i_Length = i_HitDelay;
|
||||
|
||||
while (i_Count > 1)
|
||||
{
|
||||
--i_Count;
|
||||
if (i_HitDelay > 1)
|
||||
{
|
||||
if (i_MaxCount < 5)
|
||||
{
|
||||
--i_HitDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
int delay = (int)(Math.Ceiling((1.0 + (5 * i_Count)) / i_MaxCount));
|
||||
|
||||
if (delay <= 5)
|
||||
i_HitDelay = delay;
|
||||
else
|
||||
i_HitDelay = 5;
|
||||
}
|
||||
}
|
||||
i_Length += i_HitDelay;
|
||||
}
|
||||
TimeSpan t_Duration = TimeSpan.FromSeconds(i_Length);
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Strangle, 1075794, 1075795, t_Duration, m, args.ToString()));
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool RemoveCurse( Mobile m )
|
||||
{
|
||||
Timer t = (Timer)m_Table[m];
|
||||
|
||||
if ( t == null )
|
||||
return false;
|
||||
|
||||
t.Stop();
|
||||
m.SendLocalizedMessage( 1061687 ); // You can breath normally again.
|
||||
|
||||
m_Table.Remove( m );
|
||||
return true;
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private Mobile m_Target, m_From;
|
||||
private double m_MinBaseDamage, m_MaxBaseDamage;
|
||||
|
||||
private DateTime m_NextHit;
|
||||
private int m_HitDelay;
|
||||
|
||||
private int m_Count, m_MaxCount;
|
||||
|
||||
public InternalTimer( Mobile target, Mobile from ) : base( TimeSpan.FromSeconds( 0.1 ), TimeSpan.FromSeconds( 0.1 ) )
|
||||
{
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
|
||||
m_Target = target;
|
||||
m_From = from;
|
||||
|
||||
double spiritLevel = from.Skills[SkillName.SpiritSpeak].Value / 10;
|
||||
|
||||
m_MinBaseDamage = spiritLevel - 2;
|
||||
m_MaxBaseDamage = spiritLevel + 1;
|
||||
|
||||
m_HitDelay = 5;
|
||||
m_NextHit = DateTime.UtcNow + TimeSpan.FromSeconds( m_HitDelay );
|
||||
|
||||
m_Count = (int)spiritLevel;
|
||||
|
||||
if ( m_Count < 4 )
|
||||
m_Count = 4;
|
||||
|
||||
m_MaxCount = m_Count;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( !m_Target.Alive )
|
||||
{
|
||||
m_Table.Remove( m_Target );
|
||||
Stop();
|
||||
}
|
||||
|
||||
if ( !m_Target.Alive || DateTime.UtcNow < m_NextHit )
|
||||
return;
|
||||
|
||||
--m_Count;
|
||||
|
||||
if ( m_HitDelay > 1 )
|
||||
{
|
||||
if ( m_MaxCount < 5 )
|
||||
{
|
||||
--m_HitDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
int delay = (int)(Math.Ceiling( (1.0 + (5 * m_Count)) / m_MaxCount ) );
|
||||
|
||||
if ( delay <= 5 )
|
||||
m_HitDelay = delay;
|
||||
else
|
||||
m_HitDelay = 5;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_Count == 0 )
|
||||
{
|
||||
m_Target.SendLocalizedMessage( 1061687 ); // You can breath normally again.
|
||||
m_Table.Remove( m_Target );
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NextHit = DateTime.UtcNow + TimeSpan.FromSeconds( m_HitDelay );
|
||||
|
||||
double damage = m_MinBaseDamage + (Utility.RandomDouble() * (m_MaxBaseDamage - m_MinBaseDamage));
|
||||
|
||||
damage *= (3 - (((double)m_Target.Stam / m_Target.StamMax) * 2));
|
||||
|
||||
if ( damage < 1 )
|
||||
damage = 1;
|
||||
|
||||
if ( !m_Target.Player )
|
||||
damage *= 1.75;
|
||||
|
||||
AOS.Damage( m_Target, m_From, (int)damage, 0, 0, 0, 100, 0 );
|
||||
|
||||
if ( 0.60 <= Utility.RandomDouble() ) // OSI: randomly revealed between first and third damage tick, guessing 60% chance
|
||||
m_Target.RevealingAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private StrangleSpell m_Owner;
|
||||
|
||||
public InternalTarget( StrangleSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
210
Scripts/Spells/Necromancy/SummonFamiliar.cs
Normal file
210
Scripts/Spells/Necromancy/SummonFamiliar.cs
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Gumps;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class SummonFamiliarSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Summon Familiar", "Kal Xen Bal",
|
||||
203,
|
||||
9031,
|
||||
Reagent.BatWing,
|
||||
Reagent.GraveDust,
|
||||
Reagent.DaemonBlood
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 30.0; } }
|
||||
public override int RequiredMana{ get{ return 17; } }
|
||||
|
||||
public SummonFamiliarSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static Hashtable Table{ get{ return m_Table; } }
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
BaseCreature check = (BaseCreature)m_Table[Caster];
|
||||
|
||||
if ( check != null && !check.Deleted )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061605 ); // You already have a familiar.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( CheckSequence() )
|
||||
{
|
||||
Caster.CloseGump( typeof( SummonFamiliarGump ) );
|
||||
Caster.SendGump( new SummonFamiliarGump( Caster, m_Entries, this ) );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static SummonFamiliarEntry[] m_Entries = new SummonFamiliarEntry[]
|
||||
{
|
||||
new SummonFamiliarEntry( typeof( HordeMinionFamiliar ), 1060146, 30.0, 30.0 ), // Horde Minion
|
||||
new SummonFamiliarEntry( typeof( ShadowWispFamiliar ), 1060142, 50.0, 50.0 ), // Shadow Wisp
|
||||
new SummonFamiliarEntry( typeof( DarkWolfFamiliar ), 1060143, 60.0, 60.0 ), // Dark Wolf
|
||||
new SummonFamiliarEntry( typeof( DeathAdder ), 1060145, 80.0, 80.0 ), // Death Adder
|
||||
new SummonFamiliarEntry( typeof( VampireBatFamiliar ), 1060144, 100.0, 100.0 ) // Vampire Bat
|
||||
};
|
||||
|
||||
public static SummonFamiliarEntry[] Entries{ get{ return m_Entries; } }
|
||||
}
|
||||
|
||||
public class SummonFamiliarEntry
|
||||
{
|
||||
private Type m_Type;
|
||||
private object m_Name;
|
||||
private double m_ReqNecromancy;
|
||||
private double m_ReqSpiritSpeak;
|
||||
|
||||
public Type Type{ get{ return m_Type; } }
|
||||
public object Name{ get{ return m_Name; } }
|
||||
public double ReqNecromancy{ get{ return m_ReqNecromancy; } }
|
||||
public double ReqSpiritSpeak{ get{ return m_ReqSpiritSpeak; } }
|
||||
|
||||
public SummonFamiliarEntry( Type type, object name, double reqNecromancy, double reqSpiritSpeak )
|
||||
{
|
||||
m_Type = type;
|
||||
m_Name = name;
|
||||
m_ReqNecromancy = reqNecromancy;
|
||||
m_ReqSpiritSpeak = reqSpiritSpeak;
|
||||
}
|
||||
}
|
||||
|
||||
public class SummonFamiliarGump : Gump
|
||||
{
|
||||
private Mobile m_From;
|
||||
private SummonFamiliarEntry[] m_Entries;
|
||||
|
||||
private SummonFamiliarSpell m_Spell;
|
||||
|
||||
private const int EnabledColor16 = 0x0F20;
|
||||
private const int DisabledColor16 = 0x262A;
|
||||
|
||||
private const int EnabledColor32 = 0x18CD00;
|
||||
private const int DisabledColor32 = 0x4A8B52;
|
||||
|
||||
public SummonFamiliarGump( Mobile from, SummonFamiliarEntry[] entries, SummonFamiliarSpell spell ) : base( 200, 100 )
|
||||
{
|
||||
m_From = from;
|
||||
m_Entries = entries;
|
||||
m_Spell = spell;
|
||||
|
||||
AddPage( 0 );
|
||||
|
||||
AddBackground( 10, 10, 250, 178, 9270 );
|
||||
AddAlphaRegion( 20, 20, 230, 158 );
|
||||
|
||||
AddImage( 220, 20, 10464 );
|
||||
AddImage( 220, 72, 10464 );
|
||||
AddImage( 220, 124, 10464 );
|
||||
|
||||
AddItem( 188, 16, 6883 );
|
||||
AddItem( 198, 168, 6881 );
|
||||
AddItem( 8, 15, 6882 );
|
||||
AddItem( 2, 168, 6880 );
|
||||
|
||||
AddHtmlLocalized( 30, 26, 200, 20, 1060147, EnabledColor16, false, false ); // Chose thy familiar...
|
||||
|
||||
double necro = from.Skills[SkillName.Necromancy].Value;
|
||||
double spirit = from.Skills[SkillName.SpiritSpeak].Value;
|
||||
|
||||
for ( int i = 0; i < entries.Length; ++i )
|
||||
{
|
||||
object name = entries[i].Name;
|
||||
|
||||
bool enabled = ( necro >= entries[i].ReqNecromancy && spirit >= entries[i].ReqSpiritSpeak );
|
||||
|
||||
AddButton( 27, 53 + (i * 21), 9702, 9703, i + 1, GumpButtonType.Reply, 0 );
|
||||
|
||||
if ( name is int )
|
||||
AddHtmlLocalized( 50, 51 + (i * 21), 150, 20, (int)name, enabled ? EnabledColor16 : DisabledColor16, false, false );
|
||||
else if ( name is string )
|
||||
AddHtml( 50, 51 + (i * 21), 150, 20, String.Format( "<BASEFONT COLOR=#{0:X6}>{1}</BASEFONT>", enabled ? EnabledColor32 : DisabledColor32, name ), false, false );
|
||||
}
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public override void OnResponse( NetState sender, RelayInfo info )
|
||||
{
|
||||
int index = info.ButtonID - 1;
|
||||
|
||||
if ( index >= 0 && index < m_Entries.Length )
|
||||
{
|
||||
SummonFamiliarEntry entry = m_Entries[index];
|
||||
|
||||
double necro = m_From.Skills[SkillName.Necromancy].Value;
|
||||
double spirit = m_From.Skills[SkillName.SpiritSpeak].Value;
|
||||
|
||||
BaseCreature check = (BaseCreature)SummonFamiliarSpell.Table[m_From];
|
||||
|
||||
#region Dueling
|
||||
if ( m_From is PlayerMobile && ( (PlayerMobile)m_From ).DuelContext != null && !( (PlayerMobile)m_From ).DuelContext.AllowSpellCast( m_From, m_Spell ) )
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
else if ( check != null && !check.Deleted )
|
||||
{
|
||||
m_From.SendLocalizedMessage( 1061605 ); // You already have a familiar.
|
||||
}
|
||||
else if ( necro < entry.ReqNecromancy || spirit < entry.ReqSpiritSpeak )
|
||||
{
|
||||
// That familiar requires ~1_NECROMANCY~ Necromancy and ~2_SPIRIT~ Spirit Speak.
|
||||
m_From.SendLocalizedMessage( 1061606, String.Format( "{0:F1}\t{1:F1}", entry.ReqNecromancy, entry.ReqSpiritSpeak ) );
|
||||
|
||||
m_From.CloseGump( typeof( SummonFamiliarGump ) );
|
||||
m_From.SendGump( new SummonFamiliarGump( m_From, SummonFamiliarSpell.Entries, m_Spell ) );
|
||||
}
|
||||
else if ( entry.Type == null )
|
||||
{
|
||||
m_From.SendMessage( "That familiar has not yet been defined." );
|
||||
|
||||
m_From.CloseGump( typeof( SummonFamiliarGump ) );
|
||||
m_From.SendGump( new SummonFamiliarGump( m_From, SummonFamiliarSpell.Entries, m_Spell ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
BaseCreature bc = (BaseCreature)Activator.CreateInstance( entry.Type );
|
||||
|
||||
bc.Skills.MagicResist = m_From.Skills.MagicResist;
|
||||
|
||||
if ( BaseCreature.Summon( bc, m_From, m_From.Location, -1, TimeSpan.FromDays( 1.0 ) ) )
|
||||
{
|
||||
m_From.FixedParticles( 0x3728, 1, 10, 9910, EffectLayer.Head );
|
||||
bc.PlaySound( bc.GetIdleSound() );
|
||||
SummonFamiliarSpell.Table[m_From] = bc;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_From.SendLocalizedMessage( 1061825 ); // You decide not to summon a familiar.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Scripts/Spells/Necromancy/TransformationSpell.cs
Normal file
56
Scripts/Spells/Necromancy/TransformationSpell.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Server;
|
||||
using Server.Spells.Fifth;
|
||||
using Server.Spells.Seventh;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public abstract class TransformationSpell : NecromancerSpell, ITransformationSpell
|
||||
{
|
||||
public abstract int Body{ get; }
|
||||
public virtual int Hue{ get{ return 0; } }
|
||||
|
||||
public virtual int PhysResistOffset{ get{ return 0; } }
|
||||
public virtual int FireResistOffset{ get{ return 0; } }
|
||||
public virtual int ColdResistOffset{ get{ return 0; } }
|
||||
public virtual int PoisResistOffset{ get{ return 0; } }
|
||||
public virtual int NrgyResistOffset{ get{ return 0; } }
|
||||
|
||||
public TransformationSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool BlockedByHorrificBeast{ get{ return false; } }
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if( !TransformationSpellHelper.CheckCast( Caster, this ) )
|
||||
return false;
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
TransformationSpellHelper.OnCast( Caster, this );
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
public virtual double TickRate{ get{ return 1.0; } }
|
||||
|
||||
public virtual void OnTick( Mobile m )
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void DoEffect( Mobile m )
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RemoveEffect( Mobile m )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Scripts/Spells/Necromancy/VampiricEmbrace.cs
Normal file
54
Scripts/Spells/Necromancy/VampiricEmbrace.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class VampiricEmbraceSpell : TransformationSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Vampiric Embrace", "Rel Xen An Sanct",
|
||||
203,
|
||||
9031,
|
||||
Reagent.BatWing,
|
||||
Reagent.NoxCrystal,
|
||||
Reagent.PigIron
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 99.0; } }
|
||||
public override int RequiredMana{ get{ return 23; } }
|
||||
|
||||
public override int Body{ get{ return Caster.Female ? 745 : 744; } }
|
||||
public override int Hue{ get{ return 0x847E; } }
|
||||
|
||||
public override int FireResistOffset{ get{ return -25; } }
|
||||
|
||||
public VampiricEmbraceSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
if ( Caster.Skills[CastSkill].Value >= RequiredSkill )
|
||||
{
|
||||
min = 80.0;
|
||||
max = 120.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.GetCastSkills( out min, out max );
|
||||
}
|
||||
}
|
||||
|
||||
public override void DoEffect( Mobile m )
|
||||
{
|
||||
Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x373A, 1, 17, 1108, 7, 9914, 0 );
|
||||
Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x376A, 1, 22, 67, 7, 9502, 0 );
|
||||
Effects.PlaySound( m.Location, m.Map, 0x4B1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Scripts/Spells/Necromancy/VengefulSpirit.cs
Normal file
97
Scripts/Spells/Necromancy/VengefulSpirit.cs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class VengefulSpiritSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Vengeful Spirit", "Kal Xen Bal Beh",
|
||||
203,
|
||||
9031,
|
||||
Reagent.BatWing,
|
||||
Reagent.GraveDust,
|
||||
Reagent.PigIron
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 80.0; } }
|
||||
public override int RequiredMana{ get{ return 41; } }
|
||||
|
||||
public VengefulSpiritSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( (Caster.Followers + 3) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( Caster == m )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061832 ); // You cannot exact vengeance on yourself.
|
||||
}
|
||||
else if ( CheckHSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
/* Summons a Revenant which haunts the target until either the target or the Revenant is dead.
|
||||
* Revenants have the ability to track down their targets wherever they may travel.
|
||||
* A Revenant's strength is determined by the Necromancy and Spirit Speak skills of the Caster.
|
||||
* The effect lasts for ((Spirit Speak skill level * 80) / 120) + 10 seconds.
|
||||
*/
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( ((GetDamageSkill( Caster ) * 80) / 120) + 10 );
|
||||
|
||||
Revenant rev = new Revenant( Caster, m, duration );
|
||||
|
||||
if ( BaseCreature.Summon( rev, false, Caster, m.Location, 0x81, TimeSpan.FromSeconds( duration.TotalSeconds + 2.0 ) ) )
|
||||
rev.FixedParticles( 0x373A, 1, 15, 9909, EffectLayer.Waist );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private VengefulSpiritSpell m_Owner;
|
||||
|
||||
public InternalTarget( VengefulSpiritSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
m_Owner.Target( (Mobile) o );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
Scripts/Spells/Necromancy/Wither.cs
Normal file
109
Scripts/Spells/Necromancy/Wither.cs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class WitherSpell : NecromancerSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Wither", "Kal Vas An Flam",
|
||||
203,
|
||||
9031,
|
||||
Reagent.NoxCrystal,
|
||||
Reagent.GraveDust,
|
||||
Reagent.PigIron
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill { get { return 60.0; } }
|
||||
|
||||
public override int RequiredMana { get { return 23; } }
|
||||
|
||||
public WitherSpell( Mobile caster, Item scroll )
|
||||
: base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool DelayedDamage { get { return false; } }
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if( CheckSequence() )
|
||||
{
|
||||
/* Creates a withering frost around the Caster,
|
||||
* which deals Cold Damage to all valid targets in a radius of 5 tiles.
|
||||
*/
|
||||
|
||||
Map map = Caster.Map;
|
||||
|
||||
if( map != null )
|
||||
{
|
||||
List<Mobile> targets = new List<Mobile>();
|
||||
|
||||
BaseCreature cbc = Caster as BaseCreature;
|
||||
bool isMonster = ( cbc != null && !cbc.Controlled && !cbc.Summoned );
|
||||
|
||||
foreach( Mobile m in Caster.GetMobilesInRange( Core.ML ? 4 : 5 ) )
|
||||
{
|
||||
if( Caster != m && Caster.InLOS( m ) && ( isMonster || SpellHelper.ValidIndirectTarget( Caster, m ) ) && Caster.CanBeHarmful( m, false ) )
|
||||
{
|
||||
if ( isMonster )
|
||||
{
|
||||
if ( m is BaseCreature )
|
||||
{
|
||||
BaseCreature bc = (BaseCreature)m;
|
||||
|
||||
if ( !bc.Controlled && !bc.Summoned && bc.Team == cbc.Team )
|
||||
continue;
|
||||
}
|
||||
else if ( !m.Player )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
targets.Add( m );
|
||||
}
|
||||
}
|
||||
|
||||
Effects.PlaySound( Caster.Location, map, 0x1FB );
|
||||
Effects.PlaySound( Caster.Location, map, 0x10B );
|
||||
Effects.SendLocationParticles( EffectItem.Create( Caster.Location, map, EffectItem.DefaultDuration ), 0x37CC, 1, 40, 97, 3, 9917, 0 );
|
||||
|
||||
for( int i = 0; i < targets.Count; ++i )
|
||||
{
|
||||
Mobile m = targets[ i ];
|
||||
|
||||
Caster.DoHarmful( m );
|
||||
m.FixedParticles( 0x374A, 1, 15, 9502, 97, 3, (EffectLayer)255 );
|
||||
|
||||
double damage = Utility.RandomMinMax( 30, 35 );
|
||||
|
||||
damage *= ( 300 + ( m.Karma / 100 ) + ( GetDamageSkill( Caster ) * 10 ) );
|
||||
damage /= 1000;
|
||||
|
||||
int sdiBonus = AosAttributes.GetValue( Caster, AosAttribute.SpellDamage );
|
||||
|
||||
// PvP spell damage increase cap of 15% from an item’s magic property in Publish 33(SE)
|
||||
if( Core.SE && m.Player && Caster.Player && sdiBonus > 15 )
|
||||
sdiBonus = 15;
|
||||
|
||||
damage *= ( 100 + sdiBonus );
|
||||
damage /= 100;
|
||||
|
||||
// TODO: cap?
|
||||
//if ( damage > 40 )
|
||||
// damage = 40;
|
||||
|
||||
SpellHelper.Damage( this, m, damage, 0, 0, 100, 0, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Scripts/Spells/Necromancy/WraithForm.cs
Normal file
53
Scripts/Spells/Necromancy/WraithForm.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Necromancy
|
||||
{
|
||||
public class WraithFormSpell : TransformationSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Wraith Form", "Rel Xen Um",
|
||||
203,
|
||||
9031,
|
||||
Reagent.NoxCrystal,
|
||||
Reagent.PigIron
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 20.0; } }
|
||||
public override int RequiredMana{ get{ return 17; } }
|
||||
|
||||
public override int Body{ get{ return Caster.Female ? 747 : 748; } }
|
||||
public override int Hue{ get{ return Caster.Female ? 0 : 0x4001; } }
|
||||
|
||||
public override int PhysResistOffset{ get{ return +15; } }
|
||||
public override int FireResistOffset{ get{ return -5; } }
|
||||
public override int ColdResistOffset{ get{ return 0; } }
|
||||
public override int PoisResistOffset{ get{ return 0; } }
|
||||
public override int NrgyResistOffset{ get{ return -5; } }
|
||||
|
||||
public WraithFormSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override void DoEffect( Mobile m )
|
||||
{
|
||||
if ( m is PlayerMobile )
|
||||
((PlayerMobile)m).IgnoreMobiles = true;
|
||||
|
||||
m.PlaySound( 0x17F );
|
||||
m.FixedParticles( 0x374A, 1, 15, 9902, 1108, 4, EffectLayer.Waist );
|
||||
}
|
||||
|
||||
public override void RemoveEffect( Mobile m )
|
||||
{
|
||||
if ( m is PlayerMobile && m.AccessLevel == AccessLevel.Player )
|
||||
((PlayerMobile)m).IgnoreMobiles = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
619
Scripts/Spells/Ninjitsu/AnimalForm.cs
Normal file
619
Scripts/Spells/Ninjitsu/AnimalForm.cs
Normal file
|
|
@ -0,0 +1,619 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Gumps;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
using Server.Spells.Fifth;
|
||||
using Server.Spells.Seventh;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class AnimalForm : NinjaSpell
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.Login += new LoginEventHandler(OnLogin);
|
||||
}
|
||||
|
||||
public static void OnLogin(LoginEventArgs e)
|
||||
{
|
||||
AnimalFormContext context = AnimalForm.GetContext(e.Mobile);
|
||||
|
||||
if (context != null && context.SpeedBoost)
|
||||
e.Mobile.Send(SpeedControl.MountSpeed);
|
||||
}
|
||||
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Animal Form", null,
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(1.0); } }
|
||||
|
||||
public override double RequiredSkill { get { return 0.0; } }
|
||||
public override int RequiredMana { get { return (Core.ML ? 10 : 0); } }
|
||||
public override int CastRecoveryBase { get { return (Core.ML ? 10 : base.CastRecoveryBase); } }
|
||||
|
||||
public override bool BlockedByAnimalForm { get { return false; } }
|
||||
|
||||
public AnimalForm(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if (!Caster.CanBeginAction(typeof(PolymorphSpell)))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061628); // You can't do that while polymorphed.
|
||||
return false;
|
||||
}
|
||||
else if (TransformationSpellHelper.UnderTransformation(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1063219); // You cannot mimic an animal while in that form.
|
||||
return false;
|
||||
}
|
||||
else if (DisguiseTimers.IsDisguised(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061631); // You can't do that while disguised.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override bool CheckDisturb(DisturbType type, bool firstCircle, bool resistable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CasterIsMoving()
|
||||
{
|
||||
return (Core.TickCount - Caster.LastMoveTime <= Caster.ComputeMovementSpeed(Caster.Direction));
|
||||
}
|
||||
|
||||
private bool m_WasMoving;
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
Caster.FixedEffect(0x37C4, 10, 14, 4, 3);
|
||||
m_WasMoving = CasterIsMoving();
|
||||
}
|
||||
|
||||
public override bool CheckFizzle()
|
||||
{
|
||||
// Spell is initially always successful, and with no skill gain.
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if (!Caster.CanBeginAction(typeof(PolymorphSpell)))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1061628); // You can't do that while polymorphed.
|
||||
}
|
||||
else if (TransformationSpellHelper.UnderTransformation(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1063219); // You cannot mimic an animal while in that form.
|
||||
}
|
||||
else if (!Caster.CanBeginAction(typeof(IncognitoSpell)) || (Caster.IsBodyMod && GetContext(Caster) == null))
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
else if (CheckSequence())
|
||||
{
|
||||
AnimalFormContext context = GetContext(Caster);
|
||||
|
||||
int mana = ScaleMana(RequiredMana);
|
||||
if (mana > Caster.Mana)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1060174, mana.ToString()); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
}
|
||||
else if (context != null)
|
||||
{
|
||||
RemoveContext(Caster, context, true);
|
||||
Caster.Mana -= mana;
|
||||
}
|
||||
else if (Caster is PlayerMobile)
|
||||
{
|
||||
bool skipGump = (m_WasMoving || CasterIsMoving());
|
||||
|
||||
if (GetLastAnimalForm(Caster) == -1 || !skipGump)
|
||||
{
|
||||
Caster.CloseGump(typeof(AnimalFormGump));
|
||||
Caster.SendGump(new AnimalFormGump(Caster, m_Entries, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Morph(Caster, GetLastAnimalForm(Caster)) == MorphResult.Fail)
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
|
||||
Caster.Mana -= mana;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Morph(Caster, GetLastAnimalForm(Caster)) == MorphResult.Fail)
|
||||
{
|
||||
DoFizzle();
|
||||
}
|
||||
else
|
||||
{
|
||||
Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
|
||||
Caster.Mana -= mana;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private static Hashtable m_LastAnimalForms = new Hashtable();
|
||||
|
||||
public int GetLastAnimalForm(Mobile m)
|
||||
{
|
||||
if (m_LastAnimalForms.Contains(m))
|
||||
return (int)m_LastAnimalForms[m];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public enum MorphResult
|
||||
{
|
||||
Success,
|
||||
Fail,
|
||||
NoSkill
|
||||
}
|
||||
|
||||
public static MorphResult Morph(Mobile m, int entryID)
|
||||
{
|
||||
if (entryID < 0 || entryID >= m_Entries.Length)
|
||||
return MorphResult.Fail;
|
||||
|
||||
AnimalFormEntry entry = m_Entries[entryID];
|
||||
|
||||
m_LastAnimalForms[m] = entryID; //On OSI, it's the last /attempted/ one not the last succeeded one
|
||||
|
||||
if (m.Skills.Ninjitsu.Value < entry.ReqSkill)
|
||||
{
|
||||
string args = String.Format("{0}\t{1}\t ", entry.ReqSkill.ToString("F1"), SkillName.Ninjitsu);
|
||||
m.SendLocalizedMessage(1063013, args); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
|
||||
return MorphResult.NoSkill;
|
||||
}
|
||||
|
||||
/*
|
||||
if( !m.CheckSkill( SkillName.Ninjitsu, entry.ReqSkill, entry.ReqSkill + 37.5 ) )
|
||||
return MorphResult.Fail;
|
||||
*
|
||||
* On OSI,it seems you can only gain starting at '0' using Animal form.
|
||||
*/
|
||||
|
||||
double ninjitsu = m.Skills.Ninjitsu.Value;
|
||||
|
||||
if (ninjitsu < entry.ReqSkill + 37.5)
|
||||
{
|
||||
double chance = (ninjitsu - entry.ReqSkill) / 37.5;
|
||||
|
||||
if (chance < Utility.RandomDouble())
|
||||
return MorphResult.Fail;
|
||||
}
|
||||
|
||||
m.CheckSkill(SkillName.Ninjitsu, 0.0, 37.5);
|
||||
|
||||
if (!BaseFormTalisman.EntryEnabled(m, entry.Type))
|
||||
return MorphResult.Success; // Still consumes mana, just no effect
|
||||
|
||||
BaseMount.Dismount(m);
|
||||
|
||||
int bodyMod = entry.BodyMod;
|
||||
int hueMod = entry.HueMod;
|
||||
|
||||
m.BodyMod = bodyMod;
|
||||
m.HueMod = hueMod;
|
||||
|
||||
if (entry.SpeedBoost)
|
||||
m.Send(SpeedControl.MountSpeed);
|
||||
|
||||
SkillMod mod = null;
|
||||
|
||||
if (entry.StealthBonus)
|
||||
{
|
||||
mod = new DefaultSkillMod(SkillName.Stealth, true, 20.0);
|
||||
mod.ObeyCap = true;
|
||||
m.AddSkillMod(mod);
|
||||
}
|
||||
|
||||
SkillMod stealingMod = null;
|
||||
|
||||
if (entry.StealingBonus)
|
||||
{
|
||||
stealingMod = new DefaultSkillMod(SkillName.Stealing, true, 10.0);
|
||||
stealingMod.ObeyCap = true;
|
||||
m.AddSkillMod(stealingMod);
|
||||
}
|
||||
|
||||
Timer timer = new AnimalFormTimer(m, bodyMod, hueMod);
|
||||
timer.Start();
|
||||
|
||||
AddContext(m, new AnimalFormContext(timer, mod, entry.SpeedBoost, entry.Type, stealingMod));
|
||||
m.CheckStatTimers();
|
||||
return MorphResult.Success;
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static void AddContext(Mobile m, AnimalFormContext context)
|
||||
{
|
||||
m_Table[m] = context;
|
||||
|
||||
if (context.Type == typeof(BakeKitsune) || context.Type == typeof(GreyWolf))
|
||||
m.CheckStatTimers();
|
||||
}
|
||||
|
||||
public static void RemoveContext(Mobile m, bool resetGraphics)
|
||||
{
|
||||
AnimalFormContext context = GetContext(m);
|
||||
|
||||
if (context != null)
|
||||
RemoveContext(m, context, resetGraphics);
|
||||
}
|
||||
|
||||
public static void RemoveContext(Mobile m, AnimalFormContext context, bool resetGraphics)
|
||||
{
|
||||
m_Table.Remove(m);
|
||||
|
||||
if (context.SpeedBoost)
|
||||
m.Send(SpeedControl.Disable);
|
||||
|
||||
SkillMod mod = context.Mod;
|
||||
|
||||
if (mod != null)
|
||||
m.RemoveSkillMod(mod);
|
||||
|
||||
mod = context.StealingMod;
|
||||
|
||||
if (mod != null)
|
||||
m.RemoveSkillMod(mod);
|
||||
|
||||
if (resetGraphics)
|
||||
{
|
||||
m.HueMod = -1;
|
||||
m.BodyMod = 0;
|
||||
}
|
||||
|
||||
m.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
|
||||
|
||||
context.Timer.Stop();
|
||||
}
|
||||
|
||||
public static AnimalFormContext GetContext(Mobile m)
|
||||
{
|
||||
return (m_Table[m] as AnimalFormContext);
|
||||
}
|
||||
|
||||
public static bool UnderTransformation(Mobile m)
|
||||
{
|
||||
return (GetContext(m) != null);
|
||||
}
|
||||
|
||||
public static bool UnderTransformation(Mobile m, Type type)
|
||||
{
|
||||
AnimalFormContext context = GetContext(m);
|
||||
|
||||
return (context != null && context.Type == type);
|
||||
}
|
||||
|
||||
/*
|
||||
private delegate void AnimalFormCallback( Mobile from );
|
||||
private delegate bool AnimalFormRequirementCallback( Mobile from );
|
||||
*/
|
||||
|
||||
public class AnimalFormEntry
|
||||
{
|
||||
private Type m_Type;
|
||||
private TextDefinition m_Name;
|
||||
private int m_ItemID;
|
||||
private int m_Hue;
|
||||
private int m_Tooltip;
|
||||
private double m_ReqSkill;
|
||||
private int m_BodyMod;
|
||||
private int m_HueModMin;
|
||||
private int m_HueModMax;
|
||||
private bool m_StealthBonus;
|
||||
private bool m_SpeedBoost;
|
||||
private bool m_StealingBonus;
|
||||
|
||||
public Type Type { get { return m_Type; } }
|
||||
public TextDefinition Name { get { return m_Name; } }
|
||||
public int ItemID { get { return m_ItemID; } }
|
||||
public int Hue { get { return m_Hue; } }
|
||||
public int Tooltip { get { return m_Tooltip; } }
|
||||
public double ReqSkill { get { return m_ReqSkill; } }
|
||||
public int BodyMod { get { return m_BodyMod; } }
|
||||
public int HueMod { get { return Utility.RandomMinMax( m_HueModMin, m_HueModMax ); } }
|
||||
public bool StealthBonus { get { return m_StealthBonus; } }
|
||||
public bool SpeedBoost { get { return m_SpeedBoost; } }
|
||||
public bool StealingBonus { get { return m_StealingBonus; } }
|
||||
/*
|
||||
private AnimalFormCallback m_TransformCallback;
|
||||
private AnimalFormCallback m_UntransformCallback;
|
||||
private AnimalFormRequirementCallback m_RequirementCallback;
|
||||
*/
|
||||
|
||||
public AnimalFormEntry(Type type, TextDefinition name, int itemID, int hue, int tooltip, double reqSkill, int bodyMod, int hueModMin, int hueModMax, bool stealthBonus, bool speedBoost, bool stealingBonus)
|
||||
{
|
||||
m_Type = type;
|
||||
m_Name = name;
|
||||
m_ItemID = itemID;
|
||||
m_Hue = hue;
|
||||
m_Tooltip = tooltip;
|
||||
m_ReqSkill = reqSkill;
|
||||
m_BodyMod = bodyMod;
|
||||
m_HueModMin = hueModMin;
|
||||
m_HueModMax = hueModMax;
|
||||
m_StealthBonus = stealthBonus;
|
||||
m_SpeedBoost = speedBoost;
|
||||
m_StealingBonus = stealingBonus;
|
||||
}
|
||||
}
|
||||
|
||||
private static AnimalFormEntry[] m_Entries = new AnimalFormEntry[]
|
||||
{
|
||||
new AnimalFormEntry( typeof( Kirin ), 1029632, 9632, 0, 1070811, 100.0, 0x84, 0, 0, false, true, false ),
|
||||
new AnimalFormEntry( typeof( Unicorn ), 1018214, 9678, 0, 1070812, 100.0, 0x7A, 0, 0, false, true, false ),
|
||||
new AnimalFormEntry( typeof( BakeKitsune ), 1030083, 10083, 0, 1070810, 82.5, 0xF6, 0, 0, false, true, false ),
|
||||
new AnimalFormEntry( typeof( GreyWolf ), 1028482, 9681, 2309, 1070810, 82.5, 0x19, 0x8FD, 0x90E, false, true, false ),
|
||||
new AnimalFormEntry( typeof( Llama ), 1028438, 8438, 0, 1070809, 70.0, 0xDC, 0, 0, false, true, false ),
|
||||
new AnimalFormEntry( typeof( ForestOstard ), 1018273, 8503, 2212, 1070809, 70.0, 0xDB, 0x899, 0x8B0, false, true, false ),
|
||||
new AnimalFormEntry( typeof( BullFrog ), 1028496, 8496, 2003, 1070807, 50.0, 0x51, 0x7D1, 0x7D6, false, false, false ),
|
||||
new AnimalFormEntry( typeof( GiantSerpent ), 1018114, 9663, 2009, 1070808, 50.0, 0x15, 0x7D1, 0x7E2, false, false, false ),
|
||||
new AnimalFormEntry( typeof( Dog ), 1018280, 8476, 2309, 1070806, 40.0, 0xD9, 0x8FD, 0x90E, false, false, false ),
|
||||
new AnimalFormEntry( typeof( Cat ), 1018264, 8475, 2309, 1070806, 40.0, 0xC9, 0x8FD, 0x90E, false, false, false ),
|
||||
new AnimalFormEntry( typeof( Rat ), 1018294, 8483, 2309, 1070805, 20.0, 0xEE, 0x8FD, 0x90E, true, false, false ),
|
||||
new AnimalFormEntry( typeof( Rabbit ), 1028485, 8485, 2309, 1070805, 20.0, 0xCD, 0x8FD, 0x90E, true, false, false ),
|
||||
new AnimalFormEntry( typeof( Squirrel ), 1031671, 11671, 0, 0, 20.0, 0x116, 0, 0, false, false, false ),
|
||||
new AnimalFormEntry( typeof( Ferret ), 1031672, 11672, 0, 1075220, 40.0, 0x117, 0, 0, false, false, true ),
|
||||
new AnimalFormEntry( typeof( CuSidhe ), 1031670, 11670, 0, 1075221, 60.0, 0x115, 0, 0, false, false, false ),
|
||||
new AnimalFormEntry( typeof( Reptalon ), 1075202, 11669, 0, 1075222, 90.0, 0x114, 0, 0, false, false, false ),
|
||||
};
|
||||
|
||||
public static AnimalFormEntry[] Entries { get { return m_Entries; } }
|
||||
|
||||
public class AnimalFormGump : Gump
|
||||
{
|
||||
//TODO: Convert this for ML to the BaseImageTileButtonsgump
|
||||
private Mobile m_Caster;
|
||||
private AnimalForm m_Spell;
|
||||
private Item m_Talisman;
|
||||
|
||||
public AnimalFormGump(Mobile caster, AnimalFormEntry[] entries, AnimalForm spell)
|
||||
: base(50, 50)
|
||||
{
|
||||
m_Caster = caster;
|
||||
m_Spell = spell;
|
||||
m_Talisman = caster.Talisman;
|
||||
|
||||
AddPage(0);
|
||||
|
||||
AddBackground(0, 0, 520, 404, 0x13BE);
|
||||
AddImageTiled(10, 10, 500, 20, 0xA40);
|
||||
AddImageTiled(10, 40, 500, 324, 0xA40);
|
||||
AddImageTiled(10, 374, 500, 20, 0xA40);
|
||||
AddAlphaRegion(10, 10, 500, 384);
|
||||
|
||||
AddHtmlLocalized(14, 12, 500, 20, 1063394, 0x7FFF, false, false); // <center>Polymorph Selection Menu</center>
|
||||
|
||||
AddButton(10, 374, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
|
||||
AddHtmlLocalized(45, 376, 450, 20, 1011012, 0x7FFF, false, false); // CANCEL
|
||||
|
||||
double ninjitsu = caster.Skills.Ninjitsu.Value;
|
||||
|
||||
int current = 0;
|
||||
|
||||
for (int i = 0; i < entries.Length; ++i)
|
||||
{
|
||||
bool enabled = (ninjitsu >= entries[i].ReqSkill && BaseFormTalisman.EntryEnabled(caster, entries[i].Type));
|
||||
|
||||
int page = current / 10 + 1;
|
||||
int pos = current % 10;
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
if (page > 1)
|
||||
{
|
||||
AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page);
|
||||
AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF, false, false); // Next
|
||||
}
|
||||
|
||||
AddPage(page);
|
||||
|
||||
if (page > 1)
|
||||
{
|
||||
AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 1);
|
||||
AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF, false, false); // Back
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
int x = (pos % 2 == 0) ? 14 : 264;
|
||||
int y = (pos / 2) * 64 + 44;
|
||||
|
||||
Rectangle2D b = ItemBounds.Table[entries[i].ItemID];
|
||||
|
||||
AddImageTiledButton(x, y, 0x918, 0x919, i + 1, GumpButtonType.Reply, 0, entries[i].ItemID, entries[i].Hue, 40 - b.Width / 2 - b.X, 30 - b.Height / 2 - b.Y, entries[i].Tooltip);
|
||||
AddHtmlLocalized(x + 84, y, 250, 60, entries[i].Name, 0x7FFF, false, false);
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnResponse(NetState sender, RelayInfo info)
|
||||
{
|
||||
int entryID = info.ButtonID - 1;
|
||||
|
||||
if (entryID < 0 || entryID >= m_Entries.Length)
|
||||
return;
|
||||
|
||||
int mana = m_Spell.ScaleMana(m_Spell.RequiredMana);
|
||||
AnimalFormEntry entry = AnimalForm.Entries[entryID];
|
||||
|
||||
if (mana > m_Caster.Mana)
|
||||
{
|
||||
m_Caster.SendLocalizedMessage(1060174, mana.ToString()); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
}
|
||||
else if( ( m_Caster is PlayerMobile ) && ( m_Caster as PlayerMobile ).MountBlockReason != BlockMountType.None )
|
||||
{
|
||||
m_Caster.SendLocalizedMessage( 1063108 ); // You cannot use this ability right now.
|
||||
}
|
||||
else if (BaseFormTalisman.EntryEnabled(sender.Mobile, entry.Type))
|
||||
{
|
||||
#region Dueling
|
||||
if ( m_Caster is PlayerMobile && ((PlayerMobile)m_Caster).DuelContext != null && !((PlayerMobile)m_Caster).DuelContext.AllowSpellCast( m_Caster, m_Spell ) )
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
else if (AnimalForm.Morph(m_Caster, entryID) == MorphResult.Fail)
|
||||
{
|
||||
m_Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502632 ); // The spell fizzles.
|
||||
m_Caster.FixedParticles( 0x3735, 1, 30, 9503, EffectLayer.Waist );
|
||||
m_Caster.PlaySound( 0x5C );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
|
||||
m_Caster.Mana -= mana;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimalFormContext
|
||||
{
|
||||
private Timer m_Timer;
|
||||
private SkillMod m_Mod;
|
||||
private bool m_SpeedBoost;
|
||||
private Type m_Type;
|
||||
private SkillMod m_StealingMod;
|
||||
|
||||
public Timer Timer { get { return m_Timer; } }
|
||||
public SkillMod Mod { get { return m_Mod; } }
|
||||
public bool SpeedBoost { get { return m_SpeedBoost; } }
|
||||
public Type Type { get { return m_Type; } }
|
||||
public SkillMod StealingMod { get { return m_StealingMod; } }
|
||||
|
||||
public AnimalFormContext(Timer timer, SkillMod mod, bool speedBoost, Type type, SkillMod stealingMod)
|
||||
{
|
||||
m_Timer = timer;
|
||||
m_Mod = mod;
|
||||
m_SpeedBoost = speedBoost;
|
||||
m_Type = type;
|
||||
m_StealingMod = stealingMod;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimalFormTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
private int m_Body;
|
||||
private int m_Hue;
|
||||
private int m_Counter;
|
||||
private Mobile m_LastTarget;
|
||||
|
||||
public AnimalFormTimer(Mobile from, int body, int hue)
|
||||
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
|
||||
{
|
||||
m_Mobile = from;
|
||||
m_Body = body;
|
||||
m_Hue = hue;
|
||||
m_Counter = 0;
|
||||
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Body || m_Mobile.Hue != m_Hue)
|
||||
{
|
||||
AnimalForm.RemoveContext(m_Mobile, true);
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Body == 0x115) // Cu Sidhe
|
||||
{
|
||||
if (m_Counter++ >= 8)
|
||||
{
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax && m_Mobile.Backpack != null)
|
||||
{
|
||||
Bandage b = m_Mobile.Backpack.FindItemByType(typeof(Bandage)) as Bandage;
|
||||
|
||||
if (b != null)
|
||||
{
|
||||
m_Mobile.Hits += Utility.RandomMinMax(20, 50);
|
||||
b.Consume();
|
||||
}
|
||||
}
|
||||
|
||||
m_Counter = 0;
|
||||
}
|
||||
}
|
||||
else if (m_Body == 0x114) // Reptalon
|
||||
{
|
||||
if (m_Mobile.Combatant != null && m_Mobile.Combatant != m_LastTarget)
|
||||
{
|
||||
m_Counter = 1;
|
||||
m_LastTarget = m_Mobile.Combatant;
|
||||
}
|
||||
|
||||
if (m_Mobile.Warmode && m_LastTarget != null && m_LastTarget.Alive && !m_LastTarget.Deleted && m_Counter-- <= 0)
|
||||
{
|
||||
if (m_Mobile.CanBeHarmful(m_LastTarget) && m_LastTarget.Map == m_Mobile.Map && m_LastTarget.InRange(m_Mobile.Location, BaseCreature.DefaultRangePerception) && m_Mobile.InLOS(m_LastTarget))
|
||||
{
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_LastTarget);
|
||||
m_Mobile.Freeze(TimeSpan.FromSeconds(1));
|
||||
m_Mobile.PlaySound(0x16A);
|
||||
|
||||
Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(1.3), new TimerStateCallback<Mobile>(BreathEffect_Callback), m_LastTarget);
|
||||
}
|
||||
|
||||
m_Counter = Math.Min((int)m_Mobile.GetDistanceToSqrt(m_LastTarget), 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BreathEffect_Callback(Mobile target)
|
||||
{
|
||||
if (m_Mobile.CanBeHarmful(target))
|
||||
{
|
||||
m_Mobile.RevealingAction();
|
||||
m_Mobile.PlaySound(0x227);
|
||||
Effects.SendMovingEffect(m_Mobile, target, 0x36D4, 5, 0, false, false, 0, 0);
|
||||
|
||||
Timer.DelayCall<Mobile>(TimeSpan.FromSeconds(1), new TimerStateCallback<Mobile>(BreathDamage_Callback), target);
|
||||
}
|
||||
}
|
||||
|
||||
public void BreathDamage_Callback(Mobile target)
|
||||
{
|
||||
if (m_Mobile.CanBeHarmful(target))
|
||||
{
|
||||
m_Mobile.RevealingAction();
|
||||
m_Mobile.DoHarmful(target);
|
||||
AOS.Damage(target, m_Mobile, 20, !target.Player, 0, 100, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Scripts/Spells/Ninjitsu/Backstab.cs
Normal file
80
Scripts/Spells/Ninjitsu/Backstab.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
using Server.SkillHandlers;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class Backstab : NinjaMove
|
||||
{
|
||||
public Backstab()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 30; } }
|
||||
public override double RequiredSkill{ get{ return Core.ML ? 40.0 : 20.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063089 ); } } // You prepare to Backstab your opponent.
|
||||
|
||||
public override double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
return 1.0 + (ninjitsu / 360) + Tracking.GetStalkingBonus( attacker, defender ) / 100;
|
||||
}
|
||||
|
||||
public override bool Validate( Mobile from )
|
||||
{
|
||||
if( !from.Hidden || from.AllowedStealthSteps <= 0 )
|
||||
{
|
||||
from.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.Validate( from );
|
||||
}
|
||||
|
||||
public override bool OnBeforeSwing( Mobile attacker, Mobile defender )
|
||||
{
|
||||
bool valid = Validate( attacker ) && CheckMana( attacker, true );
|
||||
|
||||
if( valid )
|
||||
{
|
||||
attacker.BeginAction( typeof( Stealth ) );
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), delegate { attacker.EndAction( typeof( Stealth ) ); } );
|
||||
}
|
||||
|
||||
return valid;
|
||||
|
||||
}
|
||||
|
||||
public override bool ValidatesDuringHit { get { return false; } }
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
//Validates before swing
|
||||
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063090 ); // You quickly stab your opponent as you come out of hiding!
|
||||
|
||||
defender.FixedParticles( 0x37B9, 1, 5, 0x251D, 0x651, 0, EffectLayer.Waist );
|
||||
|
||||
attacker.RevealingAction();
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
|
||||
public override void OnMiss( Mobile attacker, Mobile defender )
|
||||
{
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063161 ); // You failed to properly use the element of surprise.
|
||||
|
||||
attacker.RevealingAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
172
Scripts/Spells/Ninjitsu/DeathStrike.cs
Normal file
172
Scripts/Spells/Ninjitsu/DeathStrike.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
using Server.SkillHandlers;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class DeathStrike : NinjaMove
|
||||
{
|
||||
public DeathStrike()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana { get { return 30; } }
|
||||
public override double RequiredSkill { get { return 85.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage { get { return new TextDefinition( 1063091 ); } } // You prepare to hit your opponent with a Death Strike.
|
||||
|
||||
public override double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
if( !Validate( attacker ) || !CheckMana( attacker, true ) )
|
||||
return;
|
||||
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
double chance;
|
||||
bool isRanged = false; // should be defined onHit method, what if the player hit and remove the weapon before process? ;)
|
||||
|
||||
if ( attacker.Weapon is BaseRanged )
|
||||
isRanged = true;
|
||||
|
||||
if( ninjitsu < 100 ) //This formula is an approximation from OSI data. TODO: find correct formula
|
||||
chance = 30 + (ninjitsu - 85) * 2.2;
|
||||
else
|
||||
chance = 63 + (ninjitsu - 100) * 1.1;
|
||||
|
||||
if( (chance / 100) < Utility.RandomDouble() )
|
||||
{
|
||||
attacker.SendLocalizedMessage( 1070779 ); // You missed your opponent with a Death Strike.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DeathStrikeInfo info;
|
||||
|
||||
int damageBonus = 0;
|
||||
|
||||
if( m_Table.Contains( defender ) )
|
||||
{
|
||||
defender.SendLocalizedMessage( 1063092 ); // Your opponent lands another Death Strike!
|
||||
|
||||
info = (DeathStrikeInfo)m_Table[defender];
|
||||
|
||||
if( info.m_Steps > 0 )
|
||||
damageBonus = attacker.Skills[SkillName.Ninjitsu].Fixed / 150;
|
||||
|
||||
if( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
m_Table.Remove( defender );
|
||||
}
|
||||
else
|
||||
{
|
||||
defender.SendLocalizedMessage( 1063093 ); // You have been hit by a Death Strike! Move with caution!
|
||||
}
|
||||
|
||||
attacker.SendLocalizedMessage( 1063094 ); // You inflict a Death Strike upon your opponent!
|
||||
|
||||
defender.FixedParticles( 0x374A, 1, 17, 0x26BC, EffectLayer.Waist );
|
||||
attacker.PlaySound( attacker.Female ? 0x50D : 0x50E );
|
||||
|
||||
info = new DeathStrikeInfo( defender, attacker, damageBonus, isRanged );
|
||||
info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( ProcessDeathStrike ), defender );
|
||||
|
||||
m_Table[defender] = info;
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
private class DeathStrikeInfo
|
||||
{
|
||||
public Mobile m_Target;
|
||||
public Mobile m_Attacker;
|
||||
public int m_Steps;
|
||||
public int m_DamageBonus;
|
||||
public Timer m_Timer;
|
||||
public bool m_isRanged;
|
||||
|
||||
public DeathStrikeInfo( Mobile target, Mobile attacker, int damageBonus, bool isRanged )
|
||||
{
|
||||
m_Target = target;
|
||||
m_Attacker = attacker;
|
||||
m_DamageBonus = damageBonus;
|
||||
m_isRanged = isRanged;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddStep( Mobile m )
|
||||
{
|
||||
DeathStrikeInfo info = m_Table[m] as DeathStrikeInfo;
|
||||
|
||||
if( info == null )
|
||||
return;
|
||||
|
||||
if( ++info.m_Steps >= 5 )
|
||||
ProcessDeathStrike( m );
|
||||
}
|
||||
|
||||
private static void ProcessDeathStrike( object state )
|
||||
{
|
||||
Mobile defender = (Mobile)state;
|
||||
|
||||
DeathStrikeInfo info = m_Table[defender] as DeathStrikeInfo;
|
||||
|
||||
if( info == null ) //sanity
|
||||
return;
|
||||
|
||||
int maxDamage, damage = 0;
|
||||
|
||||
double ninjitsu = info.m_Attacker.Skills[SkillName.Ninjitsu].Value;
|
||||
double stalkingBonus = Tracking.GetStalkingBonus( info.m_Attacker, info.m_Target );
|
||||
|
||||
if ( Core.ML )
|
||||
{
|
||||
double scalar = ( info.m_Attacker.Skills[SkillName.Hiding].Value + info.m_Attacker.Skills[SkillName.Stealth].Value ) / 220;
|
||||
|
||||
if ( scalar > 1 )
|
||||
scalar = 1;
|
||||
|
||||
// New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30.
|
||||
if ( info.m_Steps >= 5 )
|
||||
damage = (int)Math.Floor( Math.Min( 60, ( ninjitsu / 3 ) * ( 0.3 + 0.7 * scalar ) + stalkingBonus ) );
|
||||
else
|
||||
damage = (int)Math.Floor( Math.Min( 30, ( ninjitsu / 9 ) * ( 0.3 + 0.7 * scalar ) + stalkingBonus ) );
|
||||
|
||||
if ( info.m_isRanged )
|
||||
damage /= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int divisor = (info.m_Steps >= 5) ? 30 : 80;
|
||||
double baseDamage = ninjitsu / divisor * 10;
|
||||
|
||||
maxDamage = (info.m_Steps >= 5) ? 62 : 22; // DamageBonus is 8 at most. That brings the cap up to 70/30.
|
||||
damage = Math.Max( 0, Math.Min( maxDamage, (int)( baseDamage + stalkingBonus ) ) ) + info.m_DamageBonus;
|
||||
}
|
||||
|
||||
if ( Core.ML )
|
||||
info.m_Target.Damage( damage, info.m_Attacker ); // Damage is direct.
|
||||
else
|
||||
AOS.Damage( info.m_Target, info.m_Attacker, damage, true, 100, 0, 0, 0, 0, 0, 0, false, false, true ); // Damage is physical.
|
||||
|
||||
if( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
m_Table.Remove( info.m_Target );
|
||||
}
|
||||
}
|
||||
}
|
||||
74
Scripts/Spells/Ninjitsu/FocusAttack.cs
Normal file
74
Scripts/Spells/Ninjitsu/FocusAttack.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class FocusAttack : NinjaMove
|
||||
{
|
||||
public FocusAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return Core.ML ? 10 : 20; } }
|
||||
public override double RequiredSkill{ get{ return Core.ML? 30.0 : 60 ; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063095 ); } } // You prepare to focus all of your abilities into your next strike.
|
||||
|
||||
public override bool Validate( Mobile from )
|
||||
{
|
||||
if ( from.FindItemOnLayer( Layer.TwoHanded ) as BaseShield != null )
|
||||
{
|
||||
from.SendLocalizedMessage( 1063096 ); // You cannot use this ability while holding a shield.
|
||||
return false;
|
||||
}
|
||||
|
||||
Item handOne = from.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon;
|
||||
|
||||
if ( handOne != null && !(handOne is BaseRanged) )
|
||||
return base.Validate( from );
|
||||
|
||||
Item handTwo = from.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon;
|
||||
|
||||
if ( handTwo != null && !(handTwo is BaseRanged) )
|
||||
return base.Validate( from );
|
||||
|
||||
from.SendLocalizedMessage( 1063097 ); // You must be wielding a melee weapon without a shield to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
public override double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
return 1.0 + (ninjitsu * ninjitsu) / 43636;
|
||||
}
|
||||
|
||||
public override double GetPropertyBonus( Mobile attacker )
|
||||
{
|
||||
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
double bonus = (ninjitsu * ninjitsu) / 43636;
|
||||
|
||||
return 1.0 + (bonus * 3 + 0.01);
|
||||
}
|
||||
|
||||
public override bool OnBeforeDamage( Mobile attacker, Mobile defender )
|
||||
{
|
||||
return Validate( attacker ) && CheckMana( attacker, true );
|
||||
}
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063098 ); // You focus all of your abilities and strike with deadly force!
|
||||
attacker.PlaySound( 0x510 );
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Scripts/Spells/Ninjitsu/KiAttack.cs
Normal file
149
Scripts/Spells/Ninjitsu/KiAttack.cs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class KiAttack : NinjaMove
|
||||
{
|
||||
public KiAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 25; } }
|
||||
public override double RequiredSkill{ get{ return 80.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063099 ); } } // Your Ki Attack must be complete within 2 seconds for the damage bonus!
|
||||
|
||||
public override void OnUse( Mobile from )
|
||||
{
|
||||
if ( !Validate( from ) )
|
||||
return;
|
||||
|
||||
KiAttackInfo info = new KiAttackInfo( from );
|
||||
info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( EndKiAttack ), info );
|
||||
|
||||
m_Table[from] = info;
|
||||
}
|
||||
|
||||
public override bool Validate( Mobile from )
|
||||
{
|
||||
if ( from.Hidden && from.AllowedStealthSteps > 0 )
|
||||
{
|
||||
from.SendLocalizedMessage( 1063127 ); // You cannot use this ability while in stealth mode.
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Core.ML )
|
||||
{
|
||||
BaseRanged ranged = from.Weapon as BaseRanged;
|
||||
|
||||
if( ranged != null )
|
||||
{
|
||||
from.SendLocalizedMessage( 1075858 ); // You can only use this with melee attacks.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return base.Validate( from );
|
||||
}
|
||||
|
||||
public override double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
if ( attacker.Hidden )
|
||||
return 1.0;
|
||||
|
||||
/*
|
||||
* Pub40 changed pvp damage max to 55%
|
||||
*/
|
||||
|
||||
return 1.0 + GetBonus(attacker) / ( (Core.ML && attacker.Player && defender.Player) ? 40 : 10 );
|
||||
}
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
if ( !Validate( attacker ) || !CheckMana( attacker, true ) )
|
||||
return;
|
||||
|
||||
if ( GetBonus( attacker ) == 0.0 )
|
||||
{
|
||||
attacker.SendLocalizedMessage( 1063101 ); // You were too close to your target to cause any additional damage.
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker.FixedParticles( 0x37BE, 1, 5, 0x26BD, 0x0, 0x1, EffectLayer.Waist );
|
||||
attacker.PlaySound( 0x510 );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063100 ); // Your quick flight to your target causes extra damage as you strike!
|
||||
defender.FixedParticles( 0x37BE, 1, 5, 0x26BD, 0, 0x1, EffectLayer.Waist );
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
|
||||
ClearCurrentMove( attacker );
|
||||
}
|
||||
|
||||
public override void OnClearMove( Mobile from )
|
||||
{
|
||||
KiAttackInfo info = m_Table[from] as KiAttackInfo;
|
||||
|
||||
if ( info != null )
|
||||
{
|
||||
if ( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
m_Table.Remove( info.m_Mobile );
|
||||
}
|
||||
}
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static double GetBonus( Mobile from )
|
||||
{
|
||||
KiAttackInfo info = m_Table[from] as KiAttackInfo;
|
||||
|
||||
if ( info == null )
|
||||
return 0.0;
|
||||
|
||||
int xDelta = info.m_Location.X - from.X;
|
||||
int yDelta = info.m_Location.Y - from.Y;
|
||||
|
||||
double bonus = Math.Sqrt( (xDelta * xDelta) + (yDelta * yDelta) );
|
||||
|
||||
if ( bonus > 20.0 )
|
||||
bonus = 20.0;
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
private class KiAttackInfo
|
||||
{
|
||||
public Mobile m_Mobile;
|
||||
public Point3D m_Location;
|
||||
public Timer m_Timer;
|
||||
|
||||
public KiAttackInfo( Mobile m )
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Location = m.Location;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EndKiAttack( object state )
|
||||
{
|
||||
KiAttackInfo info = (KiAttackInfo)state;
|
||||
|
||||
if ( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
ClearCurrentMove( info.m_Mobile );
|
||||
info.m_Mobile.SendLocalizedMessage( 1063102 ); // You failed to complete your Ki Attack in time.
|
||||
|
||||
m_Table.Remove( info.m_Mobile );
|
||||
}
|
||||
}
|
||||
}
|
||||
269
Scripts/Spells/Ninjitsu/MirrorImage.cs
Normal file
269
Scripts/Spells/Ninjitsu/MirrorImage.cs
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Spells.Ninjitsu;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class MirrorImage : NinjaSpell
|
||||
{
|
||||
private static Dictionary<Mobile, int> m_CloneCount = new Dictionary<Mobile, int>();
|
||||
|
||||
public static bool HasClone( Mobile m )
|
||||
{
|
||||
return m_CloneCount.ContainsKey( m );
|
||||
}
|
||||
|
||||
public static void AddClone( Mobile m )
|
||||
{
|
||||
if ( m == null )
|
||||
return;
|
||||
|
||||
if ( m_CloneCount.ContainsKey( m ) )
|
||||
m_CloneCount[m]++;
|
||||
else
|
||||
m_CloneCount[m] = 1;
|
||||
}
|
||||
|
||||
public static void RemoveClone( Mobile m )
|
||||
{
|
||||
if ( m == null )
|
||||
return;
|
||||
|
||||
if ( m_CloneCount.ContainsKey( m ) )
|
||||
{
|
||||
m_CloneCount[m]--;
|
||||
|
||||
if ( m_CloneCount[m] == 0 )
|
||||
m_CloneCount.Remove( m );
|
||||
}
|
||||
}
|
||||
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Mirror Image", null,
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return Core.ML ? 20.0 : 40.0; } }
|
||||
public override int RequiredMana{ get{ return 10; } }
|
||||
|
||||
public override bool BlockedByAnimalForm{ get{ return false; } }
|
||||
|
||||
public MirrorImage( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Caster.Mounted )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063132 ); // You cannot use this ability while mounted.
|
||||
return false;
|
||||
}
|
||||
else if ( (Caster.Followers + 1) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063133 ); // You cannot summon a mirror image because you have too many followers.
|
||||
return false;
|
||||
}
|
||||
else if( TransformationSpellHelper.UnderTransformation( Caster, typeof( HorrificBeastSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnBeginCast()
|
||||
{
|
||||
base.OnBeginCast();
|
||||
|
||||
Caster.SendLocalizedMessage( 1063134 ); // You begin to summon a mirror image of yourself.
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if ( Caster.Mounted )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063132 ); // You cannot use this ability while mounted.
|
||||
}
|
||||
else if ( (Caster.Followers + 1) > Caster.FollowersMax )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063133 ); // You cannot summon a mirror image because you have too many followers.
|
||||
}
|
||||
else if( TransformationSpellHelper.UnderTransformation( Caster, typeof( HorrificBeastSpell ) ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
Caster.FixedParticles( 0x376A, 1, 14, 0x13B5, EffectLayer.Waist );
|
||||
Caster.PlaySound( 0x511 );
|
||||
|
||||
new Clone( Caster ).MoveToWorld( Caster.Location, Caster.Map );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class Clone : BaseCreature
|
||||
{
|
||||
private Mobile m_Caster;
|
||||
|
||||
public Clone( Mobile caster ) : base( AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4 )
|
||||
{
|
||||
m_Caster = caster;
|
||||
|
||||
Body = caster.Body;
|
||||
|
||||
Hue = caster.Hue;
|
||||
Female = caster.Female;
|
||||
|
||||
Name = caster.Name;
|
||||
NameHue = caster.NameHue;
|
||||
|
||||
Title = caster.Title;
|
||||
Kills = caster.Kills;
|
||||
|
||||
HairItemID = caster.HairItemID;
|
||||
HairHue = caster.HairHue;
|
||||
|
||||
FacialHairItemID = caster.FacialHairItemID;
|
||||
FacialHairHue = caster.FacialHairHue;
|
||||
|
||||
for ( int i = 0; i < caster.Skills.Length; ++i )
|
||||
{
|
||||
Skills[i].Base = caster.Skills[i].Base;
|
||||
Skills[i].Cap = caster.Skills[i].Cap;
|
||||
}
|
||||
|
||||
for( int i = 0; i < caster.Items.Count; i++ )
|
||||
{
|
||||
AddItem( CloneItem( caster.Items[i] ) );
|
||||
}
|
||||
|
||||
Warmode = true;
|
||||
|
||||
Summoned = true;
|
||||
SummonMaster = caster;
|
||||
|
||||
ControlOrder = OrderType.Follow;
|
||||
ControlTarget = caster;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds( 30 + caster.Skills.Ninjitsu.Fixed / 40 );
|
||||
|
||||
new UnsummonTimer( caster, this, duration ).Start();
|
||||
SummonEnd = DateTime.UtcNow + duration;
|
||||
|
||||
MirrorImage.AddClone( m_Caster );
|
||||
}
|
||||
|
||||
protected override BaseAI ForcedAI { get { return new CloneAI( this ); } }
|
||||
|
||||
public override bool IsHumanInTown() { return false; }
|
||||
|
||||
private Item CloneItem( Item item )
|
||||
{
|
||||
Item newItem = new Item( item.ItemID );
|
||||
newItem.Hue = item.Hue;
|
||||
newItem.Layer = item.Layer;
|
||||
|
||||
return newItem;
|
||||
}
|
||||
|
||||
public override void OnDamage( int amount, Mobile from, bool willKill )
|
||||
{
|
||||
Delete();
|
||||
}
|
||||
|
||||
public override bool DeleteCorpseOnDeath { get { return true; } }
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 10, 15, 5042 );
|
||||
|
||||
base.OnDelete();
|
||||
}
|
||||
|
||||
public override void OnAfterDelete()
|
||||
{
|
||||
MirrorImage.RemoveClone( m_Caster );
|
||||
base.OnAfterDelete();
|
||||
}
|
||||
|
||||
public override bool IsDispellable { get { return false; } }
|
||||
public override bool Commandable { get { return false; } }
|
||||
|
||||
public Clone( Serial serial ) : base( serial )
|
||||
{
|
||||
}
|
||||
|
||||
public override void Serialize( GenericWriter writer )
|
||||
{
|
||||
base.Serialize( writer );
|
||||
|
||||
writer.WriteEncodedInt( 0 ); // version
|
||||
|
||||
writer.Write( m_Caster );
|
||||
}
|
||||
|
||||
public override void Deserialize( GenericReader reader )
|
||||
{
|
||||
base.Deserialize( reader );
|
||||
|
||||
int version = reader.ReadEncodedInt();
|
||||
|
||||
m_Caster = reader.ReadMobile();
|
||||
|
||||
MirrorImage.AddClone( m_Caster );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class CloneAI : BaseAI
|
||||
{
|
||||
public CloneAI( Clone m ) : base ( m )
|
||||
{
|
||||
m.CurrentSpeed = m.ActiveSpeed;
|
||||
}
|
||||
|
||||
public override bool Think()
|
||||
{
|
||||
// Clones only follow their owners
|
||||
Mobile master = m_Mobile.SummonMaster;
|
||||
|
||||
if ( master != null && master.Map == m_Mobile.Map && master.InRange( m_Mobile, m_Mobile.RangePerception ) )
|
||||
{
|
||||
int iCurrDist = (int)m_Mobile.GetDistanceToSqrt( master );
|
||||
bool bRun = (iCurrDist > 5);
|
||||
|
||||
WalkMobileRange( master, 2, bRun, 0, 1 );
|
||||
}
|
||||
else
|
||||
WalkRandom( 2, 2, 1 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanDetectHidden { get { return false; } }
|
||||
}
|
||||
}
|
||||
19
Scripts/Spells/Ninjitsu/NinjaMove.cs
Normal file
19
Scripts/Spells/Ninjitsu/NinjaMove.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class NinjaMove : SpecialMove
|
||||
{
|
||||
public override SkillName MoveSkill{ get{ return SkillName.Ninjitsu; } }
|
||||
|
||||
public override void CheckGain( Mobile m )
|
||||
{
|
||||
m.CheckSkill( MoveSkill, RequiredSkill - 12.5, RequiredSkill + 37.5 ); //Per five on friday 02/16/07
|
||||
}
|
||||
}
|
||||
}
|
||||
105
Scripts/Spells/Ninjitsu/NinjaSpell.cs
Normal file
105
Scripts/Spells/Ninjitsu/NinjaSpell.cs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using Server;
|
||||
using Server.Spells;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public abstract class NinjaSpell : Spell
|
||||
{
|
||||
public abstract double RequiredSkill{ get; }
|
||||
public abstract int RequiredMana{ get; }
|
||||
|
||||
public override SkillName CastSkill{ get{ return SkillName.Ninjitsu; } }
|
||||
public override SkillName DamageSkill{ get{ return SkillName.Ninjitsu; } }
|
||||
|
||||
public override bool RevealOnCast{ get{ return false; } }
|
||||
public override bool ClearHandsOnCast{ get{ return false; } }
|
||||
public override bool ShowHandMovement{ get{ return false; } }
|
||||
|
||||
public override bool BlocksMovement{ get{ return false; } }
|
||||
|
||||
//public override int CastDelayBase{ get{ return 1; } }
|
||||
|
||||
public override int CastRecoveryBase{ get{ return 7; } }
|
||||
|
||||
public NinjaSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info )
|
||||
{
|
||||
}
|
||||
|
||||
public static bool CheckExpansion( Mobile from )
|
||||
{
|
||||
if ( !( from is PlayerMobile ) )
|
||||
return true;
|
||||
|
||||
if ( from.NetState == null )
|
||||
return false;
|
||||
|
||||
return from.NetState.SupportsExpansion( Expansion.SE );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
int mana = ScaleMana( RequiredMana );
|
||||
|
||||
if ( !base.CheckCast() )
|
||||
return false;
|
||||
|
||||
if ( !CheckExpansion( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063456 ); // You must upgrade to Samurai Empire in order to use that ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( Caster.Skills[CastSkill].Value < RequiredSkill )
|
||||
{
|
||||
string args = String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), CastSkill.ToString() );
|
||||
Caster.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CheckFizzle()
|
||||
{
|
||||
int mana = ScaleMana( RequiredMana );
|
||||
|
||||
if ( Caster.Skills[CastSkill].Value < RequiredSkill )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063352, RequiredSkill.ToString( "F1" ) ); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
|
||||
return false;
|
||||
}
|
||||
else if ( Caster.Mana < mana )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !base.CheckFizzle() )
|
||||
return false;
|
||||
|
||||
Caster.Mana -= mana;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void GetCastSkills( out double min, out double max )
|
||||
{
|
||||
min = RequiredSkill - 12.5; //Per 5 on friday 2/16/07
|
||||
max = RequiredSkill + 37.5;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Scripts/Spells/Ninjitsu/ShadowJump.cs
Normal file
133
Scripts/Spells/Ninjitsu/ShadowJump.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class Shadowjump : NinjaSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Shadowjump", null,
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }
|
||||
|
||||
public override double RequiredSkill{ get{ return 50.0; } }
|
||||
public override int RequiredMana{ get{ return 15; } }
|
||||
|
||||
public override bool BlockedByAnimalForm{ get{ return false; } }
|
||||
|
||||
public Shadowjump( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
PlayerMobile pm = Caster as PlayerMobile; // IsStealthing should be moved to Server.Mobiles
|
||||
if ( !pm.IsStealthing )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063088 ); // You prepare to perform a Shadowjump.
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( IPoint3D p )
|
||||
{
|
||||
IPoint3D orig = p;
|
||||
Map map = Caster.Map;
|
||||
|
||||
SpellHelper.GetSurfaceTop( ref p );
|
||||
|
||||
Point3D from = Caster.Location;
|
||||
Point3D to = new Point3D( p );
|
||||
|
||||
PlayerMobile pm = Caster as PlayerMobile; // IsStealthing should be moved to Server.Mobiles
|
||||
|
||||
if ( !pm.IsStealthing )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability.
|
||||
}
|
||||
else if ( Factions.Sigil.ExistsOn( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil.
|
||||
}
|
||||
else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move.
|
||||
}
|
||||
else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.TeleportFrom ) || !SpellHelper.CheckTravel( Caster, map, to, TravelCheckType.TeleportTo ))
|
||||
{
|
||||
}
|
||||
else if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502831 ); // Cannot teleport to that spot.
|
||||
}
|
||||
else if ( SpellHelper.CheckMulti( to, map, true, 5 ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502831 ); // Cannot teleport to that spot.
|
||||
}
|
||||
else if ( Region.Find( to, map ).GetRegion( typeof( HouseRegion ) ) != null )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 502829 ); // Cannot teleport to that spot.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, orig );
|
||||
|
||||
Mobile m = Caster;
|
||||
|
||||
m.Location = to;
|
||||
m.ProcessDelta();
|
||||
|
||||
Effects.SendLocationParticles( EffectItem.Create( from, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 );
|
||||
|
||||
m.PlaySound( 0x512 );
|
||||
|
||||
Server.SkillHandlers.Stealth.OnUse( m ); // stealth check after the a jump
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private Shadowjump m_Owner;
|
||||
|
||||
public InternalTarget( Shadowjump owner ) : base( 11, true, TargetFlags.None )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
IPoint3D p = o as IPoint3D;
|
||||
|
||||
if ( p != null )
|
||||
m_Owner.Target( p );
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Scripts/Spells/Ninjitsu/SurpriseAttack.cs
Normal file
134
Scripts/Spells/Ninjitsu/SurpriseAttack.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
using Server.SkillHandlers;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class SurpriseAttack : NinjaMove
|
||||
{
|
||||
public SurpriseAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 20; } }
|
||||
public override double RequiredSkill{ get{ return Core.ML ? 60.0 : 30.0; } }
|
||||
|
||||
public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063128 ); } } // You prepare to surprise your prey.
|
||||
|
||||
public override bool Validate( Mobile from )
|
||||
{
|
||||
if( !from.Hidden || from.AllowedStealthSteps <= 0 )
|
||||
{
|
||||
from.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.Validate( from );
|
||||
}
|
||||
|
||||
public override bool OnBeforeSwing( Mobile attacker, Mobile defender )
|
||||
{
|
||||
bool valid = Validate( attacker ) && CheckMana( attacker, true );
|
||||
|
||||
if( valid )
|
||||
{
|
||||
attacker.BeginAction( typeof( Stealth ) );
|
||||
Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), delegate { attacker.EndAction( typeof( Stealth ) ); } );
|
||||
}
|
||||
|
||||
return valid;
|
||||
|
||||
}
|
||||
|
||||
public override bool ValidatesDuringHit { get { return false; } }
|
||||
|
||||
public override void OnHit( Mobile attacker, Mobile defender, int damage )
|
||||
{
|
||||
//Validates before swing
|
||||
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063129 ); // You catch your opponent off guard with your Surprise Attack!
|
||||
defender.SendLocalizedMessage( 1063130 ); // Your defenses are lowered as your opponent surprises you!
|
||||
|
||||
defender.FixedParticles( 0x37B9, 1, 5, 0x26DA, 0, 3, EffectLayer.Head );
|
||||
|
||||
attacker.RevealingAction();
|
||||
|
||||
SurpriseAttackInfo info;
|
||||
|
||||
if ( m_Table.Contains( defender ) )
|
||||
{
|
||||
info = (SurpriseAttackInfo)m_Table[defender];
|
||||
|
||||
if ( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
m_Table.Remove( defender );
|
||||
}
|
||||
|
||||
int ninjitsu = attacker.Skills[SkillName.Ninjitsu].Fixed;
|
||||
|
||||
int malus = ninjitsu / 60 + (int)Tracking.GetStalkingBonus( attacker, defender );
|
||||
|
||||
info = new SurpriseAttackInfo( defender, malus );
|
||||
info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 8.0 ), new TimerStateCallback( EndSurprise ), info );
|
||||
|
||||
m_Table[defender] = info;
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
|
||||
public override void OnMiss( Mobile attacker, Mobile defender )
|
||||
{
|
||||
ClearCurrentMove( attacker );
|
||||
|
||||
attacker.SendLocalizedMessage( 1063161 ); // You failed to properly use the element of surprise.
|
||||
|
||||
attacker.RevealingAction();
|
||||
}
|
||||
|
||||
|
||||
private static Hashtable m_Table = new Hashtable();
|
||||
|
||||
public static bool GetMalus( Mobile target, ref int malus )
|
||||
{
|
||||
SurpriseAttackInfo info = m_Table[target] as SurpriseAttackInfo;
|
||||
|
||||
if ( info == null )
|
||||
return false;
|
||||
|
||||
malus = info.m_Malus;
|
||||
return true;
|
||||
}
|
||||
|
||||
private class SurpriseAttackInfo
|
||||
{
|
||||
public Mobile m_Target;
|
||||
public int m_Malus;
|
||||
public Timer m_Timer;
|
||||
|
||||
public SurpriseAttackInfo( Mobile target, int effect )
|
||||
{
|
||||
m_Target = target;
|
||||
m_Malus = effect;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EndSurprise( object state )
|
||||
{
|
||||
SurpriseAttackInfo info = (SurpriseAttackInfo)state;
|
||||
|
||||
if ( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
info.m_Target.SendLocalizedMessage( 1063131 ); // Your defenses have returned to normal.
|
||||
|
||||
m_Table.Remove( info.m_Target );
|
||||
}
|
||||
}
|
||||
}
|
||||
135
Scripts/Spells/Reagent.cs
Normal file
135
Scripts/Spells/Reagent.cs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
using System;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Spells
|
||||
{
|
||||
public class Reagent
|
||||
{
|
||||
private static Type[] m_Types = {
|
||||
typeof( BlackPearl ),
|
||||
typeof( Bloodmoss ),
|
||||
typeof( Garlic ),
|
||||
typeof( Ginseng ),
|
||||
typeof( MandrakeRoot ),
|
||||
typeof( Nightshade ),
|
||||
typeof( SulfurousAsh ),
|
||||
typeof( SpidersSilk ),
|
||||
typeof( BatWing ),
|
||||
typeof( GraveDust ),
|
||||
typeof( DaemonBlood ),
|
||||
typeof( NoxCrystal ),
|
||||
typeof( PigIron ),
|
||||
typeof( Bone ),
|
||||
typeof( FertileDirt ),
|
||||
typeof( DragonsBlood ),
|
||||
typeof( DaemonBone )
|
||||
};
|
||||
|
||||
public Type[] Types
|
||||
{
|
||||
get{ return m_Types; }
|
||||
}
|
||||
|
||||
public static Type BlackPearl
|
||||
{
|
||||
get{ return m_Types[0]; }
|
||||
set{ m_Types[0] = value; }
|
||||
}
|
||||
|
||||
public static Type Bloodmoss
|
||||
{
|
||||
get{ return m_Types[1]; }
|
||||
set{ m_Types[1] = value; }
|
||||
}
|
||||
|
||||
public static Type Garlic
|
||||
{
|
||||
get{ return m_Types[2]; }
|
||||
set{ m_Types[2] = value; }
|
||||
}
|
||||
|
||||
public static Type Ginseng
|
||||
{
|
||||
get{ return m_Types[3]; }
|
||||
set{ m_Types[3] = value; }
|
||||
}
|
||||
|
||||
public static Type MandrakeRoot
|
||||
{
|
||||
get{ return m_Types[4]; }
|
||||
set{ m_Types[4] = value; }
|
||||
}
|
||||
|
||||
public static Type Nightshade
|
||||
{
|
||||
get{ return m_Types[5]; }
|
||||
set{ m_Types[5] = value; }
|
||||
}
|
||||
|
||||
public static Type SulfurousAsh
|
||||
{
|
||||
get{ return m_Types[6]; }
|
||||
set{ m_Types[6] = value; }
|
||||
}
|
||||
|
||||
public static Type SpidersSilk
|
||||
{
|
||||
get{ return m_Types[7]; }
|
||||
set{ m_Types[7] = value; }
|
||||
}
|
||||
|
||||
public static Type BatWing
|
||||
{
|
||||
get{ return m_Types[8]; }
|
||||
set{ m_Types[8] = value; }
|
||||
}
|
||||
|
||||
public static Type GraveDust
|
||||
{
|
||||
get{ return m_Types[9]; }
|
||||
set{ m_Types[9] = value; }
|
||||
}
|
||||
|
||||
public static Type DaemonBlood
|
||||
{
|
||||
get{ return m_Types[10]; }
|
||||
set{ m_Types[10] = value; }
|
||||
}
|
||||
|
||||
public static Type NoxCrystal
|
||||
{
|
||||
get{ return m_Types[11]; }
|
||||
set{ m_Types[11] = value; }
|
||||
}
|
||||
|
||||
public static Type PigIron
|
||||
{
|
||||
get{ return m_Types[12]; }
|
||||
set{ m_Types[12] = value; }
|
||||
}
|
||||
|
||||
public static Type Bone
|
||||
{
|
||||
get{ return m_Types[13]; }
|
||||
set{ m_Types[13] = value; }
|
||||
}
|
||||
|
||||
public static Type FertileDirt
|
||||
{
|
||||
get{ return m_Types[14]; }
|
||||
set{ m_Types[14] = value; }
|
||||
}
|
||||
|
||||
public static Type DragonsBlood
|
||||
{
|
||||
get{ return m_Types[15]; }
|
||||
set{ m_Types[15] = value; }
|
||||
}
|
||||
|
||||
public static Type DaemonBone
|
||||
{
|
||||
get{ return m_Types[16]; }
|
||||
set{ m_Types[16] = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
86
Scripts/Spells/Second/Agility.cs
Normal file
86
Scripts/Spells/Second/Agility.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Second
|
||||
{
|
||||
public class AgilitySpell : MagerySpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Agility", "Ex Uus",
|
||||
212,
|
||||
9061,
|
||||
Reagent.Bloodmoss,
|
||||
Reagent.MandrakeRoot
|
||||
);
|
||||
|
||||
public override SpellCircle Circle { get { return SpellCircle.Second; } }
|
||||
|
||||
public AgilitySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) )
|
||||
{
|
||||
Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget( this );
|
||||
}
|
||||
|
||||
public void Target( Mobile m )
|
||||
{
|
||||
if ( !Caster.CanSee( m ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
|
||||
}
|
||||
else if ( CheckBSequence( m ) )
|
||||
{
|
||||
SpellHelper.Turn( Caster, m );
|
||||
|
||||
SpellHelper.AddStatBonus( Caster, m, StatType.Dex );
|
||||
|
||||
m.FixedParticles( 0x375A, 10, 15, 5010, EffectLayer.Waist );
|
||||
m.PlaySound( 0x1e7 );
|
||||
|
||||
int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, false )*100);
|
||||
TimeSpan length = SpellHelper.GetDuration( Caster, m );
|
||||
|
||||
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Agility, 1075841, length, m, percentage.ToString() ) );
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class InternalTarget : Target
|
||||
{
|
||||
private AgilitySpell m_Owner;
|
||||
|
||||
public InternalTarget( AgilitySpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Beneficial )
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget( Mobile from, object o )
|
||||
{
|
||||
if ( o is Mobile )
|
||||
{
|
||||
m_Owner.Target( (Mobile)o );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish( Mobile from )
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue