/* * unique.cpp * Unique item routines. * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via 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 "mud.h" #include "dm.h" #include "commands.h" #include "unique.h" #include "property.h" //********************************************************************* // UniqueObject //********************************************************************* UniqueObject::UniqueObject() { itemLimit = 0; } //********************************************************************* // load //********************************************************************* void UniqueObject::load(xmlNodePtr curNode) { xmlNodePtr childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Item")) item.load(childNode); else if(NODE_NAME(childNode, "ItemLimit")) xml::copyToNum(itemLimit, childNode); childNode = childNode->next; } } //********************************************************************* // save //********************************************************************* void UniqueObject::save(xmlNodePtr curNode) const { item.save(curNode, "Item", true); xml::saveNonZeroNum(curNode, "ItemLimit", itemLimit); } //********************************************************************* // UniqueOwner //********************************************************************* UniqueOwner::UniqueOwner() { time = 0; owner = ""; } long UniqueOwner::getTime() const { return(time); } bool UniqueOwner::is(const Player* player, const CatRef cr) const { return((!player || owner == player->name) && item == cr); } bool UniqueOwner::is(const Player* player, const Object* object) const { if(!object) return(owner == player->name); return(is(player, object->info)); } void UniqueOwner::set(const Player* player, const Object* object) { time = object->getMade() ? object->getMade() : ::time(0); owner = player->name; item = object->info; } void UniqueOwner::set(const Player* player) { owner = player->name; } //********************************************************************* // load //********************************************************************* void UniqueOwner::load(xmlNodePtr curNode) { xmlNodePtr childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Time")) xml::copyToNum(time, childNode); else if(NODE_NAME(childNode, "Item")) item.load(childNode); else if(NODE_NAME(childNode, "Owner")) xml::copyToBString(owner, childNode); childNode = childNode->next; } } //********************************************************************* // save //********************************************************************* void UniqueOwner::save(xmlNodePtr curNode) const { xml::saveNonZeroNum(curNode, "Time", time); item.save(curNode, "Item", true); xml::saveNonNullString(curNode, "Owner", owner); } //********************************************************************* // show //********************************************************************* void UniqueOwner::show(const Player* player) { bstring t = ctime(&time); t.trim(); player->printColor(" Time: ^c%s^x Owner: ^c%s^x Object: ^c%s\n", t.c_str(), owner.c_str(), item.str().c_str()); } //********************************************************************* // doRemove //********************************************************************* void UniqueOwner::doRemove(Player* player, Object* parent, Object* object, bool online, bool destroy) { if(!destroy) object->clearFlag(O_UNIQUE); else { // dont run remove, since that deletes the iterator; // just broadcast Unique::broadcastDestruction(owner, object); if(player) player->delObj(object); else if(parent) del_obj_obj(object, parent); Room* uRoom = object->parent_room->getUniqueRoom(); object->deleteFromRoom(); if(uRoom) uRoom->saveToFile(0); delete object; } if(player) { player->save(online); if(!online) free_crt(player); } } //********************************************************************* // removeUnique //********************************************************************* void UniqueOwner::removeUnique(bool destroy) { Player* player = gServer->findPlayer(owner.c_str()); otag *cop=0, *op=0; bool online=true; if(!player && loadPlayer(owner.c_str(), &player)) online = false; if(player) { op = player->first_obj; while(op) { if(Unique::isUnique(op->obj) && op->obj->info == item) { doRemove(player, 0, op->obj, online, destroy); return; } cop = op->obj->first_obj; while(cop) { if(Unique::isUnique(cop->obj) && cop->obj->info == item) { doRemove(player, op->obj, cop->obj, online, destroy); return; } cop = cop->next_tag; } op = op->next_tag; } for(int i=0; i<MAXWEAR; i++) { if( player->ready[i] && Unique::isUnique(player->ready[i]) && player->ready[i]->info == item ) { doRemove(player, 0, player->ready[i], online, destroy); return; } } if(!online) free_crt(player); } // for properties, we only have to worry about primary owners Property* p=0; std::list<Property*>::iterator it; std::list<Range>::iterator rt; Room* uRoom=0; CatRef cr; for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) { p = (*it); if( !(p->getType() == PROP_SHOP || p->getType() == PROP_STORAGE) && !p->isOwner(owner) ) continue; for(rt = p->ranges.begin() ; rt != p->ranges.end() ; rt++) { cr = (*rt).low; for(; cr.id <= (*rt).high; cr.id++) { if(!loadRoom(cr, &uRoom)) continue; op = uRoom->first_obj; while(op) { if(Unique::isUnique(op->obj) && op->obj->info == item) { doRemove(0, 0, op->obj, online, destroy); return; } cop = op->obj->first_obj; while(cop) { if(Unique::isUnique(cop->obj) && cop->obj->info == item) { doRemove(0, op->obj, cop->obj, online, destroy); return; } cop = cop->next_tag; } op = op->next_tag; } } } } } //********************************************************************* // Unique //********************************************************************* Unique::Unique() { globalLimit = playerLimit = inGame = decay = max = 0; } Unique::~Unique() { owners.clear(); } int Unique::getGlobalLimit() const { return(globalLimit); } int Unique::getPlayerLimit() const { return(playerLimit); } int Unique::getInGame() const { return(inGame); } int Unique::getDecay() const { return(decay); } int Unique::getMax() const { return(max); } void Unique::setGlobalLimit(int num) { globalLimit = num; } void Unique::setPlayerLimit(int num) { playerLimit = num; } void Unique::setDecay(int num) { decay = num; } void Unique::setMax(int num) { max = num; } //********************************************************************* // inObjectsList //********************************************************************* bool Unique::inObjectsList(const CatRef cr) const { std::list<UniqueObject>::const_iterator it; for(it = objects.begin() ; it != objects.end() ; it++) { if((*it).item == cr) return(true); } return(false); } //********************************************************************* // setObjectLimit //********************************************************************* bool Unique::setObjectLimit(const CatRef cr, int id) { std::list<UniqueObject>::iterator it; for(it = objects.begin() ; it != objects.end() ; it++) { if((*it).item == cr) { (*it).itemLimit = id; return(true); } } return(false); } //********************************************************************* // numInOwners //********************************************************************* int Unique::numInOwners(const CatRef cr) const { std::list<UniqueOwner>::const_iterator it; int i=0; for(it = owners.begin() ; it != owners.end() ; it++) { if((*it).is(0, cr)) i++; } return(i); } //********************************************************************* // numObjects //********************************************************************* int Unique::numObjects(const Player* player) const { std::list<UniqueOwner>::const_iterator it; int i=0; for(it = owners.begin() ; it != owners.end() ; it++) { if((*it).is(player, 0)) i++; } return(i); } //********************************************************************* // getUnique //********************************************************************* Unique* Config::getUnique(const Object* object) const { if(!Unique::isUnique(object) || !validObjId(object->info)) return(0); std::list<Unique*>::const_iterator it; for(it = uniques.begin() ; it != uniques.end() ; it++) { if((*it)->inObjectsList(object->info)) return(*it); } return(0); } Unique* Config::getUnique(int id) const { std::list<Unique*>::const_iterator it; int i = 1; for(it = uniques.begin() ; it != uniques.end() ; it++) { if(i == id) return(*it); i++; } return(0); } //********************************************************************* // addUnique //********************************************************************* void Config::addUnique(Unique* unique) { uniques.push_back(unique); } //********************************************************************* // clearLimited //********************************************************************* void Config::clearLimited() { Unique* unique=0; while(!uniques.empty()) { unique = uniques.front(); delete unique; uniques.pop_front(); } uniques.clear(); Lore* l=0; while(!lore.empty()) { l = lore.front(); delete l; lore.pop_front(); } lore.clear(); } //********************************************************************* // canLoad //********************************************************************* bool Unique::canLoad(const Object* object) { Unique* unique = gConfig->getUnique(object); if(!unique) return(true); return( unique->getInGame() < unique->getGlobalLimit() && unique->checkItemLimit(object->info) ); } //********************************************************************* // checkItemLimit //********************************************************************* bool Unique::checkItemLimit(const CatRef item) const { // checking itemLimit is a little more complicated std::list<UniqueObject>::const_iterator ut; for(ut = objects.begin() ; ut != objects.end() ; ut++) { if((*ut).item == item) { int i = (*ut).itemLimit; if(!i) return(true); // there IS a limit; see how many are currently in game std::list<UniqueOwner>::const_iterator it; for(it = owners.begin() ; it != owners.end() ; it++) { if((*it).is(0, item)) { i--; if(!i) return(false); } } } } return(true); } //********************************************************************* // canGet //********************************************************************* bool Unique::canGet(const Player* player, const CatRef item, bool transfer) const { if(player->isStaff()) return(true); // if we're trying to transfer the item to this player, // we need inGame <= globalLimit, not inGame < globalLimit if((inGame-transfer) >= globalLimit) return(false); if(playerLimit && numObjects(player) >= playerLimit) return(false); // no need to check itemlimit if transfering if(!transfer && !checkItemLimit(item)) return(false); return(true); } bool Unique::canGet(const Player* player, const Object* object, bool transfer) { Unique* unique = gConfig->getUnique(object); if(!player || (unique && !unique->canGet(player, object->info, transfer))) return(false); otag* op = object->first_obj; while(op) { if(!canGet(player, op->obj, transfer)) return(false); op = op->next_tag; } return(true); } //********************************************************************* // is //********************************************************************* bool Unique::is(const Object* object) { return(isUnique(object) || hasUnique(object)); } //********************************************************************* // isUnique //********************************************************************* bool Unique::isUnique(const Object* object) { return(object->getType() != MONEY && object->flagIsSet(O_UNIQUE)); } //********************************************************************* // hasUnique //********************************************************************* bool Unique::hasUnique(const Object* object) { otag *op = object->first_obj; while(op) { if(isUnique(op->obj)) return(true); op = op->next_tag; } return(false); } //********************************************************************* // remove //********************************************************************* bool Limited::remove(Player* player, const Object* object, bool save) { if(!player) return(false); bool del = Lore::remove(player, object, true); // unique object Unique* unique = gConfig->getUnique(object); if(unique) { if(!player->isStaff()) Unique::broadcastDestruction(player->name, object); del = deleteOwner(player, object, save, false) || del; } return(del); } //********************************************************************* // is //********************************************************************* bool Limited::is(const Object* object) { return(isLimited(object) || hasLimited(object)); } //********************************************************************* // isLimited //********************************************************************* bool Limited::isLimited(const Object* object) { return(Unique::isUnique(object) || Lore::isLore(object)); } //********************************************************************* // hasLimited //********************************************************************* bool Limited::hasLimited(const Object* object) { otag *op = object->first_obj; while(op) { if(Limited::isLimited(object)) return(true); op = op->next_tag; } return(false); } //********************************************************************* // deleteUniques //********************************************************************* void Config::deleteUniques(Player* player) { otag* op = player->first_obj; Object* object=0; while(op) { object = op->obj; op = op->next_tag; if(Limited::remove(player, object, false)) { player->delObj(object, false, false, true, false); delete object; } } player->checkDarkness(); // verification, for objects in shop and storage and such std::list<Unique*>::iterator it; for(it = gConfig->uniques.begin() ; it != uniques.end() ; it++) (*it)->remove(player); gConfig->saveLimited(); } //********************************************************************* // addOwner //********************************************************************* // for uniques: // 0 = not added // 1 = added // 2 = added and killMortalObjects is run // adding lore items wont return anything int Limited::addOwner(Player* player, const Object* object) { otag* op = object->first_obj; int added=0; Unique* unique=0; while(op) { unique = gConfig->getUnique(op->obj); if(unique) { if(!added) added = 1; if(unique->addOwner(player, op->obj)) added = 2; } Lore::add(player, op->obj, true); op = op->next_tag; } unique = gConfig->getUnique(object); if(unique) { if(!added) added = 1; if(unique->addOwner(player, object)) added = 2; } Lore::add(player, object); if(added) gConfig->saveLimited(); return(added); } //********************************************************************* // addOwner //********************************************************************* // true if killMortalObjects is run bool Unique::addOwner(const Player* player, const Object* object) { if(!player || player->isStaff()) return(false); UniqueOwner uo; uo.set(player, object); inGame++; owners.push_back(uo); if(inGame >= globalLimit || !checkItemLimit(object->info)) { gConfig->killMortalObjects(); return(true); } return(false); } //********************************************************************* // deleteOwner //********************************************************************* bool Limited::deleteOwner(Player* player, const Object* object, bool save, bool lore) { if(!player) return(false); otag* op = object->first_obj; bool del=false; Unique* unique=0; while(op) { unique = gConfig->getUnique(op->obj); if(unique) del = unique->deleteOwner(player, op->obj) || del; if(lore) del = Lore::remove(player, op->obj, true) || del; op = op->next_tag; } unique = gConfig->getUnique(object); if(unique) del = unique->deleteOwner(player, object) || del; if(lore) del = Lore::remove(player, object) || del; if(del && save) gConfig->saveLimited(); return(del); } //********************************************************************* // deleteOwner //********************************************************************* bool Unique::deleteOwner(const Player* player, const Object* object) { std::list<UniqueOwner>::iterator it; for(it = owners.begin() ; it != owners.end() ; it++) { if((*it).is(player, object)) { inGame--; owners.erase(it); return(true); } } return(false); } //********************************************************************* // remove //********************************************************************* void Unique::remove(const Player* player) { std::list<UniqueOwner>::iterator it; for(it = owners.begin() ; it != owners.end() ;) { if((*it).is(player, 0)) { inGame--; owners.erase(it); it = owners.begin(); continue; } it++; } } //********************************************************************* // load //********************************************************************* void Unique::load(xmlNodePtr curNode) { xmlNodePtr subNode, childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "GlobalLimit")) xml::copyToNum(globalLimit, childNode); else if(NODE_NAME(childNode, "PlayerLimit")) xml::copyToNum(playerLimit, childNode); else if(NODE_NAME(childNode, "InGame")) xml::copyToNum(inGame, childNode); else if(NODE_NAME(childNode, "Decay")) xml::copyToNum(decay, childNode); else if(NODE_NAME(childNode, "Max")) xml::copyToNum(max, childNode); else if(NODE_NAME(childNode, "Owners")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Owner")) { UniqueOwner uo; uo.load(subNode); owners.push_back(uo); } subNode = subNode->next; } } else if(NODE_NAME(childNode, "Objects")) { subNode = childNode->children; while(subNode) { if(NODE_NAME(subNode, "Object")) { UniqueObject ub; ub.load(subNode); objects.push_back(ub); } subNode = subNode->next; } } childNode = childNode->next; } } //********************************************************************* // save //********************************************************************* void Unique::save(xmlNodePtr curNode) const { xml::saveNonZeroNum(curNode, "GlobalLimit", globalLimit); xml::saveNonZeroNum(curNode, "PlayerLimit", playerLimit); xml::saveNonZeroNum(curNode, "InGame", inGame); xml::saveNonZeroNum(curNode, "Decay", decay); xml::saveNonZeroNum(curNode, "Max", max); std::list<UniqueOwner>::const_iterator it; if(!owners.empty()) { xmlNodePtr childNode = xml::newStringChild(curNode, "Owners"); for(it = owners.begin() ; it != owners.end() ; it++) { xmlNodePtr subNode = xml::newStringChild(childNode, "Owner"); (*it).save(subNode); } } std::list<UniqueObject>::const_iterator ct; if(!objects.empty()) { xmlNodePtr childNode = xml::newStringChild(curNode, "Objects"); for(ct = objects.begin() ; ct != objects.end() ; ct++) { xmlNodePtr subNode = xml::newStringChild(childNode, "Object"); (*ct).save(subNode); } } } //********************************************************************* // loadLimited //********************************************************************* bool Config::loadLimited() { xmlDocPtr xmlDoc; xmlNodePtr cur; char filename[80]; // build an XML tree from a the file sprintf(filename, "%s/limited.xml", CONFPATH); clearLimited(); xmlDoc = xml::loadFile(filename, "Limited"); if(xmlDoc == NULL) return(false); cur = xmlDocGetRootElement(xmlDoc); // First level we expect a Unique cur = cur->children; while(cur && xmlIsBlankNode(cur)) cur = cur->next; if(cur == 0) { xmlFreeDoc(xmlDoc); xmlCleanupParser(); return(false); } Unique* unique; Lore* l; while(cur != NULL) { if(NODE_NAME(cur, "Unique")) { unique = new Unique; unique->load(cur); uniques.push_back(unique); } else if(NODE_NAME(cur, "Lore")) { l = new Lore; l->load(cur); lore.push_back(l); } cur = cur->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); return(true); } //********************************************************************* // saveLimited //********************************************************************* void Config::saveLimited() const { std::list<Unique*>::const_iterator it; std::list<Lore*>::const_iterator lt; xmlDocPtr xmlDoc; xmlNodePtr rootNode, curNode; char filename[80]; xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Limited", NULL); xmlDocSetRootElement(xmlDoc, rootNode); for(it = uniques.begin() ; it != uniques.end() ; it++) { curNode = xml::newStringChild(rootNode, "Unique"); (*it)->save(curNode); } for(lt = lore.begin() ; lt != lore.end() ; lt++) { curNode = xml::newStringChild(rootNode, "Lore"); (*lt)->save(curNode); } sprintf(filename, "%s/limited.xml", CONFPATH); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); } //********************************************************************* // firstObject //********************************************************************* const CatRef Unique::firstObject() { if(!objects.size()) { CatRef cr; return(cr); } return((*objects.begin()).item); } //********************************************************************* // listLimited //********************************************************************* void Config::listLimited(const Player* player) { std::list<Unique*>::iterator it; std::list<Lore*>::iterator lt; Object* object=0; CatRef cr; int id = 1; player->printColor("^yUniques:\n"); for(it = uniques.begin() ; it != uniques.end() ; it++) { player->printColor(" Id: ^e%-2d^x InGame: ^%c%-2d^x GlobalLimit: ^c%-2d^x First Object: ", id, (*it)->getInGame() >= (*it)->getGlobalLimit() ? 'c' : 'Y', (*it)->getInGame(), (*it)->getGlobalLimit()); cr = (*it)->firstObject(); if(!cr.id) player->printColor("^cnone\n"); else { if(loadObject(cr, &object)) { player->printColor("^c%s\n", object->name); delete object; } else player->printColor("^c%s\n", cr.str().c_str()); } id++; } player->printColor("\n\n^yLore:\n"); for(lt = lore.begin() ; lt != lore.end() ; lt++) { player->printColor(" Limit: ^c%-2d^x Object: ^c%s\n", (*lt)->getLimit(), (*lt)->getInfo().str().c_str()); } player->print("\n"); } //********************************************************************* // show //********************************************************************* void Unique::show(const Player* player) { Object* object=0; CatRef cr; player->printColor("^yUnique Item Group:\n"); player->printColor(" InGame: ^c%d\n", inGame); player->printColor(" GlobalLimit: ^c%d^x PlayerLimit: ^c%d\n", globalLimit, playerLimit);; player->printColor(" Decay: ^c%d^x Max: ^c%d\n\n", decay, max); player->print(" Owners:\n"); std::list<UniqueOwner>::iterator it; for(it = owners.begin() ; it != owners.end() ; it++) { (*it).show(player); } player->print("\n"); player->print(" Objects:\n"); std::list<UniqueObject>::const_iterator ct; for(ct = objects.begin() ; ct != objects.end() ; ct++) { player->printColor(" ^c%s", (*ct).item.str().c_str()); if(loadObject((*ct).item, &object)) { player->printColor(", ^c%s", object->name); delete object; } if((*ct).itemLimit) player->printColor(", ItemLimit: ^c%d", (*ct).itemLimit); player->print("\n"); } player->print("\n"); } //********************************************************************* // addObject //********************************************************************* void Unique::addObject(CatRef cr) { UniqueObject ub; ub.item = cr; objects.push_back(ub); } //********************************************************************* // deleteUnique //********************************************************************* void Config::deleteUnique(Unique* unique) { std::list<Unique*>::iterator it; for(it = uniques.begin() ; it != uniques.end() ; it++) { if((*it) == unique) { delete unique; uniques.erase(it); return; } } } //********************************************************************* // deUnique //********************************************************************* void Unique::deUnique(CatRef cr) { std::list<UniqueObject>::iterator ct; for(ct = objects.begin() ; ct != objects.end() ; ct++) { if((*ct).item == cr) { objects.erase(ct); break; } } std::list<UniqueOwner>::iterator it; for(it = owners.begin() ; it != owners.end() ;) { if((*it).is(0, cr)) { (*it).removeUnique(false); inGame--; owners.erase(it); it = owners.begin(); continue; } it++; } if(!objects.size()) gConfig->deleteUnique(this); } //********************************************************************* // transferOwner //********************************************************************* // for uniques only, not lore void Unique::transferOwner(const Player* owner, const Player* target, const Object* object) { std::list<UniqueOwner>::iterator it; for(it = owners.begin() ; it != owners.end() ; it++) { if((*it).is(owner, object)) { (*it).set(target); gConfig->saveLimited(); return; } } } //********************************************************************* // transferOwner //********************************************************************* void Limited::transferOwner(Player* owner, Player* target, const Object* object) { if(target && target->isStaff()) { deleteOwner(owner, object); } else if(owner && owner->isStaff()) { addOwner(target, object); } else { if(Lore::isLore(object) || Lore::hasLore(object)) { Lore::remove(owner, object, true); Lore::add(target, object, true); } Unique* unique = gConfig->getUnique(object); if(unique && owner && target) unique->transferOwner(owner, target, object); } } //********************************************************************* // dmUnique //********************************************************************* int dmUnique(Player* player, cmd* cmnd) { Object* object=0; Unique* unique=0; int id=0, len=0; if(cmnd->num < 2) { cmnd->num = 2; strcpy(cmnd->str[1], "uniques"); dmHelp(player, cmnd); return(0); } // they want to look at information if(!strcmp(cmnd->str[1], "list")) { id = atoi(getFullstrText(cmnd->fullstr, 2).c_str()); if(!id) { gConfig->listLimited(player); } else { unique = gConfig->getUnique(id); if(unique) unique->show(player); else player->printColor("No unique group with id ^y%d^x was found!\n", id); } return(0); } // setting some info on a unique group if(!strcmp(cmnd->str[1], "set")) { id = atoi(getFullstrText(cmnd->fullstr, 2).c_str()); if(id) unique = gConfig->getUnique(id); if(!unique) { player->print("What unique group do you want to modify?\n"); return(0); } len = strlen(cmnd->str[2]); if(len < 1) { player->print("Modify what field?\n"); return(0); } bstring command = getFullstrText(cmnd->fullstr, 4); if(!strncmp(command.c_str(), "limit", 5)) { id = atoi(getFullstrText(cmnd->fullstr, 5).c_str()); if(id < 0) { player->print("That is not a valid number.\n"); return(0); } CatRef cr; getCatRef(getFullstrText(cmnd->fullstr, 3), &cr, 0); if(!unique->setObjectLimit(cr, id)) { player->printColor("^c%s^x is not in that unique group.\n", cr.str().c_str()); return(0); } player->printColor("ItemLimit set to ^c%d^x.\n", id); } else { id = atoi(command.c_str()); if(id < 0) { player->print("That is not a valid number.\n"); return(0); } if(!strncmp(cmnd->str[2], "globallimit", len)) { if(!id) { player->print("Global limit must be atleast 1.\n"); return(0); } unique->setGlobalLimit(id); player->printColor("GlobalLimit set to ^c%d^x.\n", unique->getGlobalLimit()); } else if(!strncmp(cmnd->str[2], "playerlimit", len)) { unique->setPlayerLimit(id); player->printColor("PlayerLimit set to ^c%d^x.\n", unique->getPlayerLimit()); } else if(!strncmp(cmnd->str[2], "decay", len)) { if(!id) { player->print("Decay must be atleast 1.\n"); return(0); } unique->setDecay(id); player->printColor("Decay set to ^c%d^x.\n", unique->getDecay()); } else if(!strncmp(cmnd->str[2], "max", len)) { unique->setMax(id); player->printColor("Max set to ^c%d^x.\n", unique->getMax()); } else { player->print("Command not understood!\n"); return(0); } } gConfig->saveLimited(); return(0); } object = findObject(player, player->first_obj, cmnd); if(!object) { player->print("You don't have that object.\n"); return(0); } unique = gConfig->getUnique(object); len = strlen(cmnd->str[2]); // they want info about this object if(len < 1) { if(unique) unique->show(player); else player->print("That object is not unique.\n"); return(0); } if(!object->info.id) { player->print("You must save that object before you can modify its unique status.\n"); return(0); } // we want to do something with this object if(!strncmp(cmnd->str[2], "add", len)) { if(unique) { player->print("That object is already part of a unique group.\n"); return(0); } id = atoi(getFullstrText(cmnd->fullstr, 3).c_str()); unique = 0; if(id) unique = gConfig->getUnique(id); if(!unique) { player->print("What unique group do you want to add this object to?\n"); return(0); } unique->addObject(object->info); object->setFlag(O_UNIQUE); player->printColor("^yThis object is now unique.\n"); } else if(!strncmp(cmnd->str[2], "create", len)) { if(unique) { player->print("That object is already part of a unique group.\n"); return(0); } unique = new Unique; unique->setGlobalLimit(1); unique->setDecay(12*30); unique->addObject(object->info); object->setFlag(O_UNIQUE); gConfig->addUnique(unique); player->printColor("^yThis object is now unique.\n"); } else if(!strncmp(cmnd->str[2], "delete", len)) { if(!unique) { player->print("This object does not belong to a unique group.\n"); return(0); } if(strcmp(cmnd->str[3], "confirm")) { player->printColor("^rAre you sure you want to remove this object's unique status?\n"); player->print("All existing objects in-game will be reflagged. This cannot be undone.\n"); player->print("Number of items in game: %d\n", unique->numInOwners(object->info)); return(0); } unique->deUnique(object->info); object->clearFlag(O_UNIQUE); player->printColor("^yThis object is no longer unique.\n"); } else { player->print("Command not understood.\n"); return(0); } dmResaveObject(player, object, true); gConfig->saveLimited(); return(0); } //********************************************************************* // Lore //********************************************************************* Lore::Lore() { limit = 0; } CatRef Lore::getInfo() const { return(info); } int Lore::getLimit() const { return(limit); } void Lore::setInfo(const CatRef cr) { info = cr; } void Lore::setLimit(int i) { limit = i; } //********************************************************************* // load //********************************************************************* void Lore::load(xmlNodePtr curNode) { xmlNodePtr childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Limit")) xml::copyToNum(limit, childNode); else if(NODE_NAME(childNode, "Info")) info.load(childNode); childNode = childNode->next; } } //********************************************************************* // save //********************************************************************* void Lore::save(xmlNodePtr curNode) const { xml::saveNonZeroNum(curNode, "Limit", limit); info.save(curNode, "Info", true); } //********************************************************************* // isLore //********************************************************************* bool Lore::isLore(const Object* object) { return(object->info.id && object->getType() != MONEY && object->flagIsSet(O_LORE)); } //********************************************************************* // hasUnique //********************************************************************* bool Lore::hasLore(const Object* object) { otag *op = object->first_obj; while(op) { if(isLore(op->obj)) return(true); op = op->next_tag; } return(false); } //********************************************************************* // canHave //********************************************************************* bool Lore::canHave(const Player* player, const CatRef cr) { if(!validObjId(cr)) return(true); int i=0, limit=1; // if we have a lore object in memory, we update the limit, otherwise // the default is 1 Lore* lore = gConfig->getLore(cr); if(lore) limit = lore->getLimit(); std::list<CatRef>::const_iterator it; for(it = player->lore.begin() ; it != player->lore.end() ; it++) { if((*it) == cr) { i++; if(i >= limit) return(false); } } return(true); } bool Lore::canHave(const Player* player, const Object* object, bool checkBagOnly) { if(player->isStaff()) return(true); if(!checkBagOnly) { if(isLore(object) && !canHave(player, object->info)) return(false); } else { otag* op = object->first_obj; while(op) { if(isLore(op->obj) && !canHave(player, op->obj->info)) return(false); op = op->next_tag; } } return(true); } bool Lore::canHave(const Player* player, const Object* object) { return(canHave(player, object, true) && canHave(player, object, false)); } //********************************************************************* // add //********************************************************************* void Lore::add(Player* player, const Object* object, bool subObjects) { if(!player) return; if(subObjects) { otag* op = object->first_obj; while(op) { add(player, op->obj, true); op = op->next_tag; } } if(isLore(object)) { player->lore.push_back(object->info); player->save(true); } } //********************************************************************* // remove //********************************************************************* bool Lore::remove(Player* player, const Object* object, bool subObjects) { if(!player) return(false); bool del=false; if(subObjects) { otag* op = object->first_obj; while(op) { del = remove(player, op->obj, true) || del; op = op->next_tag; } } if(isLore(object)) { std::list<CatRef>::iterator it; for(it = player->lore.begin() ; it != player->lore.end() ; it++) { if((*it) == object->info) { player->lore.erase(it); player->save(true); return(true); } } } return(del); } //********************************************************************* // reset //********************************************************************* void Lore::reset(Player* player, Creature* creature) { if(!creature) { player->lore.clear(); creature = player; } otag *op = creature->first_obj; while(op) { Lore::add(player, op->obj, true); op = op->next_tag; } for(int i=0; i<MAXWEAR; i++) { if(creature->ready[i]) Lore::add(player, creature->ready[i], true); } if(player != creature) return; // for properties, we only have to worry about primary owners Property* p=0; std::list<Property*>::iterator it; std::list<Range>::iterator rt; Room* uRoom=0; CatRef cr; for(it = gConfig->properties.begin() ; it != gConfig->properties.end() ; it++) { p = (*it); if( !(p->getType() == PROP_SHOP || p->getType() == PROP_STORAGE) || !p->isOwner(player->name) ) continue; for(rt = p->ranges.begin() ; rt != p->ranges.end() ; rt++) { cr = (*rt).low; for(; cr.id <= (*rt).high; cr.id++) { if(!loadRoom(cr, &uRoom)) continue; // dont include objects in main shop room if(p->getType() == PROP_SHOP && uRoom->flagIsSet(R_SHOP)) continue; op = uRoom->first_obj; while(op) { // dont include objects on floor of storage room (chests only) if(p->getType() != PROP_STORAGE || op->obj->info.isArea("stor")) Lore::add(player, op->obj, true); op = op->next_tag; } } } } creature = player->getPet(); if(creature) reset(player, creature); } //********************************************************************* // getLore //********************************************************************* Lore* Config::getLore(const CatRef cr) const { std::list<Lore*>::const_iterator it; for(it = lore.begin() ; it != lore.end() ; it++) { if((*it)->getInfo() == cr) return(*it); } return(0); } //********************************************************************* // getLore //********************************************************************* void Config::delLore(const CatRef cr) { std::list<Lore*>::iterator it; Lore* l=0; for(it = lore.begin() ; it != lore.end() ;) { if((*it)->getInfo() == cr) { l = (*it); delete l; lore.erase(it); continue; } it++; } } //********************************************************************* // getLore //********************************************************************* void Config::addLore(const CatRef cr, int i) { Lore* l = new Lore; l->setInfo(cr); l->setLimit(i); lore.push_back(l); } //********************************************************************* // uniqueDecay //********************************************************************* void Config::uniqueDecay(Player* player) { std::list<Unique*>::iterator it; long t = time(0); for(it = uniques.begin() ; it != uniques.end() ; it++) { (*it)->runDecay(t, player); } saveLimited(); } //********************************************************************* // runDecay //********************************************************************* void Unique::runDecay(long t, Player* player) { std::list<UniqueOwner>::iterator it; bool ran = false; // player is normally empty, but if set, we only need to delete // from that person // don't run the loop 2x here since chances are determined for(it = owners.begin() ; it != owners.end() ; it++) { if(!player || (*it).is(player, 0)) ran = (*it).runDecay(t, decay, max) || ran; } // if something decayed, we should update if(ran) { for(it = owners.begin() ; it != owners.end() ; ) { // time = 0 means we should destroy it now if(!(*it).getTime()) { inGame--; owners.erase(it); it = owners.begin(); continue; } it++; } } } //********************************************************************* // runDecay //********************************************************************* bool UniqueOwner::runDecay(long t, int decay, int max) { long chance=0; // decay is in game days, turn to reals seconds if(!max) { // a Boolean, so before = 0%, after = 100% chance = ((t - time) >= (decay * 48*60)) * 100; } else { chance = ((t - time) - (decay * 48*60)) * 100 / max; } if(mrand(1,100) > chance) return(false); Player* player = gServer->findPlayer(owner.c_str()); if(player && player->inCombat()) { // we don't want to be removed just yet player->setFlag(P_UNIQUE_TO_DECAY); time = 1; } else { // this signals we want to be deleted time = 0; removeUnique(true); } return(true); } void Unique::broadcastDestruction(const bstring owner, const Object* object) { Player* player = gServer->findPlayer(owner.c_str()); if(player) player->printColor("^yThe %s^y vanishes!\n", object->name); broadcast("^y### Tragically, %s's unique item %s^y just broke!", owner.c_str(), object->name); }