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