Author Topic: Listener  (Read 459 times)

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« on: June 23, 2012, 08:49:45 am »


               What is the trick to setting up a listener?

I've got the following in the PostSpawn userdefined script:
        SetListening(OBJECT_SELF, TRUE);
        SetListenPattern(OBJECT_SELF, "**", 0);
        SetListenPattern(OBJECT_SELF, TAG_MAGIC+"**"+"SPELL"+"**", 1000);
        SetListenPattern(OBJECT_SELF, TAG_MAGIC+"**"+"BARD"+"**", 1001);
        SetListenPattern(OBJECT_SELF, TAG_MAGIC+"**"+"HOWL"+"**", 1002);

But the patterns are not being picked up in the User Defined Dialog Event Script with the following:
// dialog event
    // Send the user-defined event if appropriate
    if(GetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT))
    {
        // Store what we just 'heard'
        object oShouter = GetLastSpeaker();
        int nMatch      = GetListenPatternNumber();
        string sMatch   = GetMatchedSubstring(0);
        SetLocalObject(OBJECT_SELF, "USERD_LAST_SHOUTER", oShouter);
        SetLocalInt(OBJECT_SELF, "USERD_LISTEN_PATTERN_NUMBER", nMatch);
        SetLocalString(OBJECT_SELF, "USERD_LISTEN_PATTERN_MATCH", sMatch);
        SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DIALOGUE));
    }

// user defined dialog event
    else if(nUser == EVENT_DIALOGUE) // ON DIALOGUE
    {
        // Initialize Event Vars
        object oShouter = GetLocalObject(OBJECT_SELF, "USERD_LAST_SHOUTER");
        int nMatch      = GetLocalInt(OBJECT_SELF, "USERD_LISTEN_PATTERN_NUMBER");
        string sMatch   = GetLocalString(OBJECT_SELF, "USERD_LISTEN_PATTERN_MATCH");
        string sMsg, sConv;

        object oCaster  = GetLocalObject(OBJECT_SELF, "CREATOR");
        string sArea    = LIME+"["+GREEN+GetName(GetArea(oShouter))+LIME+"] ";
        string sCreature= GetCreatureTypeDescription(oCaster, oShouter);

        if( oShouter==oCaster )
            return;

        if(nMatch==1) // shouter attacked
        {
            sConv   = " is attacked.";
        }
        if(nMatch==3) // shouter died
        {
            sConv   = " dies.";
        }
        else if(nMatch==1000) // spell cast
        {
            sConv   = " casts a spell.";
        } 
      else if(nMatch==1001) // spell bard song
        {
            sConv   = " sings.";
        }
        else if(nMatch==1002) // spell howl
        {
            sConv   = " howls.";
        }
        else if(nMatch==0)
        {
            sConv   = ": "+GREY+sMatch;
        }
        if(sMsg!="")
        {
            sMsg     = sArea + sCreature +sConv;
            SendMessageToPC(oCaster, sMsg);
        }
        // Garbage Collection
        DeleteLocalObject(OBJECT_SELF, "USERD_LAST_SHOUTER");
        DeleteLocalInt(OBJECT_SELF, "USERD_LISTEN_PATTERN_NUMBER");
        DeleteLocalString(OBJECT_SELF, "USERD_LISTEN_PATTERN_MATCH");
    }
               
               

               


                     Modifié par henesua, 23 juin 2012 - 07:52 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Listener
« Reply #1 on: June 24, 2012, 04:33:07 am »


               

henesua wrote...

What is the trick to setting up a listener?

I've got the following in the PostSpawn userdefined script:
        SetListening(OBJECT_SELF, TRUE);
        SetListenPattern(OBJECT_SELF, "**", 0);
        SetListenPattern(OBJECT_SELF, TAG_MAGIC+"**"+"SPELL"+"**", 1000);
        SetListenPattern(OBJECT_SELF, TAG_MAGIC+"**"+"BARD"+"**", 1001);
        SetListenPattern(OBJECT_SELF, TAG_MAGIC+"**"+"HOWL"+"**", 1002);

But the patterns are not being picked up in the User Defined Dialog Event Script with the following:


You have a problem right here.   Anytime something is spoken  the patten matching is only ran one time.  The first pattern that is matched is the  patern that the OnConversation script is ran with. 

So if my matching patterns where

Patern 1 =  "**56**"
Pattern 2 = "**12**"
pattern 0 = " **89**"

and my PC spoke "123456789"

The String would be checked against pattern 0 first.   It would match and the OnConversation script would run with the  GetListenPatternNumber() returning a 0 for the matched pattern.   It would never run for pattern 1 and 2 even tought they also matched.   

Since your first pattern checked against is "**"  it will match for everything spoken and none of the other patterns will ever be matched.    
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« Reply #2 on: June 24, 2012, 05:14:52 am »


                I thought that it worked the opposite to what you are stating. I thought the highest matching pattern is returned first.

http://palmergames.c...ternNumber.html 
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Listener
« Reply #3 on: June 24, 2012, 05:24:04 am »


               Looks like the 1.68 lexicon also say the same thing. When I was testing it however, The lowest was returned.

