Help with 'omit' and 'Note'

Posted by Rakon on Fri 30 Jun 2006 09:31 PM — 20 posts, 88,462 views.

USA #0
Ok, so heres the issue, in a trigger I have to 'omit-from-output', I want to still have it do a 'world.Note'. But for some reason, the note won't show up. Is there anyway to have the trigger line gagged, WITH the note showing up, without resorting to coloring the line black??
Russia #1
Put your Note in a script function and have the trigger call that.
USA #2
Ok then, so I have to have a different def: for every different note I want?

def sub(name,output,wildcards):
	color1 = str(wildcards[0])
	color2 = str(wildcards[1])
	message = str(wildcards[2])
	world.ColourTell(color1,color2,message)

Thats the script, but the wildcards would be found in the tirgger 'match' not the send, so..how could I do that for different triggers?

Australia Forum Administrator #3
I'm not sure I understand what you are doing here. Have your read this page about scripting?

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=6030

That shows how you access wildcards in a script.
USA #4
I even tried doing this trough an alias, that is have
"sub blah blah" call the function 'sub' but through the triggers send box.(with it sent to 'Execute' of course) If the 'omit' field is checked in the trigger box though, it still won't show the note.
USA #5
Nick,
I am trying to create a def for a trigger that will:

Omit the trigger match from ouput, without resorting to colouring it black on black.

Replace the trigger match with whatever I want via the def:
'sub' and coloured.


IE
Trigger match: This is a test.
Trigger Box 'omit-from-output' checked
Trigger calls script 'sub'

On matching that trigger should not show up,
but be replaced with instead a colouredNote, that'll show whatever I want.

Essentally, I am trying to make a Mushclient equivilant Zmud
'#sub'
Australia Forum Administrator #6
You should be able to use ColourNote in your sub to output whatever you want. Even with omit from output on, the text the script outputs will appear. However for reasons which are explained elsewhere you must call a script in your script file, not use "send to script".

Your examples are Python aren't they? I'm not that familiar with Python, but will do an example in Lua. This is my trigger:




<triggers>
  <trigger
   enabled="y"
   match="&lt;*hp *m *mv *xp&gt;*"
   omit_from_output="y"
   script="fixupline"
   sequence="100"
  >
  </trigger>
</triggers>


This is matching my prompt line, with: omit_from_output="y"

Now the script fixupline is this:


function fixupline (name, line, wildcards, styles)

  for k, v in ipairs (styles) do
    ColourTell (RGBColourToName (v.textcolour), 
                RGBColourToName (v.backcolour), 
                (string.gsub (v.text, "mv", "movement"))
                )
  end -- for loop
  Note ""
end -- fixupline


Lua trigger scripts get a fourth argument which is the style runs of the matching line. You can use those to retrieve the colours of the original line, if you want them.

This example changes "mv" to "movement", however preserving all existing colours. Also see this post:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=6609

That is an example of using a plugin and OnPluginPacketReceived to gag words from the input stream and omit it. You could do a similar thing to that to match on one word and replace it by another one.
Russia #7
To do what you want you'd need an alias to match on:

sub * * *


That would call a function like:


def sub(name, output, wildcs):
    world.ColourNote(wildcs[0], wildcs[1], wildcs[2])


So you have the right idea, the only thing you got wrong is trying to call that alias/function directly from a trigger. That simply won't work if your trigger omits from output. Instead, you'll have to make the trigger call an "intermediate" function, which will Execute your sub alias:


def myfunc(n,o,w):
    world.Execute("sub red black Some text.")


Only that won't work very well either, since you'd need to set up the colours and the sub text manually for each trigger. The ultimate solution would involve using ImportXML or AddTrigger(Ex) to automatically add both triggers and their functions. Without going into the details of actually adding the triggers (I don't do that much myself, so am shaky on details), it would look something like:


# ID's for function names
fnames = 0

def addSub(n,o,wildcs):
    """ First wildc is what to match, second - fore colour,
        third - back colour, fourth - text to display. """
    global fnames
    def callSub():
        world.Execute("sub %s %s %s" % (wildcs[1], wildcs[2], wildcs[3]))
    globals()["SubTrigger"+fnames]) = callSub
    world.AddTrigger(wildcs[0], omitFromOutput=True, script="SubTrigger"+fnames)
    fnames += 1


So basically, you use an alias to add a trigger that omits from output and calls a function with a dynamically generated name, which is added to the global space on the fly. You'd need to differentiate between regular expression and "plain" triggers (maybe "addsub" and "addsubre" aliases?). Another thing to consider would be a way to store the substitutes, which could be accomplished by storing the aliases' entire match text as a "splittable" list in a Mushclient var and Executing them all when the plugin is loaded. Something like:


