SDK for SetHasTrait

You're missing the part in setHasTrait() where the Trait is actually being set

Code:
	if (m_pbTraits[(int)eTrait] == bNewValue)
	{
		return;
	}

	m_pbTraits[(int)eTrait] = bNewValue;

This code is adding the trait to the players list of traits so that it will be saved.

That led to an enormous amount of links and changes. I think I have added all of them, but I am left with a problem with NMAKE:

Spoiler :

Code:
1>_precompile.cpp
1> "C:\Program Files\Microsoft Visual C++ Toolkit 2003/bin/link.exe" /dll /nologo  /LIBPATH:Python24/libs /LIBPATH:boost-1.32.0/libs/ /LIBPATH:"C:\Program Files\Microsoft Visual C++ Toolkit 2003/lib" /LIBPATH:"C:\Program Files\Microsoft Platform SDK/Lib" /out:Final_Release\CvGameCoreDLL.dll  boost_python-vc71-mt-1_32.lib winmm.lib user32.lib  Final_Release\CvArea.obj Final_Release\CvArtFileMgr.obj Final_Release\CvCity.obj Final_Release\CvCityAI.obj Final_Release\CvDeal.obj Final_Release\CvDiploParameters.obj Final_Release\CvDLLButtonPopup.obj Final_Release\CvDLLEntity.obj Final_Release\CvDLLPython.obj Final_Release\CvDLLPythonEvents.obj Final_Release\CvDLLTranslator.obj Final_Release\CvDLLWidgetData.obj Final_Release\CvEventReporter.obj Final_Release\CvFractal.obj Final_Release\CvGame.obj Final_Release\CvGameAI.obj Final_Release\CvGameCoreDLL.obj Final_Release\CvGameCoreUtils.obj Final_Release\CvGameInterface.obj Final_Release\CvGameTextMgr.obj Final_Release\CvGlobals.obj Final_Release\CvHallOfFameInfo.obj Final_Release\CvInfoWater.obj Final_Release\CvInfos.obj Final_Release\CvInitCore.obj Final_Release\CvMap.obj Final_Release\CvMapGenerator.obj Final_Release\CvMessageControl.obj Final_Release\CvMessageData.obj Final_Release\CvPlayer.obj Final_Release\CvPlayerAI.obj Final_Release\CvPlot.obj Final_Release\CvPlotGroup.obj Final_Release\CvPopupInfo.obj Final_Release\CvPopupReturn.obj Final_Release\CvRandom.obj Final_Release\CvReplayInfo.obj Final_Release\CvReplayMessage.obj Final_Release\CvSelectionGroup.obj Final_Release\CvSelectionGroupAI.obj Final_Release\CvStatistics.obj Final_Release\CvStructs.obj Final_Release\CvTalkingHeadMessage.obj Final_Release\CvTeam.obj Final_Release\CvTeamAI.obj Final_Release\CvUnit.obj Final_Release\CvUnitAI.obj Final_Release\CvXMLLoadUtility.obj Final_Release\CvXMLLoadUtilityGet.obj Final_Release\CvXMLLoadUtilityInit.obj Final_Release\CvXMLLoadUtilitySet.obj Final_Release\CyArea.obj Final_Release\CyAreaInterface.obj Final_Release\CyArgsList.obj Final_Release\CyArtFileMgr.obj Final_Release\CyArtFileMgrInterface.obj Final_Release\CyCity.obj Final_Release\CyCityInterface1.obj Final_Release\CyDeal.obj Final_Release\CyEnumsInterface.obj Final_Release\CyGame.obj Final_Release\CyGameCoreUtils.obj Final_Release\CyGameCoreUtilsInterface.obj Final_Release\CyGameInterface.obj Final_Release\CyGameTextMgr.obj Final_Release\CyGameTextMgrInterface.obj Final_Release\CyGlobalContext.obj Final_Release\CyGlobalContextInterface1.obj Final_Release\CyGlobalContextInterface2.obj Final_Release\CyGlobalContextInterface3.obj Final_Release\CyGlobalContextInterface4.obj Final_Release\CyHallOfFameInfo.obj Final_Release\CyHallOfFameInterface.obj Final_Release\CyInfoInterface1.obj Final_Release\CyInfoInterface2.obj Final_Release\CyInfoInterface3.obj Final_Release\CyMap.obj Final_Release\CyMapGenerator.obj Final_Release\CyMapGeneratorInterface.obj Final_Release\CyMapInterface.obj Final_Release\CyMessageControl.obj Final_Release\CyMessageControlInterface.obj Final_Release\CyPlayer.obj Final_Release\CyPlayerInterface1.obj Final_Release\CyPlayerInterface2.obj Final_Release\CyPlot.obj Final_Release\CyPlotInterface1.obj Final_Release\CyRandomInterface.obj Final_Release\CyReplayInfo.obj Final_Release\CySelectionGroup.obj Final_Release\CySelectionGroupInterface.obj Final_Release\CyStructsInterface1.obj Final_Release\CyTeam.obj Final_Release\CyTeamInterface.obj Final_Release\CyUnit.obj Final_Release\CyUnitInterface1.obj Final_Release\FAssert.obj Final_Release\FDialogTemplate.obj Final_Release\_precompile.obj   /debug /INCREMENTAL:NO /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF 
1>   Creating library Final_Release\CvGameCoreDLL.lib and object Final_Release\CvGameCoreDLL.exp
1>CvPlayer.obj : error LNK2019: unresolved external symbol "public: int __thiscall CvCivilizationInfo::getCivTrait(void)const " (?getCivTrait@CvCivilizationInfo@@QBEHXZ) referenced in function "public: void __thiscall CvPlayer::init(enum PlayerTypes)" (?init@CvPlayer@@QAEXW4PlayerTypes@@@Z)
1>Final_Release\CvGameCoreDLL.dll : fatal error LNK1120: 1 unresolved externals
1>NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual C++ Toolkit 2003/bin/link.exe"' : return code '0x460'
1>Stop.
1>Project : error PRJ0019: A tool returned an error code from "Performing Makefile project actions"
1>Build log was saved at "file://c:\SDK\CvGameCoreDLL\Final_Release\BuildLog.htm"
1>CvGameCoreDLL - 4 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


