RiHatz Founding City Mod

Ri-Hatz

Chieftain
Joined
Jun 23, 2008
Messages
93
This mod is all about smaller empires and slower expansion!

It simply charges you gold for founding a city.
For every new city you want to found you have to pay exponentialy more gold.

At first I thought: "this only slows down the research", but that is not true!
For example, if your economy is not well developed you would have to pause research for a loooong time to get your 5th or 6th city.
So sticking with fewer cities for a longer time becomes the better strategie with this mod.

edit: Here is the dll plus an xml file for the button text which goes into Assets\XML\Text.
View attachment 195074 edit2: this is version 2!

This is the second AI compatible version. Lots of balancing is still needed.
Everything should work as intended though.
Feel free to test and give feedback here.

RiHatz

note:
I have very little time to mod or play civ right now, so this mod is on pause until next year.
Here's a rar containing the changed code files: View attachment source files.rar

Many thanks go to Xienwolf! Without the help I would still be scrolling through the code...
 
Sounds like a great idea. Perhaps distance from capitol could also have some effect on the cost. Cities built next to the capitol would not cost that much, but founding a "colony" on another continent would be much more expensive.

As far as AI issues go, I haven't a cue how to mod anything like that.
 
Afer endlessly scrolling through the source code Im more and more lost.

The problems I have right now:
1. - The button for founding a city disappears when the player has not enough gold. It should be darkened and not clickable with the text displaying for not beeing able to found a city (like if too close to another city)

2. - Is there a way for adding the amount of gold for founding a city to display in the help text when mouseovering the found city button? It cant be a static text. 'cause the amount changes with more and more cities.

I got this help allready:
The function you want is CvDLLWidgetData::parseActionHelp, which is HUGE, so search for MISSION_FOUND. It currently only gives a text output for mouseover if you can NOT found a city, and leaves the key for when you CAN found a city to whatever is in the <Help> field only.

so I know how to change the text for the NOT beeing able to found part, but Im confused about that <Help> field, which I cannot find.

3. - The AI not saving gold for founding cities
The Better BTS AI has some functions for letting the Ai save gold.
I will be looking there for some help and insight.

Especially nice would be:
Let some evil civs go on a pillage tour to get the gold instead of saving for it!
Would be appropriate for the Clan or Lanun, etc.
Furthermore tweak the ai for making peace easier after reaching its goal (enough gold)
 
Maybe you could you make the cost in hammers for each settler increase exponentially - that might solve your gold problem with the AI.

Really applaud this modmod intentions and will keep checking here / salute
 
here is a very specific question about some code: (in CvDLLWidgetData.cpp)
Code:
			if (!isEmpty(GC.getMissionInfo((MissionTypes)(GC.getActionInfo(widgetDataStruct.m_iData1).getMissionType())).getHelp()))
			{
				szBuffer.append(CvWString::format(L"%s%s", NEWLINE, GC.getMissionInfo((MissionTypes)(GC.getActionInfo(widgetDataStruct.m_iData1).getMissionType())).getHelp()).c_str());

				//RiHatz
				if (GC.getActionInfo(widgetDataStruct.m_iData1).getMissionType() == MISSION_FOUND)
				{	
					iPrice = DetermineCityFoundPrice([COLOR="Red"]GET_PLAYER(getOwnerINLINE()).getNumCities())[/COLOR];
					szBuffer.append(NEWLINE);
					szBuffer.append(gDLL->getText("The costs for Founding your new City will be ", [COLOR="Red"]iPrice[/COLOR]));
				}
				//RiHatz End
			}

Except for the red lines of code this works.
It adds a line of text to the found city button.
the .getNumCities function probaly doesnt work in this file 'cause of missing references to it?

now the part about the iPrice. Of course the price has to change according to the number of cities the player has. That doesnt work like in the above written code.
Any help on that?
 
To get the button visible, you have to modify CvPlayer::canFound. Anything contained inside the if (!bTestVisible) comment will keep you from being able to found a city, but still allow the button to be displayed. I assume that your gold requirement is already in this function, so you just have to include that IF statement before returning false.


