Help with DLL / Mastery Victory

Joined
Jun 7, 2008
Messages
6,149
Location
Just wonder...
I'm thinking about improving Mastery Victory but I need some help from some DLL guru. As some of you might remember, I'm working on Rise of Mankind - A New Dawn, but I've seen the code for Mastery Victory is the same for C2C;
here's the code found in CvTeam.cpp

Spoiler :
int CvTeam::getTotalVictoryScore()
{

int iI, iK, iL;
CvCity* pLoopCity;
int iLoop;
int iTotalVictoryScore = 0;

int globalCulture = 0;
int globalThreeCityCulture = 0;
int globalLand = 0;
int globalPopulation = 0;
int globalWonderScore = 0;

int teamWonderScore = 0;
int tempScore = 0;

int totalTeamReligion = 0;
int totalTeamLegendaryCities = 0;
int tempReligion = 0;

long globalPowerHistory =0;
long teamPowerHistory =0;

long tempPower =0;

globalLand = GC.getMap().getLandPlots();


for (iK = 0; iK < GC.getNumBuildingClassInfos(); iK++)
{
if (GC.getBuildingClassInfo((BuildingClassTypes)iK).getMaxGlobalInstances() == 1)
{
globalWonderScore ++;
}
}

// Get the Religion Info First
// By definition, global religion percent is 100, so we don't need a variable for it.
// Note: This detects whether the TEAM has the holy city.

for (iK = 0; iK < GC.getNumReligionInfos(); iK++)
{
if (hasHolyCity((ReligionTypes)iK))
{
// Player has holy city
tempReligion = GC.getGame().calculateReligionPercent((ReligionTypes)iK);
if (tempReligion > totalTeamReligion)
{
totalTeamReligion = tempReligion;
}

}
}



// Get land, population, culture totals for player and globally.
// Also get the starship launches and diplovictories achieved.

for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
//Calculate global totals while looping through

globalCulture += GET_PLAYER((PlayerTypes)iI).countTotalCulture();
globalPopulation += GET_PLAYER((PlayerTypes)iI).getTotalPopulation();

if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
{
teamWonderScore += GET_PLAYER((PlayerTypes)iI).getSevoWondersScore(0);
}
}
}

// Get the power history sums

for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
//Calculate global totals while looping through
tempPower = 0;
for (iL = 0; iL <= GC.getGame().getGameTurn(); iL++)
{
tempPower += GET_PLAYER((PlayerTypes)iI).getPowerHistory(iL);
}

if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
{
teamPowerHistory += tempPower;
}

globalPowerHistory += tempPower;
}
}

// Get the number of legendary cities owned by this team

for (iK = 0; iK < MAX_CIV_PLAYERS; iK++)
{
if ((GET_PLAYER((PlayerTypes)iK).getTeam() == getID()) && (GET_PLAYER((PlayerTypes)iK).isAlive()))
{
for (pLoopCity = GET_PLAYER((PlayerTypes)iK).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iK).nextCity(&iLoop))
{
// -2 is correct. We need -1 to change from 'total num' to 'last index', and -1 to get the top level.
if (pLoopCity->getCultureLevel() > GC.getNumCultureLevelInfos() - 2)
{
totalTeamLegendaryCities++;
}
}
}
}


// Add the WonderScore component
if (globalWonderScore > 0)
{
iTotalVictoryScore += int(teamWonderScore * 100 / globalWonderScore);
}


// Add the population score component
if (globalPopulation > 0)
{
iTotalVictoryScore += int(getTotalPopulation() * 100 / globalPopulation);
}

// Add the land score component
if (globalLand > 0)
{
iTotalVictoryScore += int(getTotalLand() * 100 / globalLand);
}


// Add the culture score component
if (globalCulture > 0)
{
iTotalVictoryScore += int(countTotalCulture() * 100 / globalCulture);
}


// Add the legendary cities component
if (totalTeamLegendaryCities > 0)
{
iTotalVictoryScore += (30 * totalTeamLegendaryCities);
}


// Add the Power component
if (globalPowerHistory > 0)
{
iTotalVictoryScore += (teamPowerHistory * 100 / globalPowerHistory);
}


// Add the Religion component
iTotalVictoryScore += totalTeamReligion;


//Starship points! Big money.
if (GC.getGame().getStarshipLaunched(getID()))
{
iTotalVictoryScore += 100;
}

return iTotalVictoryScore;

}


