Modmodding Q&A Thread

Spoiler :
CvGameTextMgr.cpp said:
Code:
	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isHasTech((TechTypes)CALENDAR))
		setDateStr(szString, iGameTurn, bSave, eCalendar, iStartYear, eSpeed);
	else if (GET_PLAYER(ePlayer).getCurrentEra() >= 3)
		szString = gDLL->getText("TXT_KEY_AGE_RENAISSANCE");
	else if (GET_PLAYER(ePlayer).getCurrentEra() == 2)
		szString = gDLL->getText("TXT_KEY_AGE_MEDIEVAL");
	else if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isHasTech((TechTypes)IRONWORKING))
		szString = gDLL->getText("TXT_KEY_AGE_IRON");
	else if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isHasTech((TechTypes)BRONZEWORKING))
		szString = gDLL->getText("TXT_KEY_AGE_BRONZE");
	else szString = gDLL->getText("TXT_KEY_AGE_STONE");

This evening, my Japanese class teacher taught me about the early history of the Japanese civilization; from pre-history (Jomon) period to the Yamato period.

(Hmm, how should I word this to make this relevant to DoC and what I want to ask you about...)

In Japan, the Classical Era starts from 538AD to 1185AD, followed by Medieval Era from 1185AD to 1600AD. Then, the Edo period (or we can equate with Renaissance Period in the West) from 1600AD to 1868AD. After that, during Industrial Age in the West, from 1868AD to 1945AD is the Imperial Japan period. Modern Age start after that until now.

Meanwhile, in the Europe, Classical Age start from around 700BC to around 500AD or 600AD; therefore 600AD onward is known (under umbrella term) of Medieval Age. I hope this already illustrate the idea of my question.

Spoiler :
Civ4_Screen_Shot0043.jpg


Back to the code I quoted above, is it possible to make that if the game detect that ePlayer == iJapan, and getCurrentEra() == 2, it loads "TXT_KEY_AGE_ASUKA" instead of "TXT_KEY_AGE_MEDIEVAL" for example? Off course, the text for TXT_KEY_AGE_ASUKA will be defined somewhere. Because, if I just change the <English>Bronze Age</English> in the CIV4GameText_RFCGeneral.xml to <English>Asuka Period</English>, it will change all TXT_KEY_AGE_MEDIEVAL occurrences, not only for Japan, isn't it?

Is it possible?
If yes, can it be made as modules?

Thanks!

EDIT: In order to avoid confusion, my idea is to only change the text. I do not currently wish to find out how to modify the number of eras so that there's only x Era in Japan while there's y Era in France and z Era in Mali -- which will lead to reformation of tech tree, DCN etc which is tied to Renaisssance, Classical, Medieval etc.
No. That's too complicated. I just wish to find out whether it is possible to rename Medieval Era to Asuka Period, Renaissance Era to Edo Period etc if the player choose Japan~
 
I found this guide to merging BUG. How much of this would need to be added to DoAH?
Sorry, I've only realized now that I haven't answered to this as I meant to.

Unfortunately I didn't use this guide to merge BUG into DoC, I just downloaded BUG and used WinMerge, resolving conflicts along the way to the best of my understanding of the code involved (usually it meant choosing BUG code unless RFC specific code was involved).

So I cannot tell you. But generally the problem should be with something you did while creating your mod in the first place, not merging BUG, because BUG was already merged into the version of DoC you branched off of.



This evening, my Japanese class teacher taught me about the early history of the Japanese civilization; from pre-history (Jomon) period to the Yamato period.

(Hmm, how should I word this to make this relevant to DoC and what I want to ask you about...)

In Japan, the Classical Era starts from 538AD to 1185AD, followed by Medieval Era from 1185AD to 1600AD. Then, the Edo period (or we can equate with Renaissance Period in the West) from 1600AD to 1868AD. After that, during Industrial Age in the West, from 1868AD to 1945AD is the Imperial Japan period. Modern Age start after that until now.

Meanwhile, in the Europe, Classical Age start from around 700BC to around 500AD or 600AD; therefore 600AD onward is known (under umbrella term) of Medieval Age. I hope this already illustrate the idea of my question.

Spoiler :
Civ4_Screen_Shot0043.jpg


