[sdk]:trouble editing CvPlayer.cpp

stmartin

aka. poyuzhe
Joined
Aug 4, 2007
Messages
640
Location
Shanghai
I am totally stuck. However I change the following piece of code I added to the CvPlayer.cpp, the game will crash on me when I try to change Civic. Please take a look and tell me what's possibly wrong. My change is in the bottom, bold type

Code:
void CvPlayer::processCivics(CivicTypes eCivic, int iChange)
{
	int iI, iJ;

	changeGreatPeopleRateModifier(GC.getCivicInfo(eCivic).getGreatPeopleRateModifier() * iChange);
	changeGreatGeneralRateModifier(GC.getCivicInfo(eCivic).getGreatGeneralRateModifier() * iChange);
	changeDomesticGreatGeneralRateModifier(GC.getCivicInfo(eCivic).getDomesticGreatGeneralRateModifier() * iChange);
	changeStateReligionGreatPeopleRateModifier(GC.getCivicInfo(eCivic).getStateReligionGreatPeopleRateModifier() * iChange);
	changeDistanceMaintenanceModifier(GC.getCivicInfo(eCivic).getDistanceMaintenanceModifier() * iChange);
	changeNumCitiesMaintenanceModifier(GC.getCivicInfo(eCivic).getNumCitiesMaintenanceModifier() * iChange);
	changeCorporationMaintenanceModifier(GC.getCivicInfo(eCivic).getCorporationMaintenanceModifier() * iChange);
	changeExtraHealth(GC.getCivicInfo(eCivic).getExtraHealth() * iChange);
	changeFreeExperience(GC.getCivicInfo(eCivic).getFreeExperience() * iChange);
	changeWorkerSpeedModifier(GC.getCivicInfo(eCivic).getWorkerSpeedModifier() * iChange);
	changeImprovementUpgradeRateModifier(GC.getCivicInfo(eCivic).getImprovementUpgradeRateModifier() * iChange);
	changeMilitaryProductionModifier(GC.getCivicInfo(eCivic).getMilitaryProductionModifier() * iChange);
	changeBaseFreeUnits(GC.getCivicInfo(eCivic).getBaseFreeUnits() * iChange);
	changeBaseFreeMilitaryUnits(GC.getCivicInfo(eCivic).getBaseFreeMilitaryUnits() * iChange);
	changeFreeUnitsPopulationPercent(GC.getCivicInfo(eCivic).getFreeUnitsPopulationPercent() * iChange);
	changeFreeMilitaryUnitsPopulationPercent(GC.getCivicInfo(eCivic).getFreeMilitaryUnitsPopulationPercent() * iChange);
	changeGoldPerUnit(GC.getCivicInfo(eCivic).getGoldPerUnit() * iChange);
	changeGoldPerMilitaryUnit(GC.getCivicInfo(eCivic).getGoldPerMilitaryUnit() * iChange);
	changeHappyPerMilitaryUnit(GC.getCivicInfo(eCivic).getHappyPerMilitaryUnit() * iChange);
	changeMilitaryFoodProductionCount((GC.getCivicInfo(eCivic).isMilitaryFoodProduction()) ? iChange : 0);
	changeMaxConscript(getWorldSizeMaxConscript(eCivic) * iChange);
	changeNoUnhealthyPopulationCount((GC.getCivicInfo(eCivic).isNoUnhealthyPopulation()) ? iChange : 0);
	changeBuildingOnlyHealthyCount((GC.getCivicInfo(eCivic).isBuildingOnlyHealthy()) ? iChange : 0);
	changeLargestCityHappiness(GC.getCivicInfo(eCivic).getLargestCityHappiness() * iChange);
	changeWarWearinessModifier(GC.getCivicInfo(eCivic).getWarWearinessModifier() * iChange);
	changeFreeSpecialist(GC.getCivicInfo(eCivic).getFreeSpecialist() * iChange);
	changeTradeRoutes(GC.getCivicInfo(eCivic).getTradeRoutes() * iChange);
	changeNoForeignTradeCount(GC.getCivicInfo(eCivic).isNoForeignTrade() * iChange);
	changeNoCorporationsCount(GC.getCivicInfo(eCivic).isNoCorporations() * iChange);
	changeNoForeignCorporationsCount(GC.getCivicInfo(eCivic).isNoForeignCorporations() * iChange);
	changeStateReligionCount((GC.getCivicInfo(eCivic).isStateReligion()) ? iChange : 0);
	changeNoNonStateReligionSpreadCount((GC.getCivicInfo(eCivic).isNoNonStateReligionSpread()) ? iChange : 0);
	changeStateReligionHappiness(GC.getCivicInfo(eCivic).getStateReligionHappiness() * iChange);
	changeNonStateReligionHappiness(GC.getCivicInfo(eCivic).getNonStateReligionHappiness() * iChange);
	changeStateReligionUnitProductionModifier(GC.getCivicInfo(eCivic).getStateReligionUnitProductionModifier() * iChange);
	changeStateReligionBuildingProductionModifier(GC.getCivicInfo(eCivic).getStateReligionBuildingProductionModifier() * iChange);
	changeStateReligionFreeExperience(GC.getCivicInfo(eCivic).getStateReligionFreeExperience() * iChange);
	changeExpInBorderModifier(GC.getCivicInfo(eCivic).getExpInBorderModifier() * iChange);

	for (iI = 0; iI < NUM_YIELD_TYPES; iI++)
	{
		changeYieldRateModifier(((YieldTypes)iI), (GC.getCivicInfo(eCivic).getYieldModifier(iI) * iChange));
		changeCapitalYieldRateModifier(((YieldTypes)iI), (GC.getCivicInfo(eCivic).getCapitalYieldModifier(iI) * iChange));
		changeTradeYieldModifier(((YieldTypes)iI), (GC.getCivicInfo(eCivic).getTradeYieldModifier(iI) * iChange));
	}

	for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
	{
		changeCommerceRateModifier(((CommerceTypes)iI), (GC.getCivicInfo(eCivic).getCommerceModifier(iI) * iChange));
		changeCapitalCommerceRateModifier(((CommerceTypes)iI), (GC.getCivicInfo(eCivic).getCapitalCommerceModifier(iI) * iChange));
		changeSpecialistExtraCommerce(((CommerceTypes)iI), (GC.getCivicInfo(eCivic).getSpecialistExtraCommerce(iI) * iChange));
	}

	for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
	{
		BuildingTypes eOurBuilding = (BuildingTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationBuildings(iI);
		if (NO_BUILDING != eOurBuilding)
		{
			changeExtraBuildingHappiness(eOurBuilding, (GC.getCivicInfo(eCivic).getBuildingHappinessChanges(iI) * iChange));
			changeExtraBuildingHealth(eOurBuilding, (GC.getCivicInfo(eCivic).getBuildingHealthChanges(iI) * iChange));
		}
	}

	for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
	{
		changeFeatureHappiness(((FeatureTypes)iI), (GC.getCivicInfo(eCivic).getFeatureHappinessChanges(iI) * iChange));
	}

	for (iI = 0; iI < GC.getNumHurryInfos(); iI++)
	{
		changeHurryCount(((HurryTypes)iI), ((GC.getCivicInfo(eCivic).isHurry(iI)) ? iChange : 0));
	}

	for (iI = 0; iI < GC.getNumSpecialBuildingInfos(); iI++)
	{
		changeSpecialBuildingNotRequiredCount(((SpecialBuildingTypes)iI), ((GC.getCivicInfo(eCivic).isSpecialBuildingNotRequired(iI)) ? iChange : 0));
	}

	for (iI = 0; iI < GC.getNumSpecialistInfos(); iI++)
	{
		changeSpecialistValidCount(((SpecialistTypes)iI), ((GC.getCivicInfo(eCivic).isSpecialistValid(iI)) ? iChange : 0));
	}

	for (iI = 0; iI < GC.getNumImprovementInfos(); iI++)
	{
		for (iJ = 0; iJ < NUM_YIELD_TYPES; iJ++)
		{
			changeImprovementYieldChange(((ImprovementTypes)iI), ((YieldTypes)iJ), (GC.getCivicInfo(eCivic).getImprovementYieldChanges(iI, iJ) * iChange));
		}
	}

        [B]CvCity* pLoopCity;
        int iLoop, totalGreatPeopleRateChange;
        UnitTypes eGreatPeopleUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCivicInfo(eCivic).getGreatPeopleUnitClass())));
	if (eGreatPeopleUnit != NO_UNIT)
	{
	    for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
        {
            totalGreatPeopleRateChange = pLoopCity->countNumImprovedPlots((ImprovementTypes)(GC.getCivicInfo(eCivic).getImprovementType()), 0) * GC.getCivicInfo(eCivic).getGreatPeopleRateChange() * iChange;
            pLoopCity->changeGreatPeopleUnitRate(eGreatPeopleUnit, totalGreatPeopleRateChange);
        }[/B]
	}

}

