/* * magic.cpp * Additional spell-casting 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 "magic.h" #include "commands.h" //********************************************************************* // SpellInfo //********************************************************************* int dmSpellList(Player* player, cmd* cmnd) { const SpellInfo* spell=0; std::map<bstring, SpellInfo>::const_iterator it; player->printColor("^YSpells\n"); for(it = gConfig->spells.begin() ; it != gConfig->spells.end() ; it++) { spell = &(*it).second; player->printColor(" %s %s %s\n Script: ^y%s^x\n", spell->id.c_str(), spell->name.c_str(), spell->file.c_str(), spell->script.c_str()); } return(0); } //********************************************************************* // SpellInfo //********************************************************************* SpellInfo::SpellInfo() { id = ""; name = ""; file = ""; script = ""; } //********************************************************************* // load //********************************************************************* void SpellInfo::load(xmlNodePtr rootNode) { xmlNodePtr curNode = rootNode->children; while(curNode) { if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode); else if(NODE_NAME(curNode, "File")) xml::copyToBString(file, curNode); curNode = curNode->next; } } //********************************************************************* // loadScript //********************************************************************* bool SpellInfo::loadScript() { xmlDocPtr xmlDoc; xmlNodePtr curNode; char filename[256]; if(file == "") return (false); sprintf(filename, "%s%s", SPELLPATH, file.c_str()); xmlDoc = xml::loadFile(filename, "Spell"); if(xmlDoc == NULL) return (false); curNode = xmlDocGetRootElement(xmlDoc); curNode = curNode->children; while(curNode && xmlIsBlankNode(curNode)) curNode = curNode->next; if(curNode == 0) { xmlFreeDoc(xmlDoc); return (false); } script = ""; while(curNode != NULL) { if(NODE_NAME(curNode, "Script")) xml::copyToBString(script, curNode); curNode = curNode->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); return (true); } //********************************************************************* // save //********************************************************************* void SpellInfo::save(xmlNodePtr rootNode) const { xml::newNumProp(rootNode, "Id", id); xml::saveNonNullString(rootNode, "Name", name); xml::saveNonNullString(rootNode, "File", file); } //********************************************************************* // loadSpellList //********************************************************************* bool Config::loadSpellList() { xmlDocPtr xmlDoc; xmlNodePtr curNode; xmlDoc = xml::loadFile(CONFPATH "spelllist.xml", "Spells"); if(xmlDoc == NULL) return(false); curNode = xmlDocGetRootElement(xmlDoc); curNode = curNode->children; while(curNode && xmlIsBlankNode(curNode)) curNode = curNode->next; if(curNode == 0) { xmlFreeDoc(xmlDoc); return(false); } clearSpells(); bstring id = ""; while(curNode != NULL) { if(NODE_NAME(curNode, "Spell")) { xml::copyPropToBString(id, curNode, "id"); if(id != "") { SpellInfo spell; spell.load(curNode); spell.id = id; spell.loadScript(); spells[id] = spell; } } curNode = curNode->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); return(true); } //********************************************************************** // saveSpellList //********************************************************************** bool Config::saveSpellList() const { std::map<bstring, SpellInfo>::const_iterator it; xmlDocPtr xmlDoc; xmlNodePtr rootNode; char filename[80]; xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Spells", NULL); xmlDocSetRootElement(xmlDoc, rootNode); for(it = spells.begin() ; it != spells.end() ; it++) (*it).second.save(rootNode); sprintf(filename, "%s/spelllist.xml", CONFPATH); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(true); } //********************************************************************* // clearSpells //********************************************************************* void Config::clearSpells() { spells.clear(); } //********************************************************************* // getSpell //********************************************************************* const SpellInfo *Config::getSpell(bstring id) const { std::map<bstring, SpellInfo>::const_iterator it = spells.find(id); if(it != spells.end()) return(&(*it).second); return(0); } //********************************************************************* // Anchor //********************************************************************* Anchor::Anchor() { reset(); } Anchor::Anchor(bstring a, const Player* player) { reset(); alias = a; bind(player); } Anchor::~Anchor() { if(mapmarker) delete mapmarker; } CatRef Anchor::getRoom() const { return(room); } void Anchor::setRoom(CatRef r) { room = r; } const MapMarker* Anchor::getMapMarker() const { return(mapmarker); } bstring Anchor::getAlias() const { return(alias); } bstring Anchor::getRoomName() const { return(roomName); } //********************************************************************* // reset //********************************************************************* void Anchor::reset() { alias = roomName = ""; mapmarker = 0; } //********************************************************************* // bind //********************************************************************* void Anchor::bind(const Player* player) { if(player->parent_rom) bind(player->parent_rom); else bind(player->area_room); } void Anchor::bind(const Room* uRoom) { roomName = uRoom->name; room = uRoom->info; } void Anchor::bind(const AreaRoom* aRoom) { roomName = aRoom->area->getTile( aRoom->area->getTerrain(0, &aRoom->mapmarker, 0, 0, 0, true), false )->getName().c_str(); if(mapmarker) delete mapmarker; mapmarker = new MapMarker; *mapmarker = *&aRoom->mapmarker; } //********************************************************************* // is //********************************************************************* bool Anchor::is(const BaseRoom* room) const { const Room* uRoom = room->getConstUniqueRoom(); if(uRoom) return(is(uRoom)); else return(is(room->getConstAreaRoom())); } bool Anchor::is(const Player* player) const { if(player->parent_rom) return(is(player->parent_rom)); else return(is(player->area_room)); } bool Anchor::is(const Room* uRoom) const { return(room.id && room == uRoom->info); } bool Anchor::is(const AreaRoom* aRoom) const { return(mapmarker && *mapmarker == *&aRoom->mapmarker); } //********************************************************************* // operator= //********************************************************************* Anchor& Anchor::operator=(const Anchor& a) { alias = a.alias; roomName = a.roomName; room = a.room; if(a.mapmarker) { if(mapmarker) delete mapmarker; mapmarker = new MapMarker; *mapmarker = *a.mapmarker; } return(*this); } //********************************************************************* // load //********************************************************************* void Anchor::load(xmlNodePtr curNode) { xmlNodePtr childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Alias")) xml::copyToBString(alias, childNode); else if(NODE_NAME(childNode, "RoomName")) xml::copyToBString(roomName, childNode); else if(NODE_NAME(childNode, "Room")) room.load(childNode); else if(NODE_NAME(childNode, "MapMarker")) { mapmarker = new MapMarker; mapmarker->load(childNode); } childNode = childNode->next; } } //********************************************************************* // save //********************************************************************* void Anchor::save(xmlNodePtr curNode) const { xml::newStringChild(curNode, "Alias", alias); xml::newStringChild(curNode, "RoomName", roomName); room.save(curNode, "Room", false); if(mapmarker) { xmlNodePtr childNode = xml::newStringChild(curNode, "MapMarker"); mapmarker->save(childNode); } } //********************************************************************* // spellSkill //********************************************************************* bstring spellSkill(SchoolOfMagic school) { switch(school) { case ABJURATION: return("abjuration"); case CONJURATION: return("conjuration"); case DIVINATION: return("divination"); case ENCHANTMENT: return("enchantment"); case EVOCATION: return("evocation"); case ILLUSION: return("illusion"); case NECROMANCY: return("necromancy"); case TRANSLOCATION: return("translocation"); case TRANSMUTATION: return("transmutation"); default: return(""); } } //********************************************************************* // isPureCaster //********************************************************************* bool Creature::isPureCaster() const { int second = isPlayer() ? getConstPlayer()->getSecondClass() : 0; return( (cClass == MAGE && !second) || cClass == LICH || (cClass == CLERIC && !second) || cClass == DRUID ); } //********************************************************************* // isHybridCaster //********************************************************************* bool Creature::isHybridCaster() const { int second = isPlayer() ? getConstPlayer()->getSecondClass() : 0; return( cClass == BARD || cClass == DEATHKNIGHT || cClass == PALADIN || cClass == RANGER || cClass == VAMPIRE || second == MAGE || (cClass == MAGE && second) || (cClass == CLERIC && second) ); } //********************************************************************* // SpellData //********************************************************************* SpellData::SpellData() { splno = how = 0; level = 0; object = 0; school = NO_SCHOOL; skill = ""; } //********************************************************************* // set //********************************************************************* void SpellData::set(int h, SchoolOfMagic s, Object* obj, const Creature* caster) { how = h; school = s; object = obj; skill = spellSkill(school); level = caster->getLevel(); /* * Start small: don't do skill-based levels yet if(skill == "") { level = caster->getLevel(); } else { double l = caster->getSkillLevel(skill); if(l > 0) level = (int)l; } */ } //********************************************************************* // check //********************************************************************* bool SpellData::check(const Creature* player, bool skipKnowCheck) const { if(player->isMonster()) return(true); if( !skipKnowCheck && skill != "" && !player->knowsSkill(skill) && !player->checkStaff("You are unable to cast %s spells.\n", gConfig->getSkillDisplayName(skill).c_str()) ) return(false); if( skill == "necromancy" && player->getAdjustedAlignment() > (player->getClass() == LICH ? PINKISH : REDDISH) && !player->checkStaff("You must be evil to cast %s spells.\n", gConfig->getSkillDisplayName(skill).c_str()) ) return(false); return(true); } //********************************************************************* // infoSpells //********************************************************************* // This function is the second half of info which outputs spells void infoSpells(const Player* viewer, Creature* target, bool notSelf) { Player *player=0; bstring str=""; bstring skillName=""; const Anchor* anchor=0; char spl[(int)MAX_SCHOOL][MAXSPELL][24], list[MAXSPELL][24]; int i=0, count=0, j[(int)MAX_SCHOOL], l = sizeof(list); int school = (int)NO_SCHOOL; if(notSelf) viewer->printColor("\n^Y%M's Spells Known: ", target); else viewer->printColor("\n^YSpells known: "); zero(j, sizeof(j)); zero(spl, sizeof(spl)); for(i = 0; i < get_spell_list_size() ; i++) { if(target->spellIsKnown(i)) { count++; school = (int)get_spell_school(i); // temporary if(school == HEALING) school = NO_SCHOOL; strcpy(spl[school][j[school]++], get_spell_name(i)); } } if(!count) { viewer->print("None."); } else { viewer->print("%d", count); for(school = (int)NO_SCHOOL; school < (int)MAX_SCHOOL; school++) { if(j[school]) { memcpy(list, spl[school], l); qsort((void *) list, j[school], 24, (PFNCOMPARE) strcmp); str = ""; for(i = 0; i < j[school]; i++) { str += list[i]; str += ", "; } if(school == (SchoolOfMagic)NO_SCHOOL) { skillName = "Other"; } else { skillName = gConfig->getSkillDisplayName(spellSkill((SchoolOfMagic)school)); } str = str.substr(0, str.length() - 2) + "."; viewer->printColor("\n^W%s:^x %s", skillName.c_str(), str.c_str()); } } } viewer->print("\n\n"); spellsUnder(viewer, target, notSelf); player = target->getPlayer(); if(!player) return; if(player->hasAnchor(0) || player->hasAnchor(1) || player->hasAnchor(2)) { viewer->printColor("^YCurrent Dimensional Anchors\n"); for(i=0; i<MAX_DIMEN_ANCHORS; i++) { if(player->hasAnchor(i)) { anchor = player->getAnchor(i); viewer->printColor("^c%s ^x-> ^c%s", anchor->getAlias().c_str(), anchor->getRoomName().c_str()); if(viewer->isStaff()) viewer->print(" %s", (anchor->getMapMarker() ? anchor->getMapMarker()->str() : anchor->getRoom().str()).c_str()); viewer->print("\n"); } } } } //********************************************************************* // cmdSpells //********************************************************************* int cmdSpells(Creature* player, cmd* cmnd) { Creature* target = player; Player *pTarget=0, *viewer=0; bool notSelf=false; if(player->isPet()) { viewer = player->following->getPlayer(); notSelf = true; } else { viewer = player->getPlayer(); } viewer->clearFlag(P_AFK); if(player->isBraindead()) { player->print("You are brain-dead. You can't do that.\n"); return(0); } if(player->isCt()) { if(cmnd->num > 1) { cmnd->str[1][0] = up(cmnd->str[1][0]); target = gServer->findPlayer(cmnd->str[1]); notSelf = true; if(!target) { if(!loadPlayer(cmnd->str[1], &pTarget)) { player->print("Player does not exist.\n"); return(0); } infoSpells(viewer, pTarget, notSelf); free_crt(pTarget); return(0); } else { if(!player->canSee(target)) { player->print("That player is not logged on.\n"); return(0); } } } } infoSpells(viewer, target, notSelf); return(0); } //********************************************************************* // spellsUnder //********************************************************************* void spellsUnder(const Player* viewer, Creature* target, bool notSelf) { bstring str=""; Player *player = target->getPlayer(); if(target->isEffected("armor")) str += "armor, "; if(target->isEffected("bless")) str += "bless, "; if(target->isEffected("blindness")) str += "blind, "; if(target->isEffected("breathe-water")) str += "breathe-water, "; if(target->isEffected("camouflage")) str += "camouflage, "; if(target->isEffected("comprehend-languages")) str += "comprehend-languages, "; if(target->isEffected("courage")) str += "courage, "; if(target->isEffected("damnation")) str += "damnation, "; if(target->isEffected("darkness")) str += "darkness, "; if(target->isEffected("detect-invisible")) str += "detect-invisibility, "; if(target->isEffected("detect-magic")) str += "detect-magic, "; if(player && player->flagIsSet(P_ANCHOR)) str += "dimensional-anchor, "; if(target->isEffected("drain-shield")) str += "drain-shield, "; if(target->isEffected("earth-shield")) str += "earth-shield, "; if(target->isEffected("enfeeblement")) str += "enfeeblement, "; if(target->isEffected("enlarge")) str += "enlarge, "; if(target->isEffected("farsight")) str += "farsight, "; if(target->isEffected("fear")) str += "fear, "; if(target->isEffected("feeblemind")) str += "feeblemind, "; if(target->isEffected("fly")) str += "fly, "; if(target->isEffected("fortitude")) str += "fortitude, "; if(player && player->flagIsSet(P_FREE_ACTION)) str += "free-action, "; if(target->isEffected("haste")) str += "haste, "; if(target->isEffected("heat-protection")) str += "heat-protection, "; if(target->isEffected("greater-invisibility")) str += "greater invisibility, "; if(target->isEffected("infravision")) str += "infravision, "; if(target->isEffected("insight")) str += "insight, "; if(target->isEffected("invisibility")) str += "invisibility, "; if(target->isEffected("know-aura")) str += "know-aura, "; if(target->isEffected("levitate")) str += "levitation, "; if(target->isEffected("pass-without-trace")) str += "pass-without-trace, "; if(target->isEffected("prayer")) str += "prayer, "; if(target->isEffected("protection")) str += "protection, "; if(target->isEffected("reduce")) str += "reduce, "; if(target->isEffected("reflect-magic")) str += "reflect-magic, "; if(target->isEffected("resist-air")) str += "resist-air, "; if(target->isEffected("resist-cold")) str += "resist-cold, "; if(target->isEffected("resist-earth")) str += "resist-earth, "; if(target->isEffected("resist-fire")) str += "resist-fire, "; if(target->isEffected("resist-electric")) str += "resist-lightning, "; if(target->isEffected("resist-magic")) str += "resist-magic, "; if(target->isEffected("resist-water")) str += "resist-water, "; if(target->isEffected("silence")) str += "silence, "; if(target->isEffected("slow")) str += "slow, "; if(target->isEffected("slow-poison")) str += "slow-poison, "; if(target->isEffected("static-field")) str += "static-field, "; if(target->isEffected("slow-poison")) str += "slow-poison, "; if(target->isEffected("stoneskin")) str += "stoneskin, "; if(target->isEffected("strength")) str += "strength, "; if(player && player->flagIsSet(P_STUNNED)) str += "stun, "; if(target->isEffected("tongues")) str += "tongues, "; if(target->isEffected("true-sight")) str += "true-sight, "; if(target->isEffected("undead-ward")) str += "undead-ward, "; if(target->isEffected("warmth")) str += "warmth, "; if(target->isEffected("weakness")) str += "weakness, "; if(target->isEffected("wind-protection")) str += "wind-protection, "; if(!str.length()) str = "None"; else str = str.substr(0, str.length() - 2); if(notSelf) viewer->printColor("^W%M's Spells Under:^x %s.\n\n", target, str.c_str()); else viewer->printColor("^WSpells under:^x %s.\n\n", str.c_str()); }