new to scripting

Posted by Barum on Thu 01 Apr 2004 09:40 AM — 21 posts, 81,721 views.

#0
I've been looking over the forums the last couple of days, and I'm trying to get into scripting for MUSHclient.

A lot of the examples look like XML/HTML-style stuff, like:
<trigger
match="You eat a bloodroot leaf."
enabled="y"
sent_to="12">
<send>herbeaten = "bloodroot"</send>
</trigger>

I haven't been able to find documents on how to implement this sort of thing.

I guess what I'm asking for is advice on exactly which documents I should be reading to get a firm base on which to start scripting.

The first thing I'd like to learn how to do is have any incoming and outgoing tells re-coloured and have the "tells"/"tell" portion of the text changed to "TELLs"/"TELL".
Amended on Thu 01 Apr 2004 09:42 AM by Barum
USA #1
Hmm. First off, triggers, timers and aliases are not part of the scripting as they tend to be in other clients. These things are inbuilt and handled differently. The reason they look like XML in the examples is because they are saved to your world file in that format *and*, in order to make posting and copying of them easier, Mushclient lets you 'copy' or 'paste' them to and from the editors in that format. This means you could open a world file with notepad, copy out a trigger, alias or timer and paste it into the list of a new world directly without changing anything. It also means you can paste one into the forum and someone else can directly copy and paste that into their own world.

Since world files and plugins are both entirely XML based, this also means that everything for a plugin, including the real scripts, can be placed on the forum and simply copied off, pasted into your favorite editor and saved as an XML file. All you have to do then is install the plugin. You could do the same with world files, except that the main script file is a seperate file for those, since it makes editing and reloading it easier. Plugins are supposed to be more or less already debugged and thus not something you edit a lot.

Anyway.. What you want to do is partly very easy, and partly frustrating. In the simplist terms you need a trigger that matches (I assume without an example):

^(You tell|.* tells you)\: .*

This is a regular expression, since a normal trigger can't match on more than one situation. The XML for it, if all you wanted was to change the color, would look something like:

<trigger
  match="^(You tell|.* tells you)\: .*"
  custom_colour="17"
  enabled="y"
  regexp="y"
  other_text_colour="gold"
  other_back_colour="black">
</trigger>

Again, this is just an easy copy-and-paste XML code. If it was actually done in script, it would look more like:

world.addtriggerex "telltrigger", "^(You tell|.* tells you)\: .*", "", 1057, -1, 0, "", "", 0, 100
world.settriggeroption "custom_colour", "17"
world.settriggeroption ""other_text_colour", "gold"

Needless to say that this would need to either be executed in the command window, in a script file or using the ctrl-I immediate mode input box. Unless you really are designing a script to change the trigger, you really don't want to mess with this. lol The XML is a lot easier and, even in scripting, there is now an import function that lets you import the XML, instead of using the 3 commands above.

Anyway. That would color it. Just one problem.. Currently triggers cannot 'replace' text, so your second need isn't directly possible. With a few changes you can sort of do it though.
<trigger
  match="^(You tell .*|.* tells you)\: (.*)"
  enabled="y"
  sent_to="12"
  regexp="y"
  omit_from_output="y"
  <send>telltemp = instr("%1", "tells")
telltemp2 = instr("%1", "tell")
if telltemp then
  tellname = left("%1", telltemp - 1)
  colourtell "gold", "black", tellname & " "
  colourtell "gold", "black", "TELLS you: "
  colournote "gold", "black", "%2"
else
  tellname = right("%1", len("%1") - (telltemp2 + 4))
  colourtell "gold", "black", "You TELL" & tellname & ": "
  colournote "gold", "black", "%2"
end if</send>
</trigger>

Note: I haven't tested this code, so the 'left' and 'right' commands may not work as intended. They could be off by one character, making the name retrieved incorrect. Just adjust how much gets added or subtracted (the - 1 and + 4 bits) to fix it.

This would do what you intend, more or less. Changing text in a line is problematic at best, while simply coloring it is very simple, changing it basically requires that you have to redisplay the line manually through script commands. Omit_from_output tends to complicated matters, since while you can read the original lines from the output window, omit_from_output erases them *before* this information can be retrieved. Also, if your mud automatically line wraps tells on there end, the situation become about 50 times more complex. The one I play on does and I need four triggers to color stuff properly. Even then, it fails about 10% of the time when something unexpected happens, like someone on the staff dump ansi graphics on the screen before the trigger that ends the coloring is triggered. Good luck if yours is like this too. ;)
Amended on Thu 01 Apr 2004 06:20 PM by Shadowfyr
#2
Thank you for your reply.