Back to the code I quoted above, is it possible to make that if the game detect that ePlayer == iJapan, and getCurrentEra() == 2, it loads "TXT_KEY_AGE_ASUKA" instead of "TXT_KEY_AGE_MEDIEVAL" for example? Off course, the text for TXT_KEY_AGE_ASUKA will be defined somewhere. Because, if I just change the <English>Bronze Age</English> in the CIV4GameText_RFCGeneral.xml to <English>Asuka Period</English>, it will change all TXT_KEY_AGE_MEDIEVAL occurrences, not only for Japan, isn't it?

Is it possible?
If yes, can it be made as modules?

Thanks!

EDIT: In order to avoid confusion, my idea is to only change the text. I do not currently wish to find out how to modify the number of eras so that there's only x Era in Japan while there's y Era in France and z Era in Mali -- which will lead to reformation of tech tree, DCN etc which is tied to Renaisssance, Classical, Medieval etc.
No. That's too complicated. I just wish to find out whether it is possible to rename Medieval Era to Asuka Period, Renaissance Era to Edo Period etc if the player choose Japan~
Okay, the code you quoted right at the beginning is about the era displayed in the interface instead of the game date before you have discovered Calendar, which from what I understand is not what you want to change.

If I understand you correctly, you want to basically have civ-specific era names of the eras already existing in the game. As the above example shows, it's definitely possible, although it's probably a bit complicated to do because eras show up in many different places.

Therefore the most efficient way to do it would be to go to the root of it all which is the CvEraInfo class in CvInfos.cpp, which controls everything that eras do, including their name. As you can see, this requires DLL editing and recompilation, so if you're not up for that you can stop reading now.

Other parts of the code get the name of an era from CvEraInfo's getText() method, which is what you have to edit to make it civ specific. This comes with two potential problems, though:

1. CvEraInfo doesn't explicitly define the getText() method, but instead inherits it from its superclass CvInfoBase (read up on class inheritance if this doesn't make any sense to you). You cannot modify getText() in CvInfoBase because that would affect all sorts of other classes that inherit from it, so you need to overwrite getText() in CvEraInfo.

2. CvEraInfo doesn't know the civilization or player that has an era. It just represents the general information about an era (such as its name, soundtrack ...). A specific player knows what era he has, but not the other way around. So that might be tricky. However, what you can always access is the current human player (with GC.getGame().getActivePlayer()). So it would definitely work to change all era names to Asuka Period, Edo Period, Meiji Period etc. when the human player is Japan. Since only the human sees and cares about the text anyway, that wouldn't be a problem.

The rest is only setting up the correct text keys and inserting some appropriate if clauses for them into the new CvEraInfo::getText() method.
 
Thank you for the response.

Trying to follow up summarize; so what I have to do first, is
1) Define a new getText() method special for CvEraInfo (but does not affect the rest of Cvs)
2) Link the getText() method to GC.getGame().getActivePlayer()
3) Add "TXT_KEY_AGE_MEDIEVAL_JAPAN" for example
4) In CIV4GameText_RFCGeneral.xml, add the text for "TXT_KEY_AGE_MEDIEVAL_JAPAN"

Do I get that right? Indeed it looks very complicated; but since this is kinda interesting, I should try this when I have a free time :D
 
Okay, a few minor corrections of things you might have already realized but not explicitly stated there, to prevent you from running into trouble:

1. When you define a getText() method for CvEraInfo, you have already made sure that it isn't used by other classes. CvInfoBase is the class with the getText() method that is used by everyone else, which you cannot edit because you only want to change it for CvEraInfos.

2. All you need to do is call GC.getGame().getActivePlayer() in the new getText() method and then compare the returned value to the player constants.

This is definitely a good exercise to make something non-trivial yet simple happen with the DLL code, so I definitely suggest giving it a go

Edit: I've taken a look at the actual class and realized something else, so to give you some additional help, this is the CvInfoBase::getText() method:
Code:
const wchar* CvInfoBase::getText() const
{
	// used instead of getDescription for Info entries that are not objects
	// so they do not have gender/plurality/forms defined in the Translator system
	if(m_szCachedText.empty())
	{
		m_szCachedText = gDLL->getText(m_szTextKey);
	}

        // your code goes here

	return m_szCachedText;
}
Just copy this for CvEraInfo and recompile to see if it works, additional functionality can always be added later. Any special cases you want to handle go where I've made the comment. This might sound stupid now but an era also doesn't know what era it is (as in, it does know its name, but not the era type). So you have to use m_szCachedText itself to see what era it refers to, i.e. m_szCachedText == "TXT_KEY_ERA_MEDIEVAL" to check whether it is the medieval era and so on.
 
How would I give the Ottoman spawn stack a settler? And would they settle in place if I did?
 
