/*
* 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());
}