Hey all!
I'm currently playing around on a mud that displays NPC's on a grid. Not the typical north east south west type of thing. I'm at like (2, 5) and there's all sorts of enemy around me at different points. Each unit has 2 numbers representing their position. It looks like this
Soldiers: (1, 5) (4, 6) (15, 12) (12, 2)
And if you are flying it'll add a third number for altitude. Fighters: (1, 15, 11) etc
I can create a second script for the map with 3 numbers not a problem. I just need to know how to write this out at all.
You see this information with a quick command and I want to have each set of numbers be placed into a variable so I can run a script to evaluate which one is closest to me at any given moment as I move around on the map.
Being blind it's kind of hard to swift through all the numbers. It doesn't exactly break them up real well with the screen reader it just sounds like 12, 15, 2, 3, 12, 5, 6 and it's hard to figure out while in combat.
So is there a way I can write a script that create a set of variables that collects the data? Is there away to have mush client stick to a naming convention while auto generating variables?
I was thinking like enemy_1, if enemy_1 is full create enemy_2 with this data.
I don't exactly no how to work this out. I'm quite familiar with SetVariable and GetVariable with Lua.
I would also need a way to count how many variables the script needs to look at as well. So if there's 30 it doesn't just look at the first ten.
As far as the script that evaluates who's closest to me I figured that'd just be a lone list of elseif statements.
thanks for any help and I appreciate the hell out of this site and mush client.
Tables are a fundamental part of Lua, and Lua is a great scripting language. :)
Rather than trying to create variables with names, how about just shoving the information into a table? It's a bit unclear from your description whether or not you might have multiple mobs at the same location.
Assuming you don't, for the moment, then the problem becomes simple. Just make the coordinate the key. So, for example:
mobs = {} -- create the table (once only)
mobs [ "5,6" ] = true -- there is a mob here
Once the mob is in the table you can iterate over the table (that is, see what is in it).
Another way of doing this, which would also handle multiple mobs at one location, is to push stuff into a numbered table. So in this case, you would do something like:
mobs = {} -- create the table (once only)
table.insert (mobs, "5,6") -- there is a mob here
In this case the mobs table now has an entry (number 1) with the value "5,6" which is the coordinate of the mob.
The nice thing about this, from your point of view, is that the entries are now simply numbered. So you have mob 1, mob 2, and so on.
The fact that it is in the table means it exists, and its value tells you its location.
Example code:
mobs = {} -- create the table (once only)
table.insert (mobs, "5,6")
table.insert (mobs, "2,1")
table.insert (mobs, "4,2")
print ("There are", #mobs, "enemies")
for k,v in ipairs (mobs) do
print ("Mob number", k, "is at location", v)
end
Output:
There are 3 enemies
Mob number 1 is at location 5,6
Mob number 2 is at location 2,1
Mob number 3 is at location 4,2
As for proximity, let's assume you know where the mobs are (using triggers, for example).
This example code shows how you can work out which one is closer. I amended my earlier example to have a table within a table (which is perfectly fine), where the sub-table has the x and y coordinate as two items. I'll let you work out how to do the Z coordinate as well. :)
mobs = {} -- create the table (once only)
table.insert (mobs, {x=5,y=6})
table.insert (mobs, {x=2,y=1})
table.insert (mobs, {x=4,y=2})
print ("There are", #mobs, "enemies")
for k,v in ipairs (mobs) do
print ("Mob number", k, "is at location", v.x, ",", v.y)
end -- for
me = {x=4,y=3}
-- find closest
closest_distance = nil
closest_mob = nil
for k, v in ipairs (mobs) do
-- calculate distance to me
distance = math.sqrt ((me.x - v.x)^2 + (me.y - v.y)^2)
if not closest_distance then
-- first time
closest_distance = distance
closest_mob = k
else
-- record this if it is closer
if distance < closest_distance then
closest_distance = distance
closest_mob = k
end -- if
end -- if
print (string.format ("Mob %i is %0.1f away", k, distance))
end -- for
print ("Closest mob is number", closest_mob, "at",
mobs [closest_mob].x, "," , mobs [closest_mob].y)
This calculates the distance between you (hardcoded here) and the various mobs, and tells you which is closest. In this example the output was:
There are 3 enemies
Mob number 1 is at location 5 , 6
Mob number 2 is at location 2 , 1
Mob number 3 is at location 4 , 2
Mob 1 is 3.2 away
Mob 2 is 2.8 away
Mob 3 is 1.0 away
Closest mob is number 3 at 4 , 2
thank you so much, for some reason I didn't get notified of responses to this thread so sorry it took so long. I appreciate your help with this and I'll sit down today and try to add this in.
I understand it from a read through point of view so we'll see what happens when I start to put it in the world.
As always thank you and you're the best.
Okay I have no clue what I'm doing I guess.
So in order to generate the table I need to place it in a trigger that's pulling the coordinates of the enemy right?
I read on the page about tables the table doesn't go in the trigger so how do I populate the table with the info?
Note: I'll clean up the trigger and turn it into a reg-exp once I have it all working.
Trigger is:
<triggers>
<trigger
enabled="y"
group="test"
match="*(*, *, *)*"
send_to="12"
sequence="100"
>
<send>mobs = {} -- create the table (once only)
for k,v in ipairs (mobs) do
print ("Mob number", k, "is at location", v.x, ",", v.y)
end -- for
me = {x=4,y=3}
-- find closest
closest_distance = nil
closest_mob = nil
for k, v in ipairs (mobs) do
-- calculate distance to me
distance = math.sqrt ((me.x - v.x)^2 + (me.y - v.y)^2)
if not closest_distance then
-- first time
closest_distance = distance
closest_mob = k
else
-- record this if it is closer
if distance < closest_distance then
closest_distance = distance
closest_mob = k
end -- if
end -- if
print (string.format ("Mob %i is %0.1f away", k, distance))
end -- for
So a few problems I'm getting is it generates 8 mobs when there's only 5.
Anytime I try to add a Z value to me it throws up there needs to be a ) error.
It also repeats itself 8 times with all the enemies. Here's a sample of just one. It throws all this out 7 more times.
I understand it's creating 8 mobs because there is 8 table.inserts but if I don't have 8 it only fills the table in with one. So how do I have it fill in with the right amount with out over populating or under populating?
Also the mobs locations aren't exactly right you'll notice that it repeats the same mob at the same coordinates a bunch.
There are 8 enemies
Mob number 1 is at location 10 , 20
Mob number 2 is at location 10 , 20
Mob number 3 is at location 10 , 20
Mob number 4 is at location 10 , 20
Mob number 5 is at location 10 , 20
Mob number 6 is at location 10 , 20
Mob number 7 is at location 10 , 20
Mob number 8 is at location 10 , 20
Mob 1 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 2 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 3 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 4 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 5 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 6 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 7 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Mob 8 is aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4).1f away
Closest mob is number 1 at 10 , 20
aircraft: bomber 1 (10, 20, 18), bomber 2 (6, 20, 20), bomber 3 (3, 12, 15), bomber 4 (2, 10, 13), and bomber 5 (20, 13, 4)
There are 8 enemies
Mob number 1 is at location 3 , 2
Mob number 2 is at location 3 , 2
Mob number 3 is at location 3 , 2
Mob number 4 is at location 3 , 2
Mob number 5 is at location 3 , 2
Mob number 6 is at location 3 , 2
Mob number 7 is at location 3 , 2
Mob number 8 is at location 3 , 2
Mob 1 is Current Coordinates: (3, 2, 13).1f away
Mob 2 is Current Coordinates: (3, 2, 13).1f away
Mob 3 is Current Coordinates: (3, 2, 13).1f away
Mob 4 is Current Coordinates: (3, 2, 13).1f away
Mob 5 is Current Coordinates: (3, 2, 13).1f away
Mob 6 is Current Coordinates: (3, 2, 13).1f away
Mob 7 is Current Coordinates: (3, 2, 13).1f away
Mob 8 is Current Coordinates: (3, 2, 13).1f away
Closest mob is number 1 at 3 , 2
When you find a mob you insert it into the table 8 times, so I'm not sure why you are surprised that you are seeing 8 of everything. Why not do it once?
print (string.format ("Mob %i is %0.1f away", k, distance))
Inside a trigger, %0 means the entire matching line, so you need to double the percent symbols. My example was not written for inside a trigger so I didn't to that.
In other words:
print (string.format ("Mob %%i is %%0.1f away", k, distance))
mobs = {} -- create the table (once only)
This isn't once only, that is doing it every time the trigger fires. Unless this is what you want (like, you refresh the list every time) you really want:
if not mobs then
mobs = {} -- create the table (once only)
end -- if no mobs table yet
I'm a tad confused. So the way the map works is it gives you all of the mobs with one command.
I type map and it gives me
Aircraft: (1, 5, 3) (4, 5, 5) and so on.
Their positions are random and the amount of aircraft can change as you move.
So every few kills I pull up the map again to see mob location.
It's not one trigger every time I encounter a mob. So I'm not sure if I've got the table set up right. It needs to automatically add the different mobs from one trigger.
I hope I'm explaining this well.
I thought that by adding a bunch of table.inserts it would just fill in the mobs positions and not add in the amount of table.inserts I had. So if there was 5 mobs it'd stop at 5.
I appreciate your help.
It would certainly help if you actually copy/pasted MUD output. It is hard to work from:
Aircraft: (1, 5, 3) (4, 5, 5) and so on.
Surely you can just give two or three examples? It seems you have clarified that a single trigger tells you about a lot of mobs, is that right? And you don't need to remember them, next time they will be in different positions?
Okay so mobs move randomly sorry I didn't make that super clear. So while I might be at 15, 12, 11 killing a mob the other ones might be moving towards me or away from me. to check this I just type map to see their coordinates.
Mud output:
Combat Zone Alpha: Enemy alert!
Aircraft: Bomber 1 (10, 5, 1), Bomber 6 (15, 4, 5), Bomber 3 (5, 5, 10), Bomber 20 (12, 8, 6)
Current position: (6, 6, 1)
With your examples for the closest mob I wasn't sure if it was looking at all of them and just showing the closest one or if it was only looking at one and showing me that one. That's why I was confused on how many times I needed to use table.insert.
From what I've seen there can be 30 or more mobs or 5 it just depends on if people have been killing in the area.
Tables are a great way to look at this data I'm just so new to how they work I got lost.
I apologize for not being clear.
So you have two events there. One announces where the bombers are, which we can use to save in a table, as I said.
The second event is finding your own location. That is where we can compare that to the previously-remembered aircraft locations.
Like this:
<triggers>
<trigger
enabled="y"
match="Aircraft: *"
send_to="12"
sequence="100"
>
<send>
mobs = { } -- table of mobs
function processMob (name, x, y, z)
-- get rid of comma from previous mob
name = string.gsub (name, "^, ", "")
-- insert into table
table.insert (mobs, { name = name, x = x, y = y, z = z } )
end -- processMob
-- split wildcard into individual mobs
string.gsub ("%1", "(.-) %((%d+), (%d+), (%d+)%)", processMob)
</send>
</trigger>
<trigger
enabled="y"
match="Current position: (*, *, *)"
send_to="12"
sequence="100"
>
<send>
me = { x = %1, y = %2, z = %3 }
mobs = mobs or { } -- in case no mobs
-- find closest
closest_distance = nil
closest_mob = nil
for k, v in ipairs (mobs) do
-- calculate distance to me
distance = math.sqrt ((me.x - v.x)^2 + (me.y - v.y)^2 + (me.z - v.z)^2)
if not closest_distance then
-- first time
closest_distance = distance
closest_mob = v
else
-- record this if it is closer
if distance < closest_distance then
closest_distance = distance
closest_mob = v
end -- if
end -- if
print (string.format ("%%s is %%0.1f km away", v.name, distance))
end -- for
if closest_mob then
print (string.format ("Closest is %%s at (%%i, %%i, %%i)",
closest_mob.name, closest_mob.x, closest_mob.y, closest_mob.z))
else
print ("No aircraft nearby")
end -- if
</send>
</trigger>
</triggers>
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.
Example output from your test data:
Bomber 1 is 4.1 km away
Bomber 6 is 10.0 km away
Bomber 3 is 9.1 km away
Bomber 20 is 8.1 km away
Closest is Bomber 1 at (10, 5, 1)
Awesome, I appreciate the fact you can just throw this all together.
On the second trigger it spits this out twice.
Compile error
World: Zack
Immediate execution
[string "Trigger: "]:20: 'then' expected near '&'
Compile error
World: Zack
Immediate execution
[string "Trigger: "]:20: 'then' expected near '&'
I'm not exactly sure what it wants me to do to fix it. Also I can't remember if blank lines count as line numbers.
Yep I've read how to copy xml a few times. I tried it and was getting this error as well.
[WARNING] Clipboard
Line 24: Unexpected end-of-file while looking for </triggers> (Cannot load)
I made a blank world file and tried pasting it there as well and still got the same error.
I then selected and pasted the send to script information into a trigger to see if it would work that way. It didn't throw up an error.
The second trigger was throwing up the errors no matter what I did.
I have it working now. Not sure what happened but I created a new world file and the second trigger started working after closing mush client and a restart of my computer.
I typically don't ignore documentation as I'm doing everything I can to understand the best practices for mush client. I only make a thread if I can't figure this stuff out. I've been working on this for a few weeks now.
I appreciate your help.
I must admit I've read through the code a bunch and it's made me realize how much I don't quite understand.
I'm going to paste the code I'm using but keep in mind it's me literally going in and cutting out anything related to the Z coordinate so I'm sure I messed it up.
Trigger that finds my current location.
<triggers>
<trigger
enabled="y"
group="mapammo"
ignore_case="y"
keep_evaluating="y"
match="Current Coordinates: *, *"
name="curco"
send_to="12"
sequence="100"
>
<send>
me = { x = %1, y = %2 }
mobs = mobs or { } -- in case no mobs
-- find closest
closest_distance = nil
closest_mob = nil
for k, v in ipairs (mobs) do
-- calculate distance to me
distance = math.sqrt ((me.x - v.x)^2 + (me.y - v.y)^2)
if not closest_distance then
-- first time
closest_distance = distance
closest_mob = v
else
-- record this if it is closer
if distance < closest_distance then
closest_distance = distance
closest_mob = v
end -- if
end -- if
print (string.format ("%%s is %%0.1f km away", v.name, distance))
end -- for
if closest_mob then
print (string.format ("Closest is %%s at (%%i, %%i)",
closest_mob.name, closest_mob.x, closest_mob.y))
else
print ("No aircraft nearby")
end -- if
</send>
</trigger>
</triggers>
Ammo trigger to populate the table:
<triggers>
<trigger
group="mapammo"
keep_evaluating="y"
match="Ammo: *"
name="ammoco"
omit_from_output="y"
send_to="12"
sequence="100"
>
<send>mobs = { } -- table of mobs
function processMob (name, x, y)
-- get rid of comma from previous mob
name = string.gsub (name, "^, ", "")
-- insert into table
table.insert (mobs, { name = name, x = x, y = y } )
end -- processMob
-- split wildcard into individual mobs
string.gsub ("%1", "(.-) %((%d+), (%d+), ", processMob)
</send>
</trigger>
</triggers>
Again any help is greatly appreciated and I'm sorry for failing to grasp this concept.