Would it be equally as hard to color in just a specific word, or specific portion of any line?
USA #3
Yes and no. Coloring one word is simple if at the start or end of a line, since you can specifically tell it using regular expressions to look for the beginning or end of the line. Otherwise, you can use regular expression and just the word, but it will then match any and all instances of the word. In other words:

"^word" - Will only match if the word is at the start of a line.
"word$" - Will only match only if it is the last thing on a line.
"word" - Will match any time it is used.

So.. If you had a sentence like:

word is a word, but not the word

All three triggers would match. If you had:

word is a noun, but not a verb

the first trigger and the third would match, but not the second. If it was:

sam is a word, but not a verb

then only the third trigger would match. And so on. However, unless you turn on the setting for "repeat on same line", the third trigger would only color the first instance of 'word', like so:

word is a word, but not the word

Trying to match and color single words in 'specific' lines usually requires using the same omit_from_ouput option and colourtell trick I gave you in the last post to make it what you want. The drawback is that you lose whatever original colors you had, so you have to recolor everything yourself through colournote.
#4
What about just coloring a specific word whenever it comes up? I can't figure it out.

It's a feature listed in the help file, so I assume I'm just missing something incredibly obvious.
Russia #5
Just use Shadowfyr's second trigger to do that (slightly modified), you can copy and paste the following into the Triggers dialogue:


<triggers>
  <trigger
   custom_colour="1"
   enabled="y"
   keep_evaluating="y"
   match="(?:\s|\'|\"|\-|\:|^)word(?:\s|\,|\.|\:|\;|\-||\!|\?|\'|\"$)"
   regexp="y"
   repeat="y"
   sequence="100"
  >
  </trigger>
</triggers>


Once you've pasted this trigger, open it in the Triggers dialogue and replace the 'word' in the Match box with whatever word you want to have coloured, also change the colour to whatever you prefer. That trigger will match on:

You say a word.
word
It was a simple word, yet it possessed much power!

But won't match on:

A wordless language.
You begin to wield a sword.
Amended on Fri 02 Apr 2004 01:59 PM by Ked
Australia Forum Administrator #6
Quote:

What about just coloring a specific word whenever it comes up? I can't figure it out.


Those replies give too complex a way of doing it. (Sorry guys!).

To colour a specific word simply do this (make a trigger and):


Match: the word or phrase
Regular expression: checked
Change colour to: the desired colour


If you want the same line to be matched against other words (ie. colouring *different* words) also do this:

Keep evaluating: checked

If you want to colour the *same* word more than once on a particular line also do this:

Repeat on same line: checked

Amended on Fri 02 Apr 2004 11:17 PM by Nick Gammon
#7
Thanks Nick!

Is there any easy way to have MUSHclient color in a word that occurs in certain sentence patterns? I'd like to color in the damage word in combat sentences, and have a different color for the word depending on whether or not I'm the person doing the damage.

These are the sentences:

Your pierce grazes the wooden soldier.
Barum's pierce grazes the wooden soldier.
The wooden soldier's bite misses you.
The wooden soldier's bite misses Barum.
Barum's stab injures the rabid beast.

Basically:
Your [attack type] [damage] [mob or player name].
[Mob or player name]'s [attack type] [damage] [mob or player name].
[Mob or player name]'s [attack type] [damage] you.

I only want to color the [damage] word.
USA #8
This is what I was trying to explain. The answer is 'NO'. At least not unless you capture the entire line, omit it from output and redisplay it in your script using colourtell and colournote. There is no existing way to tell Mushclient that it should 'only' effect a specific word or pattern, though a lot of us wish there was, especially since inserting information or replacing a word would be far easier if the client could be told:

Match: You hit giant.
Only change 'hit' by replacing it with 'clubbed' and making it gold.

There is no exclusionary method I am aware of (unless regular expressions have something to both 'match', but also 'exclude'). Anything that gets matched gets colored and actual replacement, like above isn't even allowed at all. :(
Australia Forum Administrator #9
The simple thing would be if you only want to colour one of the forms, and have the other the default. For instance, if you want to colour:

Your [attack type] [damage] [mob or player name].

Then make a trigger that catches the other ones, give it a lower sequence, and do *not* check "keep evaluating". That way it will match, and the line will not be passed to the colouring trigger.

The other approach is to dissect the line and recolour it in a small script. For instance, the above line might match a trigger like:

Match: ^Your (kick|punch|hit) (wounds|eviscerates|misses) (.*)$

This gives you three wildcards, %1, %2, and %3, which you can then redisplay like in this small script:


