The Expression System

The first one did work the second one doesn't :( I assumed that the condition is on the outcome on the unit in the Unit Infos file. Or should I have put it on the outcome in the Outcomes Infos file?
It should be in the unit infos file on the same level as the outcome results like EventTrigger, iHappinessTimer and Yields.
 
The action button is not appearing in any city with any population. I tried in cities size 1,2 and 5 - no button. Can you have a look and see what I did wrong?

On the UnitInfos
Code:
				<Action>
					<MissionType>MISSION_JOIN_CITY_POPULATION</MissionType>
					<ActionOutcomes>
						<Outcome>
							<OutcomeType>OUTCOME_JOIN_CITY_POPULATION</OutcomeType>
							<iChance>100</iChance>
							<EventTrigger>EVENTTRIGGER_ADD_ONE_POPULATION</EventTrigger>
							<PlotCondition>
							  <IntegrateOr>
							    <RelationType>RELATION_ASSOCIATED</RelationType>
							    <GameObjectType>GAMEOBJECT_CITY</GameObjectType>
							    <GreaterEqual>
							      <Constant>4</Constant>
							      <AttributeType>ATTRIBUTE_POPULATION</AttributeType>
							    </GreaterEqual>
							  </IntegrateOr>
							</PlotCondition>
						</Outcome>
					</ActionOutcomes>
				</Action>

The Outcome
Code:
		<OutcomeInfo>
			<Type>OUTCOME_JOIN_CITY_POPULATION</Type>
			<Description>TXT_KEY_OUTCOME_JOIN_CITY_POPULATION</Description>
			<Message>TXT_KEY_OUTCOME_JOIN_CITY_POPULATION_MESSAGE</Message>
			<PrereqTech>NONE</PrereqTech>
			<bNeutralTerritory>0</bNeutralTerritory>
			<bHostileTerritory>0</bHostileTerritory>
			<bCity>1</bCity>
		</OutcomeInfo>

The Mission
Code:
		<MissionInfo>
			<Type>MISSION_JOIN_CITY_POPULATION</Type>
			<Description>TXT_KEY_MISSION_JOIN_CITY_POPULATION</Description>
			<Help>TXT_KEY_MISSION_JOIN_CITY_POPULATION_HELP</Help>
			<Waypoint>NONE</Waypoint>
			<EntityEventType>ENTITY_EVENT_GREAT_EVENT</EntityEventType>
			<iTime>0</iTime>
			<bTarget>0</bTarget>
			<bBuild>0</bBuild>
			<bSound>0</bSound>
			<bVisible>1</bVisible>
			<Button>Art/Interface/Missions/JoinCity.dds</Button>
		</MissionInfo>
 
It is possible that there is a bug in some of the expression parts like IntegrateOr.

Try this:
Code:
<PlotCondition>
  <Greater>
    <Constant>5</Constant>
    <IntegrateSum>
      <RelationType>RELATION_ASSOCIATED</RelationType>
      <GameObjectType>GAMEOBJECT_CITY</GameObjectType>
      <AttributeType>ATTRIBUTE_POPULATION</AttributeType>
    </IntegrateSum>
  </Greater>
</PlotCondition>
 
Argh, I had everything almost working, the buttons showing up at the right time and everything. Then I changed the buttons and text and something else now it is not showing up again. maybe it is just a sign that I should just get ready and go to the family picnic.
 
@AIAndy

How do I do this ...

I am thinking we should apply some Pollution properties to some of the civics. something like ...

Green (Economic)
-10 Air Pollution Per Turn
-5 Water Pollution Per Turn

Green Agriculture (Agriculture)
-5 Air Pollution Per Turn
-10 Water Pollution Per Turn

Corporate Agriculture (Agriculture)
+5 Air Pollution Per Turn
+10 Water Pollution Per Turn

What do you think and what code do you add to do it?

What is the expression code needed for Civics?
 
@AIAndy

How do I do this ...



What is the expression code needed for Civics?
To what should those pollutions be applied each turn? Every plot of the Civs that runs those civics? Every city? Specific buildings? Farms? Something else?

The boolean expression to check for a civic is:
Code:
<Has>
  <GOMType>GOM_CIVIC</GOMType>
  <ID>CIVIC_WHATEVER</ID>
</Has>
 
Every turn to every city in that empire that has that civic chosen.

