Checking proximity of plot type

Gangor

King
Joined
Jan 21, 2002
Messages
825
Location
Berks, UK
Ok, so I'm working on a mapscript which has single tile islands with a minimum proximity. I had thought of doing it recursively (ie having an adjacency checking function which returns a table with it's surrounding tiles, then calling it on the tiles in the returned table for a certain number of iterations, depending on the proximity required), however after figuring that out my brain stopped working and I can't work out how to get tiles adjacent to a given tile. I got stuck trying to figure out which way a row is offset.

Some help with this would be very welcome, or alternatively another solution for the original problem?
 
Single tile islands?
You can mark the tiles as not eligible by using the algorithm you describe:
Look for land tiles, and mark all neighbours as invalid, repeat N times:

Givven basic neighbour functions like these:
Code:
    South = function(self, x, y)
        if y == 0 then return nil end
        return {x, (y-1)}
    end,
    North = function(self, x, y)
        if y == self.iH - 1 then return nil end
        return {x, (y+1)}
    end,
    West = function(self, x, y)
        if x == 0 then return y*self.iW + self.iW - 1 +1 end
        return {x-1, y}
    end,
    East = function(self, x, y)
        if x == self.iW - 1 then return y*self.iW + 1 end
        return {x+1, y}
    end,
    NorthWest = function(self, x, y)
        if y == self.iH - 1 then return nil end
        return self:West(x, y + 1)
    end,
    SouthWest = function(self, x, y)
        if y == 0 then return nil end
        return self:West(x, y - 1)
    end,
    Neighbours = function(self, x, y)
        local result = {}
        result["South"] = self:South(x, y)
        result["North"] = self:North(x, y)
        result["West"] = self:West(x, y)
        result["East"] = self:East(x, y)
        result["NorthWest"] = self:NorthWest(x, y)
        result["SouthWest"] = self:SouthWest(x, y)
        return result
    end,

You can call:
Code:
  Tag = function(x,y,n)
    if n == 0 then return end
    for _, coord in pairs(self:Neighbours(x,y)) do
        Tag( coord[1], coord[2], n-1 )
    end
    MarkPlotAsIneligibleForIsland(x,y)
  end
Or something along these lines.
 
I can't work out how to get tiles adjacent to a given tile.

Code:
local x = 10;
local y = 10;

for dx = -1, 1 do
	for dy = -1, 1 do
		local newx = x + dx
		local newy = y + dy
		-- get plot, do stuff
	end
end

edit: ^^adapted from vanilla examples. of course that spits out 9 tiles (when there is only 7 neighbors), 1 being the original plot, 2 being outside next tile range. a quick test seems to be that dx 1, dy -1 and dx 1, dy 1 are the odd ones out. is this always true or does it depend on grid size?
 
adapted from vanilla examples. of course that spits out 9 tiles (when there is only 7 neighbors), 1 being the original plot, 2 being outside next tile range. a quick test seems to be that dx 1, dy -1 and dx 1, dy 1 are the odd ones out. is this always true or does it depend on grid size?
My understanding of the Civ V hex grid is that if your tile is on an odd row then the -1 dx, +/-1 dy offsets would be outside the range but if the tile is on an even row then it would be +1 dx, +/-1 dy. The image below probably explains it more clearly. Note that the code would also have to do border checks as LDiCesare showed.

Spoiler :
29nc9ci.jpg

Also, according to Bhruic's investigation of the DLL there is an exported function CvPlot::isAdjacent(CvPlot const *). I haven't a clue how to check if this is made available to Lua though.

EDIT: Did some poking around in the tuner console. While it doesn't look like CvPlot::isAdjacent() is available, there are other adjacency methods available to Lua plot objects; in particular IsAdjacentToLand() or IsAdjacentToShallowWater() look like they could be useful for the OP's purposes.
 
smellymummy's code doesn't work for hexes, so will give weird results, and doesn't account for map borders and x-wrapping.
CvPlot is unusable in a map script because the plots are not created yet.
 
