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/
/*
 * afflictions.cpp
 *   Functions that handle poison/disease/curse/blind/vampirism/lycanthropy
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "effects.h"
#include "commands.h"
#include "magic.h"


//*********************************************************************
//						cmdCreepingDoom
//*********************************************************************

int cmdCreepingDoom(Player* player, cmd* cmnd) {
	Creature* creature=0;
	Monster *mCreature=0;
	long	i=0, t=0;
	int		chance=0, dmg=0;

	player->clearFlag(P_AFK);
	if(!player->ableToDoCommand())
		return(0);

	if(!player->isCt()) {
		if(!player->knowsSkill("creeping-doom")) {
			player->print("You lack the ability to strike someone with creeping doom.\n");
			return(0);
		}
		if(!player->alignInOrder()) {
			player->print("%s requires you to be evil to do that.\n", gConfig->getDeity(player->getDeity())->getName().c_str());
			return(0);
		}
	}

	if(cmnd->num < 2) {
		player->print("Strike whom with creeping doom?\n");
		return(0);
	}

	creature = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], true, true);
	if(!creature || creature == player) {
		player->print("You don't see that here.\n");
		return(0);
	}
//	pCreature = creature->getPlayer();
	mCreature = creature->getMonster();

	player->smashInvis();
	player->setFishing(false);

	if(!player->canAttack(creature))
		return(0);

	if(mCreature && mCreature->getType() == ARACHNID &&
		!player->checkStaff("You cannot harm arachnids with creeping-doom.\n")
	)
		return(0);
	if(	creature->getDeity() == ARACHNUS &&
		!player->checkStaff("You cannot harm followers of Arachnus with creeping-doom.\n")
	)
		return(0);


	double level = player->getSkillLevel("creeping-doom");
	i = LT(player, LT_SMOTHER);
	t = time(0);
	if(i > t && !player->isCt()) {
		player->pleaseWait(i-t);
		return(0);
	}

	player->lasttime[LT_SMOTHER].ltime = t;
	player->updateAttackTimer();
	player->lasttime[LT_SMOTHER].interval = 120L;


	chance = ((int)(level - creature->getLevel()) * 20) + bonus((int) player->piety.getCur()) * 5 + 25;
	chance = MIN(chance, 80);

	dmg = mrand((int)(level*2), (int)(level*3));

	if(mCreature)
		mCreature->addEnmCrt(player);

	if(!creature->isCt()) {
		if(mrand(1, 100) > chance) {
			player->print("You failed to strike %N with creeping doom.\n", creature);
			broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M tried to strike %N with creeping doom!", player, creature);
			creature->print("%M tried to strike you with creeping doom!\n", player);
			player->checkImprove("creeping-doom", false);
			return(0);
		}
		if(creature->chkSave(BRE, player, 0)) {
			player->printColor("^yYour creeping-doom strike was interrupted!\n");
			creature->print("You manage to partially avoid %N's creeping-doom strike.\n", player);
			dmg /= 2;
		}
	}

	player->printColor("You struck %N with creeping doom for %s%d^x damage.\n", creature, player->customColorize("*CC:DAMAGE*"), dmg);
	player->checkImprove("creeping-doom", true);
	player->statistics.attackDamage(dmg, "creeping-doom");

	creature->printColor("%M struck you with creeping doom for %s%d^x damage!\n", player, creature->customColorize("*CC:DAMAGE*"), dmg);
	broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M struck %N with creeping doom!", player, creature);

	// if they didn't die from the damage, curse them
	if(!player->doDamage(creature, dmg, CHECK_DIE))
		creature->addEffect("creeping-doom", 200, (int)level, true, player);
	return(0);
}


//********************************************************************
//						cmdPoison
//********************************************************************
// the Arachnus ability to inflict enemies with poison

int cmdPoison(Player* player, cmd* cmnd) {
	Creature* creature=0;
	Monster *mCreature=0;
	long	i=0, t=0;
	int		chance=0;
	unsigned int dur=0;

	player->clearFlag(P_AFK);
	if(!player->ableToDoCommand())
		return(0);

	if(!player->isCt()) {
		if(!player->knowsSkill("poison")) {
			if(player->getClass() == ASSASSIN || player->getSecondClass() == ASSASSIN) {
				player->print("Assassins must use envenom to poison people.\n");
			} else {
				player->print("You lack the ability to poison people.\n");
			}
			return(0);
		}
		if(!player->alignInOrder()) {
			player->print("%s requires you to be evil to do that.\n", gConfig->getDeity(player->getDeity())->getName().c_str());
			return(0);
		}
	}

	if(cmnd->num < 2) {
		player->print("Poison whom?\n");
		return(0);
	}

	creature = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], true, true);
	if(!creature || creature == player) {
		player->print("You don't see that here.\n");
		return(0);
	}
//	pCreature = creature->getPlayer();
	mCreature = creature->getMonster();

	player->smashInvis();
	player->setFishing(false);

	if(!player->canAttack(creature))
		return(0);

	if(mCreature && mCreature->getType() == ARACHNID &&
		!player->checkStaff("You cannot poison arachnids with this ability.\n")
	)
		return(0);
	if(	creature->getDeity() == ARACHNUS &&
		!player->checkStaff("You cannot poison followers of Arachnus with this ability.\n")
	)
		return(0);


	double level = player->getSkillLevel("poison");
	i = LT(player, LT_DRAIN_LIFE);
	t = time(0);
	if(i > t && !player->isCt()) {
		player->pleaseWait(i-t);
		return(0);
	}

	player->lasttime[LT_DRAIN_LIFE].ltime = t;
	player->updateAttackTimer();
	player->lasttime[LT_DRAIN_LIFE].interval = 120L;


	chance = ((int)(level - creature->getLevel()) * 20) + bonus((int) player->piety.getCur()) * 5 + 25;
	chance = MIN(chance, 80);
	dur = standardPoisonDuration((short)level, creature->constitution.getCur());

	if(mCreature)
		mCreature->addEnmCrt(player);

	if(!player->isCt()) {
		if(mrand(1, 100) > chance || creature->isPoisoned() || creature->immuneToPoison()) {
			player->printColor("^GYou fail to poison %N.\n", creature);
			creature->printColor("^G%M tried to poison you!\n", player);
			broadcastGroup(false, creature, "%M tried to poison %N.\n", player, creature);
			broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M tried to poison %N.", player, creature);
			player->checkImprove("poison", false);
			return(0);
		}
	}

	player->printColor("^GYou poison %N.\n", creature);
	creature->printColor("^G%M poisons you!\n", player);
	broadcastGroup(false, creature, "^G%M poisons %N.\n", player, creature);
	broadcast(player->getSock(), creature->getSock(), player->getRoom(), "^G%M poisons %N.", player, creature);

	if(creature->chkSave(POI, player, 0)) {
		player->print("%N partially resists your poison.\n", creature);
		creature->print("You partially resists %N's poison.\n", player);
		level = level * 2 / 3;
		dur = dur * 2 / 3;
	}

	player->checkImprove("poison", true);
	creature->poison(player, (unsigned int)level, dur);
	return(0);
}

//********************************************************************
//						poison
//********************************************************************
// the calling function is responsible for announcing the poisoning

void Creature::poison(Creature *enemy, unsigned int damagePerPulse, unsigned int duration) {
	if(immuneToPoison())
		return;
	setPoisonedBy("");

	if(enemy) {
		if(isPlayer()) {

			if(enemy->isPlayer() || enemy->isPet()) {
				setFlag(P_POISONED_BY_PLAYER);
				clearFlag(P_POISONED_BY_MONSTER);
			} else {
				setFlag(P_POISONED_BY_MONSTER);
				clearFlag(P_POISONED_BY_PLAYER);
			}

			if(enemy->isPet())
				setPoisonedBy(enemy->following->name);
			else if(enemy->isMonster() && !enemy->flagIsSet(M_NO_PREFIX))
				setPoisonedBy((bstring)"a " + enemy->name);
			else if(this == enemy)
				setPoisonedBy((bstring)himHer() + "self");
			else
				setPoisonedBy(enemy->name);

		} else if(isMonster()) {

			if(enemy->isPlayer())
				setPoisonedBy(enemy->name);
			else if(enemy->isPet())
				setPoisonedBy(enemy->following->name);
			else if(enemy->isMonster() && !enemy->flagIsSet(M_NO_PREFIX))
				setPoisonedBy((bstring)"a " + enemy->name);
			else
				setPoisonedBy(enemy->name);

		}
	}

	addEffect("poison", duration, damagePerPulse, false, this);
}

//********************************************************************
//						immuneToPoison
//********************************************************************

bool Creature::immuneToPoison() const {
	if(isUndead() || monType::noLivingVulnerabilities(type))
		return(true);

	if(deity == ARACHNUS)
		return(true);

	// check on mobs
	if(isMonster()) {
		if(flagIsSet(M_NO_POISON))
			return(true);
	// check on players
	} else {
		if(isStaff())
			return(true);
	}
	return(false);
}

//*********************************************************************
//						isPoisoned
//*********************************************************************

bool Creature::isPoisoned() const {
	if(immuneToPoison())
		return(false);
	return(effects.hasPoison());
}

//*********************************************************************
//						hasPoison
//*********************************************************************

bool Effects::hasPoison() const {
	std::list<EffectInfo*>::const_iterator eIt;
	for(eIt = list.begin() ; eIt != list.end() ; eIt++) {
		if((*eIt)->isPoison())
			return(true);
	}
	return(false);
}

//*********************************************************************
//						curePoison
//*********************************************************************

bool Creature::curePoison() {
	if(isPlayer()) {
		clearFlag(P_POISONED_BY_PLAYER);
		clearFlag(P_POISONED_BY_MONSTER);
	}

	removeEffect("slow-poison");
	setPoisonedBy("");
	return(effects.removePoison());
}

//*********************************************************************
//						removePoison
//*********************************************************************

bool Effects::removePoison() {
	bool removed=false;
	// remove all effects flagged as poison
	std::list<EffectInfo*>::iterator eIt;
	EffectInfo *effect=0;
	for(eIt = list.begin() ; eIt != list.end() ;) {
		effect = (*eIt);

		if(effect->isPoison()) {
			removed = true;
			effect->remove();
			delete effect;
			eIt = list.erase(eIt);
		} else
			eIt++;
	}
	return(removed);
}

//********************************************************************
//						standardPoisonDuration
//********************************************************************

unsigned int standardPoisonDuration(short level, short con) {
	int dur = 60 * mrand(1,3) - (60*bonus((int)con)) + level*10;
	if(con > 120) {
		// a spread between 400 (50%) and 120 (0%) resistance
		double percent = 1 - (con - 120) / (680 - 120);
		percent *= dur;
		dur = (int)percent;
	}
	return(MAX(60,dur));
}

// low con, level 40:  1-3 mins
// high con, level 40: 1 min
//

//********************************************************************
//						disease
//********************************************************************
// the calling function is responsible for announcing the diseasing

void Creature::disease(Creature* enemy, unsigned int damagePerPulse) {
	if(immuneToDisease())
		return;
	addPermEffect("disease", damagePerPulse, false);
}

//********************************************************************
//						immuneToDisease
//********************************************************************

bool Creature::immuneToDisease() const {
	// no need to call noLivingVulnerabilities
	if(isMonster())
		return(true);

	if(isStaff())
		return(true);
	if(isUndead())
		return(true);

	return(false);
}

//*********************************************************************
//						isDiseased
//*********************************************************************

bool Creature::isDiseased() const {
	if(immuneToDisease())
		return(false);
	return(effects.hasDisease());
}

//*********************************************************************
//						hasDisease
//*********************************************************************

bool Effects::hasDisease() const {
	std::list<EffectInfo*>::const_iterator eIt;
	for(eIt = list.begin() ; eIt != list.end() ; eIt++) {
		if((*eIt)->isDisease())
			return(true);
	}
	return(false);
}

//*********************************************************************
//						cureDisease
//*********************************************************************

bool Creature::cureDisease() {
	return(effects.removeDisease());
}

//*********************************************************************
//						removeDisease
//*********************************************************************

bool Effects::removeDisease() {
	bool removed=false;
	// remove all effects flagged as disease
	std::list<EffectInfo*>::iterator eIt;
	EffectInfo *effect=0;
	for(eIt = list.begin() ; eIt != list.end() ;) {
		effect = (*eIt);

		if(effect->isDisease()) {
			removed = true;
			effect->remove();
			delete effect;
			eIt = list.erase(eIt);
		} else
			eIt++;
	}
	return(removed);
}

//*********************************************************************
//						removeCurse
//*********************************************************************
// remove curse effects

bool Creature::removeCurse() {
	return(effects.removeCurse());
}

bool Effects::removeCurse() {
	bool removed=false;
	std::list<EffectInfo*>::iterator eIt;
	EffectInfo *effect=0;
	for(eIt = list.begin() ; eIt != list.end() ;) {
		effect = (*eIt);

		if(effect->isCurse()) {
			removed = true;
			effect->remove();
			delete effect;
			eIt = list.erase(eIt);
		} else
			eIt++;
	}
	return(removed);
}

//********************************************************************
//						isBlind
//********************************************************************

bool Creature::isBlind() const {
	if(isMonster() || isStaff())
		return(false);
	return(isEffected("blindness"));
}

//***********************************************************************
//						isNew
//***********************************************************************
// these checks distinguish legacy vampires and werewolves from new ones.
// once these legacy classes have been removed, these functions can just
// be replaced with isEffected("vampirism") and isEffected("lycanthropy")

bool Creature::isNewVampire() const {
	if(isPlayer() && cClass == VAMPIRE)
		return(false);
	return(isEffected("vampirism"));
}

bool Creature::isNewWerewolf() const {
	if(isPlayer() && cClass == WEREWOLF)
		return(false);
	return(isEffected("lycanthropy"));
}

//***********************************************************************
//						makeVampire
//***********************************************************************

void Creature::makeVampire() {
	addPermEffect("vampirism");
	if(isPlayer()) {
		setFlag(P_CHAOTIC);

		// make sure the master still exists and is still a vampire
		Player* player = getPlayer();
		if(player->getAfflictedBy() != "") {
			bool online = true;
			Player* master = gServer->findPlayer(player->getAfflictedBy());

			if(!master) {
				loadPlayer(player->getAfflictedBy().c_str(), &master);
				online = false;
			}

			if(!master || !master->isEffected("vampirism"))
				player->setAfflictedBy("");
			else {
				master->minions.push_back(name);
				master->save(online);
			}

			if(master && !online)
				free_crt(master);
		}
	}
	if(!knowsSkill("mist"))
		addSkill("mist", 1);
	if(!knowsSkill("bite"))
		addSkill("bite", 1);
	if(!knowsSkill("hypnotize"))
		addSkill("hypnotize", 1);
}

//***********************************************************************
//						willBecomeVampire
//***********************************************************************

bool Creature::willBecomeVampire() const {
	if(level < 7 || cClass == PALADIN || isEffected("lycanthropy") || isUndead())
		return(false);
	// only evil clerics become vampires
	if(cClass == CLERIC && !(deity == ARAMON || deity == ARACHNUS))
		return(false);
	if(monType::noLivingVulnerabilities(type))
		return(false);
	return(true);
}

//***********************************************************************
//						vampireCharm
//***********************************************************************
// determines if player is a spawn of the master
// if not, it does some cleanup that should have been done elsewhere
// (an extra check to make sure everything is set properly)

bool Creature::vampireCharmed(Player* master) {
	if(!isPlayer() || !master)
		return(false);

	// if they're on their way to becoming a vampire, but arent one yet, don't
	// mess with minion or afflictedBy fields
	if(isEffected("porphyria"))
		return(false);

	Player* player = getPlayer();
	bool charmed = true;

	// we need to make sure the minion lists match up;
	// this won't be the case if somebody suicides and remakes

	if(charmed && (!isEffected("vampirism") || !master->isEffected("vampirism")))
		charmed = false;
	if(charmed && player->getAfflictedBy() != master->name)
		charmed = false;

	if(charmed) {
		bool found = false;
		std::list<bstring>::iterator mIt;
		for(mIt = master->minions.begin() ; mIt != master->minions.end() && !found ; mIt++) {
			if(*mIt == (bstring)player->name)
				found = true;
		}
		if(!found)
			charmed = false;
	}

	// if they're not charmed, clean up these lists
	if(!charmed) {
		if(player->getAfflictedBy() == master->name)
			player->setAfflictedBy("");
		master->minions.remove(player->name);
	}
	return(charmed);
}


//********************************************************************
//						clearMinions
//********************************************************************

void Creature::clearMinions() {
	if(!isPlayer())
		return;
	Player* player = getPlayer(), *target=0;
	bool online = true;

	if(player->getAfflictedBy() != "") {
		target = gServer->findPlayer(player->getAfflictedBy());

		if(!target) {
			loadPlayer(player->getAfflictedBy().c_str(), &target);
			online = false;
		}

		if(target) {
			target->minions.remove(player->name);
			target->save(online);
		}
		if(!online)
			free_crt(target);
	}

	std::list<bstring>::iterator mIt;
	for(mIt = player->minions.begin() ; mIt != player->minions.end() ; mIt++) {
		online = true;
		target = gServer->findPlayer(*mIt);

		if(!target) {
			loadPlayer((*mIt).c_str(), &target);
			online = false;
		}

		if(target) {
			if(target->getAfflictedBy() == (bstring)player->name) {
				target->setAfflictedBy("");
				target->save(online);
			}
			if(!online)
				free_crt(target);
		}
	}
}

//********************************************************************
//						addPorphyria
//********************************************************************

bool Creature::addPorphyria(Creature *killer, int chance) {
	// TODO: Dom: disable porphyria until new vamps are tested
	return(false);
/*
	const RaceData* rdata = gConfig->getRace(race);
	if(rdata)
		chance -= rdata->getPorphyriaResistance();

	if(mrand(1,100) > chance)
		return(false);
	if(!willBecomeVampire())
		return(false);
	if(isEffected("porphyria") || isEffected("undead-ward") || isEffected("drain-shield"))
		return(false);
	if(!killer || !killer->isNewVampire())
		return(false);

	addEffect("porphyria", 0, 0, true, killer);

	// if they become a vampire, they'll be permanently charmed by their master
	if(killer->isPlayer()) {
		killer->print("You have infected %N with porphyria.\n", this);
		if(isPlayer())
			getPlayer()->setAfflictedBy(killer->name);
	} else if(isPlayer())
		getPlayer()->setAfflictedBy("");

	return(true);
	*/
}