Now my question is: I'd like to add 100 points for building "Ascension Gate" which gives Scientific Victory. As you know, Ascension Gate gives -30 population in every city and grants Scientific Victory. But if you use Mastery Victory, it's totally useless to build AG: on the contrary, it makes you lose points because you lose population! So my idea is to give +100 points in Mastery Victory for building AG, hence making it useful again. Given the code above, I would think it would be easy enough but it looks like I'm not able to do it.
I suppose I'd have to add something like

if (BLAH BLAH BLAH)
{
iTotalVictoryScore += 100;
}

where BLAH BLAH BLAH is a check of existance of the Ascension Gate wonder. The problem is that I don't know which code should I use to check if Ascension Gate has been built. Is there a command which works like "If building x exists"?
I hope someone can help me, thanks in advance.
 
What I'd do for this is generate a new building integer tag like iMasteryScoreOnBuild and then, in CvPlayer develop the functions: getMasteryAdjustment, setMasteryAdjustment, and changeMasteryAdjustment. Then in the processBuilding function in CvCity, add a line to send the value from getMasteryScoreOnBuild to changeMasteryAdjustment on the player that owns the city the building has been built in.

Then, in your above posted code, plug in the getMasteryAdjustment value into the equation.

Is this advice sufficient or should I show it in code more explicitly? I recall you were a pretty good coder so hopefully this overview of process is enough.

EDIT: on quick review, it seems the points are looking mostly at a team level so perhaps instead of CvPlayer, all of the aforementioned MasteryAdjustment functions should be placed into CvTeam.
 
Or just write a method to check for the existence of an AG - loop over cities owned by the player and for each call getNumRealBuilding(BUILDING_AG) - if it finds one you're done - add the 100 points. The only issue there is that you need to determine the building I'd for the AG. You can retrieve that given the string BUILDING_WHATEVER (there's a method to do that, but I'm not at the computer currently so I forget what it's called - I think it's a method on CvGlobals). That will functionally work, but it's slightly distasteful ( you have to hard code the name of the building tag into the DLL).

The clean way is to add an <extraScore> tag on the building definition XML that defines an amount of score to credit for each such building in existance.
 
I suggest that the easiest is to check CvTeam's getBuildingClassCount to see if the team has an ascension gate. It's how the game checks for that victory condition being met.

That is assuming you don't care about the details. Since it is a wonder, it could be captured which would presumably transfer the benefit to the capturing team. The game apparently does not currently set anything indicating that a team has met the condition in the past for this building related victory type, it just knows whether or not someone meets the condition now (unless I missed something - there are arrays which have elements for all victory types like m_abCanLaunch, but that one, for example, is only set to true for projects that have a victory threshold, but building classes that have one do not set it even though there is obviously an element in the array to do so). If having won the victory type is more important that having the wonder after the fact, you'd need to store some indication of who built the thing and use that.
 
I feel like God-Emperor's suggestion might be the easiest one. I'm not a very good coder to tell the truth, I'm still learning ;)

But I've seen that

CyGame().getBuildingClassCount(int BuildingClass) --> Returns (int) the number of buildings of type 'BuildingClass' in the game for all players.
So could I use something like


if CyGame().getBuildingClassCount(int BuildingClass) = 1
{
iTotalVictoryScore += 100;
}

Would this suffice? It looks too easy to me so probably I'm missing something; even if it would suffice, another problem is that I don't know what "int BuildingClass" is the right one for Ascension Gate. Any idea on how can I understand what "int BuildingClass" is the one for the AG? Thank you again guys, you're very helpful! :)
 
45°38'N-13°47'E;12126980 said:
I feel like God-Emperor's suggestion might be the easiest one. I'm not a very good coder to tell the truth, I'm still learning ;)

But I've seen that

CyGame().getBuildingClassCount(int BuildingClass) --> Returns (int) the number of buildings of type 'BuildingClass' in the game for all players.
So could I use something like


if CyGame().getBuildingClassCount(int BuildingClass) = 1
{
iTotalVictoryScore += 100;
}

Would this suffice? It looks too easy to me so probably I'm missing something; even if it would suffice, another problem is that I don't know what "int BuildingClass" is the right one for Ascension Gate. Any idea on how can I understand what "int BuildingClass" is the one for the AG? Thank you again guys, you're very helpful! :)