Of course it works, but it gives false results. The loop returns a bunch of hexes which are 0, 1 or 2 tiles away from the hex you're looking at.
It works fine for looking at the nearest neighbours, you only get a minority of wrong hexes. Now iterate and check what it gives after 3 steps of recursion. You'll get a mix of hexes from 0 to 6 tiles away, but not all those 6 tiles away, only those in some directions, which will severely bias the result:
distorsion.png

You can see the fallout tiles are the correct result: land tiles without fallout are what using recursively the proposed loop gives: A wrong result.
It creates a square instead of a hexagon. That's just bad, particularly at bigger recursion depths.

And I can't see how the CvPlot would work. You must have set the plot type before it can be used. Creating an island is made before the plot type is set, so it will fail.
In map generation, the first thing done is setting the landmasses: flat, hill, mountain or water. Creating islands is done during this step. Changing terrain afterwards is very dangerous since you run the risk of never getting features on them, or putting land on a tile with a feature like ice which is ment for water only, etc. So if you use CvPlot methods, you'll either crash or get an uninitialised terrain value, either land or water, but always the same for all plots.

So, no, neither of these methods are useful to find distance from land during the land generation phase.
 
i wrote that cvplot would work because some of the mapscripts themselves are using that class, but yeah if the plot is nil, you won't get anywhere

but Map.PlotDistance() should work. stick it in the loop and if the distance <= 1 then do stuff
 
but Map.PlotDistance() should work. stick it in the loop and if the distance <= 1 then do stuff
Yes. More < 1 since 0 would just repeat the call for the same plot. But this and checking x/y >= 0 < mapWidth/Height add a lot to the loop. So the resulting code will be much longer than the simple loop you showed.
 
it was assumed these checks would of been included in the part i wrote above "do stuff"

Code:
if ( Map.PlotDistance(x,y,newx,newy) == 1 ) then
-- do stuff
end
i have to disagree about it being longer and not so simple anymore

there's different ways to accomplish this and that goal. the for loops is what i've seen many times in the vanilla files so of course its the first thing that came to mind in regards to the OP. but thanks ldicesare for bringing up all the problems with it, i would of never bothered to nitpick the vanilla stuff so much
 
I just mean that since you're assuming several checks, you might as well write the whole code instead of a third of the code.

Compare:
Code:
for dx = -1, 1 do
	for dy = -1, 1 do
		local newx = x + dx
		local newy = y + dy
		-- get plot, do stuff
	end
end

with

Code:
for dx = -1, 1 do
	for dy = -1, 1 do
		local newx = x + dx
		local newy = y + dy
                if newx >= map.width then newx = 0 
                else if newx < 0 then newx = map.width -1
                end
                if newy < 0 then continue end
                if newy >= map.height then continue end
		if ( Map.PlotDistance(x,y,newx,newy) == 1 ) then
                   -- do actual stuff here
                end
	end
end
Where you wrote 2 significant statements in your first example, one realises one needs 5 moree statements in order to get the loop to work.
So the real solution is 3.5 times longer to write than the example you provided.
I'm not saying the program will run slower and execute in longer time, just that your example is only a very small part of the solution, and therefore was too incomplete to be useful. I strongly doubt the OP was unable to check the tiles with +1/-1 on the x and y axis by himself.
 
Wow, thanks for all the replys guys! In fact upon sitting down and having another go I did come up with a solution (although not as elegant as those here) by having a function which returns a set of it's adjacent tiles, then calling this function again on the set it returns (for n times).

One question remains though... are odd or even rows offset?

Edit: Just went back and noticed the reference to Map.PlotDistance()... what are it's arguments? Is it documented anywhere?
 
for Map.PlotDistance(), it's the four args firstplot cords (x1,y2), second plot cords (x2,y2), returns an int representing distance. i'm pretty sure its hex distance and not grid

its used in the icsing mod to find settler spots distance x from cities, and hasn't failed yet that ive seen
 
Back
Top Bottom