552 lines
16 KiB
C++
552 lines
16 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 "nel/misc/config_file.h"
|
|
#include "nel/misc/file.h"
|
|
#include "nel/misc/o_xml.h"
|
|
#include "nel/misc/path.h"
|
|
#include "nel/misc/algo.h"
|
|
#include <string>
|
|
|
|
using namespace NLMISC;
|
|
|
|
|
|
/** This app generates sheets of all spell in ryzom
|
|
* Each spell is composed of a projectile and an impact part
|
|
* ...
|
|
*/
|
|
|
|
|
|
// write a sheet atom
|
|
static writeAtom(IStream &f, const std::string &name, const std::string &value)
|
|
{
|
|
f.xmlPushBegin("ATOM");
|
|
f.xmlSetAttrib("Name");
|
|
std::string nameNotConst = name;
|
|
f.serial(nameNotConst);
|
|
f.xmlSetAttrib("Value");
|
|
std::string valueNotConst = value;
|
|
f.serial(valueNotConst);
|
|
f.xmlPushEnd();
|
|
f.xmlPop();
|
|
}
|
|
|
|
//****************************************************************************************************************************
|
|
/**
|
|
* A set of user params
|
|
*/
|
|
class CUserParams
|
|
{
|
|
public:
|
|
enum { NumUserParams = 4 };
|
|
std::string UserParam[NumUserParams];
|
|
bool empty() const
|
|
{
|
|
for(uint k = 0; k < NumUserParams; ++k)
|
|
{
|
|
if (!UserParam[k].empty()) return false;
|
|
}
|
|
return true;
|
|
}
|
|
public:
|
|
void serial(IStream &f) throw(EStream)
|
|
{
|
|
for(uint k = 0; k < NumUserParams; ++k)
|
|
{
|
|
if (!UserParam[k].empty()) writeAtom(f, toString("UserParam%d", (int) k), UserParam[k]);
|
|
}
|
|
}
|
|
};
|
|
|
|
//****************************************************************************************************************************
|
|
/** a single fx and its parameters
|
|
*/
|
|
class CFX
|
|
{
|
|
public:
|
|
std::string PSName;
|
|
CUserParams UserParams;
|
|
public:
|
|
bool empty() const
|
|
{
|
|
return PSName.empty() && UserParams.empty();
|
|
}
|
|
void serial(IStream &f) throw(EStream)
|
|
{
|
|
if (!PSName.empty()) writeAtom(f, "PSName", PSName);
|
|
UserParams.serial(f);
|
|
}
|
|
};
|
|
|
|
//****************************************************************************************************************************
|
|
/** A set of fx
|
|
*/
|
|
class CFXSet
|
|
{
|
|
public:
|
|
enum { NumFX = 4 };
|
|
CFX FX[NumFX];
|
|
public:
|
|
bool empty() const
|
|
{
|
|
for(uint k = 0; k < NumFX; ++k)
|
|
{
|
|
if (!FX[k].empty()) return false;
|
|
}
|
|
return true;
|
|
}
|
|
void serial(IStream &f) throw(EStream)
|
|
{
|
|
for(uint k = 0; k < NumFX; ++k)
|
|
{
|
|
if (!FX[k].empty())
|
|
{
|
|
f.xmlPushBegin("STRUCT");
|
|
f.xmlSetAttrib("Name");
|
|
std::string name = toString("FX%d", (int) k).c_str();
|
|
f.serial(name);
|
|
f.xmlPushEnd();
|
|
FX[k].serial(f);
|
|
f.xmlPop();
|
|
}
|
|
}
|
|
}
|
|
void setUserParams(const CUserParams ¶ms)
|
|
{
|
|
for(uint k = 0; k < NumFX; ++k)
|
|
{
|
|
FX[k].UserParams = params;
|
|
}
|
|
}
|
|
};
|
|
|
|
//****************************************************************************************************************************
|
|
/** A spell sheet, with some args defined
|
|
*/
|
|
class CSpellSheet
|
|
{
|
|
public:
|
|
std::vector<std::string> Parents;
|
|
CFXSet Projectile;
|
|
CFXSet Impact;
|
|
public:
|
|
// write that spell as a sheet
|
|
void writeSheet(const std::string &sheetName)
|
|
{
|
|
COFile f;
|
|
if (!f.open(sheetName, false, true))
|
|
{
|
|
nlwarning("Can't write %s", sheetName.c_str());
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
COXml xmlStreamOut;
|
|
xmlStreamOut.init(&f);
|
|
xmlStreamOut.xmlPushBegin("FORM");
|
|
|
|
IStream &xmlStream = xmlStreamOut;
|
|
|
|
/*
|
|
std::string revision = "$revision$";
|
|
xmlStream.xmlSerial(revision, "Revision");
|
|
std::string state = "modified";
|
|
xmlStream.xmlSerial(state, "State");
|
|
*/
|
|
xmlStream.xmlSetAttrib("Revision");
|
|
std::string revision = "$revision$";
|
|
xmlStream.serial(revision);
|
|
xmlStream.xmlSetAttrib("State");
|
|
std::string state = "modified";
|
|
xmlStream.serial(state);
|
|
xmlStream.xmlPushEnd();
|
|
// write parent list
|
|
for(uint k = 0; k < Parents.size(); ++k)
|
|
{
|
|
xmlStream.xmlPushBegin("PARENT");
|
|
xmlStream.xmlSetAttrib("Filename");
|
|
xmlStream.serial(Parents[k]);
|
|
xmlStream.xmlPushEnd();
|
|
xmlStream.xmlPop();
|
|
}
|
|
if (!Projectile.empty() || !Impact.empty())
|
|
{
|
|
xmlStream.xmlPush("STRUCT");
|
|
if (!Projectile.empty())
|
|
{
|
|
xmlStream.xmlPushBegin("STRUCT");
|
|
xmlStream.xmlSetAttrib("Name");
|
|
std::string name = "Projectile";
|
|
xmlStream.serial(name);
|
|
xmlStream.xmlPushEnd();
|
|
Projectile.serial(xmlStream);
|
|
xmlStream.xmlPop();
|
|
}
|
|
if (!Impact.empty())
|
|
{
|
|
xmlStream.xmlPushBegin("STRUCT");
|
|
xmlStream.xmlSetAttrib("Name");
|
|
std::string name = "Impact";
|
|
xmlStream.serial(name);
|
|
xmlStream.xmlPushEnd();
|
|
Impact.serial(xmlStream);
|
|
xmlStream.xmlPop();
|
|
}
|
|
xmlStream.xmlPop();
|
|
}
|
|
else
|
|
{
|
|
xmlStream.xmlPush("STRUCT");
|
|
xmlStream.xmlPop();
|
|
}
|
|
xmlStream.xmlPop();
|
|
}
|
|
catch(const EStream &)
|
|
{
|
|
nlwarning("Cant write %s", sheetName.c_str());
|
|
}
|
|
}
|
|
};
|
|
//****************************************************************************************************************************
|
|
/** Generate list of spell
|
|
*/
|
|
static void generateSpellList(CConfigFile &cf, const std::string &sheetName)
|
|
{
|
|
CConfigFile::CVar *spellList = cf.getVarPtr("spell_list");
|
|
if (!spellList)
|
|
{
|
|
nlwarning("Can't read spell list");
|
|
return;
|
|
}
|
|
COFile f;
|
|
if (!f.open(sheetName, false, true))
|
|
{
|
|
nlwarning("Can't write %s", sheetName.c_str());
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
COXml xmlStreamOut;
|
|
xmlStreamOut.init(&f);
|
|
xmlStreamOut.xmlPush("FORM");
|
|
IStream &xmlStream = xmlStreamOut;
|
|
xmlStream.xmlPush("STRUCT");
|
|
xmlStream.xmlPushBegin("ARRAY");
|
|
xmlStream.xmlSetAttrib("Name");
|
|
std::string name = "List";
|
|
xmlStream.serial(name);
|
|
xmlStream.xmlPushEnd();
|
|
for(uint k = 0; k < (uint) spellList->size(); ++k)
|
|
{
|
|
std::vector<std::string> result;
|
|
NLMISC::splitString(spellList->asString(k), "|", result);
|
|
if (result.size() < 2)
|
|
{
|
|
nlwarning("Should provide at list spell name and id");
|
|
}
|
|
xmlStream.xmlPush("STRUCT");
|
|
writeAtom(xmlStream, "ID", result[1]);
|
|
writeAtom(xmlStream, "SheetBaseName", result[0]);
|
|
xmlStream.xmlPop();
|
|
}
|
|
xmlStream.xmlPop(); // STRUCT
|
|
xmlStream.xmlPop(); // FORM
|
|
}
|
|
catch(const EStream &)
|
|
{
|
|
nlwarning("Cant write %s", sheetName.c_str());
|
|
}
|
|
}
|
|
|
|
//****************************************************************************************************************************
|
|
int main(int argc, char* argv[])
|
|
{
|
|
if (argc < 2)
|
|
{
|
|
nlwarning("Usage : %s config_file_name.cfg", argv[0]);
|
|
return -1;
|
|
}
|
|
CConfigFile cf;
|
|
try
|
|
{
|
|
cf.load(argv[1]);
|
|
}
|
|
catch(const NLMISC::EConfigFile &)
|
|
{
|
|
nlwarning("Error in config file %s", argv[1]);
|
|
return -1;
|
|
}
|
|
catch(...)
|
|
{
|
|
nlwarning("Can't read config file %s", argv[1]);
|
|
return -1;
|
|
}
|
|
// output for sheets
|
|
std::string outputPath;
|
|
CConfigFile::CVar *outputPathVar = cf.getVarPtr("output_path");
|
|
if (outputPathVar)
|
|
{
|
|
outputPath = outputPathVar->asString() + "/";
|
|
}
|
|
// output for 'levels' parents
|
|
std::string levelParentsPath;
|
|
CConfigFile::CVar *levelParentsPathVar = cf.getVarPtr("level_parents");
|
|
if (levelParentsPathVar)
|
|
{
|
|
levelParentsPath = levelParentsPathVar->asString() + "/";
|
|
}
|
|
// output for projectile parents
|
|
std::string projectileParentsPath;
|
|
CConfigFile::CVar *projectileParentsPathVar = cf.getVarPtr("projectile_base");
|
|
if (projectileParentsPathVar)
|
|
{
|
|
projectileParentsPath= projectileParentsPathVar->asString() + "/";
|
|
}
|
|
// output for 'projectile by level and mode' parents
|
|
std::string projectileByLevelAndModeParentsPath;
|
|
CConfigFile::CVar *projectileByLevelAndModeParentsPathVar = cf.getVarPtr("projectile_by_level_and_mode_parents");
|
|
if (projectileByLevelAndModeParentsPathVar)
|
|
{
|
|
projectileByLevelAndModeParentsPath = projectileByLevelAndModeParentsPathVar->asString() + "/";
|
|
}
|
|
// output for 'base spells' parents
|
|
std::string baseSpellPath;
|
|
CConfigFile::CVar *baseSpellPathVar = cf.getVarPtr("spell_base");
|
|
if (baseSpellPathVar)
|
|
{
|
|
baseSpellPath = baseSpellPathVar->asString() + "/";
|
|
}
|
|
// output for 'spell by levels' parents
|
|
std::string spellByLevelParentsPath;
|
|
CConfigFile::CVar *spellByLevelParentsPathVar = cf.getVarPtr("spell_by_level_parents");
|
|
if (spellByLevelParentsPathVar)
|
|
{
|
|
spellByLevelParentsPath = spellByLevelParentsPathVar->asString() + "/";
|
|
}
|
|
// output for 'final spell'
|
|
std::string finalSpellPath;
|
|
CConfigFile::CVar *finalSpellPathVar = cf.getVarPtr("final_spells");
|
|
if (finalSpellPathVar)
|
|
{
|
|
finalSpellPath = finalSpellPathVar->asString() + "/";
|
|
}
|
|
|
|
|
|
|
|
// read number of levels
|
|
CConfigFile::CVar *numLevelVar = cf.getVarPtr("num_levels");
|
|
if (!numLevelVar)
|
|
{
|
|
nlwarning("Can't read number of spell levels");
|
|
return -1;
|
|
}
|
|
uint numSpellLevels = numLevelVar->asInt();
|
|
|
|
std::vector<CUserParams> userParams(numSpellLevels);
|
|
|
|
// read user params set for each level
|
|
for(uint level = 0; level < numSpellLevels; ++level)
|
|
{
|
|
std::string varName = toString("user_params_level%d", (int) (level + 1));
|
|
CConfigFile::CVar *up = cf.getVarPtr(varName);
|
|
if (!up)
|
|
{
|
|
nlwarning("Can't read var %s", varName.c_str());
|
|
}
|
|
else
|
|
{
|
|
for(uint k = 0; k < CUserParams::NumUserParams; ++k)
|
|
{
|
|
userParams[level].UserParam[k] = up->asString(k);
|
|
}
|
|
}
|
|
}
|
|
|
|
// read types of spells (offensif, curatif ...)
|
|
CConfigFile::CVar *spellTypesList = cf.getVarPtr("spell_types");
|
|
if (!spellTypesList)
|
|
{
|
|
nlwarning("Can't read types of spells");
|
|
return -1;
|
|
}
|
|
// read modes of spells
|
|
CConfigFile::CVar *spellModesList = cf.getVarPtr("spell_modes");
|
|
|
|
// read name of ps for projectiles
|
|
std::vector<std::string> projPSNames;
|
|
projPSNames.resize(spellTypesList->size() * spellModesList->size());
|
|
CConfigFile::CVar *projectileNames = cf.getVarPtr("projectile_fx");
|
|
if (projectileNames)
|
|
{
|
|
for(uint k = 0; k < (uint) projectileNames->size(); ++k)
|
|
{
|
|
// entry are expected to have the following form :
|
|
// "type|mode|fx_name.ps"
|
|
std::vector<std::string> params;
|
|
NLMISC::splitString(projectileNames->asString(k), "|", params);
|
|
if (params.size() != 3)
|
|
{
|
|
nlwarning("Bad param for projectile ps name : %s", projectileNames->asString(k).c_str());
|
|
}
|
|
else
|
|
{
|
|
bool found = false;
|
|
// find the mode
|
|
for (uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
|
|
{
|
|
if (spellModesList->asString(mode) == params[1])
|
|
{
|
|
for (uint type = 0; type < (uint) spellTypesList->size(); ++type)
|
|
{
|
|
if (spellTypesList->asString(type) == params[0])
|
|
{
|
|
projPSNames[type + mode * spellTypesList->size()] = params[2];
|
|
//nlwarning("%s : found", projectileNames->asString(k).c_str());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) break;
|
|
}
|
|
}
|
|
//if (!found) nlwarning("%s : not found", projectileNames->asString(k).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
nlinfo("Generate projectiles parent sheets...");
|
|
// gen projectiles base sheet
|
|
CSpellSheet baseProjectileSheet;
|
|
baseProjectileSheet.writeSheet(outputPath + projectileParentsPath + "_projectile_base.spell");
|
|
// gen projectiles parent sheets
|
|
for(uint type = 0; type < (uint) spellTypesList->size(); ++type)
|
|
{
|
|
for(uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
|
|
{
|
|
std::string sheetName = "_projectile_" + spellTypesList->asString(type) + "_" + spellModesList->asString(mode) + ".spell";
|
|
CSpellSheet ss;
|
|
ss.Parents.push_back("_projectile_base.spell");
|
|
// affect ps name if known
|
|
ss.Projectile.FX[0].PSName = projPSNames[type + mode * spellTypesList->size()];
|
|
ss.writeSheet(outputPath + projectileParentsPath + sheetName);
|
|
}
|
|
}
|
|
|
|
nlinfo("Generate sheets by level...");
|
|
// generate sheets by level
|
|
for(uint level = 0; level < numSpellLevels; ++level)
|
|
{
|
|
// gen projectiles by level sheets (parent sheets)
|
|
std::string sheetName = toString("_projectile_lvl%d.spell", (int) (level + 1));
|
|
CSpellSheet projectileSheet;
|
|
projectileSheet.Projectile.setUserParams(userParams[level]);
|
|
projectileSheet.writeSheet(outputPath + levelParentsPath + sheetName);
|
|
// gen impact level sheets
|
|
sheetName = toString("_impact_lvl%d.spell", (int) (level + 1));
|
|
CSpellSheet impactSheet;
|
|
impactSheet.Impact.setUserParams(userParams[level]);
|
|
impactSheet.writeSheet(outputPath + levelParentsPath + sheetName);
|
|
|
|
}
|
|
|
|
nlinfo("Generate projectile list (by mode, type and levels)...");
|
|
// generate projectile list (by mode, type and levels)
|
|
for(uint type = 0; type < (uint) spellTypesList->size(); ++type)
|
|
{
|
|
for(uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
|
|
{
|
|
for(uint level = 0; level < (uint) numSpellLevels; ++level)
|
|
{
|
|
CSpellSheet ss;
|
|
ss.Parents.resize(2);
|
|
ss.Parents[0] = toString("_projectile_lvl%d.spell", (int) (level + 1)); // inherit level
|
|
ss.Parents[1] = "_projectile_" + spellTypesList->asString(type) + "_" + spellModesList->asString(mode) + ".spell"; // inherit mode and type
|
|
std::string sheetName = "_projectile_" + spellTypesList->asString(type) + "_" + spellModesList->asString(mode) + toString("_lvl%d.spell", (int) (level + 1));
|
|
ss.writeSheet(outputPath + projectileByLevelAndModeParentsPath + sheetName);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
nlinfo("Generate spell list...");
|
|
// read list of spells
|
|
// the string format for spells is : "sheet_name|ps_name"
|
|
// the name of the particle system is optionnal
|
|
CConfigFile::CVar *spellList = cf.getVarPtr("spell_list");
|
|
if (!spellList)
|
|
{
|
|
nlwarning("Can't read spell list");
|
|
return -1;
|
|
}
|
|
for(uint k = 0; k < (uint) spellList->size(); ++k)
|
|
{
|
|
std::string spellName = spellList->asString(k);
|
|
std::vector<std::string> result;
|
|
NLMISC::splitString(spellName, "|", result);
|
|
if (result.size() < 3)
|
|
{
|
|
nlwarning("Should provide at least spell name, id and mode");
|
|
}
|
|
else
|
|
{
|
|
// generate parent sheet
|
|
CSpellSheet baseSpellSheet;
|
|
if (result.size() > 3)
|
|
{
|
|
baseSpellSheet.Impact.FX[0].PSName = result[3];
|
|
}
|
|
baseSpellSheet.writeSheet(outputPath + baseSpellPath + "_" + result[0] + ".spell");
|
|
// generate child sheet
|
|
// - by spell level
|
|
// - by spell type (chain, bomb, spray ...)
|
|
|
|
// save spells by level
|
|
for(uint level = 0; level < numSpellLevels; ++level)
|
|
{
|
|
CSpellSheet leveledSpell;
|
|
leveledSpell.Parents.resize(2);
|
|
leveledSpell.Parents[0] = "_" + result[0] + ".spell";
|
|
leveledSpell.Parents[1] = toString("_impact_lvl%d.spell", (int) (level + 1));
|
|
leveledSpell.writeSheet(outputPath + spellByLevelParentsPath + "_" + result[0] + toString("_lvl%d.spell", (int) (level + 1)));
|
|
}
|
|
|
|
// save spell with good projectile and level
|
|
for(uint level = 0; level < numSpellLevels; ++level)
|
|
{
|
|
for(uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
|
|
{
|
|
CSpellSheet finalSheet;
|
|
finalSheet.Parents.resize(2);
|
|
finalSheet.Parents[0] = "_" + result[0] + toString("_lvl%d.spell", (int) (level + 1));
|
|
finalSheet.Parents[1] = "_projectile_" + result[2] + "_" + spellModesList->asString(mode) + toString("_lvl%d.spell", (int) (level + 1));
|
|
//finalSheet.writeSheet(outputPath + finalSpellPath + result[0] + toString("_lvl%d_", (int) (level + 1)) + result[2] + "_" + spellModesList->asString(mode) + ".spell");
|
|
finalSheet.writeSheet(outputPath + finalSpellPath + result[0] + "_" + spellModesList->asString(mode) + toString("_lvl%d", (int) (level + 1)) + ".spell");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// generate spell list with their ids
|
|
CConfigFile::CVar *spellListFile = cf.getVarPtr("spell_list_file");
|
|
if (spellListFile)
|
|
{
|
|
generateSpellList(cf, outputPath + spellListFile->asString());
|
|
}
|
|
nlinfo("Done");
|
|
return 0;
|
|
}
|
|
|