Object.GetNearbyObjects() returns objects in a radius

Discussion about Mods for Prison Architect

Moderator: NBJeff

murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Thu Aug 18, 2016 5:55 pm

I always assumed GetNearbyObjects() would return a square of objects, but it turns out some objects on the edge of that square would not be found unless the range was set higher. I've read posts about GetNearbyObjects() not being reliable, so I tried to figure out why.

Therefore I made a little test map with a square of sprinklers and a fire alarm scanning those sprinklers to see how many sprinklers it would find in a given range. When turning the stuff on, the riddle was solved immediately: GetNearbyObjects() returns objects in a circle around the scanning object, and not in a square.

I have no idea if this should be a bug report or a feature request - is this intentional behaviour of this commonly used function? If so, then it makes GetNearbyObjects() a lot less reliable and misunderstood by modders.
Anyway, I have filed this issue on the bug tracker.

You can try the demo map here: https://steamcommunity.com/sharedfiles/ ... =746916214

Usage: click on the Evac Alarm in the centre, play with the FireAlarm and range settings.
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Sun Aug 21, 2016 4:16 am

Well I already knew that it works in a radius instead of a square or "Manhatten distance"... and I thought this was common knowledge around modders.

But I don't know why this should make things less reliable... except if you don't want duplicate results from using GetNearbyObjects on multiple entities where their range is overlapping. Because that can seriously influence performance due to having to do the same stuff on the same itesm multiple times over or even radically change behaviour of what you are trying to do because you only might want to do something once.

But even that is solveable if you are using only one table for the overall results of all objects you are using GetNearbyObjects on because the way Lua works is that there can only be one instance of a named variable in a table, as long as you are using the entity's name as variable name (similar to how a pointer would work in other programming languages)... which means automatic filtering of duplicate entries. Though that puts you in another spot where you can't use ipairs() and you always have to use pairs() to cycle through that table.

That said one really should avoid having too many entities with a huge range where their range is overlapping... because the Cfunction behind GetNearbyObjects causes the game to lag/stutter if there are too many or too complex results the API has to map to that script instance. Like for example getting all prisoners on a map is the most ugly thing you can do in the game because 1) there are many prisoners, 2) the resulting table per prisoner is very complex with a lot of properties and subtables. Which is the reason why one should only work with "pointer"-like references to objects in the first place.



Whatever is the case I don't think that it is a bug and that is working intentionally that way.

