#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,165 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
namespace Server.Spells.Spellweaving
{
public class ArcaneCircleSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Arcane Circle", "Myrshalee",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.5 ); } }
public override double RequiredSkill { get { return 0.0; } }
public override int RequiredMana { get { return 24; } }
public ArcaneCircleSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override bool CheckCast()
{
if( !IsValidLocation( Caster.Location, Caster.Map ) )
{
Caster.SendLocalizedMessage( 1072705 ); // You must be standing on an arcane circle, pentagram or abbatoir to use this spell.
return false;
}
if ( GetArcanists().Count < 2 )
{
Caster.SendLocalizedMessage( 1080452 ); //There are not enough spellweavers present to create an Arcane Focus.
return false;
}
return base.CheckCast();
}
public override void OnCast()
{
if( CheckSequence() )
{
Caster.FixedParticles( 0x3779, 10, 20, 0x0, EffectLayer.Waist );
Caster.PlaySound( 0x5C0 );
List<Mobile> Arcanists = GetArcanists();
TimeSpan duration = TimeSpan.FromHours( Math.Max( 1, (int)(Caster.Skills.Spellweaving.Value / 24) ) );
int strengthBonus = Math.Min( Arcanists.Count, IsSanctuary( Caster.Location, Caster.Map ) ? 6 : 5 ); //The Sanctuary is a special, single location place
for( int i = 0; i < Arcanists.Count; i++ )
GiveArcaneFocus( Arcanists[i], duration, strengthBonus );
}
FinishSequence();
}
private static bool IsSanctuary( Point3D p, Map m )
{
return (m == Map.Trammel || m == Map.Felucca) && p.X == 6267 && p.Y == 131;
}
private static bool IsValidLocation( Point3D location, Map map )
{
LandTile lt = map.Tiles.GetLandTile( location.X, location.Y ); // Land Tiles
if( IsValidTile( lt.ID ) && lt.Z == location.Z )
return true;
StaticTile[] tiles = map.Tiles.GetStaticTiles( location.X, location.Y ); // Static Tiles
for( int i = 0; i < tiles.Length; ++i )
{
StaticTile t = tiles[i];
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
int tand = t.ID;
if( t.Z + id.CalcHeight != location.Z )
continue;
else if( IsValidTile( tand ) )
return true;
}
IPooledEnumerable eable = map.GetItemsInRange( location, 0 ); // Added Tiles
foreach( Item item in eable )
{
ItemData id = item.ItemData;
if( item == null || item.Z + id.CalcHeight != location.Z )
continue;
else if( IsValidTile( item.ItemID ) )
{
eable.Free();
return true;
}
}
eable.Free();
return false;
}
public static bool IsValidTile( int itemID )
{
//Per OSI, Center tile only
return (itemID == 0xFEA || itemID == 0x1216 || itemID == 0x307F || itemID == 0x1D10 || itemID == 0x1D0F || itemID == 0x1D1F || itemID == 0x1D12 ); // Pentagram center, Abbatoir center, Arcane Circle Center, Bloody Pentagram has 4 tiles at center
}
private List<Mobile> GetArcanists()
{
List<Mobile> weavers = new List<Mobile>();
weavers.Add( Caster );
//OSI Verified: Even enemies/combatants count
foreach( Mobile m in Caster.GetMobilesInRange( 1 ) ) //Range verified as 1
{
if ( m != Caster && m is PlayerMobile && Caster.CanBeBeneficial( m, false ) && Math.Abs( Caster.Skills.Spellweaving.Value - m.Skills.Spellweaving.Value ) <= 20 && !(m is Clone) )
{
weavers.Add( m );
}
// Everyone gets the Arcane Focus, power capped elsewhere
}
return weavers;
}
private void GiveArcaneFocus( Mobile to, TimeSpan duration, int strengthBonus )
{
if( to == null ) //Sanity
return;
ArcaneFocus focus = FindArcaneFocus( to );
if( focus == null )
{
ArcaneFocus f = new ArcaneFocus( duration, strengthBonus );
if( to.PlaceInBackpack( f ) )
{
f.SendTimeRemainingMessage( to );
to.SendLocalizedMessage( 1072740 ); // An arcane focus appears in your backpack.
}
else
{
f.Delete();
}
}
else //OSI renewal rules: the new one will override the old one, always.
{
to.SendLocalizedMessage( 1072828 ); // Your arcane focus is renewed.
focus.LifeSpan = duration;
focus.CreationTime = DateTime.UtcNow;
focus.StrengthBonus = strengthBonus;
focus.InvalidateProperties();
focus.SendTimeRemainingMessage( to );
}
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Server.Mobiles;
using Server.Network;
using Server.Spells.Fifth;
using Server.Spells.Seventh;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
namespace Server.Spells.Spellweaving
{
public abstract class ArcaneForm : ArcanistSpell, 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 ArcaneForm( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info )
{
}
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,51 @@
using System;
using Server.Targeting;
using Server.Mobiles;
namespace Server.Spells.Spellweaving
{
public abstract class ArcaneSummon<T> : ArcanistSpell where T : BaseCreature
{
public abstract int Sound { get; }
public ArcaneSummon( Mobile caster, Item scroll, SpellInfo info )
: base( caster, scroll, info )
{
}
public override bool CheckCast()
{
if( !base.CheckCast() )
return false;
if( (Caster.Followers + 1) > Caster.FollowersMax )
{
Caster.SendLocalizedMessage( 1074270 ); // You have too many followers to summon another one.
return false;
}
return true;
}
public override void OnCast()
{
if( CheckSequence() )
{
TimeSpan duration = TimeSpan.FromMinutes( Caster.Skills.Spellweaving.Value /24 + FocusLevel*2 );
int summons = Math.Min( 1+FocusLevel, Caster.FollowersMax - Caster.Followers );
for( int i = 0; i < summons; i++ )
{
BaseCreature bc;
try { bc = Activator.CreateInstance<T>(); }
catch { break; }
SpellHelper.Summon( bc, Caster, Sound, duration, false, false );
}
FinishSequence();
}
}
}
}

View file

@ -0,0 +1,159 @@
using System;
using System.Text;
using Server.Items;
using Server.Mobiles;
using Server.Engines.MLQuests;
namespace Server.Spells.Spellweaving
{
public abstract class ArcanistSpell : Spell
{
public abstract double RequiredSkill { get; }
public abstract int RequiredMana { get; }
public override SkillName CastSkill { get { return SkillName.Spellweaving; } }
public override SkillName DamageSkill { get { return SkillName.Spellweaving; } }
public override bool ClearHandsOnCast { get { return false; } }
private int m_CastTimeFocusLevel;
public ArcanistSpell( Mobile caster, Item scroll, SpellInfo info )
: base( caster, scroll, info )
{
}
public virtual int FocusLevel
{
get { return m_CastTimeFocusLevel; }
}
public static int GetFocusLevel( Mobile from )
{
ArcaneFocus focus = FindArcaneFocus( from );
if( focus == null || focus.Deleted )
return 0;
return focus.StrengthBonus;
}
public static ArcaneFocus FindArcaneFocus( Mobile from )
{
if( from == null || from.Backpack == null )
return null;
if ( from.Holding is ArcaneFocus )
return (ArcaneFocus)from.Holding;
return from.Backpack.FindItemByType<ArcaneFocus>();
}
public static bool CheckExpansion( Mobile from )
{
if( !(from is PlayerMobile) )
return true;
if( from.NetState == null )
return false;
return from.NetState.SupportsExpansion( Expansion.ML );
}
public override bool CheckCast()
{
if( !base.CheckCast() )
return false;
Mobile caster = Caster;
if ( !CheckExpansion( caster ) )
{
caster.SendLocalizedMessage( 1072176 ); // You must upgrade to the Mondain's Legacy Expansion Pack before using that ability
return false;
}
if ( caster is PlayerMobile )
{
MLQuestContext context = MLQuestSystem.GetContext( (PlayerMobile)caster );
if ( context == null || !context.Spellweaving )
{
caster.SendLocalizedMessage( 1073220 ); // You must have completed the epic arcanist quest to use this ability.
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;
}
else if ( caster.Skills[CastSkill].Value < RequiredSkill )
{
caster.SendLocalizedMessage( 1063013, String.Format( "{0}\t{1}", RequiredSkill.ToString( "F1" ), "#1044114" ) ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
return false;
}
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 RequiredMana;
}
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();
m_CastTimeFocusLevel = GetFocusLevel( Caster );
}
public virtual void SendCastEffect()
{
Caster.FixedEffect( 0x37C4, 10, (int)(GetCastDelay().TotalSeconds * 28), 4, 3 );
}
public virtual bool CheckResisted( Mobile m )
{
double percent = (50 + 2*(GetResistSkill( m ) - GetDamageSkill( Caster )))/100; //TODO: According to the guide this is it.. but.. is it correct per OSI?
if( percent <= 0 )
return false;
if( percent >= 1.0 )
return true;
return (percent >= Utility.RandomDouble());
}
}
}

View file

@ -0,0 +1,134 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Server.Spells.Spellweaving
{
public class AttuneWeaponSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Attune Weapon", "Haeldril",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }
public override double RequiredSkill { get { return 0.0; } }
public override int RequiredMana { get { return 24; } }
public AttuneWeaponSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override bool CheckCast()
{
if( m_Table.ContainsKey( Caster ) )
{
Caster.SendLocalizedMessage( 501775 ); // This spell is already in effect.
return false;
}
else if( !Caster.CanBeginAction( typeof( AttuneWeaponSpell ) ) )
{
Caster.SendLocalizedMessage( 1075124 ); // You must wait before casting that spell again.
return false;
}
return base.CheckCast();
}
public override void OnCast()
{
if( CheckSequence() )
{
Caster.PlaySound( 0x5C3 );
Caster.FixedParticles( 0x3728, 1, 13, 0x26B8, 0x455, 7, EffectLayer.Waist );
Caster.FixedParticles( 0x3779, 1, 15, 0x251E, 0x3F, 7, EffectLayer.Waist );
double skill = Caster.Skills[SkillName.Spellweaving].Value;
int damageAbsorb = (int)(18 + ((skill-10)/10)*3 + (FocusLevel * 6));
Caster.MeleeDamageAbsorb = damageAbsorb;
TimeSpan duration = TimeSpan.FromSeconds( 60 + (FocusLevel * 12) );
ExpireTimer t = new ExpireTimer( Caster, duration );
t.Start();
m_Table[Caster] = t;
Caster.BeginAction( typeof( AttuneWeaponSpell ) );
BuffInfo.AddBuff( Caster, new BuffInfo( BuffIcon.AttuneWeapon, 1075798, duration, Caster, damageAbsorb.ToString() ) );
}
FinishSequence();
}
private static Dictionary<Mobile, ExpireTimer> m_Table = new Dictionary<Mobile, ExpireTimer>();
public static void TryAbsorb( Mobile defender, ref int damage )
{
if( damage == 0 || !IsAbsorbing( defender ) || defender.MeleeDamageAbsorb <= 0 )
return;
int absorbed = Math.Min( damage, defender.MeleeDamageAbsorb );
damage -= absorbed;
defender.MeleeDamageAbsorb -= absorbed;
defender.SendLocalizedMessage( 1075127, String.Format( "{0}\t{1}", absorbed, defender.MeleeDamageAbsorb ) ); // ~1_damage~ point(s) of damage have been absorbed. A total of ~2_remaining~ point(s) of shielding remain.
if( defender.MeleeDamageAbsorb <= 0 )
StopAbsorbing( defender, true );
}
public static bool IsAbsorbing( Mobile m )
{
return m_Table.ContainsKey( m );
}
public static void StopAbsorbing( Mobile m, bool message )
{
ExpireTimer t;
if( m_Table.TryGetValue( m, out t ) )
{
t.DoExpire( message );
}
}
private class ExpireTimer : Timer
{
private Mobile m_Mobile;
public ExpireTimer( Mobile m, TimeSpan delay )
: base( delay )
{
m_Mobile = m;
}
protected override void OnTick()
{
DoExpire( true );
}
public void DoExpire( bool message )
{
Stop();
m_Mobile.MeleeDamageAbsorb = 0;
if( message )
{
m_Mobile.SendLocalizedMessage( 1075126 ); // Your attunement fades.
m_Mobile.PlaySound( 0x1F8 );
}
m_Table.Remove( m_Mobile );
Timer.DelayCall( TimeSpan.FromSeconds( 120 ), delegate { m_Mobile.EndAction( typeof( AttuneWeaponSpell ) ); } );
BuffInfo.RemoveBuff( m_Mobile, BuffIcon.AttuneWeapon );
}
}
}
}

View file

@ -0,0 +1,150 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Server.Spells.Spellweaving
{
public class EssenceOfWindSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo( "Essence of Wind", "Anathrae", -1 );
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 3.0 ); } }
public override double RequiredSkill { get { return 52.0; } }
public override int RequiredMana { get { return 40; } }
public EssenceOfWindSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
{
}
public override void OnCast()
{
if( CheckSequence() )
{
Caster.PlaySound( 0x5C6 );
int range = 5 + FocusLevel;
int damage = 25 + FocusLevel;
double skill = Caster.Skills[SkillName.Spellweaving].Value;
TimeSpan duration = TimeSpan.FromSeconds( (int)(skill / 24) + FocusLevel );
int fcMalus = FocusLevel + 1;
int ssiMalus = 2 * (FocusLevel + 1);
List<Mobile> targets = new List<Mobile>();
foreach( Mobile m in Caster.GetMobilesInRange( range ) )
{
if( Caster != m && Caster.InLOS( m ) && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) )
targets.Add( m );
}
for( int i = 0; i < targets.Count; i++ )
{
Mobile m = targets[i];
Caster.DoHarmful( m );
SpellHelper.Damage( this, m, damage, 0, 0, 100, 0, 0 );
if( !CheckResisted( m ) ) //No message on resist
{
m_Table[m] = new EssenceOfWindInfo( m, fcMalus, ssiMalus, duration );
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.EssenceOfWind, 1075802, duration, m, String.Format( "{0}\t{1}", fcMalus.ToString(), ssiMalus.ToString() ) ) );
}
}
}
FinishSequence();
}
private static Dictionary<Mobile, EssenceOfWindInfo> m_Table = new Dictionary<Mobile, EssenceOfWindInfo>();
private class EssenceOfWindInfo
{
private Mobile m_Defender;
private int m_FCMalus;
private int m_SSIMalus;
private ExpireTimer m_Timer;
public Mobile Defender { get { return m_Defender; } }
public int FCMalus { get { return m_FCMalus; } }
public int SSIMalus { get { return m_SSIMalus; } }
public ExpireTimer Timer { get { return m_Timer; } }
public EssenceOfWindInfo( Mobile defender, int fcMalus, int ssiMalus, TimeSpan duration )
{
m_Defender = defender;
m_FCMalus = fcMalus;
m_SSIMalus = ssiMalus;
m_Timer = new ExpireTimer( m_Defender, duration );
m_Timer.Start();
}
}
public static int GetFCMalus( Mobile m )
{
EssenceOfWindInfo info;
if( m_Table.TryGetValue( m, out info ) )
return info.FCMalus;
return 0;
}
public static int GetSSIMalus( Mobile m )
{
EssenceOfWindInfo info;
if( m_Table.TryGetValue( m, out info ) )
return info.SSIMalus;
return 0;
}
public static bool IsDebuffed( Mobile m )
{
return m_Table.ContainsKey( m );
}
public static void StopDebuffing( Mobile m, bool message )
{
EssenceOfWindInfo info;
if( m_Table.TryGetValue( m, out info ) )
info.Timer.DoExpire( message );
}
private class ExpireTimer : Timer
{
private Mobile m_Mobile;
public ExpireTimer( Mobile m, TimeSpan delay ) : base( delay )
{
m_Mobile = m;
}
protected override void OnTick()
{
DoExpire( true );
}
public void DoExpire( bool message )
{
Stop();
/*
if( message )
{
}
*/
m_Table.Remove( m_Mobile );
BuffInfo.RemoveBuff( m_Mobile, BuffIcon.EssenceOfWind );
}
}
}
}

