/* * convert.cpp * This program is a utility that will convert all old monsters/objects/rooms/players * to the newest file version. Being used initially for the new combat system. * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * This software is distributed in accordance with the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell * Contributions by Tim Callahan, Jonathan Hseu * Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman * */ #include <sstream> #include <iostream> #include <string.h> #include <stdlib.h> #include <dirent.h> #include "mud.h" #include "property.h" #include "commands.h" #ifdef __CYGWIN__ /* borrowed these definitions from Apache */ #define ap_tolower(c) (tolower(((unsigned char)(c)))) #define ap_toupper(c) (toupper(((unsigned char)(c)))) static inline const char *strcasestr( const char *h, const char *n ) { /* h="haystack", n="needle" */ const char *a=h, *e=n; if( !h || !*h || !n || !*n ) { return 0; } while( *a && *e ) { if( ap_toupper(*a)!=ap_toupper(*e) ) { ++h; a=h; e=n; } else { ++a; ++e; } } return *e ? 0 : h; } #endif int rLow = 15000; int rHigh = 17058; #define SHARP 0 #define THRUST 1 #define BLUNT 2 #define POLE 3 #define MISSILE 4 #define CLEAVE 5 void convertObj(); void convertCrt(); void convertRoom(); void convertPly(); int main(int argc, char *argv[]) { // We need the config loaded for skills gConfig = Config::getInstance(); gConfig->load(); gConfig->clearProperties(); // convertObj(); // convertCrt(); convertRoom(); // convertPly(); } void convertObject(Object* toConvert); void convertMonster(Creature* toConvert); void convertRoom(Room* toConvert); void convertPly() { struct dirent *dirp=0; DIR *dir=0; if((dir = opendir(PLAYERPATH)) == NULL) { printf("Directory could not be opened.\n"); return; } bstring pName; Player* toConvert; while((dirp = readdir(dir)) != NULL){ // is this a player file? if(dirp->d_name[0] == '.') continue; if(!isupper(dirp->d_name[0])) continue; pName = dirp->d_name; pName.Replace(".xml", ""); printf("Looking to convert %s\n", pName.c_str()); if(!loadPlayer(pName.c_str(), &toConvert)) continue; if(!toConvert) continue; printf("Converting %s\n", toConvert->name); convertMonster(toConvert); toConvert->saveToFile(); free_crt(toConvert); } } void convertObj() { char filename[256]; int curNum, curFile; xmlDocPtr xmlDoc; xmlNodePtr rootNode; xmlNodePtr curNode; int num; Object* curObject = 0; // Current mob we're looking at, start at rangeLow curNum = rLow; // Current file we're looking at curFile = curNum/OFILESIZE; while(curNum <= rHigh) { sprintf(filename, "/home/realms/realms/objmon/o%02d.xml", curFile); if((xmlDoc = xml::loadFile(filename, "Objects")) == NULL) { // Ok so we can't load this file, means none of them in this range are going to be found // so jump to the next file and increase the curNum as appropriate curFile++; curNum = curFile * OFILESIZE; continue; } rootNode = xmlDocGetRootElement(xmlDoc); curNode = rootNode->children; while(curNode != NULL) { if(NODE_NAME(curNode, "Object")) { num = xml::getIntProp(curNode, "Num"); // Passed the highest mob we want to see, so quit if(num > rHigh) break; // Lower mob than we want to see, go to the next one if(num < rLow) { curNode = curNode->next; continue; } // If we got here, we probly want to see it so lets print it out and set the curNum to what we're at curNum = num; curObject = new Object; if(!curObject) abort(); curObject->info.id = num; curObject->readFromXml(curNode); // Invert the logic here, test for cases to ignore if(!strcmp(curObject->name, "...") || !strcmp(curObject->name, "none")) { // Do nothing } else { if(curObject->info.id >= 17000 && curObject->info.id < 17999) { curObject->info.setArea("tut"); curObject->info.id -= 16999; } convertObject(curObject); curObject->saveToFile(); } delete curObject; } curNode = curNode->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); // Move on to the next file curFile++; curNum = curFile * OFILESIZE; } } void convertCrt() { char filename[256]; int curNum, curFile; xmlDocPtr xmlDoc; xmlNodePtr rootNode; xmlNodePtr curNode; int num; Monster* curMonster = 0; // Current mob we're looking at, start at rangeLow curNum = rLow; // Current file we're looking at curFile = curNum/MFILESIZE; while(curNum <= rHigh) { sprintf(filename, "/home/realms/realms/objmon/m%02d.xml", curFile); if((xmlDoc = xml::loadFile(filename, "Creatures")) == NULL) { // Ok so we can't load this file, means none of them in this range are going to be found // so jump to the next file and increase the curNum as appropriate curFile++; curNum = curFile * MFILESIZE; continue; } rootNode = xmlDocGetRootElement(xmlDoc); curNode = rootNode->children; while(curNode != NULL) { if(NODE_NAME(curNode, "Creature")) { num = xml::getIntProp(curNode, "Num"); // Passed the highest mob we want to see, so quit if(num > rHigh) break; // Lower mob than we want to see, go to the next one if(num < rLow) { curNode = curNode->next; continue; } // If we got here, we probly want to see it so lets print it out and set the curNum to what we're at curNum = num; curMonster = new Monster; if(!curMonster) abort(); curMonster->info.id = num; xml::copyPropToBString(curMonster->version, curNode, "Version"); curMonster->readFromXml(curNode); if(!strcmp(curMonster->name, "...") || !strcmp(curMonster->name, "none") || !strcmp(curMonster->name, "clay form")) { // Do nothing } else { if(curMonster->info.id >= 17000 && curMonster->info.id < 17999) { curMonster->info.setArea("tut"); curMonster->info.id -= 16999; } convertMonster(curMonster); curMonster->saveToFile(); } free_crt(curMonster); } curNode = curNode->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); // Move on to the next file curFile++; curNum = curFile * MFILESIZE; } } void doConvertRoom(Room *curRoom, bool force) { Property *p=0; bstring name = ""; bool skipThisRoom = false, isShopStorage=false; // if(!curRoom->readFromXml(rootNode) != 0) { // delete curRoom; // curNum++; // continue; // }; if(!curRoom->first_mon && !curRoom->first_obj) skipThisRoom = true; std::cout << "Room:" << curRoom->info.id << "\n"; // convert all storage rooms if(curRoom->info.id > 15000 && curRoom->info.id < 15999) { curRoom->info.setArea("stor"); curRoom->info.id -= 15000; name = curRoom->name; name = name.left(name.Find("'")); p = new Property; p->setOwner(name); p->setName(curRoom->name); p->setDateFounded(); p->setLocation("any realty office"); p->addRange(curRoom->info); p->setType(PROP_STORAGE); gConfig->addProperty(p); isShopStorage = true; skipThisRoom = false; } // convert all storage rooms if(curRoom->info.id > 16000 && curRoom->info.id < 16999) { curRoom->info.setArea("shop"); curRoom->info.id -= 16000; // only create a property for the odd numbered ones if(curRoom->info.id % 2) { Room* outside=0; name = curRoom->name; name = name.left(name.Find("'")); p = new Property; p->setOwner(name); p->setName(curRoom->name); p->setDateFounded(); p->addRange(curRoom->info.area, curRoom->info.id, curRoom->info.id+1); p->setType(PROP_SHOP); if(loadRoom(curRoom->first_ext->ext->room, &outside)) { p->setLocation(outside->name); // exits need updating as well outside->first_obj=0; xtag *xp = outside->first_ext; while(xp) { if(xp->ext->room.id > 16000 && xp->ext->room.id < 16999) { xp->ext->room.setArea("shop"); xp->ext->room.id -= 16000; } xp = xp->next_tag; } outside->saveToFile(1); } gConfig->addProperty(p); } else { // update exits leading out of the shop storage room xtag *xp = curRoom->first_ext; while(xp) { if(xp->ext->room.id > 16000 && xp->ext->room.id < 16999) { xp->ext->room.setArea("shop"); xp->ext->room.id -= 16000; } xp = xp->next_tag; } } isShopStorage = true; skipThisRoom = false; } if(curRoom->info.id >= 17000 && curRoom->info.id < 17999) { curRoom->info.setArea("tut"); curRoom->info.id -= 16999; // update exits xtag *xp = curRoom->first_ext; while(xp) { if(xp->ext->room.id >= 17000 && xp->ext->room.id < 17999) { xp->ext->room.setArea("tut"); xp->ext->room.id -= 16999; } xp = xp->next_tag; } for(int i=0; i<10; i++) { //if(curRoom->perm_mon[i].cr.id >= 17000 && curRoom->perm_mon[i].cr.id < 17999) { // curRoom->perm_mon[i].cr.setArea("tut"); // curRoom->perm_mon[i].cr.id -= 16999; //} //if(curRoom->perm_obj[i].cr.id >= 17000 && curRoom->perm_obj[i].cr.id < 17999) { // curRoom->perm_obj[i].cr.setArea("tut"); // curRoom->perm_obj[i].cr.id -= 16999; //} //if(curRoom->wander.random[i].id >= 17000 && curRoom->wander.random[i].id < 17999) { // curRoom->wander.random[i].setArea("tut"); // curRoom->wander.random[i].id -= 16999; //} } skipThisRoom = false; } // don't bother updating any other objects if(!isShopStorage) curRoom->first_obj = 0; curRoom->first_mon = 0; if(!skipThisRoom) convertRoom(curRoom); curRoom->saveToFile(1); } void convertRoom() { char filename[256]; int curNum; xmlDocPtr xmlDoc; xmlNodePtr rootNode; // xmlNodePtr curNode; int j; Room* curRoom = 0; //printRoomHeader(); curNum = rLow; while(curNum <= rHigh) { sprintf(filename, "%s/r%05d.xml", ROOMPATH, curNum); if(!file_exists(filename)) { curNum++; continue; } if((xmlDoc = xml::loadFile(filename, "Room")) == NULL) { curNum++; continue; } rootNode = xmlDocGetRootElement(xmlDoc); j = xml::getIntProp(rootNode, "Num"); if(j != curNum) { std::cout << "ERROR: Room " << j << " when expecting " << curNum << ".\n"; curNum++; continue; } curRoom = new Room; curRoom->readFromXml(rootNode); doConvertRoom(curRoom, false); delete curRoom; xmlFreeDoc(xmlDoc); xmlCleanupParser(); // Move on to the next file curNum++; } gConfig->saveProperties(); } const char* oldWeaponType(int type) { switch(type) { case SHARP: return("sharp"); case THRUST: return("thrust"); case BLUNT: return("blunt"); case MISSILE: return("missile"); case POLE: return("pole"); case CLEAVE: return("cleave"); default: return("ERROR!"); } } void convertObject(Object* toConvert) { bstring newType = ""; // Convert old weapons if(toConvert->type >= SHARP && toConvert->type <= CLEAVE) { int oldType = toConvert->type; if( strcasestr(toConvert->name, "dagger") || strcasestr(toConvert->name, "dirk") || strcasestr(toConvert->name, "bone") || strcasestr(toConvert->name, "stinger") || strcasestr(toConvert->name, "fang") || strcasestr(toConvert->name, "bodkin") || strcasestr(toConvert->name, "stiletto") || strcasestr(toConvert->name, "shank") || strcasestr(toConvert->name, "teeth") || strcasestr(toConvert->name, "tooth") || strcasestr(toConvert->name, "athame") || strcasestr(toConvert->name, "knife") || strcasestr(toConvert->name, "bilbo") || strcasestr(toConvert->name, "scalpel")) { newType = "dagger"; } else if(strcasestr(toConvert->name, "rapier") || strcasestr(toConvert->name, "sabre")) { newType = "rapier"; } else if(strcasestr(toConvert->name, "blade") || strcasestr(toConvert->description.c_str(), "blade") || strcasestr(toConvert->name, "sword") || strcasestr(toConvert->name, "claymore") || strcasestr(toConvert->name, "falchion") || strcasestr(toConvert->name, "katana") || strcasestr(toConvert->name, "scimitar") || strcasestr(toConvert->name, "cutlass") || strcasestr(toConvert->name, "machete")) { if(toConvert->flagIsSet(O_TWO_HANDED)) { newType = "great-sword"; } else { newType = "sword"; } } else if(strcasestr(toConvert->name, "axe") || strcasestr(toConvert->name, "hatchet") || strcasestr(toConvert->name, "cleaver") || strcasestr(toConvert->name, "skullsplitter")) { if(toConvert->flagIsSet(O_TWO_HANDED)) { newType = "great-axe"; } else { newType = "axe"; } } else if(strcasestr(toConvert->name, "whip")) { newType = "whip"; } else if(strcasestr(toConvert->name, "hammer") || strcasestr(toConvert->name, "mallet")) { if(toConvert->flagIsSet(O_TWO_HANDED)) { newType = "great-hammer"; } else { newType = "hammer"; } } else if(strcasestr(toConvert->name, "flail") || strcasestr(toConvert->name, "mace") || strcasestr(toConvert->name, "morning star")) { if(toConvert->flagIsSet(O_TWO_HANDED)) { newType = "great-mace"; } else { newType = "mace"; } } else if(strcasestr(toConvert->name, "club") || strcasestr(toConvert->name, "cudgel")) { newType = "club"; } else if(strcasestr(toConvert->name, "staff") || strcasestr(toConvert->name, "stave")) { newType = "staff"; } else if(strcasestr(toConvert->name, "lance") || strcasestr(toConvert->name, "halberd") || strcasestr(toConvert->name, "scythe") || strcasestr(toConvert->name, "voulge") || strcasestr(toConvert->name, "javelin") || strcasestr(toConvert->name, "pole") || strcasestr(toConvert->name, "glaive") || strcasestr(toConvert->name, "guisarme") || strcasestr(toConvert->name, "bardiche") || strcasestr(toConvert->name, "naginata") || strcasestr(toConvert->name, "spetum")) { newType = "polearm"; } else if(strcasestr(toConvert->name, "trident") || strcasestr(toConvert->name, "spear") || strcasestr(toConvert->name, "harpoon")) { newType = "spear"; } else if(strcasestr(toConvert->name, "crossbow")) { newType = "crossbow"; } else if(strcasestr(toConvert->name, "bow")) { newType = "bow"; } else if(strcasestr(toConvert->name, "sling") || strcasestr(toConvert->name, "launcher") || strcasestr(toConvert->name, "dart") || strcasestr(toConvert->name, "throwing")) { newType = "thrown"; } else if(toConvert->type == MISSILE) { newType = "bow"; } else if(toConvert->type == THRUST) { newType = "dagger"; } else if(toConvert->type == BLUNT) { newType = "club"; } else if(toConvert->type == CLEAVE) { if(toConvert->flagIsSet(O_TWO_HANDED)) { newType = "great-axe"; } else { newType = "axe"; } } else if(toConvert->type == POLE) { newType = "polearm"; } else if(strcasestr(toConvert->name, "claw")) { newType = "claw"; } toConvert->delay *= 10; if(toConvert->flagIsSet(OLD_O_PROF_20)) { toConvert->clearFlag(OLD_O_PROF_20); toConvert->requiredSkill = 30; } if(toConvert->flagIsSet(OLD_O_PROF_40)) { toConvert->clearFlag(OLD_O_PROF_40); toConvert->requiredSkill = 70; } if(toConvert->flagIsSet(OLD_O_PROF_60)) { toConvert->clearFlag(OLD_O_PROF_60); toConvert->requiredSkill = 100; } if(toConvert->flagIsSet(OLD_O_PROF_80)) { toConvert->clearFlag(OLD_O_PROF_80); toConvert->requiredSkill = 140; } // Dunno: meat cleaver if(newType != "" ) { toConvert->type = WEAPON; toConvert->setWeaponType(newType); printf("Converted Weapon %s:%s from (%s) to (%s).\n", toConvert->info.str(), toConvert->name, oldWeaponType(oldType), newType.c_str()); } else { toConvert->type = WEAPON; printf("Set to weapon type but did not set subtype for %s:%s\n", toConvert->info.str(), toConvert->name); } } else if(toConvert->type == ARMOR) { // Convert old armor // Rings if(strcasestr(toConvert->name, "ring") || toConvert->wearflag == FINGER) { newType = "ring"; } // Shields else if(strcasestr(toConvert->name, "shield") || strcasestr(toConvert->name, "buckler ") || toConvert->wearflag == SHIELD) { newType = "shield"; } else if(strcasestr(toConvert->name, "plate") && !toConvert->flagIsSet(O_LIGHT_ARMOR)) { newType = "plate"; } // If it's light armor, it's leather else if(toConvert->flagIsSet(O_LIGHT_ARMOR) || ((toConvert->flagIsSet(O_SEL_ASSASSIN) || toConvert->flagIsSet(O_SEL_THIEF) || toConvert->flagIsSet(O_SEL_ROGUE) || toConvert->flagIsSet(O_SEL_VAMPIRE) || toConvert->flagIsSet(O_SEL_DRUID) || toConvert->flagIsSet(O_SEL_CLERIC)) && !toConvert->flagIsSet(O_CSEL_INVERT)) ) { newType = "leather"; } // If a mage/lich can use it, it's cloth else if((!toConvert->flagIsSet(O_NO_MAGE) && !toConvert->flagIsSet(O_LIGHT_ARMOR)) || ((toConvert->flagIsSet(O_SEL_MAGE) || toConvert->flagIsSet(O_SEL_LICH)) && !toConvert->flagIsSet(O_CSEL_INVERT))) { newType = "cloth"; } // Otherwise if it has 'chain' or 'scale' in it's name..it's chain else if(strcasestr(toConvert->name, "chain") || strcasestr(toConvert->name, "scale")){ newType = "chain"; } // Last case, plate else { newType = "plate"; } toConvert->clearFlag(O_LIGHT_ARMOR); toConvert->setArmorType(newType); toConvert->getArmor() = (int)((float)toConvert->getArmor() * 4.4); printf("Converted Armor %s:%s to (%s) New Amror: %d.\n", toConvert->info.str(), toConvert->name, newType.c_str(), toConvert->getArmor()); } // Handle objects inside otag *ot; ot = toConvert->first_obj; Object *obj; while(ot) { obj = ot->obj; convertObject(obj); ot = ot->next_tag; } } void convertMonster(Creature* toConvert) { Monster* mTarget = toConvert->getMonster(); Player* pTarget = toConvert->getPlayer(); if(pTarget) { pTarget->addSkill("defense", (pTarget->getLevel() - 1) * 10); // Give out parry & block switch(pTarget->getClass()) { // These classes get block + parry case ASSASSIN: case BARD: case FIGHTER: case RANGER: case ROGUE: case PALADIN: case DEATHKNIGHT: pTarget->addSkill("block", (pTarget->getLevel()-1)*10); // fall through for parry case THIEF: pTarget->addSkill("parry", (pTarget->getLevel()-1)*10); break; case BERSERKER: pTarget->addSkill("block", (pTarget->getLevel()-1)*10); // Zerkers are more brute force than finnese...no parry break; default: break; } // Give out armor skills here switch(pTarget->getClass()) { case BARD: case BERSERKER: case FIGHTER: case PALADIN: case DEATHKNIGHT: case RANGER: pTarget->addSkill("plate", (pTarget->getLevel()-1)*10); pTarget->addSkill("chain", (pTarget->getLevel()-1)*10); case ASSASSIN: case THIEF: case ROGUE: case VAMPIRE: case DRUID: case CLERIC: pTarget->addSkill("leather", (pTarget->getLevel()-1)*10); case MAGE: case MONK: case LICH: case WEREWOLF: default: pTarget->addSkill("cloth", (pTarget->getLevel()-1)*10); break; } // A few clerics get plate if(pTarget->getClass() == CLERIC && !pTarget->getSecondClass() && (pTarget->getDeity() == ENOCH || pTarget->getDeity() == ARES || pTarget->getDeity() == GRADIUS)) { pTarget->addSkill("plate", (pTarget->getLevel()-1)*10); pTarget->addSkill("chain", (pTarget->getLevel()-1)*10); } // Now do weapon skills // Monks only get bare-hand, wolves only get claw if(pTarget->getClass() == MONK) { // Monks will get a quest to learn staffs pTarget->addSkill("bare-hand", (pTarget->getLevel()-1)*10); } else if(pTarget->getClass() == WEREWOLF) { pTarget->addSkill("claw", (pTarget->getLevel()-1)*10); } else { int numWeapons = 0; // Everyone gets bare-handed, but at a low level pTarget->addSkill("bare-hand", ((pTarget->getLevel()-1)*10)/2); // Everyone gets a new weapon skill every title numWeapons = (int)((pTarget->getLevel()+2)/3); switch(pTarget->getClass()) { case FIGHTER: if(!pTarget->getSecondClass()) { numWeapons += 1 + (int)(pTarget->getLevel()/4); } else { // Mutli fighters get weapon skills like other fighting classes numWeapons += (int)(pTarget->getLevel()/8); } break; case BERSERKER: numWeapons += (int)(pTarget->getLevel()/6); break; case THIEF: case RANGER: case ROGUE: case BARD: case PALADIN: case DEATHKNIGHT: case ASSASSIN: numWeapons += (int)(pTarget->getLevel()/8); break; case CLERIC: case DRUID: case VAMPIRE: if(pTarget->getSecondClass()) { // Cle/Ass numWeapons += (int)(pTarget->getLevel()/8); } else { numWeapons += (int)(pTarget->getLevel()/12); } break; case MAGE: if(pTarget->getSecondClass()) { numWeapons += (int)(pTarget->getLevel()/12); } break; case DUNGEONMASTER: case CARETAKER: numWeapons = 0; break; case LICH: default: break; } pTarget->getWeaponTrains() = numWeapons; } } else { mTarget->setDefenseSkill(((mTarget->getLevel()-1) * 10) + 5); mTarget->setWeaponSkill(((mTarget->getLevel()-1) * 10) + 5); mTarget->clearFlag(OLD_M_HIRE_FLAG); mTarget->clearFlag(OLD_M_CAN_HIRE); mTarget->clearFlag(OLD_M_EXPIRES); switch(mtarget->getClass()) { case FIGHTER: case DUNGEONMASTER: mTarget->setAttackPowermTarget->strength.getCur() * 2) + (mTarget->getLevel() * 6); break; case BERSERKER: mTarget->setAttackPower(mTarget->strength.getCur() * 2) + (mTarget->getLevel() * 6); break; case PALADIN: case DEATHKNIGHT: case BARD: case WEREWOLF: case VAMPIRE: mTarget->setAttackPower(mTarget->strength.getCur() * 2) + (mTarget->getLevel() * 4); break; case RANGER: case THIEF: case ASSASSIN: case ROGUE: case MONK: mTarget->setAttackPower(mTarget->strength.getCur() + mTarget->dexterity.getCur() + (mTarget->getLevel() * 4)); break; case CLERIC: mTarget->setAttackPower(mTarget->strength.getCur() + (mTarget->getLevel() * 2)); break; case LICH: case MAGE: default: mTarget->setAttackPower(mTarget->strength.getCur()); break; } } otag *ot; ot = toConvert->first_obj; Object *obj; // Inventory while(ot) { obj = ot->obj; convertObject(obj); ot = ot->next_tag; } } void convertRoom(Room* toConvert) { // Rather than worrying about the old versions of monsters, just clear out the room // and let perms respawn // NOTE: Yes...I know this will leak memory, but I simply don't care on a one // time run program...it shouldn't be too much as to crash us. otag *ot; ot = toConvert->first_obj; Object *obj; while(ot) { obj = ot->obj; convertObject(obj); ot = ot->next_tag; } }