Any ideas on what I must do to resolve this?
 
Im guessing that you probably don't have civilization specific traits in your mod. If so, remove any reference to getCivTrait.

Done and the DLL compiled. However, Now I get this error for each of the religions, when I start the mod:

Code:
Tag: TXT_KEY_RELIGION_JUDAISM_ADJECTIVE in Info class was incorrect
Current XML file is xml\GameInfo/CIV4ReligionInfo.xml

...And my test mod failed to give me the INDUSTRIOUS trait, when the Wonder was built. :cry:
 
No clue about the XML error.

As far as the SDK goes, I took a quick look through your files and didnt notice anything missing. You do have some extra stuff you dont need. You can delete everything regarding alignment and globalcounter and SpecialistTypeExtraCommerce.
 
No clue about the XML error.

As far as the SDK goes, I took a quick look through your files and didnt notice anything missing. You do have some extra stuff you dont need. You can delete everything regarding alignment and globalcounter and SpecialistTypeExtraCommerce.

OK removing alignment and globalcounter and SpecialistTypeExtraCommerce actually fixed the XML error and the DLL compiled. However, we are back to the same problem: The new Trait "ORGANIZED" fails to appear, after the python code to add the trait is executed. :confused:

The latest SDK changes are attached. What more needs to be done?
 

Attachments

  • SDK.zip
    113.7 KB · Views: 193
I need the new test trait (INDUSTRIOUS) and all of its effects to appear in the Leader information (Sevopeadia), just as the two default traits appear for any leader. What have I left out? :confused:

