Allocating Zero or Less Memory in CvXMLLoadUtility...

Well, my logic works too, that's good.

Now, I have one minor detail, and then some fine tuning. First off, when I am not running any of the civics that I need for the building it doesn't show up in the city examine screen. It should be there, but greyed out, but instead it's just gone. Any idea on how to fix this?

The only code I've added is in CvPlayer, in Can Construct and CvGameTextMgr...
 
canConstruct() takes a "bVisible" bool that you need to check. If it's true, it is asking if the building icon should show up. If it's false, it's asking if it should be enabled (buildable). So if it's true, skip your checking of civics.

How it works is that canConstruct() is called twice for each building. First with bVisible set to true. If you return false, the building is skipped and not shown. If it's true, canConstruct() is called again with bVisible set to false. If you return false, the building's icon is disabled so you cannot click it.
 
canConstruct() takes a "bVisible" bool that you need to check. If it's true, it is asking if the building icon should show up. If it's false, it's asking if it should be enabled (buildable). So if it's true, skip your checking of civics.

How it works is that canConstruct() is called twice for each building. First with bVisible set to true. If you return false, the building is skipped and not shown. If it's true, canConstruct() is called again with bVisible set to false. If you return false, the building's icon is disabled so you cannot click it.

Okay... I understand that, but I didn't set bVisible to true or false...

Perhaps I'm too tired to think straight...

Please just tell me what I did wrong, and why it's wrong...