//********************************************************************
//						sunlightDamage
//********************************************************************
// return true if they died, false if they lived

bool Creature::sunlightDamage() {
	if(isStaff() || !isNewVampire())
		return(false);

	int dmg = hp.getMax() / 5 + mrand(1,10);

	unmist();
	wake("Terrible nightmares disturb your sleep!");
	printColor("^Y^#The searing sunlight burns your flesh!\n");
	broadcast(getSock(), getRoom(), "^Y%M's flesh is burned by the sunlight!", this);

	// lots of damage every 5 seconds
	hp.decrease(dmg);

	if(hp.getCur() < 1) {
		printColor("^YYou have been disintegrated!\n");
		if(isPlayer())
			getPlayer()->die(SUNLIGHT);
		else
			getMonster()->mobDeath();
		return(true);
	}
	return(false);
}

//***********************************************************************
//						makeWerewolf
//***********************************************************************

void Creature::makeWerewolf() {
	addPermEffect("lycanthropy");
	if(!knowsSkill("maul"))
		skills["maul"] = new CrtSkill("maul", 1);
	if(!knowsSkill("frenzy"))
		skills["frenzy"] = new CrtSkill("frenzy", 1);
	if(!knowsSkill("howl"))
		skills["howl"] = new CrtSkill("howl", 1);
	if(!knowsSkill("claw"))
		skills["claw"] = new CrtSkill("claw", level * 5);
}