subs = GetVariable("SubCalls")
if wildcs[10] not in subs.split("_$_"):
    SetVariable(subs+"_$_"+wildcs[10])


inside the addSub function, and:


def OnPluginOpen():
    subs = GetVariable("SubCalls").split("_$_")
    [world.Execute(sub) for sub in subs]
    return 1


Of course, GetVariable/SetVariable would require additional checks to make sure that the variable is not null.

Hope at least some of that makes sense :)
Amended on Sat 01 Jul 2006 03:31 AM by Ked
USA #8
Thank you for the help, Nick and Ked.

Ked:

Lets say I have a dict setup with the color names,values are Mushclient color numbers. I have an alias 'sub blah <some text> that calls the script function 'sub'. Which in turn, takes wildcard[0],looks up the color name, such as 'red' and returns the color code for it. In the same function it then does a ColorNote(wildcards[0] Value,black,<some text>).
My question is, if i have omit-from-output checked, and call the alias 'sub' from a world.Execute in a trigger 'send' box,will it show the ColorNote??
If not, from your reply:
Quote:

So you have the right idea, the only thing you got wrong is trying to call that alias/function directly from a trigger. That simply won't work if your trigger omits from output. Instead, you'll have to make the trigger call an "intermediate" function, which will Execute your sub alias:

I would need a function that does what the my alias 'sub' does. And instead, use that function in the alias script call box??
Australia Forum Administrator #9
The technique used by MUSHclient to handle triggers etc. is described here:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=6554

In that post it describes how "omit from output" omits from the start of the line received, to the end of the output buffer. Any scripted (send-to-script) additions to the output are thus also omitted, whether directly or indirectly via world.Execute.

To avoid that you must actually call a script function in your script file (put its name in the script box), as execution of those is deferred until after the omitting is done.

An alternative would be to use DoAfterSpecial, to do a ColourNote after a brief delay (eg. 0.1 second). This effectively separates the trigger processing from the displaying, and thus the display will now appear correctly.

#10
Hmm, I am rather bummed after I found out about this. Anyone has a suggestion on how to manage a huge number of text replacement ?

Like many of you, I use achaea and I have realized that what those ZMUD users have done is actually pretty nifty. I would also like to replace the actual messages from Achaea with a concise world.note messages. But since I cant put it directly in trigger and have to use main script file instead, I will face a severe management problem.

This is an example of what I'd like to do :

Original Output:
Someone stares at you, giving you the evil eye.
You feel a tightening sensation grow in your lungs.
Someone stares at you, giving you the evil eye.
You gasp as your fine-tuned reflexes disappear into a haze of confusion.
4269h, 2318m cexkdb-
You eat a piece of kelp.
Thank Maya, the Great Mother! Your clumsiness has been cured.

changed to :

Someone EVILEYES you.
Afflict : ASTHMA
Someone EVILEYES you.
Afflict : CONFUSION
4269h, 2318m cexkdb-
EAT HERB : Kelp
ASTHMA CURED

Much less spammy and gives you an instant overview of what is happening (especially if coloring is used).
Australia Forum Administrator #11
Actually, it is easily done. Let me check what you want to achieve? I presume you wanted to make a whole lot of triggers like this...


Match: Someone stares at you, giving you the evil eye.
Omit from output: yes
Send: ColourNote ("white", "red", "Someone EVILEYES you.")
Send to script: yes


And so on. Your problem is, that the output does not appear, yes?

All you need to do is make a generic "stub" script (that goes in your script file), that simply defers the output slightly, then it appears. This is how I have done it and it works fine:


deferred_notes = {}  -- table of things to be shown

-- show a deferred note - called from a trigger
function DeferNote (name, line, wildcards)

local k, v

  for k, v in ipairs (deferred_notes) do
    ColourNote (unpack (v))
  end -- for looop

  deferred_notes = {}  -- done with table

end -- function DeferNote 

-- add a deferred note to the table
function cn (...)
  table.insert (deferred_notes, arg)
end -- function cn


This is in Lua, the recommended script language, however the same idea could be done in other languages.

What this does is maintain a table (array) of outstanding things to be sent to the output window (deferred_notes).

Instead of doing a ColourNote in the trigger you do "cn" instead (short for ColourNote, but less typing). This simply takes the arguments and puts whatever you planned to note into the table.

