mtindell submitted a new resource:

David's Conversational Townsperson - Customizable Talking NPCs

This was written for RunUO 2.0 by David, some of you may remember him from RunUO forums, he was a moderator there and genuinely helpful guy. He wrote a lot of the early tutorials also. I know this has been uploaded to some of the RunUO archive sites, but someone asked about it in another thread here at ServUO so I thought I would post it here as well so that when people search here they can find it.

I have tested the script with ServUO 55.1 on Mono 5.18.0.268 and it works without any...

Read more about this resource...
 
Crash and burn on this script.

Code:
Server Crash Report
===================

ServUO Version 0.5, Build 6472.26597
Operating System: Microsoft Windows NT 6.2.9200.0
.NET Framework: 4.0.30319.42000
Time: 3/16/2019 4:02:59 AM
Mobiles: 13929
Items: 882518
Exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.FileNotFoundException: Could not find file 'C:\Users\ted_g\Desktop\ServUO-master reboot\ServUO-master\shardbackup.3-14-2019\Data\SpeechRules.xml'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
   at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
   at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
   at System.Xml.XmlTextReaderImpl.OpenUrlDelegate(Object xmlResolver)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.CompressedStack.Run(CompressedStack compressedStack, ContextCallback callback, Object state)
   at System.Xml.XmlTextReaderImpl.OpenUrl()
   at System.Xml.XmlTextReaderImpl.Read()
   at System.Xml.XmlReader.MoveToContent()
   at System.Data.DataSet.ReadXml(XmlReader reader, XmlReadMode mode, Boolean denyResolving)
   at System.Data.DataSet.ReadXml(String fileName, XmlReadMode mode)
   at Server.Mobiles.Data.SpeechData.LoadData()
   at Server.Mobiles.Data.SpeechData.Initialize()
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Server.ScriptCompiler.Invoke(String method) in c:\Users\Ted Gress\Desktop\ServUO-master reboot\ServUO-master\ServUO-master\Server\ScriptCompiler.cs:line 667
   at Server.Core.Main(String[] args) in c:\Users\Ted Gress\Desktop\ServUO-master reboot\ServUO-master\ServUO-master\Server\Main.cs:line 558

Clients:
- Count: 0
 
Well your C:\Users\ted_g\Desktop\ServUO-master reboot\ServUO-master\shardbackup.3-14-2019\Data\SpeechRules.xml file doesnt exist
 
Come on PyrO I know that much. The question is what is the functionality of that file and why is it missing from the .rar?
 
readme said:
With version 1.1 the starter speech database is intentionally mis-named to avoid overwriting any current databases. To use the starter database look in the RunUO\Data folder and rename the file “Rename.SpeechRules.XML” to “SpeechRules.XML.”
 
or use the empty one and add the rules / speeches with the edtior. Only now looked at the package.
 
Brilliant! Would it be possible to assign different rules to different unique NPCs so not all NPCs utilize the same rules? For instance, I want a specific NPC to provide specific banter in a tavern when engaging with players, providing them interesting information regarding secrets around the town he is locally found in. But I don't want EVERY NPC Townsperson I create to have access to the same information via their speech.
 
I have not dug that deep into it, but I believe Djeryv's Odyssey server has NPCs that appear to do what you are asking. You will see 2 of them together, and they will gossip about objects and locations that can provide hints. Believe they are in the barfly script, but how easy it would be to unmarry it from the rest of the systems I do not know, haven't tried yet.

There is a much more involved script than both of these however called BaseConvo that utilizes the conversation fragments from the T2A Demo. Basically The server loads those files, which are quite extensive, with various responses based on player notoriety, gender, profession AND the NPCsjob, location and "attitude" for a myriad of questions/keywords. You have to modify a line in each vendor type to have them respond, which is nice because you can exclude some npcs vendors from the system (like bankers in crowded areas) the fragments have keywords for all the locations, major characters from the ultima series, job descriptions, and even some political responses based on the british/blackthorn rivalry. It can be found in the rebirth release, and you will need to make many changes to get it working with ServUO. I had much of it working earlier, but haven't looked at it in quite a while. Custom responses were also possible and a template was included. The fragments themselves also have notes from designer Raph Koster and others that make it an interesting thing to look at in a historial context regardless.
 
Last edited:
Well, I was trying to work backwards from the Townsperson script itself and see how it reads the SpeechRules.xml file in general. Not knowing how to do any C# is really throwing a wrench in that idea for me lol. I can see some lines that reference the SpeechHandler (line 905 to be exact in Townsperson.cs)

But this is all Chinese to me. I have no idea what any of it means and when I open the SpeechDataHandler.cs file, I see this:
Code:
        public static void LoadData()
        {
            SpeechData.blockRequests = true;

            SpeechData.dsSpeechRules.Reset();
            SpeechData.dsSpeechRules.ReadXml("Data\\SpeechRules.xml", XmlReadMode.ReadSchema);

            SpeechData.blockRequests = false;
        }