Altough in my opinion I think that GetNearbyObjects as the only way to reference to external objects (even if one doesn't even care about the distance of these objects and wants all instances of the specified type anyways) is really a cumbersome method to get these objects in the first place because of how redundant the results are ontop of being inefficient performance-wise (like NearbyObjects methods are always since it basically has to cycle through the entire list of objects to find out which ones are in range). Most of the objects are already somewhere on the stack and it would be a lot more performant and easier to manage sometimes if one was able to access them directly instead of having to "search" for them on the map first before they can be mapped to the script instance, especially if one knows which objects one wants.

I didn't have the opportunity to play around with World Scripts yet, but I wonder how it's done there... because what's the point of reference when using this.GetNearbyObjects in a WorldScript... where is the WorldScript's X, Y position? 0, 0? Or does GetNearbyObjects not even work in them...? How does one get external objects there if it's not GetNearbyObjects? Questions over questions and most answers result in another question. :roll:
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Sun Aug 21, 2016 10:05 am

Medu_Salem wrote:Well I already knew that it works in a radius instead of a square or "Manhatten distance"... and I thought this was common knowledge around modders.


How? The lua_functions.txt mentions 'the distance to search within', it doesn't say radius (which would be clearer).


Medu_Salem wrote:But I don't know why this should make things less reliable... except if you don't want duplicate results from using GetNearbyObjects on multiple entities where their range is overlapping. Because that can seriously influence performance due to having to do the same stuff on the same itesm multiple times over or even radically change behaviour of what you are trying to do because you only might want to do something once.


Well in my case there are fire alarms scanning sprinklers in cellblocks. Cellblocks always have a square or rectangle shape and never a circle shape. So to get all sprinklers in a cellblock, the range scanning must be set way higher than the height/width of the cellblock, to cover the complete square/rectangle in that search radius. Because when scanning just the width/height size it would not find the outer sprinklers of the cell block. But then you can probably guess what happens when several cellblocks are next to each other... A fire alarm in one block goes off due to a small fire, but the prisoners in the next cellblock get also evicted for no reason because there is no danger in that block. Same for kitchens: one kitchen gets a fire, alarm goes off but it also starts to evict other kitchens nearby while they are ok and really should produce the meals instead... So therefore scanning in circles is really a lot less modding friendly than scanning in squares or rectangles. Look at the average prison: it's all build with squares and rectangle shaped rooms.

It would also be much easier for players to measure the distance with the planning tools. A square of 51 meters, pinpoint the centre of it, place your object and scan a range of 25m would cover the whole 51x51m area. There is no way to draw circles with the planning tool to mimic the scanning area. So why scan in circles if we can't draw circles in the first way? The game doesn't support building round rooms...

Medu_Salem wrote:Altough in my opinion I think that GetNearbyObjects as the only way to reference to external objects (even if one doesn't even care about the distance of these objects and wants all instances of the specified type anyways) is really a cumbersome method to get these objects in the first place because of how redundant the results are ontop of being inefficient performance-wise (like NearbyObjects methods are always since it basically has to cycle through the entire list of objects to find out which ones are in range). Most of the objects are already somewhere on the stack and it would be a lot more performant and easier to manage sometimes if one was able to access them directly instead of having to "search" for them on the map first before they can be mapped to the script instance, especially if one knows which objects one wants.

I didn't have the opportunity to play around with World Scripts yet, but I wonder how it's done there... because what's the point of reference when using this.GetNearbyObjects in a WorldScript... where is the WorldScript's X, Y position? 0, 0? Or does GetNearbyObjects not even work in them...? How does one get external objects there if it's not GetNearbyObjects? Questions over questions and most answers result in another question. :roll:


Yes GetNearbyObjects() isn't enough. Would be great if we could also use Object.GetObjectById() and Object.GetObjectsByType() methods, as mentioned here http://bugs.introversion.co.uk/view.php?id=9471 and perhaps a GetRoomObjects() http://bugs.introversion.co.uk/view.php?id=6864

World scripts? No idea how to do that without an example showing the good and the bad about it first.
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Sun Aug 21, 2016 12:58 pm

murgh wrote:How? The lua_functions.txt mentions 'the distance to search within', it doesn't say radius (which would be clearer).


Nah, it isn't explicitely stated anywhere (and yeah I basically looked everywhere too)... so I found out about it by trial and error myself, though I expected it to work in a radius fashion right from the beginning because that's how most "NearbyObject" methods work in software and games because basically collision detection is using almost if not the same code to check for possible intersecting objects, even in tile-based games where it actually makes a little bit less sense at times.

A little bit off-topic... but if I had to implement a tile-based game I would do it entirely different than what is currently industry standard anyways... I probably wouldn't even have just-in-time A* pathfinding because even if it's one of the best methods out there it still sucks when a lot of entities are using it, especially if you know in advance that a lot of them is going to share the paths, which means you are actually wasting precious computation time to do the same task over and over. And the same is true when trying to find nearby objects for collision detection. Most currently established methods suck when there are a lot of objects on the map and you have to cycle through every single one of them to crosscheck if they might intersect in the next tick. I'd rather use a Quadtree structure for collision detection and apply a vector based pathfinding method ontop of that, where you only calculate something similar to a heatmap which gets converted to a vector map, all of which can be precomputed or updated in the background without having to be in real-time, and the only thing that's real time is when an entity moves onto a tile the vector existing there just has to be multiplied by the entity's velocity and that's it.

murgh wrote:Well in my case there are fire alarms scanning sprinklers in cellblocks. Cellblocks always have a square or rectangle shape and never a circle shape. So to get all sprinklers in a cellblock, the range scanning must be set way higher than the height/width of the cellblock, to cover the complete square/rectangle in that search radius. Because when scanning just the width/height size it would not find the outer sprinklers of the cell block. But then you can probably guess what happens when several cellblocks are next to each other... A fire alarm in one block goes off due to a small fire, but the prisoners in the next cellblock get also evicted for no reason because there is no danger in that block. Same for kitchens: one kitchen gets a fire, alarm goes off but it also starts to evict other kitchens nearby while they are ok and really should produce the meals instead... So therefore scanning in circles is really a lot less modding friendly than scanning in squares or rectangles. Look at the average prison: it's all build with squares and rectangle shaped rooms.

It would also be much easier for players to measure the distance with the planning tools. A square of 51 meters, pinpoint the centre of it, place your object and scan a range of 25m would cover the whole 51x51m area. There is no way to draw circles with the planning tool to mimic the scanning area. So why scan in circles if we can't draw circles in the first way? The game doesn't support building round rooms...


Oh, well now I get why you are having trouble... if the shape you want to cover is rectangular you are pretty much out of luck for your stated reasons... either the circle doesn't fully cover the rectangle or you may risk covering too much outside of the rectangle.

But that said if GetNearbyObjects would use a square it would make things only a little bit easier for square rooms but it wouldn't entirely solve your problem either because a square big enough to fully cover a rectangle (or the cellblock in your scenario) would suffer the same problem as trying to do it with a circle... it would cover area outside the rectangle (with other words other cellblocks nearby). So you'd again need more than one objekt (Fire Alarm) with a smaller range to cover that cellblock (which then doesn't really differ that much from using multiple Fire Alarms with smaller circles).

I guess it's a problem where there is no proper software solution within the possibilities of Prison Architect Modding... not without getting really, really ugly and reaching with your bare hands into sewage by tagging all the sprinklers in a cellblock one by one and attaching them to a specific fire alarm item, if that's even possible.

Another one would be to actually filter the resulting sprinkler list depending on their relative x,y position to the fire alarm item... so that you only consider ones that are inside the rectangular cellbock shape, and dismiss all others outside... but that again would be... urgh. I don't know... NOT pretty because it would require you to know the exact shape, orientation and location (since the center of the cellblock might not exactly align with the firealarm) of the cellblock to apply such a filter ontop of the sprinkler list.

For now the easiest solution would be to build the cellblocks in a near-to-square shape... and distribute the sprinklers in a circular shape only where absolutely necessary and then extend the range of the fire alarm only as far outward as necessary to cover the last sprinkler so that you don't accidently cover another cellblock. A conceptual mess I know... but from a programming perspective there's not really much choice here, or at least I can't think of a better one.

murgh wrote:Yes GetNearbyObjects() isn't enough. Would be great if we could also use Object.GetObjectById() and Object.GetObjectsByType() methods, as mentioned here http://bugs.introversion.co.uk/view.php?id=9471 and perhaps a GetRoomObjects() http://bugs.introversion.co.uk/view.php?id=6864

World scripts? No idea how to do that without an example showing the good and the bad about it first.


Yeah, the functions would come in handy at times. Especially if they are "faster" than GetNearbyObjects so that they can be used more often (every few seconds or so, not every tick because that would be crazy) without causing stuttering all the time.
Last edited by Medu_Salem on Sun Aug 21, 2016 1:44 pm, edited 1 time in total.
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Sun Aug 21, 2016 1:44 pm

Medu_Salem wrote:But that said if GetNearbyObjects would use a square it would make things only a little bit easier for square rooms but it wouldn't entirely solve your problem either because a square big enough to fully cover a rectangle (or the cellblock in your scenario) would suffer the same problem as trying to do it with a circle... it would cover area outside the rectangle (with other words other cellblocks nearby). So you'd again need more than one objekt (Fire Alarm) with a smaller range to cover that cellblock (which then doesn't really differ that much from using multiple Fire Alarms with smaller circles).


Yes, but two fire alarms covering squares will make a rectangle together, so there won't be much trouble. At least less trouble than with circles.


Medu_Salem wrote:I guess it's a problem where there is no proper software solution within the possibilities of Prison Architect Modding... not without getting really, really ugly and reaching with your bare hands into sewage by tagging all the sprinklers in a cellblock one by one and attaching them to a specific fire alarm item, if that's even possible.


Could do, but would take hours for a player to hook up 500 sprinklers for each cell....


Medu_Salem wrote:Another one would be to actually filter the resulting sprinkler list depending on their relative x,y position to the fire alarm item... so that you only consider ones that are inside the rectangular cellbock shape, and dismiss all others outside... but that again would be... urgh. I don't know... NOT pretty because it would require you to know the exact shape, orientation and location (since the center of the cellblock might not exactly align with the firealarm) of the cellblock to apply such a filter ontop of the sprinkler list.


Ugly but doable, this method is used in the basketball mod to determine the boundaries of the court. Prisoners within those boundaries count as players, prisoners outside are left alone. Guess this has to be the method. So setting a range of 25m for example would mean 'draw a cross of 50m with the alarm in the centre, then connect the points to make it a square and expect stuff within MinX/MinY to MaxX/MaxY to be found... pffff Much nicer would be GetSquareObjects(minX,minY,maxX,maxY)
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Sun Aug 21, 2016 1:50 pm

I have another crazy method...

You could require the sprinklers to be placed in a certain distance from each other... and then starting from one sprinkler you get the next nearby sprinklers and then the next nearby of them and so on... until you populated a table that's basically like a graph, listing all sprinklers that are in range of each other.

That would mean if you place all sprinklers in a cellblock in range of each other the algorithm would determine that they all belong to that one cellblock. And all that are not in range of it are not part of that network.
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Sun Aug 21, 2016 3:38 pm

For now I've worked around it with this:

Code: Select all

function FindMySprinklers()
   MySprinklers={}
   NumSprinklers=0
   ScannedSprinklers = this.GetNearbyObjects('Sprinkler',this.Range*2)
   Xmin=this.Pos.x-this.Range
   Xmax=this.Pos.x+this.Range
   Ymin=this.Pos.y-this.Range
   Ymax=this.Pos.y+this.Range
   if next(ScannedSprinklers) then
      for thatSprinkler, distance in pairs (ScannedSprinklers) do
         if (thatSprinkler.Pos.x>=Xmin and thatSprinkler.Pos.x<=Xmax) and (thatSprinkler.Pos.y>=Ymin and thatSprinkler.Pos.y<=Ymax) then
            NumSprinklers=NumSprinklers+1
            MySprinklers[NumSprinklers]=thatSprinkler
            if MySprinklers[NumSprinklers].SubType==0 then
               Object.SetProperty(MySprinklers[NumSprinklers],"SubType",1)
            end
         end
      end
   end
   this.SetInterfaceCaption("toggleFindSprinklers", "tooltip_button_scansprinklers",NumSprinklers,"X")
end


Scanning twice the range probably isn't the proper math to do it, but at least it will cover the area of the square. I forgot how to calculate it properly lol, school time is loong ago and I'm not good at maths. Any suggestions to make this more efficient? Would be better if ScannedSprinklers would discard the sprinklers out of range and not using a separate MySprinklers table, but I'm not sure how to fix that.
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Sun Aug 21, 2016 4:45 pm

murgh wrote:For now I've worked around it with this:

[...]

Scanning twice the range probably isn't the proper math to do it, but at least it will cover the area of the square. I forgot how to calculate it properly lol, school time is loong ago and I'm not good at maths. Any suggestions to make this more efficient? Would be better if ScannedSprinklers would discard the sprinklers out of range and not using a separate MySprinklers table, but I'm not sure how to fix that.


Basically "ScannedSprinklers" is a table that only contains name entries which act like pointer references to the actual object tables (the ones that get mapped to the script instance after using GetNearbyObjects), while the distance is more or less a dummy value assigned to the pointer (since a value of a key-value pair can't be empty or otherwise it would get removed from existence by Lua's garbage collector from what I understand). You can imagine it like that key= "name of object == pointer reference", value = "distance".

When the for loop is entered then the key is mapped to thatSprinkler and value is mapped to distance (that's how you named the variables).

So what this means is that you could try setting "ScannedSprinklers[thatSprinkler] = nil" in the else part if the sprinkler is out of range... that would delete the pointer reference to that Sprinkler from ScannedSprinklers table. Assigning "nil" to a variable basically removes the variable from existence (= explicitely telling garbage collector that the variable isn't needed anylonger).

If doing so then after going through the for in pairs loop the ScannedSprinklers table should only contain the sprinklers that are actually in range, because everything that's not in range got removed from the table.

I mean I never tried that approach because normally I am always working on copies of tables/arrays (and setting key-value pairs to nil works there), but hypothetically it should work on the original table as well.

One thing should be noted though... if you remove the reference to an object from the original ScannedSprinklers table, the actual object table will still be there (even if you are never using it), since you only removed the pointer to it. Normally the garbage collector would pick stuff like that up and remove it since there's no active reference to it anymore but since the object tables are mapped as global variables in the script instance they are NOT removed - it's a special behaviour of Lua's garbage collector I had to read up. In other programming languages that would cause a memory leak since you lost the reference to the object but the OS wouldn't know that you don't need the object anymore so it stays there until you terminate the program. In Lua in Prison Architect that might not be that much of a problem though because it's only a few objects anyways and you get the references back as soon as you run GetNearbyObjects again due to unique object names which never change, but many would say that it would still be unclean programming. (If it is really of concern then you can try doing "thatSprinkler = nil" additionally to removing the pointer reference which would be deleting the object table from the script instance, though I don't know what happens if one tries to delete a mapped object table, maybe it doesn't even work, that's something that needs testing)



Another question is... why are you using "if next(ScannedSprinklers) then" before the "for thatSprinkler, that in pairs(ScannedSprinklers) do"? ... because it's basically redundant. The for in pairs loop works only if the ScannedSprinklers table isn't empty anyways, and if it is empty it doesn't even enter the loop... or at least it shouldn't.


Should look something like so I guess:

Code: Select all

function FindMySprinklers()
   NumSprinklers=0
   ScannedSprinklers = this.GetNearbyObjects('Sprinkler',this.Range*2)
   Xmin=this.Pos.x-this.Range
   Xmax=this.Pos.x+this.Range
   Ymin=this.Pos.y-this.Range
   Ymax=this.Pos.y+this.Range
   for thatSprinkler, distance in pairs (ScannedSprinklers) do
      if (thatSprinkler.Pos.x>=Xmin and thatSprinkler.Pos.x<=Xmax) and (thatSprinkler.Pos.y>=Ymin and thatSprinkler.Pos.y<=Ymax) then
         NumSprinklers = NumSprinklers + 1
         <... do whatever else you need ...>
      else
         ScannedSprinklers[thatSprinkler] = nil
      end
   end
   this.SetInterfaceCaption("toggleFindSprinklers", "tooltip_button_scansprinklers",NumSprinklers,"X")
end
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Sun Aug 21, 2016 6:19 pm

After a nice dinner this came out of my hands:

Code: Select all

function GetObjectsInSquare(theObject,theRange)
   local Xmin=this.Pos.x-theRange/2
   local Xmax=this.Pos.x+theRange/2
   local Ymin=this.Pos.y-theRange/2
   local Ymax=this.Pos.y+theRange/2
   local CompiledList = {}
   local NumObjects=0
   local ListOfObjects = this.GetNearbyObjects(theObject,theRange)
   for thatObject, distance in pairs(ListOfObjects) do
      if (thatObject.Pos.x>=Xmin and thatObject.Pos.x<=Xmax) and (thatObject.Pos.y>=Ymin and thatObject.Pos.y<=Ymax) then
         NumObjects=NumObjects+1
         CompiledList[NumObjects] = thatObject
      end
   end
   return CompiledList
end   

function FindMySprinklers()
   MySprinklers={}
   MySprinklers = GetObjectsInSquare('Sprinkler',this.Range)
   NumSprinklers=#MySprinklers
   for i=1,#MySprinklers do
      if MySprinklers[i].SubType==0 then
         Object.SetProperty(MySprinklers[i],"SubType",1)
      end
   end
   this.SetInterfaceCaption("toggleFindSprinklers", "tooltip_button_scansprinklers",NumSprinklers,"X")
end


Seems to do the job well, can also be used again for other objects to find in a square.
Just call GetObjectsInSquare() with the name and a range and it will sort it out, returning a table with the corresponding objects. No messing around.
The number of corresponding objects can be found by using #MySprinklers

As an alternative, this can also be used:

Code: Select all

function GetObjectsInSquare(theObject,theRange)
   local Xmin=this.Pos.x-theRange/2
   local Xmax=this.Pos.x+theRange/2
   local Ymin=this.Pos.y-theRange/2
   local Ymax=this.Pos.y+theRange/2
--   local CompiledList = {}
--   local NumObjects=0
   local ListOfObjects = this.GetNearbyObjects(theObject,theRange)
   for thatObject, distance in pairs(ListOfObjects) do
      if (thatObject.Pos.x>=Xmin and thatObject.Pos.x<=Xmax) and (thatObject.Pos.y>=Ymin and thatObject.Pos.y<=Ymax) then
--         NumObjects=NumObjects+1
--         CompiledList[NumObjects] = thatObject
      else
         ListOfObjects[thatObject]=nil
      end
   end
--   return CompiledList
   return ListOfObjects
end

function FindMySprinklers()
   MySprinklers={}
   NumSprinklers=0
   MySprinklers = GetObjectsInSquare('Sprinkler',this.Range)
   for thatSprinkler, distance in pairs (MySprinklers) do
      if thatSprinkler.SubType==0 then
         Object.SetProperty(thatSprinkler,"SubType",1)
      end
      NumSprinklers=NumSprinklers+1
   end
   this.SetInterfaceCaption("toggleFindSprinklers", "tooltip_button_scansprinklers",NumSprinklers,"X")
end



Note that #MySprinklers will return 0 in this version.
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Sun Aug 21, 2016 11:16 pm

Hmm, made some more tweaks to it.
Instead of being the centre of the scanned area, the alarm will now be at the border of the square.
It's usually mounted on a wall anyway, so everything behind that wall will now be left alone.

So if an alarm is facing left then it will scan the sprinklers to the left of it. Facing upwards? Then only sprinklers above it will be in the list. Works very nice!
Got the idea from the Large TV broadcasting range. Wish we could implement those nice green border lines in scripts as well, those would be very handy heheheh

Code: Select all

function GetObjectsInSquare(theObject,theRange)
   if this.Or.x==-1 then print('-- rotated once')
      Xmin=math.floor(this.Pos.x-theRange)
      Xmax=this.Pos.x
      Ymin=math.ceil(this.Pos.y-theRange/2)
      Ymax=math.floor(this.Pos.y+theRange/2)
   elseif this.Or.y==-1 then print('-- rotated twice')
      Xmin=math.floor(this.Pos.x-theRange/2)
      Xmax=math.ceil(this.Pos.x+theRange/2)
      Ymin=math.ceil(this.Pos.y-theRange)
      Ymax=this.Pos.y
   elseif this.Or.x==1 then print('-- rotated three times')
      Xmin=this.Pos.x
      Xmax=math.ceil(this.Pos.x+theRange)
      Ymin=math.ceil(this.Pos.y-theRange/2)
      Ymax=math.floor(this.Pos.y+theRange/2)
   else print('--placed down')
      Xmin=math.floor(this.Pos.x-theRange/2)
      Xmax=math.ceil(this.Pos.x+theRange/2)
      Ymin=this.Pos.y
      Ymax=math.floor(this.Pos.y+theRange)
   end
   local ListOfObjects = this.GetNearbyObjects(theObject,theRange*1.2)
   for thatObject, distance in pairs(ListOfObjects) do
      if (thatObject.Pos.x>=Xmin and thatObject.Pos.x<=Xmax) and (thatObject.Pos.y>=Ymin and thatObject.Pos.y<=Ymax) then
      -- it's ok
      else
         ListOfObjects[thatObject]=nil
      end
   end
   return ListOfObjects
end   


Image
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Mon Aug 22, 2016 12:53 pm

Looks neat ^^

Is the "If" branch empty?

Because if so then you could restructure/invert the Condition in a way that the if branch is taken only if the sprinkler is out of bound, and skip the else part.

Something like so:

Code: Select all

function GetObjectsInSquare(theObject,theRange)
   if this.Or.x==-1 then print('-- rotated once')
      Xmin=math.floor(this.Pos.x-theRange)
      Xmax=this.Pos.x
      Ymin=math.ceil(this.Pos.y-theRange/2)
      Ymax=math.floor(this.Pos.y+theRange/2)
   elseif this.Or.y==-1 then print('-- rotated twice')
      Xmin=math.floor(this.Pos.x-theRange/2)
      Xmax=math.ceil(this.Pos.x+theRange/2)
      Ymin=math.ceil(this.Pos.y-theRange)
      Ymax=this.Pos.y
   elseif this.Or.x==1 then print('-- rotated three times')
      Xmin=this.Pos.x
      Xmax=math.ceil(this.Pos.x+theRange)
      Ymin=math.ceil(this.Pos.y-theRange/2)
      Ymax=math.floor(this.Pos.y+theRange/2)
   else print('--placed down')
      Xmin=math.floor(this.Pos.x-theRange/2)
      Xmax=math.ceil(this.Pos.x+theRange/2)
      Ymin=this.Pos.y
      Ymax=math.floor(this.Pos.y+theRange)
   end
   local ListOfObjects = this.GetNearbyObjects(theObject,theRange*1.2)
   for thatObject, distance in pairs(ListOfObjects) do
      if thatObject.Pos.x<Xmin or thatObject.Pos.x>Xmax or thatObject.Pos.y<Ymin or thatObject.Pos.y>Ymax then
         ListOfObjects[thatObject]=nil
      end
   end
   return ListOfObjects
end   


If one of the partial conditions returns true then it is out of bounds and due to the Or's the entire thing becomes true and the branch is taken and the sprinkler is removed from the list.

Else the sprinkler is inside and hence left in the list... but that can be skipped since you are not doing anything special in that case.

It's not really a mandatory improvement... but I somehow try to avoid the notation of empty branches (except maybe for debugging reasons). :D
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Mon Aug 22, 2016 3:21 pm

Yeah well you never know what could be put inside in the future, so I'd rather leave it like that. Same with the 'If next(thisstuff) then' constructions before using a for loop, I noticed the script would bug out with errors if it's not present now and then. Sometimes things happen which you didn't think of before, and those double-checks will catch it.
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Mon Aug 22, 2016 5:33 pm

murgh wrote:Same with the 'If next(thisstuff) then' constructions before using a for loop, I noticed the script would bug out with errors if it's not present now and then.


Weird... that really shouldn't happen... at least not within the for-loop... because the pairs(table) function of Lua basically has a built-in check if the passed table actually contains any key-value pairs because the pairs-function internally builds upon the next(table) function (as is stated in the Lua Manual), which means that the "next(table)"-check should be redundant when you are going to use a "for in pairs(table)"-loop directly afterwards. So if there are no key-value pairs present it skips the loop altogether because it can't iterate through an empty table. :roll:

Outside the for-loop it obviously might result in errors because if nothing is in range when using GerNearbyObjects or if you removed every Sprinkler from the list during the for-loop because they are all out of bound then the resulting table is empty and if something else further down the pipe (I don't know where your function is returning the table to) expects the table not to be empty then... BUGSSS.

But yeah I basically understand why you might want to leave the code as it is... one never fully knows in advance what else it might be good for later on... and probably there can never be enough error checking as long as it doesn't become of a performance concern to do error checking or not.
murgh
level2
level2
Posts: 232
Joined: Sat Jan 30, 2016 11:52 am

Re: Object.GetNearbyObjects() returns objects in a radius

Postby murgh » Mon Aug 22, 2016 10:24 pm

Yes well I tend to put some more stuff after the for loop sometimes, so it would run through the for loop and then screw up on the code after it lol. BUT I've seen code fuck up in the for loop as well if nil wasn't catched with the next() in front of it. Very weird and unexplainable behaviour indeed. My guess is that the lua implementation in this game isn't fully supporting lua. I noticed some code examples on the internet can't be used in PA since it simply doesn't understand what's going on.
User avatar
Medu_Salem
level1
level1
Posts: 46
Joined: Fri May 16, 2014 6:42 pm

Re: Object.GetNearbyObjects() returns objects in a radius

Postby Medu_Salem » Mon Aug 22, 2016 10:43 pm

Yeah, Prison Architect doesn't fully support everything that would hypothetically be possible with Lua 5.1

And the other part being that some stuff before Lua 5.1 is being deprecated in 5.1, like for example getn()/setn() for tables (it's funny though that the functions are still listed as valid table functions in the debug information but return errors when used). So now you have to store the size of a table elsewhere or use "#" though I hate using that operator because it cycles through a table in a linear fashion to count the number of items and that each time you are using the operator... which can cause nasty delays in huge tables and doesn't work on sparse tables at all because it would return the second it hits a nil value even there might be additional values following a nil. And I'm not really that much into metatable stuff or at least I'm too dumb (but probably I'm just too lazy so I'm looking for an excuse not to get into it) to understand what's going on there to emulate/re-implement the getn/setn functions using weak tables myself.

On top of that newer stuff which was added with 5.2/5.3 can't be used either, like Goto statements for example, which I also found out the hard way. But then again some other stuff was deprecated in 5.2/5.3 too, so maybe it's better the Devs didn't upgrade Prison Architect to use 5.2/5.3 instead of 5.1

Return to “Modding”

Who is online

Users browsing this forum: No registered users and 2 guests