whoward69
DLL Minion
So you've created your new civ with a list of city names - Perth, Brisbane, Cairns, etc - but what if your civ is nomadic and a city list is inappropriate as the tribes named themselves after actions or their environment?
You could create a list of appropriate tribe names - Snake River, Rocky Pass, Soaring Eagle - and randomise them (see SQL at very end of post), but that could lead to weird naming circumstances - the Snake River Tribe settling nowhere near a river, or the Rocky Pass clan at the mouth of a river.
What we really want are semi-random names based on terrain/features/resources around the settlement tile.
We need to detect a new settlement being founded, and this can easily be achieved via the SetPopulation() GameEvent when the iOldPop parameter is zero.
If this settlement has just been founded (iOldPop == 0) and it's for the required player (IsMorindimPlayer(pCity:GetOwner()) and it's not the "capital" (Players[pCity:GetOwner()]:GetCapitalCity() ~= nil) rename the city.
As the DLL will already have picked a name for the new settlement before this event is sent we still need a small list of city names for the civ, including our "capital".
So how to pick a semi-random clan name based on local terrain/features?
Firstly we need to analyse the plots surrounding the settlement, both "near" (ring-1) and "far" (ring-2) - and for convience both ring-1 and ring-2 combined
With the analysis complete, we can write some functions to determine if a feature/terrain type is present, eg for mountains we can check for both a range and an isolated peak
and for being on a river
or on/near a marsh
These anonymous functions are placed into the namingFunctions table (as we're about to randomise them) and return either a key - RANGE, PEAK, RIVER, MARSH - or nil.
If we checked the terrain/features in order, we would always pick a mountain name over a river name over a marsh name, so we need to randomise the order we check for terrain/features. By putting all of the check functions into a table we can simply randomise the table (via the GetShuffledCopyOfTable() MapmakerUtilities helper function) and then call the functions in their new order.
We also need some general "catch all" functions - for plains, grasslands and "any situation not covered"
which we will add to the table of functions after it has been randomised.
The database entries for the settlement names look like
and we can add more simply by incrementing the final number, eg
Note: pPlayer:IsCityNameValid(sNewName) is a Lua API extension in my DLL, it's effects can be duplicated in Lua by looping all the player's cities (for pCity in pPlayer:Cities() do ... end) and checking their name against sNewName (if (pCity:GetName() == sNewName) then return false end)
So why bother with "GENERIC", why not just keep any pre-assigned name from the DLL? We almost certainly need more than four settlement names for the "any other situation" cases, but if we put more than four plus the capital into the <Civilization_CityNames> table there is a chance that the Huns will "borrow" them (they only take from the sixth name onwards - hard-coded in the DLL) - and a Hunnic city called "Red Fox Clan" is probably immersion breaking!
As a working example, the complete Lua code and XML entries for the Morindim Clans follows
And the SQL that randomises the 4 clan names in the Civilization_CityNames table - just for good measure!
You could create a list of appropriate tribe names - Snake River, Rocky Pass, Soaring Eagle - and randomise them (see SQL at very end of post), but that could lead to weird naming circumstances - the Snake River Tribe settling nowhere near a river, or the Rocky Pass clan at the mouth of a river.
What we really want are semi-random names based on terrain/features/resources around the settlement tile.
We need to detect a new settlement being founded, and this can easily be achieved via the SetPopulation() GameEvent when the iOldPop parameter is zero.
Code:
function OnSetPopulation(iPlotX, iPlotY, iOldPop, iNewPop)
-- Has this city just been founded
if (iOldPop == 0) then
local pCity = Map.GetPlot(iPlotX, iPlotY):GetPlotCity()
if (IsMorindimPlayer(pCity:GetOwner()) and (Players[pCity:GetOwner()]:GetCapitalCity() ~= nil)) then
SetClanName(pCity)
end
end
end
GameEvents.SetPopulation.Add(OnSetPopulation)
If this settlement has just been founded (iOldPop == 0) and it's for the required player (IsMorindimPlayer(pCity:GetOwner()) and it's not the "capital" (Players[pCity:GetOwner()]:GetCapitalCity() ~= nil) rename the city.
As the DLL will already have picked a name for the new settlement before this event is sent we still need a small list of city names for the civ, including our "capital".
Spoiler :
Code:
<GameData>
<Civilization_CityNames>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_GREAT_SPIRIT</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_1</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_2</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_3</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_4</CityName>
</Row>
</Civilization_CityNames>
<Language_en_US>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GREAT_SPIRIT">
<Text>Great Spirit Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_1">
<Text>Musk Ox Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_2">
<Text>Moon Walkers Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_3">
<Text>Cloud Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_4">
<Text>Bear Clan</Text>
</Row>
</Language_en_US>
</GameData>
So how to pick a semi-random clan name based on local terrain/features?
Firstly we need to analyse the plots surrounding the settlement, both "near" (ring-1) and "far" (ring-2) - and for convience both ring-1 and ring-2 combined
Code:
local allPlots, ring1Plots, ring2Plots
local allTerrains, ring1Terrains, ring2Terrains
local allFeatures, ring1Features, ring2Features
function GetCount(array, index)
return array[index] or 0
end
function IncCount(singleArray, combinedArray, index)
singleArray[index] = (singleArray[index] or 0) + 1
combinedArray[index] = (combinedArray[index] or 0) + 1
end
function AnalysePlots(pPlot)
allPlots = {}
ring1Plots = {}
ring2Plots = {}
allTerrains = {}
ring1Terrains = {}
ring2Terrains = {}
allFeatures = {}
ring1Features = {}
ring2Features = {}
for pRingPlot in PlotRingIterator(pPlot, 1) do
IncCount(ring1Plots, allPlots, pRingPlot:GetPlotType())
IncCount(ring1Terrains, allTerrains, pRingPlot:GetTerrainType())
IncCount(ring1Features, allFeatures, pRingPlot:GetFeatureType())
end
for pRingPlot in PlotRingIterator(pPlot, 2) do
IncCount(ring2Plots, allPlots, pRingPlot:GetPlotType())
IncCount(ring2Terrains, allTerrains, pRingPlot:GetTerrainType())
IncCount(ring2Features, allFeatures, pRingPlot:GetFeatureType())
end
end
With the analysis complete, we can write some functions to determine if a feature/terrain type is present, eg for mountains we can check for both a range and an isolated peak
Code:
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(allPlots, PlotTypes.PLOT_MOUNTAIN) >= 3) then
sNameKey = "RANGE"
elseif (GetCount(ring1Plots, PlotTypes.PLOT_MOUNTAIN) == 1) then
sNameKey = "PEAK"
end
return sNameKey
end)
and for being on a river
Code:
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (pPlot:IsRiver()) then
sNameKey = "RIVER"
end
return sNameKey
end)
or on/near a marsh
Code:
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(ring1Features, GameInfoTypes.FEATURE_MARSH) >= 1 or GetCount(ring2Features, GameInfoTypes.FEATURE_MARSH) >= 2) then
sNameKey = "MARSH"
end
return sNameKey
end)
These anonymous functions are placed into the namingFunctions table (as we're about to randomise them) and return either a key - RANGE, PEAK, RIVER, MARSH - or nil.
If we checked the terrain/features in order, we would always pick a mountain name over a river name over a marsh name, so we need to randomise the order we check for terrain/features. By putting all of the check functions into a table we can simply randomise the table (via the GetShuffledCopyOfTable() MapmakerUtilities helper function) and then call the functions in their new order.
We also need some general "catch all" functions - for plains, grasslands and "any situation not covered"
Code:
-- Plains
function PlainsNames(pPlot)
local sNameKey = nil
if (GetCount(ring1Terrains, GameInfoTypes.TERRAIN_PLAINS) >= 3) then
sNameKey = "PLAINS"
end
return sNameKey
end
-- Grasslands
function GrassNames(pPlot)
local sNameKey = nil
if (GetCount(ring1Terrains, GameInfoTypes.TERRAIN_GRASS) >= 3) then
sNameKey = "GRASS"
end
return sNameKey
end
-- Generic
function GenericNames(pPlot)
return "GENERIC"
end
which we will add to the table of functions after it has been randomised.
Code:
local namingFunctions = {}
-- Add name determining functions to the namingFunctions table here
function SetClanName(pClan)
local pPlayer = Players[pClan:GetOwner()]
local pPlot = pClan:Plot()
local sKeyPrefix = "TXT_KEY_MORINDIM_CLAN_"
AnalysePlots(pPlot)
-- Randomise the order of looking for specific features/terrains
local nameFunctions = GetShuffledCopyOfTable(namingFunctions)
-- Add the "catch all" functions to the end of the table
table.insert(nameFunctions, PlainsNames)
table.insert(nameFunctions, GrassNames)
table.insert(nameFunctions, GenericNames)
-- Call each function in turn ...
for _, fnNaming in ipairs(nameFunctions) do
sNameKey = fnNaming(pPlot)
-- ... did we get a possible name type, if so ...
if (sNameKey) then
-- ... find all possible settlement names from the key ...
local sQuery = "SELECT Tag FROM Language_en_US WHERE Tag LIKE '" .. sKeyPrefix .. sNameKey .. "%' ORDER BY RANDOM();"
-- ... for each possible settlement name ...
for row in DB.Query(sQuery) do
local sNewClanName = Locale.ConvertTextKey(row.Tag)
-- ... if the name is available, use it
if (pPlayer:IsCityNameValid(sNewClanName)) then
pClan:SetName(row.Tag)
return
end
end
end
end
-- Just keep the game assigned name
end
The database entries for the settlement names look like
Code:
<Row Tag="TXT_KEY_MORINDIM_CLAN_RANGE_1">
<Text>Jagged Peaks Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RANGE_2">
<Text>Snowy Ranges Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RANGE_3">
<Text>Hidden Pass Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PEAK_1">
<Text>Grey Mountain Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PEAK_2">
<Text>Tooth Peak Clan</Text>
</Row>
and we can add more simply by incrementing the final number, eg
Code:
<Row Tag="TXT_KEY_MORINDIM_CLAN_PEAK_3">
<Text>Spirit Top Clan</Text>
</Row>
Note: pPlayer:IsCityNameValid(sNewName) is a Lua API extension in my DLL, it's effects can be duplicated in Lua by looping all the player's cities (for pCity in pPlayer:Cities() do ... end) and checking their name against sNewName (if (pCity:GetName() == sNewName) then return false end)
So why bother with "GENERIC", why not just keep any pre-assigned name from the DLL? We almost certainly need more than four settlement names for the "any other situation" cases, but if we put more than four plus the capital into the <Civilization_CityNames> table there is a chance that the Huns will "borrow" them (they only take from the sixth name onwards - hard-coded in the DLL) - and a Hunnic city called "Red Fox Clan" is probably immersion breaking!
As a working example, the complete Lua code and XML entries for the Morindim Clans follows
Spoiler :
Code:
--
-- Pick the name of the clan based on the surrounding terrain/features
--
include("MapmakerUtilities")
include("PlotIterators")
include("MorindimUtils")
local allPlots = {}
local ring1Plots = {}
local ring2Plots = {}
local allTerrains = {}
local ring1Terrains = {}
local ring2Terrains = {}
local allFeatures = {}
local ring1Features = {}
local ring2Features = {}
function GetCount(array, index)
return array[index] or 0
end
function IncCount(singleArray, combinedArray, index)
singleArray[index] = (singleArray[index] or 0) + 1
combinedArray[index] = (combinedArray[index] or 0) + 1
end
local namingFunctions = {}
-- River
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (pPlot:IsRiver()) then
sNameKey = "RIVER"
end
return sNameKey
end)
-- Lakes
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
if (pAdjacentPlot and pAdjacentPlot:IsLake()) then
return "LAKE"
end
end
return sNameKey
end)
-- Coast
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
if (pAdjacentPlot and pAdjacentPlot:IsWater() and not pAdjacentPlot:IsLake()) then
return "COAST"
end
end
return sNameKey
end)
-- Mountains
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(allPlots, PlotTypes.PLOT_MOUNTAIN) >= 3) then
sNameKey = "RANGE"
elseif (GetCount(ring1Plots, PlotTypes.PLOT_MOUNTAIN) == 1) then
sNameKey = "PEAK"
end
return sNameKey
end)
-- Hills
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(ring1Plots, PlotTypes.PLOT_HILLS) > 0) then
if (GetCount(ring1Plots, PlotTypes.PLOT_HILLS) >= 3 or GetCount(ring2Plots, PlotTypes.PLOT_HILLS) >= 5) then
sNameKey = "HILLS"
end
end
return sNameKey
end)
-- Marsh
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(ring1Features, GameInfoTypes.FEATURE_MARSH) >= 1 or GetCount(ring2Features, GameInfoTypes.FEATURE_MARSH) >= 2) then
sNameKey = "MARSH"
end
return sNameKey
end)
-- Tundra
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
-- Out of six ring1 plots need two or more, or from all 18 plots, need 6 or more
if (GetCount(ring1Terrains, GameInfoTypes.TERRAIN_TUNDRA) >= 2 or GetCount(allTerrains, GameInfoTypes.TERRAIN_TUNDRA) >= 6) then
sNameKey = "TUNDRA"
end
return sNameKey
end)
-- Snow/Ice
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
-- Out of six ring1 plots need two or more, or from all 18 plots, need 6 or more
if ((GetCount(ring1Terrains, GameInfoTypes.TERRAIN_SNOW) + GetCount(ring1Features, GameInfoTypes.FEATURE_ICE)) >= 2
or (GetCount(allTerrains, GameInfoTypes.TERRAIN_SNOW) + GetCount(allFeatures, GameInfoTypes.FEATURE_ICE)) >= 6) then
sNameKey = "SNOW"
end
return sNameKey
end)
-- Forest
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(ring1Features, GameInfoTypes.FEATURE_FOREST) >= 2) then
sNameKey = "FOREST"
end
return sNameKey
end)
-- Jungle
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(ring1Features, GameInfoTypes.FEATURE_JUNGLE) >= 3) then
sNameKey = "JUNGLE"
end
return sNameKey
end)
-- Desert
table.insert(namingFunctions, function(pPlot)
local sNameKey = nil
if (GetCount(ring1Terrains, GameInfoTypes.TERRAIN_DESERT) >= 3) then
sNameKey = "DESERT"
end
return sNameKey
end)
-- Plains
function PlainsNames(pPlot)
local sNameKey = nil
if (GetCount(ring1Terrains, GameInfoTypes.TERRAIN_PLAINS) >= 3) then
sNameKey = "PLAINS"
end
return sNameKey
end
-- Grasslands
function GrassNames(pPlot)
local sNameKey = nil
if (GetCount(ring1Terrains, GameInfoTypes.TERRAIN_GRASS) >= 3) then
sNameKey = "GRASS"
end
return sNameKey
end
-- Generic
function GenericNames(pPlot)
return "GENERIC"
end
function AnalysePlots(pPlot)
allPlots = {}
ring1Plots = {}
ring2Plots = {}
allTerrains = {}
ring1Terrains = {}
ring2Terrains = {}
allFeatures = {}
ring1Features = {}
ring2Features = {}
for pRingPlot in PlotRingIterator(pPlot, 1) do
IncCount(ring1Plots, allPlots, pRingPlot:GetPlotType())
IncCount(ring1Terrains, allTerrains, pRingPlot:GetTerrainType())
IncCount(ring1Features, allFeatures, pRingPlot:GetFeatureType())
end
for pRingPlot in PlotRingIterator(pPlot, 2) do
IncCount(ring2Plots, allPlots, pRingPlot:GetPlotType())
IncCount(ring2Terrains, allTerrains, pRingPlot:GetTerrainType())
IncCount(ring2Features, allFeatures, pRingPlot:GetFeatureType())
end
end
function SetClanName(pClan)
local pPlayer = Players[pClan:GetOwner()]
local pPlot = pClan:Plot()
local sKeyPrefix = "TXT_KEY_MORINDIM_CLAN_"
-- Handle Natural Wonders as a special case
for pAreaPlot in PlotAreaSpiralIterator(pPlot, 2) do
if (pAreaPlot:IsNaturalWonder()) then
local sNwNameKey = sKeyPrefix .. string.sub(GameInfo.Features[pAreaPlot:GetFeatureType()].Type, 9)
if (Locale.HasTextKey(sNwNameKey)) then
local sNewClanName = Locale.ConvertTextKey(sNwNameKey)
-- If the clan name is available, use it
if (pPlayer:IsCityNameValid(sNewClanName)) then
pClan:SetName(sNwNameKey)
return
end
end
-- Only going to be one natural wonder in range
break
end
end
-- Base the clan name on the terrain/features of surrounding plots
AnalysePlots(pPlot)
local nameFunctions = GetShuffledCopyOfTable(namingFunctions)
table.insert(nameFunctions, PlainsNames)
table.insert(nameFunctions, GrassNames)
table.insert(nameFunctions, GenericNames)
for _, fnNaming in ipairs(nameFunctions) do
sNameKey = fnNaming(pPlot)
if (sNameKey) then
-- Find all possible clan names from the key
local sQuery = "SELECT Tag FROM Language_en_US WHERE Tag LIKE '" .. sKeyPrefix .. sNameKey .. "%' ORDER BY RANDOM();"
for row in DB.Query(sQuery) do
local sNewClanName = Locale.ConvertTextKey(row.Tag)
-- If the clan name is available, use it
if (pPlayer:IsCityNameValid(sNewClanName)) then
pClan:SetName(row.Tag)
return
end
end
end
end
-- Just keep the game assigned name
end
function OnSetPopulation(iPlotX, iPlotY, iOldPop, iNewPop)
-- Has this city just been founded
if (iOldPop == 0) then
local pCity = Map.GetPlot(iPlotX, iPlotY):GetPlotCity()
if (IsMorindimPlayer(pCity:GetOwner()) and (Players[pCity:GetOwner()]:GetCapitalCity() ~= nil)) then
SetClanName(pCity)
end
end
end
GameEvents.SetPopulation.Add(OnSetPopulation)
Code:
<GameData>
<Civilization_CityNames>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_GREAT_SPIRIT</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_1</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_2</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_3</CityName>
</Row>
<Row>
<CivilizationType>CIVILIZATION_MORINDIM</CivilizationType>
<CityName>TXT_KEY_MORINDIM_CLAN_4</CityName>
</Row>
<!-- Do NOT add any more clan names here, or the Huns will "borrow" them! Instead add to the generic list below. -->
</Civilization_CityNames>
<Language_en_US>
<!-- Spirit (special) clans -->
<Row Tag="TXT_KEY_MORINDIM_CLAN_GREAT_SPIRIT">
<Text>Great Spirit Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_EARTH">
<Text>Earth Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_SKY">
<Text>Sky Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_WATER">
<Text>Water Clan</Text>
</Row>
<!-- Clans named after local Natural Wonders -->
<Row Tag="TXT_KEY_MORINDIM_CLAN_VOLCANO">
<Text>Fire Mountain Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_ULURU">
<Text>Uluru Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_CRATER">
<Text>Crater Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_MESA">
<Text>Flat Peak Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_REEF">
<Text>White Water Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GEYSER">
<Text>Smoking Water Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_POTOSI">
<Text>Silver Mountain Clan</Text>
</Row>
<!-- Clans named after terrain/features -->
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_1">
<Text>Salmon River Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_2">
<Text>Foaming Water Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_3">
<Text>Thundering Water Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_4">
<Text>Yellow River Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_5">
<Text>Forked River Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_6">
<Text>Grey River Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_7">
<Text>Beaver Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_8">
<Text>Wild Rapids Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_9">
<Text>Snake River Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_10">
<Text>Water Mist Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RIVER_11">
<Text>Red Gorge Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RANGE_1">
<Text>Jagged Peaks Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RANGE_2">
<Text>Snowy Ranges Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_RANGE_3">
<Text>Hidden Pass Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PEAK_1">
<Text>Grey Mountain Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PEAK_2">
<Text>Tooth Peak Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PEAK_3">
<Text>Spirit Top Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_HILLS_1">
<Text>Barren Hills Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_HILLS_2">
<Text>Stone Hills Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_HILLS_3">
<Text>Brown Goat Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_HILLS_4">
<Text>Gold Hills Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_HILLS_5">
<Text>Broken Pass Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_HILLS_6">
<Text>Blue Cliffs Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_LAKE_1">
<Text>Bear Lake Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_LAKE_2">
<Text>Still Lake Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_LAKE_3">
<Text>Trout Lake Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_LAKE_4">
<Text>Black Lake Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_COAST_1">
<Text>Endless Lake Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_COAST_2">
<Text>Tall Dunes Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_COAST_3">
<Text>Blue Kelp Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_COAST_4">
<Text>Sea Otter Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_COAST_5">
<Text>Red Crab Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_MARSH_1">
<Text>Black Swamp Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_MARSH_2">
<Text>Great Marsh Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_MARSH_3">
<Text>Misty Marsh Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_MARSH_4">
<Text>Caymen Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_MARSH_5">
<Text>Rushes Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_1">
<Text>Tall Pines Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_2">
<Text>Dark Forest Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_3">
<Text>Tawny Owl Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_4">
<Text>Wolverine Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_5">
<Text>Mighty Redwood Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_6">
<Text>Copse Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_FOREST_7">
<Text>Lone Tree Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_JUNGLE_1">
<Text>Creeper Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_JUNGLE_2">
<Text>Twisting Vine Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_JUNGLE_3">
<Text>Panther Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_DESERT_1">
<Text>Sidewinder Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_DESERT_2">
<Text>Scorpion Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_TUNDRA_1">
<Text>Moose Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_TUNDRA_2">
<Text>North Slope Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_TUNDRA_3">
<Text>Snow Fox Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_SNOW_1">
<Text>Aurora Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_SNOW_2">
<Text>Walrus Clan</Text>
</Row>
<!-- Plains/Grasslands clan names -->
<Row Tag="TXT_KEY_MORINDIM_CLAN_PLAINS_1">
<Text>Red Deer Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PLAINS_2">
<Text>Jumping Spider Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PLAINS_3">
<Text>Autumn Geese Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_PLAINS_4">
<Text>Red Fox Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GRASS_1">
<Text>Eagle Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GRASS_2">
<Text>Wild Fire Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GRASS_3">
<Text>Summer Swallow Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GRASS_4">
<Text>Golden Grass Clan</Text>
</Row>
<!-- Generic clans -->
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_1">
<Text>Kestrel Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_2">
<Text>Night Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_3">
<Text>Elk Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_4">
<Text>Wolf Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_5">
<Text>Weasel Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_6">
<Text>Bison Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_7">
<Text>Whispering Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_GENERIC_8">
<Text>Sabre Tooth Clan</Text>
</Row>
<!-- Random clans, very unlikely to ever appear! -->
<Row Tag="TXT_KEY_MORINDIM_CLAN_1">
<Text>Musk Ox Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_2">
<Text>Moon Walkers Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_3">
<Text>Cloud Clan</Text>
</Row>
<Row Tag="TXT_KEY_MORINDIM_CLAN_4">
<Text>Bear Clan</Text>
</Row>
<!-- The clanless lands (used when a Magician appears not in a clan) -->
<Row Tag="TXT_KEY_MORINDIM_BAD_LANDS">
<Text>Bad Lands</Text>
</Row>
</Language_en_US>
</GameData>
And the SQL that randomises the 4 clan names in the Civilization_CityNames table - just for good measure!
Code:
-- Create a temp city name table keeping all the primary clan name
CREATE TABLE Civilization_CityNames_Morindim AS
SELECT * FROM Civilization_CityNames
WHERE CityName='TXT_KEY_MORINDIM_CLAN_GREAT_SPIRIT'
ORDER BY rowid DESC LIMIT 1;
-- Copy the remaining cities to the temp table in a random order
INSERT INTO Civilization_CityNames_Morindim
SELECT * FROM Civilization_CityNames
WHERE CivilizationType='CIVILIZATION_MORINDIM' AND CityName<>'TXT_KEY_MORINDIM_CLAN_GREAT_SPIRIT'
ORDER BY RANDOM();
-- Copy everything back from the temp table, keeping the same order, and then tidy up after ourselves
DELETE FROM Civilization_CityNames WHERE CivilizationType='CIVILIZATION_MORINDIM';
INSERT INTO Civilization_CityNames SELECT * FROM Civilization_CityNames_Morindim ORDER BY rowid ASC;
DROP TABLE Civilization_CityNames_Morindim;