//***********************************************************************
//						willBecomeWerewolf
//***********************************************************************

bool Creature::willBecomeWerewolf() const {
	if(level < 7 || isEffected("lycanthropy") || isUndead())
		return(false);
	if(monType::noLivingVulnerabilities(type))
		return(false);
	return(true);
}

//********************************************************************
//						addLycanthropy
//********************************************************************

bool Creature::addLycanthropy(Creature *killer, int chance) {
	// TODO: Dom: disable lycanthropy until new wolves are tested
	return(false);
/*
	if(mrand(1,100) > chance)
		return(false);
	if(!willBecomeWerewolf())
		return(false);
	if(isEffected("lycanthropy"))
		return(false);
	if(!killer || !killer->isNewWerewolf())
		return(false);

	addEffect("lycanthropy", 0, 0, true, killer);

	if(killer->isPlayer()) {
		killer->print("You have infected %N with lycanthropy.\n", this);
		if(isPlayer())
			getPlayer()->setAfflictedBy(killer->name);
	} else if(isPlayer())
		getPlayer()->setAfflictedBy("");

	return(true);
	*/
}

//*********************************************************************
//						splCurePoison
//*********************************************************************
// This function allows a player to cast a curepoison spell on themself,
// another player or a monster. It will remove any poison that is in
// that player's system.

