Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View File

@@ -0,0 +1,699 @@
#region References
using System;
using System.Collections.Generic;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Spells.Fifth;
using Server.Spells.Seventh;
#endregion
namespace Server.Spells.Ninjitsu
{
public class AnimalForm : NinjaSpell
{
public static void Initialize()
{
EventSink.Login += OnLogin;
}
public static void OnLogin(LoginEventArgs e)
{
AnimalFormContext context = GetContext(e.Mobile);
if (context != null && context.SpeedBoost)
{
e.Mobile.SendSpeedControl(SpeedControlType.MountSpeed);
}
}
private static readonly SpellInfo m_Info = new SpellInfo("Animal Form", null, -1, 9002);
public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(1.0); } }
public override double RequiredSkill { get { return 0.0; } }
public override int RequiredMana { get { return (Core.ML ? 10 : 0); } }
public override int CastRecoveryBase { get { return (Core.ML ? 10 : base.CastRecoveryBase); } }
public override bool BlockedByAnimalForm { get { return false; } }
public AnimalForm(Mobile caster, Item scroll)
: base(caster, scroll, m_Info)
{ }
public override bool Cast()
{
if (CasterIsMoving() && GetLastAnimalForm(Caster) == 16)
{
SkillMasteries.WhiteTigerFormSpell.AutoCast(Caster);
return false;
}
return base.Cast();
}
public override bool CheckCast()
{
if (!Caster.CanBeginAction(typeof(PolymorphSpell)))
{
Caster.SendLocalizedMessage(1061628); // You can't do that while polymorphed.
return false;
}
else if (TransformationSpellHelper.UnderTransformation(Caster))
{
Caster.SendLocalizedMessage(1063219); // You cannot mimic an animal while in that form.
return false;
}
else if (DisguiseTimers.IsDisguised(Caster))
{
Caster.SendLocalizedMessage(1061631); // You can't do that while disguised.
return false;
}
return base.CheckCast();
}
public override bool CheckDisturb(DisturbType type, bool firstCircle, bool resistable)
{
return false;
}
private bool CasterIsMoving()
{
return (Core.TickCount - Caster.LastMoveTime <= Caster.ComputeMovementSpeed(Caster.Direction));
}
private bool m_WasMoving;
public override void OnBeginCast()
{
base.OnBeginCast();
Caster.FixedEffect(0x37C4, 10, 14, 4, 3);
m_WasMoving = CasterIsMoving();
}
public override bool CheckFizzle()
{
// Spell is initially always successful, and with no skill gain.
return true;
}
public override void OnCast()
{
if (!Caster.CanBeginAction(typeof(PolymorphSpell)))
{
Caster.SendLocalizedMessage(1061628); // You can't do that while polymorphed.
}
else if (TransformationSpellHelper.UnderTransformation(Caster))
{
Caster.SendLocalizedMessage(1063219); // You cannot mimic an animal while in that form.
}
else if (!Caster.CanBeginAction(typeof(IncognitoSpell)) || (Caster.IsBodyMod && GetContext(Caster) == null))
{
DoFizzle();
}
else if (CheckSequence())
{
AnimalFormContext context = GetContext(Caster);
int mana = ScaleMana(RequiredMana);
if (mana > Caster.Mana)
{
Caster.SendLocalizedMessage(1060174, mana.ToString());
// You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
}
else if (context != null)
{
RemoveContext(Caster, context, true);
Caster.Mana -= mana;
}
else if (Caster is PlayerMobile)
{
bool skipGump = (m_WasMoving || CasterIsMoving());
if (GetLastAnimalForm(Caster) == -1 || GetLastAnimalForm(Caster) == 16 || !skipGump)
{
Caster.CloseGump(typeof(AnimalFormGump));
Caster.SendGump(new AnimalFormGump(Caster, m_Entries, this));
}
else
{
if (Morph(Caster, GetLastAnimalForm(Caster)) == MorphResult.Fail)
{
DoFizzle();
}
else
{
Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
Caster.Mana -= mana;
}
}
}
else
{
if (Morph(Caster, GetLastAnimalForm(Caster)) == MorphResult.Fail)
{
DoFizzle();
}
else
{
Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
Caster.Mana -= mana;
}
}
}
FinishSequence();
}
private static readonly Dictionary<Mobile, int> m_LastAnimalForms = new Dictionary<Mobile, int>();
public static void AddLastAnimalForm(Mobile m, int id)
{
m_LastAnimalForms[m] = id;
}
public int GetLastAnimalForm(Mobile m)
{
if (m_LastAnimalForms.ContainsKey(m))
{
return 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];
AddLastAnimalForm(m, entryID); //On OSI, it's the last /attempted/ one not the last succeeded one
if (m.Skills.Ninjitsu.Value < entry.ReqSkill)
{
string args = String.Format("{0}\t{1}\t ", entry.ReqSkill.ToString("F1"), SkillName.Ninjitsu);
m.SendLocalizedMessage(1063013, args);
// You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
return MorphResult.NoSkill;
}
/*
if( !m.CheckSkill( SkillName.Ninjitsu, entry.ReqSkill, entry.ReqSkill + 37.5 ) )
return MorphResult.Fail;
*
* On OSI,it seems you can only gain starting at '0' using Animal form.
*/
double ninjitsu = m.Skills.Ninjitsu.Value;
if (ninjitsu < entry.ReqSkill + 37.5)
{
double chance = (ninjitsu - entry.ReqSkill) / 37.5;
if (chance < Utility.RandomDouble())
{
return MorphResult.Fail;
}
}
m.CheckSkill(SkillName.Ninjitsu, 0.0, 37.5);
if (!BaseFormTalisman.EntryEnabled(m, entry.Type))
{
return MorphResult.Success; // Still consumes mana, just no effect
}
BaseMount.Dismount(m);
int bodyMod = entry.BodyMod;
int hueMod = entry.HueMod;
m.BodyMod = bodyMod;
m.HueMod = hueMod;
if (entry.SpeedBoost)
{
m.SendSpeedControl(SpeedControlType.MountSpeed);
}
SkillMod mod = null;
if (entry.StealthBonus)
{
mod = new DefaultSkillMod(SkillName.Stealth, true, 20.0);
mod.ObeyCap = true;
m.AddSkillMod(mod);
}
SkillMod stealingMod = null;
if (entry.StealingBonus)
{
stealingMod = new DefaultSkillMod(SkillName.Stealing, true, 10.0);
stealingMod.ObeyCap = true;
m.AddSkillMod(stealingMod);
}
Timer timer = new AnimalFormTimer(m, bodyMod, hueMod);
timer.Start();
AddContext(m, new AnimalFormContext(timer, mod, entry.SpeedBoost, entry.Type, stealingMod));
return MorphResult.Success;
}
private static readonly Dictionary<Mobile, AnimalFormContext> m_Table = new Dictionary<Mobile, AnimalFormContext>();
public static void AddContext(Mobile m, AnimalFormContext context)
{
m_Table[m] = context;
if (context.Type == typeof(BakeKitsune) || context.Type == typeof(GreyWolf)
|| context.Type == typeof(Dog) || context.Type == typeof(Cat) || context.Type == typeof(WildWhiteTiger))
{
m.ResetStatTimers();
}
m.Delta(MobileDelta.WeaponDamage);
}
public static void RemoveContext(Mobile m, bool resetGraphics)
{
AnimalFormContext context = GetContext(m);
if (context != null)
{
RemoveContext(m, context, resetGraphics);
}
m.Delta(MobileDelta.WeaponDamage);
}
public static void RemoveContext(Mobile m, AnimalFormContext context, bool resetGraphics)
{
m_Table.Remove(m);
if (context.SpeedBoost)
{
if (m.Region is Server.Regions.TwistedWealdDesert)
m.SendSpeedControl(SpeedControlType.WalkSpeed);
else
m.SendSpeedControl(SpeedControlType.Disable);
}
SkillMod mod = context.Mod;
if (mod != null)
{
m.RemoveSkillMod(mod);
}
mod = context.StealingMod;
if (mod != null)
{
m.RemoveSkillMod(mod);
}
if (resetGraphics)
{
m.HueMod = -1;
m.BodyMod = 0;
}
m.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
context.Timer.Stop();
BuffInfo.RemoveBuff(m, BuffIcon.AnimalForm);
BuffInfo.RemoveBuff(m, BuffIcon.WhiteTigerForm);
}
public static AnimalFormContext GetContext(Mobile m)
{
if (m_Table.ContainsKey(m))
return m_Table[m];
return null;
}
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 readonly Type m_Type;
private readonly string m_Name;
private readonly int m_ItemID;
private readonly int m_Hue;
private readonly int m_Tooltip;
private readonly double m_ReqSkill;
private readonly int m_BodyMod;
private readonly int m_HueModMin;
private readonly int m_HueModMax;
private readonly bool m_StealthBonus;
private readonly bool m_SpeedBoost;
private readonly bool m_StealingBonus;
public Type Type { get { return m_Type; } }
public string Name { get { return m_Name; } }
public int ItemID { get { return m_ItemID; } }
public int Hue { get { return m_Hue; } }
public int Tooltip { get { return m_Tooltip; } }
public double ReqSkill { get { return m_ReqSkill; } }
public int BodyMod { get { return m_BodyMod; } }
public int HueMod { get { return Utility.RandomMinMax(m_HueModMin, m_HueModMax); } }
public bool StealthBonus { get { return m_StealthBonus; } }
public bool SpeedBoost { get { return m_SpeedBoost; } }
public bool StealingBonus { get { return m_StealingBonus; } }
/*
private AnimalFormCallback m_TransformCallback;
private AnimalFormCallback m_UntransformCallback;
private AnimalFormRequirementCallback m_RequirementCallback;
*/
public AnimalFormEntry(
Type type,
string name,
int itemID,
int hue,
int tooltip,
double reqSkill,
int bodyMod,
int hueModMin,
int hueModMax,
bool stealthBonus,
bool speedBoost,
bool stealingBonus)
{
m_Type = type;
m_Name = name;
m_ItemID = itemID;
m_Hue = hue;
m_Tooltip = tooltip;
m_ReqSkill = reqSkill;
m_BodyMod = bodyMod;
m_HueModMin = hueModMin;
m_HueModMax = hueModMax;
m_StealthBonus = stealthBonus;
m_SpeedBoost = speedBoost;
m_StealingBonus = stealingBonus;
}
}
private static readonly AnimalFormEntry[] m_Entries = new[]
{
new AnimalFormEntry(typeof(Kirin), "kirin", 9632, 0, 1070811, 100.0, 0x84, 0, 0, false, true, false),
new AnimalFormEntry(typeof(Unicorn), "unicorn", 9678, 0, 1070812, 100.0, 0x7A, 0, 0, false, true, false),
new AnimalFormEntry(typeof(BakeKitsune), "bake-kitsune", 10083, 0, 1070810, 82.5, 0xF6, 0, 0, false, true, false),
new AnimalFormEntry(typeof(GreyWolf), "wolf", 9681, 2309, 1070810, 82.5, 0x19, 0x8FD, 0x90E, false, true, false),
new AnimalFormEntry(typeof(Llama), "llama", 8438, 0, 1070809, 70.0, 0xDC, 0, 0, false, true, false),
new AnimalFormEntry(typeof(ForestOstard), "ostard", 8503, 2212, 1070809, 70.0, 0xDB, 0x899, 0x8B0, false, true, false),
new AnimalFormEntry(typeof(BullFrog), "bullfrog", 8496, 2003, 1070807, 50.0, 0x51, 0x7D1, 0x7D6, false, false, false),
new AnimalFormEntry(typeof(GiantSerpent), "giant serpent", 9663, 2009, 1070808, 50.0, 0x15, 0x7D1, 0x7E2, false, false, false),
new AnimalFormEntry(typeof(Dog), "dog", 8476, 2309, 1070806, 40.0, 0xD9, 0x8FD, 0x90E, false, false, false),
new AnimalFormEntry(typeof(Cat), "cat", 8475, 2309, 1070806, 40.0, 0xC9, 0x8FD, 0x90E, false, false, false),
new AnimalFormEntry(typeof(Rat), "rat", 8483, 2309, 1070805, 20.0, 0xEE, 0x8FD, 0x90E, true, false, false),
new AnimalFormEntry(typeof(Rabbit), "rabbit", 8485, 2309, 1070805, 20.0, 0xCD, 0x8FD, 0x90E, true, false, false),
new AnimalFormEntry(typeof(Squirrel), "squirrel", 11671, 0, 0, 20.0, 0x116, 0, 0, false, false, false),
new AnimalFormEntry(typeof(Ferret), "ferret", 11672, 0, 1075220, 40.0, 0x117, 0, 0, false, false, true),
new AnimalFormEntry(typeof(CuSidhe), "cu sidhe", 11670, 0, 1075221, 60.0, 0x115, 0, 0, false, false, false),
new AnimalFormEntry(typeof(Reptalon), "reptalon", 11669, 0, 1075222, 90.0, 0x114, 0, 0, false, false, false),
new AnimalFormEntry(typeof(WildWhiteTiger), "white tiger", 38980, 2500, 0, 0, 0x4E7, 0, 0, false, false, false),
};
public static AnimalFormEntry[] Entries { get { return m_Entries; } }
public class AnimalFormGump : Gump
{
//TODO: Convert this for ML to the BaseImageTileButtonsgump
private readonly Mobile m_Caster;
private readonly AnimalForm m_Spell;
private readonly Item m_Talisman;
public AnimalFormGump(Mobile caster, AnimalFormEntry[] entries, AnimalForm spell)
: base(50, 50)
{
m_Caster = caster;
m_Spell = spell;
m_Talisman = caster.Talisman;
AddPage(0);
AddBackground(0, 0, 520, 404, 0x13BE);
AddImageTiled(10, 10, 500, 20, 0xA40);
AddImageTiled(10, 40, 500, 324, 0xA40);
AddImageTiled(10, 374, 500, 20, 0xA40);
AddAlphaRegion(10, 10, 500, 384);
AddHtmlLocalized(14, 12, 500, 20, 1063394, 0x7FFF, false, false); // <center>Polymorph Selection Menu</center>
AddButton(10, 374, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
AddHtmlLocalized(45, 376, 450, 20, 1011012, 0x7FFF, false, false); // CANCEL
double ninjitsu = caster.Skills.Ninjitsu.Value;
int current = 0;
for (int i = 0; i < entries.Length; ++i)
{
bool enabled = (ninjitsu >= entries[i].ReqSkill && BaseFormTalisman.EntryEnabled(caster, entries[i].Type) && entries[i].Type != typeof(WildWhiteTiger));
int page = current / 10 + 1;
int pos = current % 10;
if (pos == 0)
{
if (page > 1)
{
AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page);
AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF, false, false); // Next
}
AddPage(page);
if (page > 1)
{
AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 1);
AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF, false, false); // Back
}
}
if (enabled)
{
int x = (pos % 2 == 0) ? 14 : 264;
int y = (pos / 2) * 64 + 44;
Rectangle2D b = ItemBounds.Table[entries[i].ItemID];
AddImageTiledButton(
x,
y,
0x918,
0x919,
i + 1,
GumpButtonType.Reply,
0,
entries[i].ItemID,
entries[i].Hue,
40 - b.Width / 2 - b.X,
30 - b.Height / 2 - b.Y,
entries[i].Tooltip);
AddHtml(x + 84, y, 250, 60, Color(String.Format(entries[i].Name), 0xFFFFFF), false, false);
current++;
}
}
}
private string Color(string str, int color)
{
return String.Format("<BASEFONT COLOR=#{0:X6}>{1}</BASEFONT>", color, str);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
int entryID = info.ButtonID - 1;
if (entryID < 0 || entryID >= m_Entries.Length)
{
return;
}
int mana = m_Spell.ScaleMana(m_Spell.RequiredMana);
AnimalFormEntry entry = AnimalForm.Entries[entryID];
if (mana > m_Caster.Mana)
{
m_Caster.SendLocalizedMessage(1060174, mana.ToString());
// You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
}
else if (BaseFormTalisman.EntryEnabled(sender.Mobile, entry.Type))
{
if (Morph(m_Caster, entryID) == MorphResult.Fail)
{
m_Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502632); // The spell fizzles.
m_Caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist);
m_Caster.PlaySound(0x5C);
}
else
{
m_Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist);
m_Caster.Mana -= mana;
string typename = entry.Name;
BuffInfo.AddBuff(m_Caster, new BuffInfo(BuffIcon.AnimalForm, 1060612, 1075823, String.Format("{0}\t{1}", "aeiouy".IndexOf(typename.ToLower()[0]) >= 0 ? "an" : "a", typename)));
}
}
}
}
}
public class AnimalFormContext
{
private readonly Timer m_Timer;
private readonly SkillMod m_Mod;
private readonly bool m_SpeedBoost;
private readonly Type m_Type;
private readonly SkillMod m_StealingMod;
public Timer Timer { get { return m_Timer; } }
public SkillMod Mod { get { return m_Mod; } }
public bool SpeedBoost { get { return m_SpeedBoost; } }
public Type Type { get { return m_Type; } }
public SkillMod StealingMod { get { return m_StealingMod; } }
public AnimalFormContext(Timer timer, SkillMod mod, bool speedBoost, Type type, SkillMod stealingMod)
{
m_Timer = timer;
m_Mod = mod;
m_SpeedBoost = speedBoost;
m_Type = type;
m_StealingMod = stealingMod;
}
}
public class AnimalFormTimer : Timer
{
private readonly Mobile m_Mobile;
private readonly int m_Body;
private readonly int m_Hue;
private int m_Counter;
private Mobile m_LastTarget;
public AnimalFormTimer(Mobile from, int body, int hue)
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
{
m_Mobile = from;
m_Body = body;
m_Hue = hue;
m_Counter = 0;
Priority = TimerPriority.FiftyMS;
}
protected override void OnTick()
{
if (m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Body || m_Mobile.Hue != m_Hue)
{
AnimalForm.RemoveContext(m_Mobile, true);
Stop();
}
else
{
if (m_Body == 0x115) // Cu Sidhe
{
if (m_Counter++ >= 8)
{
if (m_Mobile.Hits < m_Mobile.HitsMax && m_Mobile.Backpack != null)
{
Bandage b = m_Mobile.Backpack.FindItemByType(typeof(Bandage)) as Bandage;
if (b != null)
{
m_Mobile.Hits += Utility.RandomMinMax(20, 50);
b.Consume();
}
}
m_Counter = 0;
}
}
else if (m_Body == 0x114) // Reptalon
{
if (m_Mobile.Combatant is Mobile && m_Mobile.Combatant != m_LastTarget)
{
m_Counter = 1;
m_LastTarget = (Mobile)m_Mobile.Combatant;
}
if (m_Mobile.Warmode && m_LastTarget != null && m_LastTarget.Alive && !m_LastTarget.Deleted && m_Counter-- <= 0)
{
if (m_Mobile.CanBeHarmful(m_LastTarget) && m_LastTarget.Map == m_Mobile.Map &&
m_LastTarget.InRange(m_Mobile.Location, BaseCreature.DefaultRangePerception) && m_Mobile.InLOS(m_LastTarget))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_LastTarget);
m_Mobile.Freeze(TimeSpan.FromSeconds(1));
m_Mobile.PlaySound(0x16A);
DelayCall(TimeSpan.FromSeconds(1.3), BreathEffect_Callback, m_LastTarget);
}
m_Counter = Math.Min((int)m_Mobile.GetDistanceToSqrt(m_LastTarget), 10);
}
}
}
}
public void BreathEffect_Callback(Mobile target)
{
if (m_Mobile.CanBeHarmful(target))
{
m_Mobile.RevealingAction();
m_Mobile.PlaySound(0x227);
Effects.SendMovingEffect(m_Mobile, target, 0x36D4, 5, 0, false, false, 0, 0);
DelayCall(TimeSpan.FromSeconds(1), BreathDamage_Callback, target);
}
}
public void BreathDamage_Callback(Mobile target)
{
if (m_Mobile.CanBeHarmful(target))
{
m_Mobile.RevealingAction();
m_Mobile.DoHarmful(target);
AOS.Damage(target, m_Mobile, 20, !target.Player, 0, 100, 0, 0, 0);
}
}
}
}