sub OnMyAttack (name, line, wildcards)
  ColourTell "white", "black", "Your " & wildcards (1)
  ColourTell "red", "black", wildcards (2)
  ColourNote "green", "black", wildcards (3)
end sub


To make this work make the trigger call the script OnMyAttack and set it to "omit from output" so the new coloured line replaces the original one.
Canada #10
Quote:
There is no exclusionary method I am aware of (unless regular expressions have something to both 'match', but also 'exclude').
There is:

?:

For example:

^(You tell .*|.* tells you)\: (?:.*)
Russia #11
Coming back to colouring a single word in a line... For some reason I thought that this was Python's extension to regexp, but it seems to work:

\bword\b


\b is a word boundary and includes all punctuation, spaces, newline and endline, being a compressed version of those prefix and suffix groups in my trigger above.

#12
Thanks for the replies.

I got it to sort of work:

^Your (.*) (misses|
wounds|
MUTILATES|
DEMOLISHES|
scratches|
mauls|
EVISCERATES|
DEVASTATES|
grazes|
decimates|
DISMEMBERS|
OBLITERATES|
hits|
devastates|
MASSACRES|
ANNIHILATES|
injures|
maims|
MANGLES|
ERADICATES) (.*).$


sub OnMyAttack (name, line, wildcards)
ColourTell "white", "black", "Your " & wildcards (1)
ColourTell "red", "black", wildcards (2)
ColourNote "white", "black", wildcards (3)
end sub

Now there aren't any spaces between wildcard 1, 2, and 3.
I'm completely inexperienced in programming so I'm not sure how to fix it.

Nick, have you ever considered implementing an easy scripting system similar (or just support the "Z" scripting) as the "Z" client? It's very useful, and a lot of dumb people like myself would really appreciate it.

edit: broke up the matching so the message wouldn't break tables.
Amended on Sat 03 Apr 2004 07:51 AM by Barum
Canada #13
sub OnMyAttack (name, line, wildcards)
ColourTell "white", "black", "Your " & wildcards (1) & " "
ColourTell "red", "black", wildcards (2) & " "
ColourNote "white", "black", wildcards (3)
end sub
USA #14
Yeah Magnum. I am aware of that exclusion option, however from the standpoint of coloring lines or words, Mushclient has a serious flaw. It colors *everything* it matched and ignores the exclusion. This imho shouldn't work that way. Since it has to parse the string through the expression to determine what matches, it should be able to see the exclusion and correctly handle it for coloring as well. After all, you told it to *not* include that as part of the result, even though it matches it.

I don't know, maybe there is some obscure reason why it only works for wildcards, but that doesn't entirely make sense either, since I expect you can exclude things that are not explicitly placed in () too. In which case it does what? Pretty much nothing in this implimentation.
Australia Forum Administrator #15
Quote:

Nick, have you ever considered implementing an easy scripting system similar (or just support the "Z" scripting) as the "Z" client? It's very useful, and a lot of dumb people like myself would really appreciate it.


I thought the existing scripting was reasonably simple, and at least you have a choice of languages (Jscript, VBscript, Perlscript etc.).

The "send to script" option in triggers etc. lets you do one-line scripts easily enough, eg. to turn on a trigger group.

Quote:

I am aware of that exclusion option, however from the standpoint of coloring lines or words, Mushclient has a serious flaw. It colors *everything* it matched and ignores the exclusion.


The regexp processing is really a separate module (PCRE) which MUSHclient just calls. It then gets back a list of the matching parts of the line which becomes the wildcards, and the entire matching expression, which is the part that is coloured. By not "reinventing the wheel" for regexp processing it is, at least, pretty reliable.

I didn't closesly look at what happens with colouring parts of lines with exclusions, but could believe that the exclusion part forms part of the "matching expression".

eg. if you want to match on:

dog NOT FOLLOWED BY cat

then "dog fish" matches the expression, and "dog fish" is the *matching text* (not just dog).



USA #16
True.. The problem here is that the user may intended:

Find "cats and dogs", but color "dogs" NOT "cats and".

It is a matter of interpretation. On one hand, yes you want it to match the entire line, color it *and* return specific wildcards. On the other hand, there is nothing stopping you from returning the wildcards in a second trigger and specifically using the exclusions to color it like above, if you had the option to actually tell it to use only the wildcards as the 'text' to be colored. So you can very precisely match the line and word to be colored, but *only* color the one word you want.

I am not sure how complicated that would be, but it would avoid the insanity that arises when all you want is to color one stupid word on a known line, but not the entire line or every instance of the word, even in other lines. The only way to do it now is to omit from output and unless you are 100% sure of what the colors are supposed to be, you can't really do it without jumping through hoops.

