/* * psnpcloader.cpp - Author: Ian Donderwinkel * * Copyright (C) 2004 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include "util/psconst.h" #include "util/serverconsole.h" #include "psnpcloader.h" #include "pscharacter.h" #include "pscharacterloader.h" #include "psnpcdialog.h" #include "pstrainerinfo.h" #include "psraceinfo.h" #include "pssectorinfo.h" #include "psmerchantinfo.h" #include "../clients.h" #include "util/psdatabase.h" #include "../psserver.h" #include "../globals.h" #include "../exchangemanager.h" #include "../cachemanager.h" #include "../../common/admin/adminmessage.h" #include #define SUPER_CLIENT_ACCOUNT 9 //----------------------------------------------------------------------------- bool psNPCLoader::LoadFromFile(csString &filename) { area = ""; CPrintf(CON_DEBUG, "Importing NPC from file: %s\n",filename.GetData()); // try to read data from the file csRef vfs = CS_QUERY_REGISTRY(psserver->GetObjectReg(), iVFS); csRef data(vfs->ReadFile (filename.GetData())); if (!data || !data->GetSize()) { CPrintf (CON_ERROR, "Error: Couldn't load file '%s'.\n", filename.GetData()); return false; } csRef xml = csPtr(new csTinyDocumentSystem); csRef doc = xml->CreateDocument(); const char* error = doc->Parse(data); if ( error ) { Error2("Error in XML: %s", error ); return false; } npcRoot = doc->GetRoot()->GetNode("npc"); if ( !npcRoot ) { CPrintf(CON_ERROR, "Error: no tag found\n"); return false; } dialogManager = new psDialogManager; npc = new psCharacter; npc->SetCharType(PSCHARACTER_TYPE_NPC); // process xml data if (!ReadBasicInfo() || !ReadLocation()) { CPrintf(CON_ERROR, "Error: Failed to load NPC data\n"); return false; } ReadDescription(); ReadTraits(); ReadStats(); ReadMoney(); ReadSkills(); ReadTrainerInfo(); ReadMerchantInfo(); ReadFactions(); ReadKnowledgeAreas(); ReadSpecificKnowledge(); ReadSpecialResponses(); // insert processed data into the database if (!WriteToDatabase()) { CPrintf(CON_ERROR, "Error: Failed to insert data into the database\n"); return false; } return true; } bool psNPCLoader::LoadDialogsFromFile(csString &filename) { // // // .. // // // .. // // // // // // .. // // // // // .. // // // .. // // // // .. // area = ""; dialogManager = new psDialogManager; CPrintf(CON_DEBUG, "Importing NPC dialogs from file: %s\n",filename.GetData()); // try to read data from the file csRef vfs = CS_QUERY_REGISTRY(psserver->GetObjectReg(), iVFS); csRef data = vfs->ReadFile (filename.GetData()); if (!data || !data->GetSize()) { CPrintf (CON_ERROR, "Error: Couldn't load file '%s'.\n", filename.GetData()); return false; } csRef xml = csPtr(new csTinyDocumentSystem); csRef doc = xml->CreateDocument(); const char* error = doc->Parse(data); if ( error ) { Error2("Error in XML: %s", error ); return false; } npcRoot = doc->GetRoot()->GetNode("npcdialogs"); if ( !npcRoot ) { csRef questNode = doc->GetRoot()->GetNode("quest"); if(!questNode) { CPrintf(CON_ERROR, "Error: no or tag found\n"); return false; } csString name(questNode->GetAttributeValue("name")); csString task(questNode->GetAttributeValue("task")); // Find a new unused quest id from the DB Result nextId(db->Select("SELECT MAX(id) FROM quests")); int questID; int masterQuestID = questID = nextId[0].GetInt(0) + 1; CPrintf(CON_DEBUG, "Importing master quest to id %i\n", masterQuestID); npcRoot = questNode->GetNode("npcdialogs"); if (!npcRoot) { CPrintf(CON_ERROR, "Error: no tag found\n"); } ReadSpecificKnowledge(questID); csString escName; csString escTask; db->Escape( escName, name.GetDataSafe() ); db->Escape( escTask, task.GetDataSafe() ); db->Command("INSERT INTO quests (id,name,task,master_quest_id,player_lockout_time,quest_lockout_time) VALUES('%i','%s','%s','0','%i','%i')", masterQuestID, escName.GetDataSafe(), escTask.GetDataSafe(), questNode->GetAttributeValueAsInt("player_lockout_time"), questNode->GetAttributeValueAsInt("quest_lockout_time")); csRef iter = questNode->GetNodes("queststep"); while(iter->HasNext()) { csRef questStepNode = iter->Next(); csString name(questStepNode->GetAttributeValue("name")); csString task(questStepNode->GetAttributeValue("task")); int minor_step_number = questStepNode->GetAttributeValueAsInt("minor_step_number"); npcRoot = questStepNode->GetNode("npcdialogs"); questID++; CPrintf(CON_DEBUG, "Importing queststep to id %i\n", questID); ReadSpecificKnowledge(questID); ReadSpecialResponses(questID); db->Command("INSERT INTO quests (id,name,task,master_quest_id,minor_step_number,player_lockout_time,quest_lockout_time) VALUES('%i','%s','%s','%i','%i','%i','%i')", questID, escName.GetData(), escTask.GetData(), masterQuestID, questStepNode->GetAttributeValueAsInt("minor_step_number"), questStepNode->GetAttributeValueAsInt("player_lockout_time"), questStepNode->GetAttributeValueAsInt("quest_lockout_time")); } } else { this->area = npcRoot->GetAttributeValue("area"); CPrintf(CON_DEBUG, "Importing area '%s' dialogs\n", area.GetDataSafe()); // process xml data ReadSpecificKnowledge(); ReadSpecialResponses(); /* // check if area is a valid knowledge area Result result(db->Select("SELECT *" "FROM npc_knowledge_areas " "WHERE area=\"%s\";", area.GetData())); if (result.Count()==0) { CPrintf(CON_ERROR, "Error: Area %s isn't a valid knowledge area\n", area.GetData()); return false; } */ } // write npc dialog(s) to database dialogManager->area = area.GetDataSafe(); dialogManager->WriteToDatabase(); return true; } bool psNPCLoader::ReadBasicInfo() { csString name, race, sex, invulnerable; float kill_exp; name = npcRoot->GetAttributeValue("name"); race = npcRoot->GetAttributeValue("race"); sex = npcRoot->GetAttributeValue("sex"); invulnerable = npcRoot->GetAttributeValue("invulnerable"); kill_exp = npcRoot->GetAttributeValueAsFloat("kill_exp"); if ( name.IsEmpty() ) { CPrintf(CON_ERROR, "Error: No name specified\n"); return false; } if ( race.IsEmpty() ) { CPrintf(CON_ERROR, "Error: No race specified\n"); return false; } if ( sex.IsEmpty() ) { CPrintf(CON_ERROR, "Error: No gender specified\n"); return false; } PSCHARACTER_GENDER gender; if (sex=='f'||sex=='F') gender = PSCHARACTER_GENDER_FEMALE; else if (sex=='m' || sex=='M') gender = PSCHARACTER_GENDER_MALE; else if (sex=='n' || sex=='N') gender = PSCHARACTER_GENDER_NONE; else { CPrintf(CON_ERROR, "Error: Unknown gender: %s\n", sex.GetData()); return false; } psRaceInfo* raceInfo = CacheManager::GetSingleton().GetRaceInfoByNameGender(race, gender); if (!raceInfo) { CPrintf(CON_ERROR, "Error: Unknown race: %s gender: %s\n",race.GetData(), sex.GetData()); return false; } npc->SetName(name); npc->SetRaceInfo(raceInfo); npc->SetImperviousToAttack( (invulnerable=="Y") ? ALWAYS_IMPERVIOUS : 0); npc->SetKillExperience((int) kill_exp); return true; } bool psNPCLoader::ReadDescription() { csRef xmlnode = npcRoot->GetNode("description"); if ( !xmlnode ) { CPrintf(CON_ERROR, "Error: no tag found\n"); return false; } csRef iter = xmlnode->GetNodes(); while (iter->HasNext()) { csRef desc = iter->Next(); if (desc->GetType() == CS_NODE_TEXT) { npc->SetDescription(desc->GetValue()); return true; } } CPrintf(CON_ERROR, "Error: description in tag found\n"); return false; } bool psNPCLoader::ReadLocation() { csRef xmlnode = npcRoot->GetNode("pos"); if ( !xmlnode ) { CPrintf(CON_ERROR, "Error: no tag found\n"); return false; } csVector3 pos, rot; csString sector; pos.x = xmlnode->GetAttributeValueAsFloat("x"); pos.y = xmlnode->GetAttributeValueAsFloat("y"); pos.z = xmlnode->GetAttributeValueAsFloat("z"); rot.x = xmlnode->GetAttributeValueAsFloat("xr"); rot.y = xmlnode->GetAttributeValueAsFloat("yr"); rot.z = xmlnode->GetAttributeValueAsFloat("zr"); sector = xmlnode->GetAttributeValue("sct"); psSectorInfo* sectorInfo = CacheManager::GetSingleton().GetSectorInfoByName(sector); if (sectorInfo==NULL) { CPrintf(CON_ERROR, "Error: Couldn't find a sector with name: %s\n",sector.GetData()); return false; } npc->SetLocationInWorld(sectorInfo, pos.x, pos.y, pos.z, rot.y); return true; } void psNPCLoader::ReadTraits() { csString beard, face, hair, skin; beard = npcRoot->GetAttributeValue("beard"); face = npcRoot->GetAttributeValue("face"); hair = npcRoot->GetAttributeValue("hair"); skin = npcRoot->GetAttributeValue("skin"); psTrait* faceTrait = CacheManager::GetSingleton().GetTraitByName(face); psTrait* hairTrait = CacheManager::GetSingleton().GetTraitByName(hair); psTrait* beardTrait = CacheManager::GetSingleton().GetTraitByName(beard); psTrait* skinTrait = CacheManager::GetSingleton().GetTraitByName(skin); if (faceTrait) npc->SetTraitForLocation(PSTRAIT_LOCATION_FACE, faceTrait); if (hairTrait) npc->SetTraitForLocation(PSTRAIT_LOCATION_HAIR_STYLE, hairTrait); if (beardTrait) npc->SetTraitForLocation(PSTRAIT_LOCATION_BEARD_STYLE, beardTrait); if (skinTrait) npc->SetTraitForLocation(PSTRAIT_LOCATION_SKIN_TONE, skinTrait); } void psNPCLoader::ReadMoney() { csRef xmlnode = npcRoot->GetNode("money"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } float circles, hexas, octas, trias; circles = xmlnode->GetAttributeValueAsFloat("circles"); hexas = xmlnode->GetAttributeValueAsFloat("hexas"); octas = xmlnode->GetAttributeValueAsFloat("octas"); trias = xmlnode->GetAttributeValueAsFloat("trias"); psMoney money((int)circles, (int)octas, (int)hexas, (int)trias); npc->SetMoney(money); } void psNPCLoader::ReadStats() { csRef xmlnode = npcRoot->GetNode("stats"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } float agility, charisma, endurance, intelligence, strength, will; agility = xmlnode->GetAttributeValueAsFloat("agi"); charisma = xmlnode->GetAttributeValueAsFloat("cha"); endurance = xmlnode->GetAttributeValueAsFloat("end"); intelligence = xmlnode->GetAttributeValueAsFloat("int"); strength = xmlnode->GetAttributeValueAsFloat("str"); will = xmlnode->GetAttributeValueAsFloat("wil"); npc->GetAttributes()->SetStat(PSITEMSTATS_STAT_AGILITY, (unsigned int)agility); npc->GetAttributes()->SetStat(PSITEMSTATS_STAT_CHARISMA, (unsigned int)charisma); npc->GetAttributes()->SetStat(PSITEMSTATS_STAT_ENDURANCE, (unsigned int)endurance); npc->GetAttributes()->SetStat(PSITEMSTATS_STAT_INTELLIGENCE, (unsigned int)intelligence); npc->GetAttributes()->SetStat(PSITEMSTATS_STAT_STRENGTH, (unsigned int)strength); npc->GetAttributes()->SetStat(PSITEMSTATS_STAT_WILL, (unsigned int)will); } void psNPCLoader::ReadKnowledgeAreas() { knowledgeAreas.Empty(); csRef xmlnode = npcRoot->GetNode("knowledgeareas"); if ( !xmlnode ) { CPrintf(CON_ERROR, "Error: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("knowledgearea"); while(iter->HasNext()) { csRef areanode = iter->Next(); csString area; int priority; area = areanode->GetAttributeValue("name"); priority = areanode->GetAttributeValueAsInt("priority"); if (area.IsEmpty() || priority==0) { CPrintf(CON_ERROR, "Error: invalid name or priority in \n"); return; } knowledgeAreas.Push(area); knowledgeAreasPriority.Push(priority); } } void psNPCLoader::ReadTrainerInfo() { trainerSkills.Empty(); csRef xmlnode = npcRoot->GetNode("trains"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("train"); while(iter->HasNext()) { csString skill; int min_rank; int max_rank; int min_faction; csRef trainnode = iter->Next(); skill = trainnode->GetAttributeValue("name"); min_rank = trainnode->GetAttributeValueAsInt("min_rank"); max_rank = trainnode->GetAttributeValueAsInt("max_rank"); min_faction = trainnode->GetAttributeValueAsInt("min_faction"); psSkillInfo* skillInfo = CacheManager::GetSingleton().GetSkillByName(skill); if (skillInfo!=NULL) { psTrainerSkill trainerSkill; trainerSkill.skill = skillInfo; trainerSkill.min_rank = min_rank; trainerSkill.max_rank = max_rank; trainerSkill.min_faction = min_faction; trainerSkills.Push(trainerSkill); } else { CPrintf(CON_WARNING, "Unknown skill for training '%s'... skipping\n",skill.GetData()); } } } void psNPCLoader::ReadSkills() { csRef xmlnode = npcRoot->GetNode("skills"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("skill"); while(iter->HasNext()) { csString skill; int value; csRef skillnode = iter->Next(); skill = skillnode->GetAttributeValue("name"); value = skillnode->GetAttributeValueAsInt("value"); psSkillInfo* skillInfo = CacheManager::GetSingleton().GetSkillByName(skill); if (skillInfo!=NULL) { npc->GetSkills()->SetSkillRank(skillInfo->id, value); } else { CPrintf(CON_WARNING, "Unknown skill '%s'... skipping\n",skill.GetData()); } } } void psNPCLoader::ReadFactions() { csRef xmlnode = npcRoot->GetNode("factions"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("faction"); while(iter->HasNext()) { csString name; int value; csRef factionnode = iter->Next(); name = factionnode->GetAttributeValue("name"); value = factionnode->GetAttributeValueAsInt("value"); // get faction id int factionID = db->SelectSingleNumber("SELECT id FROM factions WHERE faction_name='%s'",name.GetData()); csString temp; if (factionID!=-1) { if (factionStandings=="") factionStandings+= temp.Format("%i,%i",factionID,value); else factionStandings+= temp.Format(",%i,%i",factionID,value); } else { CPrintf(CON_WARNING, "Unknown faction: '%s'... skipping\n",name.GetData()); } } } void psNPCLoader::ReadSpecificKnowledge(int questID) { csRef xmlnode = npcRoot->GetNode("specificknowledge"); if ( !xmlnode ) { CPrintf(CON_ERROR, "Error: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("trigger"); while (iter->HasNext()) { csRef triggernode = iter->Next(); // Create a new trigger psTriggerBlock* trigger = new psTriggerBlock( dialogManager ); trigger->Create( triggernode, questID); dialogManager->AddTrigger( trigger ); } } void psNPCLoader::ReadSpecialResponses(int questID) { csRef xmlnode = npcRoot->GetNode("specialresponses"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("specialresponse"); while(iter->HasNext()) { csRef responseNode = iter->Next(); psSpecialResponse * special = new psSpecialResponse( responseNode, questID ); dialogManager->AddSpecialResponse( special ); } } void psNPCLoader::ReadMerchantInfo() { buys.Empty(); sells.Empty(); csRef xmlnode = npcRoot->GetNode("buys"); if ( !xmlnode ) CPrintf(CON_WARNING, "Warning: no tag found\n"); else { csRef iter = xmlnode->GetNodes("buy"); while(iter->HasNext()) { csString name; csRef buynode = iter->Next(); name = buynode->GetAttributeValue("name"); psItemCategory* itemCategory = CacheManager::GetSingleton().GetItemCategoryByName(name); if (itemCategory!=NULL) { buys.Push(itemCategory->id); } else { CPrintf(CON_WARNING, "Unknown Item Category (Buy): '%s'... skipping\n",name.GetData()); } } } xmlnode = npcRoot->GetNode("sells"); if ( !xmlnode ) CPrintf(CON_WARNING, "Warning: no tag found\n"); else { csRef iter = xmlnode->GetNodes("sell"); while(iter->HasNext()) { // read data from section csString name; csRef sellnode = iter->Next(); name = sellnode->GetAttributeValue("name"); psItemCategory* itemCategory = CacheManager::GetSingleton().GetItemCategoryByName(name); if (itemCategory!=NULL) { sells.Push(itemCategory->id); } else { CPrintf(CON_WARNING, "Unknown Item Category (Sell): '%s'... skipping\n",name.GetData()); } } } } void psNPCLoader::SetupEquipment() { // Read data from section csRef xmlnode = npcRoot->GetNode("equipment"); if ( !xmlnode ) { CPrintf(CON_WARNING, "Warning: no tag found\n"); return; } csRef iter = xmlnode->GetNodes("obj"); while(iter->HasNext()) { csString name, crafter, guild, location; int stack_count; float item_quality; int slot; csRef objNode = iter->Next(); name = objNode->GetAttributeValue("name"); stack_count = objNode->GetAttributeValueAsInt("stack_count"); item_quality = objNode->GetAttributeValueAsFloat("item_quality"); crafter = objNode->GetAttributeValue("crafter"); guild = objNode->GetAttributeValue("guild"); location = objNode->GetAttributeValue("location"); slot = objNode->GetAttributeValueAsInt("slot"); psItem* newItem = new psItem(); // Not calling Loaded(), and will not ever save psItemStats * stat = CacheManager::GetSingleton().GetBasicItemStatsByName(name); if ( !stat ) { Error2("Could not load basic item stat for %s", name.GetData() ); continue; } newItem->SetBaseStats(stat); if (stack_count != -1) newItem->SetStackCount(stack_count); if (item_quality) newItem->SetItemQuality(item_quality); if (!crafter.IsEmpty()) { unsigned int craftid=psServer::CharacterLoader.FindCharacterID((char*)crafter.GetData()); newItem->SetCrafterID(craftid); } if (!guild.IsEmpty()) { psGuildInfo* newguild=CacheManager::GetSingleton().FindGuild(guild.GetData()); newItem->SetGuildID(newguild->id); } if (location == "equipped") { if (!npc->Inventory().EquipIn(slot, newItem)) { Error2("Could not equip item %s on npc", name.GetData() ); delete newItem; continue; } } else { if ( !npc->Inventory().PutInBulk(newItem) ) { Error2("Could not put item %s on npc", name.GetData() ); CacheManager::GetSingleton().RemoveInstance(newItem); continue; } } newItem->Save(); } } bool psNPCLoader::WriteToDatabase() { // write npc character data to database psCharacterLoader charloader; charloader.NewCharacterData(SUPER_CLIENT_ACCOUNT, npc); csString name = npc->GetCharName(); int id = npc->GetCharacterID(); csString invulnerable = (npc->GetImperviousToAttack() & ALWAYS_IMPERVIOUS) ? "Y" : "N"; int kill_exp = npc->GetKillExperience(); // add a spawn rule for the npc db->Command("UPDATE characters c SET c.npc_spawn_rule=1 WHERE c.id=%i;",id); // update factions db->Command("UPDATE characters c SET c.faction_standings='%s' WHERE c.id=%i;", factionStandings.GetDataSafe(),id); // update npc_master_id db->Command("UPDATE characters c SET c.npc_master_id=%i WHERE c.id=%i;",id,id); // update invulnerable and kill_exp db->Command("UPDATE characters c SET c.npc_impervious_ind='%s', kill_exp=%i WHERE c.id=%i;",invulnerable.GetData(),kill_exp,id); int order = 0; // This can only be called after the character has been initialised. SetupEquipment(); // write knowledge areas to database for (size_t i=0;iCommand("INSERT INTO npc_knowledge_areas(player_id, area, priority) " "VALUES (%d, '%s', '%d')", id, knowledgeAreas[i].GetData(), priority ); } // write npcdialog (triggers and (special)responses) to database dialogManager->area = name; dialogManager->WriteToDatabase(); // write trainer info to database for (size_t i=0;iCommand("INSERT INTO trainer_skills(player_id, skill_id, min_rank, max_rank, min_faction) " "VALUES (%i, %i, %i, %i, %f)", id, trainerSkills[i].skill->id, trainerSkills[i].min_rank, trainerSkills[i].max_rank, trainerSkills[i].min_faction); } // write merchant info to database for (size_t i=0;iCommand("INSERT INTO merchant_item_categories VALUES(%i,%i)",id,buys[i]); for (size_t i=0;iCommand("INSERT INTO merchant_item_categories VALUES(%i,%i)",id,sells[i]); CPrintf(CON_DEBUG, "The NPC has been assigned the following id: %i\n",npc->GetCharacterID()); return true; } //----------------------------------------------------------------------------- bool psNPCLoader::SaveToFile(int id, csString &filename) { filename.Insert(0,"/this/"); psCharacterLoader loader; npc = loader.LoadCharacterData(id,false); if (!npc) { CPrintf(CON_ERROR, "Error: Couldn't load NPC with id: %i\n",id); return false; } //*npc = *character; // removed npcID = id; area = npc->name; csRef xml = csPtr(new csTinyDocumentSystem); csRef doc = xml->CreateDocument(); csRef root = doc->CreateRoot (); npcRoot = root->CreateNodeBefore(CS_NODE_ELEMENT); npcRoot->SetValue("npc"); WriteBasicInfo(); WriteDescription(); WriteLocation(); WriteStats(); WriteMoney(); WriteEquipment(); WriteFactions(); WriteSkills(); WriteMerchantInfo(); WriteTrainerInfo(); WriteKnowledgeAreas(); WriteSpecificKnowledge(); csRef vfs = CS_QUERY_REGISTRY(psserver->GetObjectReg(), iVFS); if (!vfs) return false; csString error = doc->Write(vfs, filename); if (!error.IsEmpty()) { CPrintf(CON_ERROR, "Error writing to file %s: %s\n",filename.GetData(), error.GetData()); return false; } return true; } bool psNPCLoader::SaveDialogsToFile(csString &area, csString &filename, int questid, bool quest) { filename.Insert(0,"/this/"); csRef xml = csPtr(new csTinyDocumentSystem); csRef doc = xml->CreateDocument(); csRef root = doc->CreateRoot (); if(quest) { this->area = ""; csRef questNode = root->CreateNodeBefore(CS_NODE_ELEMENT); questNode->SetValue("quest"); Result questInfo(db->Select("SELECT name, task, player_lockout_time, quest_lockout_time FROM quests WHERE id = '%i'", questid)); questNode->SetAttribute("name", questInfo[0][0]); questNode->SetAttribute("task", questInfo[0][1]); questNode->SetAttributeAsInt("player_lockout_time", questInfo[0].GetInt(2)); questNode->SetAttributeAsInt("quest_lockout_time", questInfo[0].GetInt(3)); npcRoot = questNode->CreateNodeBefore(CS_NODE_ELEMENT); npcRoot->SetValue("npcdialogs"); if(!WriteSpecificKnowledge(questid)) { CPrintf(CON_ERROR,"Error writing specific knowledge.\n"); return false; } Result questSteps(db->Select("SELECT id, name, task, minor_step_number, player_lockout_time, quest_lockout_time FROM quests WHERE master_quest_id = '%i' ORDER BY minor_step_number", questid)); for(unsigned int i = 0;i questStepNode = questNode->CreateNodeBefore(CS_NODE_ELEMENT); questStepNode->SetValue("queststep"); questStepNode->SetAttribute("name", questInfo[i][1]); questStepNode->SetAttribute("task", questInfo[i][2]); if (questSteps[i].GetInt(3)) questStepNode->SetAttributeAsInt("minor_step_number", questSteps[i].GetInt(3)); if (questSteps[i].GetInt(4)) questStepNode->SetAttributeAsInt("player_lockout_time", questInfo[i].GetInt(4)); if (questSteps[i].GetInt(5)) questStepNode->SetAttributeAsInt("quest_lockout_time", questInfo[i].GetInt(5)); npcRoot = questStepNode->CreateNodeBefore(CS_NODE_ELEMENT); npcRoot->SetValue("npcdialogs"); if (!WriteSpecificKnowledge(questSteps[i].GetInt(0))) { CPrintf(CON_ERROR,"Error writing specific knowledge.\n"); return false; } } } else // This is when exporting areas or queststeps { npcRoot = root->CreateNodeBefore(CS_NODE_ELEMENT); npcRoot->SetValue("npcdialogs"); csString temp; if (questid == -1) { npcRoot->SetAttribute("area", area); this->area = area; } else this->area = ""; if (!WriteSpecificKnowledge(questid)) { CPrintf(CON_ERROR,"Error writing specific knowledge.\n"); return false; } } csRef vfs = CS_QUERY_REGISTRY(psserver->GetObjectReg(), iVFS); if (!vfs) return false; csString error = doc->Write(vfs, filename); if (!error.IsEmpty()) { CPrintf(CON_ERROR, "Error writing to file %s: %s\n",filename.GetData(), error.GetData()); return false; } return true; } void psNPCLoader::WriteBasicInfo() { psRaceInfo* raceInfo = npc->GetRaceInfo(); csString race = raceInfo->name; csString gender; if (raceInfo->gender==PSCHARACTER_GENDER_FEMALE) gender = "f" ; else if (raceInfo->gender==PSCHARACTER_GENDER_MALE) gender = "m"; else gender = "n"; npcRoot->SetAttribute("name", npc->name.GetData()); npcRoot->SetAttribute("race", raceInfo->name.GetData()); npcRoot->SetAttribute("sex", gender); npcRoot->SetAttribute("invulnerable", (npc->GetImperviousToAttack() & ALWAYS_IMPERVIOUS) ? "Y":"N"); npcRoot->SetAttributeAsInt("kill_exp", npc->GetKillExperience()); } void psNPCLoader::WriteDescription() { csRef descNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); descNode->SetValue("description"); csRef descText = descNode->CreateNodeBefore(CS_NODE_TEXT); descText->SetValue(npc->GetDescription()); } void psNPCLoader::WriteLocation() { csRef posNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); posNode->SetValue("pos"); posNode->SetAttribute("sct", npc->location.loc_sector->name.GetData()); posNode->SetAttributeAsFloat("x", npc->location.loc_x); posNode->SetAttributeAsFloat("y", npc->location.loc_y); posNode->SetAttributeAsFloat("z", npc->location.loc_z); posNode->SetAttributeAsFloat("xr", 0.0f); posNode->SetAttributeAsFloat("yr", npc->location.loc_yrot); posNode->SetAttributeAsFloat("zr", 0.0f); } void psNPCLoader::WriteStats() { csRef statsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); statsNode->SetValue("stats"); statsNode->SetAttributeAsInt("agi", (int)npc->GetAttributes()->GetStat(PSITEMSTATS_STAT_AGILITY, false)); statsNode->SetAttributeAsInt("cha", (int)npc->GetAttributes()->GetStat(PSITEMSTATS_STAT_CHARISMA, false)); statsNode->SetAttributeAsInt("end", (int)npc->GetAttributes()->GetStat(PSITEMSTATS_STAT_ENDURANCE, false)); statsNode->SetAttributeAsInt("int", (int)npc->GetAttributes()->GetStat(PSITEMSTATS_STAT_INTELLIGENCE, false)); statsNode->SetAttributeAsInt("str", (int)npc->GetAttributes()->GetStat(PSITEMSTATS_STAT_STRENGTH, false)); statsNode->SetAttributeAsInt("wil", (int)npc->GetAttributes()->GetStat(PSITEMSTATS_STAT_WILL, false)); } void psNPCLoader::WriteMoney() { csRef moneyNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); moneyNode->SetValue("money"); moneyNode->SetAttributeAsInt("circles", npc->Money().GetCircles()); moneyNode->SetAttributeAsInt("hexas", npc->Money().GetHexas()); moneyNode->SetAttributeAsInt("octas", npc->Money().GetOctas()); moneyNode->SetAttributeAsInt("trias", npc->Money().GetTrias()); } void psNPCLoader::WriteKnowledgeAreas() { Result result(db->Select("SELECT area, priority " "FROM npc_knowledge_areas " "WHERE player_id=%i " "ORDER BY priority;", npcID)); csRef areasNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); areasNode->SetValue("knowledgeareas"); for (int i=0; i < (int)result.Count();i++) { csRef areaNode = areasNode->CreateNodeBefore(CS_NODE_ELEMENT); areaNode->SetValue("knowledgearea"); areaNode->SetAttribute("name", result[i][0]); areaNode->SetAttribute("priority", result[i][1]); } } bool psNPCLoader::WriteSpecificKnowledge(int questid) { csRef specificsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); specificsNode->SetValue("specificknowledge"); Result result; if (questid == -1) result = db->Select("SELECT DISTINCT trigger " "FROM npc_triggers " "WHERE prior_response_required=0 and area=\"%s\" and quest_id<1 " ,area.GetData()); else result = db->Select("SELECT DISTINCT trigger " "FROM npc_triggers " "WHERE prior_response_required=0 and quest_id=%i " ,questid); if (result.IsValid()) { for (int i=0;i < (int)result.Count();i++) { csString res( result[i][0] ); if (!WriteTrigger(specificsNode, res, 0, questid)) return false; } } return true; } bool psNPCLoader::WriteResponse(csRef attitudeNode, int id, int questID) { // // // // // // Result result(db->Select("SELECT response1, response2, response3, response4, response5, " "pronoun_him, pronoun_her, pronoun_it, pronoun_them, script, quest_id " "FROM npc_responses " "WHERE id=\"%i\";", id)); // Sanity check for quest_id if (questID != -1) { for (unsigned int i = 0;i < result.Count();i++) { if (result[i].GetInt(10) != questID) { CPrintf(CON_ERROR, "Sanity check failed: Response id %i does not have expected quest id %i\n", id, questID); return false; } } } csString him = result[0][5]; csString her = result[0][6]; csString it = result[0][7]; csString them = result[0][8]; if (!him.IsEmpty() || !her.IsEmpty() || !it.IsEmpty() || !them.IsEmpty()) { csRef pronounNode = attitudeNode->CreateNodeBefore(CS_NODE_ELEMENT); pronounNode->SetValue("pronoun"); if (!him.IsEmpty()) pronounNode ->SetAttribute("him", him.GetData()); if (!her.IsEmpty()) pronounNode ->SetAttribute("her", her.GetData()); if (!it.IsEmpty()) pronounNode ->SetAttribute("it", it.GetData()); if (!them.IsEmpty()) pronounNode ->SetAttribute("them", them.GetData()); } for (int i=0;i<5;i++) { if (!csString(result[0][i]).IsEmpty()) { csRef responseNode = attitudeNode->CreateNodeBefore(CS_NODE_ELEMENT); responseNode->SetValue("response"); responseNode->SetAttribute("say", result[0][i]); } } csString script = result[0][9]; if (!script.IsEmpty()) { csRef scriptNode = attitudeNode->CreateNodeBefore(CS_NODE_ELEMENT); scriptNode->SetValue("script"); csRef textNode = scriptNode->CreateNodeBefore(CS_NODE_TEXT); textNode->SetValue(script.GetData()); } return true; } bool psNPCLoader::WriteTrigger(csRef specificsNode, csString &trigger,int priorID, int questID) { // // // // // // // .. // // // Result result; Result phrases; csString escArea; csString escTrigger; db->Escape( escArea, area ); db->Escape( escTrigger, trigger ); if (questID == -1) { result = db->Select("SELECT response_id, " " min_attitude_required, " " max_attitude_required " "FROM npc_triggers " "WHERE prior_response_required=\"%i\" and trigger=\"%s\" and area=\"%s\" and quest_id<1;", priorID, escTrigger.GetData(), escArea.GetData()); // all triggers that have the same response id will be grouped together // and written to the xml file as phrases = db->Select("SELECT id, trigger " "FROM npc_triggers " "WHERE response_id=\"%i\" and area=\"%s\" and quest_id<1;", result[0].GetInt(0), escArea.GetData()); } else { result = db->Select("SELECT response_id, " " min_attitude_required, " " max_attitude_required " "FROM npc_triggers " "WHERE prior_response_required=\"%i\" and trigger=\"%s\" and quest_id=\"%i\";", priorID, escTrigger.GetData(), questID); // all triggers that have the same response id will be grouped together // and written to the xml file as phrases = db->Select("SELECT id, trigger, area " "FROM npc_triggers " "WHERE response_id=\"%i\" and quest_id=\"%i\";", result[0].GetInt(0), questID); } csString temp; csRef triggerNode; for (int i=0; i< (int)phrases.Count();i++) { int triggerID = phrases[i].GetInt(0); bool duplicate=false; for (size_t j=0;jCreateNodeBefore(CS_NODE_ELEMENT); triggerNode->SetValue("trigger"); csString area(phrases[0][2]); if (this->area.IsEmpty() && !area.IsEmpty()) triggerNode->SetAttribute("area", area); } csRef phraseNode = triggerNode->CreateNodeBefore(CS_NODE_ELEMENT); phraseNode->SetValue("phrase"); phraseNode->SetAttribute("value", phrases[i][1]); } } if (triggerNode) { for (int i=0;i<(int)result.Count();i++) { int responseID = result[i].GetInt(0); int minAttitude = result[i].GetInt(1); int maxAttitude = result[i].GetInt(2); csRef attitudeNode = triggerNode->CreateNodeBefore(CS_NODE_ELEMENT); attitudeNode->SetValue("attitude"); attitudeNode->SetAttributeAsInt("min", minAttitude); attitudeNode->SetAttributeAsInt("max", maxAttitude); if (!WriteResponse(attitudeNode, responseID, questID)) return false; // check if this trigger contains other triggers Result childResult(db->Select("SELECT DISTINCT trigger " "FROM npc_triggers " "WHERE prior_response_required=%i;" ,responseID)); if (childResult.IsValid()) { for (int j=0;j<(int)childResult.Count();j++) { csString res(childResult[j][0]); if(!WriteTrigger(attitudeNode, res, responseID, questID)) return false; } } } } return true; } void psNPCLoader::WriteFactions() { // // // // .... // csString factions = npc->GetFactionStandings(); // consider (null) and NULL as empty entries if (factions.StartsWith("(null)") || factions.StartsWith("NULL")) return; // factions are stored in the DB like this: '3,40,2,50,..' // so this needs to be converted int pos = 0; int start = 0; csString sub; csArray intList; while (pos< (int)factions.Length()) { if (factions.GetAt(pos)==',') { factions.SubString(sub,start,pos-start); intList.Push(atoi(sub)); start = ++pos; } pos++; } csRef factionsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); factionsNode->SetValue("factions"); if (factions.Length()>0) { factions.SubString(sub, start, factions.Length()-start); intList.Push(atoi(sub)); for (size_t i=0;iSelect("SELECT faction_name " "FROM factions " "WHERE id=%i;", factionID)); csRef factionNode = factionsNode->CreateNodeBefore(CS_NODE_ELEMENT); factionNode->SetValue("faction"); factionNode->SetAttribute("name", faction[0][0]); factionNode->SetAttributeAsInt("value", value); } } } void psNPCLoader::WriteMerchantInfo() { psMerchantInfo* merchantInfo = npc->GetMerchantInfo(); csRef buysNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); buysNode->SetValue("buys"); csRef sellsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); sellsNode->SetValue("sells"); if (merchantInfo) { // buys for (size_t i=0; icategories.Length();i++) { psItemCategory* itemCategory = merchantInfo->categories[i]; csRef buyNode = buysNode->CreateNodeBefore(CS_NODE_ELEMENT); buyNode->SetValue("buy"); buyNode->SetAttribute("name", itemCategory->name.GetData()); } // sells for (size_t i=0; icategories.Length();i++) { psItemCategory* itemCategory = merchantInfo->categories[i]; csRef sellNode = sellsNode->CreateNodeBefore(CS_NODE_ELEMENT); sellNode->SetValue("sell"); sellNode->SetAttribute("name", itemCategory->name.GetData()); } } } void psNPCLoader::WriteTrainerInfo() { Result trains(db->Select("SELECT s.name, t.min_rank, t.max_rank, t.min_faction " "FROM trainer_skills t, skills s " "WHERE player_id=%i and t.skill_id=s.skill_id;", npcID)); csRef trainsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); trainsNode->SetValue("trains"); if (trains.Count()>0) { for (int i=0;i< (int)trains.Count();i++) { csRef trainNode = trainsNode->CreateNodeBefore(CS_NODE_ELEMENT); trainNode->SetValue("train"); trainNode->SetAttribute("name", trains[i][0]); trainNode->SetAttribute("min_rank", trains[i][1]); trainNode->SetAttribute("max_rank", trains[i][2]); trainNode->SetAttribute("min_faction", trains[i][3]); } } } void psNPCLoader::WriteSkills() { csRef skillsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); skillsNode->SetValue("skills"); for (int i=0;iGetSkills()->GetSkillRank((PSSKILL)i); if (rank) { csRef skillNode = skillsNode->CreateNodeBefore(CS_NODE_ELEMENT); skillNode->SetValue("skill"); psSkillInfo* skillInfo = CacheManager::GetSingleton().GetSkillByID(i); skillNode->SetAttribute("name", skillInfo->name); skillNode->SetAttributeAsInt("value", rank); } } } void psNPCLoader::WriteEquipment() { csRef equipmentNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); equipmentNode->SetValue("equipment"); psItem* item; for (int bulkslot=0; bulkslot < PSCHARACTER_BULK_COUNT; bulkslot++) { item = npc->Inventory().GetBulkItem(bulkslot); if (!item) continue; WriteItem(equipmentNode, item, 1, bulkslot); } for (int charslot=0;charslotInventory().GetEquipmentItem(charslot); if (!item) continue; WriteItem(equipmentNode, item, 0, charslot); } } void psNPCLoader::WriteItem(csRef equipmentNode, psItem* item, int bulk, int slot) { csRef objNode = equipmentNode->CreateNodeBefore(CS_NODE_ELEMENT); objNode->SetValue("obj"); objNode->SetAttribute("name", item->GetName()); if (item->GetStackCount() != 1) objNode->SetAttributeAsInt("stack_count", item->GetStackCount()); if (item->GetItemQuality()) objNode->SetAttributeAsFloat("item_quality", item->GetItemQuality()); if (item->GetIsCrafterIDValid()) { Result result (db->Select("SELECT name FROM characters WHERE id='%d'",item->GetCrafterID())); if (result.IsValid()) objNode->SetAttribute("crafter", result[0][0]); } if (item->GetIsGuildIDValid()) { psGuildInfo* guild = CacheManager::GetSingleton().FindGuild(item->GetGuildID()); if (guild) objNode->SetAttribute("guild", guild->GetName()); } if (bulk==1) objNode->SetAttribute("location", "bulk"); else objNode->SetAttribute("location", "equipped"); objNode->SetAttributeAsInt("slot", slot); } //----------------------------------------------------------------------------- bool psNPCLoader::RemoveFromDatabase(int id) { // first check a character with this id exists Result result(db->Select("SELECT name " "FROM characters " "WHERE id=\"%i\" ", id)); if (result.Count()==0) { CPrintf(CON_ERROR, "Error: No character with id %i found\n",id); return false; } csString name = result[0][0]; // todo: delete the data from the database return false; }