1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

City Nearby Map Datas Utility

Discussion in 'Civ5 - Mod Components' started by LeeS, Aug 10, 2016.

  1. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA


    Important:
    • The discussion on use of this utility does not assume you are expert in manipulating complexly-structured lua tables, but it does assume you are familiar with basic table-scanning methods of using "pairs()" and "ipairs()"
    • If you do not understand what an lua table is, nor how a scan through an lua-table works using either the "pairs()" method or the "ipairs()" method, then this utility is not for you.


    Description:
    1. Performs all lua code logic and instructions for gathering data from the game-map within the 3-tile working-range of a single city:
      • Units (Enemy, Friendly, etc)
      • Map Resources
      • Map Improvements
      • Other Cities
    2. The data to be collected can be configured so that if you want your mod's main lua to only scan for nearby units, the code can be configured to only collect unit information. The same is true for Resources, Improvements, and Other Cities.
    3. The code allows collection of multiple types of data. So you can collect data only about Units and Improvements, or only about Units and Resources, or only about Resources, or only about Resources and Improvements, as examples.
    4. If no 'selection' for the type of data to be collected is made, the code defaults to collecting data for all four types specified in #1.
    5. Data is collected for an individual city into one 'giant' lua-table that has multiple lua sub-tables.
      • For advanced users of lua-tables, documentation is provided here on all the subtable structures and the provided 'keys' and 'values' within these tables.
      • For those not so versed in the use of complex lua-tables, "interface" routines are included as part of this system to make it simple for you to extract the data you want from the larger table of data
    6. Data is gathered for a single specified city.
      • For lua code that needs to run through each of a player's cities in turn, you simply use the same data-collection routine(s) for each city in turn.
      • This is actually no different efficiency-wise than writing all the lines for inspecting an individual city's plots within a larger loop through all of the player's cities.
      • What this routine does is handle all the code busy-work of inspecting a city's nearby plots, while allowing your code to gather all information about those nearby plots that pertains to units, resources, improvements, and other cities.
    7. You should see some processing improvements as compared to scanning a city's plots first to look through for units, and then scanning the same city's plots for resources, and then scanning the same city's plots for Farms, and then scanning the same city's plots looking for Mines on Iron, for example.
    8. You will not see much if any processing speed difference as compared to only looking for at a city's plots for Improved Iron plots, for example.
    9. The utility also contains the code for a few general-use toolkit-type functions which you might find useful. These can be found here


    How to Add the System to Your Mod:
    1. Download the Utility from the CFC Download Database: City Nearby Map Datas Utility CFC Download
    2. Unzip the utility's folder, and add file CityNearbyMapDatas.lua to your mod using ModBuddy's Add Exiting File menus.
    3. In Modbuddy, set file CityNearbyMapDatas.lua as ImportIntoVFS=true in the file's properties. (See Common Novice-Modder Mistakes -- "NOT GIVING DDS FILES AN "IMPORT INTO VFS=TRUE"" where there are detailed instructions under the spoiler for more detailed instructions)
    4. In your mod's main lua file, add the following line at the top:
      Code:
      include("CityNearbyMapDatas.lua")
    5. You can now make use of the utility's functions in your mod's main lua file


    Some Notes About the Download and the Files Within It:
    1. The download contains two files within an LUA folder:
      • CityNearbyMapDatas.lua
        • This is the actual utility and all its code.
        • This is the only file you actually need in any of your mods
        • DO NOT makes any changes whatever to the code within this file.
      • Lua Script1.lua
        • This is an example of how to make use of the utility within the lua-file of your mod.
        • It is set up to make various print statemenet to the game's lua.log but not otherwise actually do anything
        • It contains several different examples of how to apply various functions that are part of the utility
        • As Packaged, the utility is a fully functioning mod that you can copy into your game's MODS folder, and enable.
        • The mod is set to allow it to be added to a game in progress with no ill effects. You can activate the mod, load a game in progesss, and see the effects in the game's lua log. Other than the fact the code makes a lot of print statements into the lua log there is no other affect of the example usage of the utility.
    2. The download contains one file within an Instructions folder:
      • ListOfFunctions.lua
        • This file is meant to be a quick reference to the various functions you can use in relation to the utility, and has no other purpose.
        • The file is no way activated by the mod and has no effect whatever on the game.
        • This file is meant as an additional quick-reference in your programming, and is not meant to be added to any of your actual mods.
        • You do not need this file to be part of your mod in order for the utility to work
    3. The download contains one file within a ToolkitFiles folder:
      • ToolkitFiles.lua
        • This file contains a few general toolkit functions that you can use in your other mods without needing the utility.
        • The file is in no way activated by the mod and has no effect whatever on the game.
        • This file is meant for your convenience in your other mods if you want to make use of the general toolkit-type functions mentioned elsewhere in this thread. (just add the file to a mod, set it as ImportIntoVFS=true, and add an "include" statement into your mod's lua-code)
        • You do not need this file to be part of your mod in order for the utility to work.


    Basic Use of the Utility:
    1. The utility gathers map-related data near an individual city, so to use the utility you must program your lua to run the utility's primary function for each city where you want to collect data.
    2. Since the utility collects the map-related data pertaining to the specified city and places the results into an lua table, you need to call the primary function of the utility as like:
      Code:
      local tNearbyDatas = GetCityMapDatas(pCity)
      This would store all the results of the data collection into an lua-table called tNearbyDatas. Thereinafter whenever we want to grab some part of the collected data back out of this lua-table, we must reference table tNearbyDatas.​
      • It does not matter what we call the table where we collect the city's nearby map data. We could just as easily call the table "Cheeseburgers", as like this:
        Code:
        local Cheeseburgers = GetCityMapDatas(pCity)
      • We just have to be consistent in how we refer to the table where the city's nearby map data is collected.
      • Either of these two methods for "naming" the city's table of collected map-data would work:
        Code:
        local Cheeseburgers = GetCityMapDatas(pCity)
        if Cheeseburgers.CityResource ~= nil then
        	print(Cheeseburgers.CityResource)
        end
        or​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        if tNearbyDatas.CityResource ~= nil then
        	print(tNearbyDatas.CityResource)
        end
        Note that in both examples the naming used within a single example is consistent throughout the example​
    3. For clarity's sake I will always use tNearbyDatas as the table where a single city's nearby map data is collected.


    Primary Function of the Utility: GetCityMapDatas(pCity)
    1. Function Name: GetCityMapDatas()
    2. Required Argument:
      • City Object in place of variable "pCity", as in:
        Code:
        GetCityMapDatas(pCity)
      • No other arguments are required, though optional arguments may be included.
    3. Optional Arguments: Up to 4 text-string arguments can be included AFTER the city-object argument, in any order.
      • Each of these tells the code which type of data to collect.
      • When no additional arguments are added, all data is collected
      • The additional text-string arguments allowed are:
        1. "Units"
        2. "Resources"
        3. "Improvements"
        4. "Cities"
      • As mentioned, these can be stated in any order following the city object.
      • If any valid optional argument is used, then ONLY data that corresponds to those listed optional arguments will be collected.
      • If oddball text-string data is included, such as "Lettuce", the code will ignore such, and the argument where such an oddball designation appears will be treated as if it did not exist.
      • Note that when "Resources" is selected by default or by adding it as an active argument-selection, the plot of the city passed to the function will always get data collected for it if the city is located on a plot with a map resource.
    4. Example 'Call' Statements:
      • Using only the required argument:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
      • Gathering only data about units near the city:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Units")
      • Gathering only data about resources near the city:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Resources")
      • Gathering only data about resources and units near the city:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Resources", "Units")
        or​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Units", "Resources")
      • Gathering only data about units near the city because "Resources" is mispelled:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Rehorses", "Units")
        or​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Units", "Rehorses")
      • Gathering data about all data-types near the city because "Cheeseburgers" is not recognized as valid:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity, "Cheeseburgers")
        In this case the code reverts to collecting all data because no valid limiting argument was stated.​
    5. For Map Plot Information, Data is only collected about plots that adhere to the following rules:
      • only plots that have a map resource, or a terrain improvement, or a unit, or a city are included
      • are not being worked by some other city (whether of the same player or a different player)
      • are not owned by some other player (except that some limited data is gathered about cities)
    6. For those not well-versed in using complex lua tables, a series of data-filtering functions is described here. All of these functions are included as an integral part of the utility.
    7. For advanced users of lua tables, the exact data and structuring of the date collected can be found here.


    Using the Utility to Scan Near a Single City:
    1. For purposes of discussion, let's assume we want to give a civilization called CIVILIZATION_WHEATGRABBERS the ability to grab any unclaimed wheat tiles within the 3-tile-range of any newly founded city. We can use this utility to gather a list of all such plots, and give them to the player whenever they found a city.
    2. PlayerCityFounded seems like the best hook event to use, so for the purposes of this example we'll use that event as our lua-hook.
    3. Our code is thus as this:
      Code:
      local iWheatGrabberCivilization = GameInfoTypes.CIVILIZATION_WHEATGRABBERS
      local iWheatResource = GameInfoTypes.RESOURCE_WHEAT
      function WheatGrabberCityFounded(iPlayer, iCityX, iCityY)
      	local pPlayer = Players[iPlayer]
      	if pPlayer:GetCivilizationType() ~= iWheatGrabberCivilization then return end
      	local pCity = Map.GetPlot(iCityX, iCityY):GetPlotCity()
      	local iCityID = pCity:GetID()
      	local tNearbyDatas = GetCityMapDatas(pCity, "Resources")
      	local tUnClaimedWheat = GetCityResourcePlots(tNearbyDatas, iWheatResource, "UnclaimedPlots")
      	if #tUnClaimedWheat > 0 then
      		for k,pPlot in pairs(tUnClaimedWheat) do
      			pPlot:SetOwner(iPlayer, iCityID)
      		end
      	end
      end
      GameEvents.PlayerCityFounded.Add(WheatGrabberCityFounded)
      
      print("Our Code for CIVILIZATION_WHEATGRABBERS PlayerCityFounded has loaded properly")
    4. I've used a function called GetCityResourcePlots() which I have not explained as yet. Info on how this function works as part of the utility is explained here.
      • GetCityResourcePlots() is a data-handling routine that looks through the data collected by GetCityMapDatas() and returns a simpler lua table that holds only the type of information specified.
      • The lua table returned by GetCityResourcePlots() will always be in a format where within each pair of data the 'k' is an integer and the 'v' is a plot object for an individual plot that met the desired conditions.
      • The contents of the lua table returned will always be in the form of an array table.
    5. This portion of the code is the part you will normally have to come up with for your self:
      Code:
      if #tUnClaimedWheat > 0 then
      	for k,pPlot in pairs(tUnClaimedWheat) do
      		pPlot:SetOwner(iPlayer, iCityID)
      	end
      end
      This portion of the code is where the data acquired by this utility is actually used to create an effect, and will vary wildly from one mod creator to another​


    Using the Utility to Scan Near all of a Player's Cities:
    1. For purposes of discussion, let's assume we want to give a civilization called CIVILIZATION_WHEATGRABBERS the ability to get 1 culture every turn for every wheat plot they own that is within 3-tiles of any of their cities, regardless of whether they are working the wheat plot.
    2. We can use this utility to gather a list of all such plots, and give this extra culture to the player.
      • However, since we are allowing 1 culture for every owned plot (not just those being worked) and two cities could potentially "grab" the same plot, we need to keep a running tally of all plots we have already given the culture for, and filter out any plots already processed.
      • The utility has a function written to ease the mechanics of tracking just such issues. It is called FilterTableContents(), and details about it can be found here.
    3. I've also used the function called GetCityResourcePlots() again, though with slightly different settings. Info on how this function works as part of the utility is explained here.
      • GetCityResourcePlots() is a data-handling routine that looks through the data collected by GetCityMapDatas() and returns a simpler lua table that holds only the type of information specified.
      • The lua table returned by GetCityResourcePlots() will always be in a format where within each pair of data the 'k' is an integer and the 'v' is a plot object for an individual plot that met the desired conditions.
      • The contents of the lua table returned will always be in the form of an array table.
    4. PlayerDoTurn seems like the best hook event to use, so for the purposes of this example we'll use that event as our lua-hook.
    5. Our code is thus as this:
      Code:
      local iWheatGrabberCivilization = GameInfoTypes.CIVILIZATION_WHEATGRABBERS
      local iWheatResource = GameInfoTypes.RESOURCE_WHEAT
      function WheatGrabberTurnFunction(iPlayer, iCityX, iCityY)
      	local pPlayer = Players[iPlayer]
      	if pPlayer:GetCivilizationType() ~= iWheatGrabberCivilization then return end
      	local tMyWheatPlotsThisTurn = {}
      	for pCity in pPlayer:Cities() do
      		local tNearbyDatas = GetCityMapDatas(pCity, "Resources")
      		local tCityWheatPlots = GetCityResourcePlots(tNearbyDatas, iWheatResource, "Plots")
      		tCityWheatPlots, tMyWheatPlotsThisTurn = FilterTableContents(tCityWheatPlots, tMyWheatPlotsThisTurn)
      		for Item,pPlot in pairs(tCityWheatPlots) do
      			pPlayer:ChangeJONSCulture(1)
      		end
      	end
      end
      GameEvents.PlayerDoTurn.Add(WheatGrabberTurnFunction)
      
      print("Our Code for CIVILIZATION_WHEATGRABBERS WheatGrabberTurnFunction has loaded properly")
    6. This portion of the code is the part you will normally have to come up with for your self:
      Code:
      for Item,pPlot in pairs(tCityWheatPlots) do
      	pPlayer:ChangeJONSCulture(1)
      end
      This portion of the code is where the data acquired by this utility is actually used to create an effect, and will vary wildly from one mod creator to another​
    7. As a general rule when running through all of a player's cities, you want your code effects to be shown within the loop so that everything pertaining to an individual city is processed before the loop moves on to the next player city.
      • Note that in the example shown, the variable tNearbyDatas is re-created in turn for each city as the loop runs through each of the player's cities in succession. This is both on purpose and the methodology needed.
    8. An alternative method is to make the processing for adding the culture after the scan through all cities is completed:
      Spoiler :
      Code:
      local iWheatGrabberCivilization = GameInfoTypes.CIVILIZATION_WHEATGRABBERS
      local iWheatResource = GameInfoTypes.RESOURCE_WHEAT
      function WheatGrabberTurnFunction(iPlayer, iCityX, iCityY)
      	local pPlayer = Players[iPlayer]
      	if pPlayer:GetCivilizationType() ~= iWheatGrabberCivilization then return end
      	local tMyWheatPlotsThisTurn = {}
      	for pCity in pPlayer:Cities() do
      		local tNearbyDatas = GetCityMapDatas(pCity, "Resources")
      		local tCityWheatPlots = GetCityResourcePlots(tNearbyDatas, iWheatResource, "Plots")
      		tCityWheatPlots, tMyWheatPlotsThisTurn = FilterTableContents(tCityWheatPlots, tMyWheatPlotsThisTurn)
      	end
      	if #tMyWheatPlotsThisTurn > 0 then
      		local iNumWheat = #tMyWheatPlotsThisTurn
      		pPlayer:ChangeJONSCulture(iNumWheat)
      	end
      end
      GameEvents.PlayerDoTurn.Add(WheatGrabberTurnFunction)
      
      print("Our Code for CIVILIZATION_WHEATGRABBERS WheatGrabberTurnFunction has loaded properly")
     
    Last edited: Oct 4, 2018
  2. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    • The following "Interface" Routines are provided to extract information from the utility into more easily usable forms for the programmer not versed in manipulating complexly-structured lua tables.

    Function FilterTableContents():
    1. FilterTableContents() is a data-handling routine that looks through the contents of a passed lua table and filters out contents that are already within a larger "accumulated data" table.
    2. FilterTableContents() will always return two tables as a result of being used:
      • the table passed as the 1st or "tCurrentTable" argument with any contents that were already in the second table removed from the "tCurrentTable" argument
      • the table passed as the 2nd or "tAccumulatedTable" argument with any new contents added to it that were in the passed "tCurrentTable" argument but not in the original version of the passed "tAccumulatedTable" argument
    3. these two returned lua tables will always be in a format where within each pair of data the 'k' is an integer and the 'v' is a data item of some kind, whether text, integer, or a pointer object.
    4. The contents of these lua tables returned will always be in the form of an array table.
    5. The entire code contents of this function will be shown because you can use this function in your other mods. It does not require the utility to run properly, and will accomplish the filtering for any conditions of two tables where one is the accumulated data placed into the lua-table's "values", and the other lua table is the one that needs repeat items removed from it.
      • The filtering function does however also need the ItemIsInTable function, which I have included in the code shown as needed.
    6. If you wish to use the function for another part of your mod, and the file where you want to do this is already using the utility, you do not need to (nor should you) copy the code of the FilterTableContents function into your lua file.
    7. Details: FilterTableContents()
      • Calling Format:
        FilterTableContents(tCurrentTable, tAccumulatedTable)​
      • Arguments:
        1. tCurrentTable: this will be an lua table with the data that needs to be filtered out for every item of data that is already existing within the table defined as "tAccumulatedTable"
          • only the "values" within this table will be looked at
          • any of the "values" within this table will be discarded (filtered out) if that "value" already exists within the table specified for "tAccumulatedTable"
          • There is no limitation on the type of data that can be used for the table's "Values": they can be integers, text, pointer objects, or even other lua tables (though as a general rule this is probably not best usage).
          • Although the "keys" are not looked at, it is better if these are integer values which also conform to an array.
        2. tAccumulatedTable: this will be an lua table that holds all previously processed pieces of data
          • table "tAccumulatedTable" will always be assumed to be constructed in such a way that the 'Keys' are all integer values arranged as an array, and the 'Values' are the corresponding data the programmer is insterested in.
      • Returned Data:
        Two pieces of information are returned, both of which will be lua-tables:
        • tNewTable: this will be a new lua table stripped of any information that was in both the originally-passed "tCurrentTable" and "tAccumulatedTable" tables
        • tAccumulatedTable: this will be the original lua table passed to the function for this argument-name, updated to also contain any new information from the table that was passed as "tCurrentTable".
        The order these two tables will be returned in will be as:
        Code:
        tNewTable, tAccumulatedTable
      • Example Usage:
        • Use as:
          Code:
          local tMyWheatPlotsThisTurn = {}
          local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
          local iResourceID = GameInfoTypes.RESOURCE_WHEAT
          for pCity in pPlayer:Cities() do
          	local tNearbyDatas = GetCityMapDatas(pCity)
          	local tCityWheatPlots = GetCityImprovementPlotswithResource(tNearbyDatas, iImprovementID, iResourceID, "Owned")
          	tCityWheatPlots, tMyWheatPlotsThisTurn = FilterTableContents(tCityWheatPlots, tMyWheatPlotsThisTurn)
          	for Item,pPlot in pairs(tCityWheatPlots) do
          		print("We have a Farm plot with Wheat on it near the city of " .. pCity:GetName() .. "! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
          		print(" ")
          	end
          end
          • Note that as in this example you should NOT localize the result of "FilterTableContents(tCityWheatPlots, tMyWheatPlotsThisTurn)" within a loop through a player's cities because this would create a 2nd version of "tMyWheatPlotsThisTurn" that would only be valid for each individual city in turn as that city is being processed by the loop through the player's cities.
          • The word "local" does NOT appear on this line within the loop through the player's cities:
            Code:
            tCityWheatPlots, tMyWheatPlotsThisTurn = FilterTableContents(tCityWheatPlots, tMyWheatPlotsThisTurn)
            This is the desired and correct method for using function FilterTableContents
        • Or use as in this example for a more advanced usage:
          Code:
          local tMyWheatPlotsThisTurn = {}
          local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
          local iResourceID = GameInfoTypes.RESOURCE_WHEAT
          for pCity in pPlayer:Cities() do
          	local tNearbyDatas = GetCityMapDatas(pCity)
          	local tCityWheatPlots = GetCityImprovementPlotswithResource(tNearbyDatas, iImprovementID, iResourceID, "Owned")
          	tCityWheatPlots, tMyWheatPlotsThisTurn = FilterTableContents(tCityWheatPlots, tMyWheatPlotsThisTurn)
          	for Item,pPlot in pairs(tCityWheatPlots) do
          		print("We have a Farm plot with Wheat on it near the city of " .. pCity:GetName() .. "! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
          		print(" ")
          	end
          end
          if #tMyWheatPlotsThisTurn > 0 then
          	local iNumFarmedWheat = #tMyWheatPlotsThisTurn
          	pPlayer:ChangeGold(iNumFarmedWheat)
          	print(iNumFarmedWheat .. " Gold added to the player's treasury")
          end
          • If you do not clear the contents of table "tMyWheatPlotsThisTurn" after the termination of the loop through a player's cities, then "tMyWheatPlotsThisTurn" will be an lua table holding all of the player's owned wheat plots with farms on them.
    8. Code of the function (in spoiler):
      Spoiler FilterTableContents :
      Code:
      function ItemIsInTable(tTable,DataItem)
      	for iTableItem,TableDataValue in pairs(tTable) do
      		if TableDataValue == DataItem then
      			return true
      		end
      	end
      	return false
      end
      function FilterTableContents(tCurrentTable, tAccumulatedTable)
      	local tNewTable = {}
      	for k,v in pairs(tCurrentTable) do
      		if not ItemIsInTable(tAccumulatedTable,v) then
      			table.insert(tNewTable, v)
      			table.insert(tAccumulatedTable, v)
      		end
      	end
      	return tNewTable, tAccumulatedTable
      end


    Function GetCityResourcePlots():
    1. GetCityResourcePlots() is a data-handling routine that looks through the data collected by GetCityMapDatas() and returns a simpler lua table that holds only the type of information specified.
    2. The lua table returned be GetCityResourcePlots() will always be in a format where within each pair of data the 'k' is an integer and the 'v' is a plot object for an individual plot that met the desired conditions.
    3. The contents of the lua table returned will always be in the form of an array table.
    4. Details:
      Returns a list of plots for the specified resource. If the resource is not present near the city the returned table will be an empty table.​
      • Calling Format:
        GetCityResourcePlots(tTable, iResourceID, sDataType)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iResourceID: the ID# of the map resource
        3. sDataType: text string of the desired list of plots. Acceptable values for this argument are:
          • "Plots": table with all plots having the resource recorded as "Values", except those in unclaimed territory
          • "WorkedPlots": table with all Worked plots having the resource recorded as "Values"
          • "WorkedPlotsThatAreNotPillaged": table with all Unpillaged Worked plots having the resource recorded as "Values"
          • "WorkedPlotsThatArePillaged": table with all Pillaged Worked plots having the resource recorded as "Values"
          • "WorkedPlotsWithIncorrectImprovement": table with all Worked plots having the resource but with an incorrect improvement for that resource recorded as "Values"
          • "WorkedPlotsWithNoImprovement": table with all Worked plots having the resource but no terrain improvemnent recorded as "Values"
          • "PillagedPlots": table with all Pillaged plots having the resource recorded as "Values"
          • "UnclaimedPlots": table with all Unowned plots having the resource recorded as "Values"
          " are required as shown since the code is looking for a text string and not a variable name
      • Returned Data:
        The returned data will be an lua table that conforms to the "iResourceID" and setting of the "sDataType" arguments. If no such resource is near the city or if there are no such plots, then the returned value will be an empty lua table.​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iResourceID = GameInfoTypes.RESOURCE_WHEAT
        local tResourcePlots = GetCityResourcePlots(tNearbyDatas, iResourceID, "WorkedPlots")
        for Item,pPlot in pairs(tResourcePlots) do
        	print("We Have a plot with Wheat on it! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
        end
    5. This function does not check whether the player can use the specified resource or not. (ie, whether the player has the tech that reveals the resource)


    Function GetCityImprovementPlots():
    1. Details:
      Returns a list of plots for the specified improvement. If the improvement is not present near the city the returned table will be an empty table​
      • Calling Format:
        GetCityImprovementPlots(tTable, iImprovementID, sDataType)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
        3. sDataType: text string of the desired list of plots. Acceptable values for this argument are:
          • "Plots": table with all plots having the improvmeent recorded as "Values", except those in unclaimed territory
          • "WorkedPlots": table with all Worked plots having the improvmeent recorded as "Values"
          • "WorkedPlotsThatAreNotPillaged": table with all Unpillaged Worked plots having the improvmeent recorded as "Values"
          • "WorkedPlotsThatArePillaged": table with all Pillaged Worked plots having the improvmeent recorded as "Values"
          • "WorkedPlotsWithIncorrectImprovementForResource": table with all Worked plots having the improvmeent and where the improvement is incorrect for the plot's resource recorded as "Values"
          • "UnWorkedPlotsWithIncorrectImprovementForResource": table with all Unworked plots having the improvmeent and where the improvement is incorrect for the plot's resource recorded as "Values"
          • "PillagedPlots": table with all Pillaged plots having the improvmeent recorded as "Values"
          • "UnclaimedPlots": table with all Unowned plots having the improvmeent recorded as "Values"
          • "PlotsWithCorrectResources": table with all plots having both the improvement and a resource that is correct for the improvement recorded as "Values"
          • "PlotsWithInCorrectResources": table with all plots having both the improvement and an incorrect resource for the improvement recorded as "Values"
          " are required as shown since the code is looking for a text string and not a variable name
      • Returned Data:
        The returned data will be an lua table that conforms to the "iImprovementID" and setting of the "sDataType" arguments. If no such improvement is near the city or if there are no such plots, then the returned value will be an empty lua table.​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        local tFarmedPlots = GetCityImprovementPlots(tNearbyDatas, iImprovementID, "WorkedPlots")
        for Item,pPlot in pairs(tFarmedPlots) do
        	print("We Have a plot with a Farm on it! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
        end
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function GetNumCityWorkingImprovementPlots():
    1. Details:
      returns the integer number of plots with the specified improvement that the city is working​
      • Calling Format:
        GetNumCityWorkingImprovementPlots(tTable, iImprovementID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
      • Returned Data:
        integer value with the number of plots the city is working that have the improvement​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        if GetNumCityWorkingImprovementPlots(tNearbyDatas, iImprovementID) > 3 then
        	print("The City of " .. pCity:GetName() .. " is working at least 3 Farms")
        end
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function DoesImprovementExistNearCity():
    1. Details: returns true/false for whether a the specified improvement exists on the map near the city but does not account for whether the plot is worked, pillaged, owned, or in unclaimed territory.
      • Calling Format:
        DoesImprovementExistNearCity(tTable, iImprovementID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
      • Returned Data:
        boolean true/false​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        if DoesImprovementExistNearCity(tNearbyDatas, iImprovementID) then
        	print("The City of " .. pCity:GetName() .. " has a Farm nearby")
        end
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function DoUnpillagedImprovementsExistNearCity():
    PSA: I had mis-spelled "Improvements" in the name of this function
    • This is now fixed, but the original mis-spelt function name is still available in updated versions of the utility
    • You do not need to alter the code in your mod to 'fix' the mis-spelling, but you should download the latest version of the utility and replace the old version of CityNearbyMapDatas.lua with the newer one in your mod.
    1. Details:
      • returns true/false for whether any unpillaged plots with the specified improvement exists on the map near the city
      • Plots that are Worked by the city, Owned by the city owner, or which are unowned will be considered for the end result
      • Calling Format:
        DoUnpillagedImprovementsExistNearCity(tTable, iImprovementID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
      • Returned Data:
        returns boolean true or false​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        if DoUnpillagedImprovementsExistNearCity(tNearbyDatas, iImprovementID) then
        	print("The City of " .. pCity:GetName() .. " has an unpillaged Farm nearby")
        end
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function GetNumCityImprovementPlotsWithResource():
    1. Details: returns the integer number of plots with the specified improvement that also have the specified resource
      • Calling Format:
        • GetNumCityImprovementPlotsWithResource(tTable, iImprovementID, iResourceID, ...)
        • The "..." argument is a stand-in for the optional arguments that can be included
      • Required Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
        3. iResourceID: the ID# of the map resource
      • Optional Arguments:
        • Optional Arguments can be included in any order after the three required arguments
        • Optional Arguments must be stated as text strings, as in "Owned" instead of Owned
        • Any number of optional arguments can be supplied, and any of the four possible arguments can be omitted
        • When all four optional arguments are omitted, the code treats this the same as all four being stated
        • If any of the optional arguments is included, then only those which are included will be considered (ie, the ones not included would be treated as if they were to defaulting to "false" for whether they should be implemented)
        The allowed optional arguments are:
        1. "Owned": the player must own the plot
        2. "Unpillaged": the plot must not be pillaged
        3. "ResourceIsRevealed": the player has to have the tech that reveals the resource
        4. "Worked": the city has to be working the plot
      • Returned Data:
        an integer value for the number of plots that conform to the restrictions stated in the arguments​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        local iResourceID = GameInfoTypes.RESOURCE_WHEAT
        local iNumFarmedWheat = GetNumCityImprovementPlotsWithResource(tNearbyDatas, iImprovementID, iResourceID, "Worked", "Unpillaged")
        if iNumFarmedWheat > 0 then
        	print("We have " .. iNumFarmedWheat .. " near the city of " .. pCity:GetName() .. " that we are actually working")
        	print("HURRAH!!! We can make bread. That is, if we have figured out how to make ovens.")
        end
        or as:​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iNumFarmedWheat = GetNumCityImprovementPlotsWithResource(tNearbyDatas, GameInfoTypes.IMPROVEMENT_FARM, GameInfoTypes.RESOURCE_WHEAT, "Unpillaged", "Worked" )
        if iNumFarmedWheat > 0 then
        	print("We have " .. iNumFarmedWheat .. " near the city of " .. pCity:GetName() .. " that we are actually working")
        	print("HURRAH!!! We can make bread. That is, if we have figured out how to make ovens.")
        end
    2. This function does not check whether the player can use any of the resources or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function GetCityImprovementPlotswithResource():
    1. Details: returns an lua table of plots with the specified improvement that also have the specified resource, where the "values" within this table are plot objects that conformed to the restrictions specified
      • Calling Format:
        • GetCityImprovementPlotswithResource(tTable, iImprovementID, iResourceID, ...)
        • The "..." argument is a stand-in for the optional arguments that can be included
      • Required Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
        3. iResourceID: the ID# of the map resource
      • Optional Arguments:
        • Optional Arguments can be included in any order after the three required arguments
        • Optional Arguments must be stated as text strings, as in "Owned" instead of Owned
        • Any number of optional arguments can be supplied, and any of the four possible arguments can be omitted
        • When all four optional arguments are omitted, the code treats this the same as all four being stated
        • If any of the optional arguments is included, then only those which are included will be considered (ie, the ones not included would be treated as if they were to defaulting to "false" for whether they should be implemented)
        The allowed optional arguments are:
        1. "Owned": the player must own the plot
        2. "Unpillaged": the plot must not be pillaged
        3. "ResourceIsRevealed": the player has to have the tech that reveals the resource
        4. "Worked": the city has to be working the plot
      • Returned Data:
        an lua table of plots that conform to the restrictions stated in the arguments​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        local iResourceID = GameInfoTypes.RESOURCE_WHEAT
        local tFarmedWheat = GetCityImprovementPlotswithResource(tNearbyDatas, iImprovementID, iResourceID, "Worked", "Unpillaged")
        for k,pPlot in pairs(tFarmedWheat) do
        	print("We have a farmed Wheat tile near the city of " .. pCity:GetName() .. " that we are actually working and which is not pillaged located at X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
        	print("HURRAH!!! We can make bread. That is, if we have figured out how to make ovens.")
        end
        or as:​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local tFarmedWheat = GetCityImprovementPlotswithResource(tNearbyDatas, GameInfoTypes.IMPROVEMENT_FARM, GameInfoTypes.RESOURCE_WHEAT, "Unpillaged", "Worked" )
        for k,pPlot in pairs(tFarmedWheat) do
        	print("We have a farmed Wheat tile near the city of " .. pCity:GetName() .. " that we are actually working and which is not pillaged located at X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
        	print("HURRAH!!! We can make bread. That is, if we have figured out how to make ovens.")
        end
    2. This function does not check whether the player can use any of the resources or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:
     
  3. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    Function GetImprovementPlotsWithResources():
    1. Details:

      • Returns a list of plots for the specified improvement.
      • If the improvement is not present near the city the returned table will be an empty table.
      • The routine only collects all plots with the specified improvement that also have a resource.
      • It does not sort out whether the resource is correct for the improvement
    2. When using this function, you will have to write additional code to scan through the list of plots returned and sort out such issues as whether the plot belongs to the city-owner, whether the plot is being worked by the city, etc.
      • Calling Format:
        GetImprovementPlotsWithResources(tTable, iImprovementID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iImprovementID: the ID# of the terrain improvement
      • Returned Data:
        an lua table with all plots having both the specified Improvement and a Resource, and where the "values" in the table are the plot objects for all qualifying plots found.​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
        local tFarmedPlots = GetImprovementPlotsWithResources(tNearbyDatas, iImprovementID)
        for Item,pPlot in pairs(tFarmedPlots) do
        	print("We Have a plot with a Farm on it! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
        	print("The plot has a resource but it might not be a correct resource for a farm")
        end
    3. To get a list of plots with the same improvement and which have a correct map resource for that improvement, instead of this function, use function GetCityImprovementPlots as so:
      Code:
      local tNearbyDatas = GetCityMapDatas(pCity)
      local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
      local tFarmedPlots = GetCityImprovementPlots(tNearbyDatas, iImprovementID, "PlotsWithCorrectResources")
      for Item,pPlot in pairs(tFarmedPlots) do
      	print("We Have a plot with a Farm on it! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
      end
      In this particular example only farm plots with wheat on them would be returned
    4. To get a list of plots with the same improvement and which have an incorrect map resource for that improvement, instead of this function, use function GetCityImprovementPlots as so:
      Code:
      local tNearbyDatas = GetCityMapDatas(pCity)
      local iImprovementID = GameInfoTypes.IMPROVEMENT_FARM
      local tFarmedPlots = GetCityImprovementPlots(tNearbyDatas, iImprovementID, "PlotsWithInCorrectResources")
      for Item,pPlot in pairs(tFarmedPlots) do
      	print("We Have a plot with a Farm on it! The plot's location is X" .. pPlot:GetX() .. ", Y" ..  pPlot:GetY())
      end
      For example, plots near the city that have the iron resource and a farm on them would be returned to you. Plots with other similar "Farms + Bad Resources for Farms" conditions would also be included.
    5. This function does not check whether the player can use any of the resources or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function DoesResourceExistNearCity():
    1. Details: returns true/false for whether a the specified resource exists on the map near the city
      • Calling Format:
        DoesResourceExistNearCity(tTable, iResourceID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iResourceID: the ID# of the map resource
      • Returned Data:
        returns true/false for whether a the specified resource exists on the map near the city​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iCoalID = GameInfoTypes.RESOURCE_COAL
        if DoesResourceExistNearCity(tNearbyDatas, iCoalID) then
        	print("The City of " .. pCity:GetName() .. " has Coal nearby")
        end
    2. This function does not check whether the player can use the specified resource or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function CityIsWorkingResourcePlots():
    1. Details: returns true/false for whether a city is working a plot with the specified resource
      • Calling Format:
        CityIsWorkingResourcePlots(tTable, iResourceID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iResourceID: the ID# of the map resource
      • Returned Data:
        returns true/false for whether a city is working a plot with the specified resource​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iCoalID = GameInfoTypes.RESOURCE_COAL
        if CityIsWorkingResourcePlots(tNearbyDatas, iCoalID) then
        	print("The City of " .. pCity:GetName() .. " is working a coal plot")
        end
    2. This function does not check whether the player can use the specified resource or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function GetNumCityWorkingResourcePlots():
    1. Details: returns the integer number of plots with the specified resource that the city is working
      • Calling Format:
        GetNumCityWorkingResourcePlots(tTable, iResourceID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iResourceID: the ID# of the map resource
      • Returned Data:
        returns the integer number of plots with the specified resource that the city is working​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iCoalID = GameInfoTypes.RESOURCE_COAL
        if GetNumCityWorkingResourcePlots(tNearbyDatas, iCoalID) > 0 then
        	print("The City of " .. pCity:GetName() .. " is working a coal plot")
        end
    2. This function does not check whether the player can use the specified resource or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function GetNumCityUnclaimedResourcePlots():
    1. Details: returns the integer number of unclaimed (unowned) plots with the specified resource that are within the city's working range
      • Calling Format:
        GetNumCityUnclaimedResourcePlots(tTable, iResourceID)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. iResourceID: the ID# of the map resource
      • Returned Data:
        returns the integer number of unclaimed (unowned) plots with the specified resource that are within the city's working range​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iCoalID = GameInfoTypes.RESOURCE_COAL
        if GetNumCityUnclaimedResourcePlots(tNearbyDatas, iCoalID) > 0 then
        	print("The City of " .. pCity:GetName() .. " has Coal unclaimed plots with Coal on them nearby: perhaps we should make this lua-script grab some of these plots")
        end
    2. This function does not check whether the player can use the specified resource or not. (ie, whether the player has the tech that reveals the resource)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function EnemyUnitsAreNearCity():
    1. Details: returns true/false for whether Enemy, Adversary, or Barbarian units are near the city
      • Calling Format:
        EnemyUnitsAreNearCity(tTable, sEnemyType, sMustBeVisible)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. sEnemyType: sEnemyType is given as a text string and must be one of:
          • "Barbarians": makes the code only look for Barbarian units
          • "Players": makes the code only look for units of enemy non-barbarian players the city owner is at war with, whether belonging to City-States or Full Major Civilizations.
          • "Any": makes the code look for any enemy unit, "Barbarians" or "Players"
        3. sMustBeVisible: sMustBeVisible is given as a text string and must be either of:
          • "MustBeVisible": makes the code only look enemy units the city owner can see
          • otherwise omit the argument entirely
      • Returned Data:
        returns boolean true/false​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        if EnemyUnitsAreNearCity(tNearbyDatas, "Barbarians", "MustBeVisible") then
        	print("Barbarians Detected Near the City of " .. pCity:GetName .. "!")
        end
        or as​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        if EnemyUnitsAreNearCity(tNearbyDatas, "Players", "MustBeVisible") then
        	print("The City of " .. pCity:GetName .. " is under attack!")
        end
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function CountPlayerUnitsNearCity():
    1. Details:
      • It will return the number of units belonging to the city owner and that are within the 3-tile-range of the city
      • You can designate whether to count only combat or only civilian units
      • You can designate whether to count only units of certain domains
      • Calling Format:
        • CountPlayerUnitsNearCity(tTable, ...)
        • The "..." argument is a stand-in for the optional arguments that can be included
      • Required Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
      • Optional Arguments:

        • Optional Arguments can be included in any order after the required table reference
        • Optional Arguments must be stated as text strings, as in "Combat" instead of Combat
        • Any number of optional arguments can be supplied, and any of them can be omitted
        • When all optional arguments are omitted, the code treats this the same as all being stated
        • If any of the optional arguments is included, then only those which are included will be considered (ie, the ones not included would be treated as if they were to defaulting to "false" for whether they should be implemented)
        The allowed optional arguments are:
        1. "Combat": Combat units should be counted
        2. "Civilian": Civilian units should be counted
        3. "Land": Land units (ie, DOMAIN_LAND) should be counted
        4. "Sea": Sea units (ie, DOMAIN_SEA) should be counted
        5. "Air": Air units (ie, DOMAIN_AIR) should be counted
        6. "Hover": Hover units (ie, DOMAIN_HOVER) should be counted
        7. "Immobile": Immobile units (ie, DOMAIN_IMMOBILE) should be counted
      • Returned Data:
        the integer number of units qualifying units belonging to the city owner and which are within the 3-tile radius of the city​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iUnitsNearCity = CountPlayerUnitsNearCity(tNearbyDatas)
        print("Total number of our units in or near the city of " .. pCity:GetName() .. " is " .. iUnitsNearCity)
        print("This Counts any of our units, Civilian or Combat, of Whatever Domain")
        or as​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iCombatNavalUnitsNearCity = CountPlayerUnitsNearCity(tNearbyDatas, "Combat", "Sea")
        print("Total number of our Naval Combat units in or near the city of " .. pCity:GetName() .. " is " .. iCombatNavalUnitsNearCity)
        or as​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local iCombatLandUnitsNearCity = CountPlayerUnitsNearCity(tNearbyDatas, "Combat", "Land")
        print("Total number of our Land Combat units in or near the city of " .. pCity:GetName() .. " is " .. iCombatLandUnitsNearCity)
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function GetPlayerUnitsNearCity():
    1. Details:
      • It will return an lua table of units belonging to the city owner and that are within the 3-tile-range of the city
      • The unit objects for these units will be the "values" in the returned table
      • You can designate whether to count only combat or only civilian units
      • You can designate whether to count only units of certain domains
      • Calling Format:
        • GetPlayerUnitsNearCity(tTable, ...)
        • The "..." argument is a stand-in for the optional arguments that can be included
      • Required Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
      • Optional Arguments:

        • Optional Arguments can be included in any order after the required table reference
        • Optional Arguments must be stated as text strings, as in "Combat" instead of Combat
        • Any number of optional arguments can be supplied, and any of them can be omitted
        • When all optional arguments are omitted, the code treats this the same as all being stated
        • If any of the optional arguments is included, then only those which are included will be considered (ie, the ones not included would be treated as if they were to defaulting to "false" for whether they should be implemented)
        The allowed optional arguments are:
        1. "Combat": Combat units should be counted
        2. "Civilian": Civilian units should be counted
        3. "Land": Land units (ie, DOMAIN_LAND) should be counted
        4. "Sea": Sea units (ie, DOMAIN_SEA) should be counted
        5. "Air": Air units (ie, DOMAIN_AIR) should be counted
        6. "Hover": Hover units (ie, DOMAIN_HOVER) should be counted
        7. "Immobile": Immobile units (ie, DOMAIN_IMMOBILE) should be counted
      • Returned Data:
        an lua table of qualifying units belonging to the city owner and which are within the 3-tile radius of the city​
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local tUnitsNearCity = GetPlayerUnitsNearCity(tNearbyDatas)
        if #tUnitsNearCity > 0 then
        	print("Total number of our units in or near the city of " .. pCity:GetName() .. " is " .. #tUnitsNearCity)
        	print("This Counts any of our units, Civilian or Combat, of Whatever Domain")
        	for k,pUnit in pairs(tUnitsNearCity) do
        		if pUnit:IsCombatUnit() then
        			print("There is a combat unit at X" .. pUnit:GetX() .. ", Y" .. pUnit:GetY())
        		else
        			print("There is a civilian unit at X" .. pUnit:GetX() .. ", Y" .. pUnit:GetY())
        		end
        	end
        end
        or as​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local tCombatNavalUnitsNearCity = GetPlayerUnitsNearCity(tNearbyDatas, "Combat", "Sea")
        if #tCombatNavalUnitsNearCity > 0 then
        	print("Total number of our Naval Combat units in or near the city of " .. pCity:GetName() .. " is " .. #tCombatNavalUnitsNearCity)
        	for k,pUnit in pairs(tCombatNavalUnitsNearCity) do
        		print("There is a Naval Combat unit at X" .. pUnit:GetX() .. ", Y" .. pUnit:GetY())
        	end
        end
        or as​
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local tCombatLandUnitsNearCity = GetPlayerUnitsNearCity(tNearbyDatas, "Combat", "Land")
        if #tCombatLandUnitsNearCity > 0 then
        	print("Total number of our Land Combat units in or near the city of " .. pCity:GetName() .. " is " .. #tCombatLandUnitsNearCity)
        	for k,pUnit in pairs(tCombatLandUnitsNearCity) do
        		print("There is a Land Combat unit at X" .. pUnit:GetX() .. ", Y" .. pUnit:GetY())
        	end
        end
    Return to Utility Description:
    Return to top of "Interface" Routines:


    Function AreEnemyUnitsOfTypeNearby():
    1. Details:
      • Depending on arguments passed, returns either:
        • boolean true/false for whether enemy or adversary units of the specified type(s) are near the city
        • an lua table of all the unit objects conforming to the arguments passed
      • The function looks for units of a specified type or unit-class
      • the function can be made to generate a player notification if the city owner is human, showing the location of each discovered unit of an enemy or adversary near the city. The Great Person Born notification is used since the Nearby Enemy Unit notification did not seem to work.
      • Calling Format:
        AreEnemyUnitsOfTypeNearby(tTable, sReturnMethod, sUnitType, sEnemyType, sMustBeVisible, sMakeNotification, sNotifyOnce)​
      • Arguments:
        1. tTable: the tablename of city data previously collected from GetCityMapDatas()
        2. sReturnMethod: specifies the type of return to be made and must be either "boolean" or "table". When you do not want the code to return boolean true/false, you state any text string that is not "boolean" (see the usage example)
        3. sUnitType: specifies the type of unit to be looked for
          • This can be either a reference to a Unit-Type or a Unit-Class Type
          • This must be stated as a text string as in "UNIT_SWORDSMAN" but not UNIT_SWORDSMAN
          • If a unit-class such as "UNITCLASS_SWORDSMAN" is stated, then all units from within that class of units will be looked for.
        4. sEnemyType: sEnemyType is given as a text string and must be one of:
          • "Enemy": makes the code only look for units of enemy non-barbarian players the city owner is at war with, whether belonging to City-States or Full Major Civilizations.
          • "Any": makes the code look for any unit belonging to a city-state or major player
        5. sMustBeVisible: sMustBeVisible is given as a text string and must be either of:
          • "MustBeVisible": makes the code only look for units the city owner can see
          • "CanBeInvisible": makes the code look for all such units near the city
        6. sMakeNotification:
          • "MakeNotification": makes the code generate notifications to the human player if they are the city owner
          • "DoNotMakeNotification": the code will not make notifications
        7. sNotifyOnce:
          • "NotifyOnce": the code will only make one notification regardless of how many units are found
          • "NotifyMoreThanOnce": the code will make one notification for each unit encountered
      • Returned Data:
        boolean true/false or an lua table depending on the argument passed for sReturnMethod
      • Example Usage:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        local tEnemyTriremes = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "return cheeseburgers", "UNITCLASS_TRIREME", "Enemy", "CanBeInvisible", "MakeNotification", "NotifyMoreThanOnce")
        if #tEnemyTriremes > 0 then
        	for k,pUnit in pairs(tEnemyTriremes) do
        		print("Enemy Trireme was detected at X" .. pUnit:GetX() .. ", Y" ..  pUnit:GetY() .. " near the city of " .. pCity:GetName())
        		pUnit:ChangeDamage(20)
        	end
        end
    2. When notifications are made, they are of the type that will redirect the game 'camera' to the associated map tile when the human player clicks the notification
    Return to Utility Description:
    Return to top of "Interface" Routines:
     
  4. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    • The following are general toolkit functions that are included as part of the utility.
    • You can copy these from here and use in your mods as needed and as you find useful, but you do not need to copy the code of these routines into the code for any lua file which is using the utility

    Function ItemIsInTable():
    1. Details: returns true/false as to whether 'DataItem' is a "value" in 'tTable'
      this function is only useful when the k,v pairs of 'tTable' are in an array format of k=integer,v=data​
      • Calling Format:
        ItemIsInTable(tTable,DataItem)​
      • Arguments:
        1. tTable:
          The lua table which might or might not have the item listed as a "value"​
        2. DataItem:
          The data item you are looking for. This can be text, an integer value, or a pointer object such as a unit object (ie, a pUnit)​
      • Returned Data:
        boolean true/false​
      • Example Usage:
        Code:
        --------------------------------------------------------------
        -- ItemIsInTable
        -- Author: LeeS
        -- returns true/false as to whether 'DataItem' is a "value" in 'tTable'
        -- this function is only useful when the k,v pairs are in an array format of k=integer,v=data
        --------------------------------------------------------------
        function ItemIsInTable(tTable,DataItem)
        	for iTableItem,TableDataValue in pairs(tTable) do
        		if TableDataValue == DataItem then
        			return true
        		end
        	end
        	return false
        end
        
        local tTableOfSomeNumbers = { 1, 7, 18, 34, 97, 6, 4 }
        local iRandom = math.random(100)
        if ItemIsInTable(tTableOfSomeNumbers,iRandom) then
        	print(iRandom .. " is a 'v' in the table called tTableOfSomeNumbers")
        else
        	print(iRandom .. " was not found anywhere in the table called tTableOfSomeNumbers")
        end
    2. Complete code of the routine:
      Code:
      --------------------------------------------------------------
      -- ItemIsInTable
      -- Author: LeeS
      -- returns true/false as to whether 'DataItem' is a "value" in 'tTable'
      -- this function is only useful when the k,v pairs are in an array format of k=integer,v=data
      --------------------------------------------------------------
      function ItemIsInTable(tTable,DataItem)
      	for iTableItem,TableDataValue in pairs(tTable) do
      		if TableDataValue == DataItem then
      			return true
      		end
      	end
      	return false
      end
    Return to Utility Description:
    Return to top of Generic Toolkit Functions:


    Function GetUnitIDSThatMatch():
    1. Details: Returns an lua table with all unit ID'S and Text descriptions for units which match to the passed argument "sUnitType".
      • Calling Format:
        GetUnitIDSThatMatch(sUnitType)​
      • Arguments:
        1. sUnitType: This must be a text string and must be either a Unit-Class or a Unit-Type, as in "UNITCLASS_SWORDSMAN" or "UNIT_SWORDSMAN"
      • Returned Data:

        • An lua table where the keys in the table will be the ID #s of all Units that match to the specified "sUnitType" argument, and the "values" within the table will be the localized text of each key's unit-description.
        • If a Unit-Class is specified for the "sUnitType" argument then all units within that class will be added to the table that is returned by the function.
      • Example Usages:
        • To place all ID #'s and descriptions for the Swordsman Class of Units into the resulting table called "tUnitsToConsider"
          Code:
          local tUnitsToConsider = GetUnitIDSThatMatch("UNITCLASS_SWORDSMAN")
        • To place all ID # and description for the Swordsman Unit into the resulting table called "tUnitsToConsider". There would only be one k,v pair of data in the resulting lua table.
          Code:
          local tUnitsToConsider = GetUnitIDSThatMatch("UNIT_SWORDSMAN")
    2. Complete code of the routine:
      Spoiler :
      Code:
      -----------------------------------------------------------------------------------------------------------------------------------------------
      function GetUnitIDSThatMatch(sUnitType)
      	local sDesignationMethod = "NONE"
      	local tUnitsToConsider = {}
      	if sUnitType ~= nil then
      		for row in GameInfo.UnitClasses("Type='" .. sUnitType .. "'") do
      			sDesignationMethod = "Class"
      		end
      		if sDesignationMethod == "NONE" then
      			for row in GameInfo.Units("Type='" .. sUnitType .. "'") do
      				tUnitsToConsider[row.ID] = Locale.ConvertTextKey(row.Description)
      				sDesignationMethod = "Unit"
      			end
      		elseif sDesignationMethod == "Class" then
      			for row in GameInfo.Units("Class='" .. sUnitType .. "'") do
      				tUnitsToConsider[row.ID] = Locale.ConvertTextKey(row.Description)
      			end
      		end
      	end
      	if sDesignationMethod == "NONE" then
      		local sLongMessage = "AreEnemyUnitsOfTypeNearby: No matching Unit or Unit-Class Was found anywhere within the game's Database to: [COLOR_NEGATIVE_TEXT] " .. sUnitType .. " [ENDCOLOR]"
      		local sShortMessage = "[COLOR_NEGATIVE_TEXT]Invalid Data for AreEnemyUnitsOfTypeNearby[ENDCOLOR]"
      		Players[0]:AddNotification(NotificationTypes["NOTIFICATION_GENERIC"], sShortMessage .. "[NEWLINE][NEWLINE]" .. sLongMessage, sShortMessage)
      		print(sLongMessage)
      		print(" ")
      		print(" ")
      	end
      	return tUnitsToConsider
      end
    3. If you load garbage information for the argument "sUnitType" then an in-game notification will be generated advising you of this, and the return information from this routine will be an empty lua table. Garbage information is defined as the value passed for argument "sUnitType" does not exist as a <Type> within xml-table <UnitClasses> or xml-table <Units>. Spellings are critical, as are usage of " to designate that the passed info is a text string, as is whether or not another mod has eliminated the Unit or UnitClass from the game's database.
    Return to Utility Description:
    Return to top of Generic Toolkit Functions:


    Function IsResourceRevealed():
    1. Details: returns boolean true/false for whether the player has the required tech to reveal a map resource
      • lua does not pay attention to whether the player has the correct tech for the resource when Plot:GetResourceType() is invoked unless the eTeam argument to Plot:GetResourceType() is used. When a plot object is not available, or when a "do not proceed with code" filter is needed before the plot objects are available, a routine like this is needed.
      • Calling Format:
        IsResourceRevealed(iPlayer, iResourceID)​
      • Arguments:
        1. iPlayer: the player's player ID# from the active in-game list of players
        2. iResourceID: the ID # of the map resource
      • Returned Data:
        boolean true or false​
      • Example Usage:
        Code:
        function IsResourceRevealed(iPlayer, iResourceID)
        	local iRequiredTech = GameInfoTypes[GameInfo.Resources[iResourceID].TechReveal]
        	if (iRequiredTech ~= nil) and (iRequiredTech ~= -1) then
        		local pPlayer = Players[iPlayer]
        		local iTeam = pPlayer:GetTeam()
        		local pTeam = Teams[iTeam]
        		return pTeam:IsHasTech(iRequiredTech)
        	end
        	return true
        end
        
        local iIron = GameInfoTypes.RESOURCE_IRON
        local iOurPlayer = 0
        if IsResourceRevealed(iOurPlayer, iIron) then
        	print("Our Player can see Iron on the map")
        else
        	print("Our Player is ignorant of the resource called 'Iron'")
        end
    2. Complete code of the routine:
      Code:
      function IsResourceRevealed(iPlayer, iResourceID)
      	local iRequiredTech = GameInfoTypes[GameInfo.Resources[iResourceID].TechReveal]
      	if (iRequiredTech ~= nil) and (iRequiredTech ~= -1) then
      		local pPlayer = Players[iPlayer]
      		local iTeam = pPlayer:GetTeam()
      		local pTeam = Teams[iTeam]
      		return pTeam:IsHasTech(iRequiredTech)
      	end
      	return true
      end
    Return to Utility Description:
    Return to top of Generic Toolkit Functions:

    Function GetCityMapDatas(pCity): Detailed Information About Table Structuring for Advanced Users of Lua-Tables:
    1. Special Data Treatments:
      • The city object for the city that was originally passed to function GetCityMapDatas(pCity) will always be recorded within the resulting table of nearby map data as tNearbyDatas.OurCity
    2. For Map Plot Information, Data is only collected about plots that adhere to the following rules:
      • only plots that have a map resource, or a terrain improvement, or a unit, or a city are included
      • are not being worked by some other city (whether of the same player or a different player)
      • are not owned by some other player (except that some limited data is gathered about cities)
    3. City Plot Map Resource Information about a resource that may be on the same plot as the city passed to the function is given in special details of the lua table. If there is a resource on the same tile as the city, this information will be contained in the following keys (assuming you stored the info on the city's resources into a table called "tNearbyDatas"):
      KeyName Value Description
      tNearbyDatas.CityResource The resource ID#
      tNearbyDatas.CityResourceIsRevealed Boolean for whether the player can "see" the resource
      tNearbyDatas.CityResourceQty The resource "Qty" on the city plot (for use with Strat. Resources: it is 0 otherwise)
      tNearbyDatas.CityResourceIsLuxury Boolean for whether the resource is a Luxury Resource
      tNearbyDatas.CityResourceIsBonus Boolean for whether the resource is a Bonus Resource
      tNearbyDatas.CityResourceIsStrategic Boolean for whether the resource is a Strategic Resource
      • Note that if there is no resource on the same plot as the city occupies, all of these would be "nil"
      • All information for the plot the city is on is also rolled into the main counts for the Resource so that the data for the Resource on the same plot as the city occupies reflects not only the city plot itself but also all plots near the city that have the same map resource. See info on map resources below.
    4. Data about all map resources near the city is collected into a subtable called "Resources", so if you stored the information gathered from the function for pCity into a local table called "tNearbyDatas", all the information about map resources near the city will be placed into a table accessed by "tNearbyDatas.Resources"
      • This table "tNearbyDatas.Resources" is organized so that all its "keys" are integer ID#s of resources actually near the city, and the "values" are themselves sub-tables holding the information about that individual type of resource.
      • The information for any resource that may be on the City plot is also rolled into the various counts and other data collected for this individual resource.
      • If a particular resource is not within range of the city, or cannot be counted because of the rules outlined previously, then that resource's ID # will not be present as a "key" within table "tNearbyDatas.Resources"
      • For each resource found near the city, the following "Sub-Keys" are created and data is collected within the proper "Sub-Keys" for that resource:
        Spoiler Resource Datas for Each Resource Found :
        Key-name Value-Type Default Explanation
        Type = text The recource's "Type" info from table <Resources>
        Class = text The recource's "ResourceClassType" info from table <Resources>
        Description = text The recource's localized "Description" TXT_KEY tag as specified table <Resources>
        NumPlotsCounted = int 0 number plots with the resource except those in unclaimed territory
        NumPlotsWorkedByCity = int 0 number plots with the resource that are being worked by the city
        NumPlotsWorkedByCityThatAreNotPillaged = int 0 number plots with the resource that are being worked by the city and which are not pillaged
        NumPlotsWorkedByCityThatArePillaged = int 0 number plots with the resource that are being worked by the city and which are pillaged
        NumPillagedPlots = int 0 number plots with the resource that are pillaged
        NumUnclaimedPlots = int 0 number plots with the resource that are in unowned territory
        NumPlotsWithIncorrectImprovement = int 0 number plots with the resource that have an incorrect improvement for the resource
        NumPlotsWithNoImprovement = int 0 number plots with the resource that have no improvmeent of any kind
        ResourceAmount = int 0 total strategic resource amount for this resource from plots either being worked by the city or which are owned by the same player.
        CityResourceAmount = int 0 strategic resource amount for this resource from the city plot itself or from plots being worked by city and which have a correct improvement type.
        UnClaimedResourceAmounts = int 0 total strategic resource amount for this resource near this city in unowned territory
        PillagedResourceAmounts = int 0 total strategic resource amount for this resource that are on pillaged plots
        Plots = {}* an lua array-table with all plots having the resource recorded as "Values", except those in unclaimed territory
        WorkedPlots = {}* an lua array-table with all Worked plots having the resource recorded as "Values"
        WorkedPlotsThatAreNotPillaged = {}* an lua array-table with all Unpillaged Worked plots having the resource recorded as "Values"
        WorkedPlotsThatArePillaged = {}* an lua array-table with all Pillaged Worked plots having the resource recorded as "Values"
        WorkedPlotsWithIncorrectImprovement = {}* an lua array-table with all Worked plots having the resource but with an incorrect improvement for that resource recorded as "Values"
        WorkedPlotsWithNoImprovement = {}* an lua array-table with all Worked plots having the resource but no terrain improvemnent recorded as "Values"
        PillagedPlots = {}* an lua array-table with all Pillaged plots having the resource recorded as "Values"
        UnclaimedPlots = {}* an lua array-table with all Unowned plots having the resource recorded as "Values"
        ResourceIsRevealed = bool true/false as to whether the player has the tech that reveals the resource
        ResourceIsLuxury = bool true/false as to whether the resource is a luxury
        ResourceIsBonus = bool true/false as to whether the resource is a bonus
        ResourceIsStrategic = bool true/false as to whether the resource is a strategic
        ResourceIsArtifact = bool true/false as to whether the resource is either an Artifact or Hidden Artifact
        ResourceArtifactType = text "Not An Atrifact Or Hidden Artifact", "artifact", "hidden artifact"

        * in all these tables, 'pPlot' is recorded as the 'v' within the table so you can iterate through the table like as:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        if tNearbyDatas.Resources[GameInfoTypes.RESOURCE_WHEAT] and (#NearbyResources.Resources[GameInfoTypes.RESOURCE_WHEAT].WorkedPlotsWithIncorrectImprovement > 0) then
        	for k,v in pairs(tNearbyDatas.Resources[GameInfoTypes.RESOURCE_WHEAT].WorkedPlotsWithIncorrectImprovement) do
        		if v:IsImprovementPillaged() then
        			print("There is a pillaged wheat tile at " .. v:GetX() .. "," .. v:GetY() .. " that is being worked by the city and that has something else than a farm on it")
        		end
        	end
        end
    5. Data about all Units near the city is collected into a subtable called "Units", so if you stored the information gathered from the function for pCity into a local table called "tNearbyDatas", all the information about map resources near the city will be placed into a table accessed by "tNearbyDatas.Units"
      • Data collected is done on a Unit-Owner basis and not on a Unit-Type or Unit-Class Basis.
      • For units found near the city, the following "Sub-Keys" are created and data is collected within the proper "Sub-Keys" for the relevant sorts of nearby units:
        Spoiler Unit Datas Found :
        Key-name Value-Type Default Explanation
        PlayerUnitCount = int 0 number units near the city that belong to the city owner
        BarbarianCount = int 0 number units near the city that the city owner can see and that are barbarians
        EnemyCount = int 0 number units near the city that the city owner can see and that belong to enemy (at war) players
        AdversaryCount = int 0 number units near the city that the city owner can see and that belong to neutral players
        BarbarianInvisibleCount = int 0 number units near the city that the city owner cannot see and that are barbarians
        EnemyInvisibleCount = int 0 number units near the city that the city owner cannot see and that belong to enemy (at war) players
        AdversaryInvisibleCount = int 0 number units near the city that the city owner cannot see and that belong to neutral players
        PlayerUnits = {}* an lua array-table with units near the city belonging to the player recorded as "Values"
        BarbarianUnits = {}* an lua array-table with units near the city that the city owner can see and that are barbarians recorded as "Values"
        EnemyUnits = {}* an lua array-table with units near the city that the city owner can see and that belong to enemy (at war) players recorded as "Values"
        AdversaryUnits = {}* an lua array-table with units near the city that the city owner can see and that belong to neutral players recorded as "Values"
        BarbarianInvisibleUnits = {}* an lua array-table with units near the city that the city owner cannot see and that are barbarians recorded as "Values"
        EnemyInvisibleUnits = {}* an lua array-table with units near the city that the city owner cannot see and that belong to enemy (at war) players recorded as "Values"
        AdversaryInvisibleUnits = {}* an lua array-table with units near the city that the city owner cannot see and that belong to neutral players recorded as "Values"

        * in all these tables, 'pUnit' is recorded as the 'v' within the table so you can iterate through the table like as:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        if tNearbyDatas.Units.BarbarianCount > 0 then
        	for k,v in pairs(tNearbyDatas.Units.BarbarianUnits) do
        		if v:GetPlot():GetOwner() == iPlayer then
        			print("The Barb Unit at " .. v:GetX() .. "," .. v:GetY() .. " is in our territory!")
        			v:ChangeDamage(10)
        		end
        	end
        end
    6. Data about all map Terrain Improvements near the city is collected into a subtable called "Improvements", so if you stored the information gathered from the function for pCity into a local table called "tNearbyDatas", all the information about map resources near the city will be placed into a table accessed by "tNearbyDatas.Improvements"
      • This table "tNearbyDatas.Improvements" is organized so that all its "keys" are integer ID#s of improvements actually near the city, and the "values" are themselves sub-tables holding the information about that individual type of improvement.
      • The information for any improvement that may be on the City plot is also rolled into the various counts and other data collected for this individual improvement.
      • If a particular improvement is not within range of the city, or cannot be counted because of the rules outlined previously, then that improvement's ID # will not be present as a "key" within table "tNearbyDatas.improvements"
      • For each improvement found near the city, the following "Sub-Keys" are created and data is collected within the proper "Sub-Keys" for that improvement:
        Spoiler improvement Datas for Each improvement Found :
        Key-name Value-Type Default Explanation
        Type = text The improvement's "Type" info from table <Improvements>
        Description = text The improvement's localized "Description" TXT_KEY tag as specified table <Improvements>
        NumPlotsCounted = int 0 number plots with the improvement except those in unclaimed territory
        NumPlotsWorkedByCity = int 0 number plots with the improvement that are being worked by the city
        NumPlotsWorkedByCityThatAreNotPillaged = int 0 number plots with the improvement that are being worked by the city and which are not pillaged
        NumPlotsWorkedByCityThatArePillaged = int 0 number plots with the improvement that are being worked by the city and which are pillaged
        NumPillagedPlots = int 0 number plots with the improvement that are pillaged
        NumUnclaimedPlots = int 0 number plots with the improvement that are in unowned territory
        NumPlotsWithIncorrectImprovementForResource = int 0 number plots with the improvement that have a map resource incorrect for the improvement1
        Plots = {}* an lua array-table with all plots having the improvement recorded as "Values", except those in unclaimed territory
        WorkedPlots = {}* an lua array-table with all Worked plots having the improvement recorded as "Values"
        WorkedPlotsThatAreNotPillaged = {}* an lua array-table with all Unpillaged Worked plots having the improvement recorded as "Values"
        WorkedPlotsThatArePillaged = {}* an lua array-table with all Pillaged Worked plots having the improvement recorded as "Values"
        WorkedPlotsWithIncorrectImprovementForResource = {}* an lua array-table with all Worked plots having the improvement but with an incorrect map resource for that improvement recorded as "Values"
        UnWorkedPlotsWithIncorrectImprovementForResource = {}* an lua array-table with all Unworked plots having the improvement but with an incorrect map resource for that improvement recorded as "Values"3
        PillagedPlots = {}* an lua array-table with all Pillaged plots having the improvement recorded as "Values"2
        UnclaimedPlots = {}* an lua array-table with all Unowned plots having the improvement recorded as "Values"
        PlotsWithCorrectResources = {}* an lua array-table with all plots having the improvement and map resources that are correct for the improvement recorded as "Values"2
        PlotsWithInCorrectResources = {}* an lua array-table with all plots having the improvement and map resources that are incorrect for the improvement recorded as "Values"2

        1 such as a Farm on a plot with Iron.
        2 plots that are owned by the city owner and unowned plots within the city's working range are counted
        3 only plots that are owned by the city owner are included
    7. Data about all other cities near the city is collected into a subtable called "OtherCities", so if you stored the information gathered from the function for pCity into a local table called "tNearbyDatas", all the information about other cities near the city will be placed into a table accessed by "tNearbyDatas.OtherCities"
      • Data on other cities within the 3-tile working range of the original city passed to the GetCityMapDatas(pCity) function is only collected as:
        Code:
        if pPlot:IsCity() and (pPlot:GetPlotCity() ~= pCity) then
        	local pOtherCity = pPlot:GetPlotCity()
        	table.insert(tNearbyDatas.OtherCities.Cities, {CityObject=pOtherCity, CityName=pOtherCity:GetName()})
        end
        and
        Code:
        table.insert(tNearbyDatas.OtherCities.CitiesWithResources, {CityObject=pOtherCity, CityName=pOtherCity:GetName(), CityResource=iResourceID, CityResourceIsStrategic=bResourceIsStrategic, CityResourceQty = (bResourceIsStrategic and pPlot:GetNumResource() or 0)})
      • No Other data is collected.
      • You will need to extract this information on other cities as like:
        Code:
        local tNearbyDatas = GetCityMapDatas(pCity)
        if #tNearbyDatas.OtherCities.Cities > 0 then
        	if #tNearbyDatas.OtherCities.CitiesWithResources > 0 then
        		for k,DataTable in pairs(tNearbyDatas.OtherCities.CitiesWithResources) do
        			if DataTable.CityResourceQty > 0 then
        				if tNearbyDatas.OurCity:GetOwner() ~= DataTable.CityObject:GetOwner() then
        					print("The City at " .. DataTable.CityObject:GetX() .. "," .. DataTable.CityObject:GetY() .. " is within working range of our city " .. tNearbyDatas.OurCity:GetName() .. " and has a strategic resource on its plot! Forward Settling! Theft!")
        					v:ChangeDamage(10)
        				end
        			end
        		end
        	end
        end
     
    Last edited: Oct 4, 2018
  5. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
  6. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    1. I've created a mod that uses the utlity as an additional practical example of how to use the utility. The mod is attached to this post as a zip of the mod-folder.
    2. The mod warns the human player whenever an AI Prophet or Missionary Unit is visible and is near a human-owned city that does not have a protecting Inquisitor Unit in the city.
      • The mod creates player notifications to the human player for each offending Missionary or Prophet unit near one their cities.
      • The mod only creates one notification to the human player for each such offending unit
      • The mod makes a notification for any mod-added units that have been added to the Prophet or Missionary class of units
      • The mod does not make notifications for offending Prophets or Missionaries that are near cities protected by an Inquisitor belonging to the human player. As for Prophets and Missionaries, any unit added by a mod that belongs to the Inquisitor-Class of units is considered an Inquisitor-Unit.
    3. I've includeed the entire code of the mod's main lua file, and I've highlighted the lines which are actually conducting the search for nearby Prophets or Missionary units in blue.
      • You will see that this is only 6 lines out of all the lines within the main lua file.
      • The other approx 90 lines are either the standard kinds of stuff any lua-script needs to have, or are devoted to determining whether an Inquisitor is in or adjacent to the city.
      • Of these 90-odd lines, I have highlighted in red the lines that are directly-related to whether a city has an Inquisitor stationed at or next to the city.
        1. These lines are grabbing all units from the Inquisitor-Class of Units, and adding them to a table called tInquisitorUnits:
          Spoiler gather inquisitor ids :
          Code:
          local tInquisitorUnits = { [GameInfoTypes.UNIT_INQUISITOR] = "true"}
          -------------------------------------------------------------------------------------------------------
          -- AddAllItemsInSameClassToTable
          -- All All Items In Same Classes To Defined Table
          -------------------------------------------------------------------------------------------------------
          function AddAllItemsInSameClassToTable(tTableName, sDataType)
          	if sDataType == "Units" then
          		for iUnit,sData in pairs(tTableName) do
          			local sUnitClass = GameInfo.Units[iUnit].Class
          			for UnitRow in GameInfo.Units("Class='" .. sUnitClass .. "'") do
          				if not tTableName[UnitRow.ID] then
          					tTableName[UnitRow.ID] = sData
          				end
          			end
          		end
          	elseif sDataType == "Buildings" then
          		for iBuilding,vData in pairs(tTableName) do
          			local sBuildingClass = GameInfo.Buildings[iBuilding].BuildingClass
          			for BuildingRow in GameInfo.Buildings("BuildingClass='" .. sBuildingClass .. "'") do
          				if not tTableName[BuildingRow.ID] then
          					tTableName[BuildingRow.ID] = vData
          				end
          			end
          		end
          	end
          end
          AddAllItemsInSameClassToTable(tInquisitorUnits, "Units")
        2. These lines are checking the city and adjacent plots for an inquisitor-class unit belonging to the city owner, and are showing the general code-methods and logic needed to find information on the game-map on or next to a given plot or city:
          Spoiler check city for inquisitor :
          Code:
          function PlotHasInquisitor(pPlot, iPlayer)
          	if pPlot:IsUnit() then
          		for i = 0, pPlot:GetNumUnits() - 1 do
          			local pUnit = pPlot:GetUnit(i)
          			if pUnit then
          				if tInquisitorUnits[pUnit:GetUnitType()] then
          					if pUnit:GetOwner() == iPlayer then
          						return true
          					end
          				end
          			end
          		end
          	end
          	return false
          end
          function CityHasInquisitor(pCity, iPlayer)
          	local pPlot = pCity:Plot()
          	if PlotHasInquisitor(pPlot, iPlayer) then
          		return true
          	end
          	for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
          		local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
          		if pAdjacentPlot then
          			if PlotHasInquisitor(pAdjacentPlot, iPlayer) then
          				return true
          			end
          		end
          	end
          	return false
          end
          Note that I needed to create approximately 30 lines of code in order to just check the plot the city is on and the plots adjacent to the city for inquisitor units owned by the city-owner.
        3. These two lines:
          Code:
          if not CityHasInquisitor(pCity, iPlayer) then
          and​
          Code:
          end
          are merely the "wrappers" used to not make the code execute these lines for adding to the list of nearby offending Prophet and Missionary Units​
          Code:
          local tNearbyDatas = GetCityMapDatas(pCity)
          local tEnemyMissionaries = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "table", "UNITCLASS_MISSIONARY", "Any", "MustBeVisible", "DoNotMakeNotification")
          tEnemyMissionaries, tEnemyReligiousUnitsThisTurn = FilterTableContents(tEnemyMissionaries, tEnemyReligiousUnitsThisTurn)
          local tEnemyProphets = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "table", "UNITCLASS_PROPHET", "Any", "MustBeVisible", "DoNotMakeNotification")
          tEnemyProphets, tEnemyReligiousUnitsThisTurn = FilterTableContents(tEnemyProphets, tEnemyReligiousUnitsThisTurn)
          when the city has a qualifying inquisitor unit in or adjacent to it​
      • These lines are running through the list of offending Prophets and Missionary units found, and executing a routine that causes a player-notification to be generated for each such unit:
        Code:
        if #tEnemyReligiousUnitsThisTurn > 0 then
        	for k,pUnit in pairs(tEnemyReligiousUnitsThisTurn) do
        		MakeAIReligiousUnitNotification(pPlayer, pUnit)
        	end
        end
      • This function (which was executed by the previously-noted iteration through table tEnemyReligiousUnitsThisTurn) actually generates the player-notification for a given unit:
        Code:
        function MakeAIReligiousUnitNotification(pPlayer, pUnit)
        	local pCity, iNearestDistance = GetNearestCityData(pUnit:GetPlot(), pPlayer)
        	local sUnitsPlayerName = Players[pUnit:GetOwner()]:GetName()
        	local tUnitTypeData = GameInfo.Units[pUnit:GetUnitType()]
        	local sUnitName = Locale.ConvertTextKey(tUnitTypeData.Description)
        	if pPlayer:IsHuman() then
        		local sMessageFull = "A religious " .. sUnitName .. " unit belonging to " .. sUnitsPlayerName .. " has been detected near the city of " .. pCity:GetName() .. "."
        		pPlayer:AddNotification(NotificationTypes["NOTIFICATION_GREAT_PERSON_ACTIVE_PLAYER"], sMessageFull, "EnemyInTerritory", pUnit:GetX(), pUnit:GetY(), pUnit:GetUnitType(), -1, false)
        	end
        end
        • I ended up using the "NOTIFICATION_GREAT_PERSON_ACTIVE_PLAYER" because the game did not want to actually create the "Nearby Enenmy" notification (NOTIFICATION_ENEMY_IN_TERRITORY). I did not get runtime or other errors, the game simply refused to make the notification.
        • This chunk of the code is used to find the nearest city to the offending Missionary or Prophet:
          Code:
          function GetNearestCityData(pPlot, pPlayer)
          	local pNearestCity, iNearestDistance = "NONE", 9999
          	for pCity in pPlayer:Cities() do
          		local iDistance = Map.PlotDistance(pPlot:GetX(), pPlot:GetY(), pCity:GetX(), pCity:GetY())
          		if (iDistance < iNearestDistance) then
          			iNearestDistance = iDistance
          			pNearestCity = pCity
          		end
          	end
          	return pNearestCity, iNearestDistance
          end
      • Note that here:
        Code:
        local tEnemyMissionaries = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "table", "UNITCLASS_MISSIONARY", "Any", "MustBeVisible", "DoNotMakeNotification")
        and here​
        Code:
        local tEnemyProphets = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "table", "UNITCLASS_PROPHET", "Any", "MustBeVisible", "DoNotMakeNotification")
        I could have used "UNIT_MISSIONARY" and "UNIT_PROPHET", but this would make the code ignore any additional Units added by other mods to the two classes of units.​
    4. Here is the code from the main lua-file:
      Spoiler whole file :
      Code:
      -- WarnForNearbyReligiousUnits
      -- Author: LeeS
      -- DateCreated: 8/10/2016 9:11:05 AM
      --------------------------------------------------------------
      
      [color="blue"]include("CityNearbyMapDatas.lua")[/color]
      
      
      [color="red"]local tInquisitorUnits = { [GameInfoTypes.UNIT_INQUISITOR] = "true"}
      -------------------------------------------------------------------------------------------------------
      -- AddAllItemsInSameClassToTable
      -- All All Items In Same Classes To Defined Table
      -------------------------------------------------------------------------------------------------------
      function AddAllItemsInSameClassToTable(tTableName, sDataType)
      	if sDataType == "Units" then
      		for iUnit,sData in pairs(tTableName) do
      			local sUnitClass = GameInfo.Units[iUnit].Class
      			for UnitRow in GameInfo.Units("Class='" .. sUnitClass .. "'") do
      				if not tTableName[UnitRow.ID] then
      					tTableName[UnitRow.ID] = sData
      				end
      			end
      		end
      	elseif sDataType == "Buildings" then
      		for iBuilding,vData in pairs(tTableName) do
      			local sBuildingClass = GameInfo.Buildings[iBuilding].BuildingClass
      			for BuildingRow in GameInfo.Buildings("BuildingClass='" .. sBuildingClass .. "'") do
      				if not tTableName[BuildingRow.ID] then
      					tTableName[BuildingRow.ID] = vData
      				end
      			end
      		end
      	end
      end
      AddAllItemsInSameClassToTable(tInquisitorUnits, "Units")[/color]
      function GetNearestCityData(pPlot, pPlayer)
      	local pNearestCity, iNearestDistance = "NONE", 9999
      	for pCity in pPlayer:Cities() do
      		local iDistance = Map.PlotDistance(pPlot:GetX(), pPlot:GetY(), pCity:GetX(), pCity:GetY())
      		if (iDistance < iNearestDistance) then
      			iNearestDistance = iDistance
      			pNearestCity = pCity
      		end
      	end
      	return pNearestCity, iNearestDistance
      end
      function MakeAIReligiousUnitNotification(pPlayer, pUnit)
      	local pCity, iNearestDistance = GetNearestCityData(pUnit:GetPlot(), pPlayer)
      	local sUnitsPlayerName = Players[pUnit:GetOwner()]:GetName()
      	local tUnitTypeData = GameInfo.Units[pUnit:GetUnitType()]
      	local sUnitName = Locale.ConvertTextKey(tUnitTypeData.Description)
      	if pPlayer:IsHuman() then
      		local sMessageFull = "A religious " .. sUnitName .. " unit belonging to " .. sUnitsPlayerName .. " has been detected near the city of " .. pCity:GetName() .. "."
      		pPlayer:AddNotification(NotificationTypes["NOTIFICATION_GREAT_PERSON_ACTIVE_PLAYER"], sMessageFull, "EnemyInTerritory", pUnit:GetX(), pUnit:GetY(), pUnit:GetUnitType(), -1, false)
      	end
      end
      [color="red"]function PlotHasInquisitor(pPlot, iPlayer)
      	if pPlot:IsUnit() then
      		for i = 0, pPlot:GetNumUnits() - 1 do
      			local pUnit = pPlot:GetUnit(i)
      			if pUnit then
      				if tInquisitorUnits[pUnit:GetUnitType()] then
      					if pUnit:GetOwner() == iPlayer then
      						return true
      					end
      				end
      			end
      		end
      	end
      	return false
      end
      function CityHasInquisitor(pCity, iPlayer)
      	local pPlot = pCity:Plot()
      	if PlotHasInquisitor(pPlot, iPlayer) then
      		return true
      	end
      	for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
      		local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
      		if pAdjacentPlot then
      			if PlotHasInquisitor(pAdjacentPlot, iPlayer) then
      				return true
      			end
      		end
      	end
      	return false
      end[/color]
      function MonitorAIReligiousSpam(iPlayer)
      	local pPlayer = Players[iPlayer]
      	if pPlayer:IsHuman() then
      		----------------------------------------------------------------
      		-- Look in all cities for nearby AI Missionaries and Prophets
      		----------------------------------------------------------------
      		local tEnemyReligiousUnitsThisTurn = {}
      		for pCity in pPlayer:Cities() do
      			[color="red"]if not CityHasInquisitor(pCity, iPlayer) then[/color]
      				---------------------------------------------------------------------------
      				--the following 5 lines are using the CityNearbyMapDatas Utility to create a table of all "enemy"
      				--	missionary or prophet units near the human player's cities
      				--	the resulting table is tEnemyReligiousUnitsThisTurn
      				---------------------------------------------------------------------------
      				[color="blue"]local tNearbyDatas = GetCityMapDatas(pCity)
      				local tEnemyMissionaries = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "table", "UNITCLASS_MISSIONARY", "Any", "MustBeVisible", "DoNotMakeNotification")
      				tEnemyMissionaries, tEnemyReligiousUnitsThisTurn = FilterTableContents(tEnemyMissionaries, tEnemyReligiousUnitsThisTurn)
      				local tEnemyProphets = AreEnemyUnitsOfTypeNearby(tNearbyDatas, "table", "UNITCLASS_PROPHET", "Any", "MustBeVisible", "DoNotMakeNotification")
      				tEnemyProphets, tEnemyReligiousUnitsThisTurn = FilterTableContents(tEnemyProphets, tEnemyReligiousUnitsThisTurn)[/color]
      			[color="red"]end[/color]
      		end
      		if #tEnemyReligiousUnitsThisTurn > 0 then
      			for k,pUnit in pairs(tEnemyReligiousUnitsThisTurn) do
      				MakeAIReligiousUnitNotification(pPlayer, pUnit)
      			end
      		end
      	end
      end
      GameEvents.PlayerDoTurn.Add(MonitorAIReligiousSpam)
      
      
      print("WarnForNearbyReligiousUnits.lua from the Warn For Nearby AI Missionary and Prophets Mod loaded to the end")
     

    Attached Files:

  7. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    Download page updated to a new version that corrected the typo in the one function name. If using the utility you should grab the latest version from the download and update to use the new version of file CityNearbyMapDatas.lua. You do not need to change anything else. Compatibility to the old version's typo'd function-name is contained within the new version.

    I also put all the lua code for the generic toolkit stuff into one file you can copy/paste into your mods and use without needing to grap the text of the toolkit functions directly from the thread. The file is called ToolkitFiles.lua: you can add it to a mod and set as Import=true and then include it into your mod's lua-file via
    Code:
    include("ToolkitFiles.lua")
    This will make the functions called ItemIsInTable, GetUnitIDSThatMatch, FilterTableContents and IsResourceRevealed available to you in your mod's lua file.
     
  8. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    1. As an Additional example on how the utility can be used, I've created a mod that adds a new game-table called Building_RequireNearbyResourcesORS which allows you to specify that a building only requires any one of several resources to be near the city and within the player's territory.
      • The new game-table does not require the resource to be improved as do the two Firaxis-supplied tables Building_LocalResourceAnds and Building_LocalResourceOrs.
    2. The definition of the table is given as
      Code:
      <GameData>
      	<Table name="Building_RequireNearbyResourcesORS">
      		<Column name="ResourceType" type="text" reference="Resources(Type)"/>
      		<Column name="BuildingType" type="text" reference="Buildings(Type)"/>
      	</Table>
      </GameData>
      Every row in the new table needs:​
      1. A "BuildingType", such as BUILDING_COLOSSEUM
      2. A "ResourceType", such as RESOURCE_HORSE
    3. A single building can have multiple resources, and when more than one resource is specified, any one of the specified resources existing near the city allows the Building to be constructed or purshased in that city.
    4. I have used the Colosseum building as a practical example of how to structure a table so that the Colosseum would require either Horses or Ivory near the city. The city can have both, and the effect is the same.
      Code:
      <GameData>
      	<Building_RequireNearbyResourcesORS>
      		<Row>
      			<BuildingType>BUILDING_COLOSSEUM</BuildingType>
      			<ResourceType>RESOURCE_HORSE</ResourceType>
      		</Row>
      		<Row>
      			<BuildingType>BUILDING_COLOSSEUM</BuildingType>
      			<ResourceType>RESOURCE_IVORY</ResourceType>
      		</Row>
      	</Building_RequireNearbyResourcesORS>
      </GameData>
      Obviously as a practical in-game requirement you would not do this to the Colosseum Building if for no other reason this would make Circus Maximus virtually impossible to ever construct.​
    5. File XML/Building_RequireNearbyResourcesORS.xml has the definition of the new xml game-table
    6. File XML/ColosseumRequireHorseIvory.xml has the example code to make the Colosseum require either Horse or Ivory resources near the city
    7. File LUA/CityNearbyMapDatas.lua is the lua file of the utility
    8. File LUA/Building_RequireNearbyResourcesORS.lua is the mod's main lua file and contains the actual logic needed to read the contents of the xml-table Building_RequireNearbyResourcesORS and to determine if a city meets the conditions set.

    In order to make use of this example in one of your mods, simply use the same activation order and methods for the xml and lua files as are designated in the mod's Building_RequireNearbyResourcesORS (v 1).modinfo file.
     

    Attached Files:

  9. bouncymischa

    bouncymischa Synthetic Genie

    Joined:
    Nov 28, 2012
    Messages:
    1,537
    Location:
    In a bottle
    Hey LeeS, while using your utility for my next update to Future Worlds, I ran into a small bug:

    Looking at the lua file, it seems you made a typo in this line:

    You're missing an 'r' in 'tImprovementValidResourcesCityNearbyMapDatas'

    Otherwise, the code seems to work fine! :D
     
  10. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA
    So far as I can tell I omitted the 'r' everywhere, so it should be okay. I think the problem is more likely a case of attemting to 'index'
    Code:
    tImprovementValidResoucesCityNearbyMapDatas[iImprovementID]
    when "iImprovementID" is not a "key" within table "tImprovementValidResoucesCityNearbyMapDatas".

    I've altered the code to account for these possibilities in this version, which I have not as yet published because I'm still working on adding some interface routines, and then the forum went crazy.

    You'll have to update your "include" statement to
    Code:
    include("CityNearbyMapDatasV4.lua")
     

    Attached Files:

  11. LeeS

    LeeS Imperator

    Joined:
    Jul 23, 2013
    Messages:
    6,121
    Location:
    Illinois, USA

Share This Page