View file

@ -0,0 +1,88 @@
using System;
namespace Server.Spells.Spellweaving
{
public class EtherealVoyageSpell : ArcaneForm
{
private static SpellInfo m_Info = new SpellInfo(
"Ethereal Voyage", "Orlavdra",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 3.5 ); } }
public override double RequiredSkill { get { return 24.0; } }
public override int RequiredMana { get { return 32; } }
public override int Body { get { return 0x302; } }
public override int Hue { get { return 0x48F; } }
public EtherealVoyageSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public static void Initialize()
{
EventSink.AggressiveAction += new AggressiveActionEventHandler( delegate( AggressiveActionEventArgs e )
{
if( TransformationSpellHelper.UnderTransformation( e.Aggressor, typeof( EtherealVoyageSpell ) ) )
{
TransformationSpellHelper.RemoveContext( e.Aggressor, true );
}
} );
}
public override bool CheckCast()
{
if( TransformationSpellHelper.UnderTransformation( Caster, typeof( EtherealVoyageSpell ) ) )
{
Caster.SendLocalizedMessage( 501775 ); // This spell is already in effect.
}
else if( !Caster.CanBeginAction( typeof( EtherealVoyageSpell ) ) )
{
Caster.SendLocalizedMessage( 1075124 ); // You must wait before casting that spell again.
}
else if( Caster.Combatant != null )
{
Caster.SendLocalizedMessage( 1072586 ); // You cannot cast Ethereal Voyage while you are in combat.
}
else
{
return base.CheckCast();
}
return false;
}
public override void DoEffect( Mobile m )
{
m.PlaySound( 0x5C8 );
m.SendLocalizedMessage( 1074770 ); // You are now under the effects of Ethereal Voyage.
double skill = Caster.Skills.Spellweaving.Value;
TimeSpan duration = TimeSpan.FromSeconds( 12 + (int)(skill / 24) + (FocusLevel * 2) );
Timer.DelayCall<Mobile>( duration, new TimerStateCallback<Mobile>( RemoveEffect ), Caster );
Caster.BeginAction( typeof( EtherealVoyageSpell ) ); //Cannot cast this spell for another 5 minutes(300sec) after effect removed.
BuffInfo.AddBuff( Caster, new BuffInfo( BuffIcon.EtherealVoyage, 1031613, 1075805, duration, Caster ) );
}
public override void RemoveEffect( Mobile m )
{
m.SendLocalizedMessage( 1074771 ); // You are no longer under the effects of Ethereal Voyage.
TransformationSpellHelper.RemoveContext( m, true );
Timer.DelayCall( TimeSpan.FromMinutes( 5 ), delegate
{
m.EndAction( typeof( EtherealVoyageSpell ) );
} );
BuffInfo.RemoveBuff( m, BuffIcon.EtherealVoyage );
}
}
}