To get the AI to save money, you have to do some tricky work in CvPlayerAI::AI_goldTarget. Just try to write an equation for how you would personally decide when to save money and make sure that you make the AI do about the same. Programming AI is a long process, so just make your best guess, then run an AI Autoplay game and watch the AI to see if they play "intelligently" in regards to settling. Once they manage to do a fair job, then run it 10 more times to make sure it wasn't a fluke.

For the actual text key on your button, you want something like this:
change:
Code:
			else if (GC.getActionInfo(widgetDataStruct.m_iData1).getMissionType() == MISSION_FOUND)
			{
				if (!(GET_PLAYER(pHeadSelectedUnit->getOwnerINLINE()).canFound(pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE())))
				{
					bValid = true;

					iRange = GC.getMIN_CITY_RANGE();

					for (iDX = -(iRange); iDX <= iRange; iDX++)
					{
						for (iDY = -(iRange); iDY <= iRange; iDY++)
						{
							pLoopPlot	= plotXY(pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE(), iDX, iDY);

							if (pLoopPlot != NULL)
							{
								if (pLoopPlot->isCity())
								{
									bValid = false;
								}
							}
						}
					}

					if (!bValid)
					{
						szBuffer.append(NEWLINE);
						szBuffer.append(gDLL->getText("TXT_KEY_ACTION_CANNOT_FOUND", GC.getMIN_CITY_RANGE()));
					}
					else
					{
						szBuffer.append(NEWLINE);
						szBuffer.append(gDLL->getText("TXT_KEY_ACTION_CANNOT_FOUND", GC.getMIN_CITY_RANGE()));
					}
				}
			}

to

Code:
			else if (GC.getActionInfo(widgetDataStruct.m_iData1).getMissionType() == MISSION_FOUND)
			{
				if (!(GET_PLAYER(pHeadSelectedUnit->getOwnerINLINE()).canFound(pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE())))
				{
					bValid = true;

					iRange = GC.getMIN_CITY_RANGE();

					for (iDX = -(iRange); iDX <= iRange; iDX++)
					{
						for (iDY = -(iRange); iDY <= iRange; iDY++)
						{
							pLoopPlot	= plotXY(pMissionPlot->getX_INLINE(), pMissionPlot->getY_INLINE(), iDX, iDY);

							if (pLoopPlot != NULL)
							{
								if (pLoopPlot->isCity())
								{
									bValid = false;
								}
							}
						}
					}
					
					if (!bValid)
					{
						szBuffer.append(NEWLINE);
						szBuffer.append(gDLL->getText("TXT_KEY_ACTION_CANNOT_FOUND", GC.getMIN_CITY_RANGE()));
					}

[COLOR="#ff8c00"]					if ([COLOR="#00ffff"]GET PLAYERS GOLD VALUE HERE[/COLOR]) < ([COLOR="#00ffff"]GET REQUIRED GOLD VALUE HERE[/COLOR])
					{
						szBuffer.append(NEWLINE);
						szBuffer.append(gDLL->getText("TXT_KEY_ACTION_CANNOT_AFFORD_FOUND", [COLOR="#00ffff"]GET REQUIRED GOLD VALUE HERE[/COLOR]));
					}
[/COLOR]				}
[COLOR="DarkOrange"]				else
				{
                    szBuffer.append(NEWLINE);
                    szBuffer.append(gDLL->getText("TXT_KEY_ACTION_PAY_FOR_FOUND", [COLOR="Cyan"]GET REQUIRED GOLD VALUE HERE[/COLOR]));
				}
[/COLOR]			}


The sections in orange are what you need to add here (after making sure that the button stays visible when you cannot afford to found). And of course the sections I put in blue need to be actual function calls instead of just a description

EDIT: Had written all of this a few hours ago, but the forums weren't working for me. In regards to your recent question, red doesn't show up for me in the forums since I use a black skin version ;) But by quoting your post I could see what was red.

