Convert movement, actions (lift/use), combat, and spells to use Core.TickCount and avoid DateTime caveats (performance, system time dependency, etc) Refactor DateTime.Now to DateTime.UtcNow Add LOS check for Iron Maiden addon
558 lines
No EOL
12 KiB
C#
558 lines
No EOL
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Gumps;
|
|
using Server.Network;
|
|
using Server.Mobiles;
|
|
using Server.Multis;
|
|
using Server.Engines.Craft;
|
|
using Server.ContextMenus;
|
|
|
|
namespace Server.Items
|
|
{
|
|
public class Runebook : Item, ISecurable, ICraftable
|
|
{
|
|
public static readonly TimeSpan UseDelay = TimeSpan.FromSeconds( 7.0 );
|
|
|
|
private BookQuality m_Quality;
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public BookQuality Quality
|
|
{
|
|
get{ return m_Quality; }
|
|
set{ m_Quality = value; InvalidateProperties(); }
|
|
}
|
|
|
|
private List<RunebookEntry> m_Entries;
|
|
private string m_Description;
|
|
private int m_CurCharges, m_MaxCharges;
|
|
private int m_DefaultIndex;
|
|
private SecureLevel m_Level;
|
|
private Mobile m_Crafter;
|
|
|
|
private DateTime m_NextUse;
|
|
|
|
private List<Mobile> m_Openers = new List<Mobile>();
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public DateTime NextUse
|
|
{
|
|
get{ return m_NextUse; }
|
|
set{ m_NextUse = value; }
|
|
}
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public Mobile Crafter
|
|
{
|
|
get{ return m_Crafter; }
|
|
set{ m_Crafter = value; InvalidateProperties(); }
|
|
}
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public SecureLevel Level
|
|
{
|
|
get{ return m_Level; }
|
|
set{ m_Level = value; }
|
|
}
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public string Description
|
|
{
|
|
get
|
|
{
|
|
return m_Description;
|
|
}
|
|
set
|
|
{
|
|
m_Description = value;
|
|
InvalidateProperties();
|
|
}
|
|
}
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public int CurCharges
|
|
{
|
|
get
|
|
{
|
|
return m_CurCharges;
|
|
}
|
|
set
|
|
{
|
|
m_CurCharges = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty( AccessLevel.GameMaster )]
|
|
public int MaxCharges
|
|
{
|
|
get
|
|
{
|
|
return m_MaxCharges;
|
|
}
|
|
set
|
|
{
|
|
m_MaxCharges = value;
|
|
}
|
|
}
|
|
|
|
public List<Mobile> Openers
|
|
{
|
|
get
|
|
{
|
|
return m_Openers;
|
|
}
|
|
set
|
|
{
|
|
m_Openers = value;
|
|
}
|
|
}
|
|
|
|
public override int LabelNumber{ get{ return 1041267; } } // runebook
|
|
|
|
[Constructable]
|
|
public Runebook( int maxCharges ) : base( Core.AOS ? 0x22C5 : 0xEFA )
|
|
{
|
|
Weight = (Core.SE ? 1.0 : 3.0);
|
|
LootType = LootType.Blessed;
|
|
Hue = 0x461;
|
|
|
|
Layer = (Core.AOS ? Layer.Invalid : Layer.OneHanded);
|
|
|
|
m_Entries = new List<RunebookEntry>();
|
|
|
|
m_MaxCharges = maxCharges;
|
|
|
|
m_DefaultIndex = -1;
|
|
|
|
m_Level = SecureLevel.CoOwners;
|
|
}
|
|
|
|
[Constructable]
|
|
public Runebook() : this( Core.SE ? 12 : 6 )
|
|
{
|
|
}
|
|
|
|
public List<RunebookEntry> Entries
|
|
{
|
|
get
|
|
{
|
|
return m_Entries;
|
|
}
|
|
}
|
|
|
|
public RunebookEntry Default
|
|
{
|
|
get
|
|
{
|
|
if ( m_DefaultIndex >= 0 && m_DefaultIndex < m_Entries.Count )
|
|
return m_Entries[m_DefaultIndex];
|
|
|
|
return null;
|
|
}
|
|
set
|
|
{
|
|
if ( value == null )
|
|
m_DefaultIndex = -1;
|
|
else
|
|
m_DefaultIndex = m_Entries.IndexOf( value );
|
|
}
|
|
}
|
|
|
|
public Runebook( Serial serial ) : base( serial )
|
|
{
|
|
}
|
|
|
|
public override bool AllowEquipedCast( Mobile from )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override void GetContextMenuEntries( Mobile from, List<ContextMenuEntry> list )
|
|
{
|
|
base.GetContextMenuEntries( from, list );
|
|
SetSecureLevelEntry.AddTo( from, this, list );
|
|
}
|
|
|
|
public override void Serialize( GenericWriter writer )
|
|
{
|
|
base.Serialize( writer );
|
|
|
|
writer.Write( (int) 3 );
|
|
|
|
writer.Write( (byte) m_Quality );
|
|
|
|
writer.Write( m_Crafter );
|
|
|
|
writer.Write( (int) m_Level );
|
|
|
|
writer.Write( m_Entries.Count );
|
|
|
|
for ( int i = 0; i < m_Entries.Count; ++i )
|
|
m_Entries[i].Serialize( writer );
|
|
|
|
writer.Write( m_Description );
|
|
writer.Write( m_CurCharges );
|
|
writer.Write( m_MaxCharges );
|
|
writer.Write( m_DefaultIndex );
|
|
}
|
|
|
|
public override void Deserialize( GenericReader reader )
|
|
{
|
|
base.Deserialize( reader );
|
|
|
|
LootType = LootType.Blessed;
|
|
|
|
if( Core.SE && Weight == 3.0 )
|
|
Weight = 1.0;
|
|
|
|
int version = reader.ReadInt();
|
|
|
|
switch ( version )
|
|
{
|
|
case 3:
|
|
{
|
|
m_Quality = (BookQuality) reader.ReadByte();
|
|
goto case 2;
|
|
}
|
|
case 2:
|
|
{
|
|
m_Crafter = reader.ReadMobile();
|
|
goto case 1;
|
|
}
|
|
case 1:
|
|
{
|
|
m_Level = (SecureLevel)reader.ReadInt();
|
|
goto case 0;
|
|
}
|
|
case 0:
|
|
{
|
|
int count = reader.ReadInt();
|
|
|
|
m_Entries = new List<RunebookEntry>( count );
|
|
|
|
for ( int i = 0; i < count; ++i )
|
|
m_Entries.Add( new RunebookEntry( reader ) );
|
|
|
|
m_Description = reader.ReadString();
|
|
m_CurCharges = reader.ReadInt();
|
|
m_MaxCharges = reader.ReadInt();
|
|
m_DefaultIndex = reader.ReadInt();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DropRune( Mobile from, RunebookEntry e, int index )
|
|
{
|
|
if ( m_DefaultIndex > index )
|
|
m_DefaultIndex -= 1;
|
|
else if ( m_DefaultIndex == index )
|
|
m_DefaultIndex = -1;
|
|
|
|
m_Entries.RemoveAt( index );
|
|
|
|
RecallRune rune = new RecallRune();
|
|
|
|
rune.Target = e.Location;
|
|
rune.TargetMap = e.Map;
|
|
rune.Description = e.Description;
|
|
rune.House = e.House;
|
|
rune.Marked = true;
|
|
|
|
from.AddToBackpack( rune );
|
|
|
|
from.SendLocalizedMessage( 502421 ); // You have removed the rune.
|
|
}
|
|
|
|
public bool IsOpen( Mobile toCheck )
|
|
{
|
|
NetState ns = toCheck.NetState;
|
|
|
|
if ( ns != null ) {
|
|
foreach ( Gump gump in ns.Gumps ) {
|
|
RunebookGump bookGump = gump as RunebookGump;
|
|
|
|
if ( bookGump != null && bookGump.Book == this ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override bool DisplayLootType{ get{ return Core.AOS; } }
|
|
|
|
public override void GetProperties( ObjectPropertyList list )
|
|
{
|
|
base.GetProperties( list );
|
|
|
|
if ( m_Quality == BookQuality.Exceptional )
|
|
list.Add( 1063341 ); // exceptional
|
|
|
|
if ( m_Crafter != null )
|
|
list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~
|
|
|
|
if ( m_Description != null && m_Description.Length > 0 )
|
|
list.Add( m_Description );
|
|
}
|
|
|
|
public override bool OnDragLift( Mobile from )
|
|
{
|
|
if ( from.HasGump( typeof( RunebookGump ) ) )
|
|
{
|
|
from.SendLocalizedMessage( 500169 ); // You cannot pick that up.
|
|
return false;
|
|
}
|
|
|
|
foreach ( Mobile m in m_Openers )
|
|
if ( IsOpen( m ) )
|
|
m.CloseGump( typeof( RunebookGump ) );
|
|
|
|
m_Openers.Clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
public override void OnSingleClick( Mobile from )
|
|
{
|
|
if ( m_Description != null && m_Description.Length > 0 )
|
|
LabelTo( from, m_Description );
|
|
|
|
base.OnSingleClick( from );
|
|
|
|
if ( m_Crafter != null )
|
|
LabelTo( from, 1050043, m_Crafter.Name );
|
|
}
|
|
|
|
public override void OnDoubleClick( Mobile from )
|
|
{
|
|
if ( from.InRange( GetWorldLocation(), (Core.ML ? 3 : 1) ) && CheckAccess( from ) )
|
|
{
|
|
if ( RootParent is BaseCreature )
|
|
{
|
|
from.SendLocalizedMessage( 502402 ); // That is inaccessible.
|
|
return;
|
|
}
|
|
|
|
if ( DateTime.UtcNow < m_NextUse )
|
|
{
|
|
from.SendLocalizedMessage( 502406 ); // This book needs time to recharge.
|
|
return;
|
|
}
|
|
|
|
from.CloseGump( typeof( RunebookGump ) );
|
|
from.SendGump( new RunebookGump( from, this ) );
|
|
|
|
m_Openers.Add( from );
|
|
}
|
|
}
|
|
|
|
public virtual void OnTravel()
|
|
{
|
|
if ( !Core.SA )
|
|
m_NextUse = DateTime.UtcNow + UseDelay;
|
|
}
|
|
|
|
public override void OnAfterDuped( Item newItem )
|
|
{
|
|
Runebook book = newItem as Runebook;
|
|
|
|
if ( book == null )
|
|
return;
|
|
|
|
book.m_Entries = new List<RunebookEntry>();
|
|
|
|
for ( int i = 0; i < m_Entries.Count; i++ )
|
|
{
|
|
RunebookEntry entry = m_Entries[i];
|
|
|
|
book.m_Entries.Add( new RunebookEntry( entry.Location, entry.Map, entry.Description, entry.House ) );
|
|
}
|
|
}
|
|
|
|
public bool CheckAccess( Mobile m )
|
|
{
|
|
if ( !IsLockedDown || m.AccessLevel >= AccessLevel.GameMaster )
|
|
return true;
|
|
|
|
BaseHouse house = BaseHouse.FindHouseAt( this );
|
|
|
|
if ( house != null && house.IsAosRules && (house.Public ? house.IsBanned( m ) : !house.HasAccess( m )) )
|
|
return false;
|
|
|
|
return ( house != null && house.HasSecureAccess( m, m_Level ) );
|
|
}
|
|
|
|
public override bool OnDragDrop( Mobile from, Item dropped )
|
|
{
|
|
if ( dropped is RecallRune )
|
|
{
|
|
if ( IsLockedDown && from.AccessLevel < AccessLevel.GameMaster )
|
|
{
|
|
from.SendLocalizedMessage( 502413, null, 0x35 ); // That cannot be done while the book is locked down.
|
|
}
|
|
else if ( IsOpen( from ) )
|
|
{
|
|
from.SendLocalizedMessage( 1005571 ); // You cannot place objects in the book while viewing the contents.
|
|
}
|
|
else if ( m_Entries.Count < 16 )
|
|
{
|
|
RecallRune rune = (RecallRune)dropped;
|
|
|
|
if ( rune.Marked && rune.TargetMap != null )
|
|
{
|
|
m_Entries.Add( new RunebookEntry( rune.Target, rune.TargetMap, rune.Description, rune.House ) );
|
|
|
|
dropped.Delete();
|
|
|
|
from.Send( new PlaySound( 0x42, GetWorldLocation() ) );
|
|
|
|
string desc = rune.Description;
|
|
|
|
if ( desc == null || (desc = desc.Trim()).Length == 0 )
|
|
desc = "(indescript)";
|
|
|
|
from.SendMessage( desc );
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage( 502409 ); // This rune does not have a marked location.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage( 502401 ); // This runebook is full.
|
|
}
|
|
}
|
|
else if ( dropped is RecallScroll )
|
|
{
|
|
if ( m_CurCharges < m_MaxCharges )
|
|
{
|
|
from.Send( new PlaySound( 0x249, GetWorldLocation() ) );
|
|
|
|
int amount = dropped.Amount;
|
|
|
|
if ( amount > (m_MaxCharges - m_CurCharges) )
|
|
{
|
|
dropped.Consume( m_MaxCharges - m_CurCharges );
|
|
m_CurCharges = m_MaxCharges;
|
|
}
|
|
else
|
|
{
|
|
m_CurCharges += amount;
|
|
dropped.Delete();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage( 502410 ); // This book already has the maximum amount of charges.
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#region ICraftable Members
|
|
|
|
public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue )
|
|
{
|
|
int charges = 5 + quality + (int)(from.Skills[SkillName.Inscribe].Value / 30);
|
|
|
|
if ( charges > 10 )
|
|
charges = 10;
|
|
|
|
MaxCharges = (Core.SE ? charges * 2 : charges);
|
|
|
|
if ( makersMark )
|
|
Crafter = from;
|
|
|
|
m_Quality = (BookQuality) ( quality - 1 );
|
|
|
|
return quality;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class RunebookEntry
|
|
{
|
|
private Point3D m_Location;
|
|
private Map m_Map;
|
|
private string m_Description;
|
|
private BaseHouse m_House;
|
|
|
|
public Point3D Location
|
|
{
|
|
get{ return m_Location; }
|
|
}
|
|
|
|
public Map Map
|
|
{
|
|
get{ return m_Map; }
|
|
}
|
|
|
|
public string Description
|
|
{
|
|
get{ return m_Description; }
|
|
}
|
|
|
|
public BaseHouse House
|
|
{
|
|
get{ return m_House; }
|
|
}
|
|
|
|
public RunebookEntry( Point3D loc, Map map, string desc, BaseHouse house )
|
|
{
|
|
m_Location = loc;
|
|
m_Map = map;
|
|
m_Description = desc;
|
|
m_House = house;
|
|
}
|
|
|
|
public RunebookEntry( GenericReader reader )
|
|
{
|
|
int version = reader.ReadByte();
|
|
|
|
switch ( version )
|
|
{
|
|
case 1:
|
|
{
|
|
m_House = reader.ReadItem() as BaseHouse;
|
|
goto case 0;
|
|
}
|
|
case 0:
|
|
{
|
|
m_Location = reader.ReadPoint3D();
|
|
m_Map = reader.ReadMap();
|
|
m_Description = reader.ReadString();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Serialize( GenericWriter writer )
|
|
{
|
|
if ( m_House != null && !m_House.Deleted )
|
|
{
|
|
writer.Write( (byte) 1 ); // version
|
|
|
|
writer.Write( m_House );
|
|
}
|
|
else
|
|
{
|
|
writer.Write( (byte) 0 ); // version
|
|
}
|
|
|
|
writer.Write( m_Location );
|
|
writer.Write( m_Map );
|
|
writer.Write( m_Description );
|
|
}
|
|
}
|
|
} |