int splCurePoison(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* target=0;

	if(player->getClass() == LICH && player->getLevel() == 1 && spellData->how == CAST) {
		player->print("You are not experienced enough to cast that yet.\n");
		return(0);
	}


	if(	player->isPlayer() &&
		player->getClass() != CLERIC &&
		player->getClass() != PALADIN &&
		!player->isCt() &&
		spellData->how == CAST
	) {
		player->print("Your class may not cast that spell.\n");
		return(0);
	}

	// Curepoison self
	if(cmnd->num == 2) {
		target = player;
		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
			player->print("Cure-poison spell cast on yourself.\n");
			player->print("You feel much better.\n");
			broadcast(player->getSock(), player->getRoom(), "%M casts cure-poison on %sself.",
				player, player->himHer());
		} else if(spellData->how == POTION && player->isPoisoned())
			player->print("You feel the poison subside.\n");
		else if(spellData->how == POTION)
			player->print("Nothing happens.\n");

	// Cure a monster or player
	} else {
		if(noPotion(player, spellData))
			return(0);

		cmnd->str[2][0] = up(cmnd->str[2][0]);
		target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], false);

		if(!target) {
			player->print("That person is not here.\n");
			return(0);
		}

		if(checkRefusingMagic(player, target))
			return(0);

		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
			player->print("Cure-poison cast on %N.\n", target);
			broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts cure-poison on %N.", player, target);
			target->print("%M casts cure-poison on you.\nYou feel much better.\n", player);
		}

	}
	if(target->inCombat(false))
		player->smashInvis();

	target->curePoison();
	return(1);
}

