The Expression System

Yes, I will definitely add a canBuild/canTrain/canConstruct boolean expression to those things.

Can't you already prevent an improvement to be built on forest or jungle with the current XML though?
 
Yes, I will definitely add a canBuild/canTrain/canConstruct boolean expression to those things.

Can't you already prevent an improvement to be built on forest or jungle with the current XML though?

Not that I can see. Unless there is an invalidfeature tag. I can and do specify vaild terrains but can't see how to not have jungle and forest.
 
Which I understand means that you can't then build it on a featureless plot. But there again I am running a fever.
Take BUILD_FARM. It specifies a number of features like forest on which you can build it (but which are destroyed in the process) but you can still build it on featureless terrain.
 
@AIAndy:

OK, it seems that most of the issues are with the AI, so we can hold off on most of those implementations until V24. However, you said that bIgnoreBuildingDefense would probably be doable without too many problems now, and I can set that up with the units soon. Could you please expose that one to the Boolean Expression System?
 
Boolean Expression Tutorial
Some information about how to use the Boolean Expressions.
At the moment I write this there are 4 places you can use Boolean Expressions at:
  • In the Active tag of property manipulators. They will only have an effect if the Active expression evaluates to true.
  • NewCityFree tag in building info. If it evaluates to true when you found a new city, then that building will be added for free (if it can be constructed).
  • ConstructCondition tag in building info. If it evaluates to false for a city, then that city won't be able to build that building.
  • TrainCondition tag in unit info. Same as ConstructCondition except for units.

Boolean expressions can consist of several different tags of which many also have subexpressions.

Literal
If a tag expects a boolean expression, you can also simply use 0 for false or 1 for true similar to what simple boolean XML tags expect. So this is valid:
<NewCityFree>1</NewCityFree>
The building would always be free in a new city if it can be constructed at that time.
As you can now with all boolean XML tags you can also refer to a boolean global define:
<NewCityFree>BUILDING_X_FREE_IN_NEW_CITY</NewCityFree>

Has
The Has tag is one of the most common tags you will use with boolean expressions. It evaluates if the referenced game object modifier is on the object. Game object modifiers or short GOM are a lot of different things from promotion to religion:
  • GOM_BUILDING
  • GOM_PROMOTION
  • GOM_TRAIT
  • GOM_FEATURE
  • GOM_OPTION
  • GOM_TERRAIN
  • GOM_GAMESPEED
  • GOM_ROUTE
  • GOM_BONUS
  • GOM_UNITTYPE
  • GOM_TECH
  • GOM_CIVIC
  • GOM_RELIGION
  • GOM_CORPORATION
So to use the Has tag you first specify the type of the GOM and then the ID:
Code:
<NewCityFree>
  <Has>
    <GOMType>GOM_OPTION</GOMType>
    <ID>GAMEOPTION_BUILDING_X_FREE</ID>
  </Has>
</NewCityFree>
In this case the building would only be free in a new city if the game option GAMEOPTION_BUILDING_X_FREE is set.
The exact meaning of evaluating if a game object has a certain GOM depends on the GOM type and the game object type. GOM_RELIGION on a city evaluates if the city has the religion, on a player it evaluates if the religion is state religion of that player.

Is
The Is tag is somewhat similar to the Has tag but it doesn't check GOMs but some other states that a game object can have.
An easy example is to check if a game object is on water or next to a river:
Code:
<Active>
  <Is>TAG_WATER</Is>
</Active>
Added to the propagators that spread water pollution they would only be active next to rivers or on water instead of spreading the water pollution deep into deserts.

And
The And tag can have any number of subexpressions and it will only evaluate to true if all subexpressions are true.
An example of an And with three subexpressions:
Code:
<ConstructCondition>
 <And>
  <Is>TAG_FRESH_WATER</Is>
  <Has>
    <GOMType>GOM_BONUS</GOMType>
    <ID>BONUS_APPLE</ID>
  </Has>
  <Has>
    <GOMType>GOM_CIVIC</GOMType>
    <ID>CIVIC_FEUDAL</ID>
  </Has>
 </And>
</ConstructCondition>
So the city will only be able to build that building if it has fresh water, access to apples and its owner runs the feudal civic.

Or
Or is exactly the same as And, except that it is enough if one subexpression evaluates to true.

BEqual
That is short for boolean equal. It takes exactly two subexpressions and evaluates to true if both subexpressions evaluate to the same.
Example:
Code:
<ConstructCondition>
 <BEqual>
  <Is>TAG_FRESH_WATER</Is>
  <Has>
    <GOMType>GOM_BONUS</GOMType>
    <ID>BONUS_APPLE</ID>
  </Has>
 </BEqual>
</ConstructCondition>
This building would only be constructable if the city either has fresh water and apples or neither has fresh water nor apples. If it has apples but no fresh water then it won't be able to build it. Ok, not a very meaningful example.

Not
This tag inverts the result of its one subexpression.
Code:
<ConstructCondition>
 <Not>
  <Has>
    <GOMType>GOM_BONUS</GOMType>
    <ID>BONUS_APPLE</ID>
  </Has>
 </Not>
</ConstructCondition>
This building can only be built if there is no access to apples.

If
If has three subexpressions (If Expression then Expression else Expression). The first one decides if the second (if true) or the third (if false) is used.
Code:
<NewCityFree>
<If>
  <Has>
    <GOMType>GOM_CIVIC</GOMType>
    <ID>CIVIC_FEUDAL</ID>
  </Has>
  <Has>
    <GOMType>GOM_FEATURE</GOMType>
    <ID>FEATURE_FOREST</ID>
  </Has>
  <Has>
    <GOMType>GOM_FEATURE</GOMType>
    <ID>FEATURE_JUNGLE</ID>
  </Has>