View file

@ -0,0 +1,218 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Server.Mobiles;
using Server.Gumps;
using Server.Targeting;
namespace Server.Spells.Spellweaving
{
public class GiftOfLifeSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Gift of Life", "Illorae",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 4.0 ); } }
public override double RequiredSkill { get { return 38.0; } }
public override int RequiredMana { get { return 70; } }
public GiftOfLifeSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public static void Initialize()
{
EventSink.PlayerDeath += new PlayerDeathEventHandler( delegate( PlayerDeathEventArgs e )
{
HandleDeath( e.Mobile );
} );
}
public override void OnCast()
{
Caster.Target = new InternalTarget( this );
}
public void Target( Mobile m )
{
BaseCreature bc = m as BaseCreature;
if( !Caster.CanSee( m ) )
{
Caster.SendLocalizedMessage( 500237 ); // Target can not be seen.
}
else if( m.IsDeadBondedPet || !m.Alive )
{
// As per Osi: Nothing happens.
}
else if( m != Caster && (bc == null || !bc.IsBonded || bc.ControlMaster != Caster) )
{
Caster.SendLocalizedMessage( 1072077 ); // You may only cast this spell on yourself or a bonded pet.
}
else if( m_Table.ContainsKey( m ) )
{
Caster.SendLocalizedMessage( 501775 ); // This spell is already in effect.
}
else if( CheckBSequence( m ) )
{
if( Caster == m )
{
Caster.SendLocalizedMessage( 1074774 ); // You weave powerful magic, protecting yourself from death.
}
else
{
Caster.SendLocalizedMessage( 1074775 ); // You weave powerful magic, protecting your pet from death.
SpellHelper.Turn( Caster, m );
}
m.PlaySound( 0x244 );
m.FixedParticles( 0x3709, 1, 30, 0x26ED, 5, 2, EffectLayer.Waist );
m.FixedParticles( 0x376A, 1, 30, 0x251E, 5, 3, EffectLayer.Waist );
double skill = Caster.Skills[SkillName.Spellweaving].Value;
TimeSpan duration = TimeSpan.FromMinutes( ((int)(skill / 24))* 2 + FocusLevel );
ExpireTimer t = new ExpireTimer( m, duration, this );
t.Start();
m_Table[m] = t;
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.GiftOfLife, 1031615, 1075807, duration, m, null, true ) );
}
FinishSequence();
}
private static Dictionary<Mobile, ExpireTimer> m_Table = new Dictionary<Mobile, ExpireTimer>();
public static void HandleDeath( Mobile m )
{
if( m_Table.ContainsKey( m ) )
Timer.DelayCall<Mobile>( TimeSpan.FromSeconds( Utility.RandomMinMax( 2, 4 ) ), new TimerStateCallback<Mobile>( HandleDeath_OnCallback ), m );
}
private static void HandleDeath_OnCallback( Mobile m )
{
ExpireTimer timer;
if( m_Table.TryGetValue( m, out timer ) )
{
double hitsScalar = timer.Spell.HitsScalar;
if( m is BaseCreature && m.IsDeadBondedPet )
{
BaseCreature pet = (BaseCreature)m;
Mobile master = pet.GetMaster();
if( master != null && master.NetState != null && Utility.InUpdateRange( pet, master ) )
{
master.CloseGump( typeof( PetResurrectGump ) );
master.SendGump( new PetResurrectGump( master, pet, hitsScalar ) );
}
else
{
List<Mobile> friends = pet.Friends;
for( int i = 0; friends != null && i < friends.Count; i++ )
{
Mobile friend = friends[i];
if( friend.NetState != null && Utility.InUpdateRange( pet, friend ) )
{
friend.CloseGump( typeof( PetResurrectGump ) );
friend.SendGump( new PetResurrectGump( friend, pet ) );
break;
}
}
}
}
else
{
m.CloseGump( typeof( ResurrectGump ) );
m.SendGump( new ResurrectGump( m, hitsScalar ) );
}
//Per OSI, buff is removed when gump sent, irregardless of online status or acceptence
timer.DoExpire();
}
}
public double HitsScalar { get { return ((Caster.Skills.Spellweaving.Value/2.4) + FocusLevel)/100; } }
public static void OnLogin( LoginEventArgs e )
{
Mobile m = e.Mobile;
if( m == null || m.Alive || m_Table[m] == null )
return;
HandleDeath_OnCallback( m );
}
private class ExpireTimer : Timer
{
private Mobile m_Mobile;
private GiftOfLifeSpell m_Spell;
public GiftOfLifeSpell Spell { get { return m_Spell; } }
public ExpireTimer( Mobile m, TimeSpan delay, GiftOfLifeSpell spell )
: base( delay )
{
m_Mobile = m;
m_Spell = spell;
}
protected override void OnTick()
{
DoExpire();
}
public void DoExpire()
{
Stop();
m_Mobile.SendLocalizedMessage( 1074776 ); // You are no longer protected with Gift of Life.
m_Table.Remove( m_Mobile );
BuffInfo.RemoveBuff( m_Mobile, BuffIcon.GiftOfLife );
}
}
public class InternalTarget : Target
{
private GiftOfLifeSpell m_Owner;
public InternalTarget( GiftOfLifeSpell owner )
: base( 10, false, TargetFlags.Beneficial )
{
m_Owner = owner;
}
protected override void OnTarget( Mobile m, object o )
{
if( o is Mobile )
{
m_Owner.Target( (Mobile)o );
}
else
{
m.SendLocalizedMessage( 1072077 ); // You may only cast this spell on yourself or a bonded pet.
}
}
protected override void OnTargetFinish( Mobile m )
{
m_Owner.FinishSequence();
}
}
}
}