//*********************************************************************
//						splSlowPoison
//*********************************************************************

int splSlowPoison(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* target=0;

	if(player->getClass() == LICH && player->getLevel() == 1 && spellData->how == CAST) {
		player->print("You are not experienced enough to cast that yet.\n");
		return(0);
	}


	// slow_poison self
	if(cmnd->num == 2) {
		target = player;
		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
			player->print("Slow-poison spell cast on yourself.\n");
			broadcast(player->getSock(), player->getRoom(), "%M casts slow-poison on %sself.",
				player, player->himHer());
			if(!player->isPoisoned()) {
				player->print("Nothing happens.\n");
				return(0);
			}
			player->print("You feel the poison subside somewhat.\n");
		} else if(spellData->how == POTION && player->isPoisoned())
			player->print("You feel the poison subside somewhat.\n");
		else if(spellData->how == POTION)
			player->print("Nothing happens.\n");

	// Cure a monster or player
	} else {
		if(noPotion(player, spellData))
			return(0);

		cmnd->str[2][0] = up(cmnd->str[2][0]);
		// monsters are now valid targets
		target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], false);

		if(!target) {
			player->print("That person is not here.\n");
			return(0);
		}

		if(checkRefusingMagic(player, target))
			return(0);

		player->print("Slow-poison cast on %N.\n", target);
		broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts slow-poison on %N.", player, target);
		target->print("%M casts slow-poison on you.\n", player);

		if(!target->isPoisoned()) {
			player->print("Nothing happens.\n");
			if(player != target)
				target->print("Nothing happens.\n");
			return(0);
		}
		target->print("You feel a little better.\n");
	}

	if(target->inCombat(false))
		player->smashInvis();

	target->addPermEffect("slow-poison");
	return(1);
}


//*********************************************************************
//						splCureDisease
//*********************************************************************

int splCureDisease(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* target=0;

	if(player->getClass() != CLERIC &&
		player->getClass() != PALADIN &&
	    !player->isStaff() &&
		spellData->how == CAST)
	{
		player->print("Only clerics and paladins may cast that spell.\n");
		return(0);
	}

	if(cmnd->num == 2) {
		target = player;

		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
			player->print("Cure-disease spell cast on yourself.\n");
			player->print("Your fever subsides.\n");
			broadcast(player->getSock(), player->getRoom(), "%M casts cure-disease on %sself.", player, player->himHer());
		} else if(spellData->how == POTION && player->isDiseased())
			player->print("You feel your fever subside.\n");
		else if(spellData->how == POTION)
			player->print("Nothing happens.\n");
	} else {
		if(noPotion(player, spellData))
			return(0);

		cmnd->str[2][0] = up(cmnd->str[2][0]);
		target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], false);

		if(!target) {
			player->print("That's not here.\n");
			return(0);
		}

		if(checkRefusingMagic(player, target))
			return(0);

		player->print("You cure-disease cast on %N.\n", target);
		broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts cure-disease on %N.", player, target);
		target->print("%M casts cure-disease on you.\nYou feel your fever subside.\n", player);

	}
	if(target->inCombat(false))
		player->smashInvis();

	target->cureDisease();
	return(1);
}

