Page 1 of 2

[FUNCTION] Find entities with text and method filtering

Posted: Thu Nov 01, 2012 7:59 pm
by Lark
Here is a utility function I wrote as part of my cryogenic chamber I hope to publish later today. It looks at one or more squares at or around a given square and return objects found there filtering on key words found in the object name or on objects that have specific methods. These filters can be satisfied if any or only if all key words are matched. It looks at a single square, the single square that is in front of the square specified by coordinates (uses self.facing), or all squares surrounding (and including) either of the first two options. If you're looking at all squares surrounding something, you can filter the objects to only return objects that are facing the given (or self.facing offset) square. Although self.facing is used instead of passing the facing value to the script, it can easily be added to the argument list if you need it. Just don't confuse it with the "facing" logical.

The function is called as follows: table = findAll(level, x, y, keys, all, surrounding, facing, facingUs)
  • level - level to inspect
  • x - the x coordinate of the base square
  • y - the y coordinate of the base square
  • keys - a table of key words to be used as a filter.
    • If omitted or specified as {"*"}, all objects are returned unfiltered.
    • if a key word starts with a colon (":" - i.e. ":getWeight") then it is interpreted as a method name which the object must have to be included.
    • if it does not start with anything special (i.e. "door") then the text must appear somewhere in the name to be included.
  • all - specified as string "all" means all key words must be true to be included in the results. Otherwise any match will include the object.
  • surrounding - true to inspect the current square and all surrounding squares for objects.
  • facing - true to "move" the base square that is inspected from the x, y location specified to the square the script itself is facing.
  • facingUs - true to include only objects that are surrounding the square inspected (as altered above) that are facing the square inspected.
Here are some examples:

Return all monsters in a given square: monsters = findAll(self.level, self.x, self.y, {":setAIState"}) Note that a group of monsters may be returned as a single monster group, or may be broken down into separate monsters, one for each group member.

