Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
197
Scripts/Spells/Spellweaving/ArcaneCircle.cs
Normal file
197
Scripts/Spells/Spellweaving/ArcaneCircle.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells.SkillMasteries;
|
||||
using System.Linq;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class ArcaneCircleSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Arcane Circle", "Myrshalee",
|
||||
-1);
|
||||
public ArcaneCircleSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
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)));
|
||||
|
||||
duration += TimeSpan.FromHours(Math.Min(6, Arcanists.Count));
|
||||
|
||||
int strengthBonus = Math.Min(IsBonus(Caster.Location, Caster.Map) ? 6 : 5, Arcanists.Sum(m => GetStrength(m))); // Math.Min(Arcanists.Count, IsBonus(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 IsBonus(Point3D p, Map m)
|
||||
{
|
||||
return (m == Map.Trammel || m == Map.Felucca) &&
|
||||
(p.X == 6267 && p.Y == 131) ||
|
||||
(p.X == 6589 && p.Y == 178) ||
|
||||
(p.X == 1431 && p.Y == 1696); // new brit bank
|
||||
}
|
||||
|
||||
private static int GetStrength(Mobile m)
|
||||
{
|
||||
return m.Skills.CurrentMastery == SkillName.Spellweaving ? MasteryInfo.GetMasteryLevel(m, SkillName.Spellweaving) : 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private List<Mobile> GetArcanists()
|
||||
{
|
||||
List<Mobile> weavers = new List<Mobile>();
|
||||
|
||||
weavers.Add(Caster);
|
||||
|
||||
//OSI Verified: Even enemies/combatants count
|
||||
IPooledEnumerable eable = Caster.GetMobilesInRange(1);
|
||||
foreach (Mobile m in eable) //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
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
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))
|
||||
{
|
||||
to.AddStatMod(new StatMod(StatType.Str, "[ArcaneFocus]", strengthBonus, duration));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
Scripts/Spells/Spellweaving/ArcaneEmpowerment.cs
Normal file
141
Scripts/Spells/Spellweaving/ArcaneEmpowerment.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class ArcaneEmpowermentSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Arcane Empowerment", "Aslavdra",
|
||||
-1);
|
||||
private static readonly Hashtable m_Table = new Hashtable();
|
||||
public ArcaneEmpowermentSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(3);
|
||||
}
|
||||
}
|
||||
public override double RequiredSkill
|
||||
{
|
||||
get
|
||||
{
|
||||
return 24.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
|
||||
public static double GetDispellBonus(Mobile m)
|
||||
{
|
||||
EmpowermentInfo info = m_Table[m] as EmpowermentInfo;
|
||||
|
||||
if (info != null)
|
||||
return 10.0 * info.Focus;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static int GetSpellBonus(Mobile m, bool playerVsPlayer)
|
||||
{
|
||||
EmpowermentInfo info = m_Table[m] as EmpowermentInfo;
|
||||
|
||||
if (info != null)
|
||||
return info.Bonus + (playerVsPlayer ? info.Focus : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void AddHealBonus(Mobile m, ref int toHeal)
|
||||
{
|
||||
EmpowermentInfo info = m_Table[m] as EmpowermentInfo;
|
||||
|
||||
if (info != null)
|
||||
toHeal = (int)Math.Floor((1 + (10 + info.Bonus) / 100.0) * toHeal);
|
||||
}
|
||||
|
||||
public static void RemoveBonus(Mobile m)
|
||||
{
|
||||
EmpowermentInfo info = m_Table[m] as EmpowermentInfo;
|
||||
|
||||
if (info != null && info.Timer != null)
|
||||
info.Timer.Stop();
|
||||
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
public static bool IsUnderEffects(Mobile m)
|
||||
{
|
||||
return m_Table.ContainsKey(m);
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if (m_Table.ContainsKey(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(501775); // This spell is already in effect.
|
||||
}
|
||||
else if (CheckSequence())
|
||||
{
|
||||
Caster.PlaySound(0x5C1);
|
||||
|
||||
int level = GetFocusLevel(Caster);
|
||||
double skill = Caster.Skills[SkillName.Spellweaving].Value;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds(15 + (int)(skill / 24) + level * 2);
|
||||
int bonus = (int)Math.Floor(skill / 12) + level * 5;
|
||||
|
||||
m_Table[Caster] = new EmpowermentInfo(Caster, duration, bonus, level);
|
||||
|
||||
BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.ArcaneEmpowerment, 1031616, 1075808, duration, Caster, new TextDefinition(String.Format("{0}\t10", bonus.ToString()))));
|
||||
|
||||
Caster.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class EmpowermentInfo
|
||||
{
|
||||
public readonly int Bonus;
|
||||
public readonly int Focus;
|
||||
public readonly ExpireTimer Timer;
|
||||
|
||||
public EmpowermentInfo(Mobile caster, TimeSpan duration, int bonus, int focus)
|
||||
{
|
||||
Bonus = bonus;
|
||||
Focus = focus;
|
||||
|
||||
Timer = new ExpireTimer(caster, duration);
|
||||
Timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
public ExpireTimer(Mobile m, TimeSpan delay)
|
||||
: base(delay)
|
||||
{
|
||||
m_Mobile = m;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
m_Mobile.PlaySound(0x5C2);
|
||||
m_Table.Remove(m_Mobile);
|
||||
|
||||
m_Mobile.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Scripts/Spells/Spellweaving/ArcaneForm.cs
Normal file
89
Scripts/Spells/Spellweaving/ArcaneForm.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public abstract class ArcaneForm : ArcanistSpell, ITransformationSpell
|
||||
{
|
||||
public ArcaneForm(Mobile caster, Item scroll, SpellInfo info)
|
||||
: base(caster, scroll, info)
|
||||
{
|
||||
}
|
||||
|
||||
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 virtual double TickRate
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if (!TransformationSpellHelper.CheckCast(this.Caster, this))
|
||||
return false;
|
||||
|
||||
return base.CheckCast();
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
TransformationSpellHelper.OnCast(this.Caster, this);
|
||||
|
||||
this.FinishSequence();
|
||||
}
|
||||
|
||||
public virtual void OnTick(Mobile m)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void DoEffect(Mobile m)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RemoveEffect(Mobile m)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Scripts/Spells/Spellweaving/ArcaneSummon.cs
Normal file
55
Scripts/Spells/Spellweaving/ArcaneSummon.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public abstract class ArcaneSummon<T> : ArcanistSpell where T : BaseCreature
|
||||
{
|
||||
public ArcaneSummon(Mobile caster, Item scroll, SpellInfo info)
|
||||
: base(caster, scroll, info)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract int Sound { get; }
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if (!base.CheckCast())
|
||||
return false;
|
||||
|
||||
if ((this.Caster.Followers + 1) > this.Caster.FollowersMax)
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(1074270); // You have too many followers to summon another one.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if (this.CheckSequence())
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromMinutes(this.Caster.Skills.Spellweaving.Value / 24 + this.FocusLevel * 2);
|
||||
int summons = Math.Min(1 + this.FocusLevel, this.Caster.FollowersMax - this.Caster.Followers);
|
||||
|
||||
for (int i = 0; i < summons; i++)
|
||||
{
|
||||
BaseCreature bc;
|
||||
|
||||
try
|
||||
{
|
||||
bc = Activator.CreateInstance<T>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
SpellHelper.Summon(bc, this.Caster, this.Sound, duration, false, false);
|
||||
}
|
||||
|
||||
this.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
197
Scripts/Spells/Spellweaving/ArcanistSpell.cs
Normal file
197
Scripts/Spells/Spellweaving/ArcanistSpell.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells.SkillMasteries;
|
||||
#endregion
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public abstract class ArcanistSpell : Spell
|
||||
{
|
||||
private int m_CastTimeFocusLevel;
|
||||
|
||||
public ArcanistSpell(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.Spellweaving; } }
|
||||
public override SkillName DamageSkill { get { return SkillName.Spellweaving; } }
|
||||
public override bool ClearHandsOnCast { get { return false; } }
|
||||
public virtual int FocusLevel { get { return m_CastTimeFocusLevel; } }
|
||||
|
||||
public static int GetFocusLevel(Mobile from)
|
||||
{
|
||||
ArcaneFocus focus = FindArcaneFocus(from);
|
||||
|
||||
if (focus == null || focus.Deleted)
|
||||
{
|
||||
if (Core.TOL && from is BaseCreature && from.Skills[SkillName.Spellweaving].Value > 0)
|
||||
{
|
||||
return (int)Math.Max(1, Math.Min(6, from.Skills[SkillName.Spellweaving].Value / 20));
|
||||
}
|
||||
|
||||
return Math.Max(GetMasteryFocusLevel(from), 0);
|
||||
}
|
||||
|
||||
return Math.Max(GetMasteryFocusLevel(from), focus.StrengthBonus);
|
||||
}
|
||||
|
||||
public static int GetMasteryFocusLevel(Mobile from)
|
||||
{
|
||||
if (!Core.TOL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (from.Skills.CurrentMastery == SkillName.Spellweaving)
|
||||
{
|
||||
return Math.Max(1, MasteryInfo.GetMasteryLevel(from, SkillName.Spellweaving));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!CheckExpansion(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1072176);
|
||||
// You must upgrade to the Mondain's Legacy Expansion Pack before using that ability
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MondainsLegacy.Spellweaving)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1042753, "Spellweaving"); // ~1_SOMETHING~ has been temporarily disabled.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Caster is PlayerMobile && !((PlayerMobile)Caster).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(CultureInfo.InvariantCulture));
|
||||
// You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Caster.Skills[CastSkill].Value < RequiredSkill)
|
||||
{
|
||||
Caster.SendLocalizedMessage(1063013, String.Format("{0}\t{1}", 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();
|
||||
|
||||
m_CastTimeFocusLevel = GetFocusLevel(Caster);
|
||||
}
|
||||
|
||||
public override void SendCastEffect()
|
||||
{
|
||||
if(Caster.Player)
|
||||
Caster.FixedEffect(0x37C4, 87, (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());
|
||||
}
|
||||
}
|
||||
}
|
||||
164
Scripts/Spells/Spellweaving/AttuneWeapon.cs
Normal file
164
Scripts/Spells/Spellweaving/AttuneWeapon.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server.Services.Virtues;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class AttuneWeaponSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Attune Weapon", "Haeldril",
|
||||
-1);
|
||||
|
||||
private static readonly Dictionary<Mobile, ExpireTimer> m_Table = new Dictionary<Mobile, ExpireTimer>();
|
||||
|
||||
public AttuneWeaponSpell(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 0.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
else if(m_Table.ContainsKey(defender))
|
||||
{
|
||||
BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.AttuneWeapon, 1075798, m_Table[defender].Expires - DateTime.UtcNow, defender, defender.MeleeDamageAbsorb.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else if (SpiritualityVirtue.IsEmbracee(Caster))
|
||||
{
|
||||
Caster.SendLocalizedMessage(1156040); // You may not cast Attunement whilst a Spirituality Shield is active!
|
||||
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();
|
||||
}
|
||||
|
||||
public class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
public ExpireTimer(Mobile m, TimeSpan delay)
|
||||
: base(delay)
|
||||
{
|
||||
m_Mobile = m;
|
||||
Expires = DateTime.UtcNow + delay;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
DoExpire(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Scripts/Spells/Spellweaving/DryadAllure.cs
Normal file
155
Scripts/Spells/Spellweaving/DryadAllure.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class DryadAllureSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Dryad Allure", "Rathril",
|
||||
-1);
|
||||
public DryadAllureSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(3);
|
||||
}
|
||||
}
|
||||
public override double RequiredSkill
|
||||
{
|
||||
get
|
||||
{
|
||||
return 52.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 40;
|
||||
}
|
||||
}
|
||||
public static bool IsValidTarget(BaseCreature bc)
|
||||
{
|
||||
if (bc == null || bc.IsParagon || (bc.Controlled && !bc.Allured) || bc.Summoned || bc.AllureImmune)
|
||||
return false;
|
||||
|
||||
SlayerEntry slayer = SlayerGroup.GetEntryByName(SlayerName.Repond);
|
||||
|
||||
if (slayer != null && slayer.Slays(bc))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
this.Caster.Target = new InternalTarget(this);
|
||||
}
|
||||
|
||||
public void Target(BaseCreature bc)
|
||||
{
|
||||
if (!this.Caster.CanSee(bc.Location) || !this.Caster.InLOS(bc))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(500237); // Target can not be seen.
|
||||
}
|
||||
else if (!IsValidTarget(bc))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(1074379); // You cannot charm that!
|
||||
}
|
||||
else if (this.Caster.Followers + 3 > this.Caster.FollowersMax)
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(1049607); // You have too many followers to control that creature.
|
||||
}
|
||||
else if (bc.Allured)
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(1074380); // This humanoid is already controlled by someone else.
|
||||
}
|
||||
else if (this.CheckSequence())
|
||||
{
|
||||
int level = GetFocusLevel(this.Caster);
|
||||
double skill = this.Caster.Skills[this.CastSkill].Value;
|
||||
|
||||
double chance = (skill / 150.0) + (level / 50.0);
|
||||
|
||||
if (chance > Utility.RandomDouble())
|
||||
{
|
||||
bc.ControlSlots = 3;
|
||||
bc.Combatant = null;
|
||||
|
||||
if (this.Caster.Combatant == bc)
|
||||
{
|
||||
this.Caster.Combatant = null;
|
||||
this.Caster.Warmode = false;
|
||||
}
|
||||
|
||||
if (bc.SetControlMaster(this.Caster))
|
||||
{
|
||||
bc.PlaySound(0x5C4);
|
||||
bc.Allured = true;
|
||||
|
||||
Container pack = bc.Backpack;
|
||||
|
||||
if (pack != null)
|
||||
{
|
||||
for (int i = pack.Items.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (i >= pack.Items.Count)
|
||||
continue;
|
||||
|
||||
pack.Items[i].Delete();
|
||||
}
|
||||
}
|
||||
|
||||
this.Caster.SendLocalizedMessage(1074377); // You allure the humanoid to follow and protect you.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bc.PlaySound(0x5C5);
|
||||
bc.ControlTarget = this.Caster;
|
||||
bc.ControlOrder = OrderType.Attack;
|
||||
bc.Combatant = this.Caster;
|
||||
|
||||
this.Caster.SendLocalizedMessage(1074378); // The humanoid becomes enraged by your charming attempt and attacks you.
|
||||
}
|
||||
}
|
||||
|
||||
this.FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private readonly DryadAllureSpell m_Owner;
|
||||
public InternalTarget(DryadAllureSpell owner)
|
||||
: base(12, false, TargetFlags.None)
|
||||
{
|
||||
this.m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile m, object o)
|
||||
{
|
||||
if (o is BaseCreature)
|
||||
{
|
||||
this.m_Owner.Target((BaseCreature)o);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.SendLocalizedMessage(1074379); // You cannot charm that!
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile m)
|
||||
{
|
||||
this.m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
181
Scripts/Spells/Spellweaving/EssenceOfWind.cs
Normal file
181
Scripts/Spells/Spellweaving/EssenceOfWind.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class EssenceOfWindSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo("Essence of Wind", "Anathrae", -1);
|
||||
private static readonly Dictionary<Mobile, EssenceOfWindInfo> m_Table = new Dictionary<Mobile, EssenceOfWindInfo>();
|
||||
|
||||
public override DamageType SpellDamageType { get { return DamageType.SpellAOE; } }
|
||||
|
||||
public EssenceOfWindSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
if (CheckSequence())
|
||||
{
|
||||
Caster.PlaySound(0x5C6);
|
||||
|
||||
int damage = 10 + FocusLevel;
|
||||
|
||||
double skill = Caster.Skills[SkillName.Spellweaving].Value;
|
||||
int dmgBonus = Math.Max((int)(skill / 24.0d), 1);
|
||||
damage += dmgBonus;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds((int)(skill / 24) + FocusLevel);
|
||||
|
||||
int fcMalus = FocusLevel + 1;
|
||||
int ssiMalus = 2 * (FocusLevel + 1);
|
||||
|
||||
foreach (var m in AcquireIndirectTargets(Caster.Location, 5 + FocusLevel).OfType<Mobile>())
|
||||
{
|
||||
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())));
|
||||
|
||||
m.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class EssenceOfWindInfo
|
||||
{
|
||||
private readonly Mobile m_Defender;
|
||||
private readonly int m_FCMalus;
|
||||
private readonly int m_SSIMalus;
|
||||
private readonly ExpireTimer 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
public ExpireTimer(Mobile m, TimeSpan delay)
|
||||
: base(delay)
|
||||
{
|
||||
m_Mobile = m;
|
||||
}
|
||||
|
||||
public void DoExpire(bool message)
|
||||
{
|
||||
Stop();
|
||||
|
||||
m_Table.Remove(m_Mobile);
|
||||
|
||||
BuffInfo.RemoveBuff(m_Mobile, BuffIcon.EssenceOfWind);
|
||||
m_Mobile.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
DoExpire(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Scripts/Spells/Spellweaving/EtherealVoyage.cs
Normal file
113
Scripts/Spells/Spellweaving/EtherealVoyage.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class EtherealVoyageSpell : ArcaneForm
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Ethereal Voyage", "Orlavdra",
|
||||
-1);
|
||||
public EtherealVoyageSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
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 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(this.Caster, typeof(EtherealVoyageSpell)))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(501775); // This spell is already in effect.
|
||||
}
|
||||
else if (!this.Caster.CanBeginAction(typeof(EtherealVoyageSpell)))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(1075124); // You must wait before casting that spell again.
|
||||
}
|
||||
else if (this.Caster.Combatant != null)
|
||||
{
|
||||
this.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 = this.Caster.Skills.Spellweaving.Value;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds(12 + (int)(skill / 24) + (this.FocusLevel * 2));
|
||||
|
||||
Timer.DelayCall<Mobile>(duration, new TimerStateCallback<Mobile>(RemoveEffect), this.Caster);
|
||||
|
||||
this.Caster.BeginAction(typeof(EtherealVoyageSpell)); //Cannot cast this spell for another 5 minutes(300sec) after effect removed.
|
||||
|
||||
BuffInfo.AddBuff(this.Caster, new BuffInfo(BuffIcon.EtherealVoyage, 1031613, 1075805, duration, this.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
261
Scripts/Spells/Spellweaving/GiftOfLife.cs
Normal file
261
Scripts/Spells/Spellweaving/GiftOfLife.cs
Normal file
@@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Gumps;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class GiftOfLifeSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Gift of Life", "Illorae",
|
||||
-1);
|
||||
|
||||
private static readonly Dictionary<Mobile, ExpireTimer> m_Table = new Dictionary<Mobile, ExpireTimer>();
|
||||
|
||||
public GiftOfLifeSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
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 double HitsScalar
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((Caster.Skills.Spellweaving.Value / 2.4) + FocusLevel) / 100;
|
||||
}
|
||||
}
|
||||
public static void Initialize()
|
||||
{
|
||||
EventSink.PlayerDeath += HandleDeath;
|
||||
EventSink.Login += Login;
|
||||
}
|
||||
|
||||
public static void HandleDeath(PlayerDeathEventArgs e)
|
||||
{
|
||||
HandleDeath(e.Mobile);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void Login(LoginEventArgs e)
|
||||
{
|
||||
Mobile m = e.Mobile;
|
||||
|
||||
if (m_Table.ContainsKey(m))
|
||||
{
|
||||
var timer = m_Table[m];
|
||||
|
||||
if (timer.EndTime > DateTime.UtcNow)
|
||||
{
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.GiftOfLife, 1031615, 1075807, timer.EndTime - DateTime.UtcNow, m, null, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnLogin(LoginEventArgs e)
|
||||
{
|
||||
Mobile m = e.Mobile;
|
||||
|
||||
if (m == null || m.Alive || m_Table[m] == null)
|
||||
return;
|
||||
|
||||
HandleDeath_OnCallback(m);
|
||||
}
|
||||
|
||||
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 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 class InternalTarget : Target
|
||||
{
|
||||
private readonly 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();
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpireTimer : Timer
|
||||
{
|
||||
private readonly Mobile m_Mobile;
|
||||
private readonly GiftOfLifeSpell m_Spell;
|
||||
|
||||
public DateTime EndTime { get; private set; }
|
||||
|
||||
public ExpireTimer(Mobile m, TimeSpan delay, GiftOfLifeSpell spell)
|
||||
: base(delay)
|
||||
{
|
||||
m_Mobile = m;
|
||||
m_Spell = spell;
|
||||
|
||||
EndTime = DateTime.UtcNow + delay;
|
||||
}
|
||||
|
||||
public GiftOfLifeSpell Spell
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Spell;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
DoExpire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
207
Scripts/Spells/Spellweaving/GiftOfRenewal.cs
Normal file
207
Scripts/Spells/Spellweaving/GiftOfRenewal.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class GiftOfRenewalSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly 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()
|
||||
{
|
||||
this.Caster.Target = new InternalTarget(this);
|
||||
}
|
||||
|
||||
public void Target(Mobile m)
|
||||
{
|
||||
if (!this.Caster.CanSee(m))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(500237); // Target can not be seen.
|
||||
}
|
||||
if (m_Table.ContainsKey(m))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(501775); // This spell is already in effect.
|
||||
}
|
||||
else if (!this.Caster.CanBeginAction(typeof(GiftOfRenewalSpell)))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(501789); // You must wait before trying again.
|
||||
}
|
||||
else if (this.CheckBSequence(m))
|
||||
{
|
||||
SpellHelper.Turn(this.Caster, m);
|
||||
|
||||
this.Caster.FixedEffect(0x374A, 10, 20);
|
||||
this.Caster.PlaySound(0x5C9);
|
||||
|
||||
if (m.Poisoned)
|
||||
{
|
||||
m.CurePoison(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
double skill = this.Caster.Skills[SkillName.Spellweaving].Value;
|
||||
|
||||
int hitsPerRound = 5 + (int)(skill / 24) + this.FocusLevel;
|
||||
TimeSpan duration = TimeSpan.FromSeconds(30 + (this.FocusLevel * 10));
|
||||
|
||||
GiftOfRenewalInfo info = new GiftOfRenewalInfo(this.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;
|
||||
|
||||
this.Caster.BeginAction(typeof(GiftOfRenewalSpell));
|
||||
|
||||
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.GiftOfRenewal, 1031602, 1075797, duration, m, hitsPerRound.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
this.FinishSequence();
|
||||
}
|
||||
|
||||
public static Dictionary<Mobile, GiftOfRenewalInfo> m_Table = new Dictionary<Mobile, GiftOfRenewalInfo>();
|
||||
|
||||
public 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)
|
||||
{
|
||||
this.m_Caster = caster;
|
||||
this.m_Mobile = mobile;
|
||||
this.m_HitsPerRound = hitsPerRound;
|
||||
|
||||
this.m_Timer = new InternalTimer(this);
|
||||
this.m_Timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public class InternalTimer : Timer
|
||||
{
|
||||
public GiftOfRenewalInfo m_Info;
|
||||
|
||||
public InternalTimer(GiftOfRenewalInfo info)
|
||||
: base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0))
|
||||
{
|
||||
this.m_Info = info;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
Mobile m = this.m_Info.m_Mobile;
|
||||
|
||||
if (!m_Table.ContainsKey(m))
|
||||
{
|
||||
this.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m.Alive)
|
||||
{
|
||||
this.Stop();
|
||||
StopEffect(m);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.Hits >= m.HitsMax)
|
||||
return;
|
||||
|
||||
int toHeal = this.m_Info.m_HitsPerRound;
|
||||
|
||||
SpellHelper.Heal(toHeal, m, this.m_Info.m_Caster);
|
||||
m.FixedParticles(0x376A, 9, 32, 5005, EffectLayer.Waist);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsUnderEffects(Mobile m)
|
||||
{
|
||||
return m_Table.ContainsKey(m);
|
||||
}
|
||||
|
||||
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 readonly GiftOfRenewalSpell m_Owner;
|
||||
|
||||
public InternalTarget(GiftOfRenewalSpell owner)
|
||||
: base(10, false, TargetFlags.Beneficial)
|
||||
{
|
||||
this.m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile m, object o)
|
||||
{
|
||||
if (o is Mobile)
|
||||
{
|
||||
this.m_Owner.Target((Mobile)o);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile m)
|
||||
{
|
||||
this.m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
153
Scripts/Spells/Spellweaving/ImmolatingWeapon.cs
Normal file
153
Scripts/Spells/Spellweaving/ImmolatingWeapon.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class ImmolatingWeaponSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Immolating Weapon", "Thalshara",
|
||||
-1);
|
||||
private static readonly Dictionary<Mobile, ImmolatingWeaponEntry> m_WeaponDamageTable = new Dictionary<Mobile, ImmolatingWeaponEntry>();
|
||||
|
||||
public ImmolatingWeaponSpell(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 10.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
public static bool IsImmolating(Mobile m, BaseWeapon weapon)
|
||||
{
|
||||
if (m == null)
|
||||
return false;
|
||||
|
||||
return m_WeaponDamageTable.ContainsKey(m) && m_WeaponDamageTable[m].m_Weapon == weapon;
|
||||
}
|
||||
|
||||
public static int GetImmolatingDamage(Mobile attacker)
|
||||
{
|
||||
ImmolatingWeaponEntry entry;
|
||||
|
||||
if (m_WeaponDamageTable.TryGetValue(attacker, out entry))
|
||||
return entry.m_Damage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void DoDelayEffect(Mobile attacker, Mobile target)
|
||||
{
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(.25), () =>
|
||||
{
|
||||
if (m_WeaponDamageTable.ContainsKey(attacker))
|
||||
AOS.Damage(target, attacker, m_WeaponDamageTable[attacker].m_Damage, 0, 100, 0, 0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
public static void StopImmolating(Mobile mob)
|
||||
{
|
||||
if (m_WeaponDamageTable.ContainsKey(mob))
|
||||
{
|
||||
StopImmolating(m_WeaponDamageTable[mob].m_Weapon, mob);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StopImmolating(BaseWeapon weapon, Mobile mob)
|
||||
{
|
||||
ImmolatingWeaponEntry entry;
|
||||
|
||||
if (m_WeaponDamageTable.TryGetValue(mob, out entry))
|
||||
{
|
||||
mob.PlaySound(0x27);
|
||||
|
||||
entry.m_Timer.Stop();
|
||||
|
||||
m_WeaponDamageTable.Remove(mob);
|
||||
|
||||
BuffInfo.RemoveBuff(mob, BuffIcon.ImmolatingWeapon);
|
||||
|
||||
weapon.InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CheckCast()
|
||||
{
|
||||
BaseWeapon weapon = Caster.Weapon as BaseWeapon;
|
||||
|
||||
if (Caster.Player && (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 (Caster.Player && (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(Caster, 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<Mobile>(TimeSpan.FromSeconds(duration), StopImmolating, Caster);
|
||||
|
||||
m_WeaponDamageTable[Caster] = new ImmolatingWeaponEntry(damage, stopTimer, weapon);
|
||||
|
||||
BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.ImmolatingWeapon, 1071028, 1153782, damage.ToString()));
|
||||
|
||||
weapon.InvalidateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private class ImmolatingWeaponEntry
|
||||
{
|
||||
public readonly int m_Damage;
|
||||
public readonly Timer m_Timer;
|
||||
public readonly BaseWeapon m_Weapon;
|
||||
|
||||
public ImmolatingWeaponEntry(int damage, Timer stopTimer, BaseWeapon weapon)
|
||||
{
|
||||
m_Damage = damage;
|
||||
m_Timer = stopTimer;
|
||||
m_Weapon = weapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Scripts/Spells/Spellweaving/Items/ArcaneFocus.cs
Normal file
88
Scripts/Spells/Spellweaving/Items/ArcaneFocus.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class ArcaneFocus : TransientItem
|
||||
{
|
||||
private int m_StrengthBonus;
|
||||
[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)
|
||||
{
|
||||
this.LootType = LootType.Blessed;
|
||||
this.m_StrengthBonus = strengthBonus;
|
||||
}
|
||||
|
||||
public ArcaneFocus(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override int LabelNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1032629;
|
||||
}
|
||||
}// Arcane Focus
|
||||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public int StrengthBonus
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_StrengthBonus;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_StrengthBonus = value;
|
||||
}
|
||||
}
|
||||
public override TextDefinition InvalidTransferMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1073480;
|
||||
}
|
||||
}// Your arcane focus disappears.
|
||||
public override bool Nontransferable
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override void GetProperties(ObjectPropertyList list)
|
||||
{
|
||||
base.GetProperties(list);
|
||||
|
||||
list.Add(1060485, this.m_StrengthBonus.ToString()); // strength bonus ~1_val~
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write((int)0);
|
||||
|
||||
writer.Write(this.m_StrengthBonus);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
int version = reader.ReadInt();
|
||||
|
||||
this.m_StrengthBonus = reader.ReadInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
131
Scripts/Spells/Spellweaving/Items/TransientItem.cs
Normal file
131
Scripts/Spells/Spellweaving/Items/TransientItem.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Items
|
||||
{
|
||||
public class TransientItem : Item
|
||||
{
|
||||
private TimeSpan m_LifeSpan;
|
||||
private DateTime m_CreationTime;
|
||||
private Timer m_Timer;
|
||||
[Constructable]
|
||||
public TransientItem(int itemID, TimeSpan lifeSpan)
|
||||
: base(itemID)
|
||||
{
|
||||
this.m_CreationTime = DateTime.UtcNow;
|
||||
this.m_LifeSpan = lifeSpan;
|
||||
|
||||
this.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), new TimerCallback(CheckExpiry));
|
||||
}
|
||||
|
||||
public TransientItem(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public TimeSpan LifeSpan
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_LifeSpan;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_LifeSpan = value;
|
||||
}
|
||||
}
|
||||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public DateTime CreationTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_CreationTime;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_CreationTime = value;
|
||||
}
|
||||
}
|
||||
public override bool Nontransferable
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public virtual TextDefinition InvalidTransferMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public override void HandleInvalidTransfer(Mobile from)
|
||||
{
|
||||
if (this.InvalidTransferMessage != null)
|
||||
TextDefinition.SendMessageTo(from, this.InvalidTransferMessage);
|
||||
|
||||
this.Delete();
|
||||
}
|
||||
|
||||
public virtual void Expire(Mobile parent)
|
||||
{
|
||||
if (parent != null)
|
||||
parent.SendLocalizedMessage(1072515, (this.Name == null ? String.Format("#{0}", this.LabelNumber) : this.Name)); // The ~1_name~ expired...
|
||||
|
||||
Effects.PlaySound(this.GetWorldLocation(), this.Map, 0x201);
|
||||
|
||||
this.Delete();
|
||||
}
|
||||
|
||||
public virtual void SendTimeRemainingMessage(Mobile to)
|
||||
{
|
||||
to.SendLocalizedMessage(1072516, String.Format("{0}\t{1}", (this.Name == null ? String.Format("#{0}", this.LabelNumber) : this.Name), (int)this.m_LifeSpan.TotalSeconds)); // ~1_name~ will expire in ~2_val~ seconds!
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
if (this.m_Timer != null)
|
||||
this.m_Timer.Stop();
|
||||
|
||||
base.OnDelete();
|
||||
}
|
||||
|
||||
public virtual void CheckExpiry()
|
||||
{
|
||||
if ((this.m_CreationTime + this.m_LifeSpan) < DateTime.UtcNow)
|
||||
this.Expire(this.RootParent as Mobile);
|
||||
else
|
||||
this.InvalidateProperties();
|
||||
}
|
||||
|
||||
public override void GetProperties(ObjectPropertyList list)
|
||||
{
|
||||
base.GetProperties(list);
|
||||
|
||||
TimeSpan remaining = ((this.m_CreationTime + this.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(this.m_LifeSpan);
|
||||
writer.Write(this.m_CreationTime);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
int version = reader.ReadInt();
|
||||
|
||||
this.m_LifeSpan = reader.ReadTimeSpan();
|
||||
this.m_CreationTime = reader.ReadDateTime();
|
||||
|
||||
this.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), new TimerCallback(CheckExpiry));
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Scripts/Spells/Spellweaving/Mobiles/ArcaneFey.cs
Normal file
88
Scripts/Spells/Spellweaving/Mobiles/ArcaneFey.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
[CorpseName("a pixie corpse")]
|
||||
public class ArcaneFey : BaseCreature
|
||||
{
|
||||
[Constructable]
|
||||
public ArcaneFey()
|
||||
: base(AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4)
|
||||
{
|
||||
this.Name = NameList.RandomName("pixie");
|
||||
this.Body = 128;
|
||||
this.BaseSoundID = 0x467;
|
||||
|
||||
this.SetStr(20);
|
||||
this.SetDex(150);
|
||||
this.SetInt(125);
|
||||
|
||||
this.SetDamage(9, 15);
|
||||
|
||||
this.SetDamageType(ResistanceType.Physical, 100);
|
||||
|
||||
this.SetResistance(ResistanceType.Physical, 80, 90);
|
||||
this.SetResistance(ResistanceType.Fire, 40, 50);
|
||||
this.SetResistance(ResistanceType.Cold, 40, 50);
|
||||
this.SetResistance(ResistanceType.Poison, 40, 50);
|
||||
this.SetResistance(ResistanceType.Energy, 40, 50);
|
||||
|
||||
this.SetSkill(SkillName.EvalInt, 70.1, 80.0);
|
||||
this.SetSkill(SkillName.Magery, 70.1, 80.0);
|
||||
this.SetSkill(SkillName.Meditation, 70.1, 80.0);
|
||||
this.SetSkill(SkillName.MagicResist, 50.5, 100.0);
|
||||
this.SetSkill(SkillName.Tactics, 10.1, 20.0);
|
||||
this.SetSkill(SkillName.Wrestling, 10.1, 12.5);
|
||||
|
||||
this.Fame = 0;
|
||||
this.Karma = 0;
|
||||
|
||||
this.ControlSlots = 1;
|
||||
}
|
||||
|
||||
public ArcaneFey(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Scripts/Spells/Spellweaving/Mobiles/ArcaneFiend.cs
Normal file
89
Scripts/Spells/Spellweaving/Mobiles/ArcaneFiend.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
[CorpseName("an imp corpse")]
|
||||
public class ArcaneFiend : BaseCreature
|
||||
{
|
||||
[Constructable]
|
||||
public ArcaneFiend()
|
||||
: base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4)
|
||||
{
|
||||
this.Name = "an imp";
|
||||
this.Body = 74;
|
||||
this.BaseSoundID = 422;
|
||||
|
||||
this.SetStr(55);
|
||||
this.SetDex(40);
|
||||
this.SetInt(60);
|
||||
|
||||
this.SetDamage(10, 14);
|
||||
|
||||
this.SetDamageType(ResistanceType.Physical, 0);
|
||||
this.SetDamageType(ResistanceType.Fire, 50);
|
||||
this.SetDamageType(ResistanceType.Poison, 50);
|
||||
|
||||
this.SetResistance(ResistanceType.Physical, 25, 35);
|
||||
this.SetResistance(ResistanceType.Fire, 40, 50);
|
||||
this.SetResistance(ResistanceType.Cold, 20, 30);
|
||||
this.SetResistance(ResistanceType.Poison, 30, 40);
|
||||
this.SetResistance(ResistanceType.Energy, 30, 40);
|
||||
|
||||
this.SetSkill(SkillName.EvalInt, 20.1, 30.0);
|
||||
this.SetSkill(SkillName.Magery, 60.1, 70.0);
|
||||
this.SetSkill(SkillName.MagicResist, 30.1, 50.0);
|
||||
this.SetSkill(SkillName.Tactics, 42.1, 50.0);
|
||||
this.SetSkill(SkillName.Wrestling, 40.1, 44.0);
|
||||
|
||||
this.Fame = 0;
|
||||
this.Karma = 0;
|
||||
|
||||
this.ControlSlots = 1;
|
||||
}
|
||||
|
||||
public ArcaneFiend(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
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.
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Scripts/Spells/Spellweaving/Mobiles/NatureFury.cs
Normal file
123
Scripts/Spells/Spellweaving/Mobiles/NatureFury.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class NatureFury : BaseCreature
|
||||
{
|
||||
[Constructable]
|
||||
public NatureFury()
|
||||
: base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4)
|
||||
{
|
||||
this.Name = "a nature's fury";
|
||||
this.Body = 0x33;
|
||||
this.Hue = 0x4001;
|
||||
|
||||
this.SetStr(150);
|
||||
this.SetDex(150);
|
||||
this.SetInt(100);
|
||||
|
||||
this.SetHits(80);
|
||||
this.SetStam(250);
|
||||
this.SetMana(0);
|
||||
|
||||
this.SetDamage(6, 8);
|
||||
|
||||
this.SetDamageType(ResistanceType.Poison, 100);
|
||||
this.SetDamageType(ResistanceType.Physical, 0);
|
||||
this.SetResistance(ResistanceType.Physical, 90);
|
||||
|
||||
this.SetSkill(SkillName.Wrestling, 90.0);
|
||||
this.SetSkill(SkillName.MagicResist, 70.0);
|
||||
this.SetSkill(SkillName.Tactics, 100.0);
|
||||
|
||||
this.Fame = 0;
|
||||
this.Karma = 0;
|
||||
|
||||
this.ControlSlots = 1;
|
||||
}
|
||||
|
||||
public NatureFury(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
public override void MoveToWorld(Point3D loc, Map map)
|
||||
{
|
||||
base.MoveToWorld(loc, map);
|
||||
Timer.DelayCall(TimeSpan.Zero, DoEffects);
|
||||
}
|
||||
|
||||
public void DoEffects()
|
||||
{
|
||||
this.FixedParticles(0x91C, 10, 180, 0x2543, 0, 0, EffectLayer.Waist);
|
||||
this.PlaySound(0xE);
|
||||
this.PlaySound(0x1BC);
|
||||
|
||||
if (this.Alive && !this.Deleted)
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(7.0), DoEffects);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
this.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Scripts/Spells/Spellweaving/NatureFury.cs
Normal file
134
Scripts/Spells/Spellweaving/NatureFury.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class NatureFurySpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Nature's Fury", "Rauvvrae",
|
||||
-1,
|
||||
false);
|
||||
public NatureFurySpell(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 0.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
public override bool CheckCast()
|
||||
{
|
||||
if (!base.CheckCast())
|
||||
return false;
|
||||
|
||||
if ((this.Caster.Followers + 1) > this.Caster.FollowersMax)
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnCast()
|
||||
{
|
||||
this.Caster.Target = new InternalTarget(this);
|
||||
}
|
||||
|
||||
public void Target(IPoint3D point)
|
||||
{
|
||||
Point3D p = new Point3D(point);
|
||||
Map map = this.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(this.Caster))
|
||||
return;
|
||||
|
||||
if (!map.CanSpawnMobile(p.X, p.Y, p.Z))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(501942); // That location is blocked.
|
||||
}
|
||||
else if (SpellHelper.CheckTown(p, this.Caster) && this.CheckSequence())
|
||||
{
|
||||
TimeSpan duration = TimeSpan.FromSeconds(this.Caster.Skills.Spellweaving.Value / 24 + 25 + this.FocusLevel * 2);
|
||||
|
||||
NatureFury nf = new NatureFury();
|
||||
BaseCreature.Summon(nf, false, this.Caster, p, 0x5CB, duration);
|
||||
|
||||
new InternalTimer(nf).Start();
|
||||
}
|
||||
|
||||
this.FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private readonly NatureFurySpell m_Owner;
|
||||
public InternalTarget(NatureFurySpell owner)
|
||||
: base(10, true, TargetFlags.None)
|
||||
{
|
||||
this.m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (o is IPoint3D)
|
||||
this.m_Owner.Target((IPoint3D)o);
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile from)
|
||||
{
|
||||
if (this.m_Owner != null)
|
||||
this.m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTimer : Timer
|
||||
{
|
||||
private readonly NatureFury m_NatureFury;
|
||||
public InternalTimer(NatureFury nf)
|
||||
: base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0))
|
||||
{
|
||||
this.m_NatureFury = nf;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (this.m_NatureFury.Deleted || !this.m_NatureFury.Alive || this.m_NatureFury.DamageMin > 20)
|
||||
{
|
||||
this.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
++this.m_NatureFury.DamageMin;
|
||||
++this.m_NatureFury.DamageMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
Scripts/Spells/Spellweaving/ReaperForm.cs
Normal file
129
Scripts/Spells/Spellweaving/ReaperForm.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class ReaperFormSpell : ArcaneForm
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo("Reaper Form", "Tarisstree", -1);
|
||||
public ReaperFormSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(2.5);
|
||||
}
|
||||
}
|
||||
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 + this.FocusLevel;
|
||||
}
|
||||
}
|
||||
public override int ColdResistOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return 5 + this.FocusLevel;
|
||||
}
|
||||
}
|
||||
public override int PoisResistOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return 5 + this.FocusLevel;
|
||||
}
|
||||
}
|
||||
public override int NrgyResistOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return 5 + this.FocusLevel;
|
||||
}
|
||||
}
|
||||
public virtual int SwingSpeedBonus
|
||||
{
|
||||
get
|
||||
{
|
||||
return 10 + this.FocusLevel;
|
||||
}
|
||||
}
|
||||
public virtual int SpellDamageBonus
|
||||
{
|
||||
get
|
||||
{
|
||||
return 10 + this.FocusLevel;
|
||||
}
|
||||
}
|
||||
public static void Initialize()
|
||||
{
|
||||
if (!Core.SA)
|
||||
{
|
||||
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.SendSpeedControl(SpeedControlType.WalkSpeed);
|
||||
}
|
||||
|
||||
public override void DoEffect(Mobile m)
|
||||
{
|
||||
m.PlaySound(0x1BA);
|
||||
|
||||
BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.ReaperForm, 1071034, 1153781, "10\t10\t5\t5\t5\t5\t25"));
|
||||
|
||||
if (!Core.SA)
|
||||
{
|
||||
m.SendSpeedControl(SpeedControlType.WalkSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveEffect(Mobile m)
|
||||
{
|
||||
if (!Core.SA)
|
||||
{
|
||||
m.SendSpeedControl(SpeedControlType.Disable);
|
||||
}
|
||||
|
||||
BuffInfo.RemoveBuff(m, BuffIcon.ReaperForm);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Scripts/Spells/Spellweaving/SummonFey.cs
Normal file
45
Scripts/Spells/Spellweaving/SummonFey.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class SummonFeySpell : ArcaneSummon<ArcaneFey>
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Summon Fey", "Alalithra",
|
||||
-1);
|
||||
public SummonFeySpell(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 38.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
public override int Sound
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0x217;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Scripts/Spells/Spellweaving/SummonFiend.cs
Normal file
45
Scripts/Spells/Spellweaving/SummonFiend.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class SummonFiendSpell : ArcaneSummon<ArcaneFiend>
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Summon Fiend", "Nylisstra",
|
||||
-1);
|
||||
public SummonFiendSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
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 override int Sound
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0x216;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Scripts/Spells/Spellweaving/Thunderstorm.cs
Normal file
113
Scripts/Spells/Spellweaving/Thunderstorm.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class ThunderstormSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Thunderstorm", "Erelonia",
|
||||
-1);
|
||||
|
||||
private static readonly Dictionary<Mobile, Timer> m_Table = new Dictionary<Mobile, Timer>();
|
||||
|
||||
public override DamageType SpellDamageType { get { return DamageType.SpellAOE; } }
|
||||
|
||||
public ThunderstormSpell(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 10.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
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);
|
||||
m.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
TimeSpan duration = TimeSpan.FromSeconds(5 + FocusLevel);
|
||||
|
||||
foreach (var m in AcquireIndirectTargets(Caster.Location, 3 + FocusLevel).OfType<Mobile>())
|
||||
{
|
||||
Caster.DoHarmful(m);
|
||||
|
||||
Spell oldSpell = m.Spell as Spell;
|
||||
|
||||
SpellHelper.Damage(this, m, (m.Player && Caster.Player) ? pvpDamage : pvmDamage, 0, 0, 0, 0, 100);
|
||||
Effects.SendPacket(m.Location, m.Map, new HuedEffect(EffectType.FixedFrom, m.Serial, Serial.Zero, 0x1B6C, m.Location, m.Location, 10, 10, false, false, 0x480, 4));
|
||||
|
||||
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)));
|
||||
m.Delta(MobileDelta.WeaponDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
239
Scripts/Spells/Spellweaving/Wildfire.cs
Normal file
239
Scripts/Spells/Spellweaving/Wildfire.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server.Targeting;
|
||||
using Server.Multis;
|
||||
using Server.Regions;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class WildfireSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo(
|
||||
"Wildfire", "Haelyn",
|
||||
-1,
|
||||
false);
|
||||
public WildfireSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(2.5);
|
||||
}
|
||||
}
|
||||
public override double RequiredSkill
|
||||
{
|
||||
get
|
||||
{
|
||||
return 66.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
public override void OnCast()
|
||||
{
|
||||
Caster.Target = new InternalTarget(this);
|
||||
}
|
||||
|
||||
public void Target(Point3D p)
|
||||
{
|
||||
if (!Caster.CanSee(p))
|
||||
{
|
||||
Caster.SendLocalizedMessage(500237); // Target can not be seen.
|
||||
}
|
||||
else if (CheckSequence())
|
||||
{
|
||||
int level = GetFocusLevel(Caster);
|
||||
double skill = Caster.Skills[CastSkill].Value;
|
||||
|
||||
int tiles = 5 + level;
|
||||
int damage = 10 + (int)Math.Max(1, (skill / 24)) + level;
|
||||
int duration = (int)Math.Max(1, skill / 24) + level;
|
||||
|
||||
for (int x = p.X - tiles; x <= p.X + tiles; x += tiles)
|
||||
{
|
||||
for (int y = p.Y - tiles; y <= p.Y + tiles; y += tiles)
|
||||
{
|
||||
if (p.X == x && p.Y == y)
|
||||
continue;
|
||||
|
||||
Point3D p3d = new Point3D(x, y, Caster.Map.GetAverageZ(x, y));
|
||||
|
||||
if (CanFitFire(p3d, Caster))
|
||||
new FireItem(duration).MoveToWorld(p3d, Caster.Map);
|
||||
}
|
||||
}
|
||||
|
||||
Effects.PlaySound(p, Caster.Map, 0x5CF);
|
||||
|
||||
NegativeAttributes.OnCombatAction(Caster);
|
||||
|
||||
new InternalTimer(this, Caster, p, damage, tiles, duration).Start();
|
||||
}
|
||||
|
||||
FinishSequence();
|
||||
}
|
||||
|
||||
private bool CanFitFire(Point3D p, Mobile caster)
|
||||
{
|
||||
if (!Caster.Map.CanFit(p, 12, true, false))
|
||||
return false;
|
||||
if (BaseHouse.FindHouseAt(p, caster.Map, 20) != null)
|
||||
return false;
|
||||
foreach(RegionRect r in caster.Map.GetSector(p).RegionRects)
|
||||
{
|
||||
if (!r.Contains(p))
|
||||
continue;
|
||||
GuardedRegion reg = (GuardedRegion)Region.Find(p, caster.Map).GetRegion(typeof(GuardedRegion));
|
||||
if (reg != null && !reg.Disabled)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<Mobile, long> m_Table = new Dictionary<Mobile, long>();
|
||||
public static Dictionary<Mobile, long> Table { get { return m_Table; } }
|
||||
|
||||
public static void DefragTable()
|
||||
{
|
||||
List<Mobile> mobiles = new List<Mobile>(m_Table.Keys);
|
||||
|
||||
foreach (Mobile m in mobiles)
|
||||
{
|
||||
if (Core.TickCount - m_Table[m] >= 0)
|
||||
m_Table.Remove(m);
|
||||
}
|
||||
|
||||
ColUtility.Free(mobiles);
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private readonly WildfireSpell m_Owner;
|
||||
public InternalTarget(WildfireSpell owner)
|
||||
: base(12, true, TargetFlags.None)
|
||||
{
|
||||
m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile m, object o)
|
||||
{
|
||||
if (o is IPoint3D)
|
||||
{
|
||||
m_Owner.Target(new Point3D((IPoint3D)o));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile m)
|
||||
{
|
||||
m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
|
||||
public class InternalTimer : Timer
|
||||
{
|
||||
private readonly Spell m_Spell;
|
||||
private readonly Mobile m_Owner;
|
||||
private readonly Point3D m_Location;
|
||||
private readonly int m_Damage;
|
||||
private readonly int m_Range;
|
||||
private int m_LifeSpan;
|
||||
private Map m_Map;
|
||||
|
||||
public InternalTimer(Spell spell, Mobile owner, Point3D location, int damage, int range, int duration)
|
||||
: base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), duration)
|
||||
{
|
||||
m_Spell = spell;
|
||||
m_Owner = owner;
|
||||
m_Location = location;
|
||||
m_Damage = damage;
|
||||
m_Range = range;
|
||||
m_LifeSpan = duration;
|
||||
m_Map = owner.Map;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (m_Owner == null || m_Map == null || m_Map == Map.Internal)
|
||||
return;
|
||||
|
||||
m_LifeSpan -= 1;
|
||||
var targets = GetTargets().Where(m => BaseHouse.FindHouseAt(m.Location, m.Map, 20) == null).ToList();
|
||||
int count = targets.Count;
|
||||
|
||||
foreach (Mobile m in targets)
|
||||
{
|
||||
m_Owner.DoHarmful(m);
|
||||
|
||||
if (m_Map.CanFit(m.Location, 12, true, false))
|
||||
new FireItem(m_LifeSpan).MoveToWorld(m.Location, m_Map);
|
||||
|
||||
Effects.PlaySound(m.Location, m_Map, 0x5CF);
|
||||
double sdiBonus = (double)AosAttributes.GetValue(m_Owner, AosAttribute.SpellDamage) / 100;
|
||||
|
||||
if (m is PlayerMobile && sdiBonus > .15)
|
||||
sdiBonus = .15;
|
||||
|
||||
int damage = m_Damage + (int)((double)m_Damage * sdiBonus);
|
||||
|
||||
if (count > 1)
|
||||
damage /= Math.Min(3, count);
|
||||
|
||||
AOS.Damage(m, m_Owner, damage, 0, 100, 0, 0, 0, 0, 0, DamageType.SpellAOE);
|
||||
WildfireSpell.Table[m] = Core.TickCount + 1000;
|
||||
}
|
||||
|
||||
ColUtility.Free(targets);
|
||||
}
|
||||
|
||||
private IEnumerable<Mobile> GetTargets()
|
||||
{
|
||||
WildfireSpell.DefragTable();
|
||||
|
||||
return m_Spell.AcquireIndirectTargets(m_Location, m_Range).OfType<Mobile>().Where(m => !m_Table.ContainsKey(m));
|
||||
}
|
||||
}
|
||||
|
||||
public class FireItem : Item
|
||||
{
|
||||
public FireItem(int duration)
|
||||
: base(Utility.RandomBool() ? 0x398C : 0x3996)
|
||||
{
|
||||
Movable = false;
|
||||
Timer.DelayCall(TimeSpan.FromSeconds(duration), new TimerCallback(Delete));
|
||||
}
|
||||
|
||||
public FireItem(Serial serial)
|
||||
: base(serial)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.Write((int)0); // version
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
int version = reader.ReadInt();
|
||||
|
||||
Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
Scripts/Spells/Spellweaving/WordOfDeath.cs
Normal file
109
Scripts/Spells/Spellweaving/WordOfDeath.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using Server.Targeting;
|
||||
using Server.Mobiles;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
public class WordOfDeathSpell : ArcanistSpell
|
||||
{
|
||||
private static readonly SpellInfo m_Info = new SpellInfo("Word of Death", "Nyraxle", -1);
|
||||
public WordOfDeathSpell(Mobile caster, Item scroll)
|
||||
: base(caster, scroll, m_Info)
|
||||
{
|
||||
}
|
||||
|
||||
public override TimeSpan CastDelayBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(3.5);
|
||||
}
|
||||
}
|
||||
public override double RequiredSkill
|
||||
{
|
||||
get
|
||||
{
|
||||
return 83.0;
|
||||
}
|
||||
}
|
||||
public override int RequiredMana
|
||||
{
|
||||
get
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
public override void OnCast()
|
||||
{
|
||||
this.Caster.Target = new InternalTarget(this);
|
||||
}
|
||||
|
||||
public void Target(Mobile m)
|
||||
{
|
||||
if (!this.Caster.CanSee(m))
|
||||
{
|
||||
this.Caster.SendLocalizedMessage(500237); // Target can not be seen.
|
||||
}
|
||||
else if (this.CheckHSequence(m))
|
||||
{
|
||||
SpellHelper.CheckReflect(0, Caster, ref 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 * this.FocusLevel;
|
||||
bool pvmThreshold = !m.Player && (((double)m.Hits / (double)m.HitsMax) < percentage);
|
||||
|
||||
int damage;
|
||||
|
||||
if (pvmThreshold)
|
||||
{
|
||||
damage = 300;
|
||||
}
|
||||
else
|
||||
{
|
||||
int minDamage = (int)this.Caster.Skills.Spellweaving.Value / 5;
|
||||
int maxDamage = (int)this.Caster.Skills.Spellweaving.Value / 3;
|
||||
damage = Utility.RandomMinMax(minDamage, maxDamage);
|
||||
}
|
||||
|
||||
int damageBonus = SpellHelper.GetSpellDamageBonus(Caster, m, CastSkill, Caster.Player && m.Player);
|
||||
|
||||
damage *= damageBonus + 100;
|
||||
damage /= 100;
|
||||
|
||||
SpellHelper.Damage(this, m, damage, 0, 0, 0, 0, 0, !pvmThreshold ? 100 : 0, pvmThreshold ? 100 : 0);
|
||||
}
|
||||
|
||||
this.FinishSequence();
|
||||
}
|
||||
|
||||
public class InternalTarget : Target
|
||||
{
|
||||
private readonly WordOfDeathSpell m_Owner;
|
||||
public InternalTarget(WordOfDeathSpell owner)
|
||||
: base(10, false, TargetFlags.Harmful)
|
||||
{
|
||||
this.m_Owner = owner;
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile m, object o)
|
||||
{
|
||||
if (o is Mobile)
|
||||
{
|
||||
this.m_Owner.Target((Mobile)o);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnTargetFinish(Mobile m)
|
||||
{
|
||||
this.m_Owner.FinishSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user