Dynamic City Naming

Another step toward RFC like mods :goodjob:

I had almost the same table in my project, but no code yet, so your's is very welcome and fit almost perfectly, thank you :)

Spoiler :

Code:
<GameData>
	<!-- Yagem -->
	<YagemCityMap>
		<!-- FRANCE -->
		<Row>
			<civilization>CIVILIZATION_FRANCE</civilization>
			<X></X>
			<Y></Y>
			<ERA_ANCIENT>Parisi</ERA_ANCIENT>
			<ERA_CLASSICAL>Lutece</ERA_CLASSICAL>
			<ERA_MEDIEVAL>Paris</ERA_MEDIEVAL>
			<ERA_RENAISSANCE>Paris</ERA_RENAISSANCE>
			<ERA_INDUSTRIAL>Paris</ERA_INDUSTRIAL>
			<ERA_MODERN>Paris</ERA_MODERN>
			<ERA_FUTURE>Paris</ERA_FUTURE>			
		</Row>
	</YagemCityMap>

	<!-- europe Standard -->
	<EuroLargeCityMap>
		<!-- FRANCE -->
		<Row>
			<civilization>CIVILIZATION_FRANCE</civilization>
			<X></X>
			<Y></Y>
			<ERA_ANCIENT>Lutece</ERA_ANCIENT>
			<ERA_CLASSICAL>Lutece</ERA_CLASSICAL>
			<ERA_MEDIEVAL>Paris</ERA_MEDIEVAL>
			<ERA_RENAISSANCE>Paris</ERA_RENAISSANCE>
			<ERA_INDUSTRIAL>Paris</ERA_INDUSTRIAL>
			<ERA_MODERN>Paris</ERA_MODERN>
			<ERA_FUTURE>Paris</ERA_FUTURE>
		</Row>
	</EuroLargeCityMap>
</GameData>


And yes, there is a small suggestion in that spoiler ;)
 
There is a very subtle bug in the IsValidCityName() method

You compare a TXT_KEY_ from the database (the nameStr parameter) with the value from pCity:GetNameKey() method, however, that value is not guarenteed to be a TXT_KEY_ (and in fact your events set it to not be a TXT_KEY_)

Found a city
Code:
> pCity:GetNameKey()
TXT_KEY_CITY_NAME_CARTHAGE
> pCity:GetName()
Carthage

rename the city from Carthage to XXX and then back to Carthage (or call pCity:SetName("Carthage") from Lua)

Code:
> pCity:GetNameKey()
Carthage
> pCity:GetName()
Carthage

You need the function to be
Code:
function IsValidCityName([COLOR="Red"][B]nameKey[/B][/COLOR])
    [COLOR="red"][B]local nameStr = Locale.ConvertTextKey(nameKey)[/B][/COLOR]
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local player = Players[iPlayerLoop];
        if player:IsAlive() then
            for city in player:Cities() do
                if city ~= nil then
                    local cityName = [COLOR="red"][B]city:GetName()[/B][/COLOR];
                    if nameStr == cityName then
                        return false;
                    end
                end
            end
        end
    end

    return true;
end
 
Some general observations on the code presented in post #1

Firstly, there is nothing wrong with the code, it does what it says so is, by definition, correct. However, it contains a number of aspects that could be better, and are quite common beginner scripting mistakes, so are probably worth pointing out. Secondly, I did ask FramedArchitect if I could hi-jack this thread before posting this critique. Thirdly, like all artistic endeavours, programming is a highly personal persuit - ignore, adapt or adopt these comments as you wish.

Pick ONE naming convention and stick with it
cityNameStr (camel-case, lower case first letter) vs CityNames (camel-case, upper case first letter) vs iPlayer (Hungarian notation)

Name things correctly
nameStr is not a "name string" but actually a "TXT_KEY_", so nameKey would be a better choice (and GetScenarioCityTxtKey for GetScenarioCityName)

Don't comment the obvious, let the code do the talking
(that way if you change the code, but not the comment, there is no confusion)
Code:
    --only add valid names to array - You don't say!
    if bValid then

Code:
    --loop through scenario city names - Well I would never have guessed that!
    for row in GameInfo.Civilization_ScenarioCityNames() do

but do comment the obscure
Code:
    --sort names by distance from new city plot - That makes more sense now :)
    table.sort(CityNames, function(x,y) return x.dist < y.dist end)

There is an old programmers adage of "if the code and comments disagree, believe neither". I have seen many, many programmers happily debugging comments and ignoring the actual code!

