Adding a new Array, having troubles.

Afforess

The White Wizard
Joined
Jul 31, 2007
Messages
12,239
Location
Austin, Texas
I've (at least I think I have) added a new array to CvBuildingInfos. I cloned DomainProductionModifer, and changed it to UnitClassProductionModifiers. I'm pretty sure I did all the C++ changes correct, (I only edited the CvCity.cpp/h and Infos.cpp/h, are there any other files I missed? [other than CvGameTextMgr, CvCityAI, and the Cy files, I will do those later])

Well, my changes compiled just fine. However, when I updated the Schema and added a test XML entry to the castle, I get XML loading errors. Any ideas why?

Schema:

Code:
    <ElementType name="iProductionModifier" content="textOnly" dt:type="int"/>
    <ElementType name="UnitClassProductionModifier" content="eltOnly">
        <element type="UnitClassType"/>
        <element type="iProductionModifier"/>
    </ElementType>
    <ElementType name="UnitClassProductionModifiers" content="eltOnly">
        <element type="UnitClassProductionModifier" minOccurs="0" maxOccurs="*"/>
    </ElementType>

XML Addition:


Code:
            <UnitClassProductionModifiers>
                <UnitClassProductionModifier>
                    <UnitClassType>UNITCLASS_WORKER</UnitClassType>
                    <iProductionModifier>100</iProductionModifier>
                </UnitClassProductionModifier>
            </UnitClassProductionModifiers>

If you need to see the code, I'll post that too.
 
Isn't there already an iProductionModifier for buildings that modifier production rate such as Forge, Factory, etc? Make sure you didn't add a duplicate element type.
 
Isn't there already an iProductionModifier for buildings that modifier production rate such as Forge, Factory, etc? Make sure you didn't add a duplicate element type.

That isn't (or shouldn't be) the problem. There is like 5 or 6 iProductionModifier in the schema already.
 
I see exactly one:

Code:
[B]<ElementType name="iProductionModifier" content="textOnly" dt:type="int"/>[/B]
<ElementType name="DomainProductionModifier" content="eltOnly">
	<element type="DomainType"/>
	<element type="iProductionModifier"/>
</ElementType>

You can define each name only once using <ElementType>. You can reference it any number of times using <element>. Remove you extra <ElementType name="iProductionModifier" .../>.
 
I see exactly one


I see it elsewhere:

Code:
[B]<ElementType name="iProductonModifier" content="textOnly" dt:type="int"/>[/B]
    <ElementType name="BonusProductionModifier" content="eltOnly">
        <element type="BonusType"/>
        <element type="iProductonModifier"/>
    </ElementType>

EDIT:

Ohhh. That one is actually a misspelling. Wow.

I changed it to:
Code:
	[B]<ElementType name="iProductionModifierPercent" content="textOnly" dt:type="int"/>[/B]
	<ElementType name="UnitClassProductionModifier" content="eltOnly">
		<element type="UnitClassType"/>
		[B]<element type="iProductionModifierPercent"/>[/B]
	</ElementType>
	<ElementType name="UnitClassProductionModifiers" content="eltOnly">
		<element type="UnitClassProductionModifier" minOccurs="0" maxOccurs="*"/>
	</ElementType>

But I still get XML load errors. Do I need to update my sources to reflect this change? I'm afraid I don't really understand how the schema's are made, at least not for arrays.
 
Nvm. I forgot to declare UnitClassType as an ElementType. I fixed it.
 
Hmm. My game will load with my schema and XML changes, but adding that building (In my case, I modded the Castle) doesn't do anything, when it should double the speed of worker production.

Could you look at my CvCity code?

Code:
int CvCity::getUnitClassProductionModifier(UnitClassTypes eIndex) const	//Afforess									 
{
	FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
	FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex expected to be < GC.getNumUnitClassInfos()");
	return m_aiUnitClassProductionModifier[eIndex];
}


void CvCity::changeUnitClassProductionModifier(UnitClassTypes eIndex, int iChange) //Afforess
{
	FAssertMsg(eIndex >= 0, "eIndex expected to be >= 0");
	FAssertMsg(eIndex < GC.getNumUnitClassInfos(), "eIndex expected to be < GC.getNumUnitClassInfos()");
	m_aiUnitClassProductionModifier[eIndex] = (m_aiUnitClassProductionModifier[eIndex] + iChange);
}
...
pStream->Read(GC.getNumUnitClassInfos(), m_aiUnitClassProductionModifier); //Afforess
...
pStream->Write(GC.getNumUnitClassInfos(), m_aiUnitClassProductionModifier); //Afforess
...
m_aiUnitClassProductionModifier = new int[GC.getNumUnitClassInfos()]; //Afforess
...
SAFE_DELETE_ARRAY(m_aiUnitClassProductionModifier);
...
...
	for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++) //Afforess
	{
		m_aiUnitClassProductionModifier[iI] = 0;
	}
...