Just to clarify, the new trait wont appear in the Sevopedia (that info is pulled from the XML files), but it should show when you mouseover that player in the scoreboard (or their flag if you're playing that civ)..
 
Just to clarify, the new trait won't appear in the Sevopedia (that info is pulled from the XML files), but it should show when you mouseover that player in the scoreboard (or their flag if you're playing that civ)..

In fact, the new trait appears on the Foriegn Advisor screen. So the test mod actually works. Outstanding! :) Now I can change the Python code to make a selection from a list of traits the player does not have. :dance::banana::bounce:

Pitty that we can't get the new trait to appear in the Sevopedia. To do that we would need some sort of script to edit a temporary XML file that would contain any additional player traits and somehow appended the additions with the information contained in the CIV4TraitInfos.xml file. :hmm:

:thanx:
 
Pitty that we can't get the new trait to appear in the Sevopedia. To do that we would need some sort of script to edit a temporary XML file that would contain any additional player traits and somehow appended the additions with the information contained in the CIV4TraitInfos.xml file. :hmm:
Way too much effort. Try doing it the easy way instead...

You could just have the 'pedia leader page Python (since they are built in Python) check to see if the leader is in use in the game and if so then show the traits the leader in the game actually has instead of the ones from the LeaderHeadInfo.

You can also update the DLL's CvGameTextMgr::parseLeaderTraits function to do the same thing, which would cover the pop-ups with the help text for the leader icons in the 'pedia (and maybe other places too).
 
You could just have the 'pedia leader page Python (since they are built in Python) check to see if the leader is in use in the game and if so then show the traits the leader in the game actually has instead of the ones from the LeaderHeadInfo.

I was about to wrap up this mod, but now you have my attention. I assume you are referring to the "placeTraits" function in the CvPediaLeader.py file. Is that correct?
 
Sounds about right.

Since that apparently calls PareseLeaderTraits from CvGameTextMgr to get the data, just changing it there in the DLL, as mentioned in the paragraph after that one, should cover just about everything. (I had assumed that it was calling parseTraits for each individual trait, but apparently not.) In CvGameTextMgr.cpp, ParseLeaderTraits uses the leader's LeaderHeadInfo in a loop over the traits and calls parseTraits for each one that it has. It seems to me that in that loop is where you'd want to make the change.
 
Sounds about right.

Since that apparently calls PareseLeaderTraits from CvGameTextMgr to get the data, just changing it there in the DLL, as mentioned in the paragraph after that one, should cover just about everything. (I had assumed that it was calling parseTraits for each individual trait, but apparently not.) In CvGameTextMgr.cpp, ParseLeaderTraits uses the leader's LeaderHeadInfo in a loop over the traits and calls parseTraits for each one that it has. It seems to me that in that loop is where you'd want to make the change.

Does this look like it might work?

Spoiler :