Just place a :

SpeakString(" Matched pattern ="+IntToString(nMatch));

into your code and see if the 0 pattern is always being matched.  

the SpeakString function will not refire the conversation event.
               
               

               


                     Modifié par Lightfoot8, 24 juin 2012 - 04:27 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« Reply #4 on: June 24, 2012, 05:29:53 am »


               so I should reverse my numbers.... from unusual to all inclusive
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Listener
« Reply #5 on: June 25, 2012, 03:34:17 pm »


               Question?
Why are you using Listening Patterns instead of the onChat event that was added with 1.69?

It is 100% reliable to be triggered on Player Chat. (Unless you need NPC Chat- in which case nwnx_chat is the plugin you need)


Listening Patterns on npcs are prone to failure for reasons such as

1. The speaking creature/object not being perceived quickly enough by the listener
2. Large Modules -> Low Priority AI on listener -> Slow response to listened phrases.
3. Possibly others.


In my server, I would typically attach a script to the areas
SetLocalString(oArea,"ON_CHAT","area_chatscript");

Then, in my module onChat event, I check the area that the player is in, and if it has a chat script, it runs that, using the chat args etc.

The system can be enhanced to get things like

1. Get nearestObject of type 'trigger' / 'waypoint' etc within 10ft etc
2. Is there an onChat event attached to that object
3. When the player chats in proximety to it, fire the onChat script attached to it instead.


This way, you simulate the ability of a listener listening in a specific area, but can do it without the need to actually have an npc listening.
I would opt for this system over listeners, because I would be able to modify/add to the scripts as much as I wanted, using nwnx_resman.
Where as your creature template might not compile/save properly, without having a ready made script at module build time - for the onListen event.


I would certainly advise you consider using the module onChat event, if you can.
It is alot more reactive.
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« Reply #6 on: June 25, 2012, 03:47:26 pm »


               Baaelos,
I agree with your approach, but I couldn't come up with a satisfactory way to use it in this instance. In general I am using the Module OnChat event (and quite extensively), but this listener is on a scry object. I implemented the clairaudience spell. The spell creates a scry object. And anything the scry object hears is sent back to the spellcaster.

I'd like to do this for familiars as well, but want to get the spell working first.

Is there an efficient way to do this via the Module's Chat event? As it is my chat script is fairly involved. I use it to parse emotes, text commands, languages, DM spy, logging etc....
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Listener
« Reply #7 on: June 25, 2012, 05:03:23 pm »


               


onModChat.nss

void main()
{
//Get objects here  (GetPCChatter(); )

//Your Code here


// Extra Object scripts here
object oArea = GetArea(oPC);
if(GetLocalString(oArea,"ON_CHAT") != "")
   {
        ExecuteScript(GetLocalString(oArea,"ON_CHAT"),OBJECT_SELF);
   }
//Now we try to take into consideration things like other local objects that might be listening
object oListener = GetNearestObjectByTag(oPC,"LISTENING_TAG");
if(oListener != OBJECT_INVALID)
{
    if(GetDistanceBetween(oPC,oListener) <= LISTENING_DISTANCE) // 5.00 - 10.00?
        {
             string sScript = GetLocalString(oListener,"ON_CHAT");
             ExecuteScript(sScript,oListener);
        }
}

//Or... how about objects in other areas?
object oSpyDevices = GetObjectByTag("SPY_DEVICE",1);
int i = 1;
while(oSpyDevices != OBJECT_INVALID)
{
     object oMaster = GetLocalObject(oSpyDevices,"MASTER");
     string sChatMessage = GetPCChatMessage();
     SendMessageToPC(oPC,sChatMessage);
     i++;
     oSpyDevices = GetObjectByTag("SPY_DEVICE",i);
}

}


Alot of this is pseudocode.
so dont rely on it being exact.

But, the gist is that
You can get objects near the pc chatter, find out if they have chat events defined via local strings, then fire them via execute script.

Or.
If the Object is a spy object, like a listening device, then you can have it created with a specific tag, and then loop through all objects in the module that share that tag, and then get the 'Master' object of that object, and relay the message to the master.
Note - You will need to add in the logic yourself to determine the rules as to whether a message is delivered or not. etc
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« Reply #8 on: June 25, 2012, 05:17:12 pm »


               I'll consider that, AND I did consider that approach once, but didn't come up with a satisfactory implementation.

Perhaps I can rewrite how messages are relayed, and clean that up. At present I relay messages to DMs, I relay translated languages etc... But this is not centralized because I am using DMFI code as well. Perhaps with one loop it could be made efficient enough.

Thanks.
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Listener
« Reply #9 on: June 25, 2012, 05:27:53 pm »


               the good thing about nwnscript, is that it does provide the basic functionality that we require, to simulate an object oriented coding language.

Just because you are dealing with your chat messages in one script, doesnt mean you cant have other objects also dealing with them.

Simply call ExecuteScript  - and target a specific placeable in your module, and turn that placeable, into some sort of processing unit for specific tasks.

