View Full Version : City names


AZSportsFan
Oct 09, 2008, 12:21 AM
From Hydro Plant thread...


Illuminatus, who has offered to do some text and civilopedia work, has inquired about the possibility of having a seperate list of names for sea bases, as existed in SMAC. I've had a look at the SDK myself to see how city names are assigned. The central function seems to be "CvWString CvPlayer::getNewCityName() const". The way to make sea base names possible seems obvious: add a seperate list of them in CIV4CivilizationInfos.xml. Then in the above-mentioned function check if the new city is built on water. If so, extract a name from the sea base name list. If the base is on land or if the list of sea base names has been already fully used, switch to the list of land base names and the rest that follows in that function.

Problem however is, I have no idea how the list of city names is created!!
The function talks about headCityNameNode()
This leads to "CLLNode<CvWString>* CvPlayer::headCityNameNode() const" and a certain thingie called "m_cityNames". Problem here is, I have no idea whatsoever how content is added to this m_cityNames (I assume) list. That string is only referenced in CvPlayer.cpp.

Likewise, the list of city names is extracted from the XML in CvInfos.cpp, resulting in stuff like 'm_paszCityNames', but that is referenced nowhere outside CvInfos.cpp.

So I don't know... do you understand how city names are assigned?


It looks to be pretty straightforward. As you have said, the city names are defined in CIV4CivilizationInfos.xml. We would need a new element on the civ for SeaBases. The SeaBases are read into CvCivilizationInfo class in CvInfos, where they become available through a new function getSeaBaseNames(int i).

In CvPlayer, when a new city is founded, getNewCityName is called, which first goes through the list of names already used by the player (this may be just "run-time player-defined" city names which it will try to reuse if available, but I am not positive) to see if any are not current city names, and if one is not found (generally the case), getCivilizationCityName() is called to get a random name from the civ city list until it finds one that is not currently in use. If it still doesn't find one, then it tries to get one from a random civ, and so one.

So we would need to do a few things - add a variable for run-time player-defined sea base names, and civ-defined sea base names, determine whether or not the plot is land or sea, and then select a name accordingly. Straightforward in algorithm, but I will see about implementation.

I will try this out when I get some time later this week.

Maniac
Oct 09, 2008, 11:26 AM
double post

Oh wait, I'll recycle it for this question:

What does "%" mean in C++ ?
As in:
int iLoopName = ((iI + iRandOffset) % GC.getNumCivilizationInfos());

I don't think I've encountered that before.

Maniac
Oct 09, 2008, 11:27 AM
I haven't got a clue what run-time player-defined stuff is, so I guess I'll leave it in your capable hands. :mischief:

In the coming "patch i" I have already added the required XML code though for your convenience, and a small list of Peacekeeper sea base names.

Btw, be sure to download the coming public patch as well, despite using SVN. You'll need it for the updated compiled DLL, and the Art and Sounds folders.

AZSportsFan
Oct 09, 2008, 03:58 PM
I haven't got a clue what run-time player-defined stuff is, so I guess I'll leave it in your capable hands. :mischief:

In the coming "patch i" I have already added the required XML code though for your convenience, and a small list of Peacekeeper sea base names.

Btw, be sure to download the coming public patch as well, despite using SVN. You'll need it for the updated compiled DLL, and the Art and Sounds folders.

When a new city is formed, you get a chance to change the name to something that you want - right? So instead of "Washington" you can name it "Waco", for instance. That is what I am calling a "player-defined" name, and it is done at "run-time" and not in an XML file.

I will grab that XML as a start - may not get to it until the weekend.

AZSportsFan
Oct 09, 2008, 04:10 PM
double post

Oh wait, I'll recycle it for this question:

What does "%" mean in C++ ?
As in:
int iLoopName = ((iI + iRandOffset) % GC.getNumCivilizationInfos());

I don't think I've encountered that before.

It means modulo, which is the remainder of an integer division operation. 5%3 = 2. In this case it is a way to make sure that the result is not greater that getNumCivilizationInfos().

"%" is pretty common for most modern languages (C, C++, C#, Java, Python...)

Maniac
Oct 09, 2008, 04:12 PM
Ah thanks!

AZSportsFan
Oct 11, 2008, 01:13 AM
When the player object is asked for a city name, nothing is passed regarding the city or the plot. I am not sure how I can tell what the current city plot is. Any ideas? Is there a global "get the current plot" method? This function (CvPlayer::getNewCityName()) is pretty generic.

Maniac
Oct 11, 2008, 09:00 AM
Ah, I hadn't thought of that.
I don't think there's a "get the current plot" method.

HOWEVER

I see the only place where the getNewCityName function is used, is on city initialization. And there, the future city plot is defined.
See:

void CvCity::init(int iID, PlayerTypes eOwner, int iX, int iY, bool bBumpUnits, bool bUpdatePlotGroups)
{
CvPlot* pAdjacentPlot;
CvPlot* pPlot;
BuildingTypes eLoopBuilding;
int iI;

pPlot = GC.getMapINLINE().plotINLINE(iX, iY);

...

setName(GET_PLAYER(getOwnerINLINE()).getNewCityNam e());

So you could determine there whether the plot is water or not, and then include that information in the getNewCityName call. I'm just writing this on the spot, so I'm not sure my C++ "spelling" is correct, but the general idea could look something like this:

bWater = false;

pPlot = GC.getMapINLINE().plotINLINE(iX, iY);

if (pPlot->isWater())
bWater = true;

...

setName(GET_PLAYER(getOwnerINLINE()).getNewCityNam e(bWater));

And then in the actual getNewCityName function, you say:
if (bWater)
then draw some name from the water base name list, however that happens. :scared:
else
if not bWater or if the sea base name list is exhausted, do the rest of the function.

Of course in
CvWString CvPlayer::getNewCityName() const
and the corresponding header in CvPlayer.h "bool bWater" should be included between the brackets or something.

AZSportsFan
Oct 11, 2008, 10:20 AM
Right, I can add to the API easily enough, but these methods are exposed such that I cannot (and would not - "Bad Design 101") just replace the existing interface. That just means it may not be reliable getting names if Firaxis/other modders call the original getNewCityName method. That is why I asked the question, because I didn't see a way to get at "the city we are trying to name" within the getNewCityName method and wanted to make sure that I didn't overlook something.

OK, I will add an additional method taking the city itself as a parameter. I think that is more robust than just passing bWater, as perhaps we may want to use it for something else in the future (plot elevation, coastal vs ocean, surrounding terrain, etc). Luckily the city is initialized before the new name is queried.

Maniac
Oct 11, 2008, 01:04 PM
OK, I will add an additional method taking the city itself as a parameter.

Hmm, I don't understand what you mean. I guess I'll see.
For the record though, regarding not replacing the existing interface, that may already be a lost cause. A lot of combat-related functions have had extra input added to them to allow psi combat.