Code:
void CvGameTextMgr::parseLeaderTraits(CvWStringBuffer &szHelpString, LeaderHeadTypes eLeader, CivilizationTypes eCivilization, bool bDawnOfMan, bool bCivilopediaText)
{
	PROFILE_FUNC();

	CvWString szTempBuffer;	// Formatting
	int iI;

	//	Build help string
	if (eLeader != NO_LEADER)
	{
		if (!bDawnOfMan && !bCivilopediaText)
		{
			szTempBuffer.Format( SETCOLR L"%s" ENDCOLR , TEXT_COLOR("COLOR_HIGHLIGHT_TEXT"), GC.getLeaderHeadInfo(eLeader).getDescription());
			szHelpString.append(szTempBuffer);
		}
		
		FAssert((GC.getNumTraitInfos() > 0) &&
			"GC.getNumTraitInfos() is less than or equal to zero but is expected to be larger than zero in CvSimpleCivPicker::setLeaderText");
		
		bool bFirst = true;
		
/************************************************************************************************/
/* FfH: Traits:                            START                                     by Kael    */
/************************************************************************************************/
		for (iI = 0; iI < GC.getNumTraitInfos(); ++iI)
		{
//			if (GC.getLeaderHeadInfo(eLeader).hasTrait(iI))
			if (gc.getGame().isLeaderEverActive(eLeader))
			{
				if (gc.getGame().isCivEverActive(eCivilization))
				{
					if (GC.getCivilizationInfo(eCivilization).hasTrait(iI))
					{
						if (!bFirst)
						{
							if (bDawnOfMan)
							{
								szHelpString.append(L", ");
							}
						}
						else
						{
							bFirst = false;
						}
						parseTraits(szHelpString, ((TraitTypes)iI), eCivilization, bDawnOfMan);
					}
				}
			}
/************************************************************************************************/
/* FfH: Traits:                            END                                       by Kael    */
/************************************************************************************************/
		}
	}
	else
	{
		//	Random leader
		szTempBuffer.Format( SETCOLR L"%s" ENDCOLR , TEXT_COLOR("COLOR_HIGHLIGHT_TEXT"), gDLL->getText("TXT_KEY_TRAIT_PLAYER_UNKNOWN").c_str());
		szHelpString.append(szTempBuffer);
	}

//	return szHelpString;
}
 
What about leaders in the Civilopedia that are not in the current game? You normally show trait info for them too. That code does not look like it would do so.

It also looks like it is only checking for civilization traits (traits from the civilization info), not leader traits (traits from the leaderhead info). Normal BtS doesn't even have civilization traits.

I think what you need is an if/else check where:
If the specified leader is in the current game you check for the traits the same place you added the extras in the first place (presumably the CvPlayer) to check against the actual current traits the leader has.
Else do what the original code did (check the leader head info).
 
What about leaders in the Civilopedia that are not in the current game? You normally show trait info for them too. That code does not look like it would do so.

It also looks like it is only checking for civilization traits (traits from the civilization info), not leader traits (traits from the leaderhead info). Normal BtS doesn't even have civilization traits.

I think what you need is an if/else check where:
If the specified leader is in the current game you check for the traits the same place you added the extras in the first place (presumably the CvPlayer) to check against the actual current traits the leader has.
Else do what the original code did (check the leader head info).

OK. I put this together:

Spoiler :

