1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2011 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "mission_compiler.h"
23 #include "nel/misc/i18n.h"
24 #include "nel/misc/common.h"
25 #include "nel/ligo/primitive_utils.h"
28 using namespace NLMISC
;
29 using namespace NLLIGO
;
32 // hack to get access to string manager item enumeration to string without including
33 // almost all of the Ryzom server side project
34 namespace STRING_MANAGER
36 NL_BEGIN_STRING_CONVERSION_TABLE (TParamType
)
37 NL_STRING_CONVERSION_TABLE_ENTRY( item
)
38 NL_STRING_CONVERSION_TABLE_ENTRY( place
)
39 NL_STRING_CONVERSION_TABLE_ENTRY( creature
)
40 NL_STRING_CONVERSION_TABLE_ENTRY( skill
)
41 NL_STRING_CONVERSION_TABLE_ENTRY( role
)
42 NL_STRING_CONVERSION_TABLE_ENTRY( ecosystem
)
43 NL_STRING_CONVERSION_TABLE_ENTRY( race
)
44 NL_STRING_CONVERSION_TABLE_ENTRY( sbrick
)
45 NL_STRING_CONVERSION_TABLE_ENTRY( faction
)
46 NL_STRING_CONVERSION_TABLE_ENTRY( guild
)
47 NL_STRING_CONVERSION_TABLE_ENTRY( player
)
48 NL_STRING_CONVERSION_TABLE_ENTRY( bot
)
50 // NL_STRING_CONVERSION_TABLE_ENTRY( integer )
51 NL_STRING_CONVERSION_TABLE_ENTRY( time
)
52 NL_STRING_CONVERSION_TABLE_ENTRY( money
)
53 NL_STRING_CONVERSION_TABLE_ENTRY( compass
)
54 NL_STRING_CONVERSION_TABLE_ENTRY( string_id
)
55 NL_STRING_CONVERSION_TABLE_ENTRY( dyn_string_id
)
56 NL_STRING_CONVERSION_TABLE_ENTRY( self
)
57 NL_STRING_CONVERSION_TABLE_ENTRY( creature_model
)
58 NL_STRING_CONVERSION_TABLE_ENTRY( entity
)
59 NL_STRING_CONVERSION_TABLE_ENTRY( body_part
)
60 NL_STRING_CONVERSION_TABLE_ENTRY( score
)
61 NL_STRING_CONVERSION_TABLE_ENTRY( sphrase
)
62 NL_STRING_CONVERSION_TABLE_ENTRY( characteristic
)
63 NL_STRING_CONVERSION_TABLE_ENTRY( damage_type
)
64 NL_STRING_CONVERSION_TABLE_ENTRY( bot_name
)
65 NL_STRING_CONVERSION_TABLE_ENTRY( power_type
)
66 NL_STRING_CONVERSION_TABLE_ENTRY( literal
)
67 NL_END_STRING_CONVERSION_TABLE(TParamType
, ParamTypeConversion
, NB_PARAM_TYPES
)
69 //-----------------------------------------------
71 //-----------------------------------------------
72 TParamType
stringToParamType( const std::string
& str
)
74 return ParamTypeConversion
.fromString( str
);
77 //-----------------------------------------------
79 //-----------------------------------------------
80 const std::string
& paramTypeToString( TParamType type
)
82 return ParamTypeConversion
.toString( type
);
86 // utility to 'tabulate' the lines in a string
87 void tabulateLine(std::string
&text
, uint nbTabs
)
91 string::size_type pos
= 0;
94 for (uint i
=0; i
<nbTabs
; ++i
)
100 // add a tab at each new line
101 while ((pos
= text
.find('\n', pos
)) != string::npos
)
103 if (pos
< text
.size()-1 && text
[pos
+1] == '\r')
105 // add after the '\r' char
108 if (pos
< text
.size()-1)
109 text
= text
.substr(0, pos
+1) + tabs
+ text
.substr(pos
+1);
115 class GenderExtractor
120 GenderExtractor(const std::string
& literal
, const std::string
& identifier
, unsigned int level
=0);
122 std::string
operator()(unsigned int i
) const;
124 unsigned int size() const;
129 bool extractMarkup(const std::string
& literal
, const std::string
& markup
, std::string
&before
, std::string
&inside
, std::string
& after
);
131 bool parseMarkup(const std::string
& literal
, const std::string
& markup
, std::string
& newPhrase
, bool include
= false );
133 std::string
getPhrase(unsigned int i
) const;
135 std::string
getExtension(unsigned int i
) const;
137 std::string
getCondition(unsigned int i
) const;
139 std::string
getEntity(unsigned int i
) const;
141 std::string
getIdentifier(unsigned int i
) const;
148 std::string _EntityName
;
151 std::string _Identifier
;
153 GenderExtractor
* _Female
;
154 GenderExtractor
* _Male
;
159 GenderExtractor::~GenderExtractor()
164 std::string
GenderExtractor::getIdentifier(unsigned int i
) const
166 return _Identifier
+ getExtension(i
);
169 std::string
GenderExtractor::operator()(unsigned int i
) const
171 std::string
ret("\t");
172 std::string condition
= getCondition(i
);
174 ret
+= condition
.empty() ? "":std::string("( ") + condition
+ " )" + NL
+ "\t\t";
175 ret
+= getIdentifier(i
) + "\t[" + getPhrase(i
) + "]" + NL
;
180 GenderExtractor::GenderExtractor(const std::string
& literal
, const std::string
& identifier
, unsigned int level
)
184 static const char * es
[] ={"e", "e1", "e2", "e3"};
185 static const char * fs
[] ={"f", "f1", "f2", "f3"};
186 static const char * hs
[] ={"h", "h1", "h2", "h3"};
188 const char * e
= es
[level
];
189 const char * f
= fs
[level
];
190 const char * h
= hs
[level
];
192 _Identifier
= toLowerAscii(identifier
);
194 std::string newPhrase
;
198 std::string femaleText
;
199 std::string maleText
;
201 _Entity
= extractMarkup(literal
, e
, before
, _EntityName
, after
);
202 if (_EntityName
.size() > 2)
204 if (_EntityName
[0] == '$' && _EntityName
[_EntityName
.size() - 1] == '$')
206 _EntityName
= _EntityName
.substr(1, _EntityName
.size() - 2);
210 std::string newLiteral
= before
+ after
;
212 bool isFemale
= parseMarkup(newLiteral
,f
,newPhrase
, true);
215 parseMarkup(newPhrase
,h
,newPhrase
, false);
216 femaleText
= newPhrase
;
219 bool isMale
= parseMarkup(newLiteral
, h
, newPhrase
, true);
223 parseMarkup(newPhrase
, f
, newPhrase
, false);
224 maleText
= newPhrase
;
227 if (isMale
!= isFemale
)
229 std::string goodMarkup
= isMale
? std::string("") +"<" + h
+ "></" + h
+ ">" : std::string("")+"<"+f
+"></"+f
+">";
230 std::string badMarkup
= isFemale
? std::string("") +"<" + h
+ "></" + h
+ ">" : std::string("")+"<"+f
+"></"+f
+">";
231 std::string exceptionText
= std::string("Expression ") + identifier
+ " that contains a tag " + goodMarkup
+ " needs also tags " + badMarkup
+ " even empty.";
232 throw EParseException(0, exceptionText
.c_str());
235 if (!isMale
&& !isFemale
)
244 if (!_Entity
) { _EntityName
= "self"; }
245 _Female
= new GenderExtractor(femaleText
, identifier
, level
+1);
246 _Male
= new GenderExtractor(maleText
, identifier
, level
+1);
253 bool GenderExtractor::extractMarkup(const std::string
& literal
, const std::string
& markup
, std::string
&before
, std::string
&inside
, std::string
& after
)
255 std::string::size_type posBegin
;
256 std::string::size_type posEnd
;
257 std::string::size_type posInside
;
259 std::string beginMarkup
= std::string("<") + markup
+ std::string(">");
260 std::string endMarkup
= std::string("</") + markup
+ std::string(">");
261 posBegin
= literal
.find(beginMarkup
);
262 if ( posBegin
!= std::string::npos
)
264 posEnd
= literal
.find(endMarkup
, posBegin
+ beginMarkup
.size());
265 if (posEnd
!= std::string::npos
)
267 before
= literal
.substr(0, posBegin
);
268 posInside
= posBegin
+ beginMarkup
.size();
269 inside
= literal
.substr(posInside
, posEnd
- posInside
);
270 after
= literal
.substr(posEnd
+endMarkup
.size());
278 bool GenderExtractor::parseMarkup(const std::string
& literal
, const std::string
& markup
, std::string
& newPhrase
, bool include
)
282 bool changed
= false;
283 std::string oldPhrase
= literal
;
291 markupExist
= extractMarkup(oldPhrase
, markup
, before
, inside
, after
);
293 if (include
){ newPhrase
+= inside
; }
295 if (markupExist
){ changed
= true; }
297 } while(markupExist
);
299 newPhrase
+= oldPhrase
;
303 std::string
GenderExtractor::getPhrase(unsigned int i
) const
305 if ( i
%2 == 0) { return _Male
? _Male
->getPhrase(i
/2) : _Text
; }
306 if ( i
%2 == 1) { nlassert(_Female
); return _Female
->getPhrase(i
/2);}
311 std::string
GenderExtractor::getExtension(unsigned int i
) const
313 if ( i
%2 == 0) { return _Male
? std::string("_m") + _Male
->getExtension(i
/2) : ""; }
314 if ( i
%2 == 1) { nlassert(_Female
); return std::string("_f") + _Female
->getExtension(i
/2);}
319 std::string
GenderExtractor::getCondition(unsigned int i
) const
322 //if ( i%2 == 0) { return _Male ? std::string("\t(") + _Male->getExtension(i/2) : "\t"; }
323 //if ( i%2 == 1) { nlassert(_Female); return std::string("_f") + _Female->getExtension(i/2);}
329 std::string next
= _Male
->getCondition(i
/2);
330 std::string current
= _EntityName
+ ".gender = male";
331 return next
.size() ? current
+ " & " + next
: current
;
342 std::string next
= _Female
->getCondition(i
/2);
343 std::string current
= _EntityName
+ ".gender = female";
344 return next
.size() ? current
+ " & " + next
: current
;
352 unsigned int GenderExtractor::size() const
354 return _Male
? _Male
->size() + _Female
->size(): 1;
358 string
CPhrase::genPhrase()
361 if (!_PhraseLiterals
.empty())
363 for (uint p
=0; p
<_PhraseLiterals
.size(); ++p
)
365 string identifier
= _PhraseId
;
367 identifier
+= toString("_%u", p
+1);
369 GenderExtractor
gender(_PhraseLiterals
[p
], identifier
, 0);
371 ret
+= identifier
+ " (";
372 // generate default param list
373 if (_DefaultParams
.size() > p
)
375 for (uint i
=0; i
<_DefaultParams
[p
].size(); ++i
)
377 ret
+= STRING_MANAGER::paramTypeToString(_DefaultParams
[p
][i
].ParamType
) + " "+_DefaultParams
[p
][i
].ParamName
;
378 if (i
!= _DefaultParams
[p
].size()-1 || !_AdditionalParams
.empty())
382 // generate additional param list
383 for (uint i
=0; i
<_AdditionalParams
.size(); ++i
)
385 ret
+= STRING_MANAGER::paramTypeToString(_AdditionalParams
[i
].ParamType
) + " "+_AdditionalParams
[i
].ParamName
;
386 if (i
!= _AdditionalParams
.size()-1)
392 for (unsigned int i
= 0; i
< gender
.size(); ++i
)
397 ret
+= "}" + NL
+ NL
;
400 nlinfo("genphrase: %s", ret
.c_str());
407 bool CMissionCompiler::generateDotScript(NLLIGO::IPrimitive
*missionPrim
, std::string
&dotScript
, std::string
&log
)
409 //assume that the mission is compiled in the last compiled mission slot
412 if (compileMission(missionPrim
, string()))
414 dotScript
= _CompiledMission
.back()->generateDotScript();
422 catch(const EParseException
& e
)
430 bool CMissionCompiler::parseGlobalMissionData(IPrimitive *mission, CMissionData &md)
434 if (!mission->getPropertyByName("name", s) || s->empty())
435 throw EParseException(mission, "missing mission name !");
436 md.setMissionName(*s);
438 // giver primitive file
439 if (!mission->getPropertyByName("giver_primitive", s) || s->empty())
440 throw EParseException(mission, "missing giver primitive !");
441 md.setGiverPrimitive(*s);
444 if (!mission->getPropertyByName("giver_primitive", s) || s->empty())
445 throw EParseException(mission, "missing giver primitive !");
447 // If the mission is under a npc_bot node, then the giver is directly taken
449 if (mission->getParent())
451 if (mission->getParent()->getPropertyByName("class", s) && *s == "npc_bot")
453 if (mission->getParent()->getPropertyByName("name", s))
458 // TODO : read all other params...
464 void CMissionData::initHeaderPhrase(IPrimitive
*prim
)
466 CPhrase::TPredefParams params
;
468 params
[0].push_back(CPhrase::TParamInfo("giver", STRING_MANAGER::bot
));
469 _MissionTitle
.initPhrase(*this, prim
, _MissionTitleRaw
, 0, params
);
470 _MissionDescription
.initPhrase(*this, prim
, _MissionDescriptionRaw
, 0, params
);
471 _MissionAutoMenu
.initPhrase(*this, prim
, _MissionAutoMenuRaw
);
474 bool CMissionCompiler::compileMission(NLLIGO::IPrimitive
*rootPrim
, const std::string
&primFileName
)
476 TPrimitiveClassPredicate
pred("mission_tree");
480 IPrimitive
*mission
= rootPrim
;
481 CMissionData
*pmd
= new CMissionData
;
482 CMissionData
&md
= *pmd
;
484 // Read the mission name
485 string missionName
= md
.getProperty(mission
, "name", false, false);
486 if( missionName
.find(' ') != string::npos
)
488 throw EParseException(mission
, toString("Mission name '%s' must not contains space", missionName
.c_str()).c_str());
490 md
.setMissionName(missionName
);
491 // Create a temporary primitive node to create default variable
494 IPrimitive
*temp
= new CPrimNode();
495 temp
->addPropertyByName("class", new CPropertyString("var_npc"));
496 temp
->addPropertyByName("name", new CPropertyString("giver = giver"));
497 temp
->addPropertyByName("npc_name", new CPropertyString("giver"));
498 temp
->addPropertyByName("var_name", new CPropertyString("giver"));
500 IVar
*var
= IVar::createVar(md
, temp
);
501 md
.addVariable(NULL
, var
);
507 // player default var
508 IPrimitive
*temp
= new CPrimNode();
509 temp
->addPropertyByName("class", new CPropertyString("var_npc"));
510 temp
->addPropertyByName("name", new CPropertyString("player = player"));
511 temp
->addPropertyByName("npc_name", new CPropertyString("player"));
512 temp
->addPropertyByName("var_name", new CPropertyString("player"));
514 IVar
*var
= IVar::createVar(md
, temp
);
515 md
.addVariable(NULL
, var
);
521 // guild_name default var
522 IPrimitive
*temp
= new CPrimNode();
523 temp
->addPropertyByName("class", new CPropertyString("var_text"));
524 temp
->addPropertyByName("name", new CPropertyString("guild_name = guild_name"));
525 temp
->addPropertyByName("npc_name", new CPropertyString("guild_name"));
526 temp
->addPropertyByName("var_name", new CPropertyString("guild_name"));
528 IVar
*var
= IVar::createVar(md
, temp
);
529 md
.addVariable(NULL
, var
);
534 // first, start by reading mission variables
535 IPrimitive
*variables
;
537 TPrimitiveClassPredicate
predTmp("variables");
538 variables
= NLLIGO::getPrimitiveChild(mission
, predTmp
);
543 nlwarning("Can't find variables !");
546 parseVariables(md
, variables
);
548 // read global mission data
549 md
.parseMissionHeader(rootPrim
);
551 // now, we can init the mission header phrase (they need variable knwoled)
552 md
.initHeaderPhrase(rootPrim
);
556 TPrimitiveClassPredicate
predTmp("pre_requisite");
557 preReq
= getPrimitiveChild(mission
, predTmp
);
562 nlwarning("Can't find pre requisite !");
565 parsePreRequisite(md
, preReq
);
567 /* IPrimitive *steps = getPrimitiveChild(mission, TPrimitivePropertyPredicate("step_tag", "true"));
570 nlwarning("Can't find steps !");
573 */ parseSteps(md
, mission
);
575 // Store the compiled mission
576 _CompiledMission
.push_back(pmd
);
578 string script
= md
.generateMissionScript(primFileName
);
580 nlinfo("The script :");
581 nlinfo("%s", script
.c_str());
583 string phrases
= md
.generatePhraseFile();
584 nlinfo("The phrase file is :");
586 vector
<string
> lines
;
587 explode(phrases
, string("\n"), lines
, false);
588 for (uint i
=0; i
<lines
.size(); ++i
)
590 if(lines
[i
][0] == '\r') lines
[i
] = lines
[i
].substr(1);
591 nlinfo("%s", lines
[i
].c_str());
595 string dot
= md
.generateDotScript();
596 nlinfo("The dot script is :");
598 vector
<string
> lines
;
599 explode(dot
, string("\n"), lines
, false);
600 for (uint i
=0; i
<lines
.size(); ++i
)
602 if(lines
[i
][0] == '\r') lines
[i
] = lines
[i
].substr(1);
603 nlinfo("%s", lines
[i
].c_str());
609 bool CMissionCompiler::compileMissions(IPrimitive
*rootPrim
, const std::string
&primFileName
)
612 // 1st, build a set of mission_scrip nodes
613 NLLIGO::TPrimitiveSet missionTrees
;
615 CPrimitiveSet
<TPrimitiveClassPredicate
> scriptsSet
;
617 TPrimitiveClassPredicate
pred("mission_tree");
618 scriptsSet
.buildSet(rootPrim
, pred
, missionTrees
);
620 nlinfo("Found %u mission tree in the primitive file", missionTrees
.size());
622 for (uint i
=0; i
<missionTrees
.size(); ++i
)
626 compileMission(missionTrees
[i
], primFileName
);
628 // catch (const EParseException &e)
630 // nlwarning("Error while parsing a mission: '%s'", e.Why.c_str());
640 bool CMissionCompiler::installCompiledMission(NLLIGO::CLigoConfig
&ligoConfig
, const std::string
&primFileName
)
642 // generate the mission script into the npcs...
644 map
<string
, TLoadedPrimitive
> loadedPrimitives
;
646 // store the previous alias value
647 map
<string
, uint32
> missionAlias
;
649 // First loop to remove any mission that belong to the compiled primitive file
650 for (uint i
=0; i
<_CompiledMission
.size(); ++i
)
652 CMissionData
&mission
= *(_CompiledMission
[i
]);
653 // first, look for the primitive file to load
654 string fileName
= mission
.getGiverPrimitive();
655 if (fileName
.empty())
657 // use mission primitive instead
658 fileName
= primFileName
;
660 if (loadedPrimitives
.find(toLowerAscii(fileName
)) == loadedPrimitives
.end())
662 string fullFileName
= CPath::lookup(fileName
, false);
663 if (fullFileName
.empty())
665 throw EParseException(NULL
, toString("Can't find primitive file '%s' in path", fileName
.c_str()).c_str());
667 // we need to load this primitive file.
668 CPrimitives
*primDoc
= new CPrimitives
;
669 CPrimitiveContext::instance().CurrentPrimitive
= primDoc
;
670 if (loadXmlPrimitiveFile(*primDoc
, fullFileName
, ligoConfig
))
672 // the primitive file is loaded correctly
673 loadedPrimitives
.insert(make_pair(toLowerAscii(fileName
), TLoadedPrimitive(primDoc
, fullFileName
)));
674 CPrimitiveContext::instance().CurrentPrimitive
= NULL
;
678 CPrimitiveContext::instance().CurrentPrimitive
= NULL
;
679 throw EParseException(NULL
, toString("Can't read primitive file '%s'", fullFileName
.c_str()).c_str());
682 TLoadedPrimitive
&loadedPrim
= loadedPrimitives
[toLowerAscii(fileName
)];
683 CPrimitives
*primDoc
= loadedPrim
.PrimDoc
;
685 TPrimitiveSet scripts
;
686 CPrimitiveSet
<TPrimitiveClassPredicate
> filter
;
687 TPrimitiveClassPredicate
pred("mission");
688 filter
.buildSet(primDoc
->RootNode
, pred
, scripts
);
690 // for each script, check if it was generated, and if so, check the name
691 // of the source primitive file.
692 for (uint i
=0; i
<scripts
.size(); ++i
)
694 vector
<string
> *script
;
695 if (scripts
[i
]->getPropertyByName("script", script
) && !script
->empty())
699 scripts
[i
]->getPropertyByName("name", missionName
);
701 // Format should be : #compiled from <source_primitive_name>
702 if (script
->front().find("generated from") != string::npos
)
704 // we have a compiled mission
705 if (script
->front().find(CFile::getFilename(primFileName
)) != string::npos
)
707 // ok, this mission is compiled from the same primitive
710 TPrimitiveClassPredicate
pred("alias");
712 IPrimitive
*p
= getPrimitiveChild(scripts
[i
], pred
);
716 CPrimAlias
*pa
= dynamic_cast<CPrimAlias
*>(p
);
719 uint32 alias
= pa
->getAlias();
720 missionAlias
.insert(make_pair(missionName
, alias
));
725 nlwarning("Can't find alias prim in primitive '%s'", buildPrimPath(scripts
[i
]).c_str());
729 scripts
[i
]->getParent()->removeChild(scripts
[i
]);
736 // second loop to assign compiled mission to giver npc
737 for (uint i
=0; i
<_CompiledMission
.size(); ++i
)
739 CMissionData
&mission
= *(_CompiledMission
[i
]);
740 string fileName
= mission
.getGiverPrimitive();
741 if (fileName
.empty())
743 // no giver primitive file specified in the mission, use the mission primitive instead
744 fileName
= primFileName
;
747 TLoadedPrimitive
&loadedPrim
= loadedPrimitives
[toLowerAscii(fileName
)];
748 CPrimitives
*primDoc
= loadedPrim
.PrimDoc
;
749 CPrimitiveContext::instance().CurrentPrimitive
= primDoc
;
752 CPrimitiveSet
<TPrimitiveClassAndNamePredicate
> filter
;
753 TPrimitiveClassAndNamePredicate
pred("npc_bot", mission
.getGiverName());
754 filter
.buildSet(primDoc
->RootNode
, pred
, bots
);
758 string err
= toString("Can't find bot '%s' in primitive '%s' !",
759 mission
.getGiverName().c_str(),
761 throw EParseException(NULL
, err
.c_str());
763 else if (bots
.size() > 1)
765 string err
= toString("Found more than one bot named '%s' in primitive '%s' !",
766 mission
.getGiverName().c_str(),
768 throw EParseException(NULL
, err
.c_str());
771 // ok, all is good, we can add the mission node to the giver
772 IPrimitive
*giver
= bots
.front();
773 // create a new node for the mission
774 IPrimitive
*script
= new CPrimNode
;
776 script
->addPropertyByName("class", new CPropertyString("mission"));
778 script
->addPropertyByName("name", new CPropertyString(mission
.getMissionName()));
779 // string alias(toString("%u", makeHash32(mission.getMissionName())));
780 // script->addPropertyByName("alias", new CPropertyString(mission.getAlias()));
781 string scriptLines
= mission
.generateMissionScript(primFileName
);
782 vector
<string
> lines
;
783 explode(scriptLines
, NL
, lines
, false);
785 script
->addPropertyByName("script", new CPropertyStringArray(lines
));
787 // insert the script into the giver
788 giver
->insertChild(script
);
792 CPrimAlias
*pa
= new CPrimAlias
;
793 pa
->addPropertyByName("class", new CPropertyString ("alias"));
794 pa
->addPropertyByName("name", new CPropertyString ("alias"));
796 if (missionAlias
.find(mission
.getMissionName()) != missionAlias
.end())
798 // restore the previous alias
799 primDoc
->forceAlias(pa
, missionAlias
.find(mission
.getMissionName())->second
);
802 // insert in first place
803 script
->insertChild(pa
, 0);
806 CPrimitiveContext::instance().CurrentPrimitive
= NULL
;
809 // Save the modified primitive files
810 while (!loadedPrimitives
.empty())
812 TLoadedPrimitive
&loadedPrim
= loadedPrimitives
.begin()->second
;
813 if (!saveXmlPrimitiveFile(*(loadedPrim
.PrimDoc
), loadedPrim
.FullFileName
))
816 _FilesToPublish
.push_back(loadedPrim
.FullFileName
);
819 delete loadedPrim
.PrimDoc
;
821 loadedPrimitives
.erase(loadedPrimitives
.begin());
825 // generate the phrase file (if any)
827 string phraseFileName
= CFile::getFilenameWithoutExtension(primFileName
) + "_wk.txt";
831 for (uint i
=0; i
<_CompiledMission
.size(); ++i
)
833 content
+= _CompiledMission
[i
]->generatePhraseFile();
835 // transform NL (\n\r) into single \n
836 content
= content
.replace(NL
.c_str(), "\n");
838 ucs
.fromUtf8(content
);
840 CI18N::writeTextFile(phraseFileName
, ucs
, true);
842 _FilesToPublish
.push_back(phraseFileName
);
849 bool CMissionCompiler::publishFiles(const std::string
&serverPathPrim
, const std::string
&serverPathText
, const std::string
&localPathText
)
851 for (uint i
=0 ; i
<_FilesToPublish
.size() ; i
++)
853 string dst
, src
= _FilesToPublish
[i
];
855 string::size_type n
= src
.find("primitives");
856 if (n
== string::npos
)
858 // text files : copy it and check include in phrase_rites_wk.txt
861 string textFile
= CPath::standardizePath(serverPathText
) + "phrase_rites_wk.txt";
862 includeText(textFile
, string("#include \"") + src
+ string("\"\n"));
863 dst
= CPath::standardizePath(serverPathText
) + src
;
864 NLMISC::CFile::copyFile(dst
, src
);
867 textFile
= CPath::standardizePath(localPathText
) + "phrase_rites_wk.txt";
868 includeText(textFile
, string("#include \"") + src
+ string("\"\n"));
869 dst
= CPath::standardizePath(localPathText
) + src
;
870 NLMISC::CFile::copyFile(dst
, src
);
874 // primitive file : copy to server
875 dst
= CPath::standardizePath(serverPathPrim
) + string(src
, n
, src
.size());
876 NLMISC::CFile::copyFile(dst
, src
);
882 bool CMissionCompiler::includeText(const std::string
&filename
, const std::string
&text
)
884 FILE *f
= nlfopen(filename
, "r+");
887 nlwarning("Unable to open %s", filename
.c_str());
894 // Check for UTF8 format
895 if (fread(buffer
, 1, 3, f
) != 3)
898 nlwarning("Unable to read 3 bytes from %s", filename
.c_str());
902 if (buffer
[0] != -17 || buffer
[1] != -69 || buffer
[2] != -65)
903 fseek(f
, 0, SEEK_SET
);
906 while(fgets(buffer
, 1024, f
))
908 if (!strcmp(text
.c_str(), buffer
))
916 fputs(text
.c_str(), f
);
922 bool CMissionCompiler::parsePreRequisite(CMissionData
&md
, IPrimitive
*preReq
)
924 md
.parsePrerequisites(preReq
);
928 bool CMissionCompiler::parseOneStep(CMissionData
&md
, IPrimitive
*stepToParse
, IStep
*parent
, bool bEndOfBranch
)
930 IStep
*step
= IStep::createStep(md
, stepToParse
);
934 if (!step
->isAJump() && !step
->getStepName().empty())
936 if (md
.getStepByName(step
->getStepName()) != NULL
)
938 string err
= toString("Step '%s' already defined !", step
->getStepName().c_str());
939 throw EParseException(step
->getPrimitive(), err
.c_str());
942 if (step
->getStepName().find(' ') != string::npos
)
944 throw EParseException(step
->getPrimitive(), toString("Step name '%s' must not contains space", step
->getStepName().c_str()).c_str());
946 md
.addStepName(step
->getStepName(), step
);
949 TPrimitiveSet subBranchs
= step
->getSubBranchs();
951 // Add the step (if no parent add to the mission data)
954 if (!md
.addStep(step
))
956 throw EParseException(stepToParse
, "Error parsing mission step");
961 parent
->addSubStep(step
);
964 CStepIf
*pSI
= dynamic_cast<CStepIf
*>(step
);
965 // If this is a IF step : parse with 'step' as a parent
967 IStep
*pParentStep
= NULL
;
969 if ((dynamic_cast<CStepIf
*>(step
) != NULL
) ||
970 (dynamic_cast<CStepPlayerReconnect
*>(step
) != NULL
))
973 if (!subBranchs
.empty())
975 // need to parse subbranch before continuing
976 for (uint i
=0; i
<subBranchs
.size(); ++i
)
978 if (!parseOneStep(md
, subBranchs
[i
], pParentStep
, i
==(subBranchs
.size()-1)))
983 // if this is the last step, flag it as such
984 step
->EndOfBranch
= bEndOfBranch
;
989 bool CMissionCompiler::parseSteps(CMissionData
&md
, IPrimitive
*steps
, IStep
*parent
)
991 TPrimitiveSet childs
;
992 TPrimitivePropertyPredicate
pred("step_tag", "true");
993 filterPrimitiveChilds(steps
, pred
, childs
);
998 node
.addPropertyByName("class", new CPropertyString("end"));
999 node
.addPropertyByName("name", new CPropertyString(""));
1000 IStep
*step
= IStep::createStep(md
, &node
);
1002 // md.addStep(step);
1004 if (!childs
.empty())
1006 for (uint i
=0; i
<childs
.size(); ++i
)
1008 IPrimitive
*child
= childs
[i
];
1010 parseOneStep(md
, childs
[i
], NULL
, i
== (childs
.size()-1));
1017 string
CMissionCompiler::getProp(IPrimitive
*prim
, const string
&propName
)
1020 bool ret
= prim
->getPropertyByName(propName
.c_str(), s
);
1022 throw EParseException(prim
, toString("Property %s does't exist", propName
.c_str()).c_str());
1027 string
CMissionCompiler::getClass(IPrimitive
*prim
)
1030 bool ret
= prim
->getPropertyByName("class", className
);
1035 bool CMissionCompiler::parseVariables(CMissionData
&md
, IPrimitive
*variables
)
1037 for (uint i
=0; i
<variables
->getNumChildren(); ++i
)
1040 if (variables
->getChild(child
, i
))
1042 IVar
*var
= IVar::createVar(md
, child
);
1045 nldebug("Adding variable '%s' as type %u", var
->getVarName().c_str(), var
->getVarType());
1046 md
.addVariable(child
, var
);
1053 template <class VectorType
>
1054 bool strtokquote(const string
&src
, VectorType
&tokens
)
1064 TMode mode
= read_blank
;
1066 for (uint i
=0; i
<src
.size(); ++i
)
1071 if (src
[i
] != ' ' && src
[i
] != '\t' && src
[i
] != '\n' && src
[i
] != '\r')
1076 // begin of a quoted string
1090 if (src
[i
] == ' ' || src
[i
] == '\t' || src
[i
] == '\n' || src
[i
] == '\r' || src
[i
] == '\"')
1093 tokens
.push_back(temp
);
1106 // special treatment for escape command
1107 if (i
< src
.size()-1)
1111 // skip escaped char
1116 nlwarning("Error parsing escape char in quoted string");
1120 else if (src
[i
] != '\"')
1122 // just add this char
1127 // end of quoted string
1129 tokens
.push_back(temp
);
1138 if (mode
== read_quoted
)
1140 nlwarning("Missing closing quote at end of string while reading text in '%s'", src
.c_str());
1143 tokens
.push_back(temp
);
1148 template <class VectorType
>
1149 bool strtokquote(const vector
<string
> &src
, VectorType
&tokens
)
1151 for (uint i
=0; i
<src
.size(); ++i
)
1153 if (!strtokquote(src
[i
], tokens
))
1159 struct TFindParamPred
: std::unary_function
<CPhrase::TParamInfo
, bool>
1162 TFindParamPred(const std::string
&name
)
1166 bool operator() (const CPhrase::TParamInfo
¶mInfo
) const
1168 return paramInfo
.ParamName
== Name
;
1173 bool CPhrase::isEmpty()
1175 return _PhraseId
.empty();
1178 bool CPhrase::asAdditionnalParams()
1180 return !_AdditionalParams
.empty();
1184 void CPhrase::initPhrase (CMissionData
&md
,
1186 const vector
<string
> &texts
,
1188 const TPredefParams
&predefParams
)
1190 // nlassert(numEntry == predefParams.size());
1192 // store the predefined/default parameters
1193 _DefaultParams
= predefParams
;
1194 // store the number of entry to generate (for literal with variant)
1195 _NumEntry
= numEntry
;
1197 numEntry
= max(uint32(1), numEntry
);
1199 _PhraseLiterals
.clear();
1200 // _PhraseLiterals.resize(numEntry);
1202 // first, concatenate the text vector
1204 for (uint i
=0; i
<texts
.size(); ++i
)
1206 text
= text
+ texts
[i
];
1207 if (i
!= texts
.size() -1)
1211 nldebug("phrase text: %s", text
.c_str());
1213 CVectorSString tokens
;
1215 if (!strtokquote(text
, tokens
))
1216 throw EParseException(prim
, toString("failed to tokenize the string '%s'", text
.c_str()).c_str());
1222 // storage for additional parameters
1223 vector
<string
> params
;
1226 // ok, the string is parsed, now we can analyze it
1227 // look at the first letter of the first token to determine the type of data we have
1228 if (tokens
[0][0] == '\"')
1230 // we have a literal, so we must found numEntry literal, then a suffix tag for the phrase name
1231 if (tokens
.size() != numEntry
+1)
1232 throw EParseException(prim
, toString("bad number of tokens in phrase : need %u (%u entries + 1 suffix), found %u\n(in : '%s')",
1239 _PhraseLiterals
.resize(numEntry
);
1240 for (uint i
=0; i
<numEntry
; ++i
)
1242 CSString text
= tokens
[i
];
1243 // remove quotation marks
1244 text
= text
.leftCrop(1);
1245 text
= text
.rightCrop(1);
1247 // store the literal phrase value
1248 _PhraseLiterals
[i
] = text
;
1249 // escape any ']' in the string
1250 _PhraseLiterals
[i
] = CSString(_PhraseLiterals
[i
]).replace("]", "\\]");
1252 // now, we can analyse the string content, looking for parameters replacement
1253 while (text
.contains('$'))
1255 // 'advance' to replacement point
1256 text
= text
.splitFrom('$');
1261 if (!text
.contains('$'))
1262 throw EParseException(prim
, "missing parameter closing tag '$'");
1264 string::size_type paramStart
= _PhraseLiterals
[i
].size() - text
.size();
1265 // ok, we found a parameter
1266 CSString p
= text
.splitTo('$', true);
1267 // remove any subpart access
1269 if (i
>= predefParams
.size() || find_if(predefParams
[i
].begin(), predefParams
[i
].end(), TFindParamPred(static_cast<string
&>(p
))) == predefParams
[i
].end())
1271 // this param is not in the predefined params list, add it to the optional params
1272 params
.push_back(p
);
1275 // remove any compiler param from the phrase literal
1276 if (p
.find("@") != string::npos
)
1278 string::size_type pos
= _PhraseLiterals
[i
].find(p
, paramStart
);
1279 if (pos
!= string::npos
)
1281 string::size_type pos2
= _PhraseLiterals
[i
].find("@", pos
);
1282 if (pos2
!= string::npos
)
1284 while (pos2
< _PhraseLiterals
[i
].size()
1285 && _PhraseLiterals
[i
][pos2
] != '.'
1286 && _PhraseLiterals
[i
][pos2
] != '$')
1288 _PhraseLiterals
[i
].erase(pos2
, 1);
1297 // this is an escaped $, skip it
1303 // last, read the suffix
1304 _Suffixe
= tokens
.back();
1306 // generate identifier
1307 _PhraseId
= toUpperAscii(md
.getMissionName()+"_"+_Suffixe
);
1310 // select only unique params
1311 ps
.insert(params
.begin(), params
.end());
1313 vector
<string
> temp(ps
.begin(), ps
.end());
1317 else if (tokens
[0][0] == '$')
1319 // we have a variable substitution. Retrieve the var and recall init
1321 // do the var replacement
1322 CVectorSString tokens2
;
1324 tokens
[0] = md
.replaceVar(prim
, tokens
[0]);
1326 if (!strtokquote(tokens
[0], tokens2
))
1327 throw EParseException(prim
, toString("failed to tokenize the string ('%s')", tokens
[0].c_str()).c_str());
1329 tokens2
.insert(tokens2
.end(), tokens
.begin()+1, tokens
.end());
1330 tokens
.swap(tokens2
);
1332 // and retry the decoding
1337 // this should be a simple identifier, followed by any number of additional parameters
1339 // do the var replacement
1340 // tokens = md.replaceVar(prim, tokens);
1341 // untagVar(tokens[0]);
1343 // ok, now extract the phrase label and the additional parameters
1344 _PhraseId
= tokens
[0];
1345 for (uint i
=1; i
<tokens
.size(); ++i
)
1347 untagVar(tokens
[i
]);
1348 if (predefParams
.empty() || find_if(predefParams
[0].begin(), predefParams
[0].end(), TFindParamPred(static_cast<string
&>(tokens
[i
]))) == predefParams
[0].end())
1350 // this param is not in the predefined params list, add it to the optional params
1351 params
.push_back(tokens
[i
]);
1356 // now, build the parameter list
1358 vector
<string
>::iterator
first(params
.begin()), last(params
.end());
1359 for (; first
!= last
; ++first
)
1362 vector
<string
> parts
;
1363 NLMISC::explode(*first
, string("@"), parts
, false);
1367 if (parts
.size() > 1)
1370 const string
&varName
= name
;
1372 if (varName
!= "self")
1374 IVar
*var
= md
.getVariable(varName
);
1377 string err
= toString("Can't find variable '%s' referenced from a phrase",
1379 throw EParseException(prim
, err
.c_str());
1383 pi
.ParamName
= name
;
1384 pi
.CompilerParam
= param
;
1385 pi
.ParamType
= var
->getStringManagerType();
1386 _AdditionalParams
.push_back(pi
);
1391 std::string
CPhrase::genScript(CMissionData
&md
)
1396 for (uint i
=0; i
<_AdditionalParams
.size(); ++i
)
1398 IVar
*var
= md
.getVariable(_AdditionalParams
[i
].ParamName
);
1401 string err
= toString("Can't find variable named '%s' to generate phrase param", _AdditionalParams
[i
].ParamName
.c_str());
1402 throw EParseException(NULL
, err
.c_str());
1404 ret
+= "; " + var
->evalVar(_AdditionalParams
[i
].CompilerParam
);
1410 CMissionData::CMissionData()
1413 _MonoInstance
= false;
1414 _MissionAuto
= false;
1416 _Replayable
= false;
1419 _NotInJournal
= false;
1420 _AutoRemoveFromJournal
= false;
1421 _PlayerReplayTimer
= 0;
1422 _GlobalReplayTimer
= 0;
1423 _NotProposed
= false;
1424 _NonAbandonnable
= false;
1425 _NeedValidation
= false;
1426 _FailIfInventoryIsFull
= false;
1429 CMissionData::~CMissionData()
1431 while (!_Variables
.empty())
1433 delete _Variables
.begin()->second
;
1434 _Variables
.erase(_Variables
.begin());
1437 while (!_Steps
.empty())
1439 delete _Steps
.back();
1444 void CMissionData::setMissionName(const string
&missionName
)
1446 _MissionName
= missionName
;
1449 const string
&CMissionData::getMissionName() { return _MissionName
;}
1451 bool CMissionData::addVariable(NLLIGO::IPrimitive
*prim
, IVar
*var
)
1453 if (_Variables
.find(var
->getVarName()) != _Variables
.end())
1454 throw EParseException(prim
, toString("Variable '%s' already defined !", var
->getVarName().c_str()).c_str());
1456 _Variables
.insert(make_pair(var
->getVarName(), var
));
1457 _VariablesOrder
.push_back(var
);
1461 IVar
*CMissionData::getVariable(const string
&varName
)
1463 map
<string
, IVar
*>::iterator
it(_Variables
.find(varName
));
1464 if (it
!= _Variables
.end())
1469 IStep
*CMissionData::getNextStep(IStep
*current
)
1471 for (uint i
=0; i
<_Steps
.size(); ++i
)
1473 if (_Steps
[i
] == current
&& i
< _Steps
.size()-1)
1479 IStep
*CMissionData::getStepByName(const std::string
&stepName
)
1481 if (_StepsByNames
.find(stepName
) != _StepsByNames
.end())
1483 return _StepsByNames
[stepName
];
1490 bool CMissionData::addStep(IStep
*step
)
1492 _Steps
.push_back(step
);
1496 string
CMissionData::genPreRequisites()
1499 if (!_ReqSkills
.empty())
1501 ret
+= "req_skill : ";
1502 for (uint i
=0; i
<_ReqSkills
.size(); ++i
)
1504 ret
+= _ReqSkills
[i
].Skill
+" "+_ReqSkills
[i
].MinLevel
+" "+_ReqSkills
[i
].MaxLevel
;
1505 if (i
< _ReqSkills
.size()-1)
1511 if (!_ReqMissionDone
.empty())
1513 for (uint i
=0; i
<_ReqMissionDone
.size(); ++i
)
1515 ret
+= "req_mission : "+ _ReqMissionDone
[i
]+NL
;
1518 if (!_ReqMissionNotDone
.empty())
1520 for (uint i
=0; i
<_ReqMissionNotDone
.size(); ++i
)
1522 ret
+= "req_mission_neg : "+_ReqMissionNotDone
[i
]+NL
;
1525 if (!_ReqMissionRunning
.empty())
1527 for (uint i
=0; i
<_ReqMissionRunning
.size(); ++i
)
1529 ret
+= "req_mission_running : "+_ReqMissionRunning
[i
]+NL
;
1532 if (!_ReqMissionNotRunning
.empty())
1535 for (uint i
=0; i
<_ReqMissionNotRunning
.size(); ++i
)
1537 ret
+= "req_mission_running_neg : "+_ReqMissionNotRunning
[i
]+NL
;
1540 if (!_ReqWearItem
.empty())
1542 ret
+= "req_wear : ";
1543 for (uint i
=0; i
<_ReqWearItem
.size(); ++i
)
1545 ret
+= _ReqWearItem
[i
];
1546 if(i
< _ReqWearItem
.size()-1)
1551 if (!_ReqOwnItem
.empty())
1553 ret
+= "req_item : ";
1554 for (uint i
=0; i
<_ReqOwnItem
.size(); ++i
)
1556 ret
+= _ReqOwnItem
[i
];
1557 if(i
< _ReqOwnItem
.size()-1)
1562 if (!_ReqTitle
.empty())
1564 ret
+= "req_title : "+_ReqTitle
+NL
;
1566 if (!_ReqFames
.empty())
1568 for (uint i
=0; i
<_ReqFames
.size(); ++i
)
1570 ret
+= "req_fame : "+_ReqFames
[i
].Faction
+" "+_ReqFames
[i
].Fame
;
1576 ret
+= "req_guild"+NL
;
1578 if (!_ReqGrade
.empty())
1580 ret
+= "req_grade : "+_ReqGrade
+NL
;
1582 if (!_ReqTeamSize
.empty())
1584 ret
+= "req_team_size : "+_ReqTeamSize
+NL
;
1586 if (!_ReqBrick
.empty())
1588 ret
+= "req_brick : ";
1589 for (uint i
=0; i
<_ReqBrick
.size(); ++i
)
1591 ret
+= _ReqBrick
[i
];
1592 if(i
< _ReqBrick
.size()-1)
1597 if (!_ReqCharacterAge
.empty())
1599 ret
+= "req_character_age : "+_ReqCharacterAge
+NL
;
1601 if (!_ReqMaxPlayerID
.empty())
1603 ret
+= "req_max_player_id : "+_ReqMaxPlayerID
+NL
;
1605 if (!_ReqSeason
.empty())
1607 ret
+= "req_season : "+_ReqSeason
+NL
;
1609 if (!_ReqEncyclo
.empty())
1611 ret
+= "req_encyclo_thema : " + _ReqEncyclo
+ NL
;
1613 if (!_ReqEncycloNeg
.empty())
1615 ret
+= "req_encyclo_thema_neg : " + _ReqEncycloNeg
+ NL
;
1617 if (!_ReqEventFaction
.empty())
1619 ret
+= "req_event_faction : " + _ReqEventFaction
+ NL
;
1626 string
CMissionData::generateMissionScript(const std::string
&primFileName
)
1628 _JumpPoints
.clear();
1629 // first, gather jump point list
1630 for (uint i
=0; i
<_Steps
.size(); ++i
)
1632 set
<TJumpInfo
> temp
;
1633 _Steps
[i
]->fillStepJump(*this, temp
);
1635 // remove any jump to the next step (normal flow)
1636 if (i
< _Steps
.size()-1)
1638 set
<TJumpInfo
>::iterator
first(temp
.begin()), last(temp
.end());
1639 for (; first
!= last
; )
1641 const TJumpInfo
&ji
= *first
;
1643 if (ji
.StepName
== _Steps
[i
+1]->getStepName() && ji
.Discardable
)
1646 first
= temp
.begin();
1653 _JumpPoints
.insert(temp
.begin(), temp
.end());
1655 // generate the script
1657 // generate mission header
1658 script
+= "# script generated from '"+CFile::getFilename(primFileName
)+"'"+NL
+NL
;
1659 script
+= "#mission tags and pre-requisites"+NL
;
1661 script
+= "mono"+NL
;
1663 script
+= "once"+NL
;
1665 script
+= "replayable"+NL
;
1667 script
+= "solo"+NL
;
1669 script
+= "guild"+NL
;
1671 script
+= "no_list"+NL
;
1672 if (_AutoRemoveFromJournal
)
1673 script
+= "auto_remove"+NL
;
1674 if (!_MissionCategory
.empty())
1675 script
+= "mission_category : "+_MissionCategory
+NL
;
1676 if (_PlayerReplayTimer
!= 0)
1677 script
+= "player_replay_timer : "+toString("%u", _PlayerReplayTimer
)+NL
;
1678 if (_GlobalReplayTimer
!= 0)
1679 script
+= "global_replay_timer : "+toString("%u", _GlobalReplayTimer
)+NL
;
1681 script
+= "not_proposed"+NL
;
1683 script
+= string("auto : ")+_MissionAutoMenu
.genScript(*this)+NL
;
1684 if (_NonAbandonnable
)
1685 script
+= "non_abandonnable"+NL
;
1686 if (!_MissionIcon
.empty())
1687 script
+= "mission_icon : "+_MissionIcon
+NL
;
1688 if (_NeedValidation
)
1689 script
+= "need_validation"+NL
;
1690 if (_FailIfInventoryIsFull
)
1691 script
+= "fail_if_inventory_is_full"+NL
;
1693 if (!_ParentMissions
.empty())
1695 set
<string
>::iterator
first(_ParentMissions
.begin()), last(_ParentMissions
.end());
1696 for (; first
!= last
; ++first
)
1698 script
+= "parent : "+ *first
+NL
;
1702 script
+= NL
+"#Variables declaration"+NL
;
1704 // declare all the variables
1706 std::vector
<IVar
*>::iterator
first(_VariablesOrder
.begin()), last(_VariablesOrder
.end());
1707 for (; first
!= last
; ++first
)
1709 script
+= (*first
)->genDecl(*this);
1713 script
+= NL
+"#pre-requisites"+NL
;
1714 script
+= genPreRequisites();
1716 script
+= NL
+"#script"+NL
;
1717 // generate mission title and desc
1718 script
+= "mission_title : "+_MissionTitle
.genScript(*this)+NL
;
1719 script
+= "mission_desc : "+_MissionDescription
.genScript(*this)+NL
;
1721 // generate steps scripts
1722 for (uint i
=0; i
<_Steps
.size(); ++i
)
1724 script
+= "# "+_Steps
[i
]->getStepName()+NL
;
1725 if (_JumpPoints
.find(_Steps
[i
]->getStepName()) != _JumpPoints
.end()
1726 && !_Steps
[i
]->isAJump())
1728 // insert a jump point
1729 script
+= "jump_point : " + _Steps
[i
]->getStepName() + NL
;
1732 script
+= _Steps
[i
]->genCode(*this);
1733 //if (_Steps[i]->EndOfBranch && !_Steps[i]->isAJump())
1734 // script += "end"+NL;
1740 string
CMissionData::generatePhraseFile()
1743 // generate header phrase
1744 ret
= _MissionTitle
.genPhrase();
1745 ret
+= _MissionDescription
.genPhrase();
1746 ret
+= _MissionAutoMenu
.genPhrase();
1748 // generate var phrase
1749 for (uint i
=0; i
<_VariablesOrder
.size(); ++i
)
1751 ret
+= _VariablesOrder
[i
]->genPhrase();
1754 // generate step phrase
1755 for (uint i
=0; i
<_Steps
.size(); ++i
)
1757 ret
+= _Steps
[i
]->genPhrase();
1762 string
CMissionData::generateDotScript()
1764 string ret
= "digraph " + _MissionName
+ NL
;
1767 // set default shape to 'record'
1768 ret
+= "node [shape=record]"+NL
;
1770 ret
+= "\t__start__ [shape=\"point\", peripheries=2, label=\"\"]"+NL
;
1772 // 1st pass, generate node for each step
1773 for (uint i
=0; i
<_Steps
.size(); ++i
)
1775 if (!_Steps
[i
]->isEnd() && !_Steps
[i
]->isAJump())
1777 ret
+= "\t"+_Steps
[i
]->getStepName();
1778 ret
+= " [URL=\""+buildPrimPath(_Steps
[i
]->getPrimitive())+"\"]"+NL
;
1782 ret
+= "\t__end__ [shape=\"point\"]"+NL
;
1784 // activate red color for shapes that are created after this points
1785 ret
+= "node [color=red]"+NL
;
1787 // 2nd pass, generate link between steps
1788 for (uint i
=0; i
<_Steps
.size(); ++i
)
1790 if (_Steps
[i
]->isAJump())
1795 ret
+= "\t__start__ -> " + _Steps
[i
]->getStepName() + NL
;
1797 set
<TJumpInfo
> jumps
;
1798 _Steps
[i
]->fillStepJump(*this, jumps
);
1799 // there is a link there
1800 while (!jumps
.empty())
1802 const TJumpInfo
&ji
= *(jumps
.begin());
1803 if (_StepsByNames
.find(ji
.StepName
) != _StepsByNames
.end()
1804 && _StepsByNames
[ji
.StepName
]->isAJump())
1806 // this step is a jump, skip to link to the jump destination
1807 IStep
*jumpStep
= _StepsByNames
[ji
.StepName
];
1808 set
<TJumpInfo
> jumpJump
;
1809 jumpStep
->fillStepJump(*this, jumpJump
);
1810 if (jumpJump
.size() != 1)
1812 string str
= toString("Step jump contains %u jumps destination instead of 1", jumpJump
.size());
1813 throw EParseException(jumpStep
->getPrimitive(), str
.c_str());
1816 ret
+= "\t"+_Steps
[i
]->getStepName() + " -> " + jumpJump
.begin()->StepName
+" [label=\""+ji
.JumpName
+"\"]" + NL
;
1820 ret
+= "\t"+_Steps
[i
]->getStepName() + " -> " + ji
.StepName
+" [label=\""+jumps
.begin()->JumpName
+"\"]" + NL
;
1822 jumps
.erase(jumps
.begin());
1833 void CMissionData::parseMissionHeader(NLLIGO::IPrimitive
*prim
)
1835 // _MissionName = getProperty(prim, "name", false, false);
1836 // if( _MissionName.find(' ') != string::npos)
1838 // throw EParseException(prim, toString("Mission name '%s' must not contains space", _MissionName.c_str()).c_str());
1840 _GiverPrimitive
= getProperty(prim
,"giver_primitive", true, false);
1841 _MissionGiver
= getProperty(prim
, "mission_giver", true, false);
1843 // _Alias = getProperty(prim, "alias", false, false);
1845 // If the mission is under a npc_bot node, then the giver is directly taken
1846 // from the npc name
1847 if (prim
->getParent())
1849 if (getProperty(prim
->getParent(), "class", false, false) == "npc_bot")
1851 _MissionGiver
= getProperty(prim
->getParent(), "name", false, false);
1856 _MissionTitleRaw
= getPropertyArray(prim
, "mission_title", false, false);
1857 // _MissionTitle.init(*this, prim, vs);
1858 _MissionDescriptionRaw
= getPropertyArray(prim
, "mission_description", false, false);
1859 // _MissionDescription.init(*this, prim, vs);
1860 _MonoInstance
= toBool(getProperty(prim
, "mono_instance", true, false));
1861 _RunOnce
= toBool(getProperty(prim
, "run_only_once", true, false));
1862 _Replayable
= toBool(getProperty(prim
, "replayable", true, false));
1864 _NeedValidation
= toBool(getProperty(prim
, "need_validation", true, false));
1866 _MissionAutoMenuRaw
= getPropertyArray(prim
, "phrase_auto_menu", false, false);
1869 string s
= getProperty(prim
, "audience", false, false);
1872 else if (s
== "guild")
1875 _NotInJournal
= NLMISC::toBool(getProperty(prim
, "not_in_journal", false, false));
1876 _AutoRemoveFromJournal
= NLMISC::toBool(getProperty(prim
, "auto_remove_from_journal", false, false));
1877 _MissionCategory
= getProperty(prim
, "mission_category", false, false);
1878 NLMISC::fromString(getProperty(prim
, "player_replay_timer", true, false), _PlayerReplayTimer
);
1879 NLMISC::fromString(getProperty(prim
, "global_replay_timer", true, false), _GlobalReplayTimer
);
1880 _NotProposed
= NLMISC::toBool(getProperty(prim
, "not_proposed", false, false));
1881 _MissionAuto
= NLMISC::toBool(getProperty(prim
, "automatic", false, false));
1882 _NonAbandonnable
= NLMISC::toBool(getProperty(prim
, "non_abandonnable", false, false));
1883 _FailIfInventoryIsFull
= NLMISC::toBool(getProperty(prim
, "fail_if_inventory_is_full", false, false));
1884 _MissionIcon
= getProperty(prim
, "mission_icon", false, false);
1888 if (_MissionAutoMenuRaw
.empty())
1890 string error
= toString("Mission is flagged automatic, but no phrase_auto_menu defined !");
1891 throw EParseException(prim
, error
.c_str());
1895 vs
= getPropertyArray(prim
, "parent_missions", true, false);
1896 _ParentMissions
.insert(vs
.begin(), vs
.end());
1900 void CMissionData::parsePrerequisites(NLLIGO::IPrimitive
*prim
)
1904 vs
= getPropertyArray(prim
, "require_skill/min_level/max_level", true, false);
1905 for (uint i
=0; i
<vs
.size(); ++i
)
1909 vector
<string
> parts
;
1910 strtokquote(vs
[i
], parts
);
1911 if (parts
.size() != 3)
1913 throw EParseException(prim
, toString("Invalide argument count in line %u of require_skill array. Need 3, found %u", i
, parts
.size()).c_str());
1916 rs
.Skill
= parts
[0];
1917 rs
.MinLevel
= parts
[1];
1918 rs
.MaxLevel
= parts
[2];
1920 _ReqSkills
.push_back(rs
);
1925 vs
= getPropertyArray(prim
, "require_mission_done", true, false);
1926 for (uint i
=0; i
<vs
.size(); ++i
)
1930 vector
<string
> parts
;
1931 strtokquote(vs
[i
], parts
);
1932 if (parts
.size() != 1)
1934 throw EParseException(prim
, toString("Invalide argument count in line %u of require_mission_done array. Need 1, found %u", i
, parts
.size()).c_str());
1936 _ReqMissionDone
.push_back(parts
[0]);
1940 vs
= getPropertyArray(prim
, "require_mission_not_done", true, false);
1941 for (uint i
=0; i
<vs
.size(); ++i
)
1945 vector
<string
> parts
;
1946 strtokquote(vs
[i
], parts
);
1947 if (parts
.size() != 1)
1949 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());
1951 _ReqMissionNotDone
.push_back(parts
[0]);
1955 vs
= getPropertyArray(prim
, "require_mission_running", true, false);
1956 for (uint i
=0; i
<vs
.size(); ++i
)
1960 vector
<string
> parts
;
1961 strtokquote(vs
[i
], parts
);
1962 if (parts
.size() != 1)
1964 throw EParseException(prim
, toString("Invalide argument count in line %u of require_mission_running array. Need 1, found %u", i
, parts
.size()).c_str());
1966 _ReqMissionRunning
.push_back(parts
[0]);
1969 // Mission not running
1970 vs
= getPropertyArray(prim
, "require_mission_not_running", true, false);
1971 for (uint i
=0; i
<vs
.size(); ++i
)
1975 vector
<string
> parts
;
1976 strtokquote(vs
[i
], parts
);
1977 if (parts
.size() != 1)
1979 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());
1981 _ReqMissionNotRunning
.push_back(parts
[0]);
1985 vs
= getPropertyArray(prim
, "require_wearing_item", true, false);
1986 for (uint i
=0; i
<vs
.size(); ++i
)
1990 vector
<string
> parts
;
1991 strtokquote(vs
[i
], parts
);
1992 if (parts
.size() != 1)
1994 throw EParseException(prim
, toString("Invalide argument count in line %u of require_wearing_item array. Need 1, found %u", i
, parts
.size()).c_str());
1996 _ReqWearItem
.push_back(parts
[0]);
2000 vs
= getPropertyArray(prim
, "require_own_item", true, false);
2001 for (uint i
=0; i
<vs
.size(); ++i
)
2005 vector
<string
> parts
;
2006 strtokquote(vs
[i
], parts
);
2007 if (parts
.size() != 1)
2009 throw EParseException(prim
, toString("Invalide argument count in line %u of require_own_item array. Need 1, found %u", i
, parts
.size()).c_str());
2011 _ReqOwnItem
.push_back(parts
[0]);
2015 _ReqTitle
= getProperty(prim
, "require_title", true, false);
2017 vs
= getPropertyArray(prim
, "require_faction/fame", true, false);
2018 for (uint i
=0; i
<vs
.size(); ++i
)
2022 vector
<string
> parts
;
2023 strtokquote(vs
[i
], parts
);
2024 if (parts
.size() != 2)
2026 throw EParseException(prim
, toString("Invalide argument count in line %u of require_faction/fame array. Need 2, found %u", i
, parts
.size()).c_str());
2029 rf
.Faction
= parts
[0];
2032 _ReqFames
.push_back(rf
);
2036 if (getProperty(prim
, "require_guild_membership", true, false) == "true")
2041 _ReqGrade
= getProperty(prim
, "require_guild_grade", true, false);
2043 _ReqTeamSize
= getProperty(prim
, "require_team_size", true, false);
2044 // character minimum age
2045 _ReqCharacterAge
= getProperty(prim
, "require_character_age", true, false);
2046 // maximum player ID
2047 _ReqMaxPlayerID
= getProperty(prim
, "require_max_player_id", true, false);
2049 vs
= getPropertyArray(prim
, "require_brick_knowledge", true, false);
2050 for (uint i
=0; i
<vs
.size(); ++i
)
2054 vector
<string
> parts
;
2055 strtokquote(vs
[i
], parts
);
2056 if (parts
.size() != 1)
2058 throw EParseException(prim
, toString("Invalide argument count in line %u of require_brick_knowledge array. Need 1, found %u", i
, parts
.size()).c_str());
2060 _ReqBrick
.push_back(parts
[0]);
2064 _ReqSeason
= getProperty(prim
, "require_season", true, false);
2066 _ReqEncyclo
= getProperty(prim
, "require_encyclo_thema", true, false);
2067 _ReqEncycloNeg
= getProperty(prim
, "require_encyclo_thema_neg", true, false);
2069 if ((!_ReqEncyclo
.empty() && !_ReqEncycloNeg
.empty())
2070 || (!_ReqEncycloNeg
.empty() && !_ReqEncyclo
.empty()))
2072 string err
= toString("You can't mix positive and negative encyclopedy requirement");
2073 throw EParseException(prim
, err
.c_str());
2076 _ReqEventFaction
= getProperty(prim
, "require_event_faction", true, false);
2079 std::string
CMissionData::replaceVar(NLLIGO::IPrimitive
*prim
, const std::string
&str
)
2081 string::size_type pos
= 0;
2082 string::size_type pos2
= 0;
2085 while (pos
< str
.size())
2087 if (str
[pos
] != '$')
2091 else if (pos
+1 < str
.size() && str
[pos
+1] == '$')
2093 // check that this $ is not escaped
2099 // ok, this is not an escaped $
2101 // skip the initial '$'
2103 // while (str[pos] != ' ' && str[pos] != '\t' && str[pos] != '\n' && str[pos] != '\r')
2104 while (pos
< str
.size() && str
[pos
] != '$')
2105 varName
+= str
[pos
++];
2107 if (pos
>= str
.size())
2109 string err
= toString("Error while parsing variable in '%s', missing closing '$'", str
.c_str());
2110 throw EParseException (NULL
, err
.c_str());
2113 // skip the final '$'
2116 // split the var name and subpart
2117 vector
<string
> varParts
;
2118 explode(string(varName
), string("@"), varParts
, true);
2120 if (varParts
.empty() || varParts
.size() > 2)
2122 throw EParseException(prim
, toString("Error parsing varName '%s' in string '%s'", varName
.c_str(), str
.c_str()).c_str());
2125 if (_Variables
.find(varParts
.front()) == _Variables
.end())
2127 string err
= toString("Unknown variable '%s' in string '%s'", varParts
.front().c_str(), str
.c_str());
2128 throw EParseException (prim
, err
.c_str());
2131 IVar
*var
= _Variables
[varParts
[0]];
2133 if (varParts
.size() == 1)
2134 ret
+= var
->evalVar("");
2136 ret
+= var
->evalVar(varParts
[1]);
2144 std::vector
<std::string
> CMissionData::replaceVar(NLLIGO::IPrimitive
*prim
, const std::vector
<std::string
> &strs
)
2148 for (uint i
=0; i
<strs
.size(); ++i
)
2150 ret
.push_back(replaceVar(prim
, strs
[i
]));
2156 std::string
CMissionData::getProperty(NLLIGO::IPrimitive
*prim
, const std::string
&propertyName
, bool replaceVar
, bool canFail
)
2160 if (!prim
->getPropertyByName(propertyName
.c_str(), s
))
2164 string err
= toString("Can't find property '%s'", propertyName
.c_str());
2165 throw EParseException (prim
, err
.c_str());
2175 ret
= this->replaceVar(prim
, ret
);
2181 std::vector
<std::string
> CMissionData::getPropertyArray(NLLIGO::IPrimitive
*prim
, const std::string
&propertyName
, bool replaceVar
, bool canFail
)
2185 if (!prim
->getPropertyByName(propertyName
.c_str(), vs
))
2189 string err
= toString("Can't find property '%s'", propertyName
.c_str());
2190 throw EParseException (prim
, err
.c_str());
2200 ret
= this->replaceVar(prim
, ret
);
2205 bool CMissionData::isThereAJumpTo(const std::string
&stepName
)
2207 if (_JumpPoints
.find(stepName
) != _JumpPoints
.end())
2213 void TCompilerVarName::init(const std::string
&defaultName
, STRING_MANAGER::TParamType type
, CMissionData
&md
, NLLIGO::IPrimitive
*prim
, const std::string propName
)
2215 _DefaultName
= defaultName
;
2218 _VarName
= md
.getProperty(prim
, propName
, false, false);
2219 // remove the variable tag if any
2222 _VarValue
= md
.getProperty(prim
, propName
, true, false);
2225 void TCompilerVarName::initWithText(const std::string
&defaultName
, STRING_MANAGER::TParamType type
, CMissionData
&md
, NLLIGO::IPrimitive
*prim
, const std::string
&text
)
2227 _DefaultName
= defaultName
;
2231 // remove the variable tag if any
2234 _VarValue
= md
.replaceVar(prim
, text
);
2238 CPhrase::TParamInfo
TCompilerVarName::getParamInfo() const
2240 if (_VarName
.empty())
2241 return CPhrase::TParamInfo(_DefaultName
, _ParamType
);
2243 return CPhrase::TParamInfo(_VarName
, _ParamType
);
2247 bool TCompilerVarName::empty() const
2249 return _VarValue
.empty();
2252 TCompilerVarName::operator const std::string () const
2257 TCompilerVarName::operator CPhrase::TParamInfo() const
2259 return getParamInfo();
2263 std::string
operator+(const TCompilerVarName
& left
, const std::string
& right
) { return left
._VarValue
+ right
;}
2265 std::string
operator+(const std::string
& left
, const TCompilerVarName
& right
) { return left
+ right
._VarValue
;}
2267 std::vector
<TCompilerVarName
> TCompilerVarName::getPropertyArrayWithText(const std::string
&defaultName
, STRING_MANAGER::TParamType type
, CMissionData
&md
, NLLIGO::IPrimitive
*prim
, const std::string
& arrayProperyName
)
2269 std::vector
<TCompilerVarName
> compilerParams
;
2271 std::vector
<std::string
> values
= md
.getPropertyArray(prim
, arrayProperyName
,false, false);
2273 uint last
= (uint
)values
.size();
2274 compilerParams
.resize(last
);
2275 for ( ; first
!= last
; ++first
)
2277 compilerParams
[first
].initWithText( toString("%s%d", defaultName
.c_str(), first
+1) , type
, md
, prim
, values
[first
]);
2280 return compilerParams
;
2283 std::vector
<TCompilerVarName
> TCompilerVarName::getPropertyArrayWithTextStaticDefaultName(const std::string
&defaultName
, STRING_MANAGER::TParamType type
, CMissionData
&md
, NLLIGO::IPrimitive
*prim
, const std::string
& arrayProperyName
)
2285 std::vector
<TCompilerVarName
> compilerParams
;
2286 std::vector
<std::string
> values
= md
.getPropertyArray(prim
, arrayProperyName
,false, false);
2288 uint last
= (uint
)values
.size();
2289 compilerParams
.resize(last
);
2290 for ( ; first
!= last
; ++first
)
2292 compilerParams
[first
].initWithText( defaultName
, type
, md
, prim
, values
[first
]);
2294 return compilerParams
;