There are several things I changed:
1. I added 3 new tags to CvCivicInfos.xml. They are: ImprovementType, GreatPeopleUnitClass, and iGreatPeopleRateChange. I've also properly implemented them into CvInfo.h and CvInfo.cpp, or at least I believe I've done so.
2. What I want to achieve with the code is: when A civic change happens, I want each city to gain certain amount of GP point per certain improvement type. For example, when I switch to Universal Suffrage, every of my city will gain GP points toward great merchant per Town improvement the city has.

Well, I tried to alter the code in every way I could imagine, but it always crash when I attempt to switch civic. SDK is so Awesome!
 
Not sure what precisely the issue is off the top of my head, but since you are not certain about other things you have done, I would say comment out the Loop over all Cities and see if that stops the crash. At the least it helps to isolate things a bit.
 
Well I'm an SDK noob myself, but if I were to create something like this, I'd just add for example a changeImprovementGPPChange function in CvPlayer.cpp/processCivic, but do the actual adding of great people points in CvCity.cpp. I think there's some city.doTurn function there in which you can refer to another function doing the GPP adding.
 
What I would do is to compile a debug version of your DLL, and set a break point just before your additions. Then step through each statement to isolate the crash, checking out variables as necessary to make sure they are sane.

If for some reason you can't debug, an alternative would be to place a bunch of logging text in and around the loop to output where you are and the values of the important variables. Where the log stops will tell you where the crash is and what was happening just before.
 