Townsperson.cs:
Code:
public virtual void SpeechHandler(SpeechResponse sr)
        {
            string m_name = (sr.Speaker.NameMod == null ? sr.Speaker.Name : sr.Speaker.NameMod);
            string m_response = String.Format(sr.Response, this.Name, m_name, this.Region);

            if (!isEmpty(sr.Response))
                Say(m_response);

            if (sr.Animation > 0)
                Animate(sr.Animation, 5, 1, true, false, 0);

            if (sr.Reaction > 0)
            {
                ReactionCallBackState rcbs = new ReactionCallBackState(sr.Speaker, sr.Reaction);
                Timer.DelayCall(TimeSpan.FromMilliseconds(1800), new TimerStateCallback(ReactionCallBack), rcbs);
            }

            if (Logging == LogLevel.Basic || Logging == LogLevel.Debug)
                TownspersonLogging.WriteLine(this, "Responding to Speech Event: \"{0}\"", m_response);

            if (!isEmpty(sr.Reward))
            {
                if (Logging >= LogLevel.Basic)
                    TownspersonLogging.WriteLine(sr.Speaker, "{0} in {1} Creating {2}", this.Name, this.Region, sr.Reward);

                Type type = SpawnerType.GetType(sr.Reward);

                try
                {
                    object o = Activator.CreateInstance(type);

                    if (o is Item)
                    {
                        Item item = (Item)o;
                        sr.Speaker.AddToBackpack(item);
                    }
                    else if (o is Mobile)
                    {
                        Mobile mob = (Mobile)o;
                        mob.MoveToWorld(this.Location, this.Map);
                    }
                }
                catch
                {
                    TownspersonLogging.WriteLine(sr.Speaker, "{0} Exception Caught creating {1}", this.Name, sr.Reward);
                    sr.Speaker.SendMessage("Exception Caught creating " + sr.Reward); // debugging
                }

            }

            if (!isEmpty(sr.DelObject))
            {
                if (Logging >= LogLevel.Basic)
                    TownspersonLogging.WriteLine(sr.Speaker, "{0} in {1} Deleting {2}", this.Name, this.Region, sr.DelObject);

                Type type = SpawnerType.GetType(sr.DelObject);

                bool ActionTaken = false;

                try
                {
                    for (int i = 0; i < sr.Speaker.Items.Count; ++i)
                    {
                        Item item = (Item)sr.Speaker.Items[i];

                        if (item.GetType() == type)
                        {
                            item.Consume();
                            ActionTaken = true;
                            break;
                        }
                    }
                    if (!ActionTaken)
                    {
                        sr.Speaker.Backpack.ConsumeTotal(type, 1, true);
                    }
                }
                catch
                {
                    TownspersonLogging.WriteLine(sr.Speaker, "{0} Exception Caught consuming {1}", this.Name, sr.DelObject);
                    sr.Speaker.SendMessage("Exception Caught consuming " + sr.DelObject); // debugging
                }
            }
        }
        #endregion

Now, if only I can add some if's or something to this where it says something like: if Tavernperson.cs use this file "SpeechRulesTavernPerson.xml" instead. But I don't know how to do that. lol

Any ideas?
 
Server Crash Report =================== ServUO Version 0.5, Build 7506.3271 Operating System: Microsoft Windows NT 6.2.9200.0 .NET Framework: 4.0.30319.42000 Time: 8/24/2020 6:05:58 AM Mobiles: 43432 Items: 164113 Exception: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index Server stack trace: at System.Collections.ArrayList.get_Item(Int32 index) at Server.Mobiles.Data.Response.GetResponse(String Said, Mobile Speaker, Townsperson tp) in E:\Electronic Arts\ServUO-master\Scripts\Custom\Townsperson\Scripts\Custom\Townsperson\SpeechDataHandlers.cs:line 178 at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs) at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase) at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData) at GetResponseDelegate.EndInvoke(IAsyncResult result) at Server.Mobiles.Townsperson.SpeechCallback(IAsyncResult ar) in E:\Electronic Arts\ServUO-master\Scripts\Custom\Townsperson\Scripts\Custom\Townsperson\Townsperson.cs:line 896 at System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(IMessage msg) at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() at System.Threading.ThreadPoolWorkQueue.Dispatch() Clients: - Count: 1 + 127.0.0.1: (account = tommenquar) (mobile = 0x75 'Mitsurugi')

Anyone have any idea on why this crash is happening? basically we use a keyword that i input and it crashes on one specific table.
 
SpeechDataHandlers.cs : line 178 - ArrayList : Index was out of range. Must be non-negative and less than the size of the collection.
 
What was the keyword used? Do others not cause this issue? Possible the keyword is used elsewhere in ServUO.

Also
This is a really weird path:
E:\Electronic Arts\ServUO-master\Scripts\Custom\Townsperson\Scripts\Custom\Townsperson\

Looks like you unnecessarily double nested your folders. Make sure you speech rules file is not also strangely pathed
 
yea that path is an oversight. i have corrected it. However i belive it has something to do ith the Specific NPC Name string. string works fine on the NPC it is intended for, however if used with any other townsperson NPC, than it crashes the server.
 
Has anyone managed yet to make this work in a way that makes each NPC unique so different NPCs can say different things?
 
Has anyone managed yet to make this work in a way that makes each NPC unique so different NPCs can say different things?

Take a look at BaseConvo from Rebirth. It does what you are asking, but you'd be making a lot of small edits to existing scripts. Basically every vendor. Regions, playermobile.

Townsperson is more of a drag and drop, add a little life to the server, BaseConvo is full fledged interaction.
 
Last edited:
Back