nwnscript can be fun like that, sometimes I try to simulate a computer/engine sort of system, where one object processes, relays, processes, and relays etc
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« Reply #10 on: June 25, 2012, 07:25:28 pm »


               well that is not all that different to how i am handling this as a listener. i am just using the game object as intended... but anyway, i'll need to rewrite my chat script to do as you suggest.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Listener
« Reply #11 on: June 25, 2012, 10:33:40 pm »


               I myself, Like the use of the listening Objects for non global listening.   The filtering for the chat  events is a lot better.   You do not have to use the VM to parse through the string to find when your listening pattern is heard.   The listening patterns are therefore handled by much faster michine code then doing it through nwscript that has to get feed to the VM.    

One good point made was that the NPC does have to be able to see the PC to pick up the chat.  if the PC is right next to the listener but invisiable to him, He just will not be able to hear what he can not see.  

It is really to Bad that NWScript Does not have a internal function that would parse the PCChat message for several set listening patterns.

That is my two cents anyway.   I will also admit that I have no Idea how NWNx would change my opinion if I used it.
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Listener
« Reply #12 on: June 26, 2012, 12:39:07 am »


               Lightfoot, what do you mean by VM? Virtual Machine? I didn't fully follow what you were saying there as I don't know how NWN runs at the core.

Regarding the "limitation" of the listener's ability to perceive. I see that as a feature for my purposes since this is for scrying. I like that the perception event is built in to the object. I can take advantage of it, and modify the listener's perception scores and types of perception (blindsight, normal sight etc...) based on the scry spell that was cast.

Anyhow, I don't have any of this working properly yet. Since none of the PCs are yet casting clairaudience I can take a little bit of time getting this to work. Jut got a bard player however who is keen on the spell. So I've got there next 3 levels to get it right. '<img'>
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Listener
« Reply #13 on: June 26, 2012, 02:26:03 am »


               

henesua wrote...

Lightfoot, what do you mean by VM? Virtual Machine? I didn't fully follow what you were saying there as I don't know how NWN runs at the core.

 

Yes, VM =  Virtual Machine?

All of NWScript is compiled into VM code,  The Virtural machine code is then feed  to the NWscript VM,   So something as simple as say.  

 int  x = 5;  

if compiled into machine code ( native x86 machine instructions )
would take about  2 machine instructions to do.  Each one taking only one cpu cycle to do.  2 CPU cycles no big deal.  Now it is true, that it is acording to how it was compiled.  It could take a few more machine instructions than that. but I would put the count at no more then 5.   

    

The same simple statement compiled into VM code.  Is  3 VM instructions. 
 
Let me run a quick test..... 

...  Compiles to 4 VM instructions.  
 
RSADDI
CONSTI 
CPDWN
MOVSP

Now every VM instruction has an overhead of at least 27 Machine Instructions.  Every machine instruction being at least 1 cycle.  We are allready at 107 cycles without any of the VM instructions even being executed yet.    

The RSADDI instruction is at least 61 machine instructions over and above the 27 overhead.  
the CONSTI is  at least  another 53 Machine instructions. 
CPDWN another 100 
MVSP  another 51.    

That puts us at a minimun of  373 CPU cycles to just declair and assign one integer compaired the the Max 5 cycles the same code would take in native code.  


I think it was Something tthe.gray.fox  said in one of his posts that really got me looking into the VM.   Basicly it boild down to the fact that the Internal functions are faster then any custom function, written in NWscript, used to replace them. Reguardless of whether the internal functions are written poorly or not. 

In Reguards to a listener, In my opinon,  using GetListenPatternNumber  from a single listener,  Will always out perform a script trying to parse the chat from the OnPlayerChat event.   

Now for catching PC chat globally in the in the module the the PCChat event is the way to go.   But for single location monitoring, I would stick with the listener.   They are also easy to turn on and off with the SetIsListening function.   
 
               
               

               


                     Modifié par Lightfoot8, 26 juin 2012 - 01:31 .
                     
                  


            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Listener
« Reply #14 on: June 26, 2012, 12:42:26 pm »


               

Lightfoot8 wrote...

I myself, Like the use of the listening Objects for non global listening. The filtering for the chat events is a lot better. You do not have to use the VM to parse through the string to find when your listening pattern is heard. The listening patterns are therefore handled by much faster michine code then doing it through nwscript that has to get feed to the VM.

One good point made was that the NPC does have to be able to see the PC to pick up the chat. if the PC is right next to the listener but invisiable to him, He just will not be able to hear what he can not see.

It is really to Bad that NWScript Does not have a internal function that would parse the PCChat message for several set listening patterns.

That is my two cents anyway. I will also admit that I have no Idea how NWNx would change my opinion if I used it.


One could argue, that anything you speak, even if it is being processed by the Listener object, it is also being handled by the onPC Chat event anyway.
If anything, the same message is being processed twice.

I would just elect to re-use one of those processing instances, to do everything I need, than to make a listener process the message that has already been processed.
But its up for debate, everyone has their own preferrences.