Then the trigger calls the script DeferNote which walks through the table, outputting each one using ColourNote, and then empties the table ready for next time.

Now your trigger looks like this:


Match: Someone stares at you, giving you the evil eye.
Omit from output: yes
Send: cn ("white", "red", "Someone EVILEYES you.")
Send to script: yes
Script: DeferNote


You can do multiple 'cn' calls from a trigger, as they just get appended to the table. The same line can match on multiple triggers and that will work OK too.

My exact triggers were:


<triggers>
  <trigger
   enabled="y"
   match="Someone stares at you, giving you the evil eye."
   omit_from_output="y"
   script="DeferNote"
   send_to="12"
   sequence="100"
  >
  <send>cn ("white", "red", "Someone EVILEYES you.")
</send>
  </trigger>
  <trigger
   enabled="y"
   match="You feel a tightening sensation grow in your lungs."
   omit_from_output="y"
   script="DeferNote"
   send_to="12"
   sequence="100"
  >
  <send>cn ("black", "white", "Afflict : ", "white", "red", "ASTHMA")
</send>
  </trigger>
  <trigger
   enabled="y"
   match="You gasp as your fine-tuned reflexes disappear into a haze of confusion."
   omit_from_output="y"
   script="DeferNote"
   send_to="12"
   sequence="100"
  >
  <send>cn ("black", "white", "Afflict : ", "white", "red", "CONFUSION")
</send>
  </trigger>
</triggers>


Once you have put the stub routines into the script file you can forget about them, and just do your line replacement inside the triggers themselves. That is easy enough to manage.
#12
I had assumed all Note messages wont be displayed if "omit from output" box is checked. Thanks a lot for the examples Nick. I knew registering would be worth it :-)

I managed to get it working but there are still some problems though :

One thing I have noticed when I can only use one color scheme per line if I use ColourNote from python.Looking at http://www.gammon.com.au/scripts/function.php?name=ColourNote, it seems only Lua can use multiple colour scheme per line.
Is it simply a case of not yet documented feature ? If yes, how I can use multiple colours in python ?

But all in all, this is awesome !
Australia Forum Administrator #13
Use ColourTell, and then eventually finish the line by doing a Note of an empty string, or make the last thing on the line do a ColourNote, and not a ColourTell.

http://www.gammon.com.au/scripts/function.php?name=ColourTell

If you are using Python you will need to modify my examples a bit, but the general idea is there. You could set up a series of things you want said, and the Python script could do all but the last with ColourTell, and the last with ColourNote.
USA #14
Also Rakon, if you wish to make a trigger that triggers on the ColourNote(Tell, whichever Used) you will need to use the Simulate() function, and have a trigger which omits that, I have used this method in my latest version of my Group Monitor to "reformat" the display, dynamically color the line's values (HP based on %, MN based on %, tnl < 100, Alignment is a word + Good = Gold, Neutral = Gainsboro, and Evil = OrangeRed)

I Simulate the line, exactly as shown via ColourNote, and omit it from output, and as a backup omit i used colour codes within it, standard ANSI returns did not work, so I had to discover the escape sequence myself (converted 1B to decimal, chose 1B as that is what the debug simulate uses) so that it would be black text, so if it just so happened to fool the trigger somehow it would not be noticed.

Simulate("\27[30m"..table.concat({name,hp,mn,tnl,alignw}).."\27[0m\n")

As you can see I use LUA in my plugins, but am still on VB within my world file.

If you wish I can create a plugin which will do all the legwork for you, I just need ALL the messages you wish to alter, and All the messages they need be altered to. And the colours you wish them to be colored in.

The plugin will be in LUA of course, as Nick has stated it is now the primary language, and imho the best of them all as is usable by Linux users as well (my original reason for converting my plugins from VB to LUA)

Let me know if you want that,
Onoitsu2 (Venificius on Aardwolf)
#15
Another problem that I encountered. I basically have to change all triggers which text I want to replace to execute script "DeferNote". For one thing, some triggers are calling their own script, for another, there's so damn many of them.

An example of my trigger :

<triggers>
<trigger
enabled="y"
group="Asthma"
match="You feel a tightening sensation grow in your lungs."
omit_from_output="y"
regexp="n"
send_to="10"
sequence="100"
>
<send>afflict asthma</send>
</trigger>
</triggers>

<aliases>
<alias
script="afflict"
match="afflict *"
enabled="y"
send_to="10"
sequence="100"
>
</alias>
</aliases>

The reason why I use an alias is because it makes it easier to test stuff, easier to create a new trigger and in the trigger dialogue box, you can sort triggers according to their "Send" box.