That would tell you how many were built in the entire game, not by a specific player. However, CvPlayer::getBuildingClassCount() tells you for a particular player. You still have the issue of figuring out what the building class id is for the AG. For that you'll need to call cvInternalGlobals::getInfoTypeForString() passing the tag name for the AG building class ("BUILDINGCLASS_ASCENTION_GATE" at a guess - something like that). That last bit is distasteful because it places a dependency inside the DLL on a particular XML name being defined, but unless you introduce new tags there's not much you can do about that.
 
That would tell you how many were built in the entire game, not by a specific player.

Since there can be only 1 AG in the game, I suppose that would be enough, wouldn't it?

You still have the issue of figuring out what the building class id is for the AG. For that you'll need to call cvInternalGlobals::getInfoTypeForString() passing the tag name for the AG building class ("BUILDINGCLASS_ASCENTION_GATE" at a guess - something like that). That last bit is distasteful because it places a dependency inside the DLL on a particular XML name being defined, but unless you introduce new tags there's not much you can do about that.

"BUILDINGCLASS_ASCENTION_GATE" is correct, so should it be


if CyGame().getBuildingClassCount(cvInternalGlobals::getInfoTypeForString("BUILDINGCLASS_ASCENTION_GATE")) = 1
{
iTotalVictoryScore += 100;
}

I know it's distasteful, but until I learn more about coding, I think it suffice (if it works) ;)
 
45°38'N-13°47'E;12127050 said:
Since there can be only 1 AG in the game, I suppose that would be enough, wouldn't it?



"BUILDINGCLASS_ASCENTION_GATE" is correct, so should it be


if CyGame().getBuildingClassCount(cvInternalGlobals::getInfoTypeForString("BUILDINGCLASS_ASCENTION_GATE")) = 1
{
iTotalVictoryScore += 100;
}

I know it's distasteful, but until I learn more about coding, I think it suffice (if it works) ;)

Who's score are you adding it to? The point about it being game-wide is that it will give the same result for EVERY player. That isn't what you want is it?
 
Who's score are you adding it to? The point about it being game-wide is that it will give the same result for EVERY player. That isn't what you want is it?

Uh, I think you're right, but then shouldn't it be CyTeam::getBuildingClassCount... ? I frankly don't know how mastery score works when playing with 2 or more players in the same team, I've never tried it. Thank you for your explanation Koshling, you're great as usual. :)

Edit: CyTeam, not CvTeam --> Sorry, it should be CvTeam
 
Mmmm... looks like there's still something missing..

if CvPlayer().getBuildingClassCount(cvInternalGlobals::getInfoTypeForString("BUILDINGCLASS_ASCENSION_GATE")) = 1;
{
iTotalVictoryScore += 100;
}

return iTotalVictoryScore;


I get an error compiling: error C2059: syntax error : ''<L_TYPE_ambig>'' on the line which begins with "if CvPlayer"
 
Put your line inside the iI player loop in the function you quoted above, and instead of CvPlayer., start off with 'GET_PLAYER((PlayerTypes)iI).'
 
Put your line inside the iI player loop in the function you quoted above, and instead of CvPlayer., start off with 'GET_PLAYER((PlayerTypes)iI).'

Mmmm... it doesn't work, but I think I understand why; if I've understood you correctly, it should read

for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount(cvInternalGlobals::getInfoTypeForString("BUILDINGCLASS_ASCENSION_GATE"))) = 1;
{
iTotalVictoryScore += 100;
}
}

Correct? Problem is that I get

'cvInternalGlobals::getInfoTypeForString' : illegal call of non-static member function

Is it possible that cvInternalGlobals is not included in A New Dawn but only in C2C? --> Disregard. I don't think that's the problem anyway.
 
== not = in "if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount (cvInternalGlobals::getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE"))) = 1;"

Also... cvInternalGlobals:: is incorrect. Should be (BuildingTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")

thus:
Code:
if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount ((BuildingTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")))==1;
 
== not = in "if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount (cvInternalGlobals::getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE"))) = 1;"