Where did you invent the function DetermineCityFoundPrice? I assume you made CvDLLWidgetData::DetermineCityFoundPrice, but you should have made CvPlayer::DetermineCityFoundPrice instead. Then you could just call for that function to get your appropriate price. getOwnerINLINE() cannot be used in the Widget data, because nobody OWNS the widget. You have to figure out another trick to decide which player you are dealing with. Your best bet is to ask who owns pSelectedUnit since that is one of the variables already established for the function.
 
Wow! Thanks for the detailed help.

I just wrote the function DetermineCityFoundPrice at the beginnig of all the files I need it in. (CvDLLWidgetData, CvPlayer and CvUnit)
For me proper programming comes after working solutions ;)

Your help worked. Now the button gets dark when not enough gold and the texts are displayed accordingly.
Im using the following in WidgetData: GET_PLAYER(pHeadSelectedUnit->getOwnerINLINE()

In your example I saw this line:
szBuffer.append(gDLL->getText("TXT_KEY_ACTION_PAY_FOR_FOUND", GET REQUIRED GOLD VALUE HERE));

the orange stuff would have to be defined in xml right?
I played a bit around with the files, but the game crashed when loading the map.
Obviously I dont know what Im doing, again.
 
I had a look through CvPlayerAI::AI_goldTarget()
very usefull!

So where or when does the Ai decide IF to found a new city or build a settler?
This decision would have to be tweaked to the number of cities and to the gross gold income.

Then the increased goldTarged has to be set only if the ai decides to settle.
 
For the line:

szBuffer.append(gDLL->getText("TXT_KEY_ACTION_PAY_FOR_FOUND", GET REQUIRED GOLD VALUE HERE));

The TXT_KEY_ACTION_PAY_FOR_FOUND would be defined in XML in your GameText file. The other part is where you would include your result from the DetermineCityFoundPrice function. Then the text key would read something like "Requires %d1 [GOLD_CHAR] to settle a city", the %d1 says to insert a number, and that the number is the first thing that was passed along with the call for the text key to be made (%D would get a number with a + or - sign, %s would get a string, %c would get a symbol. The number indicates in what order things were passed to the text key).


Not sure where/when the AI decides to expand really. Would just search the AI_Player file for any instances of the word found and see what each location is trying to accomplish. Same way I found the gold target (just opened the file and searched for gold)
 
The first version is up and running!

It only works in multiplayer. The ai is not yet able to play with this.

what will come in the next version:
- balancing it out
- (hopefully) ai using this
- greatly reduced founding price for cities if in own cultural borders

heres the CvGameCore.dll
attachment deleted because of new version
 
Holy moly, this sounds extremely interesting! Will keep my eyes on this one, keep up the good work :)
 
Heres something im still having troubles with:

The function "CvCityAI::AI_chooseProduction()" chooses when to build a settler right?

I modified the function "CvPlayer::canFound" so that the button for founding a city gets darkened if not enough gold.

This change alone stops the Ai from building settlers.
If I give Gold to the Ai via the console, it emediately starts building settlers.

So it must be, that somewhere the Ai checks if it wants to build a settler, if that settler, once build, can "press the button" for founding a city.
Now where would that be?


Maybe higher maintenance for total number of cities is easyer to mod.
Anyways, help is, as always, welcome.
 
Looks like the function which is giving you problems now is: CvPlayerAI::AI_foundValue


This value is called quite often (AI decisions, normalizing/assigning starting plots.... lots of things), and will always return a 0 if the player states he cannot found in a certain location.

Possibly this can all be solved by moving your gold cost to CvUnit::canFound instead. As this one is not checked by nearly as many locations in the code.
 
wow, can't wait for the AI compatible version to be up and running =)

I've tried just increasing number of cities maintenance before, and it doesn't have your desired effect. Rather, it just makes city states a compulsory civic. Your fix is much more original and interesting :D
 
about the AI stuff:

I got it so that the AI reduces research when having or building a settler until having the gold to found a new city.
Without slowing down the AI in building settlers this results in slower research but nothing else.