//*********************************************************************
//						splCureBlindness
//*********************************************************************

int splCureBlindness(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* target=0;

	if(player->getClass() == LICH && player->getLevel() < 3 && spellData->how == CAST) {
		player->print("You are not experienced enough to cast that yet.\n");
		return(0);
	}

	if(cmnd->num == 2) {

		target = player;
		if(!player->isStaff()) {
			if(	spellData->how != POTION ||
				!player->isEffected("blindness") ||
				player->flagIsSet(P_DM_BLINDED)
			)
				player->print("Nothing happens.\n");
		}

	} else {
		if(noPotion(player, spellData))
			return(0);

		cmnd->str[2][0] = up(cmnd->str[2][0]);
		target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], false);

		if(!target) {
			player->print("That's not here.\n");
			return(0);
		}


		if(checkRefusingMagic(player, target))
			return(0);


		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
			player->print("You cast cure blindess on %N.\n", target);
			broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts cure blindness on %N.",
				player, target);
			if(target->isPlayer()) {
				target->print("%M casts cure blindness on you.\n", player);

				if(	target->isEffected("blindness") &&
					(player->isCt() || !target->flagIsSet(P_DM_BLINDED))
				) {
					//target->print("You can see again.\n");
				} else {
					target->print("Nothing happens.\n");
				}
			}
		}


	}

	if(target->inCombat(false))
		player->smashInvis();

	if(target->isPlayer())
		if(player->isCt())
			target->clearFlag(P_DM_BLINDED);

	target->removeEffect("blindness");
	return(1);
}

//*********************************************************************
//						effectPoison
//*********************************************************************
// these effects can be removed with cure-poison
bool hearMobTick(Socket* sock);

bool effectPoison(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom)
{
	if(target->immuneToPoison())
		return(false);
	if(target->isEffected("slow-poison") && mrand(1,100) > 50)
		return(true);
	if(target->isPlayer() && target->flagIsSet(P_POISONED_BY_PLAYER) && target->inCombat())
		return(true);
	if(target->isMonster() &&
		(target->flagIsSet(M_WILL_POISON) || target->isPet()) &&
		mrand(1,100) > 50
	)
		return(true);

	switch(action) {
	case EFFECT_COMPUTE:
		break;
	case EFFECT_APPLY:
		break;
	case EFFECT_UNAPPLY:
		break;
	case EFFECT_PULSE:
		if(effect->getName() == "poison") {
			// generic poison
			target->wake("Terrible nightmares disturb your sleep!");
			target->printColor("^r^#Poison courses through your veins.\n");
			broadcast(target->getSock(), target->getRoom(), "Poison courses through %N's veins.", target);

			int dmg = effect->getStrength() + mrand(1,3);
			if(target->constitution.getCur() > 120) {
				// a spread between 400 (50%) and 120 (0%) resistance
				double percent = 1 - (target->constitution.getCur() - 120) / (680 - 120);
				percent *= dmg;
				dmg = (int)percent;
			}

			if(target->isMonster())
				broadcast(hearMobTick, "^y*** Poison courses through %N's body (L%d,R%s).", target, target->getLevel(), target->getRoom()->fullName().c_str());
			dmg = MAX(1,dmg);
			target->hp.decrease(dmg);

			if(target->isMonster() && target->getPoisonedBy() != "") {
				const Player* player = gServer->findPlayer(target->getPoisonedBy());
				if(player) {
					// everybody but arachnus gets half experience for poison damage
					int exp = dmg;
					if(player->getDeity() != ARACHNUS)
						exp /= 2;
					target->getMonster()->addEnmDmg(player, exp);
				}
			}

			// mark them for death
			if(target->hp.getCur() < 1) {
				broadcast(target->getSock(), target->getRoom(), "%M drops dead from poison.", target);
				target->setDeathType(POISON_GENERAL);
				if(target->isPlayer()) {
					if(target->flagIsSet(P_POISONED_BY_PLAYER)) {
						target->setDeathType(POISON_PLAYER);
					} else if(target->flagIsSet(P_POISONED_BY_MONSTER)) {
						target->setDeathType(POISON_MONSTER);
					}
				}
				return(false);
			}
		}
		break;
	default:
		break;
	}
	return(true);
}

//*********************************************************************
//						effectDisease
//*********************************************************************
// these effects can be removed with cure-disease

