roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * 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());
}