View file

@ -0,0 +1,187 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Server.Targeting;
namespace Server.Spells.Spellweaving
{
public class GiftOfRenewalSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Gift of Renewal", "Olorisstra",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 3.0 ); } }
public override double RequiredSkill { get { return 0.0; } }
public override int RequiredMana { get { return 24; } }
public GiftOfRenewalSpell( 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.
}
if( m_Table.ContainsKey( m ) )
{
Caster.SendLocalizedMessage( 501775 ); // This spell is already in effect.
}
else if( !Caster.CanBeginAction( typeof( GiftOfRenewalSpell ) ) )
{
Caster.SendLocalizedMessage( 501789 ); // You must wait before trying again.
}
else if( CheckBSequence( m ) )
{
SpellHelper.Turn( Caster, m );
Caster.FixedEffect( 0x374A, 10, 20 );
Caster.PlaySound( 0x5C9 );
if( m.Poisoned )
{
m.CurePoison( m );
}
else
{
double skill = Caster.Skills[SkillName.Spellweaving].Value;
int hitsPerRound = 5 + (int)(skill / 24) + FocusLevel;
TimeSpan duration = TimeSpan.FromSeconds( 30 + (FocusLevel * 10) );
GiftOfRenewalInfo info = new GiftOfRenewalInfo( Caster, m, hitsPerRound );
Timer.DelayCall( duration,
delegate
{
if( StopEffect( m ) )
{
m.PlaySound( 0x455 );
m.SendLocalizedMessage( 1075071 ); // The Gift of Renewal has faded.
}
} );
m_Table[m] = info;
Caster.BeginAction( typeof( GiftOfRenewalSpell ) );
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.GiftOfRenewal, 1031602, 1075797, duration, m, hitsPerRound.ToString() ) );
}
}
FinishSequence();
}
private static Dictionary<Mobile, GiftOfRenewalInfo> m_Table = new Dictionary<Mobile, GiftOfRenewalInfo>();
private class GiftOfRenewalInfo
{
public Mobile m_Caster;
public Mobile m_Mobile;
public int m_HitsPerRound;
public InternalTimer m_Timer;
public GiftOfRenewalInfo( Mobile caster, Mobile mobile, int hitsPerRound )
{
m_Caster = caster;
m_Mobile = mobile;
m_HitsPerRound = hitsPerRound;
m_Timer = new InternalTimer( this );
m_Timer.Start();
}
}
private class InternalTimer : Timer
{
public GiftOfRenewalInfo m_Info;
public InternalTimer( GiftOfRenewalInfo info )
: base( TimeSpan.FromSeconds( 2.0 ), TimeSpan.FromSeconds( 2.0 ) )
{
m_Info = info;
}
protected override void OnTick()
{
Mobile m = m_Info.m_Mobile;
if( !m_Table.ContainsKey( m ) )
{
Stop();
return;
}
if( !m.Alive )
{
Stop();
StopEffect( m );
return;
}
if( m.Hits >= m.HitsMax )
return;
int toHeal = m_Info.m_HitsPerRound;
SpellHelper.Heal( toHeal, m, m_Info.m_Caster );
m.FixedParticles( 0x376A, 9, 32, 5005, EffectLayer.Waist );
}
}
public static bool StopEffect( Mobile m )
{
GiftOfRenewalInfo info;
if( m_Table.TryGetValue( m, out info ) )
{
m_Table.Remove( m );
info.m_Timer.Stop();
BuffInfo.RemoveBuff( m, BuffIcon.GiftOfRenewal );
Timer.DelayCall( TimeSpan.FromSeconds( 60 ), delegate { info.m_Caster.EndAction( typeof( GiftOfRenewalSpell ) ); } );
return true;
}
return false;
}
public class InternalTarget : Target
{
private GiftOfRenewalSpell m_Owner;
public InternalTarget( GiftOfRenewalSpell owner )
: base( 10, false, TargetFlags.Beneficial )
{
m_Owner = owner;
}
protected override void OnTarget( Mobile m, object o )
{
if( o is Mobile )
{
m_Owner.Target( (Mobile)o );
}
}
protected override void OnTargetFinish( Mobile m )
{
m_Owner.FinishSequence();
}
}
}
}

