| General | Hosted Sites | Civ5 | CivRev | Civ4Col | Civ4 | Civ3 | Civ2 | Civ1 | Misc | Marketplace |
![]() |
|
|
Welcome to Civilization Fanatics' Center. You are currently viewing our site as a guest which gives you limited access to our site features. By joining our free community, you will be able to participate in the discussions, search the forum, send private messages, vote in polls, upload your own screenshots to the gallery, and access many other special features. Registration is fast, simple and absolutely free, so sign up today! If you have any problems with the registration process or your account login, please contact support. |
|
|||||||
![]() |
|
|
Thread Tools |
|
|
#1 |
|
Deity
Join Date: Jun 2008
Posts: 2,054
|
[Lua] PlotToRadiusIterator()
Iterators are hard to make but easy to use. This iterator returns x,y plot coordinates out to a given radius from given center coordinates (edit: including the center). It knows about map edges and map wrapping and deals with these appropriately. It is optimized for speed including the use of memoization (offsets are calculated once for a given radius; subsequent calls with that radius use stored result). It also has two optional args (nearX, nearY) that define a sorting order for the returned coordinates. Sorting is expensive so don't supply nearX, nearY if you don't need sorting. Radius should work up to half the width or height (whichever is smaller) of the current map (for much more than radius 10 I would suggest iteration over all plots with a distance test).
Usage: Code:
for x, y in PlotToRadiusIterator(centerX, centerY, 1) do --x, y for plots at radius 1 and center --there will be 7 iterations unless we are next to a map edge without wrap end for x, y in PlotToRadiusIterator(centerX, centerY, 5) do --x, y for plots in radius 0 to 5 end for x, y in PlotToRadiusIterator(centerX, centerY, 3, nearX, nearY) do --x, y for plots in radius 0 to 3, --sorted so that they iterate in order by closest to nearX,nearY --(sorting is expensive so don't supply nearX, nearY if you don't need it) end Code:
local iW, iH = Map.GetGridSize()
local bWrapX, bWrapY = Map.IsWrapX(), Map.IsWrapY()
local yOffsets, xOffsetsEvenY, xOffsetsOddY = {}, {}, {} --contain tables indexed by radius; created and kept as needed
local tempIdx, sortedOffsetsX, sortedOffsetsY = {}, {}, {} --used for sorting when approach plot given (indexed by radius; created and kept as needed)
function PlotToRadiusIterator(x, y, radius, myX, myY)
-- last 2 args (optional & expensive) specify an "approach" coordinate that will cause return values to be sorted by nearest-first
local Distance = Map.PlotDistance
local Floor = math.floor
local yOffset = yOffsets[radius]
if not yOffset then --calculate and keep offsets for this radius (radius up to half map size)
local centerX = Floor(iW / 2)
local centerYeven = Floor(iH / 4) * 2
local centerYodd = centerYeven + 1
local evenPos, oddPos = 1, 1
yOffsets[radius], xOffsetsEvenY[radius], xOffsetsOddY[radius] = {}, {}, {}
for testYoffset = -radius, radius do
local testYeven = centerYeven + testYoffset
local testYodd = centerYodd + testYoffset
for textXoffset = -radius, radius do
local testX = centerX + textXoffset
if Distance(centerX, centerYeven, testX, testYeven) <= radius then
xOffsetsEvenY[radius][evenPos] = textXoffset
yOffsets[radius][evenPos] = testYoffset
evenPos = evenPos + 1
end
if Distance(centerX, centerYodd, testX, testYodd) <= radius then
xOffsetsOddY[radius][oddPos] = textXoffset
oddPos = oddPos + 1
end
end
end
yOffset = yOffsets[radius]
end
local xOffset
if y % 2 == 0 then -- y is even
xOffset = xOffsetsEvenY[radius]
else -- y is odd
xOffset = xOffsetsOddY[radius]
end
local number = #yOffset
if myX then --sort returned values by direction I am comming from
local Sort = table.sort
local sortIdx, sortX, sortY = tempIdx[radius], sortedOffsetsX[radius], sortedOffsetsY[radius]
if not sortIdx then
sortIdx, sortX, sortY = {}, {}, {}
tempIdx[radius], sortedOffsetsX[radius], sortedOffsetsY[radius] = sortIdx, sortX, sortY
for i = 1, number do
sortIdx[i] = i
end
end
Sort(sortIdx, function(a, b)
local Distance = Map.PlotDistance
local myX, myY, xOffset, yOffset = myX, myY, xOffset, yOffset
return Distance(myX, myY, x + xOffset[a], y + yOffset[a]) < Distance(myX, myY, x + xOffset[b], y + yOffset[b])
end)
for i = 1, number do
local sortIndex = sortIdx[i]
sortX[i] = xOffset[sortIndex]
sortY[i] = yOffset[sortIndex]
end
xOffset, yOffset = sortX, sortY --use sorted table instead of unsorted table
end
local i = 0
return function()
local x, y, bWrapX, bWrapY, number, xOffest, yOffset = x, y, bWrapX, bWrapY, number, xOffest, yOffset --for speed
while i < number do
i = i + 1
local xAdj = x + xOffset[i]
local yAdj = y + yOffset[i]
if bWrapX then
if xAdj < 0 then
xAdj = xAdj + iW
elseif xAdj >= iW then
xAdj = xAdj - iW
end
end
if bWrapY then
if yAdj < 0 then
yAdj = yAdj + iH
elseif yAdj >= iH then
yAdj = yAdj - iH
end
end
if yAdj >= 0 and yAdj < iH and xAdj >= 0 and xAdj < iW then --only return a valid map coordinant
return xAdj, yAdj
end
end
end
end
Code:
function DebugPlotIterator(radius, nearX, nearY) --nearX, nearY optional
local unit = UI.GetHeadSelectedUnit()
Events.ClearHexHighlights()
local bStarted = false
print("calling PlotToRadiusIterator", unit:GetX(), unit:GetY(), radius, nearX, nearY)
for x, y in PlotToRadiusIterator(unit:GetX(), unit:GetY(), radius, nearX, nearY) do
print(x, y)
if bStarted then
Events.SerialEventHexHighlight( ToHexFromGrid( Vector2( x, y ) ), true, Vector4( 0, 1, 0, 1 ))
else
Events.SerialEventHexHighlight( ToHexFromGrid( Vector2( x, y ) ), true, Vector4( 1, 0, 1, 1 ))
bStarted = true
end
end
end
Last edited by Pazyryk; Jul 26, 2012 at 02:54 PM. |
|
|
|
|
|
#2 |
|
Prince
Join Date: Sep 2010
Location: Poland
Posts: 386
|
Thank you. Nice tool for map script developers, I think that i will use it for starting plots optimization.
|
|
|
|
|
|
#3 |
|
Deity
Join Date: Jun 2008
Posts: 2,054
|
I fixed a minor error in the code. Changed,
Code:
local x, y, bWrapY, bWrapY, number, xOffest, yOffset = x, y, bWrapY, bWrapY, number, xOffest, yOffset Code:
local x, y, bWrapX, bWrapY, number, xOffest, yOffset = x, y, bWrapX, bWrapY, number, xOffest, yOffset Also, in the spirit of full disclosure, I must admit that I never tested Y map wrapping. If you plan to use doughnut-shaped worlds, you might want to test this to make sure it is working properly. If not, you can delete out some code for a 0.000001 second boost if you like. Last edited by Pazyryk; Jun 13, 2012 at 10:15 AM. |
|
|
|
|
|
#4 |
|
Prince
Join Date: Mar 2009
Posts: 470
|
So I have a question about using this (yes yes I know you said it was simple to use but I am dumb). Let's say that I acquired the plot value of a city with :GetCityPlot (or whatever it is) would I then be able to simply put that variable into the location where it says centerx, centery?
For example: Spoiler:
|
|
|
|
|
|
#5 |
|
King
Join Date: May 2011
Posts: 943
|
Just use that.
![]() Code:
local plot = iCity:GetCityPlot() PlotToRadiusIterator(plot:GetX(), plot:GetY(), 2)
__________________
Reseed! Preview and regenerate map in-game. Ingame Editor (IGE): An in-game editor for terrain, units, cities, civilizations, etc. Lua and UI reference |
|
|
|
|
|
#6 |
|
Prince
Join Date: Mar 2009
Posts: 470
|
Thanks Don! For future reference it was actually iCity:Plot to get the value.
On that note however, the radius thingy did use the city tile! The radius was correctly within 2 squares, but the tile the function started on was also altered. What did I do wrong? The code looked like this: Spoiler:
|
|
|
|
|
|
#7 |
|
King
Join Date: May 2011
Posts: 943
|
You mean the iterator also returns the city plot itself? Nothing wrong with that, it's expected. For your own needs, you just need to check that the returned plot is not the city plot. Btw, GetResourceType does not take any argument.
Code:
if pPlot ~= plot and pPlot:GetResourceType() == -1 then Code:
local resourceID = GameInfo.Resources["RESOURCE_GOLD"].ID
__________________
Reseed! Preview and regenerate map in-game. Ingame Editor (IGE): An in-game editor for terrain, units, cities, civilizations, etc. Lua and UI reference Last edited by DonQuiche; Jul 17, 2012 at 11:02 AM. |
|
|
|
|
|
#8 |
|
Warlord
Join Date: Mar 2008
Location: IN.
Posts: 256
|
Hello! Here's the code from the 1066AD Viking scenario. I wonder if the Map.PlotDistance(pCity:GetX(), pCity:GetY(), iLondonX, iLondonY) has the same function.
![]() Code:
if (iBuildingType == GameInfo.Buildings["BUILDING_COURTHOUSE"].ID) then -- Only 8+ tiles from London if (Map.PlotDistance(pCity:GetX(), pCity:GetY(), iLondonX, iLondonY) < 8) then return false; end -- Only in originally Anglo-Saxon cities if (pCity:GetOriginalOwner() ~= iEngland) then return false; end end
__________________
Massive ModPacks that will bring you back to theThree Kingdom Era of ancient China: [BTS] The History of Three Kingdoms: V2.0 Released on 02.17.09! [G&K] The History of Three Kingdoms : V0.3 Released on 10.01.12! |
|
|
|
|
|
#9 |
|
Deity
Join Date: May 2011
Location: Near Portsmouth, UK
Posts: 2,494
|
Map.PlotDistance() will tell you the number of tiles in a direct line between two plots, however, it will not get you a sequence of plots X tiles from a given plot - which is what the iterator does.
Two related, but completely different, functions.
__________________
All my mods (.civ5mod files) can be downloaded from http://www.picknmixmods.com/. If you want to incorporate (parts of) my mods into your own mod(s), please read this first Snap-shots (which are slowly going out-of-date) can still be download in zipped groups from the CfC Downloads Database - search for PickNMix - a list of what is in each zipped group can be found here |
|
|
|
|
|
#10 | |
|
Deity
Join Date: Jun 2008
Posts: 2,054
|
Quote:
@xxhe, You could iterate over all plots on the map and then use Map.PlotDistance to determine which ones are within a certain radius. That would do the same exact thing. But PlotToRadiusIterator() will be much much faster, especially for short radii (or long radii if it is called repeatedly). Last edited by Pazyryk; Jul 26, 2012 at 02:44 PM. |
|
|
|
|
![]() |
| Bookmarks |
|
| Thread Tools | |
|
|