View File

@@ -0,0 +1,94 @@
using System;
using Server.SkillHandlers;
namespace Server.Spells.Ninjitsu
{
public class Backstab : NinjaMove
{
public Backstab()
{
}
public override int BaseMana
{
get
{
return 30;
}
}
public override double RequiredSkill
{
get
{
return Core.ML ? 40.0 : 20.0;
}
}
public override TextDefinition AbilityMessage
{
get
{
return new TextDefinition(1063089);
}
}// You prepare to Backstab your opponent.
public override bool ValidatesDuringHit
{
get
{
return false;
}
}
public override double GetDamageScalar(Mobile attacker, Mobile defender)
{
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
return 1.0 + (ninjitsu / 360) + Tracking.GetStalkingBonus(attacker, defender) / 100;
}
public override bool Validate(Mobile from)
{
if (!from.Hidden || from.AllowedStealthSteps <= 0)
{
from.SendLocalizedMessage(1063087); // You must be in stealth mode to use this ability.
return false;
}
return base.Validate(from);
}
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
{
bool valid = this.Validate(attacker) && this.CheckMana(attacker, true);
if (valid)
{
attacker.BeginAction(typeof(Stealth));
Timer.DelayCall(TimeSpan.FromSeconds(5.0), delegate { attacker.EndAction(typeof(Stealth)); });
}
return valid;
}
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();
this.CheckGain(attacker);
}
public override void OnMiss(Mobile attacker, Mobile defender)
{
ClearCurrentMove(attacker);
attacker.SendLocalizedMessage(1063161); // You failed to properly use the element of surprise.
attacker.RevealingAction();
}
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections;
using Server.Items;
using Server.SkillHandlers;
namespace Server.Spells.Ninjitsu
{
public class DeathStrike : NinjaMove
{
private static readonly Hashtable m_Table = new Hashtable();
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 static void AddStep(Mobile m)
{
DeathStrikeInfo info = m_Table[m] as DeathStrikeInfo;
if (info == null)
return;
if (++info.m_Steps >= 5)
ProcessDeathStrike(m);
}
public override double GetDamageScalar(Mobile attacker, Mobile defender)
{
return 0.5;
}
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
return;
ClearCurrentMove(attacker);
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
double chance;
bool isRanged = attacker.Weapon is BaseRanged;
if (ninjitsu < 100) //This formula is an approximation from OSI data. TODO: find correct formula
chance = 30 + (ninjitsu - 85) * 2.2;
else
chance = 63 + (ninjitsu - 100) * 1.1;
if ((chance / 100) < Utility.RandomDouble())
{
attacker.SendLocalizedMessage(1070779); // You missed your opponent with a Death Strike.
return;
}
DeathStrikeInfo info;
int damageBonus = 0;
if (m_Table.Contains(defender))
{
defender.SendLocalizedMessage(1063092); // Your opponent lands another Death Strike!
info = (DeathStrikeInfo)m_Table[defender];
if (info.m_Steps > 0)
damageBonus = attacker.Skills[SkillName.Ninjitsu].Fixed / 150;
if (info.m_Timer != null)
info.m_Timer.Stop();
m_Table.Remove(defender);
}
else
{
defender.SendLocalizedMessage(1063093); // You have been hit by a Death Strike! Move with caution!
}
attacker.SendLocalizedMessage(1063094); // You inflict a Death Strike upon your opponent!
defender.FixedParticles(0x374A, 1, 17, 0x26BC, EffectLayer.Waist);
attacker.PlaySound(attacker.Female ? 0x50D : 0x50E);
info = new DeathStrikeInfo(defender, attacker, damageBonus, isRanged);
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerStateCallback(ProcessDeathStrike), defender);
m_Table[defender] = info;
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.DeathStrike, 1075645, TimeSpan.FromSeconds(5.0), defender, String.Format("{0}", damageBonus)));
this.CheckGain(attacker);
}
private static void ProcessDeathStrike(object state)
{
Mobile defender = (Mobile)state;
DeathStrikeInfo info = m_Table[defender] as DeathStrikeInfo;
if (info == null) //sanity
return;
int maxDamage, damage = 0;
double ninjitsu = info.m_Attacker.Skills[SkillName.Ninjitsu].Value;
double stalkingBonus = Tracking.GetStalkingBonus(info.m_Attacker, info.m_Target);
if (Core.ML)
{
double scalar = (info.m_Attacker.Skills[SkillName.Hiding].Value + info.m_Attacker.Skills[SkillName.Stealth].Value) / 220;
if (scalar > 1)
scalar = 1;
// New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30.
if (info.m_Steps >= 5)
damage = (int)Math.Floor(Math.Min(60, (ninjitsu / 3) * (0.3 + 0.7 * scalar) + stalkingBonus));
else
damage = (int)Math.Floor(Math.Min(30, (ninjitsu / 9) * (0.3 + 0.7 * scalar) + stalkingBonus));
if (info.m_isRanged)
damage /= 2;
}
else
{
int divisor = (info.m_Steps >= 5) ? 30 : 80;
double baseDamage = ninjitsu / divisor * 10;
maxDamage = (info.m_Steps >= 5) ? 62 : 22; // DamageBonus is 8 at most. That brings the cap up to 70/30.
damage = Math.Max(0, Math.Min(maxDamage, (int)(baseDamage + stalkingBonus))) + info.m_DamageBonus;
}
if (Core.ML)
AOS.Damage(info.m_Target, info.m_Attacker, damage, 0, 0, 0, 0, 0, 0, 100); // Damage is direct.
else
AOS.Damage(info.m_Target, info.m_Attacker, damage, true, 100, 0, 0, 0, 0); // Damage is physical.
if (info.m_Timer != null)
info.m_Timer.Stop();
m_Table.Remove(info.m_Target);
}
private class DeathStrikeInfo
{
public readonly Mobile m_Target;
public readonly Mobile m_Attacker;
public readonly int m_DamageBonus;
public readonly bool m_isRanged;
public int m_Steps;
public Timer m_Timer;
public DeathStrikeInfo(Mobile target, Mobile attacker, int damageBonus, bool isRanged)
{
this.m_Target = target;
this.m_Attacker = attacker;
this.m_DamageBonus = damageBonus;
this.m_isRanged = isRanged;
}
}
public static void Initialize()
{
if (Core.SE)
{
EventSink.Movement += new MovementEventHandler(EventSink_Movement);
}
}
public static void EventSink_Movement(MovementEventArgs e)
{
AddStep(e.Mobile);
}
}
}