</If>
</NewCityFree>
If you are running feudal, then you need a forest near the new city to get it for free. Otherwise you need a jungle near it.

IntegrateOr
This tag allows you to query other game objects and if any of them evaluate the subexpression to true, then it will be true.
The way to describe the objects to query is the same as for property manipulators. So you first tell which relation to the primary game object these game objects have in the tag named RelationType. For that you can use these:
  • RELATION_ASSOCIATED // owner, owned, ... depending on type
  • RELATION_TRADE // trade routes, query from a city to cities
  • RELATION_NEAR // needs iDistance tag to specify the distance
  • RELATION_SAME_PLOT
  • RELATION_WORKING // query plots from a city to get the plots belonging to the city and vice versa get the city to which the plot belongs
Then you specify the target object type, tag name GameObjectType. These are the game object types:
  • GAMEOBJECT_GAME
  • GAMEOBJECT_TEAM
  • GAMEOBJECT_PLAYER
  • GAMEOBJECT_CITY
  • GAMEOBJECT_UNIT
  • GAMEOBJECT_PLOT

Example:
Code:
<ConstructCondition>
  <IntegrateOr>
    <RelationType>RELATION_TRADE</RelationType>
    <GameObjectType>GAMEOBJECT_CITY</GameObjectType>
    <Has>
      <GOMType>GOM_BUILDING</GOMType>
      <ID>BUILDING_PALACE</ID>
    </Has>
  </IntegrateOr>
</ConstructCondition>
This building can only be built if the city has a trade route with a city that has a palace.
 
AIAndy's Post needs to be put in one of the stickied threads! So it's always available. Maybe like DH's Modder's documents?

JosEPh
 
Can the system count and apply modifier on player wide? If it can, this could possibly be used to create a dynamic trait system. You set propriety (hidden or not) for thing you want and apply trait like benefits when you cross a threshold possibly using multiple threshold per propriety to have evolving traits.
 
Can the system count and apply modifier on player wide? If it can, this could possibly be used to create a dynamic trait system. You set propriety (hidden or not) for thing you want and apply trait like benefits when you cross a threshold possibly using multiple threshold per propriety to have evolving traits.

It's not really the expression system that you'd use for that, but what you describe (using the property system) makes a lot of sense, since the pseudo-buildings the property triggers could just have civ-wide modifiers.
 
It's not really the expression system that you'd use for that, but what you describe (using the property system) makes a lot of sense, since the pseudo-buildings the property triggers could just have civ-wide modifiers.

I discussed this idea with AIAndy a while ago, and it seemed that the Expression system would be useful for controlling the property accumulation on a player for Civ traits or powers. That would require the Integer expression system though. I don't know how far along that is.
 
I discussed this idea with AIAndy a while ago, and it seemed that the Expression system would be useful for controlling the property accumulation on a player for Civ traits or powers. That would require the Integer expression system though. I don't know how far along that is.
Around 50% completed.
 
I have added integer expressions. There are no direct uses yet but you can use them in new tags for Boolean Expressions, the comparison tags Greater, GreaterEqual and Equal. Vice versa there are integer expression tags that use a boolean expression.
Example:
Code:
                       <TrainCondition>
			  <Greater>
			    <IntegrateCount>
			      <RelationType>RELATION_WORKING</RelationType>
			      <GameObjectType>GAMEOBJECT_PLOT</GameObjectType>
			      <Has>
			        <GOMType>GOM_FEATURE</GOMType>
			        <ID>FEATURE_FOREST</ID>
			      </Has>
			    </IntegrateCount>
			    <Constant>5</Constant>
			  </Greater>
			</TrainCondition>
The unit is trainable in the city if it has more than 5 forests (so at least 6).
 
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.
 
@AIAndy (Or anyone else who knows)

How do you make a building "auto-build" without the use of a property? For instance I want to have housing automatically build when all their requirements are met. However properties like crime or pollution don't really fit as triggers. Thus how do I have them build without a property trigger?
 
@AIAndy (Or anyone else who knows)

How do you make a building "auto-build" without the use of a property? For instance I want to have housing automatically build when all their requirements are met. However properties like crime or pollution don't really fit as triggers. Thus how do I have them build without a property trigger?
I don't think that possibility is currently there (except for using a dummy property which is a somewhat stupid solution).
Best would probably be to add a new expression tag for that to the buildings which would be evaluated every turn for every city.
 
@AIAndy, I want to have a train condition of population of 10 or more and an outcome restricted to cities with population 7 or less (for OV's Immigration mod, if you are wondering). You don't mention a population anywhere that I can see.
 
@AIAndy, I want to have a train condition of population of 10 or more and an outcome restricted to cities with population 7 or less (for OV's Immigration mod, if you are wondering). You don't mention a population anywhere that I can see.
Population >= 10 you write like this in the expression system:
Code:
<GreaterEqual>
  <AttributeType>ATTRIBUTE_POPULATION</AttributeType>
  <Constant>10</Constant>
</GreaterEqual>

For the outcome you can use PlotCondition and then evaluate the city like this:
Code:
<PlotCondition>
  <IntegrateOr>
    <RelationType>RELATION_ASSOCIATED</RelationType>
    <GameObjectType>GAMEOBJECT_CITY</GameObjectType>
    <GreaterEqual>
      <Constant>7</Constant>
      <AttributeType>ATTRIBUTE_POPULATION</AttributeType>
    </GreaterEqual>
  </IntegrateOr>
</PlotCondition>
 
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?
 
Top Bottom