Not every plot, just every city.
Then you use PropertyManipulators similar to what is already used in some traits and just add them to the civic info.

Code:
			<PropertyManipulators>
  				<PropertySource>
   					<PropertySourceType>PROPERTYSOURCE_CONSTANT</PropertySourceType>
						<PropertyType>PROPERTY_AIR_POLLUTION</PropertyType>
						<GameObjectType>GAMEOBJECT_CITY</GameObjectType>
						<RelationType>RELATION_ASSOCIATED</RelationType>
						<iAmountPerTurn>-10</iAmountPerTurn>
				</PropertySource>
			</PropertyManipulators>
 
@AIAndy

Oh one more question. If I wanted to do the same for flammability how would I do it? Note that it would be a static value like how buildings do flammability.
That is not implemented (in general, static properties are still pretty basic in functionality compared to the dynamic ones).
 
That is not implemented (in general, static properties are still pretty basic in functionality compared to the dynamic ones).

Should we perchance convert Flammability into a new Property? Isn't teh old system basically your proof of concept while you were working on the Extended Property System we use now?
 
Should we perchance convert Flammability into a new Property? Isn't teh old system basically your proof of concept while you were working on the Extended Property System we use now?
They serve different purposes. Static properties have their place as well.
 
As Thunderbrd works on DLL modding as well now, here is some information about how to use the expressions in the DLL for new or old tags:

If you want to use a boolean expression in an info XML, you add a BoolExpr* to the class, replacing the bool if it is an old tag.
Initialize it with NULL in the constructor and do a SAFE_DELETE on it in the destructor.

To read it from the XML you first set it to the tag and then call the static method read of BoolExpr, which returns a pointer to the expression.
Example:
Code:
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"TrainCondition"))
	{
		m_pExprTrainCondition = BoolExpr::read(pXML);
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}

In copyNonDefault you just move the expression from the old class to the new, if the new one does not have a boolean expression (that means the expression is only changed if the new instance actually defines an expression).
Example:
Code:
	if (!m_pExprTrainCondition)
	{
		m_pExprTrainCondition = pClassInfo->m_pExprTrainCondition;
		pClassInfo->m_pExprTrainCondition = NULL;
	}

For the checksum generation BoolExpr has the method getCheckSum.
Example:
Code:
	if (m_pExprTrainCondition)
		m_pExprTrainCondition->getCheckSum(iSum);

If the info class has serialization methods from/to a stream, the BoolExpr class has the static method readExpression that takes the stream and returns the expression pointer and the method write which writes the expression into the passed stream. As your pointer is NULL if no expression is defined, you first need to read/write a bool that defines if the expression is present or not.
Example read:
Code:
	bool bExpr = false;
	stream->Read(&bExpr);
	if (bExpr)
	{
		m_pExprTrainCondition = BoolExpr::readExpression(stream);
	}
Example write:
Code:
	if (m_pExprTrainCondition)
	{
		stream->Write(true);
		m_pExprTrainCondition->write(stream);
	}
	else
	{
		stream->Write(false);
	}

In the schema you just remove the type information of the ElementType and don't define it as TextOnly or EltOnly or anything. The open model will then allow you to use the expression tags (if the expression tags are not present yet in the schema, copy them over from one which has them).

Now the important thing is how to use the expression.
The methods you will use are these:
Code:
virtual bool evaluate(CvGameObject* pObject)
virtual void buildDisplayString(CvWStringBuffer& szBuffer)
evaluate is the most important one, as it evaluates the expression in the context of a game object and then returns true or false.
buildDisplayString renders the expression into text form and appends it to the buffer. That is useful to display the information in help texts (disclaimer: not everything is properly rendered into text yet and there are a bit too many brackets).

I usually return the BoolExpr* from a method of the info class and then evaluate it in the caller's context but passing the game object to the method and just returning the result would be valid too.
Example:
Code:
BoolExpr* CvUnitInfo::getTrainCondition()
{
	return m_pExprTrainCondition;
}

The context for evaluate is a game object, which can be any of game, team, player, city, unit or plot. You get the respective game object by calling getGameObject() or getGameObjectConst() on them.
Example:
Code:
		if (kUnit.getTrainCondition())
		{
			if (!kUnit.getTrainCondition()->evaluate(const_cast<CvGameObjectCity*>(getGameObjectConst())))
			{
				return false;
			}
		}
