roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * Spells.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-2012 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 "commands.h"
#include "effects.h"

//*********************************************************************
//						load
//*********************************************************************

Spell::Spell(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
    priority = 100;

	while(curNode) {
			 if(NODE_NAME(curNode, "Name")) { 
                 xml::copyToBString(name, curNode);
                 parseName();
             }
		else if(NODE_NAME(curNode, "Script")) xml::copyToBString(script, curNode);
		else if(NODE_NAME(curNode, "Priority")) xml::copyToNum(priority, curNode);
		else if(NODE_NAME(curNode, "Description")) xml::copyToBString(description, curNode);

		curNode = curNode->next;
	}
}

//*********************************************************************
//						save
//*********************************************************************

void Spell::save(xmlNodePtr rootNode) const {
	xml::saveNonNullString(rootNode, "Name", name);
	xml::saveNonNullString(rootNode, "Script", script);
    xml::saveNonZeroNum<int>(rootNode, "Priority", priority);
    xml::saveNonNullString(rootNode,"Description", description);

}

//*********************************************************************
//						clearSpells
//*********************************************************************

void Config::clearSpells() {
    for(std::pair<bstring, Spell*> sp : spells) {
		delete sp.second;
    }
	spells.clear();
}

//*********************************************************************
//						Spell
//*********************************************************************

int dmSpellList(Player* player, cmd* cmnd) {
	const Spell* spell=0;

	player->printColor("^YSpells\n");
    for(std::pair<bstring, Spell*> sp : gConfig->spells) {
		spell = sp.second;
		player->printColor("  %s   %d - %s\n    Script: ^y%s^x\n", spell->name.c_str(),
			spell->priority, spell->description.c_str(), spell->script.c_str());
	}

	return(0);
}

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

//*********************************************************************
//						spellSkill
//*********************************************************************

bstring spellSkill(DomainOfMagic domain) {
	switch(domain) {
	case HEALING:
		return("healing");
	case DESTRUCTION:
		return("destruction");
	case EVIL:
		return("evil");
	case GOOD:
		return("good");
	case KNOWLEDGE:
		return("knowledge");
	case PROTECTION:
		return("protection");
	case NATURE:
		return("nature");
	case AUGMENTATION:
		return("augmentation");
	case TRICKERY:
		return("trickery");
	case TRAVEL:
		return("travel");
	case CREATION:
		return("creation");
	default:
		return("");
	}
}

//*********************************************************************
//						getCastingType
//*********************************************************************

MagicType Creature::getCastingType() const {
	int cls = getClass();

	if(isPlayer()) {
		if(getAsConstPlayer()->getSecondClass() == MAGE)
			cls = MAGE;
		else if(getAsConstPlayer()->getSecondClass() == CLERIC)
			cls = CLERIC;
	}

	switch(cls) {
		case DUNGEONMASTER:
		case CARETAKER:
		case BUILDER:

		// pure arcane
		case MAGE:
		case LICH:
		// hybrid arcane
		case BARD:
		case PUREBLOOD:
			return(Arcane);
		// pure divine
		case CLERIC:
		case DRUID:
		// hybrid divine
		case PALADIN:
		case RANGER:
		case DEATHKNIGHT:
			return(Divine);
		default:
			return(NO_MAGIC_TYPE);
	}
}

//*********************************************************************
//						isPureCaster
//*********************************************************************

bool Creature::isPureCaster() const {
	int second = isPlayer() ? getAsConstPlayer()->getSecondClass() : 0;
	return(	(cClass == MAGE && !second) ||
			cClass == LICH ||
			(cClass == CLERIC && !second) ||
			cClass == DRUID
	);
}

//*********************************************************************
//						isHybridCaster
//*********************************************************************

