#include "stdpch.h" #include "ais_user_models.h" #include "ai_instance.h" #include "ais_actions.h" #include "nel/misc/sstring.h" #include "nel/misc/algo.h" using namespace std; using namespace NLMISC; using namespace NLNET; using namespace AITYPES; using namespace CAISActionEnums; /** * This manager is used by the dynamic creature system, allowing leveldesigners to modify a npc's basesheet attributes, * or to define custom loot tables. * First, the primitive is parsed in order to find usermodels and/or custom loot tables (see primitive_parser.cpp, * methods: parsePrimCustomLootTable, parsePrimUserModelList). * The datas are stored in a specific class (CSCriptData for userModels, CCustomLootTableManager for clt). These classes * are defined in game_share so that they can be re-used egs side. * The datas are then sent to EGS, trough 2 AIActions "USR_MDL" for user models and "CUSTOMLT" for custom loot tables * The real objects CDynamicSheet and CLootTables are built egs-side by the egs CDynamicSheetManager. */ CAIUserModelManager* CAIUserModelManager::_instance = 0; /************************************************************************/ /* UTILS */ /************************************************************************/ void stripWhitespaces(std::string& str) { if(str.empty()) return; string::size_type startIndex = str.find_first_not_of(" "); string::size_type endIndex = str.find_last_not_of(" "); std::string tmp = str; str.erase(); str = tmp.substr(startIndex, (endIndex-startIndex+ 1) ); } std::string removeComment(const std::string &str) { string::size_type newPos= str.find("//",0); if (newPos != string::npos) { if (newPos == 0) return ""; else return str.substr(0, newPos); } return str; } /************************************************************************/ /* AI_ACTIONS */ /************************************************************************/ /** * used to send usermodels to EGS */ DEFINE_ACTION(ContextGlobal,USR_MDL) { //not true anymore : primAlias also pushed in the vector /*if (args.size() % 3 != 0) return;*/ TScripts scriptMap; std::vector::const_iterator it = args.begin(); uint32 primAlias; for (it = args.begin(); it != args.end();++it) { if (it == args.begin()) { if (it->get(primAlias) == false) { return; } continue; } std::string modelId(it->toString()); std::string sheetId((++it)->toString()); std::string script((++it)->toString()); //will contain all script info and the base sheet id TScriptContent scriptContent; //first push base sheet Id scriptContent.push_back(sheetId); //build a temporary vector. Each element of the vector is a script line std::vector tmpVector; splitString(script, "\n", tmpVector); std::vector::iterator tmpIt; for (tmpIt = tmpVector.begin(); tmpIt != tmpVector.end(); ++tmpIt) { //remove comment from the line and push back the line only if the line wasn't just a comment std::string noComment = removeComment(*tmpIt); stripWhitespaces(noComment); if (noComment != "") scriptContent.push_back(noComment); } //add the usermodel to the manager's map CAIUserModelManager::getInstance()->addToUserModels(primAlias, modelId, scriptContent); } CContextStack::setContext(ContextGlobal); //send msg to EGS if EGS up if (EGSHasMirrorReady) { CAIUserModelManager::getInstance()->sendUserModels(); } } /** * used to send custom loot tables */ DEFINE_ACTION(ContextGlobal,CUSTOMLT) { std::vector::const_iterator it = args.begin();; uint32 tablesNb; if (it->get(tablesNb) == false) { nlwarning("Unable to retrieve number of parsed loot tables, abort."); return; } nldebug("Number of parsed loot tables: %u", tablesNb); ++it; uint32 primAlias; if (it->get(primAlias) == false) { nlwarning("Unable to retrieve primitive alias, abort."); return; } TCustomLootTable customLootTables; //for each loot table for (uint i = 0; i < tablesNb; ++i) { ++it; //gets number of loot sets in a loot table uint32 lootSetsNb; if (it->get(lootSetsNb) == false) { return; } //then gets all info relative to the loot table (id, money values) std::string lootTableId((++it)->toString()); float moneyProba; if ((++it)->get(moneyProba) == false) { nlwarning(" unable to get moneyDropProbability"); return; } if (moneyProba < 0.0 || moneyProba > 1.0) { nlwarning(" invalid moneyDropProbability, don't send custom loot tables info to EGS"); return; } float moneyFactor; if ((++it)->get(moneyFactor) == false) { nlwarning(" unable to get moneyFactor"); return; } if (moneyFactor < 0.0) { nlwarning(" invalid moneyFactor, don't send custom loot tables info to EGS"); return; } uint32 moneyBase; if ((++it)->get(moneyBase) == false) { nlwarning(" unable to get moneyBase"); return; } if (moneyBase < 0) { nlwarning(" invalid moneyBase, don't send custom loot tables info to EGS"); return; } TScripts lootSets; //for each loot set inside the current table for (uint j = 0; j < lootSetsNb; ++j) { std::string dropProba((++it)->toString()); std::string script((++it)->toString()); TScriptContent lootSetContent; std::vector tmpVector; splitString(script, "\n", tmpVector); std::vector::iterator tmpIt; for (tmpIt = tmpVector.begin(); tmpIt != tmpVector.end(); ++tmpIt) { std::string noComment = removeComment(*tmpIt); stripWhitespaces(noComment); if (noComment != "") lootSetContent.push_back(noComment); } uint16 proba = static_cast(atoi(dropProba.c_str())); // FIXME: test on proba value... if (proba == 0 || proba > 100) { nlwarning(" invalid drop proba value for lootset %i in custom loot table '%s', skip the lootset", j + 1, lootTableId.c_str() ); continue; } lootSets.insert(make_pair(CCustomElementId(0, dropProba), lootSetContent)); } if (lootSets.empty()) { nlwarning(" Don't add empty lootsets to customloot table, skip loot table '%s'.", lootTableId.c_str()); continue; } nldebug(" Adding table '%s' to manager with primAlias '%u'", lootTableId.c_str(), primAlias); CAIUserModelManager::getInstance()->addCustomLootTable(primAlias, lootTableId,lootSets, moneyProba, moneyFactor, moneyBase); } CContextStack::setContext(ContextGlobal); //send msg to EGS if EGS up if (EGSHasMirrorReady) { CAIUserModelManager::getInstance()->sendCustomLootTables(); } } CAIUserModelManager *CAIUserModelManager::getInstance() { if (_instance==0) { _instance = new CAIUserModelManager(); _instance->init(); } return _instance; } void CAIUserModelManager::destroyInstance() { delete _instance; _instance = 0; } CAIUserModelManager::CAIUserModelManager() { } CAIUserModelManager::~CAIUserModelManager() { } void CAIUserModelManager::init() { nldebug(" Manager initialized."); } void CAIUserModelManager::addToUserModels(uint32 primAlias, const std::string &userModelId, const TScriptContent &userModel) { bool inserted = _UserModels.Scripts.insert(make_pair(CCustomElementId(primAlias, userModelId), userModel)).second; if (!inserted) { nlwarning(" tried to register twice the same user model with id: '%s'", userModelId.c_str()); return; } nldebug(" Added usermodel '%s' with alias '%u'", userModelId.c_str(), primAlias); } void CAIUserModelManager::sendUserModels() { NLNET::CMessage msgout("USER_MODELS"); const CScriptData &scriptData = CAIUserModelManager::getInstance()->getUserModels(); msgout.serial(const_cast(scriptData)); nldebug(" Sending %u user models to EGS", scriptData.Scripts.size()); sendMessageViaMirror("EGS", msgout); } bool CAIUserModelManager::isUserModel(uint32 primAlias, const std::string &userModelId) const { CCustomElementId id(primAlias, userModelId); return _UserModels.Scripts.find(id) != _UserModels.Scripts.end(); } bool CAIUserModelManager::isCustomLootTable(uint32 primAlias, const std::string &lootTableId) const { CCustomElementId id(primAlias, lootTableId); return _CustomLootTables.Tables.find(id) != _CustomLootTables.Tables.end(); } void CAIUserModelManager::sendCustomLootTables() { nldebug(" Sending custom loot tables to EGS"); NLNET::CMessage msgout("CUSTOM_LOOT_TABLES"); CCustomLootTableManager customLootTable = CAIUserModelManager::getInstance()->getCustomLootTables(); msgout.serial(customLootTable); sendMessageViaMirror("EGS", msgout); } void CAIUserModelManager::addCustomLootTable(uint32 primAlias, const std::string &tableId, TScripts lootSets, float moneyProba, float moneyFactor, uint32 moneyBase ) { if (CAIUserModelManager::getInstance()->isCustomLootTable(primAlias, tableId) == true) { nlwarning(" Table '%s' with primAlias '%u' is already present in manager. Skipping it.", tableId.c_str(), primAlias); return; } CScriptData tableContent; tableContent.Scripts = lootSets; CCustomLootTable customLootTable; customLootTable.LootSets = tableContent; customLootTable.MoneyProba = moneyProba; customLootTable.MoneyFactor = moneyFactor; customLootTable.MoneyBase = moneyBase; _CustomLootTables.Tables.insert(make_pair(CCustomElementId(primAlias, tableId), customLootTable)); } void CAIUserModelManager::deleteCustomDataByPrimAlias(uint32 primAlias) { CCustomElementId idLow(primAlias, ""); CCustomElementId idHigh(primAlias + 1, ""); TScripts::iterator upperBound = _UserModels.Scripts.upper_bound(idLow); TScripts::iterator lowerBound = _UserModels.Scripts.lower_bound(idHigh); if (upperBound != _UserModels.Scripts.end()) _UserModels.Scripts.erase(upperBound, lowerBound); TCustomLootTable::iterator upperBoundLootTables = _CustomLootTables.Tables.upper_bound(idLow); TCustomLootTable::iterator lowerBoundLootTables = _CustomLootTables.Tables.lower_bound(idHigh); if (upperBoundLootTables != _CustomLootTables.Tables.end()) _CustomLootTables.Tables.erase(upperBoundLootTables, lowerBoundLootTables); if (EGSHasMirrorReady) { nldebug(" Sending alias '%u' to EGS for data deletion", primAlias); NLNET::CMessage msgout("DELCUSTOM"); msgout.serial(primAlias); sendMessageViaMirror("EGS", msgout); } }