Complicated city selection code with table stuff

Craig_Sutter

Deity
Joined
Aug 13, 2002
Messages
2,773
Location
Calgary, Canada
I am working on the first part of a long string of script which I will use to generate semi-random Viking attacks upon the British Isles. To that end, I am trying to create a table of cities that will be possible subjects of attacks.

I check that when a city is founded, it is within one of the grids I have created. If that is the case, the plot is then checked to see if it is either coastal or adjacent to a coast (I used whoward69's lighthouse code for this). I hope I ported it over correctly.

If everything works, the plot index should be stored as well as if the plot is coastal or adjacent to a coast.

Later, I will create a function to randomly select one of the six grids, and then randomly select a coastal or adjacent coastal city (with a greater probability of the former) and have a random Viking civilization attack the owner (given fulfillment of certain criteria).

For now, I have just done the selection process. I am very unfamiliar with tables, so I hope I have done that aspect correctly. Later, I want to figure out how to store it over saves. For now, I just want to get the information into the table correctly.

I have a couple of questions:

1) I have cities being founded during Events.SequenceGameInitComplete... will the function record those cities?

2) I don't need cities that are neither coastal nor adjcoastal... I'm not certain my code eliminates those.

Please excuse any glaring mistakes.

Code:
tNorthscotland = {iplot,coastal,adjcoast}
tUmbria = {iplot,coastal,adjcoast}
tAnglia = {iplot,coastal,adjcoast}
tSouthengland= {iplot,coastal,adjcoast}
tIrishsea = {iplot,coastal,adjcoast}
tSouthireland = {iplot,coastal,adjcoast}

function citylist(iPlayer, iX, iY)

	local plot = Map.GetPlot(iX, iY);
	local iplot = plot:GetIndex()
		
	if iX>=43 and iX<=84 and iY>=63 and iY<=77 then
		
		citysort()
		local tNorthscotland = iplot, coastal, adjcoastal
			
	end

	if iX>=47 and iX<=57 and iY>=32 and iY<=48 then

		citysort()
		local tUmbria = iplot, coastal, adjcoastal

	end

	if iX>=46 and iX<=52 and iY>=18 and iY<=34 then
	
		citysort()
		local tAnglia = iplot, coastal, adjcoastal
	
	end

	if iX>=26 and iX<=46 and iY>=14 and iY<=28 then

		citysort()
		local tSouthengland = iplot, coastal, adjcoastal

	end

	if iX>=33 and iX<=48 and iY>=47 and iY<=67 then
	
		citysort()
		local tIrishsea = iplot, coastal, adjcoastal

	end

	if iX>=47 and iX<=57 and iY>=32 and iY<= 48 then

		citysort()
		local tSouthireland = iplot, coastal, adjcoastal

	end

end

GameEvents.PlayerCityFounded.Add(citylist)

directions = {DirectionTypes.DIRECTION_NORTHEAST, DirectionTypes.DIRECTION_EAST, DirectionTypes.DIRECTION_SOUTHEAST,
              DirectionTypes.DIRECTION_SOUTHWEST, DirectionTypes.DIRECTION_WEST, DirectionTypes.DIRECTION_NORTHWEST}


function citysort (iX, iY)

local CityPlot = Map.GetPlot(iX, iY);
if CityPlot:IsCoastalLand() then
      -- If the city is on a coastal tile
      return coastal
    else
      -- Or, if the city is adjacent to a coastal tile
      for loop, direction in ipairs(directions) do
        local pPlot = Map.PlotDirection(pCityPlot:GetX(), pCityPlot:GetY(), direction)

        if (pPlot ~= nil and iPlayer == pPlot:GetOwner()) then
          if (isCoastLine(pPlot)) then
			if (pPlot:IsWater() and not pPlot:IsLake()) then
			 return adjcoast
				end
            
          end
        end
      end
    end
end

Thank-you.

PS I have not tested the code as I am in the process of doing other tests and do not want to redo the project until that test is done.
 
Since you haven't defined any of iplot,coastal,adjcoast prior to trying to use them, doing the following:
Code:
tNorthscotland = {iplot,coastal,adjcoast}
tUmbria = {iplot,coastal,adjcoast}
tAnglia = {iplot,coastal,adjcoast}
tSouthengland= {iplot,coastal,adjcoast}
tIrishsea = {iplot,coastal,adjcoast}
tSouthireland = {iplot,coastal,adjcoast}
will be the same as doing:
Code:
tNorthscotland = {nil,nil,nil}
tUmbria = {nil,nil,nil}
tAnglia = {nil,nil,nil}
tSouthengland= {nil,nil,nil}
tIrishsea = {nil,nil,nil}
tSouthireland = {nil,nil,nil}
-------------------------------------------------------------------