bool effectDisease(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom)
{
	switch(action) {
	case EFFECT_COMPUTE:
		break;
	case EFFECT_APPLY:
		if(effect->getName() == "lycanthropy") {
			// can they even be effected by lycanthropy?
			if(!target->willBecomeWerewolf())
				return(false);
			effect->setStrength(1);
			// 90 minutes until lycanthropy takes effect
			effect->setDuration(90 * 60);
		}
		break;
	case EFFECT_UNAPPLY:
		break;
	case EFFECT_PULSE:
		if(effect->getName() == "lycanthropy") {
			// permanent lycanthropy has no pulse effects
			if(effect->getDuration() == -1)
				return(true);

			// if they let their disease run too low, it'll become permanent
			if(effect->getDuration() <= 20)
				effect->setDuration(-1);

		} else if(effect->getName() == "disease") {
			// generic disease
			target->wake("Terrible nightmares disturb your sleep!");

			target->printColor("^#^rFever grips your mind.\n");
			target->printColor("^bYou feel nauseous.\n");
			broadcast(target->getSock(), target->getRoom(), "Fever grips %N.", target);
			target->updateAttackTimer(true, dice(1,6,3));

			int dmg = effect->getStrength() + mrand(1,3);
			if(target->constitution.getCur() > 120) {
				// a spread between 400 (50%) and 120 (0%) resistance
				double percent = 1 - (target->constitution.getCur() - 120) / (680 - 120);
				percent *= dmg;
				dmg = (int)percent;
			}
			dmg = MAX(1,dmg);
			target->hp.decrease(dmg);

			// mark them for death
			if(target->hp.getCur() < 1) {
				broadcast(target->getSock(), target->getRoom(), "%M dies from disease.", target);
				target->setDeathType(DISEASE);
				return(false);
			}
		}
		break;
	default:
		break;
	}
	return(true);
}

//*********************************************************************
//						effectCurse
//*********************************************************************
// these effects can be removed with remove-curse

bool effectCurse(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom)
{
	// how many minutes until porphyria takes effect
	int minutes = 90;

	switch(action) {
	case EFFECT_COMPUTE:
		break;
	case EFFECT_APPLY:
		if(effect->getName() == "porphyria") {
			// can they even be effected by porphyria?
			if(!target->willBecomeVampire())
				return(false);
			effect->setStrength(1);
			effect->setDuration(minutes * 60);
		}
		break;
	case EFFECT_UNAPPLY:
		break;
	case EFFECT_PULSE:
		if(effect->getName() == "porphyria") {
			Player* pTarget = target->getPlayer();
			bool remove = false;
			// over 90 minutes, max will get up to about 270, so range is 1-27 at worst
			int dmg = mrand(1, MAX(2, effect->getStrength()/10));

			target->wake("Terrible nightmares disturb your sleep!");

			// player-inflicted porphyria does less damage
			if(pTarget && pTarget->getAfflictedBy() != "")
				dmg /= 2;

			dmg = MAX(1, dmg);

			// pulse once every 20 seconds, base 50% chance to take damage
			// if a player is the cause, make it happen less often
			if(mrand(0,1)) {
				effect->setStrength(effect->getStrength() + 1);
				switch(mrand(1,4)) {
				case 1:
					target->printColor("^rBlood sputters through your veins.\n");
					break;
				case 2:
					target->printColor("^rBlood drains from your head.\n");
					break;
				case 3:
					target->printColor("^rFever grips your mind.\n");
					break;
				default:
					target->printColor("^WA sharp headache strikes you.\n");
					break;
				}

				target->printColor("You take %s%d^x damage.\n",
					target->customColorize("*CC:DAMAGE*"), dmg);
				target->hp.decrease(dmg);
			}

			// 3 pulses every minute
			// death by porphyria will also trigger
			if(	(effect->getStrength() > (minutes * 3) && !target->getRoom()->isSunlight()) ||
				(pTarget && pTarget->hp.getCur() < 1)
			) {
				remove = true;

				if(target->willBecomeVampire()) {
					target->makeVampire();
					if(pTarget) {
						pTarget->printColor("^rYour body shudders, your limbs go as cold as ice!\n");
						pTarget->printColor("^rFangs suddenly grow, replacing your teeth and sending blood running down your throat!\n");
						pTarget->printColor("^R^#You have been turned into a vampire.\n");
					}
					broadcast(target->getSock(), target->getRoom(), "^r%M shudders and convulses!", pTarget);
					broadcast(target->getSock(), target->getRoom(), "^rFangs grow from %N's teeth!", pTarget);
				}
			}

			if(target->hp.getCur() < 1) {
				broadcast(target->getSock(), target->getRoom(), "%M dies from poyphyria.", target);
				target->setDeathType(DISEASE);
				return(false);
			}

			if(remove)
				return(false);
			else {
				// porphyria should never actually wear off; if they are staying in
				// the sunlight to avoid becoming a vampire, just prolong it
				if(effect->getDuration() <= 20)
					effect->setDuration(effect->getDuration() + 20);
			}

		} else if(effect->getName() == "wounded") {

			target->wake("Terrible nightmares disturb your sleep!");

			int dmg = mrand(1+target->hp.getMax()/30, 1+target->hp.getMax()/20);
			target->printColor("^RYour wounds fester and bleed for %s%d^R damage.\n",
				target->customColorize("*CC:DAMAGE*"), dmg);
			broadcast(target->getSock(), target->getRoom(), "%M's wounds fester and bleed.", target);

			target->hp.decrease(dmg);

			// mark them for death
			if(target->hp.getCur() < 1) {
				broadcast(target->getSock(), target->getRoom(), "%M dies from cursed wounds.", target);
				target->setDeathType(WOUNDED);
				return(false);
			}

		} else if(effect->getName() == "creeping-doom") {

			if(target->isMonster() && target->getMonster()->getType() == ARACHNID)
				return(true);
			if(target->getDeity() == ARACHNUS)
				return(true);

			int dmg = effect->getStrength() / 2 + mrand(1,3);
			target->printColor("^DCursed spiders crawl all over your body and bite you for %s%d^D damage.\n",
				target->customColorize("*CC:DAMAGE*"), dmg);
			broadcast(target->getSock(), target->getRoom(), "Cursed spiders crawl all over %N.", target);

			target->hp.decrease(dmg);

			// mark them for death
			if(target->hp.getCur() < 1) {
				broadcast(target->getSock(), target->getRoom(), "Cursed spiders devour %N!", target);
				target->setDeathType(CREEPING_DOOM);
				return(false);
			}
		}
		break;
	default:
		break;
	}
	return(true);
}