bool Creature::isHybridCaster() const {
	int second = isPlayer() ? getAsConstPlayer()->getSecondClass() : 0;
	return(	cClass == BARD ||
			cClass == DEATHKNIGHT ||
			cClass == PALADIN ||
			cClass == RANGER ||
			cClass == PUREBLOOD ||
			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, DomainOfMagic d, Object* obj, const Creature* caster) {
	how = h;
	school = s;
	domain = d;
	object = obj;
	if(caster->getCastingType() == Divine)
		skill = spellSkill(domain);
	else
		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" || skill == "evil") &&
		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);
	if(	skill == "good" &&
		player->getAdjustedAlignment() < BLUISH &&
		!player->checkStaff("You must be good 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 = target->getAsPlayer();
	const Anchor* anchor=0;
	MagicType castingType = target->getCastingType();
	int		min = (castingType == Divine ? (int)MIN_DOMAIN : (int)MIN_SCHOOL) + 1, max = (castingType == Divine ? (int)MAX_DOMAIN : (int)MAX_SCHOOL);
	char	spl[max][MAXSPELL][24], list[MAXSPELL][24];
	int		count=0, j[max], l = sizeof(list);
	int		i=0,n=0;
	bstring str="";
	bstring skillName="";

	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++;

			if(castingType == Divine) {
				n = (int)get_spell_domain(i);
				// monsters ignore these rules - move into the "other" category
				if(n == DOMAIN_CANNOT_CAST && !player)
					n = NO_DOMAIN;
			} else {
				n = (int)get_spell_school(i);
				// monsters ignore these rules - move into the "other" category
				if(n == SCHOOL_CANNOT_CAST && !player)
					n = NO_SCHOOL;
			}

			strcpy(spl[n][j[n]++], get_spell_name(i));
		}
	}

	if(!count) {
		viewer->print("None.");
	} else {
		viewer->print("%d", count);
		for(n = min; n < max; n++) {
			if(j[n]) {
				memcpy(list, spl[n], l);
				qsort((void *) list, j[n], 24, (PFNCOMPARE) strcmp);
				str = "";
				for(i = 0; i < j[n]; i++) {
					str += list[i];
					str += ", ";
				}

				if(castingType == Divine) {
					if((DomainOfMagic)n == NO_DOMAIN) {
						skillName = "Other";
					} else if((DomainOfMagic)n == DOMAIN_CANNOT_CAST) {
						skillName = "Not Castable";
					} else {
						skillName = gConfig->getSkillDisplayName(spellSkill((DomainOfMagic)n));
					}
				} else{
					if((SchoolOfMagic)n == NO_SCHOOL) {
						skillName = "Other";
					} else if((SchoolOfMagic)n == SCHOOL_CANNOT_CAST) {
						skillName = "Not Castable";
					} else {
						skillName = gConfig->getSkillDisplayName(spellSkill((SchoolOfMagic)n));
					}
				}
				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);

	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->getPlayerMaster();
		notSelf = true;
	} else {
		viewer = player->getAsPlayer();
	}

	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
//*********************************************************************

bstring effectSpellName(bstring effect) {
	effect = stripColor(effect.toLower());

	if(effect == "silent!") return("silence");
	if(effect == "blessed") return("bless");
	if(effect == "detect-invisible") return("detect-invisibility");
	if(effect == "invisible") return("invisibility");

	return(effect);
}

//*********************************************************************
//						spellsUnder
//*********************************************************************

void spellsUnder(const Player* viewer, const Creature* target, bool notSelf) {
	bstring str = "";
	const Player* player = target->getAsConstPlayer();
	std::list<bstring> spells;
	std::list<bstring>::const_iterator it;
	const Effect* effect=0;
	EffectList::const_iterator eIt;

	// effects are flagged as whether or not they are a spell effect
	for(eIt = target->effects.effectList.begin() ; eIt != target->effects.effectList.end() ; eIt++) {
		effect = (*eIt)->getEffect();
		if(!effect->isSpell())
			continue;
		spells.push_back(effectSpellName(effect->getDisplay()));
	}

	// we want some flags to show up, too
	if(player) {
		if(player->flagIsSet(P_FREE_ACTION))
			spells.push_back("free-action");
		if(player->flagIsSet(P_STUNNED))
			spells.push_back("stun");
	}


	spells.sort();
	for(it = spells.begin() ; it != spells.end() ; it++) {
		str += (*it);
		str += ", ";
	}


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