Code:
/************************************************************************************************/
/* FfH: Traits:                            START                                     by Kael    */
/************************************************************************************************/
		for (iI = 0; iI < GC.getNumTraitInfos(); ++iI)
		{
			if (GC.getGame().isLeaderEverActive(eLeader))
			{
				for (int iPlayer = 0; iPlayer < MAX_CIV_PLAYERS; iPlayer++)
				{
					CvPlayer& kPlayer = GET_PLAYER((PlayerTypes)iPlayer);
					if (kPlayer.isEverAlive())
					{
						if (kPlayer.getLeaderType() == eLeader)
						{
							if (kPlayer.hasTrait((TraitTypes)iI))
							{
								if (!bFirst)
								{
									if (bDawnOfMan)
									{
										szHelpString.append(L", ");
									}
								}
								else
								{
									bFirst = false;
								}
								parseTraits(szHelpString, ((TraitTypes)iI), eCivilization, bDawnOfMan);
							}
						}
					}
				}
			}
			else
			{
				if (GC.getLeaderHeadInfo(eLeader).hasTrait(iI))
				{
					if (!bFirst)
					{
						if (bDawnOfMan)
						{
							szHelpString.append(L", ");
						}
					}
					else
					{
						bFirst = false;
					}
					parseTraits(szHelpString, ((TraitTypes)iI), eCivilization, bDawnOfMan);
				}
			}
/************************************************************************************************/
/* FfH: Traits:                            END                                       by Kael    */
/************************************************************************************************/


It Works! :dance::rockon::banana::bounce:

:thanx:
 
Just to clarify, the new trait wont appear in the Sevopedia (that info is pulled from the XML files), but it should show when you mouseover that player in the scoreboard (or their flag if you're playing that civ)..

Tholal,

Thanks to God-Emporer, the data for the appearance of a new trait in the Sevopedia, is now pulled from the CvPlayer, and not the XML. The new trait appears, as it should for any leader, who builds the National Wonder.

However, we have one more issue yet to be solved. In my test mod, I wanted proof that the effects of a new trait actually show up. So, I picked a leader that did not have the financial trait for my test. Why? Because the financial trait adds one commerce that can be easily seen on coastal plots. So it is easy to see a visible change from two :c5gold: :c5gold: to three :c5gold: :c5gold: :c5gold: commerce coins. I built the wonder and verified the leaderhead shows the new financial trait. Unfortunately, the coastal plots around my city did not see a change in the commerce from two to three commerce coins. I was able to repeat this problem in two different test games. :cry:

What in the SDK have we left undone?
 
There are probably update calls you need to do.

Like CvCity::updateYield is called when you build a building in a city that changes plot yields (like the levy or Moai statues), which itself calls CvPlot::updateYield for that city's plots. Since a trait is global, you might need to call CvMap::updateYield which does them all, or at least CvPlayer::updateYield.

It looks like the specific case of the yield threshold modifiers (which is what financial uses) is done in CvPlayer::updateExtraYieldThreshold(YieldTypes eIndex). This checks all traits for the best modifier for the specified yield type and uses that one. It also calls updateYield itself.

There may be more properties that need to have things updated for them like perhaps commerce changes/modifiers and trade yield modifiers. CvPlayer has an updateCommerce function and an updateTradeRoutes function, each of which may need to be called if the trait modifies these things. On the other hand, these sorts of things might update automatically at the end of the turn - or they might not. You also need to decide what happens for things like free promotions: are they applied retroactively to all existing units? Ordinarily this is not an issue since the traits are present at the start of the game so the effects are already applied every time something happens. (The experience level modifier may make things weird - many existing units will probably suddenly be eligible for more promotions.)

I suggest looking at CvPlayer::init, which has a section that loops over the traits to apply the effects of the ones the player has (coincidentally, the specific case of the yield thresholds is not handled in this loop since the updateExtraYieldThreshold itself checks all the traits so it is called after the loop over the traits; updateMaxAnarchyTurns is also after the loop since it also already loops over all the traits). You almost certainly need to do what it does in this loop for the traits the leader has when initialized for the newly acquired trait (not all traits or you'll double the effect of most of the ones it already has, so no loop) as well as the two things after it. It may be the case that each of the functions to apply the trait's properties also calls the relevant update function when there is one, which would make things easier.

I probably should have checked the CvPlayer::init first. That paragraph may obsolete most of the stuff in the earlier paragraphs...
 
There are probably update calls you need to do.

Like CvCity::updateYield is called when you build a building in a city that changes plot yields (like the levy or Moai statues), which itself calls CvPlot::updateYield for that city's plots. Since a trait is global, you might need to call CvMap::updateYield which does them all, or at least CvPlayer::updateYield.

It looks like the specific case of the yield threshold modifiers (which is what financial uses) is done in CvPlayer::updateExtraYieldThreshold(YieldTypes eIndex). This checks all traits for the best modifier for the specified yield type and uses that one. It also calls updateYield itself.

There may be more properties that need to have things updated for them like perhaps commerce changes/modifiers and trade yield modifiers. CvPlayer has an updateCommerce function and an updateTradeRoutes function, each of which may need to be called if the trait modifies these things. On the other hand, these sorts of things might update automatically at the end of the turn - or they might not. You also need to decide what happens for things like free promotions: are they applied retroactively to all existing units? Ordinarily this is not an issue since the traits are present at the start of the game so the effects are already applied every time something happens. (The experience level modifier may make things weird - many existing units will probably suddenly be eligible for more promotions.)

I suggest looking at CvPlayer::init, which has a section that loops over the traits to apply the effects of the ones the player has (coincidentally, the specific case of the yield thresholds is not handled in this loop since the updateExtraYieldThreshold itself checks all the traits so it is called after the loop over the traits; updateMaxAnarchyTurns is also after the loop since it also already loops over all the traits). You almost certainly need to do what it does in this loop for the traits the leader has when initialized for the newly acquired trait (not all traits or you'll double the effect of most of the ones it already has, so no loop) as well as the two things after it. It may be the case that each of the functions to apply the trait's properties also calls the relevant update function when there is one, which would make things easier.

I probably should have checked the CvPlayer::init first. That paragraph may obsolete most of the stuff in the earlier paragraphs...

I left out the following lines:

Code:
for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
{
	updateExtraYieldThreshold((YieldTypes)iI);
}

updateMaxAnarchyTurns();

It tested good (i.e the coins showed up), after I put these lines back in. Now I think the SDK for my Traits Mod is done. :whew:

Thanks again.
 
I should have posted this before. Here is a final look at the function that works:

Code:
void CvPlayer::setHasTrait(TraitTypes eTrait, bool bNewValue)
{
	FAssertMsg((eTrait >= 0), "eTrait is less than zero");

	int iI, iChange;

	if (bNewValue)
	{
		iChange = 1;
	}
	else
	{
		iChange = -1;
	}

	if (m_pbTraits[(int)eTrait] == bNewValue)
	{
		return;
	}

	m_pbTraits[(int)eTrait] = bNewValue;

	changeExtraHealth(GC.getTraitInfo(eTrait).getHealth() * iChange);
	changeExtraHappiness(GC.getTraitInfo(eTrait).getHappiness() * iChange);

	for (iI = 0; iI < GC.getNumBuildingInfos(); iI++)
	{
		changeExtraBuildingHappiness((BuildingTypes)iI, GC.getBuildingInfo((BuildingTypes)iI).getHappinessTraits((int)eTrait) * iChange);
	}

	changeUpkeepModifier(GC.getTraitInfo(eTrait).getUpkeepModifier() * iChange);
	changeLevelExperienceModifier(GC.getTraitInfo(eTrait).getLevelExperienceModifier() * iChange);
	changeGreatPeopleRateModifier(GC.getTraitInfo(eTrait).getGreatPeopleRateModifier() * iChange);
	changeGreatGeneralRateModifier(GC.getTraitInfo(eTrait).getGreatGeneralRateModifier() * iChange);
	changeDomesticGreatGeneralRateModifier(GC.getTraitInfo(eTrait).getDomesticGreatGeneralRateModifier() * iChange);

	changeMaxGlobalBuildingProductionModifier(GC.getTraitInfo(eTrait).getMaxGlobalBuildingProductionModifier() * iChange);
	changeMaxTeamBuildingProductionModifier(GC.getTraitInfo(eTrait).getMaxTeamBuildingProductionModifier() * iChange);
	changeMaxPlayerBuildingProductionModifier(GC.getTraitInfo(eTrait).getMaxPlayerBuildingProductionModifier() * iChange);

	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		changeTradeYieldModifier(((YieldTypes)iI), GC.getTraitInfo(eTrait).getTradeYieldModifier(iI) * iChange);
	}

	for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
	{
		changeFreeCityCommerce(((CommerceTypes)iI), GC.getTraitInfo(eTrait).getCommerceChange(iI) * iChange);
		changeCommerceRateModifier(((CommerceTypes)iI), GC.getTraitInfo(eTrait).getCommerceModifier(iI) * iChange);
	}

	for (iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
	{
		if (GC.getCivicOptionInfo((CivicOptionTypes) iI).getTraitNoUpkeep(int(eTrait)))
		{
			changeNoCivicUpkeepCount(((CivicOptionTypes)iI), iChange);
		}
	}
	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		updateExtraYieldThreshold((YieldTypes)iI);
	}

	updateMaxAnarchyTurns();
}
 
Top Bottom