You don't have to local-ise everything!
To me, a local variable says "Look at me, I'm important!" If a local variable is only used briefly, then don't bring attention to it by making it one

Code:
  local plot = Map.GetPlot(iX, iY)
  local city = plot:GetPlotCity()
plot is not important, it is only used as an interim to get city (which is important)
Code:
local city = Map.GetPlot(iX, iY):GetPlotCity()
is just as clear where city comes from without also saying "Hey, I may (or may not) be using plot later"

Code:
function OnScenarioCityCreated( iPlayer, iX, iY )
    if not IsScenarioWBMap() then return end

    local city = [COLOR="Red"]Map.GetPlot(iX, iY)[/COLOR]:GetPlotCity()
    if city == nil then return end

    local nameKey = GetScenarioCityTxtKey(iPlayer, iX, iY)
    if nameKey ~= nil then
        city:SetName(Locale.ConvertTextKey(nameKey))
    end
end
GameEvents.PlayerCityFounded.Add( OnScenarioCityCreated )

function OnScenarioCityCaptured (iOldOwner, bIsCapital, iX, iY, iNewOwner, iPop, bConquest)
    if not IsScenarioWBMap() then return end

    local city = [COLOR="Red"]Map.GetPlot(iX, iY)[/COLOR]:GetPlotCity()
    if city == nil then return end

    local nameKey = GetScenarioCityTxtKey(iNewOwner, iX, iY)
    if nameKey ~= nil then
        city:SetName(Locale.ConvertTextKey(nameKey))
    end
end
GameEvents.CityCaptureComplete.Add( OnScenarioCityCaptured )

