2229 lines
75 KiB
C#
2229 lines
75 KiB
C#
using System;
|
|
using System.Data;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using Server;
|
|
using Server.Items;
|
|
using Server.Network;
|
|
using Server.Gumps;
|
|
using Server.Targeting;
|
|
using System.Reflection;
|
|
using Server.Commands;
|
|
using CPA = Server.CommandPropertyAttribute;
|
|
using System.Xml;
|
|
using Server.Spells;
|
|
using System.Text;
|
|
using Server.Accounting;
|
|
using System.Diagnostics;
|
|
using System.Text.RegularExpressions;
|
|
using Server.Mobiles;
|
|
|
|
namespace Server.Engines.XmlSpawner2
|
|
{
|
|
public class XmlDialog : XmlAttachment
|
|
{
|
|
|
|
|
|
// speech entry
|
|
public class SpeechEntry
|
|
{
|
|
public int EntryNumber;
|
|
private int m_ID;
|
|
public string Text; // text displayed when the entry is activated
|
|
public string Keywords; // comma separated list of keywords that can be matched to activate the entry. If no keywords are present then it is automatically activated
|
|
public string Action; // action string
|
|
public string Condition; // condition test string
|
|
public string DependsOn; // the previous entrynumber required to activate this entry
|
|
public int Pause = 1; // pause in seconds before advancing to the next entry
|
|
public int PrePause = -1; // pause in seconds before saying the speech for this entry. -1 indicates the use of auto pause calculation based on triggering speech length.
|
|
public bool LockConversation = true; // flag to determine if the conversation locks to one player
|
|
public bool AllowNPCTrigger = false; // flag to determine if npc speech can trigger it
|
|
public MessageType SpeechStyle = MessageType.Regular;
|
|
public string Gump; // GUMP specification string
|
|
public int m_SpeechHue = -1; // speech hue
|
|
public bool IgnoreCarried = false; // ignore the TriggerOnCarried/NoTriggerOnCarried settings for the dialog when activating this entry
|
|
|
|
public int SpeechHue
|
|
{
|
|
get { return m_SpeechHue; }
|
|
set
|
|
{
|
|
// dont allow invalid hues
|
|
m_SpeechHue = value;
|
|
if (m_SpeechHue > 37852) m_SpeechHue = 37852;
|
|
}
|
|
}
|
|
|
|
public int ID
|
|
{
|
|
get { return m_ID; }
|
|
set
|
|
{
|
|
// dont allow ID modification of entry 0
|
|
if (EntryNumber == 0) return;
|
|
m_ID = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static int defProximityRange = 3;
|
|
public static int defResetRange = 16;
|
|
public static TimeSpan defResetTime = TimeSpan.FromSeconds(60);
|
|
public static int defSpeechPace = 10;
|
|
|
|
private const string NPCDataSetName = "XmlQuestNPC";
|
|
private const string NPCPointName = "NPC";
|
|
private const string SpeechPointName = "SpeechEntry";
|
|
public static string DefsDir = "XmlQuestNPC";
|
|
|
|
private ArrayList m_SpeechEntries = new ArrayList(); // contains the list of speech entries
|
|
private int m_CurrentEntryNumber = -1; // used to determine which entry will be subject to modification by various entry editing calls
|
|
private SpeechEntry m_CurrentEntry;
|
|
private bool m_Running = true;
|
|
private int m_ProximityRange = defProximityRange;
|
|
private bool m_AllowGhostTriggering = false;
|
|
private AccessLevel m_TriggerAccessLevel = AccessLevel.Player;
|
|
private DateTime m_LastInteraction;
|
|
private TimeSpan m_ResetTime = defResetTime;
|
|
private int m_ResetRange = defResetRange;
|
|
private bool m_IsActive = false;
|
|
private InternalTimer m_Timer;
|
|
private string m_ConfigFile;
|
|
private Mobile m_ActivePlayer; // keep track of the player that is currently engaged in conversation so that other players speech can be ignored.
|
|
private int m_SpeechPace = defSpeechPace; // used for automatic prepause delay calculation. delayinsecs = speechlength/speechpace + 1
|
|
bool m_HoldProcessing;
|
|
private string m_ItemTriggerName;
|
|
private string m_NoItemTriggerName;
|
|
public ArrayList m_TextEntryBook;
|
|
private string m_ResponseString;
|
|
|
|
public string ResponseString
|
|
{
|
|
get { return m_ResponseString; }
|
|
set { m_ResponseString = value; }
|
|
}
|
|
|
|
public ArrayList SpeechEntries
|
|
{
|
|
get
|
|
{
|
|
return m_SpeechEntries;
|
|
}
|
|
set
|
|
{
|
|
m_SpeechEntries = value;
|
|
}
|
|
}
|
|
|
|
public Mobile ActivePlayer
|
|
{
|
|
get { return m_ActivePlayer; }
|
|
set { m_ActivePlayer = value; }
|
|
}
|
|
|
|
|
|
|
|
// a serial constructor is REQUIRED
|
|
public XmlDialog(ASerial serial)
|
|
: base(serial)
|
|
{
|
|
}
|
|
|
|
[Attachable]
|
|
public XmlDialog(string ConfigFile)
|
|
{
|
|
DoLoadNPC(null, ConfigFile);
|
|
}
|
|
|
|
[Attachable]
|
|
public XmlDialog()
|
|
{
|
|
EntryNumber = 0;
|
|
}
|
|
|
|
public void DeleteTextEntryBook()
|
|
{
|
|
if (m_TextEntryBook != null)
|
|
{
|
|
foreach (Item s in m_TextEntryBook)
|
|
s.Delete();
|
|
|
|
m_TextEntryBook = null;
|
|
}
|
|
}
|
|
|
|
private SpeechEntry GetEntry(int entryid)
|
|
{
|
|
if (entryid < 0) return null;
|
|
if (m_SpeechEntries == null)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
}
|
|
// find the speech entry that matches the current entry number
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
if (s.EntryNumber == entryid)
|
|
return s;
|
|
}
|
|
// didnt find it so make a new entry
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
newentry.EntryNumber = entryid;
|
|
newentry.ID = entryid;
|
|
m_SpeechEntries.Add(newentry);
|
|
return newentry;
|
|
}
|
|
|
|
private bool ValidMovementTrig(Mobile m)
|
|
{
|
|
if (m == null || m.Deleted) return false;
|
|
|
|
return (
|
|
((m is PlayerMobile && (m.AccessLevel <= TriggerAccessLevel))) &&
|
|
((!m.Body.IsGhost && !m_AllowGhostTriggering) || (m.Body.IsGhost && m_AllowGhostTriggering)));
|
|
}
|
|
|
|
private bool ValidSpeechTrig(Mobile m)
|
|
{
|
|
if (m == null || m.Deleted) return false;
|
|
|
|
bool allownpctrigger = false;
|
|
if (CurrentEntry != null)
|
|
{
|
|
allownpctrigger = CurrentEntry.AllowNPCTrigger;
|
|
}
|
|
|
|
return (
|
|
((m is PlayerMobile && (m.AccessLevel <= TriggerAccessLevel)) || (allownpctrigger && !(m is PlayerMobile))) &&
|
|
((!m.Body.IsGhost && !m_AllowGhostTriggering) || (m.Body.IsGhost && m_AllowGhostTriggering)));
|
|
}
|
|
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public AccessLevel TriggerAccessLevel
|
|
{
|
|
get
|
|
{
|
|
return m_TriggerAccessLevel;
|
|
}
|
|
set
|
|
{
|
|
m_TriggerAccessLevel = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime LastInteraction
|
|
{
|
|
get
|
|
{
|
|
return m_LastInteraction;
|
|
}
|
|
set
|
|
{
|
|
m_LastInteraction = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool DoReset
|
|
{
|
|
get { return false; }
|
|
set
|
|
{
|
|
if (value) Reset();
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool IsActive
|
|
{
|
|
get
|
|
{
|
|
return m_IsActive;
|
|
}
|
|
set
|
|
{
|
|
m_IsActive = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public TimeSpan GameTOD
|
|
{
|
|
get
|
|
{
|
|
int hours;
|
|
int minutes;
|
|
Map map = null;
|
|
int x = 0;
|
|
int y = 0;
|
|
if (AttachedTo is Item)
|
|
{
|
|
map = ((Item)AttachedTo).Map;
|
|
x = ((Item)AttachedTo).Location.X;
|
|
y = ((Item)AttachedTo).Location.Y;
|
|
|
|
}
|
|
else
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
map = ((Mobile)AttachedTo).Map;
|
|
x = ((Mobile)AttachedTo).Location.X;
|
|
y = ((Mobile)AttachedTo).Location.Y;
|
|
}
|
|
|
|
Server.Items.Clock.GetTime(map, x, y, out hours, out minutes);
|
|
return (new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, hours, minutes, 0).TimeOfDay);
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public TimeSpan RealTOD
|
|
{
|
|
get
|
|
{
|
|
return DateTime.UtcNow.TimeOfDay;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int RealDay
|
|
{
|
|
get
|
|
{
|
|
return DateTime.UtcNow.Day;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int RealMonth
|
|
{
|
|
get
|
|
{
|
|
return DateTime.UtcNow.Month;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DayOfWeek RealDayOfWeek
|
|
{
|
|
get
|
|
{
|
|
return DateTime.UtcNow.DayOfWeek;
|
|
}
|
|
}
|
|
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public MoonPhase MoonPhase
|
|
{
|
|
get
|
|
{
|
|
Map map = null;
|
|
int x = 0;
|
|
int y = 0;
|
|
if (AttachedTo is Item)
|
|
{
|
|
map = ((Item)AttachedTo).Map;
|
|
x = ((Item)AttachedTo).Location.X;
|
|
y = ((Item)AttachedTo).Location.Y;
|
|
|
|
}
|
|
else
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
map = ((Mobile)AttachedTo).Map;
|
|
x = ((Mobile)AttachedTo).Location.X;
|
|
y = ((Mobile)AttachedTo).Location.Y;
|
|
}
|
|
return Clock.GetMoonPhase(map, x, y);
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool AllowGhostTrig
|
|
{
|
|
get { return m_AllowGhostTriggering; }
|
|
set
|
|
{
|
|
m_AllowGhostTriggering = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool Running
|
|
{
|
|
get { return m_Running; }
|
|
set { m_Running = value; }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public TimeSpan ResetTime
|
|
{
|
|
get { return m_ResetTime; }
|
|
set { m_ResetTime = value; }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SpeechPace
|
|
{
|
|
get { return m_SpeechPace; }
|
|
set { m_SpeechPace = value; }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string Keywords
|
|
{
|
|
get
|
|
{
|
|
// return the keyword string for the current entry
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return null;
|
|
}
|
|
return m_CurrentEntry.Keywords;
|
|
}
|
|
set
|
|
{
|
|
// set the keyword string for the current entry
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.Keywords = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string Action
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return null;
|
|
}
|
|
return m_CurrentEntry.Action;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.Action = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string Gump
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return null;
|
|
}
|
|
return m_CurrentEntry.Gump;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.Gump = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int SpeechHue
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return 0;
|
|
}
|
|
return m_CurrentEntry.SpeechHue;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.SpeechHue = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string Condition
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return null;
|
|
}
|
|
return m_CurrentEntry.Condition;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.Condition = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string Text
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return null;
|
|
}
|
|
return m_CurrentEntry.Text;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.Text = value;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string DependsOn
|
|
{
|
|
get
|
|
{
|
|
// return the keyword string for the current entry
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return "-1";
|
|
}
|
|
return m_CurrentEntry.DependsOn;
|
|
}
|
|
set
|
|
{
|
|
// set the keyword string for the current entry
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.DependsOn = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool LockConversation
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return true;
|
|
}
|
|
return m_CurrentEntry.LockConversation;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.LockConversation = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool IgnoreCarried
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return true;
|
|
}
|
|
return m_CurrentEntry.IgnoreCarried;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.IgnoreCarried = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public MessageType SpeechStyle
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return MessageType.Regular;
|
|
}
|
|
return m_CurrentEntry.SpeechStyle;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.SpeechStyle = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool AllowNPCTrigger
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return false;
|
|
}
|
|
return m_CurrentEntry.AllowNPCTrigger;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.AllowNPCTrigger = value;
|
|
}
|
|
}
|
|
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int Pause
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return -1;
|
|
}
|
|
return m_CurrentEntry.Pause;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.Pause = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int PrePause
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return -1;
|
|
}
|
|
return m_CurrentEntry.PrePause;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
m_CurrentEntry.PrePause = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int ID
|
|
{
|
|
get
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return -1;
|
|
}
|
|
return m_CurrentEntry.ID;
|
|
}
|
|
set
|
|
{
|
|
if (m_CurrentEntry == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
m_CurrentEntry.ID = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int EntryNumber
|
|
{
|
|
get { return m_CurrentEntryNumber; }
|
|
set
|
|
{
|
|
m_CurrentEntryNumber = value;
|
|
// get the entry corresponding to the number
|
|
m_CurrentEntry = GetEntry(m_CurrentEntryNumber);
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int ProximityRange
|
|
{
|
|
get { return m_ProximityRange; }
|
|
set
|
|
{
|
|
m_ProximityRange = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int ResetRange
|
|
{
|
|
get { return m_ResetRange; }
|
|
set
|
|
{
|
|
m_ResetRange = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string ConfigFile
|
|
{
|
|
get { return m_ConfigFile; }
|
|
set
|
|
{
|
|
m_ConfigFile = value;
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public bool LoadConfig
|
|
{
|
|
get { return false; }
|
|
set { if (value == true) DoLoadNPC(null, ConfigFile); }
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string TriggerOnCarried
|
|
{
|
|
get { return m_ItemTriggerName; }
|
|
set
|
|
{
|
|
m_ItemTriggerName = value;
|
|
}
|
|
|
|
}
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public string NoTriggerOnCarried
|
|
{
|
|
get { return m_NoItemTriggerName; }
|
|
set
|
|
{
|
|
m_NoItemTriggerName = value;
|
|
}
|
|
|
|
}
|
|
|
|
public SpeechEntry CurrentEntry
|
|
{
|
|
get { return m_CurrentEntry; }
|
|
set
|
|
{
|
|
|
|
// get the entry corresponding to the number
|
|
m_CurrentEntry = value;
|
|
if (m_CurrentEntry != null)
|
|
m_CurrentEntryNumber = m_CurrentEntry.EntryNumber;
|
|
else
|
|
m_CurrentEntryNumber = -1;
|
|
}
|
|
}
|
|
|
|
// see if the DependsOn property contains the specified id
|
|
public bool CheckDependsOn(SpeechEntry s, int id)
|
|
{
|
|
if (s == null || s.DependsOn == null) return false;
|
|
|
|
// parse the DependsOn string
|
|
string[] args = s.DependsOn.Split(',');
|
|
for (int i = 0; i < args.Length; i++)
|
|
{
|
|
try
|
|
{
|
|
if (int.Parse(args[i].Trim()) == id) return true;
|
|
}
|
|
catch { }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private SpeechEntry FindMatchingKeyword(Mobile from, string keyword, int currententryid)
|
|
{
|
|
if (m_SpeechEntries == null) return null;
|
|
ArrayList matchlist = new ArrayList();
|
|
|
|
// go through all of the speech entries and find those that depend on the current entry
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
// ignore self-referencing entries
|
|
if (CheckDependsOn(s, s.ID)) continue;
|
|
|
|
// start processing if set for spontaneous activation (banter), already active, or waiting in the default state
|
|
if (((CheckDependsOn(s, -1) || CheckDependsOn(s, -2)) && !IsActive) || (CheckDependsOn(s, currententryid) && (IsActive || currententryid == 0)))
|
|
{
|
|
// now check for any conditions as well
|
|
// check for any condition that must be met for this entry to be processed
|
|
if (s.Condition != null)
|
|
{
|
|
string status_str;
|
|
|
|
if (!BaseXmlSpawner.CheckPropertyString(null, this, s.Condition, from, out status_str))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// testing for keyword = null will handle calls from the OnTick
|
|
if ((keyword == null && s.Keywords == null))
|
|
{
|
|
// add it to the list of match candidates
|
|
matchlist.Add(s);
|
|
}
|
|
else
|
|
// parse the keyword string
|
|
if (keyword != null && s.Keywords != null)
|
|
{
|
|
|
|
string[] arglist = s.Keywords.Split(",".ToCharArray());
|
|
for (int i = 0; i < arglist.Length; i++)
|
|
{
|
|
if (arglist[i] == "*")
|
|
{
|
|
// special match anything expression (regex doesnt parse this well)
|
|
matchlist.Add(s);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
bool error = false;
|
|
Regex r = null;
|
|
string status_str = null;
|
|
try
|
|
{
|
|
r = new Regex(arglist[i], RegexOptions.IgnoreCase);
|
|
}
|
|
catch (Exception e) { error = true; status_str = e.Message; }
|
|
|
|
if (!error && r != null)
|
|
{
|
|
|
|
Match m = r.Match(keyword);
|
|
if (m.Success)
|
|
{
|
|
// add it to the list of match candidates
|
|
matchlist.Add(s);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReportError(from, String.Format("Bad regular expression: {0} ", status_str));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (matchlist.Count > 0)
|
|
{
|
|
// found at least one match
|
|
// if there is more than one, then randomly pick one
|
|
int select = Utility.Random(matchlist.Count);
|
|
|
|
return (SpeechEntry)matchlist[select];
|
|
}
|
|
else
|
|
{
|
|
// didnt find a match
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static void DialogGumpCallback(Mobile from, object invoker, string response)
|
|
{
|
|
// insert the response into the triggering speech of the invoking attachment
|
|
if (invoker is XmlDialog)
|
|
{
|
|
XmlDialog xd = (XmlDialog)invoker;
|
|
xd.m_HoldProcessing = false;
|
|
|
|
xd.ProcessSpeech(from, response);
|
|
}
|
|
}
|
|
|
|
private void ExecuteGump(Mobile mob, string gumpstring)
|
|
{
|
|
if (gumpstring == null || gumpstring.Length <= 0) return;
|
|
|
|
string status_str = null;
|
|
|
|
Server.Mobiles.XmlSpawner.SpawnObject TheSpawn = new Server.Mobiles.XmlSpawner.SpawnObject(null, 0);
|
|
|
|
TheSpawn.TypeName = gumpstring;
|
|
string substitutedtypeName = BaseXmlSpawner.ApplySubstitution(null, this, mob, gumpstring);
|
|
string typeName = BaseXmlSpawner.ParseObjectType(substitutedtypeName);
|
|
|
|
Point3D loc = new Point3D(0, 0, 0);
|
|
Map map = null;
|
|
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
Mobile m = AttachedTo as Mobile;
|
|
loc = m.Location;
|
|
map = m.Map;
|
|
}
|
|
else
|
|
if (AttachedTo is Item && ((Item)AttachedTo).Parent == null)
|
|
{
|
|
Item i = AttachedTo as Item;
|
|
loc = i.Location;
|
|
map = i.Map;
|
|
}
|
|
|
|
if (typeName == "GUMP")
|
|
{
|
|
BaseXmlSpawner.SpawnTypeKeyword(this, TheSpawn, typeName, substitutedtypeName, true, mob, loc, map, new XmlGumpCallback(DialogGumpCallback), out status_str, 0);
|
|
// hold processing until the gump response is completed
|
|
|
|
m_HoldProcessing = true;
|
|
}
|
|
else
|
|
{
|
|
status_str = "not a GUMP specification";
|
|
}
|
|
|
|
ReportError(mob, status_str);
|
|
}
|
|
|
|
private void ExecuteAction(Mobile mob, string action)
|
|
{
|
|
if (action == null || action.Length <= 0) return;
|
|
string status_str = null;
|
|
Server.Mobiles.XmlSpawner.SpawnObject TheSpawn = new Server.Mobiles.XmlSpawner.SpawnObject(null, 0);
|
|
|
|
TheSpawn.TypeName = action;
|
|
string substitutedtypeName = BaseXmlSpawner.ApplySubstitution(null, this, mob, action);
|
|
string typeName = BaseXmlSpawner.ParseObjectType(substitutedtypeName);
|
|
|
|
Point3D loc = new Point3D(0, 0, 0);
|
|
Map map = null;
|
|
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
Mobile m = AttachedTo as Mobile;
|
|
loc = m.Location;
|
|
map = m.Map;
|
|
}
|
|
else
|
|
if (AttachedTo is Item && ((Item)AttachedTo).Parent == null)
|
|
{
|
|
Item i = AttachedTo as Item;
|
|
loc = i.Location;
|
|
map = i.Map;
|
|
}
|
|
|
|
if (BaseXmlSpawner.IsTypeOrItemKeyword(typeName))
|
|
{
|
|
BaseXmlSpawner.SpawnTypeKeyword(AttachedTo, TheSpawn, typeName, substitutedtypeName, true, mob, loc, map, out status_str);
|
|
}
|
|
else
|
|
{
|
|
// its a regular type descriptor so find out what it is
|
|
Type type = SpawnerType.GetType(typeName);
|
|
try
|
|
{
|
|
string[] arglist = BaseXmlSpawner.ParseString(substitutedtypeName, 3, "/");
|
|
object o = Server.Mobiles.XmlSpawner.CreateObject(type, arglist[0]);
|
|
|
|
if (o == null)
|
|
{
|
|
status_str = "invalid type specification: " + arglist[0];
|
|
}
|
|
else
|
|
if (o is Mobile)
|
|
{
|
|
Mobile m = (Mobile)o;
|
|
if (m is BaseCreature)
|
|
{
|
|
BaseCreature c = (BaseCreature)m;
|
|
c.Home = loc; // Spawners location is the home point
|
|
}
|
|
|
|
m.Location = loc;
|
|
m.Map = map;
|
|
|
|
BaseXmlSpawner.ApplyObjectStringProperties(null, substitutedtypeName, m, mob, AttachedTo, out status_str);
|
|
}
|
|
else
|
|
if (o is Item)
|
|
{
|
|
Item item = (Item)o;
|
|
BaseXmlSpawner.AddSpawnItem(null, AttachedTo, TheSpawn, item, loc, map, mob, false, substitutedtypeName, out status_str);
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
ReportError(mob, status_str);
|
|
}
|
|
|
|
private void ReportError(Mobile mob, string status_str)
|
|
{
|
|
if (status_str != null && mob != null && !mob.Deleted && mob is PlayerMobile && mob.AccessLevel > AccessLevel.Player)
|
|
{
|
|
mob.SendMessage(33, String.Format("{0}:{1}", AttachedTo.GetType().Name, status_str));
|
|
}
|
|
}
|
|
|
|
public override bool HandlesOnSpeech
|
|
{
|
|
get { return (m_Running); }
|
|
}
|
|
|
|
|
|
public override void OnSpeech(SpeechEventArgs e)
|
|
{
|
|
|
|
if (e.Mobile == null) return;
|
|
|
|
// dont handle your own speech
|
|
if (e.Mobile == AttachedTo as Mobile || e.Mobile.AccessLevel > TriggerAccessLevel)
|
|
{
|
|
e.Handled = false;
|
|
return;
|
|
}
|
|
|
|
if (m_HoldProcessing) return;
|
|
|
|
bool lockconversation = true;
|
|
bool ishandled = false;
|
|
|
|
Point3D loc = new Point3D(0, 0, 0);
|
|
Map map;
|
|
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
Mobile m = AttachedTo as Mobile;
|
|
loc = m.Location;
|
|
map = m.Map;
|
|
}
|
|
else
|
|
if (AttachedTo is Item && ((Item)AttachedTo).Parent == null)
|
|
{
|
|
Item i = AttachedTo as Item;
|
|
loc = i.Location;
|
|
map = i.Map;
|
|
}
|
|
|
|
if (CurrentEntry != null)
|
|
{
|
|
lockconversation = CurrentEntry.LockConversation;
|
|
}
|
|
|
|
if (!e.Handled && m_Running && m_ProximityRange >= 0 && ValidSpeechTrig(e.Mobile) && ((e.Mobile == m_ActivePlayer) || !lockconversation || m_ActivePlayer == null))
|
|
{
|
|
|
|
if (!Utility.InRange(e.Mobile.Location, loc, m_ProximityRange))
|
|
return;
|
|
|
|
CheckForReset();
|
|
|
|
// process the current speech entry
|
|
ishandled = ProcessSpeech(e.Mobile, e.Speech);
|
|
|
|
// check to make sure the timer is running
|
|
DoTimer(TimeSpan.FromSeconds(1), m_ActivePlayer);
|
|
|
|
}
|
|
|
|
if (!ishandled)
|
|
{
|
|
base.OnSpeech(e);
|
|
}
|
|
}
|
|
|
|
public override bool HandlesOnMovement { get { return (m_Running); } }
|
|
|
|
public override void OnMovement(MovementEventArgs e)
|
|
{
|
|
Mobile m = e.Mobile;
|
|
|
|
if (m == null || m.AccessLevel > TriggerAccessLevel) return;
|
|
|
|
Point3D loc = new Point3D(0, 0, 0);
|
|
Map map;
|
|
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
Mobile mob = AttachedTo as Mobile;
|
|
loc = mob.Location;
|
|
map = mob.Map;
|
|
}
|
|
else
|
|
if (AttachedTo is Item && ((Item)AttachedTo).Parent == null)
|
|
{
|
|
Item i = AttachedTo as Item;
|
|
loc = i.Location;
|
|
map = i.Map;
|
|
}
|
|
|
|
// if proximity sensing is off, a speech entry has been activated, or player is an admin then ignore
|
|
if (m_Running && m_ProximityRange >= 0 && ValidMovementTrig(m) && !IsActive && !m_HoldProcessing)
|
|
{
|
|
// check to see if player is within range of the npc
|
|
if (Utility.InRange(m.Location, loc, m_ProximityRange))
|
|
{
|
|
|
|
TimeSpan pause = TimeSpan.FromSeconds(0);
|
|
if (CurrentEntry != null && CurrentEntry.Pause > 0)
|
|
{
|
|
pause = TimeSpan.FromSeconds(CurrentEntry.Pause);
|
|
}
|
|
// check to see if the current pause interval has elapsed
|
|
if (DateTime.UtcNow - pause > m_LastInteraction)
|
|
{
|
|
// process speech that is not keyword dependent
|
|
CheckForReset();
|
|
|
|
ProcessSpeech(m, null);
|
|
}
|
|
// turn on the timer that will run until the speech list is reset
|
|
// it will control paused speech and will allow the speech entry to be reset after ResetTime timeout
|
|
DoTimer(TimeSpan.FromSeconds(1), m);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CheckForReset();
|
|
}
|
|
base.OnMovement(e);
|
|
}
|
|
|
|
private bool IsInRange(IEntity e1, IEntity e2, int range)
|
|
{
|
|
if (e1 == null || e2 == null) return false;
|
|
|
|
if (e1.Map != e2.Map) return false;
|
|
|
|
return Utility.InRange(e1.Location, e2.Location, range);
|
|
}
|
|
|
|
|
|
private void CheckForReset()
|
|
{
|
|
// check to see if the interaction time has elapsed or player has gone out of range. If so then reset to entry zero
|
|
if (!m_HoldProcessing &&
|
|
((DateTime.UtcNow - ResetTime > m_LastInteraction) ||
|
|
(AttachedTo is IEntity && m_ActivePlayer != null && !IsInRange(m_ActivePlayer, (IEntity)AttachedTo, ResetRange))))
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
EntryNumber = 0;
|
|
IsActive = false;
|
|
m_ActivePlayer = null;
|
|
// turn off the timer
|
|
if (m_Timer != null)
|
|
m_Timer.Stop();
|
|
}
|
|
|
|
private void DelayedSpeech(object state)
|
|
{
|
|
object[] states = (object[])state;
|
|
|
|
SpeechEntry matchentry = (SpeechEntry)states[0];
|
|
Mobile m = (Mobile)states[1];
|
|
|
|
if (matchentry != null)
|
|
{
|
|
CurrentEntry = matchentry;
|
|
|
|
string text = BaseXmlSpawner.ApplySubstitution(null, this, m, CurrentEntry.Text);
|
|
|
|
if (text != null)
|
|
{
|
|
// dont know why emote doesnt work, but we'll just do it manually
|
|
if (CurrentEntry.SpeechStyle == MessageType.Emote)
|
|
{
|
|
text = String.Format("*{0}*", text);
|
|
}
|
|
|
|
// items cannot produce actual speech
|
|
// display a message over the item it was attached to
|
|
if (AttachedTo is Item)
|
|
{
|
|
int speechhue = 0x3B2;
|
|
if (CurrentEntry.SpeechHue >= 0)
|
|
{
|
|
speechhue = CurrentEntry.SpeechHue;
|
|
}
|
|
((Item)AttachedTo).PublicOverheadMessage(MessageType.Regular, speechhue, true, text);
|
|
}
|
|
else
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
// mobiles can produce actual speech
|
|
// so let them. This allows mobiles to talk with one another
|
|
int speechhue = ((Mobile)AttachedTo).SpeechHue;
|
|
if (CurrentEntry.SpeechHue >= 0)
|
|
{
|
|
speechhue = CurrentEntry.SpeechHue;
|
|
}
|
|
|
|
((Mobile)AttachedTo).DoSpeech(text, new int[] { }, CurrentEntry.SpeechStyle, speechhue);
|
|
//((Mobile)AttachedTo).PublicOverheadMessage( MessageType.Regular, 0x3B2, true, text );
|
|
}
|
|
}
|
|
|
|
IsActive = true;
|
|
m_LastInteraction = DateTime.UtcNow;
|
|
|
|
// execute any action associated with it
|
|
// allow for multiple action strings on a single line separated by a semicolon
|
|
if (CurrentEntry.Action != null && CurrentEntry.Action.Length > 0)
|
|
{
|
|
string[] args = CurrentEntry.Action.Split(';');
|
|
|
|
for (int j = 0; j < args.Length; j++)
|
|
{
|
|
ExecuteAction(m, args[j]);
|
|
}
|
|
}
|
|
|
|
// execute any GUMP associated with it
|
|
ExecuteGump(m, CurrentEntry.Gump);
|
|
|
|
|
|
|
|
}
|
|
|
|
m_HoldProcessing = false;
|
|
}
|
|
|
|
public bool ProcessSpeech(Mobile m, string speech)
|
|
{
|
|
|
|
if (m_HoldProcessing) return true;
|
|
|
|
// check the speech against the entries that depend on the present entry
|
|
SpeechEntry matchentry = FindMatchingKeyword(m, speech, ID);
|
|
|
|
if (matchentry == null) return false;
|
|
|
|
// when attempting to process speech-triggered speech, check for oncarried dependencies
|
|
// This will not apply to movement-triggered speech (banter with -1 dependson) which will continue to be activated
|
|
// regardless of oncarried status
|
|
// dependson of -2 will allow non-speech triggering but will still apply oncarried dependencies
|
|
|
|
// if player-carried item triggering is set then test for the presence of an item on the player an in their pack
|
|
if ((speech != null || CheckDependsOn(matchentry, -2)) && TriggerOnCarried != null && TriggerOnCarried.Length > 0)
|
|
{
|
|
bool found = BaseXmlSpawner.CheckForCarried(m, TriggerOnCarried) || matchentry.IgnoreCarried;
|
|
|
|
// is the player carrying the right item, if not then dont process
|
|
if (!found) return false;
|
|
}
|
|
|
|
// if player-carried noitem triggering is set then test for the presence of an item in the players pack that should block triggering
|
|
if ((speech != null || CheckDependsOn(matchentry, -2)) && NoTriggerOnCarried != null && NoTriggerOnCarried.Length > 0)
|
|
{
|
|
bool notfound = BaseXmlSpawner.CheckForNotCarried(m, NoTriggerOnCarried) || matchentry.IgnoreCarried;
|
|
|
|
// is the player carrying the right item, if so then dont process
|
|
if (!notfound) return false;
|
|
}
|
|
|
|
ResponseString = speech;
|
|
|
|
// the player that successfully activates a conversation by speech becomes the exclusive conversationalist until the npc resets
|
|
if ((speech != null || LockConversation) && m != null)
|
|
m_ActivePlayer = m;
|
|
|
|
// calculate the delay before activating the entry
|
|
int prepause = 1; // 1 sec by default
|
|
if (matchentry.PrePause < 0)
|
|
{
|
|
if (SpeechPace > 0 && speech != null)
|
|
{
|
|
// do the auto delay calculation based on the length of the triggering speech
|
|
prepause = (speech.Length / SpeechPace) + 1; // make 1 sec the min pause
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prepause = matchentry.PrePause;
|
|
}
|
|
|
|
// and switch to the one that matches
|
|
|
|
m_HoldProcessing = true;
|
|
Timer.DelayCall(TimeSpan.FromSeconds(prepause), new TimerStateCallback(DelayedSpeech), new object[] { matchentry, m });
|
|
|
|
return true;
|
|
}
|
|
|
|
public void DoTimer(TimeSpan delay, Mobile trigmob)
|
|
{
|
|
if (!m_Running)
|
|
return;
|
|
|
|
if (m_Timer != null)
|
|
m_Timer.Stop();
|
|
|
|
m_Timer = new InternalTimer(this, delay, trigmob);
|
|
m_Timer.Start();
|
|
}
|
|
|
|
private class InternalTimer : Timer
|
|
{
|
|
private XmlDialog m_npc;
|
|
public Mobile m_trigmob;
|
|
public TimeSpan m_delay;
|
|
|
|
public InternalTimer(XmlDialog npc, TimeSpan delay, Mobile trigmob)
|
|
: base(delay, delay)
|
|
{
|
|
|
|
Priority = TimerPriority.OneSecond;
|
|
|
|
m_npc = npc;
|
|
m_trigmob = trigmob;
|
|
m_delay = delay;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
|
|
if (m_npc != null && !m_npc.Deleted)
|
|
{
|
|
// check to see if any speech needs to be processed
|
|
TimeSpan pause = TimeSpan.FromSeconds(0);
|
|
if (m_npc.CurrentEntry != null && m_npc.CurrentEntry.Pause > 0)
|
|
{
|
|
pause = TimeSpan.FromSeconds(m_npc.CurrentEntry.Pause);
|
|
}
|
|
// check to see if the current pause interval has elapsed
|
|
if (DateTime.UtcNow - pause > m_npc.LastInteraction)
|
|
{
|
|
// process speech that is not keyword dependent
|
|
|
|
m_npc.ProcessSpeech(m_trigmob, null);
|
|
m_npc.CheckForReset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DoLoadNPC(Mobile from, string filename)
|
|
{
|
|
if (filename == null || filename.Length <= 0) return;
|
|
|
|
string dirname;
|
|
if (System.IO.Directory.Exists(DefsDir) == true)
|
|
{
|
|
// look for it in the defaults directory
|
|
dirname = String.Format("{0}/{1}.npc", DefsDir, filename);
|
|
|
|
// Check if the file exists
|
|
if (System.IO.File.Exists(dirname) == false)
|
|
{
|
|
// didnt find it so just look in the main install dir
|
|
dirname = String.Format("{0}.npc", filename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// look in the main installation dir
|
|
dirname = String.Format("{0}.npc", filename);
|
|
}
|
|
|
|
// Check if the file exists
|
|
if (System.IO.File.Exists(dirname) == true)
|
|
{
|
|
FileStream fs = null;
|
|
try
|
|
{
|
|
fs = File.Open(dirname, FileMode.Open, FileAccess.Read);
|
|
}
|
|
catch { }
|
|
|
|
if (fs == null)
|
|
{
|
|
from.SendMessage("Unable to open {0} for loading", dirname);
|
|
return;
|
|
}
|
|
|
|
// Create the data set
|
|
DataSet ds = new DataSet(NPCDataSetName);
|
|
|
|
// Read in the file
|
|
bool fileerror = false;
|
|
|
|
try
|
|
{
|
|
ds.ReadXml(fs);
|
|
}
|
|
catch { fileerror = true; }
|
|
|
|
// close the file
|
|
fs.Close();
|
|
|
|
if (fileerror)
|
|
{
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage(33, "Error reading npc file {0}", dirname);
|
|
return;
|
|
}
|
|
|
|
// Check that at least a single table was loaded
|
|
if (ds.Tables != null && ds.Tables.Count > 0)
|
|
{
|
|
// get the npc info
|
|
if (ds.Tables[NPCPointName] != null && ds.Tables[NPCPointName].Rows.Count > 0)
|
|
{
|
|
DataRow dr = ds.Tables[NPCPointName].Rows[0];
|
|
|
|
try
|
|
{
|
|
if (AttachedTo is Item)
|
|
{
|
|
((Item)AttachedTo).Name = (string)dr["Name"];
|
|
}
|
|
else
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
((Mobile)AttachedTo).Name = (string)dr["Name"];
|
|
}
|
|
|
|
}
|
|
catch { }
|
|
try { this.ProximityRange = int.Parse((string)dr["ProximityRange"]); }
|
|
catch { }
|
|
try { this.ResetRange = int.Parse((string)dr["ResetRange"]); }
|
|
catch { }
|
|
try { this.TriggerOnCarried = (string)dr["TriggerOnCarried"]; }
|
|
catch { }
|
|
try { this.NoTriggerOnCarried = (string)dr["NoTriggerOnCarried"]; }
|
|
catch { }
|
|
try { this.m_AllowGhostTriggering = bool.Parse((string)dr["AllowGhost"]); }
|
|
catch { }
|
|
try { this.m_SpeechPace = int.Parse((string)dr["SpeechPace"]); }
|
|
catch { }
|
|
try { this.Running = bool.Parse((string)dr["Running"]); }
|
|
catch { }
|
|
try { this.ResetTime = TimeSpan.FromMinutes(double.Parse((string)dr["ResetTime"])); }
|
|
catch { }
|
|
try { this.ConfigFile = (string)dr["ConfigFile"]; }
|
|
catch { }
|
|
|
|
int entrycount = 0;
|
|
try { entrycount = int.Parse((string)dr["SpeechEntries"]); }
|
|
catch { }
|
|
}
|
|
|
|
// get the speech entry info
|
|
if (ds.Tables[NPCPointName] != null && ds.Tables[NPCPointName].Rows.Count > 0)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
foreach (DataRow dr in ds.Tables[SpeechPointName].Rows)
|
|
{
|
|
SpeechEntry s = new SpeechEntry();
|
|
// Populate the speech entry data
|
|
try { s.EntryNumber = int.Parse((string)dr["EntryNumber"]); }
|
|
catch { }
|
|
try { s.ID = int.Parse((string)dr["ID"]); }
|
|
catch { }
|
|
try { s.Text = (string)dr["Text"]; }
|
|
catch { }
|
|
try { s.Keywords = (string)dr["Keywords"]; }
|
|
catch { }
|
|
try { s.Action = (string)dr["Action"]; }
|
|
catch { }
|
|
try { s.Condition = (string)dr["Condition"]; }
|
|
catch { }
|
|
try { s.DependsOn = (string)dr["DependsOn"]; }
|
|
catch { }
|
|
try { s.Pause = int.Parse((string)dr["Pause"]); }
|
|
catch { }
|
|
try { s.PrePause = int.Parse((string)dr["PrePause"]); }
|
|
catch { }
|
|
try { s.LockConversation = bool.Parse((string)dr["LockConversation"]); }
|
|
catch { }
|
|
try { s.IgnoreCarried = bool.Parse((string)dr["IgnoreCarried"]); }
|
|
catch { }
|
|
try { s.AllowNPCTrigger = bool.Parse((string)dr["AllowNPCTrigger"]); }
|
|
catch { }
|
|
try { s.SpeechStyle = (MessageType)Enum.Parse(typeof(MessageType), (string)dr["SpeechStyle"]); }
|
|
catch { }
|
|
try { s.SpeechHue = int.Parse((string)dr["SpeechHue"]); }
|
|
catch { }
|
|
try { s.Gump = (string)dr["Gump"]; }
|
|
catch { }
|
|
|
|
m_SpeechEntries.Add(s);
|
|
}
|
|
}
|
|
|
|
Reset();
|
|
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage("Loaded npc from file {0}", dirname);
|
|
}
|
|
else
|
|
{
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage(33, "No npc data found in: {0}", dirname);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage(33, "File not found: {0}", dirname);
|
|
}
|
|
}
|
|
|
|
public void DoSaveNPC(Mobile from, string filename, bool updateconfig)
|
|
{
|
|
if (filename == null || filename.Length <= 0) return;
|
|
|
|
// Create the data set
|
|
DataSet ds = new DataSet(NPCDataSetName);
|
|
|
|
// Load the data set up
|
|
ds.Tables.Add(NPCPointName);
|
|
ds.Tables.Add(SpeechPointName);
|
|
|
|
// Create a schema for the npc
|
|
ds.Tables[NPCPointName].Columns.Add("Name");
|
|
ds.Tables[NPCPointName].Columns.Add("Running");
|
|
ds.Tables[NPCPointName].Columns.Add("ProximityRange");
|
|
ds.Tables[NPCPointName].Columns.Add("ResetRange");
|
|
ds.Tables[NPCPointName].Columns.Add("TriggerOnCarried");
|
|
ds.Tables[NPCPointName].Columns.Add("NoTriggerOnCarried");
|
|
ds.Tables[NPCPointName].Columns.Add("AllowGhost");
|
|
ds.Tables[NPCPointName].Columns.Add("SpeechPace");
|
|
ds.Tables[NPCPointName].Columns.Add("ResetTime");
|
|
ds.Tables[NPCPointName].Columns.Add("ConfigFile");
|
|
ds.Tables[NPCPointName].Columns.Add("SpeechEntries");
|
|
|
|
// Create a schema for the speech entries
|
|
ds.Tables[SpeechPointName].Columns.Add("EntryNumber");
|
|
ds.Tables[SpeechPointName].Columns.Add("ID");
|
|
ds.Tables[SpeechPointName].Columns.Add("Text");
|
|
ds.Tables[SpeechPointName].Columns.Add("Keywords");
|
|
ds.Tables[SpeechPointName].Columns.Add("Action");
|
|
ds.Tables[SpeechPointName].Columns.Add("Condition");
|
|
ds.Tables[SpeechPointName].Columns.Add("DependsOn");
|
|
ds.Tables[SpeechPointName].Columns.Add("Pause");
|
|
ds.Tables[SpeechPointName].Columns.Add("PrePause");
|
|
ds.Tables[SpeechPointName].Columns.Add("LockConversation");
|
|
ds.Tables[SpeechPointName].Columns.Add("IgnoreCarried");
|
|
ds.Tables[SpeechPointName].Columns.Add("AllowNPCTrigger");
|
|
ds.Tables[SpeechPointName].Columns.Add("SpeechStyle");
|
|
ds.Tables[SpeechPointName].Columns.Add("SpeechHue");
|
|
ds.Tables[SpeechPointName].Columns.Add("Gump");
|
|
|
|
// Create a new data row
|
|
DataRow dr = ds.Tables[NPCPointName].NewRow();
|
|
|
|
// Populate the npc data
|
|
if (AttachedTo is Item)
|
|
{
|
|
dr["Name"] = (string)((Item)AttachedTo).Name;
|
|
}
|
|
else
|
|
if (AttachedTo is Mobile)
|
|
{
|
|
dr["Name"] = (string)((Mobile)AttachedTo).Name;
|
|
}
|
|
|
|
dr["Running"] = (bool)this.Running;
|
|
dr["ProximityRange"] = (int)this.m_ProximityRange;
|
|
dr["ResetRange"] = (int)this.m_ResetRange;
|
|
dr["TriggerOnCarried"] = (string)this.TriggerOnCarried;
|
|
dr["NoTriggerOnCarried"] = (string)this.NoTriggerOnCarried;
|
|
dr["AllowGhost"] = (bool)this.m_AllowGhostTriggering;
|
|
dr["SpeechPace"] = (int)this.SpeechPace;
|
|
dr["ResetTime"] = (double)this.ResetTime.TotalMinutes;
|
|
dr["ConfigFile"] = (string)this.ConfigFile;
|
|
int entrycount = 0;
|
|
if (SpeechEntries != null)
|
|
{
|
|
entrycount = SpeechEntries.Count;
|
|
}
|
|
dr["SpeechEntries"] = (int)entrycount;
|
|
|
|
// Add the row the the table
|
|
ds.Tables[NPCPointName].Rows.Add(dr);
|
|
|
|
for (int i = 0; i < entrycount; i++)
|
|
{
|
|
SpeechEntry s = (SpeechEntry)SpeechEntries[i];
|
|
|
|
// Create a new data row
|
|
dr = ds.Tables[SpeechPointName].NewRow();
|
|
|
|
// Populate the speech entry data
|
|
dr["EntryNumber"] = (int)s.EntryNumber;
|
|
dr["ID"] = (int)s.ID;
|
|
dr["Text"] = (string)s.Text;
|
|
dr["Keywords"] = (string)s.Keywords;
|
|
dr["Action"] = (string)s.Action;
|
|
dr["Condition"] = (string)s.Condition;
|
|
dr["DependsOn"] = (string)s.DependsOn;
|
|
dr["Pause"] = (int)s.Pause;
|
|
dr["PrePause"] = (int)s.PrePause;
|
|
dr["LockConversation"] = (bool)s.LockConversation;
|
|
dr["IgnoreCarried"] = (bool)s.IgnoreCarried;
|
|
dr["AllowNPCTrigger"] = (bool)s.AllowNPCTrigger;
|
|
dr["SpeechStyle"] = (MessageType)s.SpeechStyle;
|
|
dr["SpeechHue"] = (int)s.SpeechHue;
|
|
dr["Gump"] = (string)s.Gump;
|
|
|
|
// Add the row the the table
|
|
ds.Tables[SpeechPointName].Rows.Add(dr);
|
|
}
|
|
|
|
// Write out the file
|
|
|
|
string dirname;
|
|
|
|
if (System.IO.Directory.Exists(DefsDir) == true)
|
|
{
|
|
// put it in the defaults directory if it exists
|
|
dirname = String.Format("{0}/{1}.npc", DefsDir, filename);
|
|
}
|
|
else
|
|
{
|
|
// otherwise just put it in the main installation dir
|
|
dirname = String.Format("{0}.npc", filename);
|
|
}
|
|
|
|
// check to see if the file already exists
|
|
|
|
if (System.IO.File.Exists(dirname) == true)
|
|
{
|
|
// prompt the user to save over it
|
|
if (from != null)
|
|
{
|
|
from.SendGump(new ConfirmSaveGump(this, ds, dirname, filename, updateconfig));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
SaveFile(from, ds, dirname, filename, updateconfig);
|
|
}
|
|
}
|
|
|
|
public bool SaveFile(Mobile from, DataSet ds, string dirname, string configname, bool updateconfig)
|
|
{
|
|
if (ds == null)
|
|
{
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage("Empty dataset. File {0} not saved.", dirname);
|
|
return false;
|
|
}
|
|
|
|
bool file_error = false;
|
|
|
|
try
|
|
{
|
|
ds.WriteXml(dirname);
|
|
}
|
|
catch { file_error = true; }
|
|
|
|
if (file_error)
|
|
{
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage("Error trying to save to file {0}", dirname);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (from != null && !from.Deleted)
|
|
from.SendMessage("Saved npc to file {0}", dirname);
|
|
if (updateconfig)
|
|
ConfigFile = configname;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public class ConfirmSaveGump : Gump
|
|
{
|
|
private XmlDialog m_dialog;
|
|
private DataSet m_ds;
|
|
private string m_filename;
|
|
private string m_configname;
|
|
private bool m_updateconfig;
|
|
|
|
|
|
public ConfirmSaveGump(XmlDialog dialog, DataSet ds, string filename, string configname, bool updateconfig)
|
|
: base(0, 0)
|
|
{
|
|
m_dialog = dialog;
|
|
m_ds = ds;
|
|
m_filename = filename;
|
|
m_configname = configname;
|
|
m_updateconfig = updateconfig;
|
|
|
|
Closable = false;
|
|
Dragable = true;
|
|
AddPage(0);
|
|
AddBackground(10, 200, 200, 130, 5054);
|
|
|
|
AddLabel(20, 210, 33, String.Format("{0} exists.", filename));
|
|
AddLabel(20, 230, 33, String.Format("Overwrite?", filename));
|
|
AddRadio(35, 255, 9721, 9724, false, 1); // accept/yes radio
|
|
AddRadio(135, 255, 9721, 9724, true, 2); // decline/no radio
|
|
AddHtmlLocalized(72, 255, 200, 30, 1049016, 0x7fff, false, false); // Yes
|
|
AddHtmlLocalized(172, 255, 200, 30, 1049017, 0x7fff, false, false); // No
|
|
AddButton(80, 289, 2130, 2129, 3, GumpButtonType.Reply, 0); // Okay button
|
|
|
|
}
|
|
public override void OnResponse(NetState state, RelayInfo info)
|
|
{
|
|
if (info == null || state == null || state.Mobile == null) return;
|
|
|
|
int radiostate = -1;
|
|
if (info.Switches.Length > 0)
|
|
{
|
|
radiostate = info.Switches[0];
|
|
}
|
|
switch (info.ButtonID)
|
|
{
|
|
|
|
default:
|
|
{
|
|
if (radiostate == 1)
|
|
{ // accept
|
|
if (m_dialog != null)
|
|
m_dialog.SaveFile(state.Mobile, m_ds, m_filename, m_configname, m_updateconfig);
|
|
}
|
|
else
|
|
{
|
|
state.Mobile.SendMessage("File {0} not saved.", m_filename);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[Usage("XmlSaveNPC filename")]
|
|
[Description("Saves the targeted Talking NPC to an xml file.")]
|
|
public static void SaveNPC_OnCommand(CommandEventArgs e)
|
|
{
|
|
e.Mobile.Target = new SaveNPCTarget(e);
|
|
}
|
|
|
|
private class SaveNPCTarget : Target
|
|
{
|
|
private CommandEventArgs m_e;
|
|
public SaveNPCTarget(CommandEventArgs e)
|
|
: base(30, false, TargetFlags.None)
|
|
{
|
|
m_e = e;
|
|
}
|
|
protected override void OnTarget(Mobile from, object targeted)
|
|
{
|
|
string filename = m_e.GetString(0);
|
|
|
|
if (filename == null || filename.Length <= 0)
|
|
{
|
|
from.SendMessage("must specify a save filename");
|
|
return;
|
|
}
|
|
|
|
// save the XmlDialog attachment info to xml
|
|
XmlDialog xa = XmlAttach.FindAttachment(targeted, typeof(XmlDialog)) as XmlDialog;
|
|
|
|
if (xa != null)
|
|
{
|
|
xa.DoSaveNPC(from, filename, false);
|
|
|
|
}
|
|
else
|
|
{
|
|
from.SendMessage("Must target a Talking NPC");
|
|
}
|
|
}
|
|
}
|
|
|
|
[Usage("XmlLoadNPC filename")]
|
|
[Description("Loads the targeted Talking NPC to an xml file.")]
|
|
public static void LoadNPC_OnCommand(CommandEventArgs e)
|
|
{
|
|
e.Mobile.Target = new LoadNPCTarget(e);
|
|
}
|
|
|
|
private class LoadNPCTarget : Target
|
|
{
|
|
private CommandEventArgs m_e;
|
|
public LoadNPCTarget(CommandEventArgs e)
|
|
: base(30, false, TargetFlags.None)
|
|
{
|
|
m_e = e;
|
|
}
|
|
protected override void OnTarget(Mobile from, object targeted)
|
|
{
|
|
string filename = m_e.GetString(0);
|
|
|
|
if (filename == null || filename.Length <= 0)
|
|
{
|
|
from.SendMessage("must specify a load filename");
|
|
return;
|
|
}
|
|
|
|
// load the XmlDialog attachment
|
|
XmlDialog xa = XmlAttach.FindAttachment(targeted, typeof(XmlDialog)) as XmlDialog;
|
|
|
|
if (xa != null)
|
|
{
|
|
xa.DoLoadNPC(from, filename);
|
|
|
|
}
|
|
else
|
|
{
|
|
// doesnt have a dialog attachment, so add and load
|
|
xa = new XmlDialog(filename);
|
|
XmlAttach.AttachTo(targeted, xa);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool AssignSettings(string argname, string value)
|
|
{
|
|
switch (argname)
|
|
{
|
|
case "XmlQuestNPCDir":
|
|
DefsDir = value;
|
|
break;
|
|
case "defResetTime":
|
|
defResetTime = TimeSpan.FromSeconds(XmlSpawner.ConvertToInt(value));
|
|
break;
|
|
case "defProximityRange":
|
|
defProximityRange = XmlSpawner.ConvertToInt(value);
|
|
break;
|
|
case "defResetRange":
|
|
defResetRange = XmlSpawner.ConvertToInt(value);
|
|
break;
|
|
case "defSpeechPace":
|
|
defSpeechPace = XmlSpawner.ConvertToInt(value);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public new static void Initialize()
|
|
{
|
|
XmlSpawner.LoadSettings(new XmlSpawner.AssignSettingsHandler(AssignSettings), "XmlDialog");
|
|
|
|
CommandSystem.Register("SaveNPC", AccessLevel.Administrator, new CommandEventHandler(SaveNPC_OnCommand));
|
|
CommandSystem.Register("LoadNPC", AccessLevel.Administrator, new CommandEventHandler(LoadNPC_OnCommand));
|
|
}
|
|
|
|
|
|
public override void Serialize(GenericWriter writer)
|
|
{
|
|
base.Serialize(writer);
|
|
|
|
writer.Write((int)9); // version
|
|
// Version 9 added the ResetRange property
|
|
writer.Write(m_ResetRange);
|
|
// Version 8 added the IgnoreCarried property
|
|
if (m_SpeechEntries != null)
|
|
{
|
|
writer.Write((int)m_SpeechEntries.Count);
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
writer.Write(s.IgnoreCarried);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write((int)0);
|
|
}
|
|
|
|
// Version 7
|
|
// changed DependsOn to a string
|
|
// Version 6
|
|
// write out the additional speech entry fields
|
|
if (m_SpeechEntries != null)
|
|
{
|
|
writer.Write((int)m_SpeechEntries.Count);
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
writer.Write(s.SpeechHue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write((int)0);
|
|
}
|
|
// Version 5
|
|
// write out the additional speech entry fields
|
|
if (m_SpeechEntries != null)
|
|
{
|
|
writer.Write((int)m_SpeechEntries.Count);
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
writer.Write(s.Gump);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write((int)0);
|
|
}
|
|
// Version 4
|
|
// write out the additional speech entry fields
|
|
if (m_SpeechEntries != null)
|
|
{
|
|
writer.Write((int)m_SpeechEntries.Count);
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
writer.Write(s.Condition);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write((int)0);
|
|
}
|
|
// Version 3
|
|
writer.Write(TriggerOnCarried);
|
|
writer.Write(NoTriggerOnCarried);
|
|
// Version 2
|
|
writer.Write(m_SpeechPace);
|
|
// write out the additional speech entry fields
|
|
if (m_SpeechEntries != null)
|
|
{
|
|
writer.Write((int)m_SpeechEntries.Count);
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
writer.Write(s.PrePause);
|
|
writer.Write(s.LockConversation);
|
|
writer.Write(s.AllowNPCTrigger);
|
|
writer.Write((int)s.SpeechStyle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write((int)0);
|
|
}
|
|
|
|
// Version 1
|
|
writer.Write(m_ActivePlayer);
|
|
|
|
// Version 0
|
|
writer.Write(m_IsActive);
|
|
writer.Write(m_ResetTime);
|
|
writer.Write(m_LastInteraction);
|
|
writer.Write(m_AllowGhostTriggering);
|
|
writer.Write(m_ProximityRange);
|
|
writer.Write(m_Running);
|
|
writer.Write(m_ConfigFile);
|
|
// write out the speech entries
|
|
if (m_SpeechEntries != null)
|
|
{
|
|
writer.Write((int)m_SpeechEntries.Count);
|
|
foreach (SpeechEntry s in m_SpeechEntries)
|
|
{
|
|
writer.Write(s.EntryNumber);
|
|
writer.Write(s.ID);
|
|
writer.Write(s.Text);
|
|
writer.Write(s.Keywords);
|
|
writer.Write(s.Action);
|
|
writer.Write(s.DependsOn);
|
|
writer.Write(s.Pause);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write((int)0);
|
|
}
|
|
writer.Write(m_CurrentEntryNumber);
|
|
// check to see if the timer is running
|
|
if (m_Timer != null && m_Timer.Running)
|
|
{
|
|
writer.Write(true);
|
|
writer.Write(m_Timer.m_trigmob);
|
|
writer.Write(m_Timer.m_delay);
|
|
}
|
|
else
|
|
{
|
|
writer.Write(false);
|
|
}
|
|
}
|
|
|
|
public override void Deserialize(GenericReader reader)
|
|
{
|
|
base.Deserialize(reader);
|
|
|
|
int version = reader.ReadInt();
|
|
|
|
switch (version)
|
|
{
|
|
case 9:
|
|
{
|
|
m_ResetRange = reader.ReadInt();
|
|
goto case 8;
|
|
}
|
|
case 8:
|
|
{
|
|
int count = reader.ReadInt();
|
|
m_SpeechEntries = new ArrayList();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
|
|
newentry.IgnoreCarried = reader.ReadBool();
|
|
|
|
m_SpeechEntries.Add(newentry);
|
|
}
|
|
|
|
goto case 7;
|
|
}
|
|
case 7:
|
|
{
|
|
goto case 6;
|
|
}
|
|
case 6:
|
|
{
|
|
int count = reader.ReadInt();
|
|
if (version < 8)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
}
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (version < 8)
|
|
{
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
|
|
newentry.SpeechHue = reader.ReadInt();
|
|
|
|
m_SpeechEntries.Add(newentry);
|
|
}
|
|
else
|
|
{
|
|
SpeechEntry newentry = (SpeechEntry)m_SpeechEntries[i];
|
|
|
|
newentry.SpeechHue = reader.ReadInt();
|
|
}
|
|
}
|
|
|
|
goto case 5;
|
|
}
|
|
case 5:
|
|
{
|
|
int count = reader.ReadInt();
|
|
if (version < 6)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
}
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (version < 6)
|
|
{
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
|
|
newentry.Gump = reader.ReadString();
|
|
|
|
m_SpeechEntries.Add(newentry);
|
|
}
|
|
else
|
|
{
|
|
SpeechEntry newentry = (SpeechEntry)m_SpeechEntries[i];
|
|
|
|
newentry.Gump = reader.ReadString();
|
|
}
|
|
}
|
|
|
|
goto case 4;
|
|
}
|
|
case 4:
|
|
{
|
|
int count = reader.ReadInt();
|
|
if (version < 5)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
}
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (version < 5)
|
|
{
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
|
|
newentry.Condition = reader.ReadString();
|
|
|
|
m_SpeechEntries.Add(newentry);
|
|
}
|
|
else
|
|
{
|
|
SpeechEntry newentry = (SpeechEntry)m_SpeechEntries[i];
|
|
|
|
newentry.Condition = reader.ReadString();
|
|
}
|
|
}
|
|
|
|
goto case 3;
|
|
}
|
|
case 3:
|
|
{
|
|
TriggerOnCarried = reader.ReadString();
|
|
NoTriggerOnCarried = reader.ReadString();
|
|
goto case 2;
|
|
}
|
|
case 2:
|
|
{
|
|
m_SpeechPace = reader.ReadInt();
|
|
|
|
int count = reader.ReadInt();
|
|
if (version < 4)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
}
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (version < 4)
|
|
{
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
|
|
newentry.PrePause = reader.ReadInt();
|
|
newentry.LockConversation = reader.ReadBool();
|
|
newentry.AllowNPCTrigger = reader.ReadBool();
|
|
newentry.SpeechStyle = (MessageType)reader.ReadInt();
|
|
|
|
m_SpeechEntries.Add(newentry);
|
|
}
|
|
else
|
|
{
|
|
SpeechEntry newentry = (SpeechEntry)m_SpeechEntries[i];
|
|
|
|
newentry.PrePause = reader.ReadInt();
|
|
newentry.LockConversation = reader.ReadBool();
|
|
newentry.AllowNPCTrigger = reader.ReadBool();
|
|
newentry.SpeechStyle = (MessageType)reader.ReadInt();
|
|
}
|
|
}
|
|
goto case 1;
|
|
}
|
|
case 1:
|
|
{
|
|
m_ActivePlayer = reader.ReadMobile();
|
|
goto case 0;
|
|
}
|
|
case 0:
|
|
{
|
|
m_IsActive = reader.ReadBool();
|
|
m_ResetTime = reader.ReadTimeSpan();
|
|
m_LastInteraction = reader.ReadDateTime();
|
|
m_AllowGhostTriggering = reader.ReadBool();
|
|
m_ProximityRange = reader.ReadInt();
|
|
m_Running = reader.ReadBool();
|
|
m_ConfigFile = reader.ReadString();
|
|
int count = reader.ReadInt();
|
|
if (version < 2)
|
|
{
|
|
m_SpeechEntries = new ArrayList();
|
|
}
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
|
|
if (version < 2)
|
|
{
|
|
SpeechEntry newentry = new SpeechEntry();
|
|
|
|
newentry.EntryNumber = reader.ReadInt();
|
|
newentry.ID = reader.ReadInt();
|
|
newentry.Text = reader.ReadString();
|
|
newentry.Keywords = reader.ReadString();
|
|
newentry.Action = reader.ReadString();
|
|
newentry.DependsOn = reader.ReadInt().ToString();
|
|
newentry.Pause = reader.ReadInt();
|
|
|
|
m_SpeechEntries.Add(newentry);
|
|
}
|
|
else
|
|
{
|
|
SpeechEntry newentry = (SpeechEntry)m_SpeechEntries[i];
|
|
|
|
newentry.EntryNumber = reader.ReadInt();
|
|
newentry.ID = reader.ReadInt();
|
|
newentry.Text = reader.ReadString();
|
|
newentry.Keywords = reader.ReadString();
|
|
newentry.Action = reader.ReadString();
|
|
if (version < 7)
|
|
{
|
|
newentry.DependsOn = reader.ReadInt().ToString();
|
|
}
|
|
else
|
|
{
|
|
newentry.DependsOn = reader.ReadString();
|
|
}
|
|
newentry.Pause = reader.ReadInt();
|
|
}
|
|
}
|
|
// read in the current entry number. Note this will also set the current entry
|
|
EntryNumber = reader.ReadInt();
|
|
// restart the timer if it was active
|
|
bool isrunning = reader.ReadBool();
|
|
if (isrunning)
|
|
{
|
|
Mobile trigmob = reader.ReadMobile();
|
|
TimeSpan delay = reader.ReadTimeSpan();
|
|
DoTimer(delay, trigmob);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|