Is there a way to omit the original output and replace it with colour.note called from an alias ?
That way, changing one alias / script would change all one or two hundreds affliction triggers that rely on this alias.
Australia Forum Administrator #16
Let's look at the technique first - to defer an alias we simply store up what the alias is to do, and then do it later, like this:


deferred_aliases = {}  -- table of things to be done

-- do a deferred alias - called from a trigger
function DeferAlias (name, line, wildcards)

local k, v

  for k, v in ipairs (deferred_aliases) do
    world.Execute (v)
  end -- for looop

  deferred_aliases = {}  -- done with table

end -- function DeferAlias 

-- add a deferred alias to the table
function defer (what)
  table.insert (deferred_aliases, what)
end -- function defer 


This is similar code to the previous example, but now we are calling world.Execute to execute (ie. process the command) that we previously saved. Now the trigger and alias look like this:


<triggers>
  <trigger
   enabled="y"
   match="You feel a tightening sensation grow in your lungs."
   omit_from_output="y"
   script="DeferAlias"
   send_to="12"
   sequence="100"
  >
  <send>defer ("afflict asthma")</send>
  </trigger>
</triggers>


<aliases>
  <alias
   match="afflict *"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>ColourNote ("black", "white", "Afflict : ", 
            "white", "red", string.upper ("%1"))</send>
  </alias>
</aliases>



Now the trigger adds to the list of things to be done "afflict asthma" by calling the defer function.

The defer function adds the item to a queue (list) and then the DeferAlias function called at the end of trigger processing calls world.Execute to send that to the MUSHclient command processor, which will catch the alias.
Amended on Thu 24 Aug 2006 09:39 PM by Nick Gammon
Australia Forum Administrator #17
Quote:

Is there a way to omit the original output and replace it with colour.note called from an alias ?
That way, changing one alias / script would change all one or two hundreds affliction triggers that rely on this alia


[EDIT] My original idea didn't work. :)

I tried sending all of the messages to an alias, and then calling DeferNote from the alias, but there is a timing problem there. The alias did its note, but then the trigger omitted it from output.

OK, we need another way ...
Amended on Thu 24 Aug 2006 09:20 PM by Nick Gammon
Australia Forum Administrator #18
You can automate large-scale trigger fixups, as triggers can be accessed from inside scripts. I made a script in the Immediate window to do this (although for safety you might put it into the script file).


function fix_one_trigger (name)

  if GetTriggerInfo (name, 6) -- omit from output
  and GetTriggerInfo (name, 15) == 10 -- send to execute
  and GetTriggerInfo (name, 4) == "" -- no script function
  then
    -- make "send" field into 'defer "<whatever>"'
    SetTriggerOption (name, "send", 
           'defer ("' .. GetTriggerInfo (name, 2) .. '")')
    -- make "send_to" to be "execute"
    SetTriggerOption (name, "send_to", "12")
    -- make "script" to be "DeferAlias"
    SetTriggerOption (name, "script", "DeferAlias")
    Note ("Fixed trigger " .. name)
  end -- if

end -- function fix_one_trigger


-- fix up all triggers
function fix_up_triggers ()

local k, v

  for k, v in GetTriggerList () do
    fix_one_trigger (v)
  end -- for

end -- function fix_up_triggers 

fix_up_triggers ()



What this does is:

  • Use GetTriggerList to find all triggers
  • For each one, call fix_one_trigger passing it the name of the trigger
  • If the trigger meets the requirements (you can add more of your own):
    • It is omitting from output
    • It is doing a "send to execute"
    • It has no script function yet
  • Then it changes it to "defer <whatever it was sending>"
  • Sends to script rather than execute
  • Puts the function name "DeferAlias" into the script name field


The only big problem this one would have is for multi-line stuff (eg. two aliases) if you are using them.

In that case modify the script to break the "send" text into lines and do a "defer" on each line.

You might put a test in the script for a newline inside the send text, and at least do a warning if you find it.

Warning - back up your world file before you play with this idea, you will have trouble reverting your triggers once you run the script.
Amended on Thu 24 Aug 2006 09:39 PM by Nick Gammon
#19
Hehe, I did try calling the DeferNote through an alias which is executed through world(execute) in the trigger.

I am aware of the settrigger functions, I just I dont like having triggers directly calling a script function. But I guess there's no way around it. A big thank you to Nick, for taking the time and energy in trying to solve this problem.

PS.
Maybe in the future this behaviour could be changed to make it easier, more intuitive ?