Modders Guide to FfH2

On the float discussion, it is something even many programmers wind up being fuzzy on, unless it has bit them on a project.

Here is a pretty easy to follow summation of the issue.

So pretty much you are in decent shape with floats if your value will never have more than 6 digits of precision, and hosed the moment you go beyond that. In general we only write up 3 or 4 digits for each value, but when you start thinking about percentage bonuses, that can instantly move a 4 digit precision number to 6 digits, which means you are on the precipice of disaster.

The issue isn't really one of OS used alone, though a different OS makes it far more likely to occur. Even just a different CPU can result in rounding errors popping up. They aren't a "hard and fast" break either, but rather a "soft break" which can show up sometimes and be a non-issue others. As those tend to cause debugging to go down misleading paths, it is just a good practice to avoid them unless ABSOLUTELY required.
 
I'm looking in the CvInfos section of the DLL, and wondering what exactly this does:

Spoiler :
Code:
SAFE_DELETE_ARRAY(m_pbMaintainFeatures);
    m_pbMaintainFeatures = new bool[GC.getNumFeatureInfos()];
    stream->Read(GC.getNumFeatureInfos(), m_pbMaintainFeatures);

There's another safe_delete_array function (though just a plain one) in another place in CvInfos, so it's hard to tell what this one is doing.

(I'm looking at this array as an analogy for a theoretical civilization specific terrain feature happiness, which is why I'm curious about this particular one.)

Edit: Turns out I think I know what it is. Than again, it is late, so I'm more likely than usual to mess something up, so might as well leave this up here just in case.

This is standard Array protocol really. At this point in the code you are loading data out of a savefile, and overwriting what might be already used data from a previous game.

Now, if I ran normal BtS, and then after a while loaded up FfH (bad example as you wind up opening a new client in this case, but go with it...), the actual number of resources in the game changed. So there is an array of size 32 (guessing here) already in existence. Arrays are dedicated memory blocks, so that means something else has data stored just past those 32 slots. So when I try to load in FfH's 68 (again, a guess) resources, I overwrite that array and destroy the data that follows it. So instead, I have to completely destroy that previous array (safe delete), and allocate new memory for an array of the size that I will need to use for what is about to load.
 
This is standard Array protocol really. At this point in the code you are loading data out of a savefile, and overwriting what might be already used data from a previous game.

Now, if I ran normal BtS, and then after a while loaded up FfH (bad example as you wind up opening a new client in this case, but go with it...), the actual number of resources in the game changed. So there is an array of size 32 (guessing here) already in existence. Arrays are dedicated memory blocks, so that means something else has data stored just past those 32 slots. So when I try to load in FfH's 68 (again, a guess) resources, I overwrite that array and destroy the data that follows it. So instead, I have to completely destroy that previous array (safe delete), and allocate new memory for an array of the size that I will need to use for what is about to load.