So im telling the AI now when it is clever to build a settler based upon the gold cost of the soon-to-found city and the AI's commerce.
Just for reference, this is how my city found costs are now:
Spoiler :
first city: 0 gold.
75
180
345
600
975
1500
2205
3120
4275
5700
7425
twelfth city: 9480 gold.


So saving gold for the tenth city or so early in the game would result in many many turns with low research.
This is where me telling the computer: "Don't!" comes is.

I found different functions:
Code:
int iNetCommerce = 1 + getCommerceRate(COMMERCE_GOLD) + getCommerceRate(COMMERCE_RESEARCH) + std::max(0, getGoldPerTurn());
int iNetExpenses = calculateInflatedCosts() + std::min(0, getGoldPerTurn());
int iFundedPercent = (100 * (iNetCommerce - iNetExpenses)) / std::max(1, iNetCommerce);
------------------------------
AI_isFinancialTrouble()
------------------------------
getCommercePercent(COMMERCE_GOLD)  // or COMMERCE_RESEARCH
------------------------------
getGoldPerTurn()
------------------------------
int CvPlayer::calculateTotalCommerce() const
{
	int iTotalCommerce = calculateBaseNetGold() + calculateBaseNetResearch();

	for (int i = 0; i < NUM_COMMERCE_TYPES; ++i)
	{
		if (COMMERCE_GOLD != i && COMMERCE_RESEARCH != i)
		{
			iTotalCommerce += getCommerceRate((CommerceTypes)i);
		}
	}

	return iTotalCommerce;
}

I made a copy of AI_isFinancialTrouble() and tried to modify it to my needs.
What gives me problems is that I cant do a debug build, means i dont know what those functions return for numbers. And balancing unknown numbers is rather difficult. I somehow have to compare my CityFoundPrice against the players economy.
Does anyone know how to handle these??? ( iFundedPercent and calculateTotalCommerce(),
getCommercePercent and getGoldPerTurn() is obvious.
Furthermore I need to know where the AI decides to upgrade, 'cause upgrading units has to stop if AI has a settler.

Well, I think I can admit that this mod has become a "me asking and xienwolf solving mod"
This is totaly fine with me! ;) many thanks at this point xienwolf.
 
You are quite out of the areas of the code I am well familiar with, so for the moment I'd have to poke around to find anything, and I haven't any spare time this week. So best of luck with looking about and/or hopefully someone else has a few words of wisdom before I get another chance to be of assistance :)
 
Ri-Hatz, been tracking your progress and really looking forward to trying this out. Couple of questions, ideas:

Does the cost of city founding scale with map size?
Would it simpler for the AI to handle if you scale up the hammer cost of each settler during building?
Have you thought about using a timed method - there is an example here: http://forums.civfanatics.com/showthread.php?t=243516&highlight=timed+settler

anyway, sorry i cant offer advice on coding but good luck :)
 
New AI compatible Version

heres it: edit: attachment deleted. smaler file in my next post

This also includes an updated CIV4GameText_FFH2.xml needed for the button text.

I just ran a few tests with:

-small map
-quick speed
-3 players
-always peace
-no barbarians, othus, infernals, etc.
-autoplay up till turn 200

Results:
with the original dll all the landmass was settled. up to 13 cities a civ.
with the modded dll 6 cities were the average with more landmass to settle on.
(i used the initial autosave and loaded it with the different dlls. i hope this is a correct way of testing. please correct me if im doing a mistake here)

A LOT of balancing is still needed!!!

Especially there is a found value for unsettled land (i think). And in the next version if this found value is high enough the AI will settle regardless (up to a certain degree) of the founding costs.


the city costs dont scale with map size yet. About the increase of hammer cost for settler: interesting!
A timed method is just not right imo.
 
How to test always depends on what you changed. Many changes break savegames, so such a test wouldn't be possible. But for what you are looking at, that is a nice way to watch it. You ran AIAutoPlay to do the test I assume?


As for your text file, don't overwrite Kael's, it is HUGE and makes your download take longer, plus forces you to update each time he releases something. Create a CIV4GameText_WHATEVER.xml file in the same location. Gametext files are special in that you can create a new file and the DLL will properly load everything from it.
 
Back
Top Bottom