//*********************************************************************
//						isCurse
//*********************************************************************

bool EffectInfo::isCurse() const {
	return(myEffect->effFn == effectCurse);
}

//*********************************************************************
//						isDisease
//*********************************************************************

bool EffectInfo::isDisease() const {
	// lycanthropy is only a disease if it's not permanent - once permanent, it's
	// considered an afflication and can't be removed by cure-disease
	if(myEffect->name == "lycanthropy" && duration == -1)
		return(false);
	if(myEffect->effFn == effectDisease)
		return(true);
	return(false);
}

//*********************************************************************
//						isPoison
//*********************************************************************

bool EffectInfo::isPoison() const {
	return(myEffect->effFn == effectPoison);
}

//*********************************************************************
//						splCurse
//*********************************************************************
// This function allows a player to curse a item in their inventory

int splCurse(Creature* player, cmd* cmnd, SpellData* spellData) {
	Object	*object=0;

	if(spellData->how == CAST && player->getClass() != MAGE && player->getClass() != LICH && !player->isStaff()) {
		player->print("Only mages and liches can curse.\n");
		return(0);
	}
	if(cmnd->num < 3) {
		player->print("Curse what?\n");
		return(0);
	}

	object = findObject(player, player->first_obj, cmnd, 2);

	if(!object) {
		player->print("You don't have that in your inventory.\n");
		return(0);
	}
	if(object->flagIsSet(O_CURSED)) {
		player->print("That object is already cursed.\n");
		return(1);
	}

	object->setFlag(O_CURSED);

	player->printColor("%O glows darkly.\n", object);
	broadcast(player->getSock(), player->getRoom(), "%M places a curse on %1P.", player, object);

	return(1);
}

//*********************************************************************
//						splRemoveCurse
//*********************************************************************
// This function allows a player to remove a curse on all the items
// in their inventory or on another player's inventory

int splRemoveCurse(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* target=0;
	otag*	op=0;
	int		i=0;
	bool	equipment=true;

	if(player->getClass() == LICH && player->getLevel() < 4 && spellData->how == CAST) {
		player->print("You are not experienced enough to cast that yet.\n");
		return(0);
	}

	// Cast remove-curse on self
	if(cmnd->num == 2) {

		target = player;
		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
			player->print("Remove-curse spell cast.\n");
			broadcast(player->getSock(), player->getRoom(), "%M casts remove-curse on %sself.", player, player->himHer());
		} else if(spellData->how == POTION)
			player->print("You feel relieved of burdens.\n");

	// Cast remove-curse on another player
	} else {
		if(noPotion(player, spellData))
			return(0);

		cmnd->str[2][0] = up(cmnd->str[2][0]);
		target = player->getRoom()->findPlayer(player, cmnd, 2);

		if(!target) {
			player->print("That player is not here.\n");
			return(0);
		}

		// this purposely does not check the refuse list
		if(target->flagIsSet(P_LINKDEAD) && target->isPlayer()) {
			player->print("%M doesn't want that cast on them right now.\n", target);
			return(0);
		}

		player->print("Remove-curse cast on %N.\n", target);
		target->print("%M casts a remove-curse spell on you.\n", player);
		broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts remove-curse on %N.", player, target);
	}

	if(target->inCombat(false))
		player->smashInvis();

	// remove all effects flagged as cursed
	if(target->removeCurse())
		equipment = false;

	// if the remove-curse spell didn't remove a curse on the player,
	// it wants to remove a curse on their equipment
	if(equipment) {
		for(i=0; i<MAXWEAR; i++) {
			if(target->ready[i]) {
				target->ready[i]->clearFlag(O_CURSED);
				target->ready[i]->remDarkness();
			}
		}

		if(target->flagIsSet(P_DARKNESS)) {
			op = target->first_obj;
			while(op) {
				op->obj->remDarkness();
				op = op->next_tag;
			}
			player->print("The aura of darkness around you dissipates.\n");
			broadcast(player->getSock(), player->getRoom(), "The aura of darkness around %N dissipates.", player);
			target->clearFlag(P_DARKNESS);
		}
	}
	return(1);
}