mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-11-27 09:36:15 +00:00
2255 lines
61 KiB
C++
2255 lines
61 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"
|
||
|
||
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 char * fs[] ={"f", "f1", "f2", "f3"};
|
||
static 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("La phrase ") + identifier + " qui contient une balise " + goodMarkup + " n<>cessite aussi les balises " + badMarkup + " m<>me vide.";
|
||
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 = getPrimitiveChild(mission, TPrimitiveClassPredicate("variables"));
|
||
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 = getPrimitiveChild(mission, TPrimitiveClassPredicate("pre_requisite"));
|
||
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;
|
||
|
||
scriptsSet.buildSet(rootPrim, TPrimitiveClassPredicate("mission_tree"), 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 (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;
|
||
filter.buildSet(primDoc->RootNode, TPrimitiveClassPredicate("mission"), 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;
|
||
filter.buildSet(primDoc->RootNode, TPrimitiveClassAndNamePredicate("npc_bot", mission.getGiverName()), 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.c_str(), src.c_str());
|
||
|
||
// 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.c_str(), src.c_str());
|
||
}
|
||
else
|
||
{
|
||
// primitive file : copy to server
|
||
dst = CPath::standardizePath(serverPathPrim) + string(src, n, src.size());
|
||
NLMISC::CFile::copyFile(dst.c_str(), src.c_str());
|
||
}
|
||
}
|
||
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;
|
||
filterPrimitiveChilds(steps, TPrimitivePropertyPredicate("step_tag", "true"), 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 (!_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 = strlwr(getProperty(prim, "mono_instance", true, false)) == "true";
|
||
_RunOnce = strlwr(getProperty(prim, "run_only_once", true, false)) == "true";
|
||
_Replayable = strlwr(getProperty(prim, "replayable", true, false)) == "true";
|
||
|
||
_NeedValidation = strlwr(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 = strlwr(getProperty(prim, "not_in_journal", false, false)) == "true";
|
||
_AutoRemoveFromJournal = strlwr(getProperty(prim, "auto_remove_from_journal", false, false)) == "true";
|
||
_MissionCategory = getProperty(prim, "mission_category", false, false);
|
||
_PlayerReplayTimer = atoi(getProperty(prim, "player_replay_timer", true, false).c_str());
|
||
_GlobalReplayTimer = atoi(getProperty(prim, "global_replay_timer", true, false).c_str());
|
||
_NotProposed = strlwr(getProperty(prim, "not_proposed", false, false)) == "true";
|
||
_MissionAuto = strlwr(getProperty(prim, "automatic", false, false)) == "true";
|
||
_NonAbandonnable = strlwr(getProperty(prim, "non_abandonnable", false, false)) == "true";
|
||
_FailIfInventoryIsFull = strlwr(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);
|
||
// 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;
|
||
}
|
||
|