function GetScenarioCityTxtKey(iPlayer, iX, iY)
    local cityNameKey = nil
    local CityNames = {}
    
    for row in GameInfo.Civilization_ScenarioCityNames() do
        local civType = GameInfoTypes[ row.CivilizationType ]
        local pCivType = Players[iPlayer]:GetCivilizationType()
        if ( pCivType == civType ) then
            local plotDistance = Map.PlotDistance(iX, iY, [COLOR="Red"]row.CityX[/COLOR], [COLOR="Red"]row.CityY[/COLOR])
            if ( plotDistance <= row.CityRadius ) then
                local cNameKey = row.CityName
                if [COLOR="Red"]IsValidCityName(cNameKey)[/COLOR] then
                    [COLOR="Red"]table.insert(CityNames, {dist = plotDistance, name = cNameKey})[/COLOR]
                end
            end
        end
    end

    local cityChoices = #CityNames
    if cityChoices > 0 then
        -- sort names by distance from new city plot
        table.sort(CityNames, function(x,y) return x.dist < y.dist end)
        
        local randName = {}
        local minDist = CityNames[1].dist

        for i,v in ipairs(CityNames) do
            if cityChoices == 1 then
                cityNameKey = [COLOR="Red"]CityNames[i].name[/COLOR]
            else
                if [COLOR="Red"]CityNames[i].dist[/COLOR] == minDist then
                    table.insert(randName, [COLOR="Red"]CityNames[i].name[/COLOR])
                end
            end                                                                                                                                        
        end

        if #randName > 0 then
            cityNameKey = randName[ math.random( #randName ) ]
        end
    end

    return cityNameKey
end

function IsValidCityName(nameKey)
    local nameKey = Locale.ConvertTextKey(nameKey)
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local player = Players[iPlayerLoop]
        if player:IsAlive() then
            for city in player:Cities() do
                if city ~= nil then
                    if nameKey == [COLOR="Red"]city:GetName()[/COLOR] then
                        return false
                    end
                end
            end
        end
    end

    return true
end

function IsScenarioWBMap()
    return Path.UsesExtension(PreGame.GetMapScript(),".Civ5Map") 
end

If you don't use it, don't name it
Some Lua functions return multiple results, the obvious ones being ipairs() and pairs(). If you don't need all the results, don't give them (meaningful) names. The accepted way to do this in Lua is to use _

Code:
-- We never use v so don't name it
for i,[COLOR="Red"]_[/COLOR] in ipairs(CityNames) do
    if cityChoices == 1 then
        cityNameKey = CityNames[i].name
    else
        if CityNames[i].dist == minDist then
            table.insert(randName, CityNames[i].name)
        end
    end                                                                                                                                        
end

nil is false
In Lua both "false" and "nil" evaluate to false in a condition. Consider replacing "if something ~= nil then" with "if something then" and changing
Code:
if var == nil then return end
-- Do something positive
with
Code:
if var then
  -- Do something positive
end

Code:
function OnScenarioCityCreated( iPlayer, iX, iY )
    if not IsScenarioWBMap() then return end

    local city = Map.GetPlot(iX, iY):GetPlotCity()
    [COLOR="Red"]if city then[/COLOR]
        local nameKey = GetScenarioCityTxtKey(iPlayer, iX, iY)
        if [COLOR="Red"]nameKey[/COLOR] then
            city:SetName(Locale.ConvertTextKey(nameKey))
        end
    [COLOR="Red"]end[/COLOR]
end
GameEvents.PlayerCityFounded.Add( OnScenarioCityCreated )

function OnScenarioCityCaptured (iOldOwner, bIsCapital, iX, iY, iNewOwner, iPop, bConquest)
    if not IsScenarioWBMap() then return end

    local city = Map.GetPlot(iX, iY):GetPlotCity()
    [COLOR="Red"]if city then[/COLOR]
        local nameKey = GetScenarioCityTxtKey(iNewOwner, iX, iY)
        if [COLOR="Red"]nameKey[/COLOR] then
            city:SetName(Locale.ConvertTextKey(nameKey))
        end
    [COLOR="Red"]end[/COLOR]
end
GameEvents.CityCaptureComplete.Add( OnScenarioCityCaptured )

function GetScenarioCityTxtKey(iPlayer, iX, iY)
    local cityNameKey = nil
    local CityNames = {}
    
    for row in GameInfo.Civilization_ScenarioCityNames() do
        local civType = GameInfoTypes[ row.CivilizationType ]
        local pCivType = Players[iPlayer]:GetCivilizationType()
        if ( pCivType == civType ) then
            local plotDistance = Map.PlotDistance(iX, iY, row.CityX, row.CityY)
            if ( plotDistance <= row.CityRadius ) then
                local cNameKey = row.CityName
                if IsValidCityName(cNameKey) then
                    table.insert(CityNames, {dist = plotDistance, name = cNameKey})
                end
            end
        end
    end

    local cityChoices = #CityNames
    if cityChoices > 0 then
        -- sort names by distance from new city plot
        table.sort(CityNames, function(x,y) return x.dist < y.dist end)
        
        local randName = {}
        local minDist = CityNames[1].dist

        for i,_ in ipairs(CityNames) do
            if cityChoices == 1 then
                cityNameKey = CityNames[i].name
            else
                if CityNames[i].dist == minDist then
                    table.insert(randName, CityNames[i].name)
                end
            end                                                                                                                                        
        end

        if #randName > 0 then
            cityNameKey = randName[ math.random( #randName ) ]
        end
    end

    return cityNameKey
end

function IsValidCityName(nameKey)
    local nameKey = Locale.ConvertTextKey(nameKey)
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local player = Players[iPlayerLoop]
        if player:IsAlive() then
            for city in player:Cities() do
                if [COLOR="Red"]city[/COLOR] then
                    if nameKey == city:GetName() then
                        return false
                    end
                end
            end
        end
    end

    return true
end

function IsScenarioWBMap()
    return Path.UsesExtension(PreGame.GetMapScript(),".Civ5Map") 
end

Remove invariants from loops
An invariant is something that doesn't change everytime it is evaluated, common occurances are

Code:
  for iLoopPlayer = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
    local pLoopPlayer = Players[iLoopPlayer]
    local pPlayer = Players[Game.GetActivePlayer()]
    -- do something with pLoopPlayer and pPlayer
  end
pPlayer never changes but is evaluated 22 times, use
Code:
  local pPlayer = Players[Game.GetActivePlayer()]
  for iLoopPlayer = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
    local pLoopPlayer = Players[iLoopPlayer]
    -- do something with pLoopPlayer and pPlayer
  end
which only evaluates it once

Code:
function GetScenarioCityTxtKey(iPlayer, iX, iY)
    local cityNameKey = nil
    local CityNames = {}
    [COLOR="Red"]local pCivType = Players[iPlayer]:GetCivilizationType()[/COLOR]
    
    for row in GameInfo.Civilization_ScenarioCityNames() do
        local civType = GameInfoTypes[ row.CivilizationType ]
        if ( pCivType == civType ) then
            local plotDistance = Map.PlotDistance(iX, iY, row.CityX, row.CityY)
            if ( plotDistance <= row.CityRadius ) then
                local cNameKey = row.CityName
                if IsValidCityName(cNameKey) then
                    table.insert(CityNames, {dist = plotDistance, name = cNameKey})
                end
            end
        end
    end

    [COLOR="Red"]if (#CityNames == 1) then
        cityNameKey = CityNames[1].name
    elseif (#CityNames > 1) then[/COLOR]
        -- sort names by distance from new city plot
        table.sort(CityNames, function(x,y) return x.dist < y.dist end)
        
        local randName = {}
        local minDist = CityNames[1].dist

        for i,_ in ipairs(CityNames) do
            [COLOR="Red"]if CityNames[i].dist == minDist then
                table.insert(randName, CityNames[i].name)
            end[/COLOR]
        end

        if #randName > 0 then
            cityNameKey = randName[ math.random( #randName ) ]
        end
    end

    return cityNameKey
end

Know what you're dealing with
Know what you've got. Don't bother testing for nil if it can never be nil. Both the events only fire if there is a city at (iX, iY) and pPlayer:Cities() will never return non-existent (ie nil) cities

Code:
function OnScenarioCityCreated( iPlayer, iX, iY )
    if not IsScenarioWBMap() then return end

    local city = Map.GetPlot(iX, iY):GetPlotCity()
    local nameKey = GetScenarioCityTxtKey(iPlayer, iX, iY)
    if nameKey then
        city:SetName(Locale.ConvertTextKey(nameKey))
    end
end
GameEvents.PlayerCityFounded.Add( OnScenarioCityCreated )

function OnScenarioCityCaptured (iOldOwner, bIsCapital, iX, iY, iNewOwner, iPop, bConquest)
    if not IsScenarioWBMap() then return end

    local city = Map.GetPlot(iX, iY):GetPlotCity()
    local nameKey = GetScenarioCityTxtKey(iNewOwner, iX, iY)
    if nameKey then
        city:SetName(Locale.ConvertTextKey(nameKey))
    end
end
GameEvents.CityCaptureComplete.Add( OnScenarioCityCaptured )

function GetScenarioCityTxtKey(iPlayer, iX, iY)
    local cityNameKey = nil
    local CityNames = {}
    [COLOR="Red"]-- row.CivilizationType below is a Type (eg CIVILIZATION_ENGLAND) so let's get the same thing here to simplify the code in the loop
    local pCivType = GameInfo.Civilizations[Players[iPlayer]:GetCivilizationType()].Type[/COLOR]
    
    for row in GameInfo.Civilization_ScenarioCityNames() do
        [COLOR="Red"]if ( pCivType == row.CivilizationType ) then[/COLOR]
            local plotDistance = Map.PlotDistance(iX, iY, row.CityX, row.CityY)
            if ( plotDistance <= row.CityRadius ) then
                local cNameKey = row.CityName
                if IsValidCityName(cNameKey) then
                    table.insert(CityNames, {dist = plotDistance, name = cNameKey})
                end
            end
        end
    end

    if (#CityNames == 1) then
        cityNameKey = CityNames[1].name
    elseif (#CityNames > 1) then
        -- sort names by distance from new city plot
        table.sort(CityNames, function(x,y) return x.dist < y.dist end)
        
        local randName = {}
        local minDist = CityNames[1].dist

        [COLOR="Red"]-- cn is CityNames[i] so use it!
        for _,cn in ipairs(CityNames) do
            if cn.dist == minDist then
                table.insert(randName, cn.name)
            end
        end[/COLOR]

        [COLOR="Red"]-- There will always be at least one entry in randName
        cityNameKey = randName[math.random(#randName)][/COLOR]
    end

    return cityNameKey
end

function IsValidCityName(nameKey)
    local nameKey = Locale.ConvertTextKey(nameKey)
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local player = Players[iPlayerLoop]
        if player:IsAlive() then
            for city in player:Cities() do
                [COLOR="Red"]if nameKey == city:GetName() then
                    return false
                end[/COLOR]
            end
        end
    end

    return true
end

Know when to give up
Use break to exit loops when you know there is nothing else of interest

Code:
for _,cn in ipairs(CityNames) do
    if cn.dist == minDist then
        table.insert(randName, cn.name)
    else
        [COLOR="Red"]-- As the list is sorted by distance, every other city will also be further away, so stop looking
        break[/COLOR]
    end
end

Don't store it if you've already got it
There is no need to copy the first N items of CityNames into randName, we just need to know what N is!

Code:
if (#CityNames == 1) then
    cityNameKey = CityNames[1].name
elseif (#CityNames > 1) then
    -- sort names by distance from new city plot
    table.sort(CityNames, function(x,y) return x.dist < y.dist end)
       
    [COLOR="Red"]local possibleCities = 0[/COLOR]
    local minDist = CityNames[1].dist

    for _,cn in ipairs(CityNames) do
        if cn.dist == minDist then
            [COLOR="Red"]possibleCities = possibleCities + 1[/COLOR]
        else
            -- As the list is sorted by distance, every other city will also be further away, so stop looking
            break
        end
    end

    [COLOR="Red"]cityNameKey = CityNames[math.random(possibleCities)] [/COLOR]
end

Watch out for special cases
The function passed to table.sort() MUST generate the same list every time. If we have London/8, Paris/6, Berlin/6, Dublin/12 there are two possible sort orders if we use just the distance
"Paris/6, Berlin/6, London/8, Dublin/12" and "Berlin/6, Paris/6, London/8, Dublin/12", that can cause Lua to produce incorrect results.

Code:
-- sort names by distance from new city plot, sort alphabetically when both are the same distance from the plot
table.sort(CityNames, function(x,y) if (x.dist == y.dist) then return (x.name < y.name) else return (x.dist < y.dist) end end)

Civ5 GameInfo tricks
GameInfo.TableName() takes parameters, know when to use them!

Code:
-- SELECT * FROM Civilization_ScenarioCityNames
GameInfo.Civilization_ScenarioCityNames()

Code:
-- SELECT * FROM Civilization_ScenarioCityNames WHERE CivilizationType='CIVILIZATION_ENGLAND'
GameInfo.Civilization_ScenarioCityNames("CivilizationType='CIVILIZATION_ENGLAND'")

Code:
[COLOR="Red"]for row in GameInfo.Civilization_ScenarioCityNames("CivilizationType='" .. pCivType .. "'") do[/COLOR]
    local plotDistance = Map.PlotDistance(iX, iY, row.CityX, row.CityY)
    if ( plotDistance <= row.CityRadius ) then
        local cNameKey = row.CityName
        if IsValidCityName(cNameKey) then
            table.insert(CityNames, {dist = plotDistance, name = cNameKey})
        end
    end
end

Know when to break the rules
(Or just because you can doesn't mean you should!)
Clarity should be the over-riding rule!!!

Good
Code:
    local pCivType = GameInfo.Civilizations[Players[iPlayer]:GetCivilizationType()].Type
    for row in GameInfo.Civilization_ScenarioCityNames("CivilizationType='" .. pCivType .. "'") do

Bad
Code:
    for row in GameInfo.Civilization_ScenarioCityNames("CivilizationType='" .. GameInfo.Civilizations[Players[iPlayer]:GetCivilizationType()].Type .. "'") do

FINAL VERSION
Warning! The process of editing all the changes may have introduced unintentional typos!

Code:
function OnScenarioCityCreated( iPlayer, iX, iY )
    if not IsScenarioWBMap() then return end

    local city = Map.GetPlot(iX, iY):GetPlotCity()
    local nameKey = GetScenarioCityTxtKey(iPlayer, iX, iY)
    if nameKey then
       city:SetName(Locale.ConvertTextKey(nameKey))
    end
end
GameEvents.PlayerCityFounded.Add( OnScenarioCityCreated )

function OnScenarioCityCaptured (iOldOwner, bIsCapital, iX, iY, iNewOwner, iPop, bConquest)
    if not IsScenarioWBMap() then return end

    local city = Map.GetPlot(iX, iY):GetPlotCity()
    local nameKey = GetScenarioCityTxtKey(iNewOwner, iX, iY)
    if nameKey then
        city:SetName(Locale.ConvertTextKey(nameKey))
    end
end
GameEvents.CityCaptureComplete.Add( OnScenarioCityCaptured )

function GetScenarioCityTxtKey(iPlayer, iX, iY)
    local cityNameKey = nil
    local CityNames = {}
    local pCivType = GameInfo.Civilizations[Players[iPlayer]:GetCivilizationType()].Type
    
    for row in GameInfo.Civilization_ScenarioCityNames("CivilizationType='" .. pCivType .. "'") do
        local plotDistance = Map.PlotDistance(iX, iY, row.CityX, row.CityY)
        if ( plotDistance <= row.CityRadius ) then
            local cNameKey = row.CityName
            if IsValidCityName(cNameKey) then
                table.insert(CityNames, {dist = plotDistance, name = cNameKey})
            end
        end
    end

    if (#CityNames == 1) then
        cityNameKey = CityNames[1].name
    elseif (#CityNames > 1) then
        -- sort names by distance from new city plot, use the city names when both the same distance from the plot
        table.sort(CityNames, function(x,y) if (x.dist == y.dist) then return (x.name < y.name) else return (x.dist < y.dist) end end)
        
        local possibleCities = 0
        local minDist = CityNames[1].dist

        for _,cn in ipairs(CityNames) do
            if cn.dist == minDist then
                possibleCities = possibleCities + 1
            else
                -- As the list is sorted by distance, every other city will also be further away, so stop looking
                break
            end
        end

        cityNameKey = CityNames[math.random(possibleCities)]
    end

    return cityNameKey
end

function IsValidCityName(nameKey)
    local nameKey = Locale.ConvertTextKey(nameKey)
    for iPlayerLoop = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
        local player = Players[iPlayerLoop]
        if player:IsAlive() then
            for city in player:Cities() do
                if nameKey == city:GetName() then
                    return false
                end
            end
        end
    end

    return true
end

function IsScenarioWBMap()
    return Path.UsesExtension(PreGame.GetMapScript(),".Civ5Map") 
end
 
^^^Was is just me or did that seem a little condescending? Might've been just me...

Great job, FramedArchitect. I look forward to using this and Gedemon's Historical Spawn Dates component together someday, as a makeshift RFC until the (hopefully eventual) real thing. :goodjob:
 
Firstly, there is nothing wrong with the code, it does what it says so is, by definition, correct. However, it contains a number of aspects that could be better, and are quite common beginner scripting mistakes, so are probably worth pointing out.

Hey William,
Thanks for the lesson; very useful. If only professionals were always available to translate mod ideas into working code.... Since this is not the case, I've just resigned myself to cobbling together code to make my ideas happen in game.

I hadn't even thought about doing this kind of thing until last November, so I don't think I've reached the "beginner" scripting stage yet. It's my mistake for sharing lua code on this site before I've made something that not only works, but works well. I'll be much more circumspect in future.

Finally, while your critique of my code comments may be generally valid, you're missing a critical piece of knowledge here. That code was written for use in a group project I'm working on with several people who have no knowledge of lua, and the comments were inserted for them. For my own purposes, I rarely comment my code unless it's a "to-do" note or a method that works, but I don't yet fully understand.

Again, thanks for the complete revision!

Best,
FA
 
What does this mod do exactly? There wasn't much info on that in post 6(except for programmers).
 
How do you use this? How do you assign city names to plots? I'd like some more documentation, as I would love to use this on some giant TSL maps.

First you have to add a new table to the db.

Code:
CREATE TABLE Civilization_ScenarioCityNames
( 
  CivilizationType TEXT DEFAULT NULL,
  CityX INTEGER DEFAULT 0,
  CityY INTEGER DEFAULT 0,
  CityRadius INTEGER DEFAULT 0,
  CityName TEXT DEFAULT NULL
);

Then add city names for civilizations with the x, y coordinates that define the center of the city radius.

Code:
INSERT INTO Civilization_ScenarioCityNames  (CivilizationType,	CityX,	CityY,	CityRadius, CityName)
SELECT 'CIVILIZATION_SPAIN',	18,		12,		2,			'TXT_KEY_SCENARIO_CITY_TUMBES' UNION ALL
SELECT 'CIVILIZATION_SPAIN',	21,		8,		2,			'TXT_KEY_SCENARIO_CITY_TALARA' UNION ALL
SELECT 'CIVILIZATION_SPAIN',	28,		5,		2,			'TXT_KEY_SCENARIO_CITY_CHICLAYO' UNION ALL
SELECT 'CIVILIZATION_SPAIN',	38,		7,		2,			'TXT_KEY_SCENARIO_CITY_TRUJILLO' UNION ALL
SELECT 'CIVILIZATION_SPAIN',	71,		8,		2,			'TXT_KEY_SCENARIO_CITY_LIMA';

Don't forget to include the text strings for the city names. Also, if you assign a different civilization and city name to the same coordinates, then when that civ captures a city on that plot it will change names.
 
Excellent! Probably should include information like this in the first post, but thank you for answering!

All this was included in the original post, but I deleted it.

After reading whoward's lengthy critique above, I decided it was best for the community if I did not post code on this site, as it was most likely wrong or inelegant. I not only deleted my post here, but also every post I had made that included code.

I am not comfortable presenting the db code again, but I am fairly confident it is accurate and concise.
 
Top Bottom