Return all monsters in a given square that you can add objects to (you can't add objects to groups that I can tell): CanCarry = findAll(self.level, self.x, self.y, {":addItem", ":setAIState"}, "all")

Return all doors that open into the square where our script is located: doors = findAll(self.level, self.x, self.y, {"door"}, "any", true, false, true) Any object that has "door" in its name will be returned. The surrounding flag is true, so nine squares will be examined for doors. Remember the "northern" door out of your chamber might actually be placed as the "southern" door in the square immediately to your north.

Code: Select all

---
---  Generic Find:  return all objects at, or surrounding, a given location containing all/any string/method in keys
---

--  keys                   == {"keyword", ":method", ...} a match with any/all items will include the object
--  all                    == "all"  all keywords must be matched; else any keyword must be matched
--  surrounding            == true - looks in the current space and all surrounding spaces
--  facing                 == true - looks in the space adjacent the direction we're facing
--  surrounding and facing == true - current space adjusted to where we're facing, then look at all surrounding
--  facingUs               == true - limit matches to things that are facing us or that are in our own space

function findAll(level, x, y, keys, all, surrounding, facing, facingUs)
  local answers, r, dx, dy, entity, match, _ = {}, 0
  all = all == "all"
  local any = not all
  if surrounding then r = 1 end
  if facing then 
    dx, dy = getForward(self.facing)
    x, y = x + dx, y + dy
    end
  if keys == nil then keys = {"*"} end

  for i = x - r, x + r do
    for j = y - r, y + r do
      if 0 < i and i < 33 and 0 < j and j < 33 then 
        for entity in entitiesAt(level, i, j) do
          
          match = true
          for _, value in ipairs(keys) do
            if value == "*" then pass = true
            elseif string.sub(value, 1, 1) == ":" then pass = entity[(string.sub(value, 2))] ~= nil
            else pass = string.find(entity.name, value) ~= nil end
            
            if all then match = pass and match
            else match = pass end
            if (match and any) or ((not match) and all) then break end
            end
          
          if match then
            dx, dy = getForward(entity.facing)
            if (not facingUs) or (facingUs and entity.x + dx == self.x and entity.y + dy == self.y) or (self.x == entity.x and self.y == entity.y) then 
              answers[# answers + 1] = entity 
              end 
            end
          
          end
        end
      end
    end
  return answers
  end
Have Fun, -Lark

Re: [FUNCTION] Find entities with text and method filtering

Posted: Thu Nov 01, 2012 9:11 pm
by Xanathar
This is very valuable, thanks!

Re: [FUNCTION] Find entities with text and method filtering

Posted: Fri Nov 02, 2012 3:40 am
by Lark
Xanathar wrote:This is very valuable, thanks!
You're most welcome! See viewtopic.php?f=14&t=3986 for the reason I wrote it! :)

Re: [FUNCTION] Find entities with text and method filtering

Posted: Thu Nov 15, 2012 2:54 am
by Komag
This is good stuff, added to superthread! ;)

Re: [FUNCTION] Find entities with text and method filtering

Posted: Fri Nov 16, 2012 5:52 pm
by flatline
I've been using this for a spell I'm working on, Arrow protection.
In short, it should detect arrows in front of the player and destroy them. Right now, I'm using
arrows = findAll(party.level, party.x, party.y, {"arrow"}, "any", false, false, true)
It should return a table of entities and I want to destroy any entities it returns. What is the right way to do that?

just putting :destroy() at the end, or arrows:destroy() won't work.

Re: [FUNCTION] Find entities with text and method filtering

Posted: Mon Nov 19, 2012 2:16 pm
by JohnWordsworth
I hope you don't mind, but I've added this method to the Wiki, as it's very useful!

Re: [FUNCTION] Find entities with text and method filtering

Posted: Tue Dec 04, 2012 4:02 am
by Komag
I can't make a certain piece of this work, and I just can't quite grasp what's going on. I don't want to drop in the whole code, I just want to detect monsters, all monsters, without checking against a complete monster list.

So I want to check against whether it has :setAIState but I don't know how, nor can I see how that worked above.

Here my non-working script:

Code: Select all

totalEntities = 0
totalMonsters = 0
function countMonsters()
  for i=1,13 do
    for j in allEntities(i) do
      totalEntities = totalEntities + 1
      if j:setAIState() ~= nil then
         totalMonsters = totalMonsters + 1
      end
    end
  end
  print("there are "..totalEntities.." entities")
  print("there are "..totalMonsters.." monsters")
end
It starts for a moment, but then just crashes back to editor without any error indication. If I change it to this:

Code: Select all

totalEntities = 0
totalMonsters = 0
function countMonsters()
  for i=1,13 do
    for j in allEntities(i) do
      totalEntities = totalEntities + 1
      if j[setAIState] ~= nil then
         totalMonsters = totalMonsters + 1
      end
    end
  end
  print("there are "..totalEntities.." entities")
  print("there are "..totalMonsters.." monsters")
end
It works to print that there are 5602 entities, but says 0 monsters (after a long pause of about 5 seconds)

I have no clue what those [ ] do or how they work - aren't they usually for tables for the item number? Lark's code is way above my head. Any help?

Re: [FUNCTION] Find entities with text and method filtering

Posted: Tue Dec 04, 2012 4:33 am
by Batty
change it to this:

Code: Select all

totalEntities = 0
totalMonsters = 0
function countMonsters()
   value = "setAIState"
   for i=1, 13 do
      for j in allEntities(i) do
         totalEntities = totalEntities + 1
         if j[value] ~= nil then
            totalMonsters = totalMonsters + 1
         end
      end
   end
   print("there are "..totalEntities.." entities")
   print("there are "..totalMonsters.." monsters")
end
It needs the string value to work. :D

Re: [FUNCTION] Find entities with text and method filtering

Posted: Tue Dec 04, 2012 4:51 am
by Komag
great, that works!

But I don't understand, why doesn't:
value = ":setAIState"
j[(string.sub(value, 2))]

equal this:
j[(setAIState)]
? because I tried that too and it also says 0 monsters, but I thought that's how string.sub worked?

EDIT - AHHH, because it needs to be a STRING, like this:
j[("setAIState")]

Great, so I know it better now, don't need the value = ":setAIState" or the string.sub, just the quote marks!
Thanks for the help Batty! :D 8-)

But how does that work?
j[("setAIState")] never heard of this type of entity query/info, not documented as far as I can see. What do the [ ] and ( ) mean here?
"If somewhere in the [ properties ] of entity j there is a function ( ) that has "setAIState" string, then it is not nil..." or something like that?

Re: [FUNCTION] Find entities with text and method filtering

Posted: Tue Dec 04, 2012 5:05 am
by Batty
It works because setAIState is a string unique to monster tables.

You're looking up a key in a table which is what the entities in the game are, tables. That's what the brackets are for.

You lookup a key in a table & it returns the corresponding value.

The parentheses are new to me, that's allowing you to search the table keys "literally" for a string, I suppose.

Google lua tables, there's a good wiki that explains it all.

*some of my assumptions are just that, assumptions & possibly incorrect. I claim no responsibility for misinformation leading to poor scripting practices. :lol:

edit: Lua tables - a good tutorial to get started understanding this $#&*@