View File

@@ -0,0 +1,108 @@
using System;
using Server.Items;
namespace Server.Spells.Ninjitsu
{
public class FocusAttack : NinjaMove
{
public FocusAttack()
{
}
public override int BaseMana
{
get
{
return Core.ML ? 10 : 20;
}
}
public override double RequiredSkill
{
get
{
return Core.ML ? 30.0 : 60 ;
}
}
public override TextDefinition AbilityMessage
{
get
{
return new TextDefinition(1063095);
}
}// You prepare to focus all of your abilities into your next strike.
public override bool Validate(Mobile from)
{
if (from.FindItemOnLayer(Layer.TwoHanded) as BaseShield != null)
{
from.SendLocalizedMessage(1063096); // You cannot use this ability while holding a shield.
return false;
}
Item handOne = from.FindItemOnLayer(Layer.OneHanded) as BaseWeapon;
if (handOne != null && !(handOne is BaseRanged))
return base.Validate(from);
Item handTwo = from.FindItemOnLayer(Layer.TwoHanded) as BaseWeapon;
if (handTwo != null && !(handTwo is BaseRanged))
return base.Validate(from);
from.SendLocalizedMessage(1063097); // You must be wielding a melee weapon without a shield to use this ability.
return false;
}
public override double GetDamageScalar(Mobile attacker, Mobile defender)
{
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
return 1.0 + (ninjitsu * ninjitsu) / 43636;
}
public override double GetPropertyBonus(Mobile attacker)
{
double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value;
double bonus = (ninjitsu * ninjitsu) / 43636;
return 1.0 + (bonus * 3 + 0.01);
}
public override bool OnBeforeDamage(Mobile attacker, Mobile defender)
{
return this.Validate(attacker) && this.CheckMana(attacker, true);
}
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
ClearCurrentMove(attacker);
attacker.SendLocalizedMessage(1063098); // You focus all of your abilities and strike with deadly force!
attacker.PlaySound(0x510);
this.CheckGain(attacker);
}
public override void OnUse(Mobile from)
{
BaseWeapon wep = from.Weapon as BaseWeapon;
if (wep != null)
{
wep.FocusWeilder = from;
wep.InvalidateProperties();
}
}
public override void OnClearMove(Mobile from)
{
BaseWeapon wep = from.Weapon as BaseWeapon;
if (wep != null && wep.FocusWeilder != null)
{
wep.FocusWeilder = null;
wep.InvalidateProperties();
}
}
}
}

