This commit is contained in:
commit
47711d616e
2644 changed files with 479454 additions and 0 deletions
433
Scripts/Spells/Ninjitsu/AnimalForm.cs
Normal file
433
Scripts/Spells/Ninjitsu/AnimalForm.cs
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
using Server.Network;
|
||||
using Server.Mobiles;
|
||||
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( SpeedBoost.Enabled );
|
||||
}
|
||||
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Animal Form", null,
|
||||
SpellCircle.Fourth, // 1.0s base cast delay
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
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 ( Necromancy.TransformationSpell.UnderTransformation( Caster ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1063219 ); // You cannot mimic an animal while in that 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.FixedEffect( 0x37C4, 10, 14, 4, 3 );
|
||||
}
|
||||
|
||||
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 ( Necromancy.TransformationSpell.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 );
|
||||
|
||||
if ( context != null )
|
||||
{
|
||||
RemoveContext( Caster, context, true );
|
||||
}
|
||||
else if ( Caster is PlayerMobile )
|
||||
{
|
||||
if ( GetLastAnimalForm( Caster ) == -1 || DateTime.Now - Caster.LastMoveTime > Caster.ComputeMovementSpeed( Caster.Direction ) )
|
||||
{
|
||||
Caster.CloseGump( typeof( AnimalFormGump ) );
|
||||
Caster.SendGump( new AnimalFormGump( Caster, m_Entries, this ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Morph( Caster, GetLastAnimalForm( Caster ) ) == MorphResult.Fail )
|
||||
DoFizzle();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Morph( Caster, GetLastAnimalForm( Caster ) ) == MorphResult.Fail )
|
||||
DoFizzle();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
BaseMount.Dismount( m );
|
||||
|
||||
m.BodyMod = entry.BodyMod;
|
||||
|
||||
if ( entry.HueMod > 0 )
|
||||
m.HueMod = entry.HueMod;
|
||||
|
||||
if ( entry.SpeedBoost )
|
||||
m.Send( SpeedBoost.Instantiate( true ) );
|
||||
|
||||
SkillMod mod = null;
|
||||
|
||||
if ( entry.StealthBonus )
|
||||
{
|
||||
mod = new DefaultSkillMod( SkillName.Stealth, true, 20.0 );
|
||||
mod.ObeyCap = true;
|
||||
m.AddSkillMod( mod );
|
||||
}
|
||||
|
||||
Timer timer = new AnimalFormTimer( m, entry.BodyMod, entry.HueMod );
|
||||
timer.Start();
|
||||
|
||||
AddContext( m, new AnimalFormContext( timer, mod, entry.SpeedBoost, entry.Type ) );
|
||||
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.Hits += 20;
|
||||
}
|
||||
|
||||
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( SpeedBoost.Instantiate( false ) );
|
||||
|
||||
SkillMod mod = context.Mod;
|
||||
|
||||
if ( mod != null )
|
||||
m.RemoveSkillMod( mod );
|
||||
|
||||
if ( resetGraphics )
|
||||
{
|
||||
m.HueMod = -1;
|
||||
m.BodyMod = 0;
|
||||
}
|
||||
|
||||
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_HueMod;
|
||||
private bool m_StealthBonus;
|
||||
private bool m_SpeedBoost;
|
||||
|
||||
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 m_HueMod; } }
|
||||
public bool StealthBonus{ get{ return m_StealthBonus; } }
|
||||
public bool SpeedBoost{ get{ return m_SpeedBoost; } }
|
||||
/*
|
||||
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, bool stealthBonus, bool speedBoost )
|
||||
: this( type, name, itemID, hue, tooltip, reqSkill, bodyMod, 0, stealthBonus, speedBoost )
|
||||
{
|
||||
}
|
||||
|
||||
public AnimalFormEntry( Type type, TextDefinition name, int itemID, int hue, int tooltip, double reqSkill, int bodyMod, int hueMod, bool stealthBonus, bool speedBoost )
|
||||
{
|
||||
m_Type = type;
|
||||
m_Name = name;
|
||||
m_ItemID = itemID;
|
||||
m_Hue = hue;
|
||||
m_Tooltip = tooltip;
|
||||
m_ReqSkill = reqSkill;
|
||||
m_BodyMod = bodyMod;
|
||||
m_HueMod = hueMod;
|
||||
m_StealthBonus = stealthBonus;
|
||||
m_SpeedBoost = speedBoost;
|
||||
}
|
||||
}
|
||||
|
||||
private static AnimalFormEntry[] m_Entries = new AnimalFormEntry[]
|
||||
{
|
||||
new AnimalFormEntry( typeof( Kirin ), 1029632, 9632, 0, 1070811, 100.0, 0x84, false, true ),
|
||||
new AnimalFormEntry( typeof( Unicorn ), 1018214, 9678, 0, 1070812, 100.0, 0x7A, false, true ),
|
||||
new AnimalFormEntry( typeof( BakeKitsune ), 1030083, 10083, 0, 1070810, 82.5, 0xF6, false, true ),
|
||||
new AnimalFormEntry( typeof( GreyWolf ), 1028482, 9681, 2309, 1070810, 82.5, 0x19, false, true ),
|
||||
new AnimalFormEntry( typeof( Llama ), 1028438, 8438, 0, 1070809, 70.0, 0xDC, false, true ),
|
||||
new AnimalFormEntry( typeof( ForestOstard ), 1018273, 8503, 2212, 1070809, 70.0, 0xDA, false, true ),
|
||||
new AnimalFormEntry( typeof( BullFrog ), 1028496, 8496, 2003, 1070807, 50.0, 0x51, 0x5A3, false, false ),
|
||||
new AnimalFormEntry( typeof( GiantSerpent ), 1018114, 9663, 2009, 1070808, 50.0, 0x15, false, false ),
|
||||
new AnimalFormEntry( typeof( Dog ), 1018280, 8476, 2309, 1070806, 40.0, 0xD9, false, false ),
|
||||
new AnimalFormEntry( typeof( Cat ), 1018264, 8475, 2309, 1070806, 40.0, 0xC9, false, false ),
|
||||
new AnimalFormEntry( typeof( Rat ), 1018294, 8483, 2309, 1070805, 20.0, 0xEE, true, false ),
|
||||
new AnimalFormEntry( typeof( Rabbit ), 1028485, 8485, 2309, 1070805, 20.0, 0xCD, true, 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;
|
||||
|
||||
public AnimalFormGump( Mobile caster, AnimalFormEntry[] entries, AnimalForm spell ) : base( 50, 50 )
|
||||
{
|
||||
m_Caster = caster;
|
||||
m_Spell = spell;
|
||||
|
||||
AddPage( 0 );
|
||||
|
||||
AddBackground( 0, 0, 408, 298, 0x13BE );
|
||||
AddBackground( 4, 28, 400, 240, 0xBB8 );
|
||||
|
||||
AddHtmlLocalized( 4, 4, 400, 20, 1063394, 0x0, false, false ); // <center>Animal Form Selection Menu</center>
|
||||
|
||||
AddButton( 25, 272, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 );
|
||||
AddHtmlLocalized( 60, 274, 150, 20, 1011036, 0x0, false, false ); // OKAY
|
||||
|
||||
AddButton( 285, 272, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 );
|
||||
AddHtmlLocalized( 320, 274, 150, 20, 1011012, 0x0, false, false ); // CANCEL
|
||||
|
||||
double ninjitsu = caster.Skills.Ninjitsu.Value;
|
||||
|
||||
for ( int i = 0; i < entries.Length; ++i )
|
||||
{
|
||||
bool enabled = ( ninjitsu >= entries[i].ReqSkill );
|
||||
|
||||
int x = 100 * ( i % 4 );
|
||||
int y = 80 * ( i / 4 );
|
||||
|
||||
TextDefinition.AddHtmlText( this, 10 + x, 30 + y, 100, 18, entries[i].Name, false, false );
|
||||
|
||||
if ( enabled )
|
||||
{
|
||||
AddRadio( 10 + x, 50 + y, 0xD2, 0xD3, false, 100 + i );
|
||||
AddItem( 30 + x, 50 + y, entries[i].ItemID, entries[i].Hue );
|
||||
}
|
||||
else
|
||||
AddItem( 10 + x, 50 + y, entries[i].ItemID, 0x3E3 );
|
||||
|
||||
AddTooltip( enabled ? entries[i].Tooltip : 1070708 );
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnResponse( NetState sender, RelayInfo info )
|
||||
{
|
||||
if ( info.ButtonID == 1 && info.Switches.Length > 0 )
|
||||
{
|
||||
int entryID = info.Switches[0] - 100;
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimalFormContext
|
||||
{
|
||||
private Timer m_Timer;
|
||||
private SkillMod m_Mod;
|
||||
private bool m_SpeedBoost;
|
||||
private Type m_Type;
|
||||
|
||||
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 AnimalFormContext( Timer timer, SkillMod mod, bool speedBoost, Type type )
|
||||
{
|
||||
m_Timer = timer;
|
||||
m_Mod = mod;
|
||||
m_SpeedBoost = speedBoost;
|
||||
m_Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimalFormTimer : Timer
|
||||
{
|
||||
private Mobile m_Mobile;
|
||||
private int m_Body;
|
||||
private int m_Hue;
|
||||
|
||||
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;
|
||||
|
||||
Priority = TimerPriority.FiftyMS;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if ( m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Body || (m_Hue != 0 && m_Mobile.Hue != m_Hue) )
|
||||
{
|
||||
AnimalForm.RemoveContext( m_Mobile, true );
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Scripts/Spells/Ninjitsu/Backstab.cs
Normal file
73
Scripts/Spells/Ninjitsu/Backstab.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
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
|
||||
{
|
||||
// TODO: Cannot hide for 5s
|
||||
|
||||
public Backstab()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 30; } }
|
||||
public override double RequiredSkill{ get{ return 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 )
|
||||
{
|
||||
return Validate( attacker ) && CheckMana( attacker, true );
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
138
Scripts/Spells/Ninjitsu/DeathStrike.cs
Normal file
138
Scripts/Spells/Ninjitsu/DeathStrike.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
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;
|
||||
|
||||
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 ) )
|
||||
{
|
||||
attacker.SendLocalizedMessage( 1063092 ); // Your opponent lands another Death Strike!
|
||||
|
||||
info = (DeathStrikeInfo)m_Table[defender];
|
||||
|
||||
if( info.m_Steps > 0 )
|
||||
damageBonus = info.m_Attacker.Skills[SkillName.Ninjitsu].Fixed / 150;
|
||||
|
||||
if( info.m_Timer != null )
|
||||
info.m_Timer.Stop();
|
||||
|
||||
m_Table.Remove( defender );
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker.SendLocalizedMessage( 1063094 ); // You inflict a Death Strike upon your opponent!
|
||||
defender.SendLocalizedMessage( 1063093 ); // You have been hit by a Death Strike! Move with caution!
|
||||
}
|
||||
|
||||
defender.FixedParticles( 0x374A, 1, 17, 0x26BC, EffectLayer.Waist );
|
||||
attacker.PlaySound( 0x50D );
|
||||
|
||||
info = new DeathStrikeInfo( defender, attacker, damageBonus );
|
||||
info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( ProcessDeathStrike ), info );
|
||||
|
||||
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 DeathStrikeInfo( Mobile target, Mobile attacker, int damageBonus )
|
||||
{
|
||||
m_Target = target;
|
||||
m_Attacker = attacker;
|
||||
m_DamageBonus = damageBonus;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddStep( Mobile m )
|
||||
{
|
||||
DeathStrikeInfo info = m_Table[m] as DeathStrikeInfo;
|
||||
|
||||
if( info == null )
|
||||
return;
|
||||
|
||||
info.m_Steps++;
|
||||
}
|
||||
|
||||
private static void ProcessDeathStrike( object state )
|
||||
{
|
||||
DeathStrikeInfo info = (DeathStrikeInfo)state;
|
||||
|
||||
double ninjitsu = info.m_Attacker.Skills[SkillName.Ninjitsu].Fixed;
|
||||
int divisor = (info.m_Steps >= 5) ? 30 : 80;
|
||||
|
||||
double baseDamage = ninjitsu / divisor;
|
||||
double stalkingBonus = Tracking.GetStalkingBonus( info.m_Attacker, info.m_Target );
|
||||
|
||||
int maxDamage = (info.m_Steps >= 5) ? 62 : 22;
|
||||
|
||||
int damage = Math.Max( 0, Math.Min( maxDamage, (int)(baseDamage + stalkingBonus) ) );
|
||||
|
||||
// This bonus is 8 at most. That brings the cap up to 70/30.
|
||||
damage += info.m_DamageBonus;
|
||||
|
||||
// Damage is direct.
|
||||
//info.m_Target.Damage( damage, info.m_Attacker );
|
||||
|
||||
AOS.Damage( info.m_Target, info.m_Attacker, damage, true, 100, 0, 0, 0, 0 );
|
||||
m_Table.Remove( info.m_Target );
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Scripts/Spells/Ninjitsu/FocusAttack.cs
Normal file
75
Scripts/Spells/Ninjitsu/FocusAttack.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class FocusAttack : NinjaMove
|
||||
{
|
||||
// TODO: Display property bonus on equipped weapons.
|
||||
|
||||
public FocusAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 20; } }
|
||||
public override double RequiredSkill{ get{ return 60.0; } }
|
||||
|
||||
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!
|
||||
|
||||
CheckGain( attacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Scripts/Spells/Ninjitsu/KiAttack.cs
Normal file
128
Scripts/Spells/Ninjitsu/KiAttack.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
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;
|
||||
}
|
||||
|
||||
return base.Validate( from );
|
||||
}
|
||||
|
||||
public override double GetDamageScalar( Mobile attacker, Mobile defender )
|
||||
{
|
||||
if ( attacker.Hidden )
|
||||
return 1.0;
|
||||
|
||||
return 1.0 + GetBonus( attacker ) / 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.SendLocalizedMessage( 1063100 ); // Your quick flight to your target causes extra damage as you strike!
|
||||
defender.FixedParticles( 0x37BE, 1, 5, 0x26BD, 0, 0x1, EffectLayer.Waist );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
266
Scripts/Spells/Ninjitsu/MirrorImage.cs
Normal file
266
Scripts/Spells/Ninjitsu/MirrorImage.cs
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Mobiles;
|
||||
using Server.Items;
|
||||
using Server.Spells.Ninjitsu;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class MirrorImage : NinjaSpell
|
||||
{
|
||||
private static Hashtable m_CloneCount = new Hashtable();
|
||||
|
||||
public static bool HasClone( Mobile m )
|
||||
{
|
||||
return (m_CloneCount.Contains( m ) && ((int)m_CloneCount[m]) > 0);
|
||||
}
|
||||
|
||||
public static void AddClone( Mobile m )
|
||||
{
|
||||
if( m == null )
|
||||
return;
|
||||
|
||||
if( m_CloneCount.Contains( m ) )
|
||||
m_CloneCount[m] = ((int)m_CloneCount[m] +1);
|
||||
else
|
||||
m_CloneCount.Add( m, 1 );
|
||||
}
|
||||
|
||||
public static void RemoveClone( Mobile m )
|
||||
{
|
||||
if( m == null )
|
||||
return;
|
||||
|
||||
if( m_CloneCount.Contains( m ) )
|
||||
{
|
||||
m_CloneCount[m] = ((int)m_CloneCount[m] -1);
|
||||
|
||||
if( ((int)m_CloneCount[m]) <= 0 )
|
||||
m_CloneCount.Remove( m );
|
||||
}
|
||||
}
|
||||
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Mirror Image", null,
|
||||
SpellCircle.Sixth, // 1.5s base cast delay
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
public override double RequiredSkill{ get{ return 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 ( Necromancy.TransformationSpell.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 ( Necromancy.TransformationSpell.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.Now + 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Scripts/Spells/Ninjitsu/NinjaMove.cs
Normal file
14
Scripts/Spells/Ninjitsu/NinjaMove.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
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; } }
|
||||
}
|
||||
}
|
||||
102
Scripts/Spells/Ninjitsu/NinjaSpell.cs
Normal file
102
Scripts/Spells/Ninjitsu/NinjaSpell.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
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 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.Flags & 0x10) != 0 );
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
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 < ScaleMana( RequiredMana ) )
|
||||
{
|
||||
Caster.SendLocalizedMessage( 1060174, RequiredMana.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;
|
||||
max = RequiredSkill + 37.5;
|
||||
}
|
||||
|
||||
public override int GetMana()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Scripts/Spells/Ninjitsu/ShadowJump.cs
Normal file
127
Scripts/Spells/Ninjitsu/ShadowJump.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Server.Network;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Ninjitsu
|
||||
{
|
||||
public class Shadowjump : NinjaSpell
|
||||
{
|
||||
private static SpellInfo m_Info = new SpellInfo(
|
||||
"Shadowjump", null,
|
||||
SpellCircle.Fourth, // 1.0s base cast delay
|
||||
-1,
|
||||
9002
|
||||
);
|
||||
|
||||
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()
|
||||
{
|
||||
if ( !Caster.Hidden || Caster.AllowedStealthSteps <= 0 )
|
||||
{
|
||||
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 );
|
||||
|
||||
if ( !Caster.Hidden || Caster.AllowedStealthSteps <= 0 )
|
||||
{
|
||||
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, new Point3D( p ), 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( new Point3D( p ), map ) )
|
||||
{
|
||||
// TODO: Cannot shadowjump within 5 tiles from any house.
|
||||
Caster.SendLocalizedMessage( 502831 ); // Cannot teleport to that spot.
|
||||
}
|
||||
else if ( CheckSequence() )
|
||||
{
|
||||
SpellHelper.Turn( Caster, orig );
|
||||
|
||||
Mobile m = Caster;
|
||||
|
||||
Point3D from = m.Location;
|
||||
Point3D to = new Point3D( p );
|
||||
|
||||
m.Location = to;
|
||||
m.ProcessDelta();
|
||||
|
||||
Effects.SendLocationParticles( EffectItem.Create( from, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 );
|
||||
|
||||
m.PlaySound( 0x512 );
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if ( !from.CheckSkill( SkillName.Hiding, 0.0, 100.0 ) ) //TODO: Hiding check or stealth check?
|
||||
from.RevealingAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Scripts/Spells/Ninjitsu/SurpriseAttack.cs
Normal file
127
Scripts/Spells/Ninjitsu/SurpriseAttack.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
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
|
||||
{
|
||||
// TODO: Cannot hide for 5s
|
||||
|
||||
public SurpriseAttack()
|
||||
{
|
||||
}
|
||||
|
||||
public override int BaseMana{ get{ return 20; } }
|
||||
public override double RequiredSkill{ get{ return 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 )
|
||||
{
|
||||
return Validate( attacker ) && CheckMana( attacker, true );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue