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/
/*
 * necromancy.cpp
 *	 Necromancy spells
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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"


//*********************************************************************
//						splHarm
//*********************************************************************

int splHarm(Creature* player, cmd* cmnd, SpellData* spellData) {
	Player	*pPlayer = player->getPlayer();
	Creature* target=0;
	int		nocast=0, roll=0, dmg=0, bns=0, saved=0;


	if(player->getClass() != CLERIC && !player->isCt() && spellData->how == CAST) {
		player->print("Your class prohibits you from casting that spell.\n");
		return(0);
	}

	if(pPlayer && pPlayer->getSecondClass())
		nocast=1;

	switch(player->getDeity()) {
	case ENOCH:
	case CERIS:
	case JAKAR:
	case GRADIUS:
	case ARES:
	case KAMIRA:
	case LINOTHAN:
		nocast = 1;
		break;
	}

	if(nocast && player->getClass() == CLERIC && !player->isStaff() && spellData->how != POTION) {
		player->print("%s does not allow you to cast that spell.\n", gConfig->getDeity(player->getDeity())->getName().c_str());
		return(0);
	}

	// Harm self
	if(cmnd->num == 2) {

		if(spellData->how != POTION) {
			player->print("You can't do that!\n");
			return(0);
		} else {
			if(spellData->how == CAST && player->isPlayer())
				player->getPlayer()->statistics.offensiveCast();
			player->hp.setCur(MIN(player->hp.getCur(), mrand(1,10)));
			player->print("Your lifeforce is nearly sucked away by deadly magic.\n");
			return(0);
		}

	// Cast harm on another player or monster
	} else {
		if(noPotion(player, spellData))
			return(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(!dec_daily(&player->daily[DL_HARM]) && spellData->how == CAST &&
		        !player->isCt() && player->isPlayer()) {
			player->print("You have been granted that spell enough times for today.\n");
			return(0);
		}

		if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {

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

			if(pPlayer && target->isMonster() && !target->isPet()
					&& target->getMonster()->nearEnemy() && !target->getMonster()->isEnmCrt(player->name)) {
				player->print("Not while %N is in combat with someone.\n", target);
				return(0);
			}

			if(target->flagIsSet(P_LINKDEAD) && target->isPlayer() && target->getClass() != LICH) {
				player->print("%M is immune to that right now.\n", target);
				return(0);
			}

			if(target->isPlayer() && target->getClass() != LICH)
				target->wake("Terrible nightmares disturb your sleep!");
			player->print("You cast a harm spell on %N.\n", target);
			target->print("%M casts a harm spell on you!\n", player);
			broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts a harm spell on %N!", player, target);
			if(target->isMonster())
				target->getMonster()->addEnmCrt(player);

			if(spellData->how == CAST && player->isPlayer())
				player->getPlayer()->statistics.offensiveCast();

			if(target->isPlayer() && target->getClass() == LICH) {
				target->hp.restore();

				player->print("Your harm spell completely heals %s.\n", target->name);
				broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M's harm spell completely heals %N.", player, target);
				target->print("Your life force is completely regenerated by %N's harm spell.\n", player);
				return(0);
			}


			bns = 10 * ((int)target->getLevel() - (int)player->getLevel());

			if(target->isMonster() && target->flagIsSet(M_PERMENANT_MONSTER))
				bns += 40;

			saved = target->chkSave(DEA, player, bns);



			if(!saved || player->isCt()) {
				roll = mrand(1,10);
				dmg = target->hp.getCur() - roll;

				target->hp.setCur(MIN(target->hp.getCur(), roll));
				target->print("Your lifeforce is nearly sucked away by deadly magic.\n");
				player->print("Your harm spell nearly sucks the life out of %N!\n", target);
				broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M's harm spell sucks away %N's life.", player, target);
				if(target->isMonster())
					target->getMonster()->addEnmDmg(player, dmg);

			} else {

				dmg = target->hp.getCur()/4;

				player->print("Your harm spell doubles %N over in pain.\n", target);
				target->print("Part of your lifeforce is sucked away by deadly magic.\n");
				broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M's harm wracks %N with pain.", player, target);
				player->doDamage(target, dmg, NO_CHECK);
			}
		}
	}

	return(1);
}

//********************************************************************
//						drain_exp
//*********************************************************************
// The spell drain_exp causes a player to lose a selected amout of
// exp.  When a player loses exp, the player's magical realm and
// weapon procifiency will reflect the change.  This spell is not
// intended to be learned or casted by a player.

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

	unsigned long loss=0;

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

	if(spellData->how == SCROLL) {
		player->print("You may not cast that spell.\n");
		return(0);
	}

	// drain exp on self
	if(cmnd->num == 2) {

		if(spellData->how == POTION || spellData->how == WAND)
			loss = dice(player->getLevel(), player->getLevel(), (player->getLevel()) * 10);

		else if(spellData->how == CAST)
			loss = dice(player->getLevel(), player->getLevel(), 1);

		loss = MIN(loss, player->getExperience());

		if(spellData->how == CAST || spellData->how == WAND) {
			player->print("You cast an energy drain spell on yourself.\n");
			player->print("You lose %d experience.\n", loss);
			broadcast(player->getSock(), player->getRoom(), "%M casts energy drain on %sself.", player, player->himHer());
		} else if(spellData->how == POTION) {
			player->print("You feel your experience slipping away.\n");
			player->print("You lose %d experience.\n", loss);
		}
		player->subExperience(loss);

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

		loss = dice(player->getLevel(), player->getLevel(), 1);

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


		loss = MIN(loss, target->getExperience());
		if(spellData->how == CAST || spellData->how == WAND) {
			player->print("You cast energy drain on %N.\n", target);
			broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts energy drain on %N.", player, target);
			target->print(
			      "%M casts energy drain on you.\nYou feel your experience slipping away.\n",
			      player);
			target->print("You lose %d experience.\n", loss);
			player->print("%M loses %d experience.\n", target, loss);
		}

		target->subExperience(loss);
	}

	return(1);
}

//*********************************************************************
//						animate_dead
//*********************************************************************
// This function allows clerics of Aramon to invoke undead targets.

int animateDeadCmd(Player* player, cmd* cmnd) {
	SpellData data;
	data.set(SKILL, NECROMANCY, 0, player);
	if(!data.check(player))
		return(0);
	return(animate_dead(player, cmnd, &data));
}

int animate_dead(Creature* player, cmd* cmnd, SpellData* spellData) {
	Player* pPlayer = player->getPlayer();
	if(!pPlayer)
		return(0);

	Monster *target=0;
	int		title=0, mp=0, shocked=0, level=0, skLevel=0;
	int		crt_num=0, buff=0, interval=0;
	ctag	*cp=0;
	time_t	t, i;


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

	if(!strncmp(cmnd->str[0], "animate", strlen(cmnd->str[0])))
		spellData->how = SKILL;

	if(noPotion(player, spellData))
		return(0);

	if(spellData->how == SKILL && !player->knowsSkill("animate")) {
		player->print("You lack the ability to call forth the dead.\n");
		return(0);
	}

	if(spellData->how == CAST) {
		if(!(player->getClass() == CLERIC && player->getDeity() == ARAMON) &&
			!player->isCt()
		) {
			player->print("Only Clerics of %s may cast that spell.\n", gConfig->getDeity(ARAMON)->getName().c_str());
			return(0);
		}
	}



	if(!player->isCt() &&
		(spellData->how == CAST || spellData->how == SKILL) &&
		player->getAdjustedAlignment() > REDDISH
	) {
		player->print("You are not evil enough to do that!\n");
		shocked = mrand(5,10);

		player->print("You are shocked for %d damage by %s's wrath!\n", shocked, gConfig->getDeity(player->getDeity())->getName().c_str());

		if(player->hp.getCur() > 10)
			player->hp.decrease(shocked);
		else
			player->hp.setCur(1);

		return(0);
	}

	cp = player->first_fol;
	while(cp) {
		if(cp->crt->isMonster() && cp->crt->isPet()) {
			player->print("Only one target may follow you at a time!\n");
			return(0);
		}
		cp = cp->next_tag;
	}

	/*
	if(spellData->how == SKILL) {
		skLevel=(int)player->getSkillLevel("animate");
	} else {
		skLevel = player->getLevel();
	}
	*/
	// TODO: conjure/animate is no longer a skill until progression
	// has been fixed
	skLevel = player->getLevel();


	if(player->getClass() == CLERIC && pPlayer->getSecondClass() == ASSASSIN)
		skLevel = MAX(1, skLevel-3);

	title = getPetTitle(spellData->how, skLevel, spellData->how == WAND && player->getClass() != CLERIC && player->getDeity() != ARAMON, true);
	mp = 4 * title;

	if(spellData->how == CAST && !player->checkMp(mp))
		return(0);

	t = time(0);
	i = LT(player, LT_ANIMATE);
	if(i > t && !player->isCt() && spellData->how != WAND) {
		player->pleaseWait(i-t);
		return(0);
	}
	if(spellData->how == CAST) {
		if(spell_fail(player, spellData->how)) {
			player->subMp(mp);
			return(0);
		}
	}


	crt_num = MAX(BASE_UNDEAD, BASE_UNDEAD + 3*(title-1));
	// 0 = weak, 1 = normal, 2 = buff
	buff = mrand(1,3) - 1;

	switch(buff) {
	case 0:
		// medium undead, add 1
		crt_num++;
		level = player->getLevel() - 3;
		break;
	case 1:
		// weak undead
		level = player->getLevel() - 2;
		break;
	case 2:
		// Buff undead, add 2
		level = player->getLevel() - mrand(0,1);
		crt_num += 2;
		break;
	default:
		player->print("Something's wrong.\n");
		break;
	}


	//	player->print("Title-1: %d     crt_num: %d        buff: %d\n", title-1, crt_num, buff);
	// load the undead Creature
	if(!loadMonster(crt_num, &target)) {
		player->print("Error loading undead target (%d)!\n", crt_num);
		return(0);
	}

	player->print("A %s crawls forth from the earth to aid you.\n", target->name);
	player->checkImprove("animate", true);
	broadcast(pPlayer->getSock(), player->getRoom(), "A %s crawls forth from the earth to aid %N.",
		target->name, player);

	// add mob to the room, make it active, and make it follow the summoner
	target->updateAttackTimer();
	target->addToRoom(player->getRoom());
	gServer->addActive(target);

	addFollower(player, target, FALSE);

	petTalkDesc(target, player);
	target->setFlag(M_PET);
	target->setFlag(M_UNDEAD);
	// pets use LT_SPELL
	target->lasttime[LT_SPELL].ltime = t;
	target->lasttime[LT_SPELL].interval = 3;
	target->proficiency[1] = CONJUREANIM;


	level = MAX(MIN(40,level), 1);
	target->setLevel(level);

	// This will be adjusted in 2.50, for now just str*1.75
	target->setAttackPower((unsigned int)(target->strength.getCur()*1.75));

	target->setDefenseSkill(((target->getLevel()-1) * 10) + (buff*5));
	target->setWeaponSkill(((target->getLevel()-1) * 10) + (buff*5));


	// find out how long it's going to last and create all the timeouts
	//interval = (60L*mrand(2,4)) + (bonus((int) player->piety.getCur())*60L >= 0 ? bonus((int) pPlayer->piety.getCur())*60L : 0);		/* + 60 * title; */

	interval = ((60L*mrand(3,5)) + (60L*bonus((int) player->piety.getCur())));
	target->lasttime[LT_ANIMATE].ltime = t;
	target->lasttime[LT_ANIMATE].interval = interval;
	player->lasttime[LT_ANIMATE].ltime = t;
	player->lasttime[LT_ANIMATE].interval = 900L;
	if(player->isCt())
		player->lasttime[LT_ANIMATE].interval = 6L;

	if(spellData->how == CAST)
		player->mp.decrease(mp);

	if(spellData->how == SKILL)
		return(PROMPT);
	return(1);
}

