Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ MUSHclient
➜ Lua
➜ Auto Rescue(sort of)
It is now over 60 days since the last post. This thread is closed.
Refresh page
Pages: 1
2
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #15 on Tue 10 Apr 2007 10:47 PM (UTC) |
| Message
| One approach here is multi-line triggers. I suggest searching the forum for that, there have been a few posts about them.
Basically they work best if you have a well-defined "marker" at the start and end of the multiple lines. For example:
You are carrying: <-- start marker
blah
blah
blah
3 items. <-- end marker
That way a multi-line regexp can match reliably. The end marker could be something less specific (like a prompt line), eg.
You are carrying: <-- start marker
blah
blah
blah
<28hp 105m 110mv> <-- end marker
Or it can be something that the other lines don't have, eg.
You are carrying: <-- start marker
blah
blah
blah
The Cathedral Altar <-- end marker (not indented)
You are before the cathedral's altar. Those of ill luck or poor faith often
come here to pray to their God for guidance, or chance. An aisle leads back
south through the cathedral, while a large public board lies to the west.
In this example the inventory lines are indented, but normal lines are not. So, the multi-line trigger could look for a line that is not indented.
Whatever method you use, the matching text will now be a multiple-line string, that you can then parse and extract out individual info (like being short of water).
A handy way of iterating through a multi-line string is to use the getlines.lua extension supplied with MUSHclient.
Here is an example to get you started, although it isn't perfect:
require "getlines"
test = [[
You are carrying:
a big waterskin [2]
a loaf of bread
a small waterskin
a note with a brown feather
a small leather bag
a map
a long, black sword
]]
for line in getlines (test) do
-- check water
count = string.match (line, "a big waterskin %[(%d+)%]")
if count and tonumber (count) < 5 then
print "We are low on water"
end -- match and too little water
end -- for
This uses the getlines iterator inside a for loop to read one line at a time. It then does a string.match to find a line with "a big waterskin [n]" in it.
However this particular one wouldn't handle "a big waterskin" on its own (presumably one of them), or having no waterskins at all. I'll leave you to play with that. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| ReallyCurious
USA (50 posts) Bio
|
| Date
| Reply #16 on Wed 11 Apr 2007 08:27 AM (UTC) Amended on Wed 11 Apr 2007 08:28 AM (UTC) by ReallyCurious
|
| Message
| I'm not sure how to use this.
Example:
The wooden surface of the chest is in perfect condition.
chest (carried) :
a cup of tea [2]
a cup of coffee [2]
I can set up a multi-line trigger to match: \AThe wooden surface of the chest is in perfect condition.
and then specify 4 lines for multi-lines.
But I'm not sure how to iterate this using getlines.
The trigger will capture those four lines into a string, but the string is going to be dynamic for each time I look into the chest so I'm not sure how to word the test = [[]]
Can you give me an example on how to use getlines with the example from above?
thanks | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #17 on Wed 11 Apr 2007 10:05 PM (UTC) |
| Message
| | Show us your trigger and let's take it from there. With the trigger matching you have a start point. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| ReallyCurious
USA (50 posts) Bio
|
| Date
| Reply #18 on Thu 12 Apr 2007 09:03 AM (UTC) Amended on Thu 12 Apr 2007 10:02 AM (UTC) by ReallyCurious
|
| Message
| <triggers>
<trigger
enabled="y"
expand_variables="y"
lines_to_match="4"
match="\AThe wooden surface of the chest is in perfect condition.(.*)"
multi_line="y"
regexp="y"
send_to="12"
sequence="100"
>
<send>require "getlines"
test = [[%1]]
count = string.match (line, "a cup of tea %[(%d+)%]")
if count and tonumber (count) < 5 then
ColourNote ("white", "green", "LOW ON TEA!")
end
</send>
</trigger>
</triggers>
er, well, basically I was trying to do something like this. Define 'test' to equal every line afterwards using a wildcard.
The wooden surface of the chest is in perfect condition.
chest (carried) :
a cup of tea [2]
a cup of coffee [2]
And then I tried to changed match to:
\AThe wooden surface of the chest is in perfect condition.\n(?P<line1>.*)\n(?P<line2>.*)\n(?P<line3>.*)\z
At least the last one didn't give me an error :/ even though nothing happened. | | Top |
|
| Posted by
| ReallyCurious
USA (50 posts) Bio
|
| Date
| Reply #19 on Thu 12 Apr 2007 09:19 AM (UTC) Amended on Thu 12 Apr 2007 10:27 AM (UTC) by ReallyCurious
|
| Message
| count = string.match (line, "a big waterskin %[(%d+)%]")
Also, about this line. %[(%d+)%] - Could this just be [/d+] ?
p.s. What's the syntax for 'does not equal'?
example:
for i,v in ipairs(resc) do
if v == "%2" then
I want if v 'does not equal' "%2" then
cheers | | Top |
|
| Posted by
| Shaun Biggs
USA (644 posts) Bio
|
| Date
| Reply #21 on Sat 14 Apr 2007 09:47 PM (UTC) |
| Message
| The Lua matching uses % instead of a \ for escaping characters. You need to escape brackets because they would normally define a list of valid characters. [A-Z] matches anything from "A" to "Z", while %[A-Z%] matches "[A-Z]" What you are trying to grab is within brackets, so you have to declare that the number is inside of it. The %d+ is the same as \d+ when declaring the trigger.
And ~= is does not equal.
if v ~= "%2" then |
It is much easier to fight for one's ideals than to live up to them. | | Top |
|
| Posted by
| Shaun Biggs
USA (644 posts) Bio
|
| Date
| Reply #22 on Sat 14 Apr 2007 09:58 PM (UTC) |
| Message
| Another quick observation. If there is only one cup of tea, then you will get an error, since the string.match will return a nil value. To fix that:
count = tonumber( string.match(line, "a cup of tea %[(%d+)%]") ) or 1
if count < 5 then
ColourNote ("white", "green", "LOW ON TEA!")
end
|
It is much easier to fight for one's ideals than to live up to them. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #23 on Sat 14 Apr 2007 11:10 PM (UTC) |
| Message
| I have been experimenting with this a bit. Multi-line triggers are the devil to get right, but this is moving in the right direction.
<triggers>
<trigger
enabled="y"
expand_variables="y"
lines_to_match="4"
match="\AThe wooden surface of the chest is in perfect condition\.(?s)(.*)"
multi_line="y"
regexp="y"
send_to="12"
sequence="100"
>
<send>
require "getlines"
test = [[%1]]
function get_count (s)
local count = string.match (s, ".- %[(%d+)%]") or 1
return tonumber (count)
end -- get_count
local tea = 0
local coffee = 0
for line in getlines (test) do
if string.match (line, "a cup of tea") then
tea = get_count (line)
end -- some tea
if string.match (line, "a cup of coffee") then
coffee = get_count (line)
end -- some coffee
end -- for
print ("We have", tea, "tea")
if tea < 5 then
ColourNote ("white", "green", "LOW ON TEA!")
end
print ("We have", coffee, "coffee")
if coffee < 5 then
ColourNote ("white", "green", "LOW ON COFFEE!")
end
</send>
</trigger>
</triggers>
There were a number of problems with your initial one, for one thing it wasn't matching at all, on the subsequent lines, which you could see if you had printed out %1.
I have added the '(?s)' option to the regexp. This is "dot matches all" option. Without it, newlines are not considered a match for dot. With it added it considers everything, including newlines, for the wildcard.
Next, in processing tea and coffee, you need to allow for 3 possibilities:
- Not mentioned --> that is, no tea at all
- a cup of tea --> that is, a single cup (no number given)
- a cup of tea [n] --> that is, more than one cup
Thus, the first thing I do, before the getlines loop, is to set tea and coffee counts to zero initially. If we never get a line about tea or coffee, then they stay zero.
Next, we look for a line matching "a cup of tea" or "a cup of coffee", without a number necessarily.
Then, if we get such a line we pass it to a function (get_count), which extracts out the number. If it doesn't find a number we assume a single item.
Finally, outside the loop, we test for having enough tea and coffee.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #24 on Sat 14 Apr 2007 11:19 PM (UTC) |
| Message
| You still have a problem here, that you are matching only 4 lines. If you get something like this:
The wooden surface of the chest is in perfect condition.
chest (carried) :
a fig leaf
a cup of tea [2]
a cup of coffee [2]
Then it will not notice the coffee because that is in the 5th line. The obvious thing to do is bump up the number of lines to match, but then you hit another problem.
Say you make it 50 lines, and then receive this:
The wooden surface of the chest is in perfect condition.
chest (carried) :
a fig leaf
a cup of tea [2]
a cup of coffee [2]
The Cathedral Altar
You are before the cathedral's altar. Those of ill luck or poor faith often
come here to pray to their God for guidance, or chance. An aisle leads back
south through the cathedral, while a large public board lies to the west.
Exits: south west.
Now the problem is that it won't match at all, because it hasn't received 50 lines yet. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #25 on Sat 14 Apr 2007 11:23 PM (UTC) |
| Message
| The ideal thing is to have something that marks the end of the inventory, and put that as part of the regular expression, and make a "tail match" rather than a "head match".
That is, rather than \A at the start of the regular expression put \Z at the end of it.
It would be nice if the inventory list finished with something you can rely on (like: 10 items).
If not, you might have to force something out. For example:
inventory
think End of inventory
Anything like that which will put a "marker" at the end of your inventory list. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Shaun Biggs
USA (644 posts) Bio
|
| Date
| Reply #26 on Sat 14 Apr 2007 11:34 PM (UTC) |
| Message
| | Wouldn't it just be easier to have a table stored in the world script space that you add to each time you get a line from the inventory? That way you could have a trigger matching on anything that doesn't look like an inventory item to turn it off. That's how I generally deal with this stuff, since I can't ever get multi line triggers to work quite right 100% of the time. |
It is much easier to fight for one's ideals than to live up to them. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #27 on Sun 15 Apr 2007 12:25 AM (UTC) Amended on Sun 15 Apr 2007 12:26 AM (UTC) by Nick Gammon
|
| Message
| All in all that is probably safer, and easier at the end of the day. Here is an example of doing that:
<triggers>
<trigger
match="(^<)|^$"
name="inventory_stopper"
regexp="y"
send_to="12"
sequence="40"
>
<send>
-- don't need to track inventories any more
EnableTrigger ("inventory_line", false)
EnableTrigger ("inventory_stopper", false)
-- in case no table yet
inventory_table = inventory_table or {}
for _, item in ipairs (inventory_table) do
print ("Found inventory item", item)
end -- for
</send>
</trigger>
<trigger
match="*"
name="inventory_line"
send_to="12"
sequence="50"
>
<send>
table.insert (inventory_table, "%0") -- add inventory item to our table
</send>
</trigger>
<trigger
enabled="y"
match="The wooden surface of the chest is in perfect condition."
send_to="12"
sequence="100"
>
<send>
inventory_table = {}
EnableTrigger ("inventory_line", true)
EnableTrigger ("inventory_stopper", true)
</send>
</trigger>
</triggers>
We have 3 triggers here. Two of them are initially not enabled.
The third one matches the start of an inventory list (in this case "The wooden surface of the chest is in perfect condition.". It empties out the inventory_table.
It enables the other two triggers. One is designed to capture inventory lines and add them to the table. The other is designed to detect the end of an inventory list. At present I have it set to detect blank lines, and lines starting with "<" (ie. a prompt).
All I am doing at present at the end of the inventory list is to list the items it has found, however this is the place to analyze the inventory and see what you have. This trigger also turns off the capture of inventory lines, so that we need an "inventory starter" to start the whole process off again. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| ReallyCurious
USA (50 posts) Bio
|
| Date
| Reply #28 on Mon 16 Apr 2007 09:32 AM (UTC) Amended on Tue 17 Apr 2007 12:13 AM (UTC) by Nick Gammon
|
| Message
| I tried out both ways and after a while of messin around I decided to stay without using getlines(had some trouble with it still after hours). This is working great now. So, using these three triggers I 'PRETTY MUCH' have a multi-line trigger. Here's the end result:
<triggers>
<trigger
expand_variables="y"
match="^(\d+)H|^$"
name="inventory_stopper"
regexp="y"
send_to="12"
sequence="40"
>
<send>
-- don't need to track inventories any more
EnableTrigger ("inventory_line", false)
EnableTrigger ("inventory_stopper", false)
-- in case no table yet
inventory_table = inventory_table or {}
local tea = 0
local coffee = 0
function get_count (item)
local count = string.match (item, ".- %[(%d+)%]") or 1
return tonumber (count)
end
for _, item in ipairs (inventory_table) do
if string.match (item, "a cup of tea") then
tea = get_count(item)
end
if string.match (item, "a cup of coffee") then
coffee = get_count(item)
end
end
print ("tea", tea)
print ("coffee", coffee)
</send>
</trigger>
<trigger
enabled="y"
match="The wooden surface of the chest is in perfect condition."
send_to="12"
sequence="100"
>
<send>
inventory_table = {}
EnableTrigger ("inventory_line", true)
EnableTrigger ("inventory_stopper", true)
</send>
</trigger>
<trigger
match="*"
name="inventory_line"
send_to="12"
sequence="50"
>
<send>
table.insert (inventory_table, "%0") -- add inventory item to our table
</send>
</trigger>
</triggers>
Thanks for the help. | | Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
94,664 views.
This is page 2, subject is 2 pages long:
1
2
It is now over 60 days since the last post. This thread is closed.
Refresh page
top