2282 lines
59 KiB
C++
2282 lines
59 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/i18n.h"
|
|
#include "nel/ligo/primitive_utils.h"
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
using namespace NLLIGO;
|
|
|
|
|
|
// hack to get access to string manager item enumeration to string without including
|
|
// almost all of the Ryzom server side project
|
|
namespace STRING_MANAGER
|
|
{
|
|
NL_BEGIN_STRING_CONVERSION_TABLE (TParamType)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( item )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( place )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( creature )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( skill )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( role )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( ecosystem )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( race )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( sbrick )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( faction )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( guild )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( player )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( bot )
|
|
{ "int", integer},
|
|
// NL_STRING_CONVERSION_TABLE_ENTRY( integer )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( time )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( money )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( compass )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( string_id )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( dyn_string_id )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( self )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( creature_model )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( entity )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( body_part )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( score )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( sphrase )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( characteristic )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( damage_type )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( bot_name)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( power_type )
|
|
NL_STRING_CONVERSION_TABLE_ENTRY( literal )
|
|
NL_END_STRING_CONVERSION_TABLE(TParamType, ParamTypeConversion, NB_PARAM_TYPES)
|
|
|
|
//-----------------------------------------------
|
|
// stringToParamType
|
|
//-----------------------------------------------
|
|
TParamType stringToParamType( const std::string & str )
|
|
{
|
|
return ParamTypeConversion.fromString( str );
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
// stringToParamType
|
|
//-----------------------------------------------
|
|
const std::string & paramTypeToString( TParamType type )
|
|
{
|
|
return ParamTypeConversion.toString( type );
|
|
}
|
|
}
|
|
|
|
// utility to 'tabulate' the lines in a string
|
|
void tabulateLine(std::string &text, uint nbTabs)
|
|
{
|
|
if (text.empty())
|
|
return;
|
|
string::size_type pos = 0;
|
|
string tabs;
|
|
|
|
for (uint i=0; i<nbTabs; ++i)
|
|
tabs += "\t";
|
|
|
|
// add a tab at start
|
|
text = tabs + text;
|
|
|
|
// add a tab at each new line
|
|
while ((pos = text.find('\n', pos)) != string::npos)
|
|
{
|
|
if (pos < text.size()-1 && text[pos+1] == '\r')
|
|
{
|
|
// add after the '\r' char
|
|
++pos;
|
|
}
|
|
if (pos < text.size()-1)
|
|
text = text.substr(0, pos+1) + tabs + text.substr(pos+1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class GenderExtractor
|
|
{
|
|
public:
|
|
|
|
|
|
GenderExtractor(const std::string & literal, const std::string& identifier, unsigned int level=0);
|
|
|
|
std::string operator()(unsigned int i) const;
|
|
|
|
unsigned int size() const;
|
|
|
|
|
|
~GenderExtractor();
|
|
private:
|
|
bool extractMarkup(const std::string& literal, const std::string & markup, std::string &before, std::string &inside, std::string & after);
|
|
|
|
bool parseMarkup(const std::string& literal, const std::string & markup, std::string& newPhrase, bool include = false );
|
|
|
|
std::string getPhrase(unsigned int i) const;
|
|
|
|
std::string getExtension(unsigned int i) const;
|
|
|
|
std::string getCondition(unsigned int i) const;
|
|
|
|
std::string getEntity(unsigned int i) const;
|
|
|
|
std::string getIdentifier(unsigned int i) const;
|
|
|
|
|
|
private:
|
|
|
|
|
|
bool _Entity;
|
|
std::string _EntityName;
|
|
|
|
std::string _Text;
|
|
std::string _Identifier;
|
|
|
|
GenderExtractor* _Female;
|
|
GenderExtractor* _Male;
|
|
|
|
|
|
|
|
};
|
|
GenderExtractor::~GenderExtractor()
|
|
{
|
|
delete _Female;
|
|
delete _Male;
|
|
}
|
|
std::string GenderExtractor::getIdentifier(unsigned int i) const
|
|
{
|
|
return _Identifier + getExtension(i);
|
|
}
|
|
|
|
std::string GenderExtractor::operator()(unsigned int i) const
|
|
{
|
|
std::string ret("\t");
|
|
std::string condition = getCondition(i);
|
|
|
|
ret += condition.empty() ? "":std::string("( ") + condition + " )" + NL + "\t\t";
|
|
ret += getIdentifier(i) + "\t[" + getPhrase(i) + "]" + NL;
|
|
return ret;
|
|
}
|
|
|
|
|
|
GenderExtractor::GenderExtractor(const std::string & literal, const std::string& identifier, unsigned int level)
|
|
{
|
|
|
|
|
|
static const char * es[] ={"e", "e1", "e2", "e3"};
|
|
static const char * fs[] ={"f", "f1", "f2", "f3"};
|
|
static const char * hs[] ={"h", "h1", "h2", "h3"};
|
|
|
|
const char * e = es[level];
|
|
const char * f = fs[level];
|
|
const char * h = hs[level];
|
|
|
|
_Identifier = toLower(identifier);
|
|
|
|
std::string newPhrase;
|
|
|
|
std::string before;
|
|
std::string after;
|
|
std::string femaleText;
|
|
std::string maleText;
|
|
|
|
_Entity = extractMarkup(literal, e, before, _EntityName, after);
|
|
if (_EntityName.size() > 2)
|
|
{
|
|
if (_EntityName[0] == '$' && _EntityName[_EntityName.size() - 1] == '$')
|
|
{
|
|
_EntityName = _EntityName.substr(1, _EntityName.size() - 2);
|
|
}
|
|
}
|
|
|
|
std::string newLiteral = before + after;
|
|
|
|
bool isFemale = parseMarkup(newLiteral,f,newPhrase, true);
|
|
if ( isFemale)
|
|
{
|
|
parseMarkup(newPhrase,h,newPhrase, false);
|
|
femaleText = newPhrase;
|
|
}
|
|
|
|
bool isMale = parseMarkup(newLiteral, h, newPhrase, true);
|
|
if (isMale)
|
|
{
|
|
|
|
parseMarkup(newPhrase, f, newPhrase, false);
|
|
maleText = newPhrase;
|
|
}
|
|
|
|
if (isMale != isFemale)
|
|
{
|
|
std::string goodMarkup = isMale ? std::string("") +"<" + h + "></" + h + ">" : std::string("")+"<"+f+"></"+f+">";
|
|
std::string badMarkup = isFemale ? std::string("") +"<" + h + "></" + h + ">" : std::string("")+"<"+f+"></"+f+">";
|
|
std::string exceptionText = std::string("Expression ") + identifier + " that contains a tag " + goodMarkup + " needs also tags " + badMarkup + " even empty.";
|
|
throw EParseException(0, exceptionText.c_str());
|
|
}
|
|
|
|
if (!isMale && !isFemale)
|
|
{
|
|
_Text = literal;
|
|
_Female = 0;
|
|
_Male = 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (!_Entity) { _EntityName = "self"; }
|
|
_Female = new GenderExtractor(femaleText, identifier, level+1);
|
|
_Male = new GenderExtractor(maleText, identifier, level+1);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
bool GenderExtractor::extractMarkup(const std::string& literal, const std::string & markup, std::string &before, std::string &inside, std::string & after)
|
|
{
|
|
std::string::size_type posBegin;
|
|
std::string::size_type posEnd;
|
|
std::string::size_type posInside;
|
|
|
|
std::string beginMarkup = std::string("<") + markup + std::string(">");
|
|
std::string endMarkup = std::string("</") + markup + std::string(">");
|
|
posBegin = literal.find(beginMarkup);
|
|
if ( posBegin != std::string::npos )
|
|
{
|
|
posEnd = literal.find(endMarkup, posBegin + beginMarkup.size());
|
|
if (posEnd != std::string::npos)
|
|
{
|
|
before = literal.substr(0, posBegin);
|
|
posInside = posBegin + beginMarkup.size();
|
|
inside = literal.substr(posInside, posEnd - posInside);
|
|
after = literal.substr(posEnd+endMarkup.size());
|
|
return true;
|
|
}
|
|
}
|
|
after = literal;
|
|
return false;
|
|
}
|
|
|
|
bool GenderExtractor::parseMarkup(const std::string& literal, const std::string & markup, std::string& newPhrase, bool include )
|
|
{
|
|
|
|
bool markupExist;
|
|
bool changed = false;
|
|
std::string oldPhrase = literal;
|
|
|
|
newPhrase = "";
|
|
do
|
|
{
|
|
std::string before;
|
|
std::string inside;
|
|
std::string after;
|
|
markupExist = extractMarkup(oldPhrase, markup, before, inside, after);
|
|
newPhrase += before;
|
|
if (include){ newPhrase += inside; }
|
|
oldPhrase = after;
|
|
if (markupExist){ changed = true; }
|
|
|
|
} while(markupExist);
|
|
|
|
newPhrase += oldPhrase;
|
|
return changed;
|
|
}
|
|
|
|
std::string GenderExtractor::getPhrase(unsigned int i) const
|
|
{
|
|
if ( i%2 == 0) { return _Male ? _Male->getPhrase(i/2) : _Text; }
|
|
if ( i%2 == 1) { nlassert(_Female); return _Female->getPhrase(i/2);}
|
|
nlassert(0);
|
|
return "";
|
|
}
|
|
|
|
std::string GenderExtractor::getExtension(unsigned int i) const
|
|
{
|
|
if ( i%2 == 0) { return _Male ? std::string("_m") + _Male->getExtension(i/2) : ""; }
|
|
if ( i%2 == 1) { nlassert(_Female); return std::string("_f") + _Female->getExtension(i/2);}
|
|
nlassert(0);
|
|
return "";
|
|
}
|
|
|
|
std::string GenderExtractor::getCondition(unsigned int i) const
|
|
{
|
|
|
|
//if ( i%2 == 0) { return _Male ? std::string("\t(") + _Male->getExtension(i/2) : "\t"; }
|
|
//if ( i%2 == 1) { nlassert(_Female); return std::string("_f") + _Female->getExtension(i/2);}
|
|
|
|
if ( i%2 == 0)
|
|
{
|
|
if (_Male)
|
|
{
|
|
std::string next = _Male->getCondition(i/2);
|
|
std::string current = _EntityName + ".gender = male";
|
|
return next.size() ? current + " & " + next : current;
|
|
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
if ( i%2 == 1)
|
|
{
|
|
std::string next = _Female->getCondition(i/2);
|
|
std::string current = _EntityName + ".gender = female";
|
|
return next.size() ? current + " & " + next : current;
|
|
}
|
|
|
|
nlassert(0);
|
|
|
|
return "";
|
|
}
|
|
|
|
unsigned int GenderExtractor::size() const
|
|
{
|
|
return _Male ? _Male->size() + _Female->size(): 1;
|
|
}
|
|
|
|
|
|
string CPhrase::genPhrase()
|
|
{
|
|
string ret;
|
|
if (!_PhraseLiterals.empty())
|
|
{
|
|
for (uint p=0; p<_PhraseLiterals.size(); ++p)
|
|
{
|
|
string identifier = _PhraseId;
|
|
if (_NumEntry != 0)
|
|
identifier += toString("_%u", p+1);
|
|
|
|
GenderExtractor gender(_PhraseLiterals[p], identifier, 0);
|
|
|
|
ret += identifier + " (";
|
|
// generate default param list
|
|
if (_DefaultParams.size() > p)
|
|
{
|
|
for (uint i=0; i<_DefaultParams[p].size(); ++i)
|
|
{
|
|
ret += STRING_MANAGER::paramTypeToString(_DefaultParams[p][i].ParamType) + " "+_DefaultParams[p][i].ParamName;
|
|
if (i != _DefaultParams[p].size()-1 || !_AdditionalParams.empty())
|
|
ret += ", ";
|
|
}
|
|
}
|
|
// generate additional param list
|
|
for (uint i=0; i<_AdditionalParams.size(); ++i)
|
|
{
|
|
ret += STRING_MANAGER::paramTypeToString(_AdditionalParams[i].ParamType) + " "+_AdditionalParams[i].ParamName;
|
|
if (i != _AdditionalParams.size()-1)
|
|
ret += ", ";
|
|
}
|
|
ret += ")" + NL;
|
|
ret += "{" + NL;
|
|
|
|
for (unsigned int i = 0; i < gender.size(); ++i)
|
|
{
|
|
ret += gender(i);
|
|
}
|
|
|
|
ret += "}" + NL + NL;
|
|
}
|
|
}
|
|
nlinfo("genphrase: %s", ret.c_str());
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CMissionCompiler::generateDotScript(NLLIGO::IPrimitive *missionPrim, std::string &dotScript, std::string &log)
|
|
{
|
|
//assume that the mission is compiled in the last compiled mission slot
|
|
try
|
|
{
|
|
if (compileMission(missionPrim, string()))
|
|
{
|
|
dotScript = _CompiledMission.back()->generateDotScript();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
catch(const EParseException & e)
|
|
{
|
|
log = e.Why;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
bool CMissionCompiler::parseGlobalMissionData(IPrimitive *mission, CMissionData &md)
|
|
{
|
|
// Mission name
|
|
string *s;
|
|
if (!mission->getPropertyByName("name", s) || s->empty())
|
|
throw EParseException(mission, "missing mission name !");
|
|
md.setMissionName(*s);
|
|
|
|
// giver primitive file
|
|
if (!mission->getPropertyByName("giver_primitive", s) || s->empty())
|
|
throw EParseException(mission, "missing giver primitive !");
|
|
md.setGiverPrimitive(*s);
|
|
|
|
// giver name
|
|
if (!mission->getPropertyByName("giver_primitive", s) || s->empty())
|
|
throw EParseException(mission, "missing giver primitive !");
|
|
md.setGiverName(*s);
|
|
// If the mission is under a npc_bot node, then the giver is directly taken
|
|
// from the npc name
|
|
if (mission->getParent())
|
|
{
|
|
if (mission->getParent()->getPropertyByName("class", s) && *s == "npc_bot")
|
|
{
|
|
if (mission->getParent()->getPropertyByName("name", s))
|
|
md.setGiverName(*s);
|
|
}
|
|
}
|
|
|
|
// TODO : read all other params...
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
void CMissionData::initHeaderPhrase(IPrimitive *prim)
|
|
{
|
|
CPhrase::TPredefParams params;
|
|
params.resize(1);
|
|
params[0].push_back(CPhrase::TParamInfo("giver", STRING_MANAGER::bot));
|
|
_MissionTitle.initPhrase(*this, prim, _MissionTitleRaw, 0, params);
|
|
_MissionDescription.initPhrase(*this, prim, _MissionDescriptionRaw, 0, params);
|
|
_MissionAutoMenu.initPhrase(*this, prim, _MissionAutoMenuRaw);
|
|
}
|
|
|
|
bool CMissionCompiler::compileMission(NLLIGO::IPrimitive *rootPrim, const std::string &primFileName)
|
|
{
|
|
TPrimitiveClassPredicate pred("mission_tree");
|
|
if (!pred(rootPrim))
|
|
return false;
|
|
|
|
IPrimitive *mission = rootPrim;
|
|
CMissionData *pmd = new CMissionData;
|
|
CMissionData &md = *pmd;
|
|
|
|
// Read the mission name
|
|
string missionName = md.getProperty(mission, "name", false, false);
|
|
if( missionName.find(' ') != string::npos)
|
|
{
|
|
throw EParseException(mission, toString("Mission name '%s' must not contains space", missionName.c_str()).c_str());
|
|
}
|
|
md.setMissionName(missionName);
|
|
// Create a temporary primitive node to create default variable
|
|
{
|
|
// giver default var
|
|
IPrimitive *temp = new CPrimNode();
|
|
temp->addPropertyByName("class", new CPropertyString("var_npc"));
|
|
temp->addPropertyByName("name", new CPropertyString("giver = giver"));
|
|
temp->addPropertyByName("npc_name", new CPropertyString("giver"));
|
|
temp->addPropertyByName("var_name", new CPropertyString("giver"));
|
|
|
|
IVar *var = IVar::createVar(md, temp);
|
|
md.addVariable(NULL, var);
|
|
|
|
delete temp;
|
|
}
|
|
|
|
{
|
|
// player default var
|
|
IPrimitive *temp = new CPrimNode();
|
|
temp->addPropertyByName("class", new CPropertyString("var_npc"));
|
|
temp->addPropertyByName("name", new CPropertyString("player = player"));
|
|
temp->addPropertyByName("npc_name", new CPropertyString("player"));
|
|
temp->addPropertyByName("var_name", new CPropertyString("player"));
|
|
|
|
IVar *var = IVar::createVar(md, temp);
|
|
md.addVariable(NULL, var);
|
|
|
|
delete temp;
|
|
}
|
|
|
|
{
|
|
// guild_name default var
|
|
IPrimitive *temp = new CPrimNode();
|
|
temp->addPropertyByName("class", new CPropertyString("var_text"));
|
|
temp->addPropertyByName("name", new CPropertyString("guild_name = guild_name"));
|
|
temp->addPropertyByName("npc_name", new CPropertyString("guild_name"));
|
|
temp->addPropertyByName("var_name", new CPropertyString("guild_name"));
|
|
|
|
IVar *var = IVar::createVar(md, temp);
|
|
md.addVariable(NULL, var);
|
|
|
|
delete temp;
|
|
}
|
|
|
|
// first, start by reading mission variables
|
|
IPrimitive *variables;
|
|
{
|
|
TPrimitiveClassPredicate predTmp("variables");
|
|
variables= NLLIGO::getPrimitiveChild(mission, predTmp);
|
|
}
|
|
|
|
if (!variables)
|
|
{
|
|
nlwarning("Can't find variables !");
|
|
return false;
|
|
}
|
|
parseVariables(md, variables);
|
|
|
|
// read global mission data
|
|
md.parseMissionHeader(rootPrim);
|
|
|
|
// now, we can init the mission header phrase (they need variable knwoled)
|
|
md.initHeaderPhrase(rootPrim);
|
|
|
|
IPrimitive *preReq;
|
|
{
|
|
TPrimitiveClassPredicate predTmp("pre_requisite");
|
|
preReq = getPrimitiveChild(mission, predTmp);
|
|
}
|
|
|
|
if (!preReq)
|
|
{
|
|
nlwarning("Can't find pre requisite !");
|
|
return false;
|
|
}
|
|
parsePreRequisite(md, preReq);
|
|
|
|
/* IPrimitive *steps = getPrimitiveChild(mission, TPrimitivePropertyPredicate("step_tag", "true"));
|
|
if (!steps)
|
|
{
|
|
nlwarning("Can't find steps !");
|
|
return false;
|
|
}
|
|
*/ parseSteps(md, mission);
|
|
|
|
// Store the compiled mission
|
|
_CompiledMission.push_back(pmd);
|
|
|
|
string script = md.generateMissionScript(primFileName);
|
|
|
|
nlinfo("The script :");
|
|
nlinfo("%s", script.c_str());
|
|
|
|
string phrases = md.generatePhraseFile();
|
|
nlinfo("The phrase file is :");
|
|
{
|
|
vector<string> lines;
|
|
explode(phrases, string("\n"), lines, false);
|
|
for (uint i=0; i<lines.size(); ++i)
|
|
{
|
|
if(lines[i][0] == '\r') lines[i] = lines[i].substr(1);
|
|
nlinfo("%s", lines[i].c_str());
|
|
}
|
|
}
|
|
|
|
string dot = md.generateDotScript();
|
|
nlinfo("The dot script is :");
|
|
{
|
|
vector<string> lines;
|
|
explode(dot, string("\n"), lines, false);
|
|
for (uint i=0; i<lines.size(); ++i)
|
|
{
|
|
if(lines[i][0] == '\r') lines[i] = lines[i].substr(1);
|
|
nlinfo("%s", lines[i].c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CMissionCompiler::compileMissions(IPrimitive *rootPrim, const std::string &primFileName)
|
|
{
|
|
bool ret = true;
|
|
// 1st, build a set of mission_scrip nodes
|
|
NLLIGO::TPrimitiveSet missionTrees;
|
|
|
|
CPrimitiveSet<TPrimitiveClassPredicate> scriptsSet;
|
|
|
|
TPrimitiveClassPredicate pred("mission_tree");
|
|
scriptsSet.buildSet(rootPrim, pred, missionTrees);
|
|
|
|
nlinfo("Found %u mission tree in the primitive file", missionTrees.size());
|
|
|
|
for (uint i=0; i<missionTrees.size(); ++i)
|
|
{
|
|
// try
|
|
// {
|
|
compileMission(missionTrees[i], primFileName);
|
|
// }
|
|
// catch (const EParseException &e)
|
|
// {
|
|
// nlwarning("Error while parsing a mission: '%s'", e.Why.c_str());
|
|
// ret = false;
|
|
// }
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
bool CMissionCompiler::installCompiledMission(NLLIGO::CLigoConfig &ligoConfig, const std::string &primFileName)
|
|
{
|
|
// generate the mission script into the npcs...
|
|
{
|
|
map<string, TLoadedPrimitive > loadedPrimitives;
|
|
|
|
// store the previous alias value
|
|
map<string, uint32> missionAlias;
|
|
|
|
// First loop to remove any mission that belong to the compiled primitive file
|
|
for (uint i=0; i<_CompiledMission.size(); ++i)
|
|
{
|
|
CMissionData &mission = *(_CompiledMission[i]);
|
|
// first, look for the primitive file to load
|
|
string fileName = mission.getGiverPrimitive();
|
|
if (fileName.empty())
|
|
{
|
|
// use mission primitive instead
|
|
fileName = primFileName;
|
|
}
|
|
if (loadedPrimitives.find(toLower(fileName)) == loadedPrimitives.end())
|
|
{
|
|
string fullFileName = CPath::lookup(fileName, false);
|
|
if (fullFileName.empty())
|
|
{
|
|
throw EParseException(NULL, toString("Can't find primitive file '%s' in path", fileName.c_str()).c_str());
|
|
}
|
|
// we need to load this primitive file.
|
|
CPrimitives *primDoc = new CPrimitives;
|
|
CPrimitiveContext::instance().CurrentPrimitive = primDoc;
|
|
if (loadXmlPrimitiveFile(*primDoc, fullFileName, ligoConfig))
|
|
{
|
|
// the primitive file is loaded correctly
|
|
loadedPrimitives.insert(make_pair(toLower(fileName), TLoadedPrimitive(primDoc, fullFileName)));
|
|
CPrimitiveContext::instance().CurrentPrimitive = NULL;
|
|
}
|
|
else
|
|
{
|
|
CPrimitiveContext::instance().CurrentPrimitive = NULL;
|
|
throw EParseException(NULL, toString("Can't read primitive file '%s'", fullFileName.c_str()).c_str());
|
|
}
|
|
}
|
|
TLoadedPrimitive &loadedPrim = loadedPrimitives[toLower(fileName)];
|
|
CPrimitives *primDoc = loadedPrim.PrimDoc;
|
|
|
|
TPrimitiveSet scripts;
|
|
CPrimitiveSet<TPrimitiveClassPredicate> filter;
|
|
TPrimitiveClassPredicate pred("mission");
|
|
filter.buildSet(primDoc->RootNode, pred, scripts);
|
|
|
|
// for each script, check if it was generated, and if so, check the name
|
|
// of the source primitive file.
|
|
for (uint i=0; i<scripts.size(); ++i)
|
|
{
|
|
vector<string> *script;
|
|
if (scripts[i]->getPropertyByName("script", script) && !script->empty())
|
|
{
|
|
string missionName;
|
|
|
|
scripts[i]->getPropertyByName("name", missionName);
|
|
|
|
// Format should be : #compiled from <source_primitive_name>
|
|
if (script->front().find("generated from") != string::npos)
|
|
{
|
|
// we have a compiled mission
|
|
if (script->front().find(CFile::getFilename(primFileName)) != string::npos)
|
|
{
|
|
// ok, this mission is compiled from the same primitive
|
|
|
|
// store it's alias
|
|
TPrimitiveClassPredicate pred("alias");
|
|
|
|
IPrimitive *p = getPrimitiveChild(scripts[i], pred);
|
|
|
|
if (p)
|
|
{
|
|
CPrimAlias *pa = dynamic_cast<CPrimAlias*>(p);
|
|
if (pa)
|
|
{
|
|
uint32 alias = pa->getAlias();
|
|
missionAlias.insert(make_pair(missionName, alias));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlwarning("Can't find alias prim in primitive '%s'", buildPrimPath(scripts[i]).c_str());
|
|
}
|
|
|
|
// and remove it
|
|
scripts[i]->getParent()->removeChild(scripts[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// second loop to assign compiled mission to giver npc
|
|
for (uint i=0; i<_CompiledMission.size(); ++i)
|
|
{
|
|
CMissionData &mission = *(_CompiledMission[i]);
|
|
string fileName = mission.getGiverPrimitive();
|
|
if (fileName.empty())
|
|
{
|
|
// no giver primitive file specified in the mission, use the mission primitive instead
|
|
fileName = primFileName;
|
|
}
|
|
|
|
TLoadedPrimitive &loadedPrim = loadedPrimitives[toLower(fileName)];
|
|
CPrimitives *primDoc = loadedPrim.PrimDoc;
|
|
CPrimitiveContext::instance().CurrentPrimitive = primDoc;
|
|
|
|
TPrimitiveSet bots;
|
|
CPrimitiveSet<TPrimitiveClassAndNamePredicate> filter;
|
|
TPrimitiveClassAndNamePredicate pred("npc_bot", mission.getGiverName());
|
|
filter.buildSet(primDoc->RootNode, pred, bots);
|
|
|
|
if (bots.empty())
|
|
{
|
|
string err = toString("Can't find bot '%s' in primitive '%s' !",
|
|
mission.getGiverName().c_str(),
|
|
fileName.c_str());
|
|
throw EParseException(NULL, err.c_str());
|
|
}
|
|
else if (bots.size() > 1)
|
|
{
|
|
string err = toString("Found more than one bot named '%s' in primitive '%s' !",
|
|
mission.getGiverName().c_str(),
|
|
fileName.c_str());
|
|
throw EParseException(NULL, err.c_str());
|
|
}
|
|
|
|
// ok, all is good, we can add the mission node to the giver
|
|
IPrimitive *giver = bots.front();
|
|
// create a new node for the mission
|
|
IPrimitive *script = new CPrimNode;
|
|
// set the class
|
|
script->addPropertyByName("class", new CPropertyString("mission"));
|
|
// set the name
|
|
script->addPropertyByName("name", new CPropertyString(mission.getMissionName()));
|
|
// string alias(toString("%u", makeHash32(mission.getMissionName())));
|
|
// script->addPropertyByName("alias", new CPropertyString(mission.getAlias()));
|
|
string scriptLines = mission.generateMissionScript(primFileName);
|
|
vector<string> lines;
|
|
explode(scriptLines, NL, lines, false);
|
|
|
|
script->addPropertyByName("script", new CPropertyStringArray(lines));
|
|
|
|
// insert the script into the giver
|
|
giver->insertChild(script);
|
|
|
|
// add the alias
|
|
{
|
|
CPrimAlias *pa = new CPrimAlias;
|
|
pa->addPropertyByName("class", new CPropertyString ("alias"));
|
|
pa->addPropertyByName("name", new CPropertyString ("alias"));
|
|
|
|
if (missionAlias.find(mission.getMissionName()) != missionAlias.end())
|
|
{
|
|
// restore the previous alias
|
|
primDoc->forceAlias(pa, missionAlias.find(mission.getMissionName())->second);
|
|
}
|
|
|
|
// insert in first place
|
|
script->insertChild(pa, 0);
|
|
}
|
|
|
|
CPrimitiveContext::instance().CurrentPrimitive = NULL;
|
|
}
|
|
|
|
// Save the modified primitive files
|
|
while (!loadedPrimitives.empty())
|
|
{
|
|
TLoadedPrimitive &loadedPrim = loadedPrimitives.begin()->second;
|
|
if (!saveXmlPrimitiveFile(*(loadedPrim.PrimDoc), loadedPrim.FullFileName))
|
|
return false;
|
|
|
|
_FilesToPublish.push_back(loadedPrim.FullFileName);
|
|
|
|
// Free the memory
|
|
delete loadedPrim.PrimDoc;
|
|
|
|
loadedPrimitives.erase(loadedPrimitives.begin());
|
|
}
|
|
}
|
|
|
|
// generate the phrase file (if any)
|
|
{
|
|
string phraseFileName = CFile::getFilenameWithoutExtension(primFileName) + "_wk.txt";
|
|
|
|
CSString content;
|
|
|
|
for (uint i=0; i<_CompiledMission.size(); ++i)
|
|
{
|
|
content += _CompiledMission[i]->generatePhraseFile();
|
|
}
|
|
// transform NL (\n\r) into single \n
|
|
content = content.replace(NL.c_str(), "\n");
|
|
ucstring ucs;
|
|
ucs.fromUtf8(content);
|
|
|
|
CI18N::writeTextFile(phraseFileName, ucs, true);
|
|
|
|
_FilesToPublish.push_back(phraseFileName);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CMissionCompiler::publishFiles(const std::string &serverPathPrim, const std::string &serverPathText, const std::string &localPathText)
|
|
{
|
|
for (uint i=0 ; i<_FilesToPublish.size() ; i++)
|
|
{
|
|
string dst, src = _FilesToPublish[i];
|
|
|
|
string::size_type n = src.find("primitives");
|
|
if (n == string::npos)
|
|
{
|
|
// text files : copy it and check include in phrase_rites_wk.txt
|
|
|
|
// server
|
|
string textFile = CPath::standardizePath(serverPathText) + "phrase_rites_wk.txt";
|
|
includeText(textFile, string("#include \"") + src + string("\"\n"));
|
|
dst = CPath::standardizePath(serverPathText) + src;
|
|
NLMISC::CFile::copyFile(dst, src);
|
|
|
|
// local
|
|
textFile = CPath::standardizePath(localPathText) + "phrase_rites_wk.txt";
|
|
includeText(textFile, string("#include \"") + src + string("\"\n"));
|
|
dst = CPath::standardizePath(localPathText) + src;
|
|
NLMISC::CFile::copyFile(dst, src);
|
|
}
|
|
else
|
|
{
|
|
// primitive file : copy to server
|
|
dst = CPath::standardizePath(serverPathPrim) + string(src, n, src.size());
|
|
NLMISC::CFile::copyFile(dst, src);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CMissionCompiler::includeText(const std::string filename, const std::string text)
|
|
{
|
|
FILE *f = fopen(filename.c_str(), "r+");
|
|
if (f == NULL)
|
|
return false;
|
|
|
|
bool isIn = false;
|
|
char buffer[1024];
|
|
|
|
// Check for UTF8 format
|
|
fread(buffer, 1, 3, f);
|
|
if (buffer[0] != -17 || buffer[1] != -69 || buffer[2] != -65)
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
// Compare each line
|
|
while(fgets(buffer, 1024, f))
|
|
{
|
|
if (!strcmp(text.c_str(), buffer))
|
|
{
|
|
isIn = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isIn)
|
|
fputs(text.c_str(), f);
|
|
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
bool CMissionCompiler::parsePreRequisite(CMissionData &md, IPrimitive *preReq)
|
|
{
|
|
md.parsePrerequisites(preReq);
|
|
return true;
|
|
}
|
|
|
|
bool CMissionCompiler::parseOneStep(CMissionData &md, IPrimitive *stepToParse, IStep *parent, bool bEndOfBranch)
|
|
{
|
|
IStep *step = IStep::createStep(md, stepToParse);
|
|
if (step != NULL)
|
|
{
|
|
|
|
if (!step->isAJump() && !step->getStepName().empty())
|
|
{
|
|
if (md.getStepByName(step->getStepName()) != NULL)
|
|
{
|
|
string err = toString("Step '%s' already defined !", step->getStepName().c_str());
|
|
throw EParseException(step->getPrimitive(), err.c_str());
|
|
}
|
|
|
|
if (step->getStepName().find(' ') != string::npos)
|
|
{
|
|
throw EParseException(step->getPrimitive(), toString("Step name '%s' must not contains space", step->getStepName().c_str()).c_str());
|
|
}
|
|
md.addStepName(step->getStepName(), step);
|
|
}
|
|
|
|
TPrimitiveSet subBranchs = step->getSubBranchs();
|
|
|
|
// Add the step (if no parent add to the mission data)
|
|
if (parent == NULL)
|
|
{
|
|
if (!md.addStep(step))
|
|
{
|
|
throw EParseException(stepToParse, "Error parsing mission step");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parent->addSubStep(step);
|
|
}
|
|
|
|
CStepIf *pSI = dynamic_cast<CStepIf *>(step);
|
|
// If this is a IF step : parse with 'step' as a parent
|
|
|
|
IStep *pParentStep = NULL;
|
|
|
|
if ((dynamic_cast<CStepIf*>(step) != NULL) ||
|
|
(dynamic_cast<CStepPlayerReconnect*>(step) != NULL))
|
|
pParentStep = step;
|
|
|
|
if (!subBranchs.empty())
|
|
{
|
|
// need to parse subbranch before continuing
|
|
for (uint i=0; i<subBranchs.size(); ++i)
|
|
{
|
|
if (!parseOneStep(md, subBranchs[i], pParentStep, i==(subBranchs.size()-1)))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// if this is the last step, flag it as such
|
|
step->EndOfBranch = bEndOfBranch;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CMissionCompiler::parseSteps(CMissionData &md, IPrimitive *steps, IStep *parent)
|
|
{
|
|
TPrimitiveSet childs;
|
|
TPrimitivePropertyPredicate pred("step_tag", "true");
|
|
filterPrimitiveChilds(steps, pred, childs);
|
|
|
|
if (childs.empty())
|
|
{
|
|
CPrimNode node;
|
|
node.addPropertyByName("class", new CPropertyString("end"));
|
|
node.addPropertyByName("name", new CPropertyString(""));
|
|
IStep *step = IStep::createStep(md, &node);
|
|
delete step;
|
|
// md.addStep(step);
|
|
}
|
|
if (!childs.empty())
|
|
{
|
|
for (uint i=0; i<childs.size(); ++i)
|
|
{
|
|
IPrimitive *child = childs[i];
|
|
|
|
parseOneStep(md, childs[i], NULL, i == (childs.size()-1));
|
|
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
string CMissionCompiler::getProp(IPrimitive *prim, const string &propName)
|
|
{
|
|
string s;
|
|
bool ret = prim->getPropertyByName(propName.c_str(), s);
|
|
if (!ret)
|
|
throw EParseException(prim, toString("Property %s does't exist", propName.c_str()).c_str());
|
|
|
|
return s;
|
|
}
|
|
|
|
string CMissionCompiler::getClass(IPrimitive *prim)
|
|
{
|
|
string className;
|
|
bool ret = prim->getPropertyByName("class", className);
|
|
nlassert(ret);
|
|
return className;
|
|
}
|
|
|
|
bool CMissionCompiler::parseVariables(CMissionData &md, IPrimitive *variables)
|
|
{
|
|
for (uint i=0; i<variables->getNumChildren(); ++i)
|
|
{
|
|
IPrimitive *child;
|
|
if (variables->getChild(child, i))
|
|
{
|
|
IVar *var = IVar::createVar(md, child);
|
|
if (var)
|
|
{
|
|
nldebug("Adding variable '%s' as type %u", var->getVarName().c_str(), var->getVarType());
|
|
md.addVariable(child, var);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <class VectorType>
|
|
bool strtokquote(const string &src, VectorType &tokens)
|
|
{
|
|
enum TMode
|
|
{
|
|
read_blank,
|
|
read_token,
|
|
read_quoted
|
|
};
|
|
|
|
string temp;
|
|
TMode mode = read_blank;
|
|
|
|
for (uint i=0; i<src.size(); ++i)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case read_blank:
|
|
if (src[i] != ' ' && src[i] != '\t' && src[i] != '\n' && src[i] != '\r')
|
|
{
|
|
// end of blank !
|
|
if (src[i] == '\"')
|
|
{
|
|
// begin of a quoted string
|
|
temp = "\"";
|
|
mode = read_quoted;
|
|
}
|
|
else
|
|
{
|
|
// begin of a token
|
|
temp.clear();
|
|
temp += src[i];
|
|
mode = read_token;
|
|
}
|
|
}
|
|
break;
|
|
case read_token:
|
|
if (src[i] == ' ' || src[i] == '\t' || src[i] == '\n' || src[i] == '\r' || src[i] == '\"')
|
|
{
|
|
// end of token
|
|
tokens.push_back(temp);
|
|
temp.clear();
|
|
--i;
|
|
mode = read_blank;
|
|
}
|
|
else
|
|
{
|
|
temp += src[i];
|
|
}
|
|
break;
|
|
case read_quoted:
|
|
if (src[i] == '\\')
|
|
{
|
|
// special treatment for escape command
|
|
if (i < src.size()-1)
|
|
{
|
|
temp += src[i];
|
|
temp += src[i+1];
|
|
// skip escaped char
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
nlwarning("Error parsing escape char in quoted string");
|
|
return false;
|
|
}
|
|
}
|
|
else if (src[i] != '\"')
|
|
{
|
|
// just add this char
|
|
temp += src[i];
|
|
}
|
|
else
|
|
{
|
|
// end of quoted string
|
|
temp += src[i];
|
|
tokens.push_back(temp);
|
|
temp.clear();
|
|
mode = read_blank;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!temp.empty())
|
|
{
|
|
if (mode == read_quoted)
|
|
{
|
|
nlwarning("Missing closing quote at end of string while reading text in '%s'", src.c_str());
|
|
return false;
|
|
}
|
|
tokens.push_back(temp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <class VectorType>
|
|
bool strtokquote(const vector<string> &src, VectorType &tokens)
|
|
{
|
|
for (uint i=0; i<src.size(); ++i)
|
|
{
|
|
if (!strtokquote(src[i], tokens))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
struct TFindParamPred : std::unary_function<CPhrase::TParamInfo, bool>
|
|
{
|
|
string Name;
|
|
TFindParamPred(const std::string &name)
|
|
: Name (name)
|
|
{}
|
|
|
|
bool operator() (const CPhrase::TParamInfo ¶mInfo) const
|
|
{
|
|
return paramInfo.ParamName == Name;
|
|
}
|
|
};
|
|
|
|
|
|
bool CPhrase::isEmpty()
|
|
{
|
|
return _PhraseId.empty();
|
|
}
|
|
|
|
bool CPhrase::asAdditionnalParams()
|
|
{
|
|
return !_AdditionalParams.empty();
|
|
}
|
|
|
|
|
|
void CPhrase::initPhrase (CMissionData &md,
|
|
IPrimitive *prim,
|
|
const vector<string> &texts,
|
|
uint32 numEntry,
|
|
const TPredefParams &predefParams )
|
|
{
|
|
// nlassert(numEntry == predefParams.size());
|
|
|
|
// store the predefined/default parameters
|
|
_DefaultParams = predefParams;
|
|
// store the number of entry to generate (for literal with variant)
|
|
_NumEntry = numEntry;
|
|
|
|
numEntry = max(uint32(1), numEntry);
|
|
|
|
_PhraseLiterals.clear();
|
|
// _PhraseLiterals.resize(numEntry);
|
|
|
|
// first, concatenate the text vector
|
|
string text;
|
|
for (uint i=0; i<texts.size(); ++i)
|
|
{
|
|
text = text + texts[i];
|
|
if (i != texts.size() -1)
|
|
text += "\n";
|
|
}
|
|
|
|
nldebug("phrase text: %s", text.c_str());
|
|
|
|
CVectorSString tokens;
|
|
|
|
if (!strtokquote(text, tokens))
|
|
throw EParseException(prim, toString("failed to tokenize the string '%s'", text.c_str()).c_str());
|
|
|
|
if (tokens.empty())
|
|
// nothing to parse
|
|
return;
|
|
|
|
// storage for additional parameters
|
|
vector<string> params;
|
|
|
|
retry:
|
|
// ok, the string is parsed, now we can analyze it
|
|
// look at the first letter of the first token to determine the type of data we have
|
|
if (tokens[0][0] == '\"')
|
|
{
|
|
// we have a literal, so we must found numEntry literal, then a suffix tag for the phrase name
|
|
if (tokens.size() != numEntry +1)
|
|
throw EParseException(prim, toString("bad number of tokens in phrase : need %u (%u entries + 1 suffix), found %u\n(in : '%s')",
|
|
numEntry+1,
|
|
numEntry,
|
|
tokens.size(),
|
|
text.c_str()
|
|
).c_str());
|
|
|
|
_PhraseLiterals.resize(numEntry);
|
|
for (uint i=0; i<numEntry; ++i)
|
|
{
|
|
CSString text = tokens[i];
|
|
// remove quotation marks
|
|
text = text.leftCrop(1);
|
|
text = text.rightCrop(1);
|
|
|
|
// store the literal phrase value
|
|
_PhraseLiterals[i] = text;
|
|
// escape any ']' in the string
|
|
_PhraseLiterals[i] = CSString(_PhraseLiterals[i]).replace("]", "\\]");
|
|
|
|
// now, we can analyse the string content, looking for parameters replacement
|
|
while (text.contains('$'))
|
|
{
|
|
// 'advance' to replacement point
|
|
text = text.splitFrom('$');
|
|
if (!text.empty())
|
|
{
|
|
if (text[0] != '$')
|
|
{
|
|
if (!text.contains('$'))
|
|
throw EParseException(prim, "missing parameter closing tag '$'");
|
|
|
|
string::size_type paramStart = _PhraseLiterals[i].size() - text.size();
|
|
// ok, we found a parameter
|
|
CSString p = text.splitTo('$', true);
|
|
// remove any subpart access
|
|
p = p.splitTo('.');
|
|
if (i >= predefParams.size() || find_if(predefParams[i].begin(), predefParams[i].end(), TFindParamPred(static_cast<string&>(p))) == predefParams[i].end())
|
|
{
|
|
// this param is not in the predefined params list, add it to the optional params
|
|
params.push_back(p);
|
|
}
|
|
|
|
// remove any compiler param from the phrase literal
|
|
if (p.find("@") != string::npos)
|
|
{
|
|
string::size_type pos = _PhraseLiterals[i].find(p, paramStart);
|
|
if (pos != string::npos)
|
|
{
|
|
string::size_type pos2 = _PhraseLiterals[i].find("@", pos);
|
|
if (pos2 != string::npos)
|
|
{
|
|
while (pos2 < _PhraseLiterals[i].size()
|
|
&& _PhraseLiterals[i][pos2] != '.'
|
|
&& _PhraseLiterals[i][pos2] != '$')
|
|
{
|
|
_PhraseLiterals[i].erase(pos2, 1);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// this is an escaped $, skip it
|
|
text.leftCrop(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// last, read the suffix
|
|
_Suffixe = tokens.back();
|
|
|
|
// generate identifier
|
|
_PhraseId = toUpper(md.getMissionName()+"_"+_Suffixe);
|
|
|
|
set<string> ps;
|
|
// select only unique params
|
|
ps.insert(params.begin(), params.end());
|
|
|
|
vector<string> temp(ps.begin(), ps.end());
|
|
params.swap(temp);
|
|
|
|
}
|
|
else if (tokens[0][0] == '$')
|
|
{
|
|
// we have a variable substitution. Retrieve the var and recall init
|
|
|
|
// do the var replacement
|
|
CVectorSString tokens2;
|
|
|
|
tokens[0] = md.replaceVar(prim, tokens[0]);
|
|
|
|
if (!strtokquote(tokens[0], tokens2))
|
|
throw EParseException(prim, toString("failed to tokenize the string ('%s')", tokens[0].c_str()).c_str());
|
|
|
|
tokens2.insert(tokens2.end(), tokens.begin()+1, tokens.end());
|
|
tokens.swap(tokens2);
|
|
|
|
// and retry the decoding
|
|
goto retry;
|
|
}
|
|
else
|
|
{
|
|
// this should be a simple identifier, followed by any number of additional parameters
|
|
|
|
// do the var replacement
|
|
// tokens = md.replaceVar(prim, tokens);
|
|
// untagVar(tokens[0]);
|
|
|
|
// ok, now extract the phrase label and the additional parameters
|
|
_PhraseId = tokens[0];
|
|
for (uint i=1; i<tokens.size(); ++i)
|
|
{
|
|
untagVar(tokens[i]);
|
|
if (predefParams.empty() || find_if(predefParams[0].begin(), predefParams[0].end(), TFindParamPred(static_cast<string&>(tokens[i]))) == predefParams[0].end())
|
|
{
|
|
// this param is not in the predefined params list, add it to the optional params
|
|
params.push_back(tokens[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// now, build the parameter list
|
|
|
|
vector<string>::iterator first(params.begin()), last(params.end());
|
|
for (; first != last; ++first)
|
|
{
|
|
string name, param;
|
|
vector<string> parts;
|
|
NLMISC::explode(*first, string("@"), parts, false);
|
|
|
|
if (parts.size() > 0)
|
|
name = parts[0];
|
|
if (parts.size() > 1)
|
|
param = parts[1];
|
|
|
|
const string &varName = name;
|
|
|
|
if (varName != "self")
|
|
{
|
|
IVar *var = md.getVariable(varName);
|
|
if (var == NULL)
|
|
{
|
|
string err = toString("Can't find variable '%s' referenced from a phrase",
|
|
name.c_str());
|
|
throw EParseException(prim, err.c_str());
|
|
}
|
|
|
|
TParamInfo pi;
|
|
pi.ParamName = name;
|
|
pi.CompilerParam = param;
|
|
pi.ParamType = var->getStringManagerType();
|
|
_AdditionalParams.push_back(pi);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string CPhrase::genScript(CMissionData &md)
|
|
{
|
|
std::string ret;
|
|
|
|
ret = _PhraseId;
|
|
for (uint i=0; i<_AdditionalParams.size(); ++i)
|
|
{
|
|
IVar *var = md.getVariable(_AdditionalParams[i].ParamName);
|
|
if (var == NULL)
|
|
{
|
|
string err = toString("Can't find variable named '%s' to generate phrase param", _AdditionalParams[i].ParamName.c_str());
|
|
throw EParseException(NULL, err.c_str());
|
|
}
|
|
ret += "; " + var->evalVar(_AdditionalParams[i].CompilerParam);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
CMissionData::CMissionData()
|
|
{
|
|
// init all datas
|
|
_MonoInstance = false;
|
|
_MissionAuto = false;
|
|
_RunOnce = false;
|
|
_Replayable = false;
|
|
_Solo = false;
|
|
_Guild = false;
|
|
_NotInJournal = false;
|
|
_AutoRemoveFromJournal = false;
|
|
_PlayerReplayTimer = 0;
|
|
_GlobalReplayTimer = 0;
|
|
_NotProposed = false;
|
|
_NonAbandonnable = false;
|
|
_NeedValidation = false;
|
|
_FailIfInventoryIsFull = false;
|
|
}
|
|
|
|
CMissionData::~CMissionData()
|
|
{
|
|
while (!_Variables.empty())
|
|
{
|
|
delete _Variables.begin()->second;
|
|
_Variables.erase(_Variables.begin());
|
|
}
|
|
|
|
while (!_Steps.empty())
|
|
{
|
|
delete _Steps.back();
|
|
_Steps.pop_back();
|
|
}
|
|
}
|
|
|
|
void CMissionData::setMissionName(const string &missionName)
|
|
{
|
|
_MissionName = missionName;
|
|
}
|
|
|
|
const string &CMissionData::getMissionName() { return _MissionName;}
|
|
|
|
bool CMissionData::addVariable(NLLIGO::IPrimitive *prim, IVar *var)
|
|
{
|
|
if (_Variables.find(var->getVarName()) != _Variables.end())
|
|
throw EParseException(prim, toString("Variable '%s' already defined !", var->getVarName().c_str()).c_str());
|
|
|
|
_Variables.insert(make_pair(var->getVarName(), var));
|
|
_VariablesOrder.push_back(var);
|
|
return true;
|
|
}
|
|
|
|
IVar *CMissionData::getVariable(const string &varName)
|
|
{
|
|
map<string, IVar*>::iterator it(_Variables.find(varName));
|
|
if (it != _Variables.end())
|
|
return it->second;
|
|
return NULL;
|
|
}
|
|
|
|
IStep *CMissionData::getNextStep(IStep *current)
|
|
{
|
|
for (uint i=0; i<_Steps.size(); ++i)
|
|
{
|
|
if (_Steps[i] == current && i < _Steps.size()-1)
|
|
return _Steps[i+1];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
IStep *CMissionData::getStepByName(const std::string &stepName)
|
|
{
|
|
if (_StepsByNames.find(stepName) != _StepsByNames.end())
|
|
{
|
|
return _StepsByNames[stepName];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool CMissionData::addStep(IStep *step)
|
|
{
|
|
_Steps.push_back(step);
|
|
return true;
|
|
}
|
|
|
|
string CMissionData::genPreRequisites()
|
|
{
|
|
string ret;
|
|
if (!_ReqSkills.empty())
|
|
{
|
|
ret += "req_skill : ";
|
|
for (uint i=0; i<_ReqSkills.size(); ++i)
|
|
{
|
|
ret += _ReqSkills[i].Skill+" "+_ReqSkills[i].MinLevel+" "+_ReqSkills[i].MaxLevel;
|
|
if (i < _ReqSkills.size()-1)
|
|
ret +="; ";
|
|
else
|
|
ret += NL;
|
|
}
|
|
}
|
|
if (!_ReqMissionDone.empty())
|
|
{
|
|
for (uint i=0; i<_ReqMissionDone.size(); ++i)
|
|
{
|
|
ret += "req_mission : "+ _ReqMissionDone[i]+NL;
|
|
}
|
|
}
|
|
if (!_ReqMissionNotDone.empty())
|
|
{
|
|
for (uint i=0; i<_ReqMissionNotDone.size(); ++i)
|
|
{
|
|
ret += "req_mission_neg : "+_ReqMissionNotDone[i]+NL;
|
|
}
|
|
}
|
|
if (!_ReqMissionRunning.empty())
|
|
{
|
|
for (uint i=0; i<_ReqMissionRunning.size(); ++i)
|
|
{
|
|
ret += "req_mission_running : "+_ReqMissionRunning[i]+NL;
|
|
}
|
|
}
|
|
if (!_ReqMissionNotRunning.empty())
|
|
{
|
|
|
|
for (uint i=0; i<_ReqMissionNotRunning.size(); ++i)
|
|
{
|
|
ret += "req_mission_running_neg : "+_ReqMissionNotRunning[i]+NL;
|
|
}
|
|
}
|
|
if (!_ReqWearItem.empty())
|
|
{
|
|
ret += "req_wear : ";
|
|
for (uint i=0; i<_ReqWearItem.size(); ++i)
|
|
{
|
|
ret += _ReqWearItem[i];
|
|
if(i < _ReqWearItem.size()-1)
|
|
ret +="; ";
|
|
ret += NL;
|
|
}
|
|
}
|
|
if (!_ReqOwnItem.empty())
|
|
{
|
|
ret += "req_item : ";
|
|
for (uint i=0; i<_ReqOwnItem.size(); ++i)
|
|
{
|
|
ret += _ReqOwnItem[i];
|
|
if(i < _ReqOwnItem.size()-1)
|
|
ret +="; ";
|
|
ret += NL;
|
|
}
|
|
}
|
|
if (!_ReqTitle.empty())
|
|
{
|
|
ret += "req_title : "+_ReqTitle+NL;
|
|
}
|
|
if (!_ReqFames.empty())
|
|
{
|
|
for (uint i=0; i<_ReqFames.size(); ++i)
|
|
{
|
|
ret += "req_fame : "+_ReqFames[i].Faction+" "+_ReqFames[i].Fame;
|
|
ret += NL;
|
|
}
|
|
}
|
|
if(_ReqGuild)
|
|
{
|
|
ret += "req_guild"+NL;
|
|
}
|
|
if (!_ReqGrade.empty())
|
|
{
|
|
ret += "req_grade : "+_ReqGrade+NL;
|
|
}
|
|
if (!_ReqTeamSize.empty())
|
|
{
|
|
ret += "req_team_size : "+_ReqTeamSize+NL;
|
|
}
|
|
if (!_ReqBrick.empty())
|
|
{
|
|
ret += "req_brick : ";
|
|
for (uint i=0; i<_ReqBrick.size(); ++i)
|
|
{
|
|
ret += _ReqBrick[i];
|
|
if(i < _ReqBrick.size()-1)
|
|
ret +="; ";
|
|
ret += NL;
|
|
}
|
|
}
|
|
if (!_ReqCharacterAge.empty())
|
|
{
|
|
ret += "req_character_age : "+_ReqCharacterAge+NL;
|
|
}
|
|
if (!_ReqMaxPlayerID.empty())
|
|
{
|
|
ret += "req_max_player_id : "+_ReqMaxPlayerID+NL;
|
|
}
|
|
if (!_ReqSeason.empty())
|
|
{
|
|
ret += "req_season : "+_ReqSeason+NL;
|
|
}
|
|
if (!_ReqEncyclo.empty())
|
|
{
|
|
ret += "req_encyclo_thema : " + _ReqEncyclo + NL;
|
|
}
|
|
if (!_ReqEncycloNeg.empty())
|
|
{
|
|
ret += "req_encyclo_thema_neg : " + _ReqEncycloNeg + NL;
|
|
}
|
|
if (!_ReqEventFaction.empty())
|
|
{
|
|
ret += "req_event_faction : " + _ReqEventFaction + NL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
string CMissionData::generateMissionScript(const std::string &primFileName)
|
|
{
|
|
_JumpPoints.clear();
|
|
// first, gather jump point list
|
|
for (uint i=0; i<_Steps.size(); ++i)
|
|
{
|
|
set<TJumpInfo> temp;
|
|
_Steps[i]->fillStepJump(*this, temp);
|
|
|
|
// remove any jump to the next step (normal flow)
|
|
if (i < _Steps.size()-1)
|
|
{
|
|
set<TJumpInfo>::iterator first(temp.begin()), last(temp.end());
|
|
for (; first != last; )
|
|
{
|
|
const TJumpInfo &ji = *first;
|
|
|
|
if (ji.StepName == _Steps[i+1]->getStepName() && ji.Discardable)
|
|
{
|
|
temp.erase(first);
|
|
first = temp.begin();
|
|
}
|
|
else
|
|
++first;
|
|
}
|
|
}
|
|
|
|
_JumpPoints.insert(temp.begin(), temp.end());
|
|
}
|
|
// generate the script
|
|
string script;
|
|
// generate mission header
|
|
script += "# script generated from '"+CFile::getFilename(primFileName)+"'"+NL+NL;
|
|
script += "#mission tags and pre-requisites"+NL;
|
|
if (_MonoInstance)
|
|
script += "mono"+NL;
|
|
if (_RunOnce)
|
|
script += "once"+NL;
|
|
if (_Replayable)
|
|
script += "replayable"+NL;
|
|
if (_Solo)
|
|
script += "solo"+NL;
|
|
if (_Guild)
|
|
script += "guild"+NL;
|
|
if (_NotInJournal)
|
|
script += "no_list"+NL;
|
|
if (_AutoRemoveFromJournal)
|
|
script += "auto_remove"+NL;
|
|
if (!_MissionCategory.empty())
|
|
script += "mission_category : "+_MissionCategory+NL;
|
|
if (_PlayerReplayTimer != 0)
|
|
script += "player_replay_timer : "+toString("%u", _PlayerReplayTimer)+NL;
|
|
if (_GlobalReplayTimer != 0)
|
|
script += "global_replay_timer : "+toString("%u", _GlobalReplayTimer)+NL;
|
|
if (_NotProposed)
|
|
script += "not_proposed"+NL;
|
|
if (_MissionAuto)
|
|
script += string("auto : ")+_MissionAutoMenu.genScript(*this)+NL;
|
|
if (_NonAbandonnable)
|
|
script += "non_abandonnable"+NL;
|
|
if (!_MissionIcon.empty())
|
|
script += "mission_icon : "+_MissionIcon+NL;
|
|
if (_NeedValidation)
|
|
script += "need_validation"+NL;
|
|
if (_FailIfInventoryIsFull)
|
|
script += "fail_if_inventory_is_full"+NL;
|
|
|
|
if (!_ParentMissions.empty())
|
|
{
|
|
set<string>::iterator first(_ParentMissions.begin()), last(_ParentMissions.end());
|
|
for (; first != last; ++first)
|
|
{
|
|
script += "parent : "+ *first+NL;
|
|
}
|
|
}
|
|
|
|
script += NL+"#Variables declaration"+NL;
|
|
|
|
// declare all the variables
|
|
{
|
|
std::vector<IVar*>::iterator first(_VariablesOrder.begin()), last(_VariablesOrder.end());
|
|
for (; first != last; ++first)
|
|
{
|
|
script += (*first)->genDecl(*this);
|
|
}
|
|
}
|
|
|
|
script += NL+"#pre-requisites"+NL;
|
|
script += genPreRequisites();
|
|
|
|
script += NL+"#script"+NL;
|
|
// generate mission title and desc
|
|
script += "mission_title : "+_MissionTitle.genScript(*this)+NL;
|
|
script += "mission_desc : "+_MissionDescription.genScript(*this)+NL;
|
|
|
|
// generate steps scripts
|
|
for (uint i=0; i<_Steps.size(); ++i)
|
|
{
|
|
script += "# "+_Steps[i]->getStepName()+NL;
|
|
if (_JumpPoints.find(_Steps[i]->getStepName()) != _JumpPoints.end()
|
|
&& !_Steps[i]->isAJump())
|
|
{
|
|
// insert a jump point
|
|
script += "jump_point : " + _Steps[i]->getStepName() + NL;
|
|
}
|
|
|
|
script += _Steps[i]->genCode(*this);
|
|
//if (_Steps[i]->EndOfBranch && !_Steps[i]->isAJump())
|
|
// script += "end"+NL;
|
|
}
|
|
|
|
return script;
|
|
}
|
|
|
|
string CMissionData::generatePhraseFile()
|
|
{
|
|
string ret;
|
|
// generate header phrase
|
|
ret = _MissionTitle.genPhrase();
|
|
ret += _MissionDescription.genPhrase();
|
|
ret += _MissionAutoMenu.genPhrase();
|
|
|
|
// generate var phrase
|
|
for (uint i=0; i<_VariablesOrder.size(); ++i)
|
|
{
|
|
ret += _VariablesOrder[i]->genPhrase();
|
|
}
|
|
|
|
// generate step phrase
|
|
for (uint i=0; i<_Steps.size(); ++i)
|
|
{
|
|
ret += _Steps[i]->genPhrase();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
string CMissionData::generateDotScript()
|
|
{
|
|
string ret = "digraph " + _MissionName + NL;
|
|
ret += "{" + NL;
|
|
|
|
// set default shape to 'record'
|
|
ret += "node [shape=record]"+NL;
|
|
|
|
ret += "\t__start__ [shape=\"point\", peripheries=2, label=\"\"]"+NL;
|
|
|
|
// 1st pass, generate node for each step
|
|
for (uint i=0; i<_Steps.size(); ++i)
|
|
{
|
|
if (!_Steps[i]->isEnd() && !_Steps[i]->isAJump())
|
|
{
|
|
ret += "\t"+_Steps[i]->getStepName();
|
|
ret += " [URL=\""+buildPrimPath(_Steps[i]->getPrimitive())+"\"]"+NL;
|
|
}
|
|
}
|
|
|
|
ret += "\t__end__ [shape=\"point\"]"+NL;
|
|
|
|
// activate red color for shapes that are created after this points
|
|
ret += "node [color=red]"+NL;
|
|
|
|
// 2nd pass, generate link between steps
|
|
for (uint i=0; i<_Steps.size(); ++i)
|
|
{
|
|
if (_Steps[i]->isAJump())
|
|
continue;
|
|
|
|
if (i == 0)
|
|
{
|
|
ret += "\t__start__ -> " + _Steps[i]->getStepName() + NL;
|
|
}
|
|
set<TJumpInfo> jumps;
|
|
_Steps[i]->fillStepJump(*this, jumps);
|
|
// there is a link there
|
|
while (!jumps.empty())
|
|
{
|
|
const TJumpInfo &ji = *(jumps.begin());
|
|
if (_StepsByNames.find(ji.StepName) != _StepsByNames.end()
|
|
&& _StepsByNames[ji.StepName]->isAJump())
|
|
{
|
|
// this step is a jump, skip to link to the jump destination
|
|
IStep *jumpStep = _StepsByNames[ji.StepName];
|
|
set<TJumpInfo> jumpJump;
|
|
jumpStep->fillStepJump(*this, jumpJump);
|
|
if (jumpJump.size() != 1)
|
|
{
|
|
string str = toString("Step jump contains %u jumps destination instead of 1", jumpJump.size());
|
|
throw EParseException(jumpStep->getPrimitive(), str.c_str());
|
|
}
|
|
|
|
ret += "\t"+_Steps[i]->getStepName() + " -> " + jumpJump.begin()->StepName+" [label=\""+ji.JumpName+"\"]" + NL;
|
|
}
|
|
else
|
|
{
|
|
ret += "\t"+_Steps[i]->getStepName() + " -> " + ji.StepName+" [label=\""+jumps.begin()->JumpName+"\"]" + NL;
|
|
}
|
|
jumps.erase(jumps.begin());
|
|
}
|
|
|
|
}
|
|
|
|
ret += "}" + NL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void CMissionData::parseMissionHeader(NLLIGO::IPrimitive *prim)
|
|
{
|
|
// _MissionName = getProperty(prim, "name", false, false);
|
|
// if( _MissionName.find(' ') != string::npos)
|
|
// {
|
|
// throw EParseException(prim, toString("Mission name '%s' must not contains space", _MissionName.c_str()).c_str());
|
|
// }
|
|
_GiverPrimitive = getProperty(prim,"giver_primitive", true, false);
|
|
_MissionGiver = getProperty(prim, "mission_giver", true, false);
|
|
|
|
// _Alias = getProperty(prim, "alias", false, false);
|
|
|
|
// If the mission is under a npc_bot node, then the giver is directly taken
|
|
// from the npc name
|
|
if (prim->getParent())
|
|
{
|
|
if (getProperty(prim->getParent(), "class", false, false) == "npc_bot")
|
|
{
|
|
_MissionGiver = getProperty(prim->getParent(), "name", false, false);
|
|
}
|
|
}
|
|
|
|
vector<string> vs;
|
|
_MissionTitleRaw = getPropertyArray(prim, "mission_title", false, false);
|
|
// _MissionTitle.init(*this, prim, vs);
|
|
_MissionDescriptionRaw = getPropertyArray(prim, "mission_description", false, false);
|
|
// _MissionDescription.init(*this, prim, vs);
|
|
_MonoInstance = toLower(getProperty(prim, "mono_instance", true, false)) == "true";
|
|
_RunOnce = toLower(getProperty(prim, "run_only_once", true, false)) == "true";
|
|
_Replayable = toLower(getProperty(prim, "replayable", true, false)) == "true";
|
|
|
|
_NeedValidation = toLower(getProperty(prim, "need_validation", true, false)) == "true";
|
|
|
|
_MissionAutoMenuRaw = getPropertyArray(prim, "phrase_auto_menu", false, false);
|
|
|
|
// audience setting
|
|
string s = getProperty(prim, "audience", false, false);
|
|
if (s == "solo")
|
|
_Solo = true;
|
|
else if (s == "guild")
|
|
_Guild = true;
|
|
|
|
_NotInJournal = NLMISC::toLower(getProperty(prim, "not_in_journal", false, false)) == "true";
|
|
_AutoRemoveFromJournal = NLMISC::toLower(getProperty(prim, "auto_remove_from_journal", false, false)) == "true";
|
|
_MissionCategory = getProperty(prim, "mission_category", false, false);
|
|
NLMISC::fromString(getProperty(prim, "player_replay_timer", true, false), _PlayerReplayTimer);
|
|
NLMISC::fromString(getProperty(prim, "global_replay_timer", true, false), _GlobalReplayTimer);
|
|
_NotProposed = NLMISC::toLower(getProperty(prim, "not_proposed", false, false)) == "true";
|
|
_MissionAuto = NLMISC::toLower(getProperty(prim, "automatic", false, false)) == "true";
|
|
_NonAbandonnable = NLMISC::toLower(getProperty(prim, "non_abandonnable", false, false)) == "true";
|
|
_FailIfInventoryIsFull = NLMISC::toLower(getProperty(prim, "fail_if_inventory_is_full", false, false)) == "true";
|
|
_MissionIcon = getProperty(prim, "mission_icon", false, false);
|
|
|
|
if (_MissionAuto)
|
|
{
|
|
if (_MissionAutoMenuRaw.empty())
|
|
{
|
|
string error = toString("Mission is flagged automatic, but no phrase_auto_menu defined !");
|
|
throw EParseException(prim, error.c_str());
|
|
}
|
|
}
|
|
|
|
vs = getPropertyArray(prim, "parent_missions", true, false);
|
|
_ParentMissions.insert(vs.begin(), vs.end());
|
|
|
|
}
|
|
|
|
void CMissionData::parsePrerequisites(NLLIGO::IPrimitive *prim)
|
|
{
|
|
// skills
|
|
vector<string> vs;
|
|
vs = getPropertyArray(prim, "require_skill/min_level/max_level", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 3)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_skill array. Need 3, found %u", i, parts.size()).c_str());
|
|
}
|
|
TReqSkill rs;
|
|
rs.Skill = parts[0];
|
|
rs.MinLevel = parts[1];
|
|
rs.MaxLevel = parts[2];
|
|
|
|
_ReqSkills.push_back(rs);
|
|
|
|
}
|
|
}
|
|
// Mission done
|
|
vs = getPropertyArray(prim, "require_mission_done", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_mission_done array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqMissionDone.push_back(parts[0]);
|
|
}
|
|
}
|
|
// Mission not done
|
|
vs = getPropertyArray(prim, "require_mission_not_done", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_mission_not_done array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqMissionNotDone.push_back(parts[0]);
|
|
}
|
|
}
|
|
// Mission running
|
|
vs = getPropertyArray(prim, "require_mission_running", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_mission_running array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqMissionRunning.push_back(parts[0]);
|
|
}
|
|
}
|
|
// Mission not running
|
|
vs = getPropertyArray(prim, "require_mission_not_running", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_mission_not_running array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqMissionNotRunning.push_back(parts[0]);
|
|
}
|
|
}
|
|
// wearing item
|
|
vs = getPropertyArray(prim, "require_wearing_item", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_wearing_item array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqWearItem.push_back(parts[0]);
|
|
}
|
|
}
|
|
// own item
|
|
vs = getPropertyArray(prim, "require_own_item", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_own_item array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqOwnItem.push_back(parts[0]);
|
|
}
|
|
}
|
|
// title
|
|
_ReqTitle = getProperty(prim, "require_title", true, false);
|
|
// fame
|
|
vs = getPropertyArray(prim, "require_faction/fame", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 2)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_faction/fame array. Need 2, found %u", i, parts.size()).c_str());
|
|
}
|
|
TReqFame rf;
|
|
rf.Faction = parts[0];
|
|
rf.Fame = parts[1];
|
|
|
|
_ReqFames.push_back(rf);
|
|
}
|
|
}
|
|
// guild
|
|
if (getProperty(prim, "require_guild_membership", true, false) == "true")
|
|
_ReqGuild = true;
|
|
else
|
|
_ReqGuild = false;
|
|
// grade
|
|
_ReqGrade = getProperty(prim, "require_guild_grade", true, false);
|
|
// team size
|
|
_ReqTeamSize = getProperty(prim, "require_team_size", true, false);
|
|
// character minimum age
|
|
_ReqCharacterAge = getProperty(prim, "require_character_age", true, false);
|
|
// maximum player ID
|
|
_ReqMaxPlayerID = getProperty(prim, "require_max_player_id", true, false);
|
|
// brick
|
|
vs = getPropertyArray(prim, "require_brick_knowledge", true, false);
|
|
for (uint i=0; i<vs.size(); ++i)
|
|
{
|
|
if (!vs[i].empty())
|
|
{
|
|
vector<string> parts;
|
|
strtokquote(vs[i], parts);
|
|
if (parts.size() != 1)
|
|
{
|
|
throw EParseException(prim, toString("Invalide argument count in line %u of require_brick_knowledge array. Need 1, found %u", i, parts.size()).c_str());
|
|
}
|
|
_ReqBrick.push_back(parts[0]);
|
|
}
|
|
}
|
|
// season
|
|
_ReqSeason = getProperty(prim, "require_season", true, false);
|
|
// encyclopedia
|
|
_ReqEncyclo = getProperty(prim, "require_encyclo_thema", true, false);
|
|
_ReqEncycloNeg = getProperty(prim, "require_encyclo_thema_neg", true, false);
|
|
|
|
if ((!_ReqEncyclo.empty() && !_ReqEncycloNeg.empty())
|
|
|| (!_ReqEncycloNeg.empty() && !_ReqEncyclo.empty()))
|
|
{
|
|
string err = toString("You can't mix positive and negative encyclopedy requirement");
|
|
throw EParseException(prim, err.c_str());
|
|
}
|
|
// event faction
|
|
_ReqEventFaction = getProperty(prim, "require_event_faction", true, false);
|
|
}
|
|
|
|
std::string CMissionData::replaceVar(NLLIGO::IPrimitive *prim, const std::string &str)
|
|
{
|
|
string::size_type pos = 0;
|
|
string::size_type pos2 = 0;
|
|
string ret;
|
|
|
|
while (pos < str.size())
|
|
{
|
|
if (str[pos] != '$')
|
|
{
|
|
ret += str[pos++];
|
|
}
|
|
else if (pos+1 < str.size() && str[pos+1] == '$')
|
|
{
|
|
// check that this $ is not escaped
|
|
ret += '$';
|
|
pos+=2;
|
|
}
|
|
else
|
|
{
|
|
// ok, this is not an escaped $
|
|
CSString varName;
|
|
// skip the initial '$'
|
|
pos++;
|
|
// while (str[pos] != ' ' && str[pos] != '\t' && str[pos] != '\n' && str[pos] != '\r')
|
|
while (pos < str.size() && str[pos] != '$')
|
|
varName += str[pos++];
|
|
|
|
if (pos >= str.size())
|
|
{
|
|
string err = toString("Error while parsing variable in '%s', missing closing '$'", str.c_str());
|
|
throw EParseException (NULL, err.c_str());
|
|
}
|
|
|
|
// skip the final '$'
|
|
pos++;
|
|
|
|
// split the var name and subpart
|
|
vector<string> varParts;
|
|
explode(string(varName), string("@"), varParts, true);
|
|
|
|
if (varParts.empty() || varParts.size() > 2)
|
|
{
|
|
throw EParseException(prim, toString("Error parsing varName '%s' in string '%s'", varName.c_str(), str.c_str()).c_str());
|
|
}
|
|
|
|
if (_Variables.find(varParts.front()) == _Variables.end())
|
|
{
|
|
string err = toString("Unknown variable '%s' in string '%s'", varParts.front().c_str(), str.c_str());
|
|
throw EParseException (prim, err.c_str());
|
|
}
|
|
|
|
IVar *var = _Variables[varParts[0]];
|
|
|
|
if (varParts.size() == 1)
|
|
ret += var->evalVar("");
|
|
else
|
|
ret += var->evalVar(varParts[1]);
|
|
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> CMissionData::replaceVar(NLLIGO::IPrimitive *prim, const std::vector<std::string> &strs)
|
|
{
|
|
vector<string> ret;
|
|
|
|
for (uint i=0; i<strs.size(); ++i)
|
|
{
|
|
ret.push_back(replaceVar(prim, strs[i]));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string CMissionData::getProperty(NLLIGO::IPrimitive *prim, const std::string &propertyName, bool replaceVar, bool canFail)
|
|
{
|
|
string ret;
|
|
string *s;
|
|
if (!prim->getPropertyByName(propertyName.c_str(), s))
|
|
{
|
|
if (!canFail)
|
|
{
|
|
string err = toString("Can't find property '%s'", propertyName.c_str());
|
|
throw EParseException (prim, err.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = *s;
|
|
}
|
|
|
|
if (replaceVar)
|
|
{
|
|
ret = this->replaceVar(prim, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> CMissionData::getPropertyArray(NLLIGO::IPrimitive *prim, const std::string &propertyName, bool replaceVar, bool canFail)
|
|
{
|
|
vector<string> ret;
|
|
vector<string> *vs;
|
|
if (!prim->getPropertyByName(propertyName.c_str(), vs))
|
|
{
|
|
if (!canFail)
|
|
{
|
|
string err = toString("Can't find property '%s'", propertyName.c_str());
|
|
throw EParseException (prim, err.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = *vs;
|
|
}
|
|
|
|
if (replaceVar)
|
|
{
|
|
ret = this->replaceVar(prim, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool CMissionData::isThereAJumpTo(const std::string &stepName)
|
|
{
|
|
if (_JumpPoints.find(stepName) != _JumpPoints.end())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void TCompilerVarName::init(const std::string &defaultName, STRING_MANAGER::TParamType type, CMissionData &md, NLLIGO::IPrimitive *prim, const std::string propName)
|
|
{
|
|
_DefaultName = defaultName;
|
|
_ParamType = type;
|
|
|
|
_VarName = md.getProperty(prim, propName, false, false);
|
|
// remove the variable tag if any
|
|
untagVar(_VarName);
|
|
|
|
_VarValue = md.getProperty(prim, propName, true, false);
|
|
}
|
|
|
|
void TCompilerVarName::initWithText(const std::string &defaultName, STRING_MANAGER::TParamType type, CMissionData &md, NLLIGO::IPrimitive *prim, const std::string &text)
|
|
{
|
|
_DefaultName = defaultName;
|
|
_ParamType = type;
|
|
|
|
_VarName = text;
|
|
// remove the variable tag if any
|
|
untagVar(_VarName);
|
|
|
|
_VarValue = md.replaceVar(prim, text);
|
|
}
|
|
|
|
|
|
CPhrase::TParamInfo TCompilerVarName::getParamInfo() const
|
|
{
|
|
if (_VarName.empty())
|
|
return CPhrase::TParamInfo(_DefaultName, _ParamType);
|
|
else
|
|
return CPhrase::TParamInfo(_VarName, _ParamType);
|
|
}
|
|
|
|
|
|
bool TCompilerVarName::empty() const
|
|
{
|
|
return _VarValue.empty();
|
|
}
|
|
|
|
TCompilerVarName::operator const std::string () const
|
|
{
|
|
return _VarValue;
|
|
}
|
|
|
|
TCompilerVarName::operator CPhrase::TParamInfo() const
|
|
{
|
|
return getParamInfo();
|
|
}
|
|
|
|
|
|
std::string operator+(const TCompilerVarName& left, const std::string & right) { return left._VarValue + right;}
|
|
|
|
std::string operator+(const std::string & left, const TCompilerVarName& right) { return left + right._VarValue;}
|
|
|
|
std::vector<TCompilerVarName> TCompilerVarName::getPropertyArrayWithText(const std::string &defaultName, STRING_MANAGER::TParamType type, CMissionData &md, NLLIGO::IPrimitive *prim, const std::string & arrayProperyName)
|
|
{
|
|
std::vector<TCompilerVarName> compilerParams;
|
|
|
|
std::vector<std::string> values = md.getPropertyArray(prim, arrayProperyName,false, false);
|
|
uint first = 0;
|
|
uint last = (uint)values.size();
|
|
compilerParams.resize(last);
|
|
for ( ; first != last; ++first)
|
|
{
|
|
compilerParams[first].initWithText( toString("%s%d", defaultName.c_str(), first+1) , type, md, prim, values[first]);
|
|
}
|
|
|
|
return compilerParams;
|
|
}
|
|
|
|
std::vector<TCompilerVarName> TCompilerVarName::getPropertyArrayWithTextStaticDefaultName(const std::string &defaultName, STRING_MANAGER::TParamType type, CMissionData &md, NLLIGO::IPrimitive *prim, const std::string & arrayProperyName)
|
|
{
|
|
std::vector<TCompilerVarName> compilerParams;
|
|
std::vector<std::string> values = md.getPropertyArray(prim, arrayProperyName,false, false);
|
|
uint first = 0;
|
|
uint last = (uint)values.size();
|
|
compilerParams.resize(last);
|
|
for ( ; first != last; ++first)
|
|
{
|
|
compilerParams[first].initWithText( defaultName, type, md, prim, values[first]);
|
|
}
|
|
return compilerParams;
|
|
}
|
|
|