1411 lines
33 KiB
C#
1411 lines
33 KiB
C#
#region References
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using Server.Commands;
|
|
using Server.Engines.Craft;
|
|
using Server.Ethics;
|
|
using Server.Multis;
|
|
using Server.Network;
|
|
using Server.Spells;
|
|
using Server.Targeting;
|
|
using Server.Mobiles;
|
|
using Server.Spells.Mysticism;
|
|
using Server.Factions;
|
|
#endregion
|
|
|
|
namespace Server.Items
|
|
{
|
|
public enum SpellbookType
|
|
{
|
|
Invalid = -1,
|
|
Regular,
|
|
Necromancer,
|
|
Paladin,
|
|
Ninja,
|
|
Samurai,
|
|
Arcanist,
|
|
Mystic,
|
|
SkillMasteries
|
|
}
|
|
|
|
public enum BookQuality
|
|
{
|
|
Regular,
|
|
Exceptional,
|
|
}
|
|
|
|
public class Spellbook : Item, ICraftable, ISlayer, IEngravable, IVvVItem, IOwnerRestricted, IWearableDurability, IFactionItem
|
|
{
|
|
private static readonly Dictionary<Mobile, List<Spellbook>> m_Table = new Dictionary<Mobile, List<Spellbook>>();
|
|
|
|
private static readonly int[] m_LegendPropertyCounts = new[]
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 properties : 21/52 : 40%
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 property : 15/52 : 29%
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 2 properties : 10/52 : 19%
|
|
3, 3, 3, 3, 3, 3 // 3 properties : 6/52 : 12%
|
|
};
|
|
|
|
private static readonly int[] m_ElderPropertyCounts = new[]
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 properties : 15/34 : 44%
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 property : 10/34 : 29%
|
|
2, 2, 2, 2, 2, 2, // 2 properties : 6/34 : 18%
|
|
3, 3, 3 // 3 properties : 3/34 : 9%
|
|
};
|
|
|
|
private static readonly int[] m_GrandPropertyCounts = new[]
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 properties : 10/20 : 50%
|
|
1, 1, 1, 1, 1, 1, // 1 property : 6/20 : 30%
|
|
2, 2, 2, // 2 properties : 3/20 : 15%
|
|
3 // 3 properties : 1/20 : 5%
|
|
};
|
|
|
|
private static readonly int[] m_MasterPropertyCounts = new[]
|
|
{
|
|
0, 0, 0, 0, 0, 0, // 0 properties : 6/10 : 60%
|
|
1, 1, 1, // 1 property : 3/10 : 30%
|
|
2 // 2 properties : 1/10 : 10%
|
|
};
|
|
|
|
private static readonly int[] m_AdeptPropertyCounts = new[]
|
|
{
|
|
0, 0, 0, // 0 properties : 3/4 : 75%
|
|
1 // 1 property : 1/4 : 25%
|
|
};
|
|
|
|
#region Factions
|
|
private FactionItem m_FactionState;
|
|
|
|
public FactionItem FactionItemState
|
|
{
|
|
get { return m_FactionState; }
|
|
set
|
|
{
|
|
m_FactionState = value;
|
|
|
|
LootType = (m_FactionState == null ? LootType.Regular : LootType.Blessed);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
private string m_EngravedText;
|
|
private BookQuality m_Quality;
|
|
private AosAttributes m_AosAttributes;
|
|
private AosSkillBonuses m_AosSkillBonuses;
|
|
private NegativeAttributes m_NegativeAttributes;
|
|
private ulong m_Content;
|
|
private int m_Count;
|
|
private Mobile m_Crafter;
|
|
private SlayerName m_Slayer;
|
|
private SlayerName m_Slayer2;
|
|
//Currently though there are no dual slayer spellbooks, OSI has a habit of putting dual slayer stuff in later
|
|
[Constructable]
|
|
public Spellbook()
|
|
: this((ulong)0)
|
|
{ }
|
|
|
|
[Constructable]
|
|
public Spellbook(ulong content)
|
|
: this(content, 0xEFA)
|
|
{ }
|
|
|
|
public Spellbook(ulong content, int itemID)
|
|
: base(itemID)
|
|
{
|
|
m_AosAttributes = new AosAttributes(this);
|
|
m_AosSkillBonuses = new AosSkillBonuses(this);
|
|
m_NegativeAttributes = new NegativeAttributes(this);
|
|
|
|
Weight = 3.0;
|
|
Layer = Layer.OneHanded;
|
|
LootType = LootType.Blessed;
|
|
|
|
Content = content;
|
|
}
|
|
|
|
public Spellbook(Serial serial)
|
|
: base(serial)
|
|
{ }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string EngravedText
|
|
{
|
|
get { return m_EngravedText; }
|
|
set
|
|
{
|
|
m_EngravedText = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public BookQuality Quality
|
|
{
|
|
get { return m_Quality; }
|
|
set
|
|
{
|
|
m_Quality = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
public override bool DisplayWeight { get { return false; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosAttributes Attributes { get { return m_AosAttributes; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AosSkillBonuses SkillBonuses { get { return m_AosSkillBonuses; } set { } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public NegativeAttributes NegativeAttributes { get { return m_NegativeAttributes; } set { } }
|
|
|
|
public virtual SpellbookType SpellbookType { get { return SpellbookType.Regular; } }
|
|
public virtual int BookOffset { get { return 0; } }
|
|
public virtual int BookCount { get { return 64; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public ulong Content
|
|
{
|
|
get { return m_Content; }
|
|
set
|
|
{
|
|
if (m_Content != value)
|
|
{
|
|
m_Content = value;
|
|
|
|
m_Count = 0;
|
|
|
|
while (value > 0)
|
|
{
|
|
m_Count += (int)(value & 0x1);
|
|
value >>= 1;
|
|
}
|
|
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SpellCount { get { return m_Count; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Mobile Crafter
|
|
{
|
|
get { return m_Crafter; }
|
|
set
|
|
{
|
|
m_Crafter = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
public override bool DisplayLootType { get { return Core.AOS; } }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public SlayerName Slayer
|
|
{
|
|
get { return m_Slayer; }
|
|
set
|
|
{
|
|
m_Slayer = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public SlayerName Slayer2
|
|
{
|
|
get { return m_Slayer2; }
|
|
set
|
|
{
|
|
m_Slayer2 = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
#region IVvVItem / IOwnerRestricted
|
|
private bool _VvVItem;
|
|
private Mobile _Owner;
|
|
private string _OwnerName;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool IsVvVItem
|
|
{
|
|
get { return _VvVItem; }
|
|
set { _VvVItem = value; InvalidateProperties(); }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public Mobile Owner
|
|
{
|
|
get { return _Owner; }
|
|
set { _Owner = value; if (_Owner != null) _OwnerName = _Owner.Name; InvalidateProperties(); }
|
|
}
|
|
|
|
public virtual string OwnerName
|
|
{
|
|
get { return _OwnerName; }
|
|
set { _OwnerName = value; InvalidateProperties(); }
|
|
}
|
|
#endregion
|
|
|
|
#region IWearableDurability
|
|
private int m_MaxHitPoints;
|
|
private int m_HitPoints;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int HitPoints
|
|
{
|
|
get { return m_HitPoints; }
|
|
set
|
|
{
|
|
if (m_HitPoints == value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (value > m_MaxHitPoints)
|
|
{
|
|
value = m_MaxHitPoints;
|
|
}
|
|
|
|
m_HitPoints = value;
|
|
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int MaxHitPoints
|
|
{
|
|
get { return m_MaxHitPoints; }
|
|
set
|
|
{
|
|
m_MaxHitPoints = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
public virtual bool CanFortify { get { return false; } }
|
|
|
|
public virtual int InitMinHits { get { return 0; } }
|
|
public virtual int InitMaxHits { get { return 0; } }
|
|
|
|
public virtual void ScaleDurability()
|
|
{
|
|
}
|
|
|
|
public virtual void UnscaleDurability()
|
|
{
|
|
}
|
|
|
|
public virtual int OnHit(BaseWeapon weap, int damage)
|
|
{
|
|
if (m_MaxHitPoints == 0)
|
|
return damage;
|
|
|
|
int chance = m_NegativeAttributes.Antique > 0 ? 50 : 25;
|
|
|
|
if (chance > Utility.Random(100)) // 25% chance to lower durability
|
|
{
|
|
if (m_HitPoints >= 1)
|
|
{
|
|
HitPoints--;
|
|
}
|
|
else if (m_MaxHitPoints > 0)
|
|
{
|
|
MaxHitPoints--;
|
|
|
|
if (Parent is Mobile)
|
|
((Mobile)Parent).LocalOverheadMessage(MessageType.Regular, 0x3B2, 1061121); // Your equipment is severely damaged.
|
|
|
|
if (m_MaxHitPoints == 0)
|
|
{
|
|
Delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
#endregion
|
|
|
|
public static void Initialize()
|
|
{
|
|
EventSink.OpenSpellbookRequest += EventSink_OpenSpellbookRequest;
|
|
EventSink.CastSpellRequest += EventSink_CastSpellRequest;
|
|
EventSink.TargetedSpell += Targeted_Spell;
|
|
|
|
CommandSystem.Register("AllSpells", AccessLevel.GameMaster, AllSpells_OnCommand);
|
|
}
|
|
|
|
#region Enhanced Client
|
|
private static void Targeted_Spell(TargetedSpellEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Mobile from = e.Mobile;
|
|
|
|
if (!DesignContext.Check(from))
|
|
{
|
|
return; // They are customizing
|
|
}
|
|
|
|
Spellbook book = null;
|
|
int spellID = e.SpellID;
|
|
|
|
if (book == null || !book.HasSpell(spellID))
|
|
{
|
|
book = Find(from, spellID);
|
|
}
|
|
|
|
if (book != null && book.HasSpell(spellID))
|
|
{
|
|
SpecialMove move = SpellRegistry.GetSpecialMove(spellID);
|
|
|
|
if (move != null)
|
|
{
|
|
SpecialMove.SetCurrentMove(from, move);
|
|
}
|
|
else
|
|
{
|
|
Mobile to = World.FindMobile(e.Target.Serial);
|
|
Item toI = World.FindItem(e.Target.Serial);
|
|
Spell spell = SpellRegistry.NewSpell(spellID, from, null);
|
|
|
|
if (to != null)
|
|
{
|
|
spell.InstantTarget = to;
|
|
}
|
|
else if (toI != null)
|
|
{
|
|
spell.InstantTarget = toI as IDamageableItem;
|
|
}
|
|
|
|
if (spell != null)
|
|
{
|
|
spell.Cast();
|
|
}
|
|
else if (!Server.Spells.SkillMasteries.MasteryInfo.IsPassiveMastery(spellID))
|
|
{
|
|
from.SendLocalizedMessage(502345); // This spell has been temporarily disabled.
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage(500015); // You do not have that spell!
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
#endregion
|
|
|
|
public static SpellbookType GetTypeForSpell(int spellID)
|
|
{
|
|
if (spellID >= 0 && spellID < 64)
|
|
{
|
|
return SpellbookType.Regular;
|
|
}
|
|
else if (spellID >= 100 && spellID < 117)
|
|
{
|
|
return SpellbookType.Necromancer;
|
|
}
|
|
else if (spellID >= 200 && spellID < 210)
|
|
{
|
|
return SpellbookType.Paladin;
|
|
}
|
|
else if (spellID >= 400 && spellID < 406)
|
|
{
|
|
return SpellbookType.Samurai;
|
|
}
|
|
else if (spellID >= 500 && spellID < 508)
|
|
{
|
|
return SpellbookType.Ninja;
|
|
}
|
|
else if (spellID >= 600 && spellID < 617)
|
|
{
|
|
return SpellbookType.Arcanist;
|
|
}
|
|
else if (spellID >= 677 && spellID < 693)
|
|
{
|
|
return SpellbookType.Mystic;
|
|
}
|
|
else if (spellID >= 700 && spellID < 746)
|
|
{
|
|
return SpellbookType.SkillMasteries;
|
|
}
|
|
|
|
return SpellbookType.Invalid;
|
|
}
|
|
|
|
public static Spellbook FindRegular(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Regular);
|
|
}
|
|
|
|
public static Spellbook FindNecromancer(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Necromancer);
|
|
}
|
|
|
|
public static Spellbook FindPaladin(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Paladin);
|
|
}
|
|
|
|
public static Spellbook FindSamurai(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Samurai);
|
|
}
|
|
|
|
public static Spellbook FindNinja(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Ninja);
|
|
}
|
|
|
|
public static Spellbook FindArcanist(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Arcanist);
|
|
}
|
|
|
|
public static Spellbook FindMystic(Mobile from)
|
|
{
|
|
return Find(from, -1, SpellbookType.Mystic);
|
|
}
|
|
|
|
public static Spellbook Find(Mobile from, int spellID)
|
|
{
|
|
return Find(from, spellID, GetTypeForSpell(spellID));
|
|
}
|
|
|
|
public static Spellbook Find(Mobile from, int spellID, SpellbookType type)
|
|
{
|
|
if (from == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (from.Deleted)
|
|
{
|
|
m_Table.Remove(from);
|
|
return null;
|
|
}
|
|
|
|
List<Spellbook> list = null;
|
|
|
|
m_Table.TryGetValue(from, out list);
|
|
|
|
bool searchAgain = false;
|
|
|
|
if (list == null)
|
|
{
|
|
m_Table[from] = list = FindAllSpellbooks(from);
|
|
}
|
|
else
|
|
{
|
|
searchAgain = true;
|
|
}
|
|
|
|
Spellbook book = FindSpellbookInList(list, from, spellID, type);
|
|
|
|
if (book == null && searchAgain)
|
|
{
|
|
m_Table[from] = list = FindAllSpellbooks(from);
|
|
|
|
book = FindSpellbookInList(list, from, spellID, type);
|
|
}
|
|
|
|
return book;
|
|
}
|
|
|
|
public static Spellbook FindSpellbookInList(List<Spellbook> list, Mobile from, int spellID, SpellbookType type)
|
|
{
|
|
Container pack = from.Backpack;
|
|
|
|
for (int i = list.Count - 1; i >= 0; --i)
|
|
{
|
|
if (i >= list.Count)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Spellbook book = list[i];
|
|
|
|
if (!book.Deleted && (book.Parent == from || (pack != null && book.Parent == pack)) &&
|
|
ValidateSpellbook(book, spellID, type))
|
|
{
|
|
return book;
|
|
}
|
|
|
|
list.RemoveAt(i);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static List<Spellbook> FindAllSpellbooks(Mobile from)
|
|
{
|
|
var list = new List<Spellbook>();
|
|
|
|
Item item = from.FindItemOnLayer(Layer.OneHanded);
|
|
|
|
if (item is Spellbook)
|
|
{
|
|
list.Add((Spellbook)item);
|
|
}
|
|
|
|
Container pack = from.Backpack;
|
|
|
|
if (pack == null)
|
|
{
|
|
return list;
|
|
}
|
|
|
|
for (int i = 0; i < pack.Items.Count; ++i)
|
|
{
|
|
item = pack.Items[i];
|
|
|
|
if (item is Spellbook)
|
|
{
|
|
list.Add((Spellbook)item);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public static Spellbook FindEquippedSpellbook(Mobile from)
|
|
{
|
|
return (from.FindItemOnLayer(Layer.OneHanded) as Spellbook);
|
|
}
|
|
|
|
public static bool ValidateSpellbook(Spellbook book, int spellID, SpellbookType type)
|
|
{
|
|
return (book.SpellbookType == type && (spellID == -1 || book.HasSpell(spellID)));
|
|
}
|
|
|
|
public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted)
|
|
{
|
|
if (!Ethic.CheckTrade(from, to, newOwner, this))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return base.AllowSecureTrade(from, to, newOwner, accepted);
|
|
}
|
|
|
|
public override bool CanEquip(Mobile from)
|
|
{
|
|
if (!Ethic.CheckEquip(from, this))
|
|
{
|
|
return false;
|
|
}
|
|
else if (!from.CanBeginAction(typeof(BaseWeapon)))
|
|
{
|
|
return false;
|
|
}
|
|
else if (_Owner != null && _Owner != from)
|
|
{
|
|
from.SendLocalizedMessage(501023); // You must be the owner to use this item.
|
|
return false;
|
|
}
|
|
else if (IsVvVItem && !Engines.VvV.ViceVsVirtueSystem.IsVvV(from))
|
|
{
|
|
from.SendLocalizedMessage(1155496); // This item can only be used by VvV participants!
|
|
return false;
|
|
}
|
|
|
|
return base.CanEquip(from);
|
|
}
|
|
|
|
public override bool AllowEquipedCast(Mobile from)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override bool OnDragDrop(Mobile from, Item dropped)
|
|
{
|
|
if (dropped is SpellScroll && !(dropped is SpellStone))
|
|
{
|
|
SpellScroll scroll = (SpellScroll)dropped;
|
|
|
|
SpellbookType type = GetTypeForSpell(scroll.SpellID);
|
|
|
|
if (type != SpellbookType)
|
|
{
|
|
return false;
|
|
}
|
|
else if (HasSpell(scroll.SpellID))
|
|
{
|
|
from.SendLocalizedMessage(500179); // That spell is already present in that spellbook.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int val = scroll.SpellID - BookOffset;
|
|
|
|
if (val >= 0 && val < BookCount)
|
|
{
|
|
from.Send(new PlaySound(0x249, GetWorldLocation()));
|
|
|
|
m_Content |= (ulong)1 << val;
|
|
++m_Count;
|
|
|
|
if (dropped.Amount > 1)
|
|
{
|
|
dropped.Amount--;
|
|
return base.OnDragDrop(from, dropped);
|
|
}
|
|
else
|
|
{
|
|
InvalidateProperties();
|
|
scroll.Delete();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override void OnAfterDuped(Item newItem)
|
|
{
|
|
Spellbook book = newItem as Spellbook;
|
|
|
|
if (book == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
book.m_AosAttributes = new AosAttributes(newItem, m_AosAttributes);
|
|
book.m_AosSkillBonuses = new AosSkillBonuses(newItem, m_AosSkillBonuses);
|
|
book.m_NegativeAttributes = new NegativeAttributes(newItem, m_NegativeAttributes);
|
|
|
|
base.OnAfterDuped(newItem);
|
|
}
|
|
|
|
public override void OnAdded(object parent)
|
|
{
|
|
if (Core.AOS && parent is Mobile)
|
|
{
|
|
Mobile from = (Mobile)parent;
|
|
|
|
m_AosSkillBonuses.AddTo(from);
|
|
|
|
int strBonus = m_AosAttributes.BonusStr;
|
|
int dexBonus = m_AosAttributes.BonusDex;
|
|
int intBonus = m_AosAttributes.BonusInt;
|
|
|
|
if (strBonus != 0 || dexBonus != 0 || intBonus != 0)
|
|
{
|
|
string modName = Serial.ToString();
|
|
|
|
if (strBonus != 0)
|
|
{
|
|
from.AddStatMod(new StatMod(StatType.Str, modName + "Str", strBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (dexBonus != 0)
|
|
{
|
|
from.AddStatMod(new StatMod(StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (intBonus != 0)
|
|
{
|
|
from.AddStatMod(new StatMod(StatType.Int, modName + "Int", intBonus, TimeSpan.Zero));
|
|
}
|
|
}
|
|
|
|
if (HasSocket<Caddellite>())
|
|
{
|
|
Caddellite.UpdateBuff(from);
|
|
}
|
|
|
|
from.CheckStatTimers();
|
|
}
|
|
}
|
|
|
|
public override void OnRemoved(object parent)
|
|
{
|
|
if (Core.AOS && parent is Mobile)
|
|
{
|
|
Mobile from = (Mobile)parent;
|
|
|
|
m_AosSkillBonuses.Remove();
|
|
|
|
if (HasSocket<Caddellite>())
|
|
{
|
|
Caddellite.UpdateBuff(from);
|
|
}
|
|
|
|
string modName = Serial.ToString();
|
|
|
|
from.RemoveStatMod(modName + "Str");
|
|
from.RemoveStatMod(modName + "Dex");
|
|
from.RemoveStatMod(modName + "Int");
|
|
|
|
from.CheckStatTimers();
|
|
}
|
|
}
|
|
|
|
public bool HasSpell(int spellID)
|
|
{
|
|
spellID -= BookOffset;
|
|
|
|
return (spellID >= 0 && spellID < BookCount && (m_Content & ((ulong)1 << spellID)) != 0);
|
|
}
|
|
|
|
public void DisplayTo(Mobile to)
|
|
{
|
|
// The client must know about the spellbook or it will crash!
|
|
NetState ns = to.NetState;
|
|
|
|
if (ns == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Parent == null)
|
|
{
|
|
to.Send(WorldPacket);
|
|
}
|
|
else if (Parent is Item)
|
|
{
|
|
// What will happen if the client doesn't know about our parent?
|
|
if (ns.ContainerGridLines)
|
|
{
|
|
to.Send(new ContainerContentUpdate6017(this));
|
|
}
|
|
else
|
|
{
|
|
to.Send(new ContainerContentUpdate(this));
|
|
}
|
|
}
|
|
else if (Parent is Mobile)
|
|
{
|
|
// What will happen if the client doesn't know about our parent?
|
|
to.Send(new EquipUpdate(this));
|
|
}
|
|
|
|
if (ns.HighSeas)
|
|
{
|
|
to.Send(new DisplaySpellbookHS(this));
|
|
}
|
|
else
|
|
{
|
|
to.Send(new DisplaySpellbook(this));
|
|
}
|
|
|
|
if (ObjectPropertyList.Enabled)
|
|
{
|
|
if (ns.NewSpellbook)
|
|
{
|
|
to.Send(new NewSpellbookContent(this, ItemID, BookOffset + 1, m_Content));
|
|
}
|
|
else
|
|
{
|
|
if (ns.ContainerGridLines)
|
|
{
|
|
to.Send(new SpellbookContent6017(m_Count, BookOffset + 1, m_Content, this));
|
|
}
|
|
else
|
|
{
|
|
to.Send(new SpellbookContent(m_Count, BookOffset + 1, m_Content, this));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ns.ContainerGridLines)
|
|
{
|
|
to.Send(new SpellbookContent6017(m_Count, BookOffset + 1, m_Content, this));
|
|
}
|
|
else
|
|
{
|
|
to.Send(new SpellbookContent(m_Count, BookOffset + 1, m_Content, this));
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void AddNameProperties(ObjectPropertyList list)
|
|
{
|
|
base.AddNameProperties(list);
|
|
|
|
if (m_Quality == BookQuality.Exceptional)
|
|
{
|
|
list.Add(1063341); // exceptional
|
|
}
|
|
|
|
if (m_EngravedText != null)
|
|
{
|
|
list.Add(1072305, Utility.FixHtml(m_EngravedText)); // Engraved: ~1_INSCRIPTION~
|
|
}
|
|
|
|
if (m_Crafter != null)
|
|
{
|
|
list.Add(1050043, m_Crafter.TitleName); // crafted by ~1_NAME~
|
|
}
|
|
|
|
#region Factions
|
|
FactionEquipment.AddFactionProperties(this, list);
|
|
#endregion
|
|
|
|
if (IsVvVItem)
|
|
{
|
|
list.Add(1154937); // VvV Item
|
|
}
|
|
|
|
if (OwnerName != null)
|
|
{
|
|
list.Add(1153213, OwnerName);
|
|
}
|
|
|
|
if (m_NegativeAttributes != null)
|
|
{
|
|
m_NegativeAttributes.GetProperties(list, this);
|
|
}
|
|
|
|
m_AosSkillBonuses.GetProperties(list);
|
|
|
|
if (m_Slayer != SlayerName.None)
|
|
{
|
|
SlayerEntry entry = SlayerGroup.GetEntryByName(m_Slayer);
|
|
if (entry != null)
|
|
{
|
|
list.Add(entry.Title);
|
|
}
|
|
}
|
|
|
|
if (m_Slayer2 != SlayerName.None)
|
|
{
|
|
SlayerEntry entry = SlayerGroup.GetEntryByName(m_Slayer2);
|
|
if (entry != null)
|
|
{
|
|
list.Add(entry.Title);
|
|
}
|
|
}
|
|
|
|
if (HasSocket<Caddellite>())
|
|
{
|
|
list.Add(1158662); // Caddellite Infused
|
|
}
|
|
|
|
int prop;
|
|
|
|
if ((prop = m_AosAttributes.SpellChanneling) != 0)
|
|
{
|
|
list.Add(1060482); // spell channeling
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.NightSight) != 0)
|
|
{
|
|
list.Add(1060441); // night sight
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusStr) != 0)
|
|
{
|
|
list.Add(1060485, prop.ToString()); // strength bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusDex) != 0)
|
|
{
|
|
list.Add(1060409, prop.ToString()); // dexterity bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusInt) != 0)
|
|
{
|
|
list.Add(1060432, prop.ToString()); // intelligence bonus ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusHits) != 0)
|
|
{
|
|
list.Add(1060431, prop.ToString()); // hit point increase ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusStam) != 0)
|
|
{
|
|
list.Add(1060484, prop.ToString()); // stamina increase ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.BonusMana) != 0)
|
|
{
|
|
list.Add(1060439, prop.ToString()); // mana increase ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.RegenHits) != 0)
|
|
{
|
|
list.Add(1060444, prop.ToString()); // hit point regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.RegenStam) != 0)
|
|
{
|
|
list.Add(1060443, prop.ToString()); // stamina regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.RegenMana) != 0)
|
|
{
|
|
list.Add(1060440, prop.ToString()); // mana regeneration ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.Luck) != 0)
|
|
{
|
|
list.Add(1060436, prop.ToString()); // luck ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.EnhancePotions) != 0)
|
|
{
|
|
list.Add(1060411, prop.ToString()); // enhance potions ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.ReflectPhysical) != 0)
|
|
{
|
|
list.Add(1060442, prop.ToString()); // reflect physical damage ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.AttackChance) != 0)
|
|
{
|
|
list.Add(1060415, prop.ToString()); // hit chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.WeaponSpeed) != 0)
|
|
{
|
|
list.Add(1060486, prop.ToString()); // swing speed increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.WeaponDamage) != 0)
|
|
{
|
|
list.Add(1060401, prop.ToString()); // damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.DefendChance) != 0)
|
|
{
|
|
list.Add(1060408, prop.ToString()); // defense chance increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.CastRecovery) != 0)
|
|
{
|
|
list.Add(1060412, prop.ToString()); // faster cast recovery ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.CastSpeed) != 0)
|
|
{
|
|
list.Add(1060413, prop.ToString()); // faster casting ~1_val~
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.SpellDamage) != 0)
|
|
{
|
|
list.Add(1060483, prop.ToString()); // spell damage increase ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.LowerManaCost) != 0)
|
|
{
|
|
list.Add(1060433, prop.ToString()); // lower mana cost ~1_val~%
|
|
}
|
|
|
|
if ((prop = m_AosAttributes.LowerRegCost) != 0)
|
|
{
|
|
list.Add(1060434, prop.ToString()); // lower reagent cost ~1_val~%
|
|
}
|
|
|
|
if (Core.ML && (prop = m_AosAttributes.IncreasedKarmaLoss) != 0)
|
|
{
|
|
list.Add(1075210, prop.ToString()); // Increased Karma Loss ~1val~%
|
|
}
|
|
|
|
AddProperty(list);
|
|
|
|
list.Add(1042886, m_Count.ToString()); // ~1_NUMBERS_OF_SPELLS~ Spells
|
|
|
|
if (this.m_MaxHitPoints > 0)
|
|
list.Add(1060639, "{0}\t{1}", this.m_HitPoints, this.m_MaxHitPoints); // durability ~1_val~ / ~2_val~
|
|
}
|
|
|
|
public virtual void AddProperty(ObjectPropertyList list)
|
|
{
|
|
}
|
|
|
|
public override void OnSingleClick(Mobile from)
|
|
{
|
|
base.OnSingleClick(from);
|
|
|
|
if (m_Crafter != null)
|
|
{
|
|
LabelTo(from, 1050043, m_Crafter.TitleName); // crafted by ~1_NAME~
|
|
}
|
|
|
|
LabelTo(from, 1042886, m_Count.ToString());
|
|
}
|
|
|
|
public override void OnDoubleClick(Mobile from)
|
|
{
|
|
Container pack = from.Backpack;
|
|
|
|
if (Parent == from || (pack != null && Parent == pack))
|
|
{
|
|
DisplayTo(from);
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage(500207);
|
|
// The spellbook must be in your backpack (and not in a container within) to open.
|
|
}
|
|
}
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.Write(6); // version
|
|
|
|
m_NegativeAttributes.Serialize(writer);
|
|
|
|
writer.Write(m_HitPoints);
|
|
writer.Write(m_MaxHitPoints);
|
|
|
|
writer.Write(_VvVItem);
|
|
writer.Write(_Owner);
|
|
writer.Write(_OwnerName);
|
|
|
|
writer.Write((byte)m_Quality);
|
|
|
|
writer.Write(m_EngravedText);
|
|
|
|
writer.Write(m_Crafter);
|
|
|
|
writer.Write((int)m_Slayer);
|
|
writer.Write((int)m_Slayer2);
|
|
|
|
m_AosAttributes.Serialize(writer);
|
|
m_AosSkillBonuses.Serialize(writer);
|
|
|
|
writer.Write(m_Content);
|
|
writer.Write(m_Count);
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadInt();
|
|
|
|
switch (version)
|
|
{
|
|
case 6:
|
|
{
|
|
m_NegativeAttributes = new NegativeAttributes(this, reader);
|
|
|
|
|
|
m_MaxHitPoints = reader.ReadInt();
|
|
m_HitPoints = reader.ReadInt();
|
|
|
|
_VvVItem = reader.ReadBool();
|
|
_Owner = reader.ReadMobile();
|
|
_OwnerName = reader.ReadString();
|
|
|
|
goto case 5;
|
|
}
|
|
case 5:
|
|
{
|
|
m_Quality = (BookQuality)reader.ReadByte();
|
|
|
|
goto case 4;
|
|
}
|
|
case 4:
|
|
{
|
|
m_EngravedText = reader.ReadString();
|
|
|
|
goto case 3;
|
|
}
|
|
case 3:
|
|
{
|
|
m_Crafter = reader.ReadMobile();
|
|
goto case 2;
|
|
}
|
|
case 2:
|
|
{
|
|
m_Slayer = (SlayerName)reader.ReadInt();
|
|
m_Slayer2 = (SlayerName)reader.ReadInt();
|
|
goto case 1;
|
|
}
|
|
case 1:
|
|
{
|
|
m_AosAttributes = new AosAttributes(this, reader);
|
|
m_AosSkillBonuses = new AosSkillBonuses(this, reader);
|
|
|
|
goto case 0;
|
|
}
|
|
case 0:
|
|
{
|
|
m_Content = reader.ReadULong();
|
|
m_Count = reader.ReadInt();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_AosAttributes == null)
|
|
{
|
|
m_AosAttributes = new AosAttributes(this);
|
|
}
|
|
|
|
if (m_AosSkillBonuses == null)
|
|
{
|
|
m_AosSkillBonuses = new AosSkillBonuses(this);
|
|
}
|
|
|
|
if (m_NegativeAttributes == null)
|
|
{
|
|
m_NegativeAttributes = new NegativeAttributes(this);
|
|
}
|
|
|
|
if (Core.AOS && Parent is Mobile)
|
|
{
|
|
m_AosSkillBonuses.AddTo((Mobile)Parent);
|
|
}
|
|
|
|
int strBonus = m_AosAttributes.BonusStr;
|
|
int dexBonus = m_AosAttributes.BonusDex;
|
|
int intBonus = m_AosAttributes.BonusInt;
|
|
|
|
if (Parent is Mobile && (strBonus != 0 || dexBonus != 0 || intBonus != 0))
|
|
{
|
|
Mobile m = (Mobile)Parent;
|
|
|
|
string modName = Serial.ToString();
|
|
|
|
if (strBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Str, modName + "Str", strBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (dexBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero));
|
|
}
|
|
|
|
if (intBonus != 0)
|
|
{
|
|
m.AddStatMod(new StatMod(StatType.Int, modName + "Int", intBonus, TimeSpan.Zero));
|
|
}
|
|
}
|
|
|
|
if (Parent is Mobile)
|
|
{
|
|
((Mobile)Parent).CheckStatTimers();
|
|
}
|
|
}
|
|
|
|
public virtual int OnCraft(
|
|
int quality,
|
|
bool makersMark,
|
|
Mobile from,
|
|
CraftSystem craftSystem,
|
|
Type typeRes,
|
|
ITool tool,
|
|
CraftItem craftItem,
|
|
int resHue)
|
|
{
|
|
int magery = from.Skills.Magery.BaseFixedPoint;
|
|
|
|
if (magery >= 800)
|
|
{
|
|
int[] propertyCounts;
|
|
int minIntensity;
|
|
int maxIntensity;
|
|
|
|
if (magery >= 1000)
|
|
{
|
|
if (magery >= 1200)
|
|
{
|
|
propertyCounts = m_LegendPropertyCounts;
|
|
}
|
|
else if (magery >= 1100)
|
|
{
|
|
propertyCounts = m_ElderPropertyCounts;
|
|
}
|
|
else
|
|
{
|
|
propertyCounts = m_GrandPropertyCounts;
|
|
}
|
|
|
|
minIntensity = 55;
|
|
maxIntensity = 75;
|
|
}
|
|
else if (magery >= 900)
|
|
{
|
|
propertyCounts = m_MasterPropertyCounts;
|
|
minIntensity = 25;
|
|
maxIntensity = 45;
|
|
}
|
|
else
|
|
{
|
|
propertyCounts = m_AdeptPropertyCounts;
|
|
minIntensity = 0;
|
|
maxIntensity = 15;
|
|
}
|
|
|
|
int propertyCount = propertyCounts[Utility.Random(propertyCounts.Length)];
|
|
|
|
GuaranteedSpellbookImprovementTalisman talisman = from.FindItemOnLayer(Layer.Talisman) as GuaranteedSpellbookImprovementTalisman;
|
|
|
|
if (talisman != null && talisman.Charges > 0)
|
|
{
|
|
propertyCount++;
|
|
talisman.Charges--;
|
|
|
|
from.SendLocalizedMessage(1157210); // Your talisman magically improves your spellbook.
|
|
|
|
if (talisman.Charges <= 0)
|
|
{
|
|
from.SendLocalizedMessage(1157211); // Your talisman has been destroyed.
|
|
talisman.Delete();
|
|
}
|
|
}
|
|
|
|
BaseRunicTool.ApplyAttributesTo(this, true, 0, propertyCount, minIntensity, maxIntensity);
|
|
}
|
|
|
|
if (makersMark)
|
|
{
|
|
Crafter = from;
|
|
}
|
|
|
|
m_Quality = (BookQuality)(quality - 1);
|
|
|
|
return quality;
|
|
}
|
|
|
|
[Usage("AllSpells")]
|
|
[Description("Completely fills a targeted spellbook with scrolls.")]
|
|
private static void AllSpells_OnCommand(CommandEventArgs e)
|
|
{
|
|
e.Mobile.BeginTarget(-1, false, TargetFlags.None, AllSpells_OnTarget);
|
|
e.Mobile.SendMessage("Target the spellbook to fill.");
|
|
}
|
|
|
|
private static void AllSpells_OnTarget(Mobile from, object obj)
|
|
{
|
|
if (obj is Spellbook)
|
|
{
|
|
Spellbook book = (Spellbook)obj;
|
|
|
|
if (book.BookCount == 64)
|
|
{
|
|
book.Content = ulong.MaxValue;
|
|
}
|
|
else
|
|
{
|
|
book.Content = (1ul << book.BookCount) - 1;
|
|
}
|
|
|
|
from.SendMessage("The spellbook has been filled.");
|
|
|
|
CommandLogging.WriteLine(
|
|
from, "{0} {1} filling spellbook {2}", from.AccessLevel, CommandLogging.Format(from), CommandLogging.Format(book));
|
|
}
|
|
else
|
|
{
|
|
from.BeginTarget(-1, false, TargetFlags.None, AllSpells_OnTarget);
|
|
from.SendMessage("That is not a spellbook. Try again.");
|
|
}
|
|
}
|
|
|
|
private static void EventSink_OpenSpellbookRequest(OpenSpellbookRequestEventArgs e)
|
|
{
|
|
Mobile from = e.Mobile;
|
|
|
|
if (!DesignContext.Check(from))
|
|
{
|
|
return; // They are customizing
|
|
}
|
|
|
|
SpellbookType type;
|
|
|
|
switch (e.Type)
|
|
{
|
|
default:
|
|
case 1:
|
|
type = SpellbookType.Regular;
|
|
break;
|
|
case 2:
|
|
type = SpellbookType.Necromancer;
|
|
break;
|
|
case 3:
|
|
type = SpellbookType.Paladin;
|
|
break;
|
|
case 4:
|
|
type = SpellbookType.Ninja;
|
|
break;
|
|
case 5:
|
|
type = SpellbookType.Samurai;
|
|
break;
|
|
case 6:
|
|
type = SpellbookType.Arcanist;
|
|
break;
|
|
case 7:
|
|
type = SpellbookType.Mystic;
|
|
break;
|
|
}
|
|
|
|
Spellbook book = Find(from, -1, type);
|
|
|
|
if (book != null)
|
|
{
|
|
book.DisplayTo(from);
|
|
}
|
|
}
|
|
|
|
private static void EventSink_CastSpellRequest(CastSpellRequestEventArgs e)
|
|
{
|
|
Mobile from = e.Mobile;
|
|
|
|
if (!DesignContext.Check(from))
|
|
{
|
|
return; // They are customizing
|
|
}
|
|
|
|
Spellbook book = e.Spellbook as Spellbook;
|
|
int spellID = e.SpellID;
|
|
|
|
if (book == null || !book.HasSpell(spellID))
|
|
{
|
|
book = Find(from, spellID);
|
|
}
|
|
|
|
if (book != null && book.HasSpell(spellID))
|
|
{
|
|
SpecialMove move = SpellRegistry.GetSpecialMove(spellID);
|
|
|
|
if (move != null)
|
|
{
|
|
SpecialMove.SetCurrentMove(from, move);
|
|
}
|
|
else
|
|
{
|
|
Spell spell = SpellRegistry.NewSpell(spellID, from, null);
|
|
|
|
if (spell != null)
|
|
{
|
|
spell.Cast();
|
|
}
|
|
else if ( !Server.Spells.SkillMasteries.MasteryInfo.IsPassiveMastery( spellID ) )
|
|
{
|
|
from.SendLocalizedMessage( 502345 ); // This spell has been temporarily disabled.
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage(500015); // You do not have that spell!
|
|
}
|
|
}
|
|
}
|
|
}
|