//*********************************************************************
//						splNecroDrain
//*********************************************************************

int splNecroDrain(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* target=0;
	bstring spell = "";
	int tier=0, c=0;
	osp_t osp;

	for(c=0; ospell[c].splno != get_spell_num(spellData->splno); c++)
		if(ospell[c].splno == -1)
			return(0);
	osp = ospell[c];

	switch(spellData->splno) {
	case S_SAP_LIFE:
		tier = 1;
		spell = "sap life";
		break;
	case S_LIFETAP:
		tier = 2;
		spell = "lifetap";
		break;
	case S_LIFEDRAW:
		tier = 3;
		spell = "lifedraw";
		break;
	case S_DRAW_SPIRIT:
		spell = "draw spirit";
		tier = 4;
		break;
	case S_SIPHON_LIFE:
		tier = 5;
		spell = "siphon life";
		break;
	case S_SPIRIT_STRIKE:
		tier = 6;
		spell = "spirit strike";
		break;
	case S_SOULSTEAL:
		tier = 7;
		spell = "soulsteal";
		break;
	case S_TOUCH_OF_KESH:
		tier = 8;
		spell = "touch of Kesh";
		break;
	default:
		player->print("Your spell fails unexpectedly.\n");
		return(0);
	}

	if(cmnd->num == 2) {
		target = player;
	} else {
		if(noPotion(player, spellData))
			return(0);

		target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], true, true);
		if(!target || target == player || (target->isPlayer() && strlen(cmnd->str[2]) < 3)) {
			player->print("That's not here.\n");
			return(0);
		}
		if(player->isMonster()) {
			// for monster casting we need to make sure its not on itself
			if(player == target) {
				// look for second creature with same name
				target = player->getRoom()->findCreature(player, cmnd->str[2], 2, true, true);
				if(!target || target == player)
					return(0);
			}
		}

	}

	return(doOffensive(player, target, spellData, spell.c_str(), &osp));
}