Also... cvInternalGlobals:: is incorrect. Should be (BuildingTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")

thus:
Code:
if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount ((BuildingTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")))==1;

I think I've done it. At least I've been able to compile the dll. There was still a couple of errors
Code:
((BuildingTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")))==1;

should have been
Code:
((BuildingClassTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")) == 1)

Thank you Thunderbird and Koshling, you are both great! I'll let you know if everything works as expected. :)
 
Yeah.. I wasn't paying much attention to the ')'s there... sorry about that. Otherwise, cool! Good job!

My suggestion would be to down the road use a new tag to eliminate this hardcoding. It's generally bad form to have to specify a specific entry in the xml (in case it gets removed or changed somehow) and you may find other building situations you'd like to play into that system as well. Having a tag would enable an easy adjustment there, not only for you but future modders perhaps as well.
 
Yeah.. I wasn't paying much attention to the ')'s there... sorry about that. Otherwise, cool! Good job!

My suggestion would be to down the road use a new tag to eliminate this hardcoding. It's generally bad form to have to specify a specific entry in the xml (in case it gets removed or changed somehow) and you may find other building situations you'd like to play into that system as well. Having a tag would enable an easy adjustment there, not only for you but future modders perhaps as well.

It will be my next step :) For the time being, I only want to check if everything works as expected! Thank you again!
 
You don't need a new tag. There is already a tag, which is how it works in the first place.

You'd want to: Loop over all building classes. In each of those you loop over all victory types. In that loop you check for a victory threshold that is greater than 0 for this victory type for this building class. When you find one, check to see if the team has built enough to win. If so, give the points.

This would work fine if any given victory condition can only require one building class, but if you want to be able to set it up to require more than one it needs to be slightly more complex (like if you need 2 of class X and 1 of class Y to win).

The actual victory check used to win a game checks if the team has built it so that would probably be the way to go, not per player as your code is currently doing.

The key function for this, which you'd use to get the number of buildings of class iBuildingClass needed to win victory type iVictory:
Code:
GC.getBuildingClassInfo((BuildingClassTypes)iBuildingClass).getVictoryThreshold((VictoryTypes)iVictory)
 
Well, it looks like I was being too positive about solving my problem. After a few tests, I've seen it doesn't work; moreover, I've discovered a bug in the Mastery Victory code, even without any changes to introduce score from Ascension Gate.
But it's probably something related to Ascension Gate or SpaceShip: in fact, if you start a game with Mastery Victory and then use the worldbuilder to build an AG, after 1 turn in the Victory Screen you can see that you get 100 points and your spaceship has been launched (even if you haven't built a single part of it). So probably there's a bug in how SS points are calculated in CvTeam.cpp
Code:
	//Starship points!  Big money.
	if (GC.getGame().getStarshipLaunched(getID()))
	{
		iTotalVictoryScore += 100;
	}

Or better, this part of the code understands that if you build an AG, you've launched a SS.
And it looks like it's true for both AND and C2C (I've just tested it, and the problem arises in C2C too): in fact, the code in CvTeam.cpp is the same for AND and C2C.
So I could try what God-Emperor suggested, but I still have not a very clear idea on how to do it and moreover there's always the score bug I've just mentioned. The code I've discussed about some posts ago with Thunderbird has anyway no effect at all. Adding
Code:
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if ((BuildingClassTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")) == 1);
{
iTotalVictoryScore += 100;
}
}

doesn't add those 100 points to the score when I build AG, and I don't know why. On the contrary, when I build AG 100 points are added from SS launch (SS that I haven't built yet). I know which points come where from because I've tried changing the score given from AG and SS.
 
The first issue, the bug you're talking about... I'd have to look into getStarshipLaunched but its a bit suspicious in the naming 'get' which is usually an integer call rather than a boolean and it seems like there should be a boolean to that as in 'has the starship launched?' rather than what it seems to be saying being 'what's the number of starship(s) launched?'. I suspect the call itself may actually LAUNCH the starship! Something to look into. I'll take a look at that later tonight perhaps.

As for the line we discussed, you're asking if the building class exists, not if the player has built it. The line is correct except you need to start with
Code:
if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount
and THAT's why you had one too many ')'s. It apparently should've had that extra but you eliminated the first part of the line so it wasn't paired to what it should've been paired to. So, to be specific, the whole line should read:
Code:
if (GET_PLAYER((PlayerTypes)iI).getBuildingClassCount((BuildingTypes)GC.getInfoTypeForString("BUILDING CLASS_ASCENSION_GATE")))==1;


Third, you probably don't need that line at all as GE pointed out... there should be a tag on buildings VictoryTypes or something to that extent, that can be used to set up a Mastery victory score point adjustment. You'd have to find an example of use somewhere in the xml or read into the schema to see how its supposed to be setup.
 
Back
Top Bottom