View file

@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Server.Items;
namespace Server.Spells.Spellweaving
{
public class ImmolatingWeaponSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Immolating Weapon", "Thalshara",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.0 ); } }
public override double RequiredSkill { get { return 10.0; } }
public override int RequiredMana { get { return 32; } }
public ImmolatingWeaponSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override bool CheckCast()
{
BaseWeapon weapon = Caster.Weapon as BaseWeapon;
if ( weapon == null || weapon is Fists || weapon is BaseRanged )
{
Caster.SendLocalizedMessage( 1060179 ); // You must be wielding a weapon to use this ability!
return false;
}
return base.CheckCast();
}
public override void OnCast()
{
BaseWeapon weapon = Caster.Weapon as BaseWeapon;
if ( weapon == null || weapon is Fists || weapon is BaseRanged )
{
Caster.SendLocalizedMessage( 1060179 ); // You must be wielding a weapon to use this ability!
}
else if ( CheckSequence() )
{
Caster.PlaySound( 0x5CA );
Caster.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head );
if ( !IsImmolating( weapon ) ) // On OSI, the effect is not re-applied
{
double skill = Caster.Skills.Spellweaving.Value;
int duration = 10 + (int)( skill / 24 ) + FocusLevel;
int damage = 5 + (int)( skill / 24 ) + FocusLevel;
Timer stopTimer = Timer.DelayCall<BaseWeapon>( TimeSpan.FromSeconds( duration ), StopImmolating, weapon );
m_WeaponDamageTable[weapon] = new ImmolatingWeaponEntry( damage, stopTimer, Caster );
weapon.InvalidateProperties();
}
}
FinishSequence();
}
private static Dictionary<BaseWeapon, ImmolatingWeaponEntry> m_WeaponDamageTable = new Dictionary<BaseWeapon, ImmolatingWeaponEntry>();
public static bool IsImmolating( BaseWeapon weapon )
{
return m_WeaponDamageTable.ContainsKey( weapon );
}
public static int GetImmolatingDamage( BaseWeapon weapon )
{
ImmolatingWeaponEntry entry;
if ( m_WeaponDamageTable.TryGetValue( weapon, out entry ) )
return entry.m_Damage;
return 0;
}
public static void DoEffect( BaseWeapon weapon, Mobile target )
{
Timer.DelayCall<DelayedEffectEntry>( TimeSpan.FromSeconds( 0.25 ), FinishEffect, new DelayedEffectEntry( weapon, target ) );
}
private static void FinishEffect( DelayedEffectEntry effect )
{
ImmolatingWeaponEntry entry;
if ( m_WeaponDamageTable.TryGetValue( effect.m_Weapon, out entry ) )
AOS.Damage( effect.m_Target, entry.m_Caster, entry.m_Damage, 0, 100, 0, 0, 0 );
}
public static void StopImmolating( BaseWeapon weapon )
{
ImmolatingWeaponEntry entry;
if ( m_WeaponDamageTable.TryGetValue( weapon, out entry ) )
{
if ( entry.m_Caster != null )
entry.m_Caster.PlaySound( 0x27 );
entry.m_Timer.Stop();
m_WeaponDamageTable.Remove( weapon );
weapon.InvalidateProperties();
}
}
private class ImmolatingWeaponEntry
{
public int m_Damage;
public Timer m_Timer;
public Mobile m_Caster;
public ImmolatingWeaponEntry( int damage, Timer stopTimer, Mobile caster )
{
m_Damage = damage;
m_Timer = stopTimer;
m_Caster = caster;
}
}
private class DelayedEffectEntry
{
public BaseWeapon m_Weapon;
public Mobile m_Target;
public DelayedEffectEntry( BaseWeapon weapon, Mobile target )
{
m_Weapon = weapon;
m_Target = target;
}
}
}
}

View file

