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