The ugly const_cast was necessary in this case as evaluate uses some methods that could possibly change something so I could not declare it const (although it does not actually change anything).

Finally an example of buildDisplayString, this case displaying the requirements for training this unit if it does not already evaluate to true:
Code:
	BoolExpr* pExpr = GC.getUnitInfo(eUnit).getTrainCondition();
	if (pExpr)
	{
		bool bEval = false;
		if (pCity)
		{
			bEval = pExpr->evaluate(const_cast<CvGameObjectCity*>(pCity->getGameObjectConst())); // Const wegcasten ist hier ok da evaluate nicht wirklich etwas ändert
		}
		if (!bEval)
		{
			szBuffer.append(NEWLINE);
			szBuffer.append(gDLL->getText("TXT_KEY_REQUIRES"));
			szBuffer.append(" ");
			pExpr->buildDisplayString(szBuffer);
		}
	}

IntExpr works very similar except that evaluate returns an int instead of a bool.
Finally found an extremely appropriate use for this (I think)... and the setup is going along nice and easy. Great tutorial!

But I actually need to know if this is going to work for what I'm trying to do...

I want to make the handing out of a FreePromotion from a building conditional to the unit that is receiving it. Is that possible to do with this kind of boolean tag? It occurred to me as I was setting this up that we may only be able to evaluate the building's details itself, and not the unit interacting with the city that has this building in it. Is that true? Or is it possible to make this work somehow?

I read through some of the previous tutorials thoroughly and it doesn't look like I can make this work unless the relationship of <is being given promotion> can be somehow defined so the unit itself can be evaluated by the building tag. That may be possible for ya' I don't know.


A little background if you want to look into this from a deeper perspective so we can develop a really great application for this rather than the patch I had initially considered:

I recently added the Throwing combat class. I believe this class, despite being unmounted, relies on being fleet of foot. So I enabled them to obtain the Speed promotion. However, that caused them to start receiving the Speed promotion from the Riding School (which obviously doesn't fit the concept of a 'riding' school.)

Upon a touch of research into the situation I realized that our building's free promos have absolutely no filter. If the unit itself CAN get the promotion on its own merits, the promotion will be given if the promotion is indicated as free by the building. There's no check against the unit's merits aside from if it can keep the promotion.

We have 3 free promotion slots from 3 linear reference tags and I've implemented a vector based FreePromos tag that pretty much works along the same lines but opens up an unlimited number of potential free promos. This was in preparation for the potential volume of equipment types to be handed out by a given building. However, I'd really like to enable a bit more filtering for if the unit can receive the promo or not.

For example, I don't want to have to disable the ability of Throwing units to gain the Speed promo - I just don't want them getting it from the Riding School but I'd love for the Riding School to continue to give that promo out for Mounted Units (and perhaps NOT for Wheeled units as that's not exactly 'Riding' either (Chariots and Wagons would be both Mounted and Wheeled) and Scouts are also gaining it from this inappropriate source as well.)

Primarily I'm thinking of the filters only from a Combat Class perspective right now but I realize some other filters could be useful, tech, civic, trait perhaps - and I was about to setup a whole new FilteredFreePromo tag so that I could establish a number of dependencies to be indicated for the specific FreePromotion IN that self-filtering tag. Then I remembered this and recalled that I'd been wanting to find a project to use it on but I'm thinking from everything you've explained that we may not be able to get it working - and furthermore, I'm wondering how I would I apply these conditions to specific free promotions indicated on the building. My answer to that so far has been to establish an expression condition that subordinates to each free promotion tag, thus we could have up to four varieties of conditions, one for each of the three static free promo tags and one for the unlimited list of FreePromos (this COULD become problematic and it certainly doesn't seem ideal.)

Any ideas?
 
Finally found an extremely appropriate use for this (I think)... and the setup is going along nice and easy. Great tutorial!

But I actually need to know if this is going to work for what I'm trying to do...

I want to make the handing out of a FreePromotion from a building conditional to the unit that is receiving it. Is that possible to do with this kind of boolean tag? It occurred to me as I was setting this up that we may only be able to evaluate the building's details itself, and not the unit interacting with the city that has this building in it. Is that true? Or is it possible to make this work somehow?

I read through some of the previous tutorials thoroughly and it doesn't look like I can make this work unless the relationship of <is being given promotion> can be somehow defined so the unit itself can be evaluated by the building tag. That may be possible for ya' I don't know.