View File

@@ -0,0 +1,155 @@
using System;
using System.Collections;
using Server.Items;
namespace Server.Spells.Ninjitsu
{
public class KiAttack : NinjaMove
{
private static readonly Hashtable m_Table = new Hashtable();
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 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;
}
public override void OnUse(Mobile from)
{
if (!this.Validate(from))
return;
KiAttackInfo info = new KiAttackInfo(from);
info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(2.0), new TimerStateCallback(EndKiAttack), info);
m_Table[from] = info;
}
public override bool Validate(Mobile from)
{
if (from.Hidden && from.AllowedStealthSteps > 0)
{
from.SendLocalizedMessage(1063127); // You cannot use this ability while in stealth mode.
return false;
}
if (Core.ML)
{
BaseRanged ranged = from.Weapon as BaseRanged;
if (ranged != null)
{
from.SendLocalizedMessage(1075858); // You can only use this with melee attacks.
return false;
}
}
return base.Validate(from);
}
public override double GetDamageScalar(Mobile attacker, Mobile defender)
{
if (attacker.Hidden)
return 1.0;
return 1.0 + GetBonus(attacker) / 10;
}
public override void OnHit(Mobile attacker, Mobile defender, int damage)
{
if (!this.Validate(attacker) || !this.CheckMana(attacker, true))
return;
if (GetBonus(attacker) == 0.0)
{
attacker.SendLocalizedMessage(1063101); // You were too close to your target to cause any additional damage.
}
else
{
attacker.FixedParticles(0x37BE, 1, 5, 0x26BD, 0x0, 0x1, EffectLayer.Waist);
attacker.PlaySound(0x510);
attacker.SendLocalizedMessage(1063100); // Your quick flight to your target causes extra damage as you strike!
defender.FixedParticles(0x37BE, 1, 5, 0x26BD, 0, 0x1, EffectLayer.Waist);
this.CheckGain(attacker);
}
ClearCurrentMove(attacker);
}
public override void OnClearMove(Mobile from)
{
KiAttackInfo info = m_Table[from] as KiAttackInfo;
if (info != null)
{
if (info.m_Timer != null)
info.m_Timer.Stop();
m_Table.Remove(info.m_Mobile);
}
}
private static 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);
}
private class KiAttackInfo
{
public readonly Mobile m_Mobile;
public readonly Point3D m_Location;
public Timer m_Timer;
public KiAttackInfo(Mobile m)
{
this.m_Mobile = m;
this.m_Location = m.Location;
}
}
}
}