@ -0,0 +1,67 @@
using System;
using Server;
namespace Server.Items
{
public class ArcaneFocus : TransientItem
{
public override int LabelNumber { get { return 1032629; } } // Arcane Focus
private int m_StrengthBonus;
[CommandProperty( AccessLevel.GameMaster )]
public int StrengthBonus
{
get { return m_StrengthBonus; }
set { m_StrengthBonus = value; }
}
[Constructable]
public ArcaneFocus()
: this( TimeSpan.FromHours( 1 ), 1 )
{
}
[Constructable]
public ArcaneFocus( int lifeSpan, int strengthBonus )
: this( TimeSpan.FromSeconds( lifeSpan ), strengthBonus )
{
}
public ArcaneFocus( TimeSpan lifeSpan, int strengthBonus ) : base( 0x3155, lifeSpan )
{
LootType = LootType.Blessed;
m_StrengthBonus = strengthBonus;
}
public ArcaneFocus( Serial serial ) : base( serial )
{
}
public override void GetProperties( ObjectPropertyList list )
{
base.GetProperties( list );
list.Add( 1060485, m_StrengthBonus.ToString() ); // strength bonus ~1_val~
}
public override TextDefinition InvalidTransferMessage{ get { return 1073480; } } // Your arcane focus disappears.
public override bool Nontransferable { get { return true; } }
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int)0 );
writer.Write( m_StrengthBonus );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
m_StrengthBonus = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using Server;
using Server.Mobiles;
namespace Server.Items
{
public class TransientItem : Item
{
private TimeSpan m_LifeSpan;
[CommandProperty( AccessLevel.GameMaster )]
public TimeSpan LifeSpan
{
get { return m_LifeSpan; }
set { m_LifeSpan = value; }
}
private DateTime m_CreationTime;
[CommandProperty( AccessLevel.GameMaster )]
public DateTime CreationTime
{
get { return m_CreationTime; }
set { m_CreationTime = value; }
}
private Timer m_Timer;
public override bool Nontransferable { get { return true; } }
public override void HandleInvalidTransfer( Mobile from )
{
if( InvalidTransferMessage != null )
TextDefinition.SendMessageTo( from, InvalidTransferMessage );
this.Delete();
}
public virtual TextDefinition InvalidTransferMessage { get { return null; } }
public virtual void Expire( Mobile parent )
{
if( parent != null )
parent.SendLocalizedMessage( 1072515, (this.Name == null ? String.Format( "#{0}", LabelNumber ): this.Name) ); // The ~1_name~ expired...
Effects.PlaySound( GetWorldLocation(), Map, 0x201 );
this.Delete();
}
public virtual void SendTimeRemainingMessage( Mobile to )
{
to.SendLocalizedMessage( 1072516, String.Format( "{0}\t{1}", (this.Name == null ? String.Format( "#{0}", LabelNumber ): this.Name), (int)m_LifeSpan.TotalSeconds ) ); // ~1_name~ will expire in ~2_val~ seconds!
}
public override void OnDelete()
{
if( m_Timer != null )
m_Timer.Stop();
base.OnDelete();
}
public virtual void CheckExpiry()
{
if( (m_CreationTime + m_LifeSpan) < DateTime.UtcNow )
Expire( RootParent as Mobile );
else
InvalidateProperties();
}
[Constructable]
public TransientItem( int itemID, TimeSpan lifeSpan )
: base( itemID )
{
m_CreationTime = DateTime.UtcNow;
m_LifeSpan = lifeSpan;
m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5 ), TimeSpan.FromSeconds( 5 ), new TimerCallback( CheckExpiry ) );
}
public TransientItem( Serial serial )
: base( serial )
{
}
public override void GetProperties( ObjectPropertyList list )
{
base.GetProperties( list );
TimeSpan remaining = ((m_CreationTime + m_LifeSpan) - DateTime.UtcNow);
list.Add( 1072517, ((int)remaining.TotalSeconds).ToString() ); // Lifespan: ~1_val~ seconds
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int)0 );
writer.Write( m_LifeSpan );
writer.Write( m_CreationTime );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
m_LifeSpan = reader.ReadTimeSpan();
m_CreationTime = reader.ReadDateTime();
m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5 ), TimeSpan.FromSeconds( 5 ), new TimerCallback( CheckExpiry ) );
}
}
}

View file

@ -0,0 +1,66 @@
using System;
using Server;
using Server.Misc;
namespace Server.Mobiles
{
[CorpseName( "a pixie corpse" )]
public class ArcaneFey : BaseCreature
{
public override double DispelDifficulty { get { return 70.0; } }
public override double DispelFocus { get { return 20.0; } }
public override OppositionGroup OppositionGroup { get { return OppositionGroup.FeyAndUndead; } }
public override bool InitialInnocent { get { return true; } }
[Constructable]
public ArcaneFey() : base( AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 )
{
Name = NameList.RandomName( "pixie" );
Body = 128;
BaseSoundID = 0x467;
SetStr( 20 );
SetDex( 150 );
SetInt( 125 );
SetDamage( 9, 15 );
SetDamageType( ResistanceType.Physical, 100 );
SetResistance( ResistanceType.Physical, 80, 90 );
SetResistance( ResistanceType.Fire, 40, 50 );
SetResistance( ResistanceType.Cold, 40, 50 );
SetResistance( ResistanceType.Poison, 40, 50 );
SetResistance( ResistanceType.Energy, 40, 50 );
SetSkill( SkillName.EvalInt, 70.1, 80.0 );
SetSkill( SkillName.Magery, 70.1, 80.0 );
SetSkill( SkillName.Meditation, 70.1, 80.0 );
SetSkill( SkillName.MagicResist, 50.5, 100.0 );
SetSkill( SkillName.Tactics, 10.1, 20.0 );
SetSkill( SkillName.Wrestling, 10.1, 12.5 );
Fame = 0;
Karma = 0;
ControlSlots = 1;
}
public ArcaneFey( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,67 @@
using System;
using Server;
using Server.Items;
namespace Server.Mobiles
{
[CorpseName( "an imp corpse" )]
public class ArcaneFiend : BaseCreature
{
public override double DispelDifficulty { get { return 70.0; } }
public override double DispelFocus { get { return 20.0; } }
public override PackInstinct PackInstinct { get { return PackInstinct.Daemon; } }
public override bool BleedImmune { get { return true; } } //TODO: Verify on OSI. Guide says this.
[Constructable]
public ArcaneFiend() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 )
{
Name = "an imp";
Body = 74;
BaseSoundID = 422;
SetStr( 55 );
SetDex( 40 );
SetInt( 60 );
SetDamage( 10, 14 );
SetDamageType( ResistanceType.Physical, 0 );
SetDamageType( ResistanceType.Fire, 50 );
SetDamageType( ResistanceType.Poison, 50 );
SetResistance( ResistanceType.Physical, 25, 35 );
SetResistance( ResistanceType.Fire, 40, 50 );
SetResistance( ResistanceType.Cold, 20, 30 );
SetResistance( ResistanceType.Poison, 30, 40 );
SetResistance( ResistanceType.Energy, 30, 40 );
SetSkill( SkillName.EvalInt, 20.1, 30.0 );
SetSkill( SkillName.Magery, 60.1, 70.0 );
SetSkill( SkillName.MagicResist, 30.1, 50.0 );
SetSkill( SkillName.Tactics, 42.1, 50.0 );
SetSkill( SkillName.Wrestling, 40.1, 44.0 );
Fame = 0;
Karma = 0;
ControlSlots = 1;
}
public ArcaneFiend( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,87 @@
using System;
using Server;
using Server.Misc;
namespace Server.Mobiles
{
public class NatureFury : BaseCreature
{
public override bool DeleteCorpseOnDeath { get { return Core.AOS; } }
public override bool IsHouseSummonable { get { return true; } }
public override double DispelDifficulty { get { return 125.0; } }
public override double DispelFocus { get { return 90.0; } }
public override bool BleedImmune { get { return true; } }
public override Poison PoisonImmune { get { return Poison.Lethal; } }
public override bool AlwaysMurderer { get { return true; } }
[Constructable]
public NatureFury()
: base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 )
{
Name = "a nature's fury";
Body = 0x33;
Hue = 0x4001;
SetStr( 150 );
SetDex( 150 );
SetInt( 100 );
SetHits( 80 );
SetStam( 250 );
SetMana( 0 );
SetDamage( 6, 8 );
SetDamageType( ResistanceType.Poison, 100 );
SetDamageType( ResistanceType.Physical, 0 );
SetResistance( ResistanceType.Physical, 90 );
SetSkill( SkillName.Wrestling, 90.0 );
SetSkill( SkillName.MagicResist, 70.0 );
SetSkill( SkillName.Tactics, 100.0 );
Fame = 0;
Karma = 0;
ControlSlots = 1;
}
public override void MoveToWorld( Point3D loc, Map map )
{
base.MoveToWorld( loc, map );
Timer.DelayCall( TimeSpan.Zero, DoEffects );
}
public void DoEffects()
{
FixedParticles( 0x91C, 10, 180, 0x2543, 0, 0, EffectLayer.Waist );
PlaySound( 0xE );
PlaySound( 0x1BC );
if( Alive && !Deleted )
Timer.DelayCall( TimeSpan.FromSeconds( 7.0 ), DoEffects );
}
public NatureFury( Serial serial )
: base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int)0 );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
Delete();
}
}
}

View file

@ -0,0 +1,122 @@
using System;
using Server.Mobiles;
using Server.Targeting;
using Server.Regions;
namespace Server.Spells.Spellweaving
{
public class NatureFurySpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Nature's Fury", "Rauvvrae",
-1,
false
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
public override double RequiredSkill { get { return 0.0; } }
public override int RequiredMana { get { return 24; } }
public NatureFurySpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override bool CheckCast()
{
if ( !base.CheckCast() )
return false;
if ( ( Caster.Followers + 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 point )
{
Point3D p = new Point3D( point );
Map map = Caster.Map;
if ( map == null )
return;
HouseRegion r = Region.Find( p, map ).GetRegion( typeof( HouseRegion ) ) as HouseRegion;
if ( r != null && r.House != null && !r.House.IsFriend( Caster ) )
return;
if ( !map.CanSpawnMobile( p.X, p.Y, p.Z ) )
{
Caster.SendLocalizedMessage( 501942 ); // That location is blocked.
}
else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() )
{
TimeSpan duration = TimeSpan.FromSeconds( Caster.Skills.Spellweaving.Value / 24 + 25 + FocusLevel * 2 );
NatureFury nf = new NatureFury();
BaseCreature.Summon( nf, false, Caster, p, 0x5CB, duration );
new InternalTimer( nf ).Start();
}
FinishSequence();
}
private class InternalTarget : Target
{
private NatureFurySpell m_Owner;
public InternalTarget( NatureFurySpell owner )
: base( 10, 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 )
{
if ( m_Owner != null )
m_Owner.FinishSequence();
}
}
private class InternalTimer : Timer
{
private NatureFury m_NatureFury;
public InternalTimer( NatureFury nf )
: base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) )
{
m_NatureFury = nf;
}
protected override void OnTick()
{
if ( m_NatureFury.Deleted || !m_NatureFury.Alive || m_NatureFury.DamageMin > 20 )
{
Stop();
}
else
{
++m_NatureFury.DamageMin;
++m_NatureFury.DamageMax;
}
}
}
}
}

View file

@ -0,0 +1,55 @@
using System;
using Server.Network;
namespace Server.Spells.Spellweaving
{
public class ReaperFormSpell : ArcaneForm
{
private static SpellInfo m_Info = new SpellInfo( "Reaper Form", "Tarisstree", -1 );
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.5 ); } }
public static void Initialize()
{
EventSink.Login += new LoginEventHandler( OnLogin );
}
public static void OnLogin( LoginEventArgs e )
{
TransformContext context = TransformationSpellHelper.GetContext( e.Mobile );
if( context != null && context.Type == typeof( ReaperFormSpell ) )
e.Mobile.Send( SpeedControl.WalkSpeed );
}
public override double RequiredSkill { get { return 24.0; } }
public override int RequiredMana { get { return 34; } }
public override int Body { get { return 0x11D; } }
public override int FireResistOffset { get { return -25; } }
public override int PhysResistOffset { get { return 5 + FocusLevel; } }
public override int ColdResistOffset { get { return 5 + FocusLevel; } }
public override int PoisResistOffset { get { return 5 + FocusLevel; } }
public override int NrgyResistOffset { get { return 5 + FocusLevel; } }
public virtual int SwingSpeedBonus { get { return 10 + FocusLevel; } }
public virtual int SpellDamageBonus { get { return 10 + FocusLevel; } }
public ReaperFormSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info )
{
}
public override void DoEffect( Mobile m )
{
m.PlaySound( 0x1BA );
m.Send( SpeedControl.WalkSpeed );
}
public override void RemoveEffect( Mobile m )
{
m.Send( SpeedControl.Disable );
}
}
}

View file

@ -0,0 +1,45 @@
using System;
using Server.Mobiles;
using Server.Engines.MLQuests;
namespace Server.Spells.Spellweaving
{
public class SummonFeySpell : ArcaneSummon<ArcaneFey>
{
private static SpellInfo m_Info = new SpellInfo(
"Summon Fey", "Alalithra",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
public override double RequiredSkill { get { return 38.0; } }
public override int RequiredMana { get { return 10; } }
public SummonFeySpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override int Sound { get { return 0x217; } }
public override bool CheckSequence()
{
Mobile caster = Caster;
// This is done after casting completes
if ( caster is PlayerMobile )
{
MLQuestContext context = MLQuestSystem.GetContext( (PlayerMobile)caster );
if ( context == null || !context.SummonFey )
{
caster.SendLocalizedMessage( 1074563 ); // You haven't forged a friendship with the fey and are unable to summon their aid.
return false;
}
}
return base.CheckSequence();
}
}
}

View file

@ -0,0 +1,45 @@
using System;
using Server.Mobiles;
using Server.Engines.MLQuests;
namespace Server.Spells.Spellweaving
{
public class SummonFiendSpell : ArcaneSummon<ArcaneFiend>
{
private static SpellInfo m_Info = new SpellInfo(
"Summon Fiend", "Nylisstra",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 2.0 ); } }
public override double RequiredSkill { get { return 38.0; } }
public override int RequiredMana { get { return 10; } }
public SummonFiendSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override int Sound { get { return 0x216; } }
public override bool CheckSequence()
{
Mobile caster = Caster;
// This is done after casting completes
if ( caster is PlayerMobile )
{
MLQuestContext context = MLQuestSystem.GetContext( (PlayerMobile)caster );
if ( context == null || !context.SummonFiend )
{
caster.SendLocalizedMessage( 1074564 ); // You haven't demonstrated mastery to summon a fiend.
return false;
}
}
return base.CheckSequence();
}
}
}

View file

@ -0,0 +1,101 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Server.Spells.Spellweaving
{
public class ThunderstormSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo(
"Thunderstorm", "Erelonia",
-1
);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 1.5 ); } }
public override double RequiredSkill { get { return 10.0; } }
public override int RequiredMana { get { return 32; } }
public ThunderstormSpell( Mobile caster, Item scroll )
: base( caster, scroll, m_Info )
{
}
public override void OnCast()
{
if( CheckSequence() )
{
Caster.PlaySound( 0x5CE );
double skill = Caster.Skills[SkillName.Spellweaving].Value;
int damage = Math.Max( 11, 10 + (int)(skill / 24) ) + FocusLevel;
int sdiBonus = AosAttributes.GetValue( Caster, AosAttribute.SpellDamage );
int pvmDamage = damage * ( 100 + sdiBonus );
pvmDamage /= 100;
if ( sdiBonus > 15 )
sdiBonus = 15;
int pvpDamage = damage * ( 100 + sdiBonus );
pvpDamage /= 100;
int range = 2 + FocusLevel;
TimeSpan duration = TimeSpan.FromSeconds( 5 + FocusLevel );
List<Mobile> targets = new List<Mobile>();
foreach( Mobile m in Caster.GetMobilesInRange( range ) )
{
if( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) && Caster.InLOS( m ) )
targets.Add( m );
}
for( int i = 0; i < targets.Count; i++ )
{
Mobile m = targets[i];
Caster.DoHarmful( m );
Spell oldSpell = m.Spell as Spell;
SpellHelper.Damage( this, m, ( m.Player && Caster.Player ) ? pvpDamage : pvmDamage, 0, 0, 0, 0, 100 );
if( oldSpell != null && oldSpell != m.Spell )
{
if( !CheckResisted( m ) )
{
m_Table[m] = Timer.DelayCall<Mobile>( duration, DoExpire, m );
BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Thunderstorm, 1075800, duration, m, GetCastRecoveryMalus( m ) ) );
}
}
}
}
FinishSequence();
}
private static Dictionary<Mobile, Timer> m_Table = new Dictionary<Mobile, Timer>();
public static int GetCastRecoveryMalus( Mobile m )
{
return m_Table.ContainsKey( m ) ? 6 : 0;
}
public static void DoExpire( Mobile m )
{
Timer t;
if( m_Table.TryGetValue( m, out t ) )
{
t.Stop();
m_Table.Remove( m );
BuffInfo.RemoveBuff( m, BuffIcon.Thunderstorm );
}
}
}
}

View file

@ -0,0 +1,93 @@
using System;
using Server.Targeting;
using Server.Network;
namespace Server.Spells.Spellweaving
{
public class WordOfDeathSpell : ArcanistSpell
{
private static SpellInfo m_Info = new SpellInfo( "Word of Death", "Nyraxle", -1 );
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 3.5 ); } }
public override double RequiredSkill { get { return 80.0; } }
public override int RequiredMana { get { return 50; } }
public WordOfDeathSpell( 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 ) )
{
Point3D loc = m.Location;
loc.Z += 50;
m.PlaySound( 0x211 );
m.FixedParticles( 0x3779, 1, 30, 0x26EC, 0x3, 0x3, EffectLayer.Waist );
Effects.SendMovingParticles( new Entity( Serial.Zero, loc, m.Map ), new Entity( Serial.Zero, m.Location, m.Map ), 0xF5F, 1, 0, true, false, 0x21, 0x3F, 0x251D, 0, 0, EffectLayer.Head, 0 );
double percentage = 0.05 * FocusLevel;
int damage;
if( !m.Player && (((double)m.Hits / (double)m.HitsMax) < percentage ))
{
damage = 300;
}
else
{
int minDamage = (int)Caster.Skills.Spellweaving.Value / 5;
int maxDamage = (int)Caster.Skills.Spellweaving.Value / 3;
damage = Utility.RandomMinMax(minDamage, maxDamage);
int damageBonus = AosAttributes.GetValue( Caster, AosAttribute.SpellDamage );
if (m.Player && damageBonus > 15)
damageBonus = 15;
damage *= damageBonus + 100;
damage /= 100;
}
int[] types = new int[4];
types[Utility.Random( types.Length )] = 100;
SpellHelper.Damage( this, m, damage, 0, types[0], types[1], types[2], types[3] ); //Chaos damage. Random elemental damage
}
FinishSequence();
}
public class InternalTarget : Target
{
private WordOfDeathSpell m_Owner;
public InternalTarget( WordOfDeathSpell owner ) : base( 10, false, TargetFlags.Harmful )
{
m_Owner = owner;
}
protected override void OnTarget( Mobile m, object o )
{
if( o is Mobile )
{
m_Owner.Target( (Mobile)o );
}
}
protected override void OnTargetFinish( Mobile m )
{
m_Owner.FinishSequence();
}
}
}
}