mirror of
https://github.com/runuo/runuo
synced 2026-08-11 22:26:04 -04:00
- Added base quest giver items, both timed and untimed. - Added BatteredBucket and Lost and Found quest. - Moved quester 'friendly names' to a class attribute. If left unspecified, the type name is used. - Misc quest corrections. - The bag of sending will now reject nontransferable items. - Fixed quest items generating error messages when StackWith was attempted on them. (StackWith is not supposed to produce feedback.) - Quest items can now be dropped onto the player's backpack.
782 lines
21 KiB
C#
782 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Commands;
|
|
using Server.Engines.MLQuests.Definitions;
|
|
using Server.Engines.MLQuests.Gumps;
|
|
using Server.Engines.MLQuests.Objectives;
|
|
using Server.Gumps;
|
|
using Server.Mobiles;
|
|
using Server.Network;
|
|
using System.Collections;
|
|
using Server.Commands.Generic;
|
|
using Server.Items;
|
|
using Server.Targeting;
|
|
using System.IO;
|
|
|
|
namespace Server.Engines.MLQuests
|
|
{
|
|
public static class MLQuestSystem
|
|
{
|
|
public static bool Enabled { get { return Core.ML; } }
|
|
|
|
public const int MaxConcurrentQuests = 10;
|
|
public const int SpeechColor = 0x3B2;
|
|
|
|
public static readonly bool AutoGenerateNew = true;
|
|
public static readonly bool Debug = false;
|
|
|
|
private static Dictionary<Type, MLQuest> m_Quests;
|
|
private static Dictionary<Type, List<MLQuest>> m_QuestGivers;
|
|
private static Dictionary<PlayerMobile, MLQuestContext> m_Contexts;
|
|
|
|
public static readonly List<MLQuest> EmptyList = new List<MLQuest>();
|
|
|
|
public static Dictionary<Type, MLQuest> Quests
|
|
{
|
|
get { return m_Quests; }
|
|
}
|
|
|
|
public static Dictionary<Type, List<MLQuest>> QuestGivers
|
|
{
|
|
get { return m_QuestGivers; }
|
|
}
|
|
|
|
public static Dictionary<PlayerMobile, MLQuestContext> Contexts
|
|
{
|
|
get { return m_Contexts; }
|
|
}
|
|
|
|
static MLQuestSystem()
|
|
{
|
|
m_Quests = new Dictionary<Type, MLQuest>();
|
|
m_QuestGivers = new Dictionary<Type, List<MLQuest>>();
|
|
m_Contexts = new Dictionary<PlayerMobile, MLQuestContext>();
|
|
|
|
string cfgPath = Path.Combine( Core.BaseDirectory, Path.Combine( "Data", "MLQuests.cfg" ) );
|
|
|
|
Type baseQuestType = typeof( MLQuest );
|
|
Type baseQuesterType = typeof( IQuestGiver );
|
|
|
|
if ( File.Exists( cfgPath ) )
|
|
{
|
|
using ( StreamReader sr = new StreamReader( cfgPath ) )
|
|
{
|
|
string line;
|
|
|
|
while ( ( line = sr.ReadLine() ) != null )
|
|
{
|
|
if ( line.Length == 0 || line.StartsWith( "#" ) )
|
|
continue;
|
|
|
|
string[] split = line.Split( '\t' );
|
|
|
|
Type type = ScriptCompiler.FindTypeByName( split[0] );
|
|
|
|
if ( type == null || !baseQuestType.IsAssignableFrom( type ) )
|
|
{
|
|
if ( Debug )
|
|
Console.WriteLine( "Warning: {1} quest type '{0}'", split[0], ( type == null ) ? "Unknown" : "Invalid" );
|
|
|
|
continue;
|
|
}
|
|
|
|
MLQuest quest = null;
|
|
|
|
try
|
|
{
|
|
quest = Activator.CreateInstance( type ) as MLQuest;
|
|
}
|
|
catch { }
|
|
|
|
if ( quest == null )
|
|
continue;
|
|
|
|
Register( type, quest );
|
|
|
|
for ( int i = 1; i < split.Length; ++i )
|
|
{
|
|
Type questerType = ScriptCompiler.FindTypeByName( split[i] );
|
|
|
|
if ( questerType == null || !baseQuesterType.IsAssignableFrom( questerType ) )
|
|
{
|
|
if ( Debug )
|
|
Console.WriteLine( "Warning: {1} quester type '{0}'", split[i], ( questerType == null ) ? "Unknown" : "Invalid" );
|
|
|
|
continue;
|
|
}
|
|
|
|
RegisterQuestGiver( quest, questerType );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void Register( Type type, MLQuest quest )
|
|
{
|
|
m_Quests[type] = quest;
|
|
}
|
|
|
|
private static void RegisterQuestGiver( MLQuest quest, Type questerType )
|
|
{
|
|
List<MLQuest> questList;
|
|
|
|
if ( !m_QuestGivers.TryGetValue( questerType, out questList ) )
|
|
m_QuestGivers[questerType] = questList = new List<MLQuest>();
|
|
|
|
questList.Add( quest );
|
|
}
|
|
|
|
public static void Register( MLQuest quest, params Type[] questerTypes )
|
|
{
|
|
Register( quest.GetType(), quest );
|
|
|
|
foreach ( Type questerType in questerTypes )
|
|
RegisterQuestGiver( quest, questerType );
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
if ( !Enabled )
|
|
return;
|
|
|
|
if ( AutoGenerateNew )
|
|
{
|
|
foreach ( MLQuest quest in m_Quests.Values )
|
|
{
|
|
if ( quest != null && !quest.Deserialized )
|
|
quest.Generate();
|
|
}
|
|
}
|
|
|
|
MLQuestPersistence.EnsureExistence();
|
|
|
|
CommandSystem.Register( "MLQuestsInfo", AccessLevel.Administrator, new CommandEventHandler( MLQuestsInfo_OnCommand ) );
|
|
CommandSystem.Register( "SaveQuest", AccessLevel.Administrator, new CommandEventHandler( SaveQuest_OnCommand ) );
|
|
CommandSystem.Register( "SaveAllQuests", AccessLevel.Administrator, new CommandEventHandler( SaveAllQuests_OnCommand ) );
|
|
CommandSystem.Register( "InvalidQuestItems", AccessLevel.Administrator, new CommandEventHandler( InvalidQuestItems_OnCommand ) );
|
|
|
|
TargetCommands.Register( new ViewQuestsCommand() );
|
|
TargetCommands.Register( new ViewContextCommand() );
|
|
|
|
EventSink.QuestGumpRequest += new QuestGumpRequestHandler( EventSink_QuestGumpRequest );
|
|
}
|
|
|
|
[Usage( "MLQuestsInfo" )]
|
|
[Description( "Displays general information about the ML quest system, or a quest by type name." )]
|
|
public static void MLQuestsInfo_OnCommand( CommandEventArgs e )
|
|
{
|
|
Mobile m = e.Mobile;
|
|
|
|
if ( e.Length == 0 )
|
|
{
|
|
m.SendMessage( "Quest table length: {0}", m_Quests.Count );
|
|
return;
|
|
}
|
|
|
|
Type index = ScriptCompiler.FindTypeByName( e.GetString( 0 ) );
|
|
MLQuest quest;
|
|
|
|
if ( index == null || !m_Quests.TryGetValue( index, out quest ) )
|
|
{
|
|
m.SendMessage( "Invalid quest type name." );
|
|
return;
|
|
}
|
|
|
|
m.SendMessage( "Activated: {0}", quest.Activated );
|
|
m.SendMessage( "Number of objectives: {0}", quest.Objectives.Count );
|
|
m.SendMessage( "Objective type: {0}", quest.ObjectiveType );
|
|
m.SendMessage( "Number of active instances: {0}", quest.Instances.Count );
|
|
}
|
|
|
|
public class ViewQuestsCommand : BaseCommand
|
|
{
|
|
public ViewQuestsCommand()
|
|
{
|
|
AccessLevel = AccessLevel.GameMaster;
|
|
Supports = CommandSupport.Simple;
|
|
Commands = new string[] { "ViewQuests" };
|
|
ObjectTypes = ObjectTypes.Mobiles;
|
|
Usage = "ViewQuests";
|
|
Description = "Displays a targeted mobile's quest overview.";
|
|
}
|
|
|
|
public override void Execute( CommandEventArgs e, object obj )
|
|
{
|
|
Mobile from = e.Mobile;
|
|
PlayerMobile pm = obj as PlayerMobile;
|
|
|
|
if ( pm == null )
|
|
{
|
|
LogFailure( "That is not a player." );
|
|
return;
|
|
}
|
|
|
|
CommandLogging.WriteLine( from, "{0} {1} viewing quest overview of {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( pm ) );
|
|
from.SendGump( new QuestLogGump( pm, false ) );
|
|
}
|
|
}
|
|
|
|
private class ViewContextCommand : BaseCommand
|
|
{
|
|
public ViewContextCommand()
|
|
{
|
|
AccessLevel = AccessLevel.GameMaster;
|
|
Supports = CommandSupport.Simple;
|
|
Commands = new string[] { "ViewMLContext" };
|
|
ObjectTypes = ObjectTypes.Mobiles;
|
|
Usage = "ViewMLContext";
|
|
Description = "Opens the ML quest context for a targeted mobile.";
|
|
}
|
|
|
|
public override void Execute( CommandEventArgs e, object obj )
|
|
{
|
|
PlayerMobile pm = obj as PlayerMobile;
|
|
|
|
if ( pm == null )
|
|
LogFailure( "They have no ML quest context." );
|
|
else
|
|
e.Mobile.SendGump( new PropertiesGump( e.Mobile, GetOrCreateContext( pm ) ) );
|
|
}
|
|
}
|
|
|
|
[Usage( "SaveQuest <type> [saveEnabled=true]" )]
|
|
[Description( "Allows serialization for a specific quest to be turned on or off." )]
|
|
public static void SaveQuest_OnCommand( CommandEventArgs e )
|
|
{
|
|
Mobile m = e.Mobile;
|
|
|
|
if ( e.Length == 0 || e.Length > 2 )
|
|
{
|
|
m.SendMessage( "Syntax: SaveQuest <id> [saveEnabled=true]" );
|
|
return;
|
|
}
|
|
|
|
Type index = ScriptCompiler.FindTypeByName( e.GetString( 0 ) );
|
|
MLQuest quest;
|
|
|
|
if ( index == null || !m_Quests.TryGetValue( index, out quest ) )
|
|
{
|
|
m.SendMessage( "Invalid quest type name." );
|
|
return;
|
|
}
|
|
|
|
bool enable = ( e.Length == 2 ) ? e.GetBoolean( 1 ) : true;
|
|
|
|
quest.SaveEnabled = enable;
|
|
m.SendMessage( "Serialization for quest {0} is now {1}.", quest.GetType().Name, enable ? "enabled" : "disabled" );
|
|
|
|
if ( AutoGenerateNew && !enable )
|
|
m.SendMessage( "Please note that automatic generation of new quests is ON. This quest will be regenerated on the next server start." );
|
|
}
|
|
|
|
[Usage( "SaveAllQuests [saveEnabled=true]" )]
|
|
[Description( "Allows serialization for all quests to be turned on or off." )]
|
|
public static void SaveAllQuests_OnCommand( CommandEventArgs e )
|
|
{
|
|
Mobile m = e.Mobile;
|
|
|
|
if ( e.Length > 1 )
|
|
{
|
|
m.SendMessage( "Syntax: SaveAllQuests [saveEnabled=true]" );
|
|
return;
|
|
}
|
|
|
|
bool enable = ( e.Length == 1 ) ? e.GetBoolean( 0 ) : true;
|
|
|
|
foreach ( MLQuest quest in m_Quests.Values )
|
|
quest.SaveEnabled = enable;
|
|
|
|
m.SendMessage( "Serialization for all quests is now {0}.", enable ? "enabled" : "disabled" );
|
|
|
|
if ( AutoGenerateNew && !enable )
|
|
m.SendMessage( "Please note that automatic generation of new quests is ON. All quests will be regenerated on the next server start." );
|
|
}
|
|
|
|
[Usage( "InvalidQuestItems" )]
|
|
[Description( "Provides an overview of all quest items not located in the top-level of a player's backpack." )]
|
|
public static void InvalidQuestItems_OnCommand( CommandEventArgs e )
|
|
{
|
|
Mobile m = e.Mobile;
|
|
|
|
ArrayList found = new ArrayList();
|
|
|
|
foreach ( Item item in World.Items.Values )
|
|
{
|
|
if ( item.QuestItem )
|
|
{
|
|
Backpack pack = item.Parent as Backpack;
|
|
|
|
if ( pack != null )
|
|
{
|
|
PlayerMobile player = pack.Parent as PlayerMobile;
|
|
|
|
if ( player != null && player.Backpack == pack )
|
|
continue;
|
|
}
|
|
|
|
found.Add( item );
|
|
}
|
|
}
|
|
|
|
if ( found.Count == 0 )
|
|
m.SendMessage( "No matching objects found." );
|
|
else
|
|
m.SendGump( new InterfaceGump( m, new string[] { "Object" }, found, 0, null ) );
|
|
}
|
|
|
|
private static bool FindQuest( IQuestGiver quester, PlayerMobile pm, MLQuestContext context, out MLQuest quest, out MLQuestInstance entry )
|
|
{
|
|
quest = null;
|
|
entry = null;
|
|
|
|
List<MLQuest> quests = quester.MLQuests;
|
|
Type questerType = quester.GetType();
|
|
|
|
// 1. Check quests in progress with this NPC (overriding deliveries is intended)
|
|
if ( context != null )
|
|
{
|
|
foreach ( MLQuest questEntry in quests )
|
|
{
|
|
MLQuestInstance instance = context.FindInstance( questEntry );
|
|
|
|
if ( instance != null && ( instance.Quester == quester || ( !questEntry.IsEscort && instance.QuesterType == questerType ) ) )
|
|
{
|
|
entry = instance;
|
|
quest = questEntry;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Check deliveries (overriding chain offers is intended)
|
|
if ( ( entry = HandleDelivery( pm, quester, questerType ) ) != null )
|
|
{
|
|
quest = entry.Quest;
|
|
return true;
|
|
}
|
|
|
|
// 3. Check chain quest offers
|
|
if ( context != null )
|
|
{
|
|
foreach ( MLQuest questEntry in quests )
|
|
{
|
|
if ( questEntry.IsChainTriggered && context.ChainOffers.Contains( questEntry ) )
|
|
{
|
|
quest = questEntry;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Random quest
|
|
quest = RandomStarterQuest( quester, pm, context );
|
|
|
|
return ( quest != null );
|
|
}
|
|
|
|
public static void OnDoubleClick( IQuestGiver quester, PlayerMobile pm )
|
|
{
|
|
if ( quester.Deleted || !pm.Alive )
|
|
return;
|
|
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
MLQuest quest;
|
|
MLQuestInstance entry;
|
|
|
|
if ( !FindQuest( quester, pm, context, out quest, out entry ) )
|
|
{
|
|
Tell( quester, pm, 1080107 ); // I'm sorry, I have nothing for you at this time.
|
|
return;
|
|
}
|
|
|
|
if ( entry != null )
|
|
{
|
|
TurnToFace( quester, pm );
|
|
|
|
if ( entry.Failed )
|
|
return; // Note: OSI sends no gump at all for failed quests, they have to be cancelled in the quest overview
|
|
else if ( entry.ClaimReward )
|
|
entry.SendRewardOffer();
|
|
else if ( entry.IsCompleted() )
|
|
entry.SendReportBackGump();
|
|
else
|
|
entry.SendProgressGump();
|
|
}
|
|
else if ( quest.CanOffer( quester, pm, context, true ) )
|
|
{
|
|
TurnToFace( quester, pm );
|
|
|
|
quest.SendOffer( quester, pm );
|
|
}
|
|
}
|
|
|
|
public static bool CanMarkQuestItem( PlayerMobile pm, Item item, Type type )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context != null )
|
|
{
|
|
foreach ( MLQuestInstance quest in context.QuestInstances )
|
|
{
|
|
if ( !quest.ClaimReward && quest.AllowsQuestItem( item, type ) )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void OnMarkQuestItem( PlayerMobile pm, Item item, Type type )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context == null )
|
|
return;
|
|
|
|
List<MLQuestInstance> instances = context.QuestInstances;
|
|
|
|
// We don't foreach because CheckComplete() can potentially modify the MLQuests list
|
|
for ( int i = instances.Count - 1; i >= 0; --i )
|
|
{
|
|
MLQuestInstance instance = instances[i];
|
|
|
|
if ( instance.ClaimReward )
|
|
continue;
|
|
|
|
foreach ( BaseObjectiveInstance objective in instance.Objectives )
|
|
{
|
|
if ( !objective.Expired && objective.AllowsQuestItem( item, type ) )
|
|
{
|
|
objective.CheckComplete(); // yes, this can happen multiple times (for multiple quests)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool MarkQuestItem( PlayerMobile pm, Item item )
|
|
{
|
|
Type type = item.GetType();
|
|
|
|
if ( CanMarkQuestItem( pm, item, type ) )
|
|
{
|
|
item.QuestItem = true;
|
|
OnMarkQuestItem( pm, item, type );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static void HandleSkillGain( PlayerMobile pm, SkillName skill )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context == null )
|
|
return;
|
|
|
|
List<MLQuestInstance> instances = context.QuestInstances;
|
|
|
|
for ( int i = instances.Count - 1; i >= 0; --i )
|
|
{
|
|
MLQuestInstance instance = instances[i];
|
|
|
|
if ( instance.ClaimReward )
|
|
continue;
|
|
|
|
foreach ( BaseObjectiveInstance objective in instance.Objectives )
|
|
{
|
|
if ( !objective.Expired && objective is GainSkillObjectiveInstance && ( (GainSkillObjectiveInstance)objective ).Handles( skill ) )
|
|
{
|
|
objective.CheckComplete();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void HandleKill( PlayerMobile pm, Mobile mob )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context == null )
|
|
return;
|
|
|
|
List<MLQuestInstance> instances = context.QuestInstances;
|
|
|
|
Type type = null;
|
|
|
|
for ( int i = instances.Count - 1; i >= 0; --i )
|
|
{
|
|
MLQuestInstance instance = instances[i];
|
|
|
|
if ( instance.ClaimReward )
|
|
continue;
|
|
|
|
/* A kill only counts for a single objective within a quest,
|
|
* but it can count for multiple quests. This is something not
|
|
* currently observable on OSI, so it is assumed behavior.
|
|
*/
|
|
foreach ( BaseObjectiveInstance objective in instance.Objectives )
|
|
{
|
|
if ( !objective.Expired && objective is KillObjectiveInstance )
|
|
{
|
|
KillObjectiveInstance kill = (KillObjectiveInstance)objective;
|
|
|
|
if ( type == null )
|
|
type = mob.GetType();
|
|
|
|
if ( kill.AddKill( mob, type ) )
|
|
{
|
|
kill.CheckComplete();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static MLQuestInstance HandleDelivery( PlayerMobile pm, IQuestGiver quester, Type questerType )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context == null )
|
|
return null;
|
|
|
|
List<MLQuestInstance> instances = context.QuestInstances;
|
|
MLQuestInstance deliverInstance = null;
|
|
|
|
for ( int i = instances.Count - 1; i >= 0; --i )
|
|
{
|
|
MLQuestInstance instance = instances[i];
|
|
|
|
// Do NOT skip quests on ClaimReward, because the quester still needs the quest ref!
|
|
//if ( instance.ClaimReward )
|
|
// continue;
|
|
|
|
foreach ( BaseObjectiveInstance objective in instance.Objectives )
|
|
{
|
|
// Note: On OSI, expired deliveries can still be completed. Bug?
|
|
if ( !objective.Expired && objective is DeliverObjectiveInstance )
|
|
{
|
|
DeliverObjectiveInstance deliver = (DeliverObjectiveInstance)objective;
|
|
|
|
if ( deliver.IsDestination( quester, questerType ) )
|
|
{
|
|
if ( !deliver.HasCompleted ) // objective completes only once
|
|
{
|
|
deliver.HasCompleted = true;
|
|
deliver.CheckComplete();
|
|
|
|
// The quest is continued with this NPC (important for chains)
|
|
instance.Quester = quester;
|
|
}
|
|
|
|
if ( deliverInstance == null )
|
|
deliverInstance = instance;
|
|
|
|
break; // don't return, we may have to complete more deliveries
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return deliverInstance;
|
|
}
|
|
|
|
public static MLQuestContext GetContext( PlayerMobile pm )
|
|
{
|
|
MLQuestContext context;
|
|
m_Contexts.TryGetValue( pm, out context );
|
|
|
|
return context;
|
|
}
|
|
|
|
public static MLQuestContext GetOrCreateContext( PlayerMobile pm )
|
|
{
|
|
MLQuestContext context;
|
|
|
|
if ( !m_Contexts.TryGetValue( pm, out context ) )
|
|
m_Contexts[pm] = context = new MLQuestContext( pm );
|
|
|
|
return context;
|
|
}
|
|
|
|
public static void HandleDeath( PlayerMobile pm )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context != null )
|
|
context.HandleDeath();
|
|
}
|
|
|
|
public static void HandleDeletion( PlayerMobile pm )
|
|
{
|
|
MLQuestContext context = GetContext( pm );
|
|
|
|
if ( context != null )
|
|
{
|
|
context.HandleDeletion();
|
|
m_Contexts.Remove( pm );
|
|
}
|
|
}
|
|
|
|
public static void HandleDeletion( IQuestGiver quester )
|
|
{
|
|
foreach ( MLQuest quest in quester.MLQuests )
|
|
{
|
|
List<MLQuestInstance> instances = quest.Instances;
|
|
|
|
for ( int i = instances.Count - 1; i >= 0; --i )
|
|
{
|
|
MLQuestInstance instance = instances[i];
|
|
|
|
if ( instance.Quester == quester )
|
|
instance.OnQuesterDeleted();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void EventSink_QuestGumpRequest( QuestGumpRequestArgs args )
|
|
{
|
|
PlayerMobile pm = args.Mobile as PlayerMobile;
|
|
|
|
if ( !Enabled || pm == null )
|
|
return;
|
|
|
|
pm.SendGump( new QuestLogGump( pm ) );
|
|
}
|
|
|
|
private static List<MLQuest> m_EligiblePool = new List<MLQuest>();
|
|
|
|
public static MLQuest RandomStarterQuest( IQuestGiver quester, PlayerMobile pm, MLQuestContext context )
|
|
{
|
|
List<MLQuest> quests = quester.MLQuests;
|
|
|
|
if ( quests.Count == 0 )
|
|
return null;
|
|
|
|
m_EligiblePool.Clear();
|
|
MLQuest fallback = null;
|
|
|
|
foreach ( MLQuest quest in quests )
|
|
{
|
|
if ( quest.IsChainTriggered || ( context != null && context.IsDoingQuest( quest ) ) )
|
|
continue;
|
|
|
|
/*
|
|
* Save first quest that reaches the CanOffer call.
|
|
* If no quests are valid at all, return this quest for displaying the CanOffer error message.
|
|
*/
|
|
if ( fallback == null )
|
|
fallback = quest;
|
|
|
|
if ( quest.CanOffer( quester, pm, context, false ) )
|
|
m_EligiblePool.Add( quest );
|
|
}
|
|
|
|
if ( m_EligiblePool.Count == 0 )
|
|
return fallback;
|
|
|
|
return m_EligiblePool[Utility.Random( m_EligiblePool.Count )];
|
|
}
|
|
|
|
public static void TurnToFace( IQuestGiver quester, Mobile mob )
|
|
{
|
|
if ( quester is Mobile )
|
|
{
|
|
Mobile m = (Mobile)quester;
|
|
m.Direction = m.GetDirectionTo( mob );
|
|
}
|
|
}
|
|
|
|
public static void Tell( IQuestGiver quester, PlayerMobile pm, int cliloc )
|
|
{
|
|
TurnToFace( quester, pm );
|
|
|
|
if ( quester is Mobile )
|
|
((Mobile)quester).PrivateOverheadMessage( MessageType.Regular, SpeechColor, cliloc, pm.NetState );
|
|
else if ( quester is Item )
|
|
MessageHelper.SendLocalizedMessageTo( (Item)quester, pm, cliloc, SpeechColor );
|
|
else
|
|
pm.SendLocalizedMessage( cliloc, "", SpeechColor );
|
|
}
|
|
|
|
public static void Tell( IQuestGiver quester, PlayerMobile pm, int cliloc, string args )
|
|
{
|
|
TurnToFace( quester, pm );
|
|
|
|
if ( quester is Mobile )
|
|
((Mobile)quester).PrivateOverheadMessage( MessageType.Regular, SpeechColor, cliloc, args, pm.NetState );
|
|
else if ( quester is Item )
|
|
MessageHelper.SendLocalizedMessageTo( (Item)quester, pm, cliloc, args, SpeechColor );
|
|
else
|
|
pm.SendLocalizedMessage( cliloc, args, SpeechColor );
|
|
}
|
|
|
|
public static void Tell( IQuestGiver quester, PlayerMobile pm, string message )
|
|
{
|
|
TurnToFace( quester, pm );
|
|
|
|
if ( quester is Mobile )
|
|
((Mobile)quester).PrivateOverheadMessage( MessageType.Regular, SpeechColor, false, message, pm.NetState );
|
|
else if ( quester is Item )
|
|
MessageHelper.SendMessageTo( (Item)quester, pm, message, SpeechColor );
|
|
else
|
|
pm.SendMessage( SpeechColor, message );
|
|
}
|
|
|
|
public static void TellDef( IQuestGiver quester, PlayerMobile pm, TextDefinition def )
|
|
{
|
|
if ( def == null )
|
|
return;
|
|
|
|
if ( def.Number > 0 )
|
|
Tell( quester, pm, def.Number );
|
|
else if ( def.String != null )
|
|
Tell( quester, pm, def.String );
|
|
}
|
|
|
|
public static void WriteQuestRef( GenericWriter writer, MLQuest quest )
|
|
{
|
|
writer.Write( ( quest != null && quest.SaveEnabled ) ? quest.GetType().FullName : null );
|
|
}
|
|
|
|
public static MLQuest ReadQuestRef( GenericReader reader )
|
|
{
|
|
string typeName = reader.ReadString();
|
|
|
|
if ( typeName == null )
|
|
return null; // not serialized
|
|
|
|
Type questType = ScriptCompiler.FindTypeByFullName( typeName );
|
|
|
|
if ( questType == null )
|
|
return null; // no longer a type
|
|
|
|
return FindQuest( questType );
|
|
}
|
|
|
|
public static MLQuest FindQuest( Type questType )
|
|
{
|
|
MLQuest result;
|
|
m_Quests.TryGetValue( questType, out result );
|
|
|
|
return result;
|
|
}
|
|
|
|
public static List<MLQuest> FindQuestList( Type questerType )
|
|
{
|
|
List<MLQuest> result;
|
|
|
|
if ( m_QuestGivers.TryGetValue( questerType, out result ) )
|
|
return result;
|
|
|
|
return EmptyList;
|
|
}
|
|
}
|
|
}
|