View File

@@ -0,0 +1,352 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
using Server.Spells;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
namespace Server.Spells.Ninjitsu
{
public class MirrorImage : NinjaSpell
{
private static readonly Dictionary<Mobile, int> m_CloneCount = new Dictionary<Mobile, int>();
private static readonly SpellInfo m_Info = new SpellInfo(
"Mirror Image", null,
-1,
9002);
public MirrorImage(Mobile caster, Item scroll)
: base(caster, scroll, m_Info)
{
}
public override TimeSpan CastDelayBase
{
get
{
return TimeSpan.FromSeconds(1.5);
}
}
public override double RequiredSkill
{
get
{
return Core.ML ? 20.0 : 40.0;
}
}
public override int RequiredMana
{
get
{
return 10;
}
}
public override bool BlockedByAnimalForm
{
get
{
return false;
}
}
public static bool HasClone(Mobile m)
{
return m_CloneCount.ContainsKey(m);
}
public static void AddClone(Mobile m)
{
if (m == null)
return;
if (m_CloneCount.ContainsKey(m))
m_CloneCount[m]++;
else
m_CloneCount[m] = 1;
}
public static void RemoveClone(Mobile m)
{
if (m == null)
return;
if (m_CloneCount.ContainsKey(m))
{
m_CloneCount[m]--;
if (m_CloneCount[m] == 0)
m_CloneCount.Remove(m);
}
}
public override bool CheckCast()
{
if (Caster.Mounted)
{
Caster.SendLocalizedMessage(1063132); // You cannot use this ability while mounted.
return false;
}
else if ((Caster.Followers + 1) > Caster.FollowersMax)
{
Caster.SendLocalizedMessage(1063133); // You cannot summon a mirror image because you have too many followers.
return false;
}
else if (TransformationSpellHelper.UnderTransformation(Caster, typeof(HorrificBeastSpell)))
{
Caster.SendLocalizedMessage(1061091); // You cannot cast that spell in this form.
return false;
}
else if (Caster.Flying)
{
Caster.SendLocalizedMessage(1113415); // You cannot use this ability while flying.
return false;
}
return base.CheckCast();
}
public override bool CheckDisturb(DisturbType type, bool firstCircle, bool resistable)
{
return false;
}
public override void OnBeginCast()
{
base.OnBeginCast();
Caster.SendLocalizedMessage(1063134); // You begin to summon a mirror image of yourself.
}
public override void OnCast()
{
if (Caster.Mounted)
{
Caster.SendLocalizedMessage(1063132); // You cannot use this ability while mounted.
}
else if ((Caster.Followers + 1) > Caster.FollowersMax)
{
Caster.SendLocalizedMessage(1063133); // You cannot summon a mirror image because you have too many followers.
}
else if (TransformationSpellHelper.UnderTransformation(Caster, typeof(HorrificBeastSpell)))
{
Caster.SendLocalizedMessage(1061091); // You cannot cast that spell in this form.
}
else if (CheckSequence())
{
Caster.FixedParticles(0x376A, 1, 14, 0x13B5, EffectLayer.Waist);
Caster.PlaySound(0x511);
new Clone(Caster).MoveToWorld(Caster.Location, Caster.Map);
}
FinishSequence();
}
public static Clone GetDeflect(Mobile attacker, Mobile defender)
{
Clone clone = null;
if (HasClone(defender) && (defender.Skills.Ninjitsu.Value / 150.0) > Utility.RandomDouble())
{
IPooledEnumerable eable = defender.GetMobilesInRange(4);
foreach (Mobile m in eable)
{
clone = m as Clone;
if (clone != null && clone.Summoned && clone.SummonMaster == defender)
{
attacker.SendLocalizedMessage(1063141); // Your attack has been diverted to a nearby mirror image of your target!
defender.SendLocalizedMessage(1063140); // You manage to divert the attack onto one of your nearby mirror images.
break;
}
}
eable.Free();
}
return clone;
}
}
}
namespace Server.Mobiles
{
public class Clone : BaseCreature
{
private Mobile m_Caster;
public Clone(Mobile caster)
: base(AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4)
{
m_Caster = caster;
Body = caster.Body;
Hue = caster.Hue;
Female = caster.Female;
Name = caster.Name;
NameHue = caster.NameHue;
Title = caster.Title;
Kills = caster.Kills;
HairItemID = caster.HairItemID;
HairHue = caster.HairHue;
FacialHairItemID = caster.FacialHairItemID;
FacialHairHue = caster.FacialHairHue;
for (int i = 0; i < caster.Skills.Length; ++i)
{
Skills[i].Base = caster.Skills[i].Base;
Skills[i].Cap = caster.Skills[i].Cap;
}
for (int i = 0; i < caster.Items.Count; i++)
{
AddItem(CloneItem(caster.Items[i]));
}
Warmode = true;
Summoned = true;
SummonMaster = caster;
ControlOrder = OrderType.Follow;
ControlTarget = caster;
TimeSpan duration = TimeSpan.FromSeconds(30 + caster.Skills.Ninjitsu.Fixed / 40);
new UnsummonTimer(caster, this, duration).Start();
SummonEnd = DateTime.UtcNow + duration;
MirrorImage.AddClone(m_Caster);
IgnoreMobiles = true;
}
public Clone(Serial serial)
: base(serial)
{
}
public override bool DeleteCorpseOnDeath
{
get
{
return true;
}
}
public override bool IsDispellable
{
get
{
return false;
}
}
public override bool Commandable
{
get
{
return false;
}
}
protected override BaseAI ForcedAI
{
get
{
return new CloneAI(this);
}
}
public override bool CanDetectHidden { get { return false; } }
public override bool IsHumanInTown()
{
return false;
}
public override bool OnMoveOver(Mobile m)
{
return true;
}
public override void OnDamage(int amount, Mobile from, bool willKill)
{
Delete();
}
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 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);
}
private Item CloneItem(Item item)
{
Item newItem = new Item(item.ItemID);
newItem.Hue = item.Hue;
newItem.Layer = item.Layer;
return newItem;
}
}
}
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;
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace Server.Spells
{
public class NinjaMove : SpecialMove
{
public override SkillName MoveSkill
{
get
{
return SkillName.Ninjitsu;
}
}
public override void CheckGain(Mobile m)
{
m.CheckSkill(this.MoveSkill, this.RequiredSkill - 12.5, this.RequiredSkill + 37.5); //Per five on friday 02/16/07
}
}
}

