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