ZOMG you're mixing tabs and spaces!!1!1!eleven :D

Sorry, couldn't resist. :lol:

Compile the DLL with asserts turned on. Hopefully someone has written up how to do that. I haven't done any SDK work, but it should be a matter of defining a compiler flag (like #define ASSERTS 1 but in the compiler itself). This will turn on all the checks that values or in the correct bounds.

One thing that I notice is that you pass getGreatPeopleUnitClass() to getCivilizationUnits(). Does every civic have to have a valid unit class for getGreatPeopleUnitClass()? If it's -1, I think you may be triggering getCivilizationUnits()'s assertion that it's a valid unit. Also, perhaps casting NO_UNIT to a UnitTypes value is problematic? Seems like a stretch, though.

For maximum safety, try this:

Code:
if (GC.getCivicInfo(eCivic).getGreatPeopleUnitClass() != NO_UNIT)
{
    if (GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCivicInfo(eCivic).getGreatPeopleUnitClass())) != NO_UNIT)
    {
        UnitTypes eGreatPeopleUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCivicInfo(eCivic).getGreatPeopleUnitClass())));
        ...for loop...
    }
}
 
Not sure what precisely the issue is off the top of my head, but since you are not certain about other things you have done, I would say comment out the Loop over all Cities and see if that stops the crash. At the least it helps to isolate things a bit.

I tried it, and I found out it's not the loop's fault. :lol:

Well I'm an SDK noob myself, but if I were to create something like this, I'd just add for example a changeImprovementGPPChange function in CvPlayer.cpp/processCivic, but do the actual adding of great people points in CvCity.cpp. I think there's some city.doTurn function there in which you can refer to another function doing the GPP adding.

Yes, I will try your method after I figured out what's wrong with my current code. Your method is better but it will require some time for a noob like me to get a hold of how to do it that way.

What I would do is to compile a debug version of your DLL, and set a break point just before your additions. Then step through each statement to isolate the crash, checking out variables as necessary to make sure they are sane.

If for some reason you can't debug, an alternative would be to place a bunch of logging text in and around the loop to output where you are and the values of the important variables. Where the log stops will tell you where the crash is and what was happening just before.

I tried to compile a debug version in code block but it told me in the linking phase that it can't open msvcxxx.dll(forgot to write down the file name) and fatal errored. Any hints how to make this work? Also, I don't understand how to make a log, if it's fstream that you meant could you give me a little sample code? I tried fstream but probably got it wrong. Thanks in advance!
 
ZOMG you're mixing tabs and spaces!!1!1!eleven :D

Sorry, couldn't resist. :lol:

Compile the DLL with asserts turned on. Hopefully someone has written up how to do that. I haven't done any SDK work, but it should be a matter of defining a compiler flag (like #define ASSERTS 1 but in the compiler itself). This will turn on all the checks that values or in the correct bounds.

One thing that I notice is that you pass getGreatPeopleUnitClass() to getCivilizationUnits(). Does every civic have to have a valid unit class for getGreatPeopleUnitClass()? If it's -1, I think you may be triggering getCivilizationUnits()'s assertion that it's a valid unit. Also, perhaps casting NO_UNIT to a UnitTypes value is problematic? Seems like a stretch, though.

For maximum safety, try this:

Code:
if (GC.getCivicInfo(eCivic).getGreatPeopleUnitClass() != NO_UNIT)
{
    if (GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCivicInfo(eCivic).getGreatPeopleUnitClass())) != NO_UNIT)
    {
        UnitTypes eGreatPeopleUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCivicInfo(eCivic).getGreatPeopleUnitClass())));
        ...for loop...
    }
}

You Got It!!!

Yes, your sample code is totally useful. I tried that and the crash is gone. However, the code still didn't have any effect on game! When I change civic, no GPP change happens. So I had another look at CvCity.cpp and found a function named changeBaseGreatPeopleRate(). I found out that if I want changeGreatPeopleUnitRate() to have any effect, I must run changeBaseGreatPeopleRate() first! So this the code's current form:

Code:
    CvCity* pLoopCity;
    int iLoop, totalGreatPeopleRateChange;
    if (GC.getCivicInfo(eCivic).getGreatPeopleUnitClass() != NO_UNIT)
    {
        UnitTypes eGreatPeopleUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(GC.getCivicInfo(eCivic).getGreatPeopleUnitClass())));
        ImprovementTypes eImprovement = (ImprovementTypes)(GC.getCivicInfo(eCivic).getImprovementType());
        int iGreatPeopleRateChange = GC.getCivicInfo(eCivic).getGreatPeopleRateChange();
        if (eGreatPeopleUnit != NO_UNIT && eImprovement != NO_IMPROVEMENT && iGreatPeopleRateChange != 0)
        {
            for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop))
            {
                totalGreatPeopleRateChange = pLoopCity->countNumImprovedPlots(eImprovement, 0)* iGreatPeopleRateChange * iChange;
                pLoopCity->changeBaseGreatPeopleRate(totalGreatPeopleRateChange);
                pLoopCity->changeGreatPeopleUnitRate(eGreatPeopleUnit, totalGreatPeopleRateChange);
            }
        }
    }

It works just fine. Finally...:lol:

BTW, what does turn on Asserts mean? Yes it's amazing but I don't get it. :lol:
 
However, there's still one problem remained:

