#W# Source and Scripts added. Added Scripts/Settings.cs to expose some basic tweak able settings.

This commit is contained in:
WarrentyExpired 2026-08-06 11:06:05 -04:00
parent b51c58f514
commit 3045c83799
3512 changed files with 627673 additions and 0 deletions

View file

@ -0,0 +1,15 @@
using System;
using Server;
namespace Server.Spells
{
public enum DisturbType
{
Unspecified,
EquipRequest,
UseRequest,
Hurt,
Kill,
NewCast
}
}

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

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

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

View file

@ -0,0 +1,16 @@
using System;
namespace Server.Spells
{
public enum SpellCircle
{
First,
Second,
Third,
Fourth,
Fifth,
Sixth,
Seventh,
Eighth
}
}

File diff suppressed because it is too large Load diff

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View 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 oneven after logging out, even after dyinguntil 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 );
}
}
}
}

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

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

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

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

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

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

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

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

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

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

View 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 oneven after logging out, even after dyinguntil 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 );
}
}
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View 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