How would I give the Ottoman spawn stack a settler? And would they settle in place if I did?
All the starting units are spawned in the createStartingUnits() method in RiseAndFall.py. Settlers are usually created with the extra method utils.createSettlers() that spawns the number of settlers specified in the argument minus the amount of flipping cities in their core (to give more settlers in case core cities aren't founded or destroyed and vice versa), but you can also just create them like any other unit.

And yeah, they'll use one of these settlers to found their capital on the spot (which is the main reason they don't have any right now). If you want them to have settlers without founding on that spot you might consider to spawn them along with their workers in createStartingWorkers(), the line is even still there in the comments.
 
All the starting units are spawned in the createStartingUnits() method in RiseAndFall.py. Settlers are usually created with the extra method utils.createSettlers() that spawns the number of settlers specified in the argument minus the amount of flipping cities in their core (to give more settlers in case core cities aren't founded or destroyed and vice versa), but you can also just create them like any other unit.

And yeah, they'll use one of these settlers to found their capital on the spot (which is the main reason they don't have any right now). If you want them to have settlers without founding on that spot you might consider to spawn them along with their workers in createStartingWorkers(), the line is even still there in the comments.

I opened the file and did a search. I have no idea what I'm looking at; could you tell me what to do? I just want them to found a city on their spawn tile (it seems very natural- they really should already have one).

Also, I'm not sure how the new stability works. I want to put Ethiopia in the 600AD scenario, but I recall when China collapsed in another game (due to some badly thought out tinkering of mine) and I put them back in using WB, they had a historicity penalty and their stability couldn't get above collapsing. Will this still happen, and if so, how do I avoid it?
 
I opened the file and did a search. I have no idea what I'm looking at; could you tell me what to do? I just want them to found a city on their spawn tile (it seems very natural- they really should already have one).
Have you found createStartingUnits()?

The method has a parameter referring to the civilization that is supposed to receive the units. Let's call it iCiv, it might also be called iPlayer, not sure right now. The method itself is just a huge if clause that checks for all possible values of iCiv. You want to edit Turkey's units, so you go to "elif iCiv == con.iTurkey:" (elif is short for else if in Python). Below that is an indented block containing everything that is supposed to happen in that case. Here, it's a bunch of units being spawned using the utils.makeUnit() method (or maybe utils.makeUnitAI(), but you don't have to worry about that right now).

