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) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "nel/misc/i18n.h"
21 #include "mission_compiler.h"
22 #include "nel/misc/config_file.h"
25 using namespace NLMISC
;
26 using namespace NLLIGO
;
33 int main(int argc
, char *argv
[])
35 NLMISC::CApplicationContext appContext
;
37 const char *leveldesignPath
= getenv("RYZOM_LEVELDESIGN");
39 if (leveldesignPath
== NULL
)
41 printf("Error: You need to define RYZOM_LEVELDESIGN environment variable that points to previous R:\\leveldesign\\ equivalent under Windows\n");
45 CPath::addSearchPath(NLMISC::CPath::standardizePath(leveldesignPath
), true, false);
48 if (argc
== 4 && string(argv
[3]) == "-test")
54 printf("%s <world_edit_class> <primitive_file> [-test]", argv
[0]);
60 sourceDocName
= argv
[2];
62 sourceDocName
= "test_compilateur.primitive";
65 sourceDocName
= CFile::getFilename(sourceDocName
);
67 NLLIGO::CLigoConfig LigoConfig
;
69 CPrimitiveContext::instance().CurrentLigoConfig
= &LigoConfig
;
71 nlinfo("Reading ligo configuration file...");
72 if (!LigoConfig
.readPrimitiveClass (argv
[1], false))
74 nlwarning("Can't read '%s' !", argv
[1]);
80 nlinfo("Reading primitive file...");
83 CPrimitiveContext::instance().CurrentPrimitive
= &primDoc
;
84 loadXmlPrimitiveFile(primDoc
, sourceDocName
, LigoConfig
);
90 nlinfo("Compiling test mission");
94 mc
.compileMissions(primDoc
.RootNode
, sourceDocName
);
95 TMissionDataPtr testMission
= mc
.getMission(0);
97 CSString script
= testMission
->generateMissionScript(sourceDocName
);
98 script
+= "======================================================"+NL
;
99 script
+= testMission
->generatePhraseFile();
100 script
+= "======================================================"+NL
;
101 script
+= testMission
->generateDotScript();
102 script
= script
.replace(NL
.c_str(), "\n");
104 const char *tmp
= ::getenv("TEMP");
106 FILE *fp
= ::fopen((string(tmp
)+"/compiled_mission.script").c_str(), "w");
107 ::fwrite(script
.data(), script
.size(), 1, fp
);
110 // TODO: set diff program in .cfg
111 std::string compareApp
;
112 std::string command
= NLMISC::toString("%s %s/compiled_mission.script test_compilateur.script", compareApp
.c_str(), tmp
);
113 sint error
= system(command
.c_str());
116 nlwarning("'%s' failed with error code %d", "", error
);
118 catch(const EParseException
&e
)
120 nlwarning(e
.Why
.c_str());
126 nlinfo("Compiling missions...");
129 mc
.compileMissions(primDoc
.RootNode
, sourceDocName
);
131 mc
.installCompiledMission(LigoConfig
, sourceDocName
);
132 /* std::vector <TMissionDataPtr> &missions = mc.getMissions();
133 // generate the mission script into the npcs...
135 map<string, TLoadedPrimitive > loadedPrimitives;
137 // First loop to remove any mission that belong to the compiled primitive file
138 for (uint i=0; i<missions.size(); ++i)
140 CMissionData &mission = *(missions[i]);
141 // first, look for the primitive file to load
142 string fileName = mission.getGiverPrimitive();
143 if (fileName.empty())
145 // use mission primitive instead
146 fileName = sourceDocName;
148 if (loadedPrimitives.find(fileName) == loadedPrimitives.end())
150 string fullFileName = CPath::lookup(fileName);
151 if (fullFileName.empty())
153 nlwarning("Can't find primitive file '%s' in path", fileName.c_str());
154 throw EParseException(NULL, "Destination primitive file not found");
156 // we need to load this primitive file.
157 CPrimitives *primDoc = new CPrimitives;
158 if (loadXmlPrimitiveFile(*primDoc, fullFileName, LigoConfig))
160 // the primitive file is loaded correctly
161 loadedPrimitives.insert(make_pair(fileName, TLoadedPrimitive(primDoc, fullFileName)));
164 throw EParseException(NULL, "Can't read primitive file");
166 TLoadedPrimitive &loadedPrim = loadedPrimitives[fileName];
167 CPrimitives *primDoc = loadedPrim.PrimDoc;
169 TPrimitiveSet scripts;
170 CPrimitiveSet<TPrimitiveClassPredicate> filter;
171 filter.buildSet(primDoc->RootNode, TPrimitiveClassPredicate("mission"), scripts);
173 // for each script, check if it was generated, and if so, check the name
174 // of the source primitive file.
175 for (uint i=0; i<scripts.size(); ++i)
177 vector<string> *script;
178 if (scripts[i]->getPropertyByName("script", script) && !script->empty())
180 // Format should be : #compiled from <source_primitive_name>
181 if (script->front().find("compiled from"))
183 // we have a compiled mission
184 if (script->front().find(sourceDocName))
186 // ok, this mission is compiled from the same primitive, remove it
187 scripts[i]->getParent()->removeChild(scripts[i]);
194 // second loop to assign compiled mission to giver npc
195 for (uint i=0; i<missions.size(); ++i)
197 CMissionData &mission = *(missions[i]);
198 string fileName = mission.getGiverPrimitive();
199 if (fileName.empty())
201 // no giver primitive file specified in the mission, use the mission primitive instead
202 fileName = sourceDocName;
205 TLoadedPrimitive &loadedPrim = loadedPrimitives[fileName];
206 CPrimitives *primDoc = loadedPrim.PrimDoc;
209 CPrimitiveSet<TPrimitiveClassAndNamePredicate> filter;
210 filter.buildSet(primDoc->RootNode, TPrimitiveClassAndNamePredicate("npc_bot", mission.getGiverName()), bots);
214 nlwarning("Can't find bot '%s' in primitive '%s' !",
215 mission.getGiverName().c_str(),
217 throw EParseException(NULL, "Can't find giver in primitive");
219 else if (bots.size() > 1)
221 nlwarning("Found more than one bot named '%s' in primitive '%s' !",
222 mission.getGiverName().c_str(),
224 throw EParseException(NULL, "More than one bot with giver name in primitive");
227 // ok, all is good, we can add the mission node to the giver
228 IPrimitive *giver = bots.front();
229 // create a new node for the mission
230 IPrimitive *script = new CPrimNode;
232 script->addPropertyByName("class", new CPropertyString("mission"));
234 script->addPropertyByName("name", new CPropertyString(mission.getMissionName()));
235 // string alias(toString("%u", makeHash32(mission.getMissionName())));
236 script->addPropertyByName("alias", new CPropertyString(mission.getAlias()));
237 string scriptLines = mission.generateMissionScript();
238 vector<string> lines;
239 explode(scriptLines, NL, lines, false);
241 script->addPropertyByName("script", new CPropertyStringArray(lines));
243 // insert the script into the giver
244 giver->insertChild(script);
247 // Save the modified primitive files
248 while (!loadedPrimitives.empty())
250 TLoadedPrimitive &loadedPrim = loadedPrimitives.begin()->second;
251 saveXmlPrimitiveFile(*(loadedPrim.PrimDoc), loadedPrim.FullFileName);
254 delete loadedPrim.PrimDoc;
256 loadedPrimitives.erase(loadedPrimitives.begin());
260 // generate the phrase file (in any)
262 string phraseFileName = CFile::getFilenameWithoutExtension(sourceDocName) + "_en.txt";
266 for (uint i=0; i<missions.size(); ++i)
268 content += missions[i]->generatePhraseFile();
270 // transform NL (\n\r) into single \n
271 content = content.replace(NL.c_str(), "\n");
273 ucs.fromUtf8(content);
275 CI18N::writeTextFile(phraseFileName, ucs, true);
279 catch (const EParseException
&e
)
281 CPrimitiveContext::instance().CurrentLigoConfig
= NULL
;
282 nlerror("Compilation error : '%s'", e
.Why
.c_str());
285 CPrimitiveContext::instance().CurrentLigoConfig
= NULL
;