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.