For example, if a village newly grown into a town a few turns after player changing into Universal Suffrage, no additional GPP will be added to that city, and when the player tries to change civic back to something else such as Representation, due to the additional town the city now owns, the city's GPP will be overly reduced.

The solution currently used:

In CvCity::doturn(), remove improvement caused GPP beore city improvements are processed, then re-add improvement caused GPP after City improvements are processed.

It seems to work. I'm pretty sure there are still some problems hiding somewhere though...
 
Glad that worked. Asserts are checks in code that can be turned on and off when compiling the DLL. You use them (on) during development as sanity checks to make sure your code is doing what it's supposed to do. You turn them off for a release (to ship the DLL to users) to speed up the runtime of the game.

You'll see them throughout the code like this:

Code:
FAssertMsg(!isWater(), "Cities cannot be founded on water")

This assumes that there was a check somewhere else in the code that would keep this codepath from being hit if the CyPlot was a water tile. So asserts are used to detect programming errors, not make choices in the game.

In your case, getCivilizationUnits() doesn't allow you to call it with NO_UNIT; that's known as part of its contract or API. If you call it, you agree not to pass in -1 (NO_UNIT). When you do, the assert would give you a nice message explaining the situation. Without the assert, the code blindly tries to lookup a unit in the -1st slot of an array (illegal) which causes C++ to take a dump.

If you want to turn asserts on (highly recommended), you have two options. Figure out how to define a variable in CodeBlocks and define FASSERT_ENABLE. Otherwise open FAsset.h and change it like this:

Code:
// Only compile in FAssert's if FASSERT_ENABLE is defined.  By default, however, let's key off of
// _DEBUG.  Sometimes, however, it's useful to enable asserts in release builds, and you can do that
// simply by changing the following lines to define FASSERT_ENABLE or using project settings to override
[B]//#ifdef _DEBUG   <-- EF: comment this line out or remove it entirely[/B]
#define FASSERT_ENABLE
[B]//#endif          <-- EF: same here[/b]
 
I tried to compile a debug version in code block but it told me in the linking phase that it can't open msvcxxx.dll(forgot to write down the file name) and fatal errored. Any hints how to make this work?
I use Visual Studio rather than codeblocks so I can't tell you exactly what to do. For my situation I started with Refar's makefile and the only change I had to make was to have the Debug version link against boost_python-vc71-mt-1_32.lib instead of boost_python-vc71-mt-gd-1_32.lib. There might be some pointers in Kael's pinned topic about getting Debug builds to work under Code Blocks.

continued... said:
Also, I don't understand how to make a log, if it's fstream that you meant could you give me a little sample code? I tried fstream but probably got it wrong. Thanks in advance!
The following is an example on how I do logging from the sdk; this example was from within the CvPlayer class.
Code:
CvString szLogMsg;
szLogMsg.Format("CityName: Getting city name for {%ls} who currently has %d cities.",GC.getCivilizationInfo(getCivilizationType()).getText(), getNumCities());
gDLL->logMsg("sdk.log", szLogMsg);
The basic idea is to use the gDLL->logMsg() function which is provided by the game. The first parameter is the filename of the log (pick whatever you like) and the second is the string which will actually be logged. Your logfile will appear in the My Games\<game name>\Logs directory along with the "normal" logs.
 
The following is an example on how I do logging from the sdk; this example was from within the CvPlayer class.
Code:
CvString szLogMsg;
szLogMsg.Format("CityName: Getting city name for {%ls} who currently has %d cities.",GC.getCivilizationInfo(getCivilizationType()).getText(), getNumCities());
gDLL->logMsg("sdk.log", szLogMsg);
The basic idea is to use the gDLL->logMsg() function which is provided by the game. The first parameter is the filename of the log (pick whatever you like) and the second is the string which will actually be logged. Your logfile will appear in the My Games\<game name>\Logs directory along with the "normal" logs.

Thanks, I was enlightened. Also after studying Kael's post I'm convinced that there isn't a known way to debug under codeblocks. Guess I need to forget about debugging for the time being.
 
Top Bottom