View File

@@ -0,0 +1,138 @@
using System;
using Server.Mobiles;
namespace Server.Spells.Ninjitsu
{
public abstract class NinjaSpell : Spell
{
public NinjaSpell(Mobile caster, Item scroll, SpellInfo info)
: base(caster, scroll, info)
{
}
public abstract double RequiredSkill { get; }
public abstract int RequiredMana { get; }
public override SkillName CastSkill
{
get
{
return SkillName.Ninjitsu;
}
}
public override SkillName DamageSkill
{
get
{
return SkillName.Ninjitsu;
}
}
public override bool RevealOnCast
{
get
{
return false;
}
}
public override bool ClearHandsOnCast
{
get
{
return false;
}
}
public override bool ShowHandMovement
{
get
{
return false;
}
}
public override bool BlocksMovement
{
get
{
return false;
}
}
//public override int CastDelayBase{ get{ return 1; } }
public override int CastRecoveryBase
{
get
{
return 7;
}
}
public static bool CheckExpansion(Mobile from)
{
if (!(from is PlayerMobile))
return true;
if (from.NetState == null)
return false;
return from.NetState.SupportsExpansion(Expansion.SE);
}
public override bool CheckCast()
{
int mana = this.ScaleMana(this.RequiredMana);
if (!base.CheckCast())
return false;
if (!CheckExpansion(this.Caster))
{
this.Caster.SendLocalizedMessage(1063456); // You must upgrade to Samurai Empire in order to use that ability.
return false;
}
if (this.Caster.Skills[this.CastSkill].Value < this.RequiredSkill)
{
string args = String.Format("{0}\t{1}\t ", this.RequiredSkill.ToString("F1"), this.CastSkill.ToString());
this.Caster.SendLocalizedMessage(1063013, args); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability.
return false;
}
else if (this.Caster.Mana < mana)
{
this.Caster.SendLocalizedMessage(1060174, mana.ToString()); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
return false;
}
return true;
}
public override bool CheckFizzle()
{
int mana = this.ScaleMana(this.RequiredMana);
if (this.Caster.Skills[this.CastSkill].Value < this.RequiredSkill)
{
this.Caster.SendLocalizedMessage(1063352, this.RequiredSkill.ToString("F1")); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack!
return false;
}
else if (this.Caster.Mana < mana)
{
this.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;
this.Caster.Mana -= mana;
return true;
}
public override void GetCastSkills(out double min, out double max)
{
min = this.RequiredSkill - 12.5; //Per 5 on friday 2/16/07
max = this.RequiredSkill + 37.5;
}
public override int GetMana()
{
return 0;
}
}
}

View File

@@ -0,0 +1,152 @@
using System;
using Server.Items;
using Server.Mobiles;
using Server.Regions;
using Server.Targeting;
namespace Server.Spells.Ninjitsu
{
public class Shadowjump : NinjaSpell
{
private static readonly SpellInfo m_Info = new SpellInfo(
"Shadowjump", null,
-1,
9002);
public Shadowjump(Mobile caster, Item scroll)
: base(caster, scroll, m_Info)
{
}
public override TimeSpan CastDelayBase
{
get
{
return TimeSpan.FromSeconds(1.0);
}
}
public override double RequiredSkill
{
get
{
return 50.0;
}
}
public override int RequiredMana
{
get
{
return 15;
}
}
public override bool BlockedByAnimalForm
{
get
{
return false;
}
}
public override bool CheckCast()
{
PlayerMobile pm = this.Caster as PlayerMobile; // IsStealthing should be moved to Server.Mobiles
if (!pm.IsStealthing)
{
this.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()
{
this.Caster.SendLocalizedMessage(1063088); // You prepare to perform a Shadowjump.
this.Caster.Target = new InternalTarget(this);
}
public void Target(IPoint3D p)
{
IPoint3D orig = p;
Map map = this.Caster.Map;
SpellHelper.GetSurfaceTop(ref p);
Point3D from = this.Caster.Location;
Point3D to = new Point3D(p);
PlayerMobile pm = this.Caster as PlayerMobile; // IsStealthing should be moved to Server.Mobiles
if (!pm.IsStealthing)
{
this.Caster.SendLocalizedMessage(1063087); // You must be in stealth mode to use this ability.
}
else if (Factions.Sigil.ExistsOn(this.Caster))
{
this.Caster.SendLocalizedMessage(1061632); // You can't do that while carrying the sigil.
}
else if (Server.Misc.WeightOverloading.IsOverloaded(this.Caster))
{
this.Caster.SendLocalizedMessage(502359, "", 0x22); // Thou art too encumbered to move.
}
else if (!SpellHelper.CheckTravel(this.Caster, TravelCheckType.TeleportFrom) || !SpellHelper.CheckTravel(this.Caster, map, to, TravelCheckType.TeleportTo))
{
}
else if (map == null || !map.CanSpawnMobile(p.X, p.Y, p.Z))
{
this.Caster.SendLocalizedMessage(502831); // Cannot teleport to that spot.
}
else if (SpellHelper.CheckMulti(to, map, true, 5))
{
this.Caster.SendLocalizedMessage(502831); // Cannot teleport to that spot.
}
else if (Region.Find(to, map).GetRegion(typeof(HouseRegion)) != null)
{
this.Caster.SendLocalizedMessage(502829); // Cannot teleport to that spot.
}
else if (this.CheckSequence())
{
SpellHelper.Turn(this.Caster, orig);
Mobile m = this.Caster;
m.Location = to;
m.ProcessDelta();
Effects.SendLocationParticles(EffectItem.Create(from, m.Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 2023);
m.PlaySound(0x512);
Server.SkillHandlers.Stealth.OnUse(m); // stealth check after the a jump
}
this.FinishSequence();
}
public class InternalTarget : Target
{
private readonly Shadowjump m_Owner;
public InternalTarget(Shadowjump owner)
: base(11, true, TargetFlags.None)
{
this.m_Owner = owner;
}
protected override void OnTarget(Mobile from, object o)
{
IPoint3D p = o as IPoint3D;
if (p != null)
this.m_Owner.Target(p);
}
protected override void OnTargetFinish(Mobile from)
{
this.m_Owner.FinishSequence();
}
}
}
}

View File

@@ -0,0 +1,146 @@
using System;
using System.Collections;
using Server.SkillHandlers;
namespace Server.Spells.Ninjitsu
{
public class SurpriseAttack : NinjaMove
{
private static readonly Hashtable m_Table = new Hashtable();
public SurpriseAttack()
{
}
public override int BaseMana
{
get
{
return 20;
}
}
public override double RequiredSkill
{
get
{
return Core.ML ? 60.0 : 30.0;
}
}
public override TextDefinition AbilityMessage
{
get
{
return new TextDefinition(1063128);
}
}// You prepare to surprise your prey.
public override bool ValidatesDuringHit
{
get
{
return false;
}
}
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;
}
public override bool Validate(Mobile from)
{
if (!from.Hidden || from.AllowedStealthSteps <= 0)
{
from.SendLocalizedMessage(1063087); // You must be in stealth mode to use this ability.
return false;
}
return base.Validate(from);
}
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
{
bool valid = this.Validate(attacker) && this.CheckMana(attacker, true);
if (valid)
{
attacker.BeginAction(typeof(Stealth));
Timer.DelayCall(TimeSpan.FromSeconds(5.0), delegate { attacker.EndAction(typeof(Stealth)); });
}
return valid;
}
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;
this.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 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);
}
private class SurpriseAttackInfo
{
public readonly Mobile m_Target;
public readonly int m_Malus;
public Timer m_Timer;
public SurpriseAttackInfo(Mobile target, int effect)
{
this.m_Target = target;
this.m_Malus = effect;
}
}
}
}