int CvCity::getProductionModifier(UnitTypes eUnit) const
{
	int iI;

	int iMultiplier = GET_PLAYER(getOwnerINLINE()).getProductionModifier(eUnit);

	iMultiplier += getDomainProductionModifier((DomainTypes)(GC.getUnitInfo(eUnit).getDomainType()));
	[B]iMultiplier += getUnitClassProductionModifier((UnitClassTypes)(GC.getUnitInfo(eUnit).getUnitClassType()));[/B] //Afforess
...

I believe that's all of it. I'm not sure what's wrong though...
 
Did you add a call to changeUnitClassProductionModifier() in processBuilding(), looping over all unit classes and calling the function for each non-zero value in the CvBuildingInfo?
 
Did you add a call to changeUnitClassProductionModifier() in processBuilding(), looping over all unit classes and calling the function for each non-zero value in the CvBuildingInfo?

Nope. I forgot that. So I should add this to CvCity:processBuilding

Code:
        for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
        {
            changeUnitClassProductionModifier(((UnitClassTypes)iI), GC.getBuildingInfo(eBuilding).getUnitClassProductionModifier(iI) * iChange);
        }
 
Code:
<ElementType name="iProductionModifier" content="textOnly" dt:type="int"/>

and

Code:
<element type="iProductonModifier"/>

Are very different items. The first one declares what iProductionModifier is, there must be only one of those for each item in any schema. The second one just USES iProductionModifier, and there can be as many of those as you want to have.

It is basically the difference between:

Code:
CvUnit::setHasPromotion(PromotionTypes ePromotion, bool bNewValue)

and

Code:
setHasPromotion((PromotionTypes)iI, true);

You can use the second one all you like, but if you have an exact duplication of the first one... things break.
 
Thanks EmperorFool.

Now, if I'm understanding Xienwolf correctly, my schema should be:

Code:
	<ElementType name="UnitClassType" content="textOnly"/>
	<ElementType name="UnitClassProductionModifier" content="eltOnly">
		<element type="UnitClassType"/>
		<element type="iProductionModifier"/>
	</ElementType>
	<ElementType name="UnitClassProductionModifiers" content="eltOnly">
		<element type="UnitClassProductionModifier" minOccurs="0" maxOccurs="*"/>
	</ElementType>
 
Update.

I just tested it out with that Schema and the new CvCity.cpp. It's working great. Thanks guys.
 
Okay, now I'm trying to set the values in CvCityAI to something reasonable, but I am not very experienced with coding AI logic. I would like the AI to value a building that gives a unitclass production modifier a lot more if they need a bunch of those type of units, say if it needed 15 more riflemen, and there was a building that gave 100% riflemen production. That way the AI would be smart and build the building, then build the 15 riflemen. But I don't know how to do this...

Currently my AI code is:

Code:
			for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++) //Afforess
				{
					iValue += (kBuilding.getUnitClassProductionModifier(iI) / 10);
					
					if (bIsHighProductionCity)
					{
						iValue += (kBuilding.getUnitClassProductionModifier(iI) / 5);
					}
				}
 
That's a tricky call to make, most of the AI's "I need units" functions revolve around AI Types, not Unit Types, so deciding that they are likely to build some unit type is going to depend on how you have AI types assigned through your mod.
 
That's a tricky call to make, most of the AI's "I need units" functions revolve around AI Types, not Unit Types, so deciding that they are likely to build some unit type is going to depend on how you have AI types assigned through your mod.

Hmm. Well, I know my AI code as it stands now it pretty terrible. The AI would build the building even if it only gave a production boost to a unit that players couldn't actually build. Can you offer any suggestions?
 
Code:
		eLoopUnit = ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)));

						if (canTrain(eLoopUnit))

This pair of lines will ensure that the city can build the unit.

Code:
eUnitAI = GC.getUnitInfo(eLoopUnit).getDefaultUnitAIType();
							iOriginalValue = GET_PLAYER(getOwnerINLINE()).AI_unitValue(eLoopUnit, eUnitAI, area());

This pair of lines will give you a value number which will reflect the opinion of the player toward that particular unit for the job it is most likely to perform.


With those two in use, it should work fairly well, at least at deciding which building among many which offer bonuses toward different units, it should build.

I took those lines from UnitTypes CvCityAI::AI_bestUnitAI, which is another function that may prove useful to you. Instead of using all of the above, you could make a building only have value for this field if the unit identified as matching the bonus is also the answer given from this function
Code:
AI_bestUnitAI(...) == ((UnitTypes)(GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI)))
 
Putting those together in an effort to keep this function performant (canTrain() can be costly) gives this:

Code:
				iOriginalValue = 0;
				
				for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++) //Afforess
				{
					int iModifier = kBuilding.getUnitClassProductionModifier(iI);
					
					if (iModifier != 0)
					{
						UnitTypes eLoopUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI);
						
						if (canTrain(eLoopUnit))
						{
							UnitAITypes eUnitAI = GC.getUnitInfo(eLoopUnit).getDefaultUnitAIType();
							iOriginalValue += GET_PLAYER(getOwnerINLINE()).AI_unitValue(eLoopUnit, eUnitAI, area()) * iModifier / 10;
					}
				}
				
				if (bIsHighProductionCity)
				{
					iOriginalValue *= 3; // adding 10% and 20% is same as adding 30% or 3 * 10%
				}
 
Update:

I changed that code to this, to keep the code uniform and avoid declaring another variable, but I'm getting a compiler error...

Code:
				iTempValue = 0;
				
				for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++) //Afforess
				{
					int iModifier = kBuilding.getUnitClassProductionModifier(iI);
					
					if (iModifier != 0)
					{
						UnitTypes eLoopUnit = (UnitTypes)GC.getCivilizationInfo(getCivilizationType()).getCivilizationUnits(iI);
						
						if (canTrain(eLoopUnit))
						{
							UnitAITypes eUnitAI = GC.getUnitInfo(eLoopUnit).getDefaultUnitAIType();
							iTempValue += GET_PLAYER(getOwnerINLINE()).AI_unitValue(eLoopUnit, eUnitAI, area()) * iModifier / 10;
					}
				}
				
				if (bIsHighProductionCity)
				{
					iTempValue *= 3; // adding 10% and 20% is same as adding 30% or 3 * 10%
				}
				
				iValue += iTempValue;

The error is
CvCityAI.cpp(4348) : error C2440: 'initializing' : cannot convert from 'int' to 'UnitAITypes'
Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
 
Top Bottom