The makeUnit() method takes a bunch of arguments of which some are always the same, so no need to think about them either. Important are the first and the last argument. The first is the unit you want to spawn, expressed as a unit ID in form of a variable that is defined in Consts.py (variables defined in Consts.py are accessible with the prefix con, which is why they're all con.iArcher and so on). You can look them up there, but usually there is an example already somewhere in that method. But in your case you can simply use con.iSettler. The last argument on the other hand is simply the number of units you want to create.

So all you need to do is to go to Turkey's part of the if clause, copy one of the makeUnit() method calls (make sure to keep the same indentation) and change the parameters to suit your needs, in this case your desired number of settlers.

AIs are hardwired to found their first city on spot if possible, so you don't have to take care of that.

Also, I'm not sure how the new stability works. I want to put Ethiopia in the 600AD scenario, but I recall when China collapsed in another game (due to some badly thought out tinkering of mine) and I put them back in using WB, they had a historicity penalty and their stability couldn't get above collapsing. Will this still happen, and if so, how do I avoid it?
Huh? A historicity penalty sounds like the old stability system.
 
You want to edit Turkey's units, so you go to "elif iCiv == con.iTurkey:"

There are only five civs for which this appears, and none of them are Turkey.

From my search:
Line 1455: elif iCiv == iPersia:
Line 1463: elif iCiv == iAztecs:
Line 1475: elif iCiv == iMaya:
Line 1754: elif iCiv == iThailand:
Line 3103: elif iCiv == iBrazil:
 
There might be brackets, just search the method name and scroll down.
 
I have no idea what you're talking about. You're overestimating my ability to comprehend this stuff. Just highlight the line I'm supposed to change.

Huh? A historicity penalty sounds like the old stability system.

That's what I meant. I saw the historicity penalty before switching to SVN. So I assume it won't still be there?
 
I have no idea what you're talking about. You're overestimating my ability to comprehend this stuff. Just highlight the line I'm supposed to change.
It seems more like before, the only thing I've overestimated is your willingness to make an effort.

The point of this thread isn't to ask me bit by bit to do your stuff for you.

That's what I meant. I saw the historicity penalty before switching to SVN. So I assume it won't still be there?
The penalty as such doesn't exist anymore, yes.
 
Just highlight the line I'm supposed to change.

In Python/RiseAndFall.py, the following lines appear:
Code:
	if iCiv == iPoland:
		utils.createSettlers(iCiv, 2)
		utils.makeUnitAI(con.iLongbowman, iCiv, tPlot, UnitAITypes.UNITAI_CITY_DEFENSE, 2)
		utils.makeUnit(con.iCrossbowman, iCiv, tPlot, 2)
		utils.makeUnit(con.iMaceman, iCiv, tPlot, 1)
		if utils.getHumanID() != iPoland:
			utils.makeUnit(con.iPikeman, iCiv, tPlot, 2)
		utils.makeUnit(con.iChristianMissionary, iCiv, tPlot, 1)
        if (iCiv == iTurkey):
                utils.makeUnit(con.iLongbowman, iCiv, tPlot, 2)
                utils.makeUnit(con.iCrossbowman, iCiv, tPlot, 3)
                utils.makeUnit(con.iHorseArcher, iCiv, tPlot, 3)
                utils.makeUnit(con.iBombard, iCiv, tPlot, 2)
                utils.makeUnit(con.iTrebuchet, iCiv, tPlot, 3)
                utils.makeUnit(con.iIslamicMissionary, iCiv, tPlot, 3)
	        if utils.getHumanID() != iTurkey:
		        utils.makeUnit(con.iBombard, iCiv, tPlot, 3)
		        utils.makeUnit(con.iOttomanJanissary, iCiv, tPlot, 5)
		        utils.makeUnit(con.iKnight, iCiv, tPlot, 2)
        if (iCiv == iPortugal):
		utils.createSettlers(iCiv, 1)
                utils.makeUnitAI(con.iLongbowman, iCiv, tPlot, UnitAITypes.UNITAI_CITY_DEFENSE, 2)
                utils.makeUnit(con.iCrossbowman, iCiv, tPlot, 2)
                utils.makeUnit(con.iPikeman, iCiv, tPlot, 2)
		utils.makeUnit(con.iChristianMissionary, iCiv, tPlot, 1)
                tSeaPlot = self.findSeaPlots(tPlot, 1, iCiv)
                if (tSeaPlot):                                
                        utils.makeUnit(con.iWorkBoat, iCiv, tSeaPlot, 2)
                        pPortugal.initUnit(con.iGalley, tSeaPlot[0], tSeaPlot[1], UnitAITypes.UNITAI_SETTLER_SEA, DirectionTypes.DIRECTION_SOUTH)
                        utils.makeUnit(con.iSettler, iCiv, tPlot, 1)
                        utils.makeUnit(con.iLongbowman, iCiv, tPlot, 1)
                        utils.makeUnit(con.iGalley, iCiv, tSeaPlot, 1)

Then add the code in green:
Code:
if (iCiv == iTurkey):
                [COLOR="Green"][B]utils.makeUnit(con.iSettler, iCiv, tPlot, 1)[/B][/COLOR]
                utils.makeUnit(con.iLongbowman, iCiv, tPlot, 2)
                utils.makeUnit(con.iCrossbowman, iCiv, tPlot, 3)
 
OK, I added it. There were a ton of lines that all dealt with this and I had no way of distinguishing them from each other. I don't get Leoreth's attitude, given that it would have been immensely easier just to do that rather than give me a lecture on Python.
 
OK, I added it. There were a ton of lines that all dealt with this and I had no way of distinguishing them from each other. I don't get Leoreth's attitude, given that it would have been immensely easier just to do that rather than give me a lecture on Python.
Mom, tie my shoes, learning it is so hard!
 
Of course, every person who learns to program is a competitor for the job market.
 
The only reason why I haven't thrown up my hands in defeat and asked Leoreth to look at my mod is because I'm nearly 100% sure he'll say "I'm not here to mod for you or teach you how to mod Civ 4, I'm here for questions".

Speaking of that...
 
The main purpose of this thread is to point people in the right direction because RFC is a mess and in general it's not very easy to figure out where to make a specific change happen.

Teaching Civ4 modding or programming languages isn't really the point of it, but sometimes I help out with that too, see the pointless waste of time above.

But the point is definitely not to mod for others. I have my own mod to work on.
 
I've figured out why I'm getting python errors- it was because of the changes that NKN made. It must have been leaving something out.
 
Back
Top Bottom