These constructions are going to be problematical:
Code:
if iX>=43 and iX<=84 and iY>=63 and iY<=77 then
	citysort()
	local tNorthscotland = iplot, coastal, adjcoastal
end
Because:
  1. You haven't passed any info to function "citysort()" therefore it is going to have "nil" for all parameters it is expecting
  2. You haven't used a method to 'trap' in some way the information that function "citysort()" returns as a result of its calculations
  3. This line
    Code:
    local tNorthscotland = iplot, coastal, adjcoastal
    is not updating the table you named as tNorthscotland, it is instead trying to create a local variable of the same name and assigning the value of iplot (which had previously been defined as the city plot's ID#) as its value.
    • If you had instead, these two lines of code:
      Code:
      local coastal, adjcoastal = true, false 
      local tNorthscotland = iplot, coastal, adjcoastal
      You would get exactly the same result (ie, local variable tNorthscotland is temporarily created with the same value assigned as is in "iplot")
    • If you had instead, these two lines of code:
      Code:
      local coastal, adjcoastal = true, false 
      local tNorthscotland = {iplot, coastal, adjcoastal}
      You would get a new lua-table localized to the level of the code in function citylist(). Its data pairs would be:
      Code:
      1		The ID# of the city's plot
      2		true
      3		false
      Item # 2 in the table would be a value of "true" because that is what is in variable "coastal" in this example, and Item # 3 in the table would be a value of "false" because that is what is in variable "adjcoastal" in this example
  4. In order to 'trap' the data returned from function "citysort()" you need something like:
    Code:
    local sCitySortResult = citysort(iX, iY)
    Which you can then stick wherever you need it. If it were me I would only have the line one time within function "citylist" and I would move it directly under the line
    Code:
    local iplot = plot:GetIndex()
  5. It looks like you have a duplicate set of conditions for Umbria and Southireland so they would always both be containing the same information.
  6. I would restructure each of the plot-location requirements so that your "if iX>=xx ... then...." blocks are as something like this (for tNorthscotland):
    Code:
    if iX>=43 and iX<=84 and iY>=63 and iY<=77 then
    	tNorthscotland[iplot] = sCitySortResult
    end
-------------------------------------------------------------------

You have some mis-matches in variable names in the function "citysort"
Code:
function citysort(iX, iY)
	local [color="blue"]CityPlot[/color] = Map.GetPlot(iX, iY);
	if [color="blue"]CityPlot[/color]:IsCoastalLand() then
		-- If the city is on a coastal tile
		return coastal
	else
		-- Or, if the city is adjacent to a coastal tile
		for loop, direction in ipairs(directions) do
			local pPlot = Map.PlotDirection([color="red"]pCityPlot[/color]:GetX(), [color="red"]pCityPlot[/color]:GetY(), direction)

			if (pPlot ~= nil and iPlayer == pPlot:GetOwner()) then
				if (isCoastLine(pPlot)) then
					if (pPlot:IsWater() and not pPlot:IsLake()) then
						return adjcoast
					end
				end
			end
		end
	end
end
Also, this line
Code:
if (isCoastLine(pPlot)) then
appears to be referencing a function that is not defined anywhere whereas what I suspect you wanted was
Code:
if (pPlot ~= nil and pPlot:IsCoastalLand()) then
	return adjcoast
end
Because you are looking at plots that are adjacent to the city, and if the city was already determined to be on a coastal plot, the lines of code in the "else" clause would not be executing. Therefore all we care about when looking at plots adjacent to the city is whether or not they are coastal-lands, and if so, then the plot in question is (a) land and (b) a coastline. And this all assumes that Plot:IsCoastalLand() works in the way I am assuming (which given Firaxis, might not actually be true).

Also, function "citysort" appears to have no code on what to return when the city is neither built on a coastline or one of its adjacent tiles is on a coastline

In addition, what you are "returning" will be seen by lua to be variables, so the values currently within those variables is what would be returned. However, the variables you are specifying have never been given a value, so lua will evaluate these as being equal to "nil", and that is what will actually be returned.

Compare to what I would use as a starting place for that function:
Code:
function citysort(iX, iY)
	local CityPlot = Map.GetPlot(iX, iY);
	if CityPlot:IsCoastalLand() then
		-- If the city is on a coastal tile
		return "coastal"
	else
		-- Or, if the city is adjacent to a coastal tile
		for loop, direction in ipairs(directions) do
			local pPlot = Map.PlotDirection(pCityPlot:GetX(), pCityPlot:GetY(), direction)

			if (pPlot ~= nil and pPlot:IsCoastalLand()) then
				return "adjcoast"
			end
		end
	end
	return "inland"
end
I haven't looked through the code any closer to see if there are any additional optimizations that could be made.

-------------------------------------------------------------------

I would alter the overall code as follows, assuming I understand what you want these tables to hold after cities are founded (ie, the plot ID# for a city that is either coastal or adjcoastal, and whether or not it is "coastal" or "adjcoast"):
Spoiler suggested redraft :
Code:
tNorthscotland = {}
tUmbria = {}
tAnglia = {}
tSouthengland= {}
tIrishsea = {}
tSouthireland = {}

function citylist(iPlayer, iX, iY)
	local plot = Map.GetPlot(iX, iY);
	local iplot = plot:GetIndex()
	local sCitySortResult = citysort(iX, iY)

	if sCitySortResult ~= "inland" then	
		if iX>=43 and iX<=84 and iY>=63 and iY<=77 then
			tNorthscotland[iplot] = sCitySortResult
		end

		if iX>=47 and iX<=57 and iY>=32 and iY<=48 then
			tUmbria[iplot] = sCitySortResult
		end

		if iX>=46 and iX<=52 and iY>=18 and iY<=34 then
			tAnglia[iplot] = sCitySortResult
		end

		if iX>=26 and iX<=46 and iY>=14 and iY<=28 then
			tSouthengland[iplot] = sCitySortResult
		end

		if iX>=33 and iX<=48 and iY>=47 and iY<=67 then
			tIrishsea[iplot] = sCitySortResult
		end

		if iX>=47 and iX<=57 and iY>=32 and iY<= 48 then
			tSouthireland[iplot] = sCitySortResult
		end
	end
end
GameEvents.PlayerCityFounded.Add(citylist)

directions = {DirectionTypes.DIRECTION_NORTHEAST, DirectionTypes.DIRECTION_EAST, DirectionTypes.DIRECTION_SOUTHEAST,
              DirectionTypes.DIRECTION_SOUTHWEST, DirectionTypes.DIRECTION_WEST, DirectionTypes.DIRECTION_NORTHWEST}


function citysort(iX, iY)
	local CityPlot = Map.GetPlot(iX, iY);
	if CityPlot:IsCoastalLand() then
		-- If the city is on a coastal tile
		return "coastal"
	else
		-- Or, if the city is adjacent to a coastal tile
		for loop, direction in ipairs(directions) do
			local pPlot = Map.PlotDirection(pCityPlot:GetX(), pCityPlot:GetY(), direction)

			if (pPlot ~= nil and pPlot:IsCoastalLand()) then
				return "adjcoast"
			end
		end
	end
	return "inland"
end
Obviously I have not tested this. I haven't taken a closer look to see if it could be optimized a bit more. Also, as is, you will need to think about what to do regarding cities that have their plot ID# in the list but that might later be destroyed.

After several cities are founded, only three of them might qualify as being either "coastal" or "adjcoast", and so you might have table structures such as, say:
Code:
tNorthscotland = {[497] = "coastal" }
tUmbria = {}
tAnglia = {[642] = "adjcoast", [847] = "coastal" }
tSouthengland= {}
tIrishsea = {}
tSouthireland = {}
"497", "642", and "847" would be plot ID#'s and I just made up some numbers off the top of my head to show what the table contents would be like. So if you were to follow up and do this series of commands:
Code:
print("Contents of the key, value pairs in table tNorthscotland:")
for k,v in pairs(tNorthscotland) do print(k,v) end
print("Contents of the key, value pairs in table tUmbria:")
for k,v in pairs(tUmbria) do print(k,v) end
print("Contents of the key, value pairs in table tAnglia:")
for k,v in pairs(tAnglia) do print(k,v) end
print("Contents of the key, value pairs in table tSouthengland:")
for k,v in pairs(tSouthengland) do print(k,v) end
print("Contents of the key, value pairs in table tIrishsea:")
for k,v in pairs(tIrishsea) do print(k,v) end
print("Contents of the key, value pairs in table tSouthireland:")
for k,v in pairs(tSouthireland) do print(k,v) end
You would get this printed in the lua log:
Contents of the key, value pairs in table tNorthscotland:
497 coastal
Contents of the key, value pairs in table tUmbria:
Contents of the key, value pairs in table tAnglia:
642 adjcoast
847 coastal
Contents of the key, value pairs in table tSouthengland:
Contents of the key, value pairs in table tIrishsea:
Contents of the key, value pairs in table tSouthireland:
 
Thank-you. Amazingly enough, your explanation makes total sense to me :) . It is clear and concise and very instructive.

This has helped me immensely in trying to figure out the necessity of defining variables correctly, nested functions, and tables. I am mildly pleased that I was not totally out to lunch on the creation of tables :).

I probably won't be able to test this until later this week (work and life :) ).

Again, thank-you.

PS I think destroyed cities can be handled in later code by checking if the plot has a city and going from there.

Next step is to figure out how to make a weighted selection amongst the regions and cities within the region.
 
Back
Top Bottom