Code:
bool CvPlayer::canConstruct(BuildingTypes eBuilding, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{...
    bool bValidOrCivic = false;
    bool bNoReqOrCivic = true;
    bool bValidAndCivic = true;
    bool bReqAndCivic = true;
    for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
    {
        if (GC.getBuildingInfo(eBuilding).isPrereqOrCivics(iI))
        {
            bNoReqOrCivic = false;
            if (isCivic(CivicTypes(iI)))
            {
                bValidOrCivic = true;
            }
        }
        
        if (GC.getBuildingInfo(eBuilding).isPrereqAndCivics(iI))
        {
            bReqAndCivic = true;
            if (!isCivic(CivicTypes(iI)))
            {
                bValidAndCivic = false;
            }
        }
    }
    
    if (!bNoReqOrCivic && !bValidOrCivic)
    {
        return false;
    }

    if (bReqAndCivic && !bValidAndCivic)
    {
        return false;
    }
...
}
 
No, you don't need to set bTestVisible. You need to check it's value.

Code:
bool CvPlayer::canConstruct(BuildingTypes eBuilding, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{...
[B][COLOR="Red"]    if (!bTestVisible)
    {[/COLOR][/B]
        bool bValidOrCivic = false;
        ...
        if (bReqAndCivic && !bValidAndCivic)
        {
            return false;
        }
[B][COLOR="Red"]    }[/COLOR][/B]
...
}
 
No, you don't need to set bTestVisible. You need to check it's value.

Code:
bool CvPlayer::canConstruct(BuildingTypes eBuilding, bool bContinue, bool bTestVisible, bool bIgnoreCost) const
{...
[B][COLOR=Red]    if (!bTestVisible)
    {[/COLOR][/B]
        bool bValidOrCivic = false;
        ...
        if (bReqAndCivic && !bValidAndCivic)
        {
            return false;
        }
[B][COLOR=Red]    }[/COLOR][/B]
...
}

Ohh...

Okay. I probably should have looked at the CvCity Can Contruct for examples before posting...

I am more tired than I though...
 
Okay I have 2 major hurdles left. I fixed the CvGameTextMgr code, set up the Civilopedia to correctly show the civic buttons on the building page, but I am having trouble making the buildings "go obsolete" when the player switches civics. I figured the easiest way would be just to change the getNumActiveBuilding() function in CvCity to 0 if the player is no longer running the necessary civics. However, despite adding the code there, the buildings still gave their usual modifiers. Either I'm looking at the wrong function, in which case I would like to hear what function I should be looking at, or my code is not properly set up, in which case, could someone tell me what I did wrong?

For reference, getNumActiveBuildings() after my modifications:

Code:
int CvCity::getNumActiveBuilding(BuildingTypes eIndex) const
{
	FAssertMsg(eIndex != NO_BUILDING, "BuildingType eIndex is expected to not be NO_BUILDING");

	if (GET_TEAM(getTeam()).isObsoleteBuilding(eIndex))
	{
		return 0;
	}
	//Afforess

		bool bValidOrCivic = false;
		bool bNoReqOrCivic = true;
		bool bValidAndCivic = true;
		bool bReqAndCivic = true;
		for (int iI = 0; iI < GC.getNumCivicInfos(); iI++)
		{
			if (GC.getBuildingInfo(eIndex).isPrereqOrCivics(iI))
			{
				bNoReqOrCivic = false;
				if (GET_PLAYER(getOwnerINLINE()).isCivic(CivicTypes(iI)))
				{
					bValidOrCivic = true;
				}
			}
			
			if (GC.getBuildingInfo(eIndex).isPrereqAndCivics(iI))
			{
				bReqAndCivic = true;
				if (!(GET_PLAYER(getOwnerINLINE()).isCivic(CivicTypes(iI))))
				{
					bValidAndCivic = false;
				}
			}
		}
		
		if (!bNoReqOrCivic && !bValidOrCivic)
		{
			return 0;
		}

		if (bReqAndCivic && !bValidAndCivic)
		{
			return 0;
		}
	
	return (getNumBuilding(eIndex));
}
 
CvCity::processBuilding() is what applies building effects to the city. Changing getNumBuilding() doesn't do anything immediately.

You need to pattern it after how obsolete buildings work. In CvTeam::setHasTech(), if the tech obsoletes a building it calls CvTeam::changeObsoleteBuildingCount(). It tracks the number of techs that have made each building go obsolete. You won't need a count since you'll be checking all buildings for all civics at once, but you do need an update function like this one in CvPlayer.

To really be safe you need to catch this at the end of setCivics(). This function is called to set a single civic option type, such as Government, to a value. It is called from a few places and is exposed to Python. Switching the obsolete buildings there will cause churn, but it ensures you maintain a consistent state for the buildings.

Create a new function CvPlayer::adjustObsoleteBuildingsForCivics() and call it in CvPlayer::setCivics():

Code:
void CvPlayer::setCivics(CivicOptionTypes eIndex, CivicTypes eNewValue)
{
	...
	eOldCivic = getCivics(eIndex);

	if (eOldCivic != eNewValue)
	{
		m_paeCivics[eIndex] = eNewValue;

		if (eOldCivic != NO_CIVIC)
		{
			processCivics(eOldCivic, -1);
		}
		if (getCivics(eIndex) != NO_CIVIC)
		{
			processCivics(getCivics(eIndex), 1);
		}

		[B][COLOR="Red"]adjustObsoleteBuildingsForCivics();[/COLOR][/B]

		GC.getGameINLINE().updateSecretaryGeneral();

		GC.getGameINLINE().AI_makeAssignWorkDirty();
		...

This new function should look very similar to CvTeam::changeObsoleteBuildingCount(). It needs to loop over all cities, telling each one to check every building for obsolescence based on civics. Civics are changed infrequently, but there is a way to speed this up a bit. I say get it working normally before you worry about speed, though.

Code:
void CvPlayer::changeObsoleteBuildingCount() const
{
    for each city
        for each building type
            if city.getNumBuilding(building) > 0
                city.processBuilding(building, -x or x where x is getnumbuilding)
}
 
Okay, I've got the function written, but as of now, it doesn't do anything.


Spoiler :

Code:
void CvPlayer::adjustObsoleteBuildingsForCivics()
{
	CvCity* pLoopCity;
	int iLoop;
	int iI, iJ;

	for (iI = 0; iI < GC.getNumBuildingInfos();  iI++)
	{
		for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
		{
			if (GET_PLAYER((PlayerTypes)iJ).isAlive())
			{
				if (GET_PLAYER((PlayerTypes)iJ).getTeam() == getID())
				{
					for (pLoopCity = GET_PLAYER((PlayerTypes)iJ).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iJ).nextCity(&iLoop))
					{
						if (pLoopCity->getNumBuilding((BuildingTypes)iI) > 0)
						{
							pLoopCity->processBuilding((BuildingTypes)iI, ((GET_TEAM(getTeam()).isObsoleteBuilding((BuildingTypes)iI)) ? -pLoopCity->getNumBuilding((BuildingTypes)iI) : pLoopCity->getNumBuilding((BuildingTypes)iI)), true);
						}
					}
				}
			}
		}
	}
}
 
It should not loop over players nor check for their inclusion in a team. This is a CvPlayer method which means that you should only loop over "this" player's cities.

Code:
void CvPlayer::adjustObsoleteBuildingsForCivics()
{
	CvCity* pLoopCity;
	int iLoop;
	int iI;

	for (iI = 0; iI < GC.getNumBuildingInfos();  iI++)
	{
		bool bObsolete = isBuildingDisabledByCivics((BuildingTypes)iI);

		for (pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = .nextCity(&iLoop))
		{
			if (pLoopCity->getNumBuilding((BuildingTypes)iI) > 0)
			{
				pLoopCity->processBuilding((BuildingTypes)iI, bObsolete ? -pLoopCity->getNumBuilding((BuildingTypes)iI) : pLoopCity->getNumBuilding((BuildingTypes)iI)), true);
			}
		}
	}
}

Next you need to use the code you posted previously to check if the building should be disabled by the current civics. Place it into a new function as you may want to use it in multiple places.

Code:
bool CvPlayer::isBuildingDisabledByCivics(BuildingTypes eBuilding) const
 
It should not loop over players nor check for their inclusion in a team. This is a CvPlayer method which means that you should only loop over "this" player's cities.
Next you need to use the code you posted previously to check if the building should be disabled by the current civics. Place it into a new function as you may want to use it in multiple places.

Code:
bool CvPlayer::isBuildingDisabledByCivics(BuildingTypes eBuilding) const

Okay, I have that function:

Code:
bool CvPlayer::isBuildingDisabledByCivics(BuildingTypes eBuilding) const
{
    bool bValidOrCivic = false;
    bool bNoReqOrCivic = true;
    bool bValidAndCivic = true;
    bool bReqAndCivic = true;
    for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
    {
        if (GC.getBuildingInfo(eBuilding).isPrereqOrCivics(iI))
        {
            bNoReqOrCivic = false;
            if (isCivic(CivicTypes(iI)))
            {
                bValidOrCivic = true;
            }
        }
        
        if (GC.getBuildingInfo(eBuilding).isPrereqAndCivics(iI))
        {
            bReqAndCivic = true;
            if (!isCivic(CivicTypes(iI)))
            {
                bValidAndCivic = false;
            }
        }
    }
            
    if (!bNoReqOrCivic && !bValidOrCivic)
    {
        return true;
    }

    if (bReqAndCivic && !bValidAndCivic)
    {
        return true;
    }
    
    return false;

}
 
It's in the code I posted for adjustObsoleteBuildingsForCivics():

Code:
bool bObsolete = isBuildingDisabledByCivics((BuildingTypes)iI);
 
It's in the code I posted for adjustObsoleteBuildingsForCivics():

Code:
bool bObsolete = isBuildingDisabledByCivics((BuildingTypes)iI);

*facepalm*

Oh. Duh. I should read the code... Sorry for wasting your time. :(
 
I'm sure you noticed by now, but I found that I rather liked that the buildings still worked even without the proper civics. With Revolutions, Anarchy is it's own punishment, so no one willingly exploits it, and many of the buildings are already balanced for when a player switches out of the civic.

That said, I am getting an assert with a debug build. The XML works fine and I have no discernible problems, except for this assert:

Assert Failed

File: CvInfos.cpp
Line: 13550
Expression: GC.getInfoTypeForString(m_aszPrereqAndCivicsforPass3[iI]) >= 0
Message: Warning, about to leak memory in CvBuildingInfo::readPass3

----------------------------------------------------------

Leaking memory is very, very, bad; as I understand it. The funny thing is that the PrereqOrCivic Array isn't leaking memory or giving this warning, just the And array...

I don't understand it at all...
 
Keep in mind that the message is programmer-controlled and in this case quite wrong. The assert is checking that the string XML key in slot iI is a valid CivicType by passing it to getInfoTypeForString(). This says to me that one of the civics specified in that array is using the wrong XML key (typo?).
 
Back
Top Bottom