khanat-opennel-code/code/ryzom/tools/leveldesign/mission_compiler_lib/steps.cpp
2017-03-15 20:43:10 +01:00

1402 lines
32 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mission_compiler.h"
#include "step.h"
#include "nel/misc/factory.h"
#include <memory>
using namespace std;
using namespace NLMISC;
using namespace NLLIGO;
// *****
// IStep
// *****
IStep *IStep::createStep(CMissionData &md, IPrimitive *prim)
{
string *c;
if (!prim->getPropertyByName("class", c))
throw EParseException(prim, "Can't find property 'class' on primitive");
IStep *ret = CFactoryIndirect<IStepFactory, string>::instance().getFactory(*c)->createStep(md, prim);
ret->init(md, prim);
return ret;
};
IStep::IStep(CMissionData &md, NLLIGO::IPrimitive *prim)
: _Primitive(prim),
EndOfBranch(false),
JumpPoint(false)
{
if (prim == NULL)
return;
prim->getPropertyByName("class", _StepType);
prim->getPropertyByName("name", _StepName);
// parse the sub prim to create action & objectives;
IPrimitive *child;
// parse the preactions
{
TPrimitiveClassAndNamePredicate pred("actions", "pre_actions");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
for (uint i=0; i<child->getNumChildren(); ++i)
{
IPrimitive *subChild;
child->getChild(subChild, i);
if (subChild)
{
// ok, we got one
IStepContent *sc = IStepContent::createStepContent(md, subChild);
if (sc)
_PreActions.push_back(sc);
}
}
}
// parse the objectives
{
TPrimitiveClassAndNamePredicate pred("mission_objectives", "objectives");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
for (uint i=0; i<child->getNumChildren(); ++i)
{
IPrimitive *subChild;
child->getChild(subChild, i);
if (subChild)
{
// ok, we got one
IStepContent *sc = IStepContent::createStepContent(md, subChild);
if (sc)
_Objectives.push_back(sc);
}
}
}
// parse the post actions
{
TPrimitiveClassAndNamePredicate pred("actions", "post_actions");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
for (uint i=0; i<child->getNumChildren(); ++i)
{
IPrimitive *subChild;
child->getChild(subChild, i);
if (subChild)
{
// ok, we got one
IStepContent *sc = IStepContent::createStepContent(md, subChild);
if (sc)
_PostActions.push_back(sc);
}
}
}
}
void IStep::fillStepJump(CMissionData &md, std::set<TJumpInfo> &jumpPoints)
{
uint i;
// extract step content jump points
for (i=0; i<_PreActions.size(); ++i)
{
_PreActions[i]->fillJump(md, jumpPoints);
}
for (i=0; i<_Objectives.size(); ++i)
{
_Objectives[i]->fillJump(md, jumpPoints);
}
for (i=0; i<_PostActions.size(); ++i)
{
_PostActions[i]->fillJump(md, jumpPoints);
}
fillJump(md, jumpPoints);
for (i = 0; i < _SubSteps.size(); ++i)
if (_SubSteps[i] != NULL)
_SubSteps[i]->fillStepJump(md, jumpPoints);
}
void IStep::fillJump(CMissionData &md, std::set<TJumpInfo> &jumpPoints)
{
// jump to the next step
IStep *next = md.getNextStep(this);
if (!EndOfBranch && next)
{
TJumpInfo ji(next->getStepName());
jumpPoints.insert(ji);
}
else
{
TJumpInfo ji("__end__");
jumpPoints.insert(ji);
}
}
std::string IStep::genCode(CMissionData &md)
{
string ret;
ret += genCodePreActions(md);
ret += genCodeObjectives(md);
ret += genCodePostActions(md);
return ret;
}
std::string IStep::genCodePreActions(CMissionData &md)
{
string ret;
// generate code for pre actions
for (uint i=0; i<_PreActions.size(); ++i)
{
ret += _PreActions[i]->genStepContentCode(md);
}
return ret;
}
std::string IStep::genCodeObjectives(CMissionData &md)
{
string ret;
// generate code for objectives
for (uint i=0; i<_Objectives.size(); ++i)
{
ret += _Objectives[i]->genStepContentCode(md);
}
return ret;
}
std::string IStep::genCodePostActions(CMissionData &md)
{
string ret;
//generate code for post actions
for (uint i=0; i<_PostActions.size(); ++i)
{
ret += _PostActions[i]->genStepContentCode(md);
}
return ret;
}
std::string IStep::genPhrase()
{
string ret;
// generate code for pre actions
for (uint i=0; i<_PreActions.size(); ++i)
{
ret += _PreActions[i]->genStepContentPhrase();
}
// generate code for objectives
for (uint i=0; i<_Objectives.size(); ++i)
{
ret += _Objectives[i]->genStepContentPhrase();
}
//generate code for post actions
for (uint i=0; i<_PostActions.size(); ++i)
{
ret += _PostActions[i]->genStepContentPhrase();
}
//generate code for sub steps
for (uint i=0; i<_SubSteps.size(); ++i)
{
ret += _SubSteps[i]->genPhrase();
}
return ret;
}
class CStep : public IStep
{
public:
CStep(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
// nothing special to do
}
};
REGISTER_STEP_INDIRECT(CStep, "step");
class CStepObjective : public CStep
{
public:
virtual void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef)
{
numEntry = 0;
predef.clear();
};
CStepObjective(CMissionData &md, IPrimitive *prim, const std::string &prefix = "")
: CStep(md, prim),
_Prefix(prefix),
_HideObj(false)
{
}
void init(CMissionData &md, IPrimitive *prim)
{
_HideObj = md.getProperty(prim, _Prefix + "hide_obj", true, false) == "true";
_OverloadObj = md.getPropertyArray(prim, _Prefix + "overload_objective", false, false);
_RoleplayObj = md.getPropertyArray(prim, _Prefix + "roleplay_objective", false, false);
uint32 numEntry;
CPhrase::TPredefParams params;
// ask derived class for predefined params
getPredefParam(numEntry, params);
_OverloadPhrase.initPhrase(md, prim, _OverloadObj, numEntry, params);
_RoleplayPhrase.initPhrase(md, prim, _RoleplayObj, numEntry, params);
}
virtual bool isAction() { return true; }
string genCode(CMissionData &md)
{
string ret;
if (_HideObj)
ret = "hide_obj" + NL;
if (!_OverloadObj.empty())
{
ret += "set_obj : " + _OverloadPhrase.genScript(md) + NL;
}
if (!_RoleplayObj.empty())
{
ret += "set_obj_rp : " + _RoleplayPhrase.genScript(md) + NL;
}
return ret;
}
string genPhrase()
{
string ret;
ret += _OverloadPhrase.genPhrase();
ret += _RoleplayPhrase.genPhrase();
ret += IStep::genPhrase();
return ret;
}
protected:
string _Prefix;
bool _HideObj;
vector<string> _OverloadObj;
CPhrase _OverloadPhrase;
vector<string> _RoleplayObj;
CPhrase _RoleplayPhrase;
};
class CStepDynChatTalkTo : public CStepObjective
{
// string _BotNameVar;
// string _BotName;
TCompilerVarName _BotName;
CPhrase _Phrase;
void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef)
{
numEntry = 0;
predef.resize(1);
predef[0].resize(1);
predef[0][0] = _BotName.getParamInfo();
// if (_BotNameVar.empty())
// predef[0][0] = CPhrase::TParamInfo("npc", STRING_MANAGER::bot);
// else
// predef[0][0] = CPhrase::TParamInfo(_BotNameVar, STRING_MANAGER::bot);
}
public:
CStepDynChatTalkTo(CMissionData &md, IPrimitive *prim, const std::string &prefix = "")
: CStepObjective(md, prim, prefix)
{
}
void init(CMissionData &md, IPrimitive *prim)
{
_BotName.init("npc", STRING_MANAGER::bot, md, prim, "npc_name");
// _BotNameVar = md.getProperty(prim, "npc_name", false, false);
// remove the variable tag if any
// untagVar(_BotNameVar);
// _BotName = md.getProperty(prim, "npc_name", true, false);
vector<string> vs;
vs = md.getPropertyArray(prim, "talk_to_menu", false, false);
CPhrase::TPredefParams pp(1);
pp[0].push_back(_BotName.getParamInfo());
// if (_BotNameVar.empty())
// pp[0].push_back(CPhrase::TParamInfo("npc", STRING_MANAGER::bot));
// else
// pp[0].push_back(CPhrase::TParamInfo(_BotNameVar, STRING_MANAGER::bot));
_Phrase.initPhrase(md, prim, vs, 0, pp);
// _Phrase.initPhrase(md, prim, vs);
// if (_Phrase.asAdditionnalParams())
// {
// // we need to remove the default 'npc' parameter if add params are found
// CPhrase temp;
// temp.initPhrase(md, prim, vs);
//
// _Phrase = temp;
// }
CStepObjective::init(md, prim);
}
string genCode(CMissionData &md)
{
string ret;
ret = CStepObjective::genCode(md);
ret += "talk_to : "+_BotName._VarValue;
if (!_Phrase.isEmpty())
ret += " : "+_Phrase.genScript(md);
ret += NL;
return ret;
}
string genPhrase()
{
string ret;
ret = CStepObjective::genPhrase();
ret += _Phrase.genPhrase();
return ret;
}
};
class CStepJump : public IStep
{
public:
CStepJump(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
_StepName = md.getProperty(prim, "target", true, false);
}
void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
jumpPoints.insert(TJumpInfo(_StepName, "", false));
}
string genCode(CMissionData &md)
{
string ret;
ret += "jump : " + _StepName + NL;
return ret;
}
bool isAJump()
{
return true;
}
string _StepName;
};
REGISTER_STEP_INDIRECT(CStepJump, "jump_to");
/// pseudo step used in step parsing
class CStepEnd : public IStep
{
public:
CStepEnd(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
}
string genCode(CMissionData &md)
{
string ret;
ret = "end"+NL;
return ret;
}
bool isEnd() { return true; }
};
REGISTER_STEP_INDIRECT(CStepEnd, "end");
class CStepFailure : public IStep
{
// Optional jump at end of failure
string _JumpTo;
public:
CStepFailure(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
// parse the sub prim to create action & objectives;
IPrimitive *child;
// parse the pre-actions
{
TPrimitiveClassAndNamePredicate pred("actions", "actions");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
for (uint i=0; i<child->getNumChildren(); ++i)
{
IPrimitive *subChild;
child->getChild(subChild, i);
if (subChild)
{
// ok, we got one
IStepContent *sc = IStepContent::createStepContent(md, subChild);
if (sc)
_PreActions.push_back(sc);
}
}
}
// look for an optional jump
{
TPrimitiveClassPredicate pred("jump_to");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
// ok, we have a jump at end of fail step
_JumpTo = md.getProperty(child, "target", true, false);
}
}
string genCode(CMissionData &md)
{
string ret;
ret = "failure" + NL;
string tmp = IStep::genCode(md);
tabulateLine(tmp, 1);
ret += tmp;
if (!_JumpTo.empty())
{
ret += "\tjump : "+_JumpTo + NL;
}
ret += "/failure"+NL;
return ret;
}
void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
IStep::fillJump(md, jumpPoints);
if (!_JumpTo.empty())
jumpPoints.insert(TJumpInfo(_JumpTo));
}
};
REGISTER_STEP_INDIRECT(CStepFailure, "step_failure");
class CStepCrash : public IStep
{
// Optional jump at end of failure
string _JumpTo;
vector<string> _AiInstances;
public:
CStepCrash(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
// parse the sub prim to create action & objectives;
IPrimitive *child;
// parse the pre-actions
{
TPrimitiveClassAndNamePredicate pred("actions", "actions");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
for (uint i=0; i<child->getNumChildren(); ++i)
{
IPrimitive *subChild;
child->getChild(subChild, i);
if (subChild)
{
// ok, we got one
IStepContent *sc = IStepContent::createStepContent(md, subChild);
if (sc)
_PreActions.push_back(sc);
}
}
}
// parse ai instance list
vector<string> vs = md.getPropertyArray(prim, "ai_instances", true, false);
/* if (vs.empty())
{
string err = toString("crash block need at least one ai instance !");
throw EParseException(prim, err.c_str());
}
*/
for (uint i=0; i<vs.size(); ++i)
{
_AiInstances.push_back(vs[i]);
}
// look for an optional jump
{
TPrimitiveClassPredicate pred("jump_to");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
// ok, we have a jump at end of fail step
_JumpTo = md.getProperty(child, "target", true, false);
}
}
string genCode(CMissionData &md)
{
string ret;
ret = "crash";
if (!_AiInstances.empty())
{
ret += " :";
for (uint i=0; i<_AiInstances.size(); ++i)
{
ret += string(" ")+_AiInstances[i];
}
}
ret += NL;
string tmp = IStep::genCode(md);
tabulateLine(tmp, 1);
ret += tmp;
if (!_JumpTo.empty())
{
ret += "\tjump : "+_JumpTo + NL;
}
ret += "/crash"+NL;
return ret;
}
void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
IStep::fillJump(md, jumpPoints);
if (!_JumpTo.empty())
jumpPoints.insert(TJumpInfo(_JumpTo));
}
};
REGISTER_STEP_INDIRECT(CStepCrash, "step_crash");
// ///////////////////// //
// STEP PLAYER RECONNECT //
// ///////////////////// //
// ***************************************************************************
CStepPlayerReconnect::CStepPlayerReconnect(CMissionData &md, IPrimitive *prim) : IStep(md, prim)
{
// parse the sub prim to create action & objectives;
IPrimitive *child;
TPrimitiveSet resp;
{
TPrimitivePropertyPredicate pred("step_tag", "true");
filterPrimitiveChilds(prim, pred, resp);
}
for (uint i=0; i<resp.size(); ++i)
{
_SubBranchs.push_back(resp[i]);
}
// look for an optional jump
{
TPrimitiveClassPredicate pred("jump_to");
child = getPrimitiveChild(prim, pred);
}
if (child)
{
// ok, we have a jump at end of fail step
_JumpTo = md.getProperty(child, "target", true, false);
}
}
// ***************************************************************************
TPrimitiveSet CStepPlayerReconnect::getSubBranchs()
{
return _SubBranchs;
}
// ***************************************************************************
string CStepPlayerReconnect::genCode(CMissionData &md)
{
string ret;
ret = "player_reconnect"+NL;
string tmp = IStep::genCode(md);
tabulateLine(tmp, 1);
ret += tmp;
for (uint i = 0; i < _SubSteps.size(); ++i)
{
if ( md.isThereAJumpTo(_SubSteps[i]->getStepName()) )
{
// insert a jump point
ret += "jump_point : " + _SubSteps[i]->getStepName() + NL;
}
ret += _SubSteps[i]->genCode(md);
}
if (!_JumpTo.empty())
{
ret += "\tjump : "+_JumpTo + NL;
}
ret += "/player_reconnect"+NL;
return ret;
}
// ***************************************************************************
void CStepPlayerReconnect::fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
IStep::fillJump(md, jumpPoints);
if (!_JumpTo.empty())
jumpPoints.insert(TJumpInfo(_JumpTo));
}
REGISTER_STEP_INDIRECT(CStepPlayerReconnect, "step_player_reconnect");
// //////////////////////////// //
// END OF STEP PLAYER RECONNECT //
// //////////////////////////// //
// ***************************************************************************
// get the jump point of a node. NB: if the node is itself a jump, then directly jump to its target
static std::string *getJumpTarget(IPrimitive *child)
{
if(!child)
return NULL;
// default: jump to the node
string *s= NULL;
child->getPropertyByName("name", s);
// if the node is a jump itself
string *className= NULL;
child->getPropertyByName("class", className);
if (className && *className == "jump_to")
{
// generate jump to the step jump target
child->getPropertyByName("target", s);
}
return s;
}
// ***************************************************************************
class CStepDynChat : public IStep
{
public:
CStepDynChat(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
_BotName = md.getProperty(prim, "npc_name", true, false);
vector<string> temp = md.getPropertyArray(prim, "phrase", false, false);
_Phrase.initPhrase(md, prim, temp);
// talk_to menu
temp = md.getPropertyArray(prim, "talk_to_menu", false, false);
// _TalkToMenu.initPhrase(md, prim, temp);
_TalkToObjective = NULL;
CUniquePtr<CStepDynChatTalkTo> talkToObjective; // next calls could throw exceptions, so take care...
if (!temp.empty())
{
talkToObjective.reset(new CStepDynChatTalkTo(md, prim, "talk_to_"));
talkToObjective->init(md, prim);
}
// build the sub branch list
IPrimitive *noResp;
{
TPrimitiveClassPredicate pred("no_answer");
noResp = getPrimitiveChild(prim, pred);
}
nlassert(noResp);
_SubBranchs.push_back(noResp);
TPrimitiveSet resp;
{
TPrimitiveClassPredicate pred("dyn_answer");
filterPrimitiveChilds(prim, pred, resp);
}
_Responses.resize(resp.size());
for (uint i=0; i<resp.size(); ++i)
{
_SubBranchs.push_back(resp[i]);
if (resp[i]->getNumChildren() == 0)
throw EParseException(prim, "missing step under 'dyn_answer' node for response");
vector<string> temp = md.getPropertyArray(resp[i], "phrase_answer", false, false);
_Responses[i].initPhrase(md, resp[i], temp);
}
_TalkToObjective = talkToObjective.release(); // commit result
}
~CStepDynChat()
{
delete _TalkToObjective;
}
TPrimitiveSet getSubBranchs()
{
TPrimitiveSet vStepsToReturn;
for (uint i = 0; i < _SubBranchs.size(); ++i)
{
TPrimitiveSet childs;
{
TPrimitivePropertyPredicate pred("step_tag", "true");
filterPrimitiveChilds(_SubBranchs[i], pred, childs);
}
for (uint j = 0; j < childs.size(); ++j)
vStepsToReturn.push_back(childs[j]);
}
return vStepsToReturn;
}
void fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
for (uint i=0; i<_SubBranchs.size(); ++i)
{
string *s;
IPrimitive *firstStep;
if (!_SubBranchs[i]->getChild(firstStep, 0))
throw EParseException(_Primitive, toString("Can't find child in %uth sub branch", i+1).c_str());
bool ret = firstStep->getPropertyByName("name", s);
if (!ret)
throw EParseException(_Primitive, toString("Can't find property 'name' in %uth sub branch", i+1).c_str());
string label;
_SubBranchs[i]->getPropertyByName("name", label);
jumpPoints.insert(TJumpInfo(*s, label));
}
}
string genCode(CMissionData &md)
{
string ret;
// if there's a talk_to menu, add it
// if (!_TalkToMenu.isEmpty())
if (_TalkToObjective != NULL)
{
ret += _TalkToObjective->genCode(md);
// ret += "talk_to : "+_BotName;
// ret += " : " + _TalkToMenu.genScript(md);
// ret += NL;
}
ret += "dyn_chat : " + _BotName + " : " + _Phrase.genScript(md);
for (uint i=1; i<_SubBranchs.size(); ++i)
{
IPrimitive *child;
_SubBranchs[i]->getChild(child, 0);
// get the jump target
string *s= getJumpTarget(child);
ret += " : " + *s + " " + _Responses[i-1].genScript(md);
}
ret += NL;
return ret;
}
string genPhrase()
{
string ret; /* = CStepObjective::genPhrase();*/
// if (!_TalkToMenu.isEmpty())
if (_TalkToObjective != NULL)
{
ret += _TalkToObjective->genPhrase();
// ret += _TalkToMenu.genPhrase();
}
ret += _Phrase.genPhrase();
for (uint i=0; i<_Responses.size(); ++i)
{
ret += _Responses[i].genPhrase();
}
return ret;
}
/// the list of sub branch for the dyn chat.
TPrimitiveSet _SubBranchs;
// phrase for the main text
CPhrase _Phrase;
// list of phrase for each response
vector<CPhrase> _Responses;
string _BotName;
// 'talk_to' part
// CPhrase _TalkToMenu;
CStepDynChatTalkTo *_TalkToObjective;
};
REGISTER_STEP_INDIRECT(CStepDynChat, "step_dyn_chat");
class CLinearDynChat : public IStep
{
enum TLinearMode
{
mode_fail,
mode_retry,
mode_continue,
// mode_skipable
};
TLinearMode _Mode;
string _BotName;
CPhrase _Phrase;
CPhrase _PhraseYes;
public:
CLinearDynChat(CMissionData &md, IPrimitive *prim)
: IStep(md, prim)
{
_BotName = md.getProperty(prim, "npc_name", true, false);
vector<string> temp = md.getPropertyArray(prim, "phrase", false, false);
_Phrase.initPhrase(md, prim, temp);
// set the linear mode
string className;
prim->getPropertyByName("class", className);
if (className == "linear_dyn_chat_fail")
_Mode = mode_fail;
else if (className == "linear_dyn_chat_retry")
_Mode = mode_retry;
else if (className == "linear_dyn_chat_continue")
_Mode = mode_continue;
else
{
string err = toString("Unknow class '%s' for dyn chat !", className.c_str());
throw EParseException(prim, err.c_str());
}
// read the response phrase
vector<string> phraseYes;
phraseYes = md.getPropertyArray(prim, "phrase_yes", false, false);
_PhraseYes.initPhrase(md, prim, phraseYes);
}
string genCode(CMissionData &md)
{
string ret;
switch (_Mode)
{
case mode_fail:
ret = "dyn_chat : "+_BotName+" : "+_Phrase.genScript(md)+" : "+_StepName+"_resp "+_PhraseYes.genScript(md)+NL;
ret += "fail"+NL;
ret += "jump_point : " +_StepName+"_resp"+NL;
break;
case mode_retry:
ret += "jump_point : " +_StepName+"_retry"+NL;
ret += "dyn_chat : "+_BotName+" : "+_Phrase.genScript(md)+" : "+_StepName+"_resp "+_PhraseYes.genScript(md)+NL;
ret += "jump : "+_StepName+"_retry"+NL;
ret += "jump_point : " +_StepName+"_resp"+NL;
break;
case mode_continue:
ret = "dyn_chat : "+_BotName+" : "+_Phrase.genScript(md)+" : "+_StepName+"_resp "+_PhraseYes.genScript(md)+NL;
ret += "jump_point : " +_StepName+"_resp"+NL;
break;
}
return ret;
}
string genPhrase()
{
string ret;
// ret = CStepObjective::genPhrase();
ret += _Phrase.genPhrase();
ret += _PhraseYes.genPhrase();
return ret;
}
};
typedef CLinearDynChat TLinearDynChatFail;
REGISTER_STEP_INDIRECT(TLinearDynChatFail, "linear_dyn_chat_fail");
typedef CLinearDynChat TLinearDynChatRetry;
REGISTER_STEP_INDIRECT(TLinearDynChatRetry, "linear_dyn_chat_retry");
typedef CLinearDynChat TLinearDynChatContinue;
REGISTER_STEP_INDIRECT(TLinearDynChatContinue, "linear_dyn_chat_continue");
// /////// //
// STEP IF //
// /////// //
CStepIf::CStepIf(CMissionData &md, IPrimitive *prim) : IStep(md, prim)
{
if (prim->getNumChildren() != 2)
throw EParseException(prim, toString("step_if need two child primitive for 'not_ok' and 'ok' clause, found %u child", prim->getNumChildren()).c_str());
IPrimitive *notOk = const_cast<IPrimitive *>(prim->getPrimitive("result_no"));
//prim->getChild(notOk, 0);
IPrimitive *ok = const_cast<IPrimitive *>(prim->getPrimitive("result_yes"));
//prim->getChild(ok, 1);
if (notOk == NULL)
throw EParseException(prim, "Can't find 'not ok' step branch");
if (ok == NULL)
throw EParseException(prim, "Can't find 'ok' step branch");
string name;
notOk->getPropertyByName("class", name);
if (name != "result_no")
throw EParseException(prim, toString("first child must be of class 'result_no', found '%s' instead", name.c_str()).c_str());
ok->getPropertyByName("class", name);
if (name != "result_yes")
throw EParseException(prim, toString("second child must be of class 'result_yes', found '%s' instead", name.c_str()).c_str());
// push in order : not ok, then ok
_SubBranchs.push_back(notOk);
_SubBranchs.push_back(ok);
string s = md.getProperty(prim, "condition_type", true, false);
if (s == "test_missions_done")
_IfType = it_mission_done;
else if (s == "test_skills_level")
_IfType = it_skills;
else if (s == "test_bricks_known")
_IfType = it_bricks;
else if (s == "test_sdb")
_IfType = it_sdb;
else if (s == "test_race")
_IfType = it_race;
else if (s == "test_cult")
_IfType = it_cult;
else if (s == "test_civ")
_IfType = it_civ;
else if (s == "test_faction_point")
_IfType = it_faction_point;
else if (s == "test_guild_civ")
_IfType = it_guild_civ;
else if (s == "test_guild_cult")
_IfType = it_guild_cult;
else if (s == "test_guild_fame")
_IfType = it_guild_fame;
else if (s == "test_no_trial")
_IfType = it_no_trial;
else if (s == "test_item_in_inv")
_IfType = it_item_in_inv;
else
throw EParseException(prim, "Unknow test type !");
_IfParams = md.getPropertyArray(prim, "test_parameters", true, false);
}
TPrimitiveSet CStepIf::getSubBranchs()
{
TPrimitiveSet vStepsToReturn;
for (uint i = 0; i < _SubBranchs.size(); ++i)
{
TPrimitiveSet childs;
{
TPrimitivePropertyPredicate pred("step_tag", "true");
filterPrimitiveChilds(_SubBranchs[i], pred, childs);
}
for (uint j = 0; j < childs.size(); ++j)
vStepsToReturn.push_back(childs[j]);
}
return vStepsToReturn;
}
void CStepIf::fillJump(CMissionData &md, set<TJumpInfo> &jumpPoints)
{
/*string *s;
TPrimitiveSet childs;
bool ret;
filterPrimitiveChilds(_SubBranchs[1], TPrimitivePropertyPredicate("step_tag", "true"), childs);
if (!childs.empty())
{
ret = childs[0]->getPropertyByName("name", s);
if (!ret)
throw EParseException(childs[0], "Can't find property 'name' in first step of 'ok' sub branch");
jumpPoints.insert(TJumpInfo(*s, "yes"));
}
// insert link to 'no' step
childs.clear();
filterPrimitiveChilds(_SubBranchs[0], TPrimitivePropertyPredicate("step_tag", "true"), childs);
if (!childs.empty())
{
ret = childs[0]->getPropertyByName("name", s);
if (!ret)
throw EParseException(childs[0], "Can't find property 'name' in first step of 'not ok' sub branch");
jumpPoints.insert(TJumpInfo(*s, "no"));
}*/
}
string CStepIf::genCode(CMissionData &md)
{
string ret;
uint32 i;
TPrimitiveSet childs;
vector<IStep*> yesSteps;
vector<IStep*> noSteps;
// Get the 'yes branch' jump point
{
TPrimitivePropertyPredicate pred("step_tag", "true");
filterPrimitiveChilds(_SubBranchs[1], pred, childs);
}
if (!childs.empty())
{
for (i = 0; i < _SubSteps.size(); ++i)
if (std::find(childs.begin(), childs.end(), _SubSteps[i]->getPrimitive()) != childs.end())
yesSteps.push_back(_SubSteps[i]);
if (yesSteps.empty())
{
string err = toString("In step '%s', can't find sub step in 'yes' sub branch", getStepName().c_str());
throw EParseException(_SubBranchs[1]->getParent(), err.c_str());
}
}
// Get the 'no branch' jump point
childs.clear();
{
TPrimitivePropertyPredicate pred("step_tag", "true");
filterPrimitiveChilds(_SubBranchs[0], pred, childs);
}
if (!childs.empty())
{
for (i = 0; i < _SubSteps.size(); ++i)
if (std::find(childs.begin(), childs.end(), _SubSteps[i]->getPrimitive()) != childs.end())
noSteps.push_back(_SubSteps[i]);
if (noSteps.empty())
{
string err = toString("In step '%s', can't find sub step in 'no' sub branch", getStepName().c_str());
throw EParseException(_SubBranchs[0]->getParent(), err.c_str());
}
}
if ((yesSteps.empty()) && (noSteps.empty()))
{
string err = toString("In step '%s', no yes-branch and no no-branch", getStepName().c_str());
throw EParseException(_SubBranchs[0]->getParent(), err.c_str());
}
string jumpToYes;
if (!yesSteps.empty())
jumpToYes = yesSteps[0]->getStepName();
else
jumpToYes = getStepName() + "_endif";
// type of IF:
switch(_IfType)
{
case it_mission_done:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_mission_done need at least one mission to test");
for (uint i=0; i<_IfParams.size(); ++i)
ret += "if_mission_done : " + _IfParams[i] + " : " + jumpToYes + NL;
break;
case it_skills:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_skills need at least one <skill level> to test");
ret = "if_skills : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += "; ";
}
ret += " : " + jumpToYes + NL;
break;
case it_bricks:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_bricks need at least one sbrick to test");
ret = "if_bricks : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += "; ";
}
ret += " : " + jumpToYes + NL;
break;
case it_sdb:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_sdb need an expression to test");
ret = "if_sdb : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_race:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_race need an expression to test");
ret = "if_race : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_cult:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_cult need an expression to test");
ret = "if_cult : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_civ:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_civ need an expression to test");
ret = "if_civ : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_faction_point:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_faction_point need an expression to test");
ret = "if_faction_point : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_guild_civ:
if (_IfParams.empty())
throw EParseException(_Primitive, "it_guild_civ need an expression to test");
ret = "if_guild_civ : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_guild_cult:
if (_IfParams.empty())
throw EParseException(_Primitive, "it_guild_cult need an expression to test");
ret = "if_guild_cult : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_guild_fame:
if (_IfParams.empty())
throw EParseException(_Primitive, "it_guild_fame need an expression to test");
ret = "if_guild_fame : ";
for (uint i=0; i<_IfParams.size(); ++i)
{
ret += _IfParams[i];
if (i < _IfParams.size()-1)
ret += " ";
}
ret += " : " + jumpToYes + NL;
break;
case it_no_trial:
ret = "if_no_trial : " + jumpToYes + NL;
break;
case it_item_in_inv:
if (_IfParams.empty())
throw EParseException(_Primitive, "if_item_in_inv need at least one mission to test");
for (uint i=0; i<_IfParams.size(); ++i)
ret += "if_item_in_inv : " + _IfParams[i] + " : " + jumpToYes + NL;
break;
}
// Generate the 'no branch'
if (!noSteps.empty())
{
ret += "# no: " + noSteps[0]->getStepName() + NL;
for (uint i = 0; i < noSteps.size(); ++i)
{
if ( md.isThereAJumpTo(noSteps[i]->getStepName()) )
{
// insert a jump point
ret += "jump_point : " + noSteps[i]->getStepName() + NL;
}
ret += noSteps[i]->genCode(md);
}
}
// Generate the 'yes branch'
if (!yesSteps.empty())
{
ret += "jump : " + getStepName() + "_endif" + NL;
ret += "# yes: " + yesSteps[0]->getStepName() + NL;
for (uint i = 0; i < yesSteps.size(); ++i)
{
if ((i == 0) || md.isThereAJumpTo(yesSteps[i]->getStepName()) )
{
// insert a jump point
ret += "jump_point : " + yesSteps[i]->getStepName() + NL;
}
ret += yesSteps[i]->genCode(md);
}
}
// Generate ending jump point
ret += "jump_point : " + getStepName() + "_endif" + NL;
return ret;
}
REGISTER_STEP_INDIRECT(CStepIf, "step_if");
// ////////////// //
// END OF STEP IF //
// ////////////// //
class CStepOOO : public CStepObjective
{
public:
CStepOOO(CMissionData &md, IPrimitive *prim)
: CStepObjective(md, prim)
{
}
string genCode(CMissionData &md)
{
string ret;
ret += CStepObjective::genCode(md);
ret += IStep::genCodePreActions(md);
ret += "ooo" + NL;
string tmp = IStep::genCodeObjectives(md);
tabulateLine(tmp, 1);
ret += tmp;
ret += "/ooo"+NL;
ret += genCodePostActions(md);
return ret;
}
};
REGISTER_STEP_INDIRECT(CStepOOO, "step_ooo");
class CStepAny : public CStepObjective
{
public:
CStepAny(CMissionData &md, IPrimitive *prim)
: CStepObjective(md, prim)
{
}
string genCode(CMissionData &md)
{
string ret;
ret += CStepObjective::genCode(md);
ret += IStep::genCodePreActions(md);
ret += "any" + NL;
string tmp = IStep::genCodeObjectives(md);
tabulateLine(tmp, 1);
ret += tmp;
ret += "/any"+NL;
ret += genCodePostActions(md);
return ret;
}
};
REGISTER_STEP_INDIRECT(CStepAny, "step_any");