Of course... It did occur to me that you might be able to match the line in an earlier sequenced trigger, read the line style, then omit it in a second later trigger and finally output the changed line. I haven't tried it though. In theory... The omition shouldn't happen until 'after' the first trigger finishes, so the line should still be there and intact when reading the original, then be removed only after the second trigger hits. If so, then it solves most problems, though it isn't exactly what I would call elligant.
Russia #17
I still don't understand the need for reading the styles and replacing the line with custom text, maybe I am missing something. Why not just use triggers without any "loops" instead?


<triggers>
  <trigger
   enabled="y"
   keep_evaluating="y"
   match="cats and dogs"
   name="cats_and_dogs"
   regexp="y"
   send_to="12"
   sequence="10"
  >
  <send>EnableTrigger &quot;colour_dogs&quot;, 1</send>
  </trigger>
  <trigger
   custom_colour="14"
   enabled="n"
   match="\bdogs\b"
   name="colour_dogs"
   regexp="y"
   send_to="12"
   sequence="11"
  >
  <send>EnableTrigger &quot;colour_dogs&quot;, 0</send>
  </trigger>

</triggers>


The output would be:

cats and dogs, went for a walk
(cats and dogs) went for a walk
fishes and dogs are swimming in the pool

You could obviously make the first trigger in the sequence as complicated as you wanted to, and make as many secondary triggers as you like. This is much simpler than parsing styles.
Amended on Tue 06 Apr 2004 07:45 AM by Ked
USA #18
you could also do the same thing weve gotten used to doing for multiline triggers.

Have one trigger which matches on the whole line, which turns on a second trigger (which only matches on the word) and then colors it, turns itself off. The only complications one would run into is if the word is multiple lines on that line.

I suppose you could get around that by turning off repeat on same line (if you wanted only the first) or if youd prefer one of the latter ones, write a small script, or a second one-word trigger (and have trigger A(full line) turn on trigger B(one word, first word) which turns on trigger c (one word, and color) and then they all turn off save A.

Yes, its not quite streamlined, but it does work.

Still have the problem of replacing words, but thats a whole other issue entirely, it just continues coming up with the omit from output/resend new text stuff.

edit: seems like Ked and I were barking up the same tree (no pun intended) at the same time.
Amended on Tue 06 Apr 2004 07:46 AM by Flannel
Russia #19
Ah, I see - completely forgot about that "replace a single word in a line" issue. Maybe because I never needed to do that :) How about a new callback - World.Substitute? That could be called only while a trigger is being processed and would pass back to the client a number of the trigger's wildcard along with the text to replace the contents of that wildcard in the line and the colour the new text should be displayed in:


<triggers>
  <trigger
   enabled="y"
   keep_evaluating="y"
   match="cats and (dogs)"
   name="cats_and_dogs"
   regexp="y"
   send_to="12"
   sequence="10"
  >
  <send>Substitute 1, "lions", "red"</send>
</triggers>


Output: cats and lions

This would mean that any style processing would be done by the client itself. Since I don't know how exactly Mushclient parses output through triggers and what information it has access to at any given point of time while processing a trigger, I have no idea if this is doable at all.

P.S. Yep, that happens to me alot lately :P
USA #20
Well Ked.. If you do want it to match say the first and third instance of a word in the same line, but not the second? Point is, your can kind of get around the coloring issue, but doing so requires a rediculous hack job, tricks that only work in specific cases or complicated scripting to bypass the built in coloring features. I hate hacks when the only reason you need them is because the existing feature doesn't work the way you expect. Same goes for word substitution, assuming the two trigger thing I suggested works, you still have to jump through hoops to make it happen, despite the fact that any feature that would let you tell it, "make changes effect wildcards only", would solve both problems. In theory. Yes, there is bound to be a little bit of extra processing to make it work right if/when you used that option, but it would work. Best we can claim of other sultions is 'they work, sometimes...'

The option would always both *replace* and *color*. So, if the send field is blank, you replace with the original text, if not, you replace it with what it in that field. Anything replaced will bump the rest of the line (and its styles) to the right, but the word inserted will either inherit the style at the point the match happened or gain a new style from the color settings. The only major issue is being able to fine down the coloring to a specific wildcard(s). I doubt people are going to stop screaming about this Nick.

The methods needed to 'fix' the issue are too complicated, can't be done without those complicated methods and don't always work as expected. All of which are turn offs for anyone that did use such things in another client, but are trying to make the switch to this one. Its a bit of a turn off even for me, and I am good enough to think my way around most problems.