A little background if you want to look into this from a deeper perspective so we can develop a really great application for this rather than the patch I had initially considered:

I recently added the Throwing combat class. I believe this class, despite being unmounted, relies on being fleet of foot. So I enabled them to obtain the Speed promotion. However, that caused them to start receiving the Speed promotion from the Riding School (which obviously doesn't fit the concept of a 'riding' school.)

Upon a touch of research into the situation I realized that our building's free promos have absolutely no filter. If the unit itself CAN get the promotion on its own merits, the promotion will be given if the promotion is indicated as free by the building. There's no check against the unit's merits aside from if it can keep the promotion.

We have 3 free promotion slots from 3 linear reference tags and I've implemented a vector based FreePromos tag that pretty much works along the same lines but opens up an unlimited number of potential free promos. This was in preparation for the potential volume of equipment types to be handed out by a given building. However, I'd really like to enable a bit more filtering for if the unit can receive the promo or not.

For example, I don't want to have to disable the ability of Throwing units to gain the Speed promo - I just don't want them getting it from the Riding School but I'd love for the Riding School to continue to give that promo out for Mounted Units (and perhaps NOT for Wheeled units as that's not exactly 'Riding' either (Chariots and Wagons would be both Mounted and Wheeled) and Scouts are also gaining it from this inappropriate source as well.)

Primarily I'm thinking of the filters only from a Combat Class perspective right now but I realize some other filters could be useful, tech, civic, trait perhaps - and I was about to setup a whole new FilteredFreePromo tag so that I could establish a number of dependencies to be indicated for the specific FreePromotion IN that self-filtering tag. Then I remembered this and recalled that I'd been wanting to find a project to use it on but I'm thinking from everything you've explained that we may not be able to get it working - and furthermore, I'm wondering how I would I apply these conditions to specific free promotions indicated on the building. My answer to that so far has been to establish an expression condition that subordinates to each free promotion tag, thus we could have up to four varieties of conditions, one for each of the three static free promo tags and one for the unlimited list of FreePromos (this COULD become problematic and it certainly doesn't seem ideal.)

Any ideas?
In general that is something that can be done well with expression tags as you evaluate it at a specific point, when the unit is created.
The DLL programmer chooses what he evaluates the tag on and since this is free promotions on creation of a unit best evaluate it on the unit after it has been created ot see if it qualifies for the free promotion.
If you want an unlimited amount of possible free promotions I assume you have set up XML like this:
Code:
<FreePromotions>
  <FreePromotion>PROMOTION_WHATEVER</FreePromotion>
  <FreePromotion>PROMOTION_YETANOTHER</FreePromotion>
</FreePromotions>
If you want a condition for each of those, change it to something like this:
Code:
<FreePromotions>
  <FreePromotion>
    <PromotionType>PROMOTION_WHATEVER</PromotionType>
    <Condition>
      <Has>
        Whatever
      </Has>
    </Condition>
  </FreePromotion>
  <FreePromotion>
    <PromotionType>PROMOTION_YETANOTHER</PromotionType>
    <Condition>1</Condition>
  </FreePromotion>
</FreePromotions>
 
In general that is something that can be done well with expression tags as you evaluate it at a specific point, when the unit is created.
The DLL programmer chooses what he evaluates the tag on and since this is free promotions on creation of a unit best evaluate it on the unit after it has been created ot see if it qualifies for the free promotion.
If you want an unlimited amount of possible free promotions I assume you have set up XML like this:
Code:
<FreePromotions>
  <FreePromotion>PROMOTION_WHATEVER</FreePromotion>
  <FreePromotion>PROMOTION_YETANOTHER</FreePromotion>
</FreePromotions>
If you want a condition for each of those, change it to something like this:
Code:
<FreePromotions>
  <FreePromotion>
    <PromotionType>PROMOTION_WHATEVER</PromotionType>
    <Condition>
      <Has>
        Whatever
      </Has>
    </Condition>
  </FreePromotion>
  <FreePromotion>
    <PromotionType>PROMOTION_YETANOTHER</PromotionType>
    <Condition>1</Condition>
  </FreePromotion>
</FreePromotions>

Awesome answer!

EDIT: I had some questions here that I believe I've figured out how to resolve. Though answers to the following issues may correct my course on those issues as well.
 
Top Bottom