Yea, I was able to figure this out somewhat from your tutorial, and looking at the code more carefully. (Doing it late at night doesn't help the learning process. :) ), but thanks for this more full explanation.
 
I want to check something with regard to the Event system:

An Event Trigger with iPercentGamesActive set to "0" will never fire unless explicitly called, like say say by a per-turn python function. True?
 
Ive added some custom guilds (corporations) into my own modmod and got everything working fine except for the buttons on the citybar. For some reason a bunch of the old guilds from earlier versions of ffh (cult of the dragon etc) appear instead of my custom buttons, which work fine in the pedia and in the city interface. Ive even unloaded all of the artwork using pakbuild to look for this old button artwork but I cant find it. Anyone know how I can get my own buttons on the citybar instead of the old guild buttons?
 
Might be in the gamefonts.tga file. (Unless you've checked there already.) That file does have a lot of the small icons used for resources, religions, and such.
 
Where exactly are feats located? (I'm in the process of a "look through all files" method, but figure this might speed things up.)

Edit: should probably expand the question more. I've seen the functions for "setfeattypes" and such that changes whether a player has achieved a feat or not, plus some other functions related to feats as a list, but haven't seen where the actual list of feats is stored in the DLL.
 
Any suggestions on how to track down an event error? I can get the event trigger and event id numbers, but I don't know how to figure out which event those numbers correspond to.
 
I discovered that if I open the 'Locals' window and dig down through the variables, I can find the details about whatever it is I'm looking for. Turns out this particular error was in relation to the Insane and Adaptive traits, which are triggered by events.
 
Is there a spell that will change the unitcombat? I take it as no because each unit has a pre-defined unitcombat. Is that correct?
 
Is there a spell that will change the unitcombat? I take it as no because each unit has a pre-defined unitcombat. Is that correct?

As far as I know there are no built-in commands to directly change a unit's UNITCOMBAT, but you can have an upgrade spell that does so. Check out SPELL_UPGRADE_DOVIELLO_WARRIOR_SLAVE in Civ4SpellInfos.xml for an example.
 
Thanks for the help guys. I am actually working off of Orbis, and I notice there is the TempUnitCombat tag in the promotion spreadsheet. Perhaps this is similar to what you Valkrionn and what I am looking for. It is used for mounted units for example. I will try that out.

EDIT: Actually, I would like to change the domain of the unit too. I guess a spell to change the unit into a similar unit with a different domain is the only way.

@ Valkrionn: What about the secondary combats? Did that require much coding? That is an interesting modification to have. Could you give a little more information please?
 
Valk may not have seen your edit. The secondary unit combat would require fairly minimal code, but in fairly numerous locations. Since it was used only to authorize additional promotions it isn't TOO spread out, but still should be at least 5 or 6 places you have to modify the DLL code for it. Then of course there is whatever python nightmare you decide to endure for display purposes.
 
Xienwolf's right, I didn't see the edit. :p

Like he said, I only used it to qualify for promotions; No other effects. For example, if you have a unit with a bonus against mounted, and it attacks a unit with a secondary combat of mounted, it's not getting that bonus.

It's displayed via DLL changes as well, so just as text on the unit mouseover. Wanted it very visually distinct from the main combat, given that effects aren't applied. All DLL edits for it are commented, believe under Second Job or something along those lines.
 
Thanks for explaining. Good to know. I think for now, I may just look to an upgrade/spell to a similar unit with different domain. I will keep the secondary domain in mind though. ;)
 
How do you set up the interface to display another commerce type? (This is for the addition of mana, faith ,and espionage to the acetum cucumbis mod.)


I've looked through the Master of Mana main interface file, as well as looking for functions that seem to display the amount of gold, but the changes attempted cause the interface not to work. The changes at the moment just update the displayGameDataStrings (Which are shown below), but past this I'm lost as to how to do this sort of thing.

Spoiler :
Code:
def updateGameDataStrings( self ):
	
		screen = CyGInterfaceScreen( "MainInterface", CvScreenEnums.MAIN_INTERFACE )

		screen.hide( "ResearchText" )
		screen.hide( "GoldText" )
		screen.hide( "ManaText" )
		screen.hide( "FaithText" )
		screen.hide( "TimeText" )
		screen.hide( "ResearchBar" )

		bShift = CyInterface().shiftKey()
		
		xResolution = screen.getXResolution()
		yResolution = screen.getYResolution()

		pHeadSelectedCity = CyInterface().getHeadSelectedCity()

		if (pHeadSelectedCity):
			ePlayer = pHeadSelectedCity.getOwner()
		else:
			ePlayer = gc.getGame().getActivePlayer()

		if ( ePlayer < 0 or ePlayer >= gc.getMAX_PLAYERS() ):
			return 0

		for iI in range(CommerceTypes.NUM_COMMERCE_TYPES):
			szString = "PercentText" + str(iI)
			screen.hide(szString)
			szString = "RateText" + str(iI)
			screen.hide(szString)

		if ( CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_HIDE_ALL and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_MINIMAP_ONLY  and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_ADVANCED_START):

			# Percent of commerce
			if (gc.getPlayer(ePlayer).isAlive()):
				iCount = 0
				for iI in range( CommerceTypes.NUM_COMMERCE_TYPES ):
					eCommerce = (iI + 1) % CommerceTypes.NUM_COMMERCE_TYPES
					if (gc.getPlayer(ePlayer).isCommerceFlexible(eCommerce) or (CyInterface().isCityScreenUp() and (eCommerce == CommerceTypes.COMMERCE_GOLD))):
						iShift = 60
						if (CyInterface().isCityScreenUp()):
							iShift = 0
						szOutText = u"<font=2>%c:%d%%</font>" %(gc.getCommerceInfo(eCommerce).getChar(), gc.getPlayer(ePlayer).getCommercePercent(eCommerce))
						szString = "PercentText" + str(iI)
						screen.setLabel( szString, "Background", szOutText, CvUtil.FONT_LEFT_JUSTIFY, 14 + iShift, 50 + (iCount * 19), -0.1, FontTypes.SMALL_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1 )
						screen.show( szString )

						if not CyInterface().isCityScreenUp():
							szOutText = u"<font=2>" + localText.getText("TXT_KEY_MISC_POS_GOLD_PER_TURN", (gc.getPlayer(ePlayer).getCommerceRate(CommerceTypes(eCommerce)), )) + u"</font>"
							szString = "RateText" + str(iI)
							screen.setLabel( szString, "Background", szOutText, CvUtil.FONT_LEFT_JUSTIFY, 112 + iShift, 50 + (iCount * 19), -0.1, FontTypes.SMALL_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1 )
							screen.show( szString )

						iCount = iCount + 1;
                                                
			self.updateTimeText()
			screen.setLabel( "TimeText", "Background", g_szTimeText, CvUtil.FONT_RIGHT_JUSTIFY, xResolution - 56, 6, -0.3, FontTypes.GAME_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1 )
			screen.show( "TimeText" )
			
			if (gc.getPlayer(ePlayer).isAlive()):
				
				szText1 = CyGameTextMgr().getGoldStr(ePlayer)
				szText2 = CyGameTextMgr().getManaStr(ePlayer)
				szText3 = CyGameTextMgr().getFaithStr(ePlayer)

#FfH: Added by Kael 12/08/2007
				if (gc.getPlayer(ePlayer).getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_KHAZAD') and gc.getPlayer(ePlayer).getNumCities() > 0):
					iGold = gc.getPlayer(ePlayer).getGold() / gc.getPlayer(ePlayer).getNumCities()
					if iGold <= 49:
						szText = szText + " " + localText.getText("TXT_KEY_MISC_DWARVEN_VAULT_EMPTY", ())
					if (iGold >= 50 and iGold <= 99):
						szText = szText + " " + localText.getText("TXT_KEY_MISC_DWARVEN_VAULT_LOW", ())
					if (iGold >= 150 and iGold <= 199):
						szText = szText + " " + localText.getText("TXT_KEY_MISC_DWARVEN_VAULT_STOCKED", ())
					if (iGold >= 200 and iGold <= 299):
						szText = szText + " " + localText.getText("TXT_KEY_MISC_DWARVEN_VAULT_ABUNDANT", ())
					if (iGold >= 300 and iGold <= 499):
						szText = szText + " " + localText.getText("TXT_KEY_MISC_DWARVEN_VAULT_FULL", ())
					if iGold >= 500:
						szText = szText + " " + localText.getText("TXT_KEY_MISC_DWARVEN_VAULT_OVERFLOWING", ())
#FfH: End Add
				iShift = 60
				if (CyInterface().isCityScreenUp()):
					iShift = 0

				screen.setLabel( "GoldText", "Background", szText, CvUtil.FONT_LEFT_JUSTIFY, 12 + iShift, 6, -0.3, FontTypes.GAME_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1 )
				screen.show( "GoldText" )
				
				if not CyInterface().isCityScreenUp():
					screen.setLabel( "ManaText", "Background", szText1, CvUtil.FONT_LEFT_JUSTIFY, 12 + iShift+iTier1, 30, -0.3, FontTypes.GAME_FONT, WidgetTypes.WIDGET_HELP_MANA, -1, -1 )
					screen.show( "ManaText" )

					screen.setLabel( "FaithText", "Background", szText2, CvUtil.FONT_LEFT_JUSTIFY, 12 + iShift+iTier3, 30, -0.3, FontTypes.GAME_FONT, WidgetTypes.WIDGET_HELP_FAITH, -1, -1 )
					screen.show( "FaithText" )
				
				if (((gc.getPlayer(ePlayer).calculateGoldRate() != 0) and not (gc.getPlayer(ePlayer).isAnarchy())) or (gc.getPlayer(ePlayer).getGold() != 0)):
					screen.show( "GoldText" )

				if (gc.getPlayer(ePlayer).isAnarchy()):
				
					szText = localText.getText("INTERFACE_ANARCHY", (gc.getPlayer(ePlayer).getAnarchyTurns(), ))
					screen.setText( "ResearchText", "Background", szText, CvUtil.FONT_CENTER_JUSTIFY, screen.centerX(512), 3, -0.4, FontTypes.GAME_FONT, WidgetTypes.WIDGET_RESEARCH, -1, -1 )
					if ( gc.getPlayer(ePlayer).getCurrentResearch() != -1 ):
						screen.show( "ResearchText" )
					else:
						screen.hide( "ResearchText" )
					
				elif (gc.getPlayer(ePlayer).getCurrentResearch() != -1):

					szText = CyGameTextMgr().getResearchStr(ePlayer)
					screen.setText( "ResearchText", "Background", szText, CvUtil.FONT_CENTER_JUSTIFY, screen.centerX(512), 3, -0.4, FontTypes.GAME_FONT, WidgetTypes.WIDGET_RESEARCH, -1, -1 )
					screen.show( "ResearchText" )

					researchProgress = gc.getTeam(gc.getPlayer(ePlayer).getTeam()).getResearchProgress(gc.getPlayer(ePlayer).getCurrentResearch())
					overflowResearch = (gc.getPlayer(ePlayer).getOverflowResearch() * gc.getPlayer(ePlayer).calculateResearchModifier(gc.getPlayer(ePlayer).getCurrentResearch()))/100
					researchCost = gc.getTeam(gc.getPlayer(ePlayer).getTeam()).getResearchCost(gc.getPlayer(ePlayer).getCurrentResearch())
					researchRate = gc.getPlayer(ePlayer).calculateResearchRate(-1)
					
					iFirst = float(researchProgress + overflowResearch) / float(researchCost)
					screen.setBarPercentage( "ResearchBar", InfoBarTypes.INFOBAR_STORED, iFirst )
					if ( iFirst == 1 ):
						screen.setBarPercentage( "ResearchBar", InfoBarTypes.INFOBAR_RATE, ( float(researchRate) / float(researchCost) ) )
					else:
						screen.setBarPercentage( "ResearchBar", InfoBarTypes.INFOBAR_RATE, ( ( float(researchRate) / float(researchCost) ) ) / ( 1 - iFirst ) )

					screen.show( "ResearchBar" )
					
		return 0

(The entire main interface file is actually quite confusing to me. Too bad there isn't a guide of some kind that I've seen just for this file, and I imagine I'll probably be asking a lot of questions about it in the future. :) )
 
Back
Top Bottom