/*
* magic1.cpp
* User routines dealing with magic spells. Potions, wands, scrolls, and casting are all covered.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* 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 "move.h"
#include "magic.h"
#include "commands.h"
#include "craft.h"
#include "unique.h"
#include "effects.h"
#include "alchemy.h"
void spellShortcut(char *spell) {
if(!strcasecmp(spell, "m"))
strcpy(spell, "mend");
if(!strcasecmp(spell, "v"))
strcpy(spell, "vigor");
if(!strcasecmp(spell, "d-i"))
strcpy(spell, "detect-invis");
if(!strcasecmp(spell, "d-m"))
strcpy(spell, "detect-magic");
}
int doMpCheck(Creature* target, int splno) {
// If required MP is set to -1, the spell function is reponsible for verifying
// the caster has enough MP, otherwise it is checked here
int reqMp = getSpellMp(splno);
if(reqMp != -1) {
if(!target->checkMp(reqMp))
return(0);
// Handle spell fail here now if the spell doesn't handle mp on it's own
if(!(splno == S_VIGOR || splno == S_MEND_WOUNDS) && spell_fail(target, CAST)) {
// Reduced spell fails to half mp
target->subMp(MAX(1,reqMp/2));
return(0);
}
}
return(reqMp);
}
//*********************************************************************
// cmdCast
//*********************************************************************
// wrapper for doCast because doCast returns more information than we want
int cmdCast(Creature* creature, cmd* cmnd) {
doCast(creature, cmnd);
return(0);
}
//*********************************************************************
// cmdCast
//*********************************************************************
// This function allows a creature to cast a magical spell. It looks at
// the second parsed word to find out if the spell-name is valid, and
// then calls the appropriate spell function.
CastResult doCast(Creature* creature, cmd* cmnd) {
long i=0, t=0;
int (*fn)(SpellFn);
int c=0, match=0, n=0, num=0, lvl=0, reqMp=0;
ctag *cp=0;
Player* player = creature->getMaster();
bool offensive=false, self = (!player || player == creature);
Creature* listen = (player ? player : creature);
SpellData data;
if(player) {
player->clearFlag(P_AFK);
player->setFishing(false);
if(!player->ableToDoCommand())
return(CAST_RESULT_FAILURE);
}
if(creature->getClass() == BERSERKER) {
listen->print("Cast? BAH! Magic is for the weak! Bash and cut instead!\n");
return(CAST_RESULT_FAILURE);
}
if(player && player->getClass() == FIGHTER && !player->getSecondClass() && player->flagIsSet(P_PTESTER)) {
listen->print("You lack training in the arcane arts.\n");
return(CAST_RESULT_FAILURE);
}
if(cmnd->num < 2) {
if(self)
listen->print("Cast what?\n");
else
listen->print("Tell %N to cast what?\n", creature);
return(CAST_RESULT_FAILURE);
}
// always let staff cast
if(!player || !player->isStaff()) {
if(creature->isEffected("confusion") || creature->isEffected("drunkenness")) {
if(self)
listen->print("Your mind is too clouded for that right now.\n");
else
listen->print("%M's mind is too clouded for that right now.\n", creature);
return(CAST_RESULT_CURRENT_FAILURE);
}
if(player && player->isBlind()) {
listen->printColor("^CYou can't see to direct the incantation!\n");
return(CAST_RESULT_CURRENT_FAILURE);
}
if(creature->isBlind()) {
listen->printColor("^C%M can't see to direct the incantation!\n", creature);
return(CAST_RESULT_CURRENT_FAILURE);
}
if(player && !player->canSpeak()) {
listen->printColor("^yYou can't speak the incantation!\n");
return(CAST_RESULT_CURRENT_FAILURE);
}
if(!creature->canSpeak()) {
listen->printColor("^y%M can't speak the incantation!\n", creature);
return(CAST_RESULT_CURRENT_FAILURE);
}
}
spellShortcut(cmnd->str[1]);
do {
if(!strcmp(cmnd->str[1], get_spell_name(c))) {
match = 1;
data.splno = c;
break;
} else if(!strncmp(cmnd->str[1], get_spell_name(c), strlen(cmnd->str[1]))) {
match++;
data.splno = c;
}
c++;
} while(get_spell_num(c) != -1);
if(match == 0) {
listen->print("That spell does not exist.\n");
return(CAST_RESULT_FAILURE);
} else if(match > 1) {
listen->print("Spell name is not unique.\n");
return(CAST_RESULT_FAILURE);
}
if(!creature->spellIsKnown(get_spell_num(data.splno))) {
if(self)
listen->print("You don't know that spell.\n");
else
listen->print("%M doesn't know that spell.\n", creature);
return(CAST_RESULT_FAILURE);
}
if( creature->getRoom()->flagIsSet(R_NO_MAGIC) &&
!creature->checkStaff("Nothing happens.\n")
)
return(CAST_RESULT_FAILURE);
if( player == creature &&
player->flagIsSet(P_SITTING) &&
data.splno != S_VIGOR &&
data.splno != S_MEND_WOUNDS &&
data.splno != S_BLESS &&
data.splno != S_PROTECTION &&
data.splno != S_HEAL
) {
listen->print("You cannot cast that while sitting.\n");
return(CAST_RESULT_FAILURE);
}
if(player == creature)
i = MAX(LT(creature, LT_SPELL), creature->lasttime[LT_READ_SCROLL].interval + 2);
else
i = LT(creature, LT_SPELL);
t = time(0);
if(t < i && i != MAXINT) {
listen->pleaseWait(i-t);
return(CAST_RESULT_CURRENT_FAILURE);
}
num = get_spell_lvl(data.splno);
if(num == 3)
lvl = 5;
else if(num == 4)
lvl = 10;
else if(num == 5)
lvl = 16;
else if(num == 6)
lvl = 16;
else
lvl = 0;
if(player == creature && !player->isCt() && num > 0) {
if( (num == 3 && player->getLevel() < 5) ||
(num == 4 && player->getLevel() < 10) ||
(num == 5 && player->getLevel() < 16) ||
(num == 6 && player->getLevel() < 16)
) {
listen->print("You must be at least level %d to cast that spell.\n", lvl);
return(CAST_RESULT_FAILURE);
}
}
if(!player || !player->isStaff()) {
cp = creature->getRoom()->first_mon;
while(cp) {
if(cp->crt->flagIsSet(M_ANTI_MAGIC_AURA)) {
broadcast(NULL, creature->getRoom(), "^B%M glows bright blue.", cp->crt);
if(self)
listen->printColor("^yYour spell fails.\n");
else
listen->printColor("^y%M's spell fails.\n", creature);
return(CAST_RESULT_SPELL_FAILURE);
}
cp = cp->next_tag;
}
}
reqMp = doMpCheck(creature, data.splno);
if(!reqMp)
return(CAST_RESULT_CURRENT_FAILURE);
fn = get_spell_function(data.splno);
data.set(CAST, get_spell_school(data.splno), 0, creature);
if(!data.check(creature))
return(CAST_RESULT_FAILURE);
offensive = (int(*)(SpellFn, char*, osp_t*))fn == splOffensive ||
(int(*)(SpellFn, char*, osp_t*))fn == splMultiOffensive;
if(offensive) {
for(c=0; ospell[c].splno != get_spell_num(data.splno); c++)
if(ospell[c].splno == -1)
return(CAST_RESULT_FAILURE);
n = ((int(*)(SpellFn, const char*, osp_t*))*fn) (creature, cmnd, &data, get_spell_name(data.splno), &ospell[c]);
} else {
n = ((int(*)(SpellFn))*fn) (creature, cmnd, &data);
}
// Spell failed or didn't cast
if(!n)
return(CAST_RESULT_SPELL_FAILURE);
// If reqMp is valid, subtract the mp here
if(reqMp != -1)
creature->subMp(reqMp);
if(player) {
player->statistics.cast();
player->unhide();
}
if(player != creature)
creature->unhide();
creature->lasttime[LT_SPELL].ltime = t;
if(player != creature) {
creature->lasttime[LT_SPELL].interval = 4;
creature->updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
return(CAST_RESULT_SUCCESS);
}
switch(player->getClass()) {
case DUNGEONMASTER:
case CARETAKER:
player->lasttime[LT_SPELL].interval = 0;
break;
case MAGE:
if(player->getSecondClass() == MAGE || player->getSecondClass() == ASSASSIN)
player->lasttime[LT_SPELL].interval = 4;
else
player->lasttime[LT_SPELL].interval = 3;
break;
case BUILDER:
case LICH:
player->lasttime[LT_SPELL].interval = 3;
break;
case BARD:
case VAMPIRE:
case PALADIN:
case DRUID:
player->lasttime[LT_SPELL].interval = 4;
break;
case CLERIC:
if(player->getSecondClass() == ASSASSIN)
player->lasttime[LT_SPELL].interval = 5;
else
player->lasttime[LT_SPELL].interval = 4;
break;
case FIGHTER:
if(player->getSecondClass() == MAGE) {
bool heavy = false;
for(int z = 0 ; z < MAXWEAR ; z++) {
if(player->ready[z] && (player->ready[z]->isHeavyArmor() || player->ready[z]->isMediumArmor())) {
player->printColor("^W%P^x hinders your movement!\n",player->ready[z]);
heavy = true;
break;
}
}
if(heavy)
player->lasttime[LT_SPELL].interval = 5;
else
player->lasttime[LT_SPELL].interval = 4;
}
else
player->lasttime[LT_SPELL].interval = 5;
break;
case THIEF:
player->lasttime[LT_SPELL].interval = 5;
break;
default:
player->lasttime[LT_SPELL].interval = 5;
break;
}
// casting time settings for some specific spells override standard
// casting delays for classes. -TC
switch(data.splno) {
case S_REJUVENATE:
player->lasttime[LT_SPELL].interval = 20L;
break;
}
// improve schools of magic
// Can't improve evocation spells by casting on yourself
if(data.skill != "" && (data.school != EVOCATION || !self))
player->checkImprove(data.skill, true);
return(CAST_RESULT_SUCCESS);
}
//*********************************************************************
// cmdTeach
//*********************************************************************
// This function allowsspell-casters to teach other players basic spells
int cmdTeach(Player* player, cmd* cmnd) {
Creature* target=0;
int splno=0, c=0, match=0;
bstring skill = "";
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 3) {
player->print("Teach whom what?\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CYou're blind!\n");
return(0);
}
if(!player->canSpeak()) {
player->printColor("^yYou can't speak to do that!\n");
return(0);
}
if(!player->isStaff()) {
if((player->getClass() != MAGE && player->getClass() != CLERIC)) {
player->print("Only mages and clerics may teach spells.\n");
return(0);
}
if(player->getSecondClass()) {
player->print("Only true mages and clerics are able to teach.\n");
return(0);
}
} else if(player->getClass() == BUILDER) {
if(!player->canBuildMonsters())
return(cmdNoAuth(player));
if(!player->checkBuilder(player->parent_rom)) {
player->print("Error: room number not in any of your alotted ranges.\n");
return(0);
}
}
target = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], false);
if(!target || target == player) {
player->print("You don't see that person here.\n");
return(0);
}
if(target->flagIsSet(P_LINKDEAD) && target->isPlayer()) {
player->print("%M doesn't want to be taught right now.\n", target);
return(0);
}
spellShortcut(cmnd->str[2]);
do {
if(!strcmp(cmnd->str[2], get_spell_name(c))) {
match = 1;
splno = c;
break;
} else if(!strncmp(cmnd->str[2], get_spell_name(c),
strlen(cmnd->str[2]))) {
match++;
splno = c;
}
c++;
} while(get_spell_num(c) != -1);
if(match == 0) {
player->print("That spell does not exist.\n");
return(0);
}
else if(match > 1) {
player->print("Spell name is not unique.\n");
return(0);
}
if(!player->spellIsKnown(get_spell_num(splno))) {
player->print("You don't know that spell.\n");
return(0);
}
if(!player->isStaff()) {
if(player->getClass() == CLERIC && get_spell_num(splno) != 0 && get_spell_num(splno) != 3 && get_spell_num(splno) != 4) {
player->print("You may not teach that spell to anyone.\n");
return(0);
}
if(player->getClass() == CLERIC && (get_spell_num(splno) == 3) && target->getClass() != CLERIC && target->getClass() != PALADIN) {
player->print("You may only teach that spell to clerics and paladins.\n");
return(0);
}
if(player->getClass() == MAGE && get_spell_num(splno) != 2
&& get_spell_num(splno) != 5 && get_spell_num(splno) != 27 && get_spell_num(splno) != 28) {
player->print("You may not teach that spell to anyone.\n");
return(0);
}
if(target->isMonster()) {
player->print("You may not teach spells to monsters.\n");
return(0);
}
} else if(player->getClass() == BUILDER) {
if(target->isPlayer()) {
player->print("You may not teach spells to players.\n");
return(0);
}
}
if(target->isPlayer() && !player->isDm() && get_spell_num(splno) != 2 && get_spell_num(splno) != 5 && get_spell_num(splno) != 27
&& get_spell_num(splno) != 28 && get_spell_num(splno) != 0 && get_spell_num(splno) != 3 && get_spell_num(splno) != 4) {
player->print("You may not teach those spells to anyone.\n");
return(0);
}
player->unhide();
if(target->spellIsKnown(get_spell_num(splno)) && target->isMonster() && player->isStaff()) {
logn("log.teach", "%s caused %s to forget %s.\n", player->name, target->name, get_spell_name(splno));
player->print("Spell \"%s\" removed from %N's memory.\n", get_spell_name(splno), target);
target->forgetSpell(get_spell_num(splno));
} else if(target->spellIsKnown(get_spell_num(splno)) && target->isPlayer() && player->isDm()) {
logn("log.teach", "%s caused %s to forget %s.\n", player->name, target->name, get_spell_name(splno));
player->print("Spell \"%s\" removed from %N's memory.\n", get_spell_name(splno), target);
target->forgetSpell(get_spell_num(splno));
} else {
if(target->isPlayer()) {
skill = spellSkill(get_spell_school(splno));
if(skill != "") {
if(!player->knowsSkill(skill)) {
player->print("You are unable to teach %s spells.\n", gConfig->getSkillDisplayName(skill).c_str());
return(0);
}
if(!target->knowsSkill(skill)) {
player->print("%M is unable to learn %s spells.\n", target, gConfig->getSkillDisplayName(skill).c_str());
return(0);
}
}
}
target->learnSpell(get_spell_num(splno));
target->print("%M teaches you the %s spell.\n", player,
get_spell_name(splno));
player->print("Spell \"%s\" taught to %N.\n", get_spell_name(splno), target);
if(!player->flagIsSet(P_DM_INVIS) && !player->flagIsSet(P_INCOGNITO)) {
broadcast(player->getSock(), target->getSock(), player->getRoom(),
"%M taught %N the %s spell.", player, target, get_spell_name(splno));
}
}
return(0);
}
//*********************************************************************
// cmdStudy
//*********************************************************************
// This function allows a player to study a scroll, and learn the spell
// that is on it.
int cmdStudy(Player* player, cmd* cmnd) {
Object *object=0;
bstring skill = "";
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
player->print("Study what?\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CYou're blind!\n");
return(0);
}
object = findObject(player, player->first_obj, cmnd);
if(!object) {
player->print("You don't have that.\n");
return(0);
}
if(object->getType() != SCROLL && object->getType() != SONGSCROLL && !object->getRecipe()) {
player->print("That's not a scroll.\n");
return(0);
}
if(object->doRestrict(player, true))
return(0);
player->unhide();
if(!object->getRecipe() && (object->getMagicpower() - 1 < 0 || object->getMagicpower() - 1 > MAXSPELL)) {
player->print("Error: Bad Scroll.\n");
broadcast(isCt, "^y%s has a bad scroll!\n", player->name);
loge("Study: %s has a bad scroll.\n", player->name);
return(0);
}
if(object->getRecipe()) {
Recipe* recipe = gConfig->getRecipe(object->getRecipe());
if(!recipe) {
player->print("Error: Bad Recipe.\n");
broadcast(isCt, "^y%s has a bad recipe!^x\n", player->name);
loge("Study: %s has a bad recipe.\n", player->name);
return(0);
}
player->checkFreeSkills(recipe->getSkill());
if(recipe->getSkill() != "" && !player->knowsSkill(recipe->getSkill())) {
player->print("Sorry, you don't have enough training in %s to learn that recipe.\n", recipe->getSkill().c_str());
return(0);
}
player->printColor("You learn the art of making %s.\n", recipe->getResultName().c_str());
player->learnRecipe(recipe);
} else if(object->getType() == SCROLL) {
skill = spellSkill(get_spell_school(object->getMagicpower() - 1));
if(skill != "" && !player->knowsSkill(skill)) {
player->print("You are unable to learn %s spells.\n", gConfig->getSkillDisplayName(skill).c_str());
return(0);
}
player->print("You learn the %s spell.\n", get_spell_name(object->getMagicpower() - 1));
player->learnSpell(object->getMagicpower() - 1);
} else if(object->getType() == SONGSCROLL) {
player->print("You learn the song of %s.\n", get_song_name(object->getMagicpower() - 1));
player->learnSong(object->getMagicpower() - 1);
}
player->printColor("%O disintegrates!\n", object);
broadcast(player->getSock(), player->getRoom(), "%M studies %1P.", player, object);
player->delObj(object, true);
delete object;
return(0);
}
//*********************************************************************
// cmdReadScroll
//*********************************************************************
// This function allows a player to read a scroll and cause its magical
// spell to be cast. If a third word is used in the command, then that
// player or monster will be the target of the spell.
int cmdReadScroll(Player* player, cmd* cmnd) {
Object *object=0;
int (*fn)(SpellFn);
long i=0, t=0;
int n=0, match=0, c=0;
ctag *cp=0;
bool dimensionalFailure=false;
SpellData data;
fn = 0;
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
player->print("Read what?\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CYou're blind!\n");
return(0);
}
object = findObject(player, player->first_obj, cmnd);
if(!object || !cmnd->val[1]) {
for(n = 0; n < MAXWEAR; n++) {
if(!player->ready[n])
continue;
if(keyTxtEqual(player->ready[n], cmnd->str[1]))
match++;
else
continue;
if(match == cmnd->val[1] || !cmnd->val[1]) {
object = player->ready[n];
break;
}
}
}
if(!object) {
player->print("You don't have that.\n");
return(0);
}
if(object->getSpecial()) {
n = special_obj(player, cmnd, SP_MAPSC);
if(n != -2)
return(MAX(0, n));
}
if(object->getType() != SCROLL) {
player->print("That's not a scroll.\n");
return(0);
}
if(object->doRestrict(player, true))
return(0);
if( (player->getRoom()->flagIsSet(R_NO_MAGIC)) ||
(object->getMagicpower() - 1 < 0)
) {
player->print("Nothing happens.\n");
return(0);
}
i = MAX(LT(player, LT_READ_SCROLL), player->lasttime[LT_SPELL].ltime + 2);
t = time(0);
if(i > t) {
player->pleaseWait(i - t);
return(0);
}
cp = player->getRoom()->first_mon;
while(cp) {
if(cp->crt->flagIsSet(M_ANTI_MAGIC_AURA)) {
broadcast(NULL, player->getRoom(), "^B%M glows bright blue.", cp->crt);
player->print("Nothing happens.\n");
return(0);
}
cp = cp->next_tag;
}
player->unhide();
player->lasttime[LT_READ_SCROLL].ltime = t;
player->lasttime[LT_READ_SCROLL].interval = 3;
if(LT(player, LT_PLAYER_BITE) <= t) {
player->lasttime[LT_PLAYER_BITE].ltime = t;
player->lasttime[LT_PLAYER_BITE].interval = 3L;
}
if(object->getMagicpower() - 1 < 0 || object->getMagicpower() - 1 > MAXSPELL) {
player->print("Error: Bad Scroll.\n");
broadcast(isCt, "^y%s has a bad scroll!\n", player->name);
loge("Readscroll: %s has a bad scroll.\n", player->name);
return(0);
}
if(spell_fail(player, SCROLL)) {
player->printColor("%O disintegrates.\n", object);
player->delObj(object, true);
delete object;
return(0);
}
data.splno = object->getMagicpower() - 1;
fn = get_spell_function(data.splno);
data.set(SCROLL, get_spell_school(data.splno), object, player);
if(!data.check(player))
return(0);
// check for dimensional anchor
if(hinderedByDimensionalAnchor(data.splno) && player->checkDimensionalAnchor())
dimensionalFailure = true;
// if the spell failed due to dimensional anchor, don't even run this
if(!dimensionalFailure) {
if((int(*)(SpellFn, char*, osp_t*))fn == splOffensive ||
(int(*)(SpellFn, char*, osp_t*))fn == splMultiOffensive) {
for(c = 0; ospell[c].splno != get_spell_num(data.splno); c++)
if(ospell[c].splno == -1)
return(0);
n = ((int(*)(SpellFn, const char*, osp_t*))*fn) (player, cmnd, &data, get_spell_name(data.splno),
&ospell[c]);
} else {
n = ((int(*)(SpellFn))*fn) (player, cmnd, &data);
}
}
if(n || dimensionalFailure) {
if(object->use_output[0] && !dimensionalFailure)
player->printColor("%s\n", object->use_output);
player->printColor("%O disintegrates.\n", object);
player->delObj(object, true);
delete object;
}
return(0);
}
//*********************************************************************
// consume
//*********************************************************************
// This function allows players to drink potions / eat food, thereby casting any
// spell it was meant to contain.
// return 0 on failure, 1 on drink, 2 on deleted object
// lagprot means we can bypass
int endConsume(Object* object, Player* player, bool forceDelete=false) {
if(object->flagIsSet(O_EATABLE)) {
player->print("Food eaten.\n");
broadcast(player->getSock(), player->getRoom(), "%M eats %1P.", player, object);
} else {
player->print("Potion drank.\n");
broadcast(player->getSock(), player->getRoom(), "%M drinks %1P.", player, object);
}
if(!forceDelete)
object->decShotscur();
if(forceDelete || object->getShotscur() < 1) {
if(object->flagIsSet(O_EATABLE))
player->printColor("You ate all of %P.\n", object);
else
player->printColor("You drank all of %P.\n", object);
player->delObj(object, true);
delete object;
return(2);
}
return(1);
}
int consume(Player* player, Object* object, cmd* cmnd) {
int c=0, splno=0, n=0;
int (*fn)(SpellFn);
bool dimensionalFailure=false;
bool eat = object->flagIsSet(O_EATABLE), drink=object->flagIsSet(O_DRINKABLE) || object->getType() == POTION;
const bstring& effect = object->getEffect();
fn = 0;
if(player->isStaff() && object->getType() != POTION && !strcmp(cmnd->str[0], "eat")) {
broadcast(player->getSock(), player->getRoom(), "%M eats %1P.", player, object);
player->printColor("You ate %P.\n", object);
player->delObj(object);
delete object;
return(0);
}
if(!strcmp(cmnd->str[0], "eat")) {
if(!eat && !player->checkStaff("You may not eat that.\n"))
return(0);
} else {
if(!drink && !player->checkStaff("You may not drink that.\n"))
return(0);
}
if(object->doRestrict(player, true))
return(0);
// Handle Alchemy Potions
if(object->isAlchemyPotion()) {
if(object->consumeAlchemyPotion(player))
return(endConsume(object,player));
else
return(0);
}
// they are eating a non-potion object
if( object->getShotscur() < 1 ||
(object->getMagicpower() - 1 < 0) ||
object->getType() != POTION
) {
player->unhide();
if(object->use_output[0])
player->printColor("%s\n", object->use_output);
// some food can heal you
if(object->flagIsSet(O_CONSUME_HEAL) && !player->isUndead())
player->hp.increase(object->damage.roll());
return(endConsume(object, player, true));
}
if(player->getRoom()->flagIsSet(R_NO_POTION)) {
if(!player->checkStaff("%O starts to %s before you %s it.\n",
object, eat ? "get moldy" : "evaporate", eat ? "eat" : "drink"))
return(0);
}
player->unhide();
if(object->getMagicpower() - 1 < 0 || object->getMagicpower() - 1 > MAXSPELL) {
player->print("Error: Bad Potion.\n");
broadcast(isCt, "^y%s has a bad potion!\n", player->name);
loge("Quaff: %s has a bad potion.\n", player->name);
return(0);
}
splno = object->getMagicpower() - 1;
fn = get_spell_function(splno);
// check for dimensional anchor
if(hinderedByDimensionalAnchor(splno) && player->checkDimensionalAnchor())
dimensionalFailure = true;
// if the spell failed due to dimensional anchor, don't even run this
if(!dimensionalFailure) {
SpellData data;
data.splno = splno;
data.set(POTION, get_spell_school(data.splno), object, player);
if( (int(*)(SpellFn, char*, osp_t*))fn == splOffensive ||
(int(*)(SpellFn, char*, osp_t*))fn == splMultiOffensive
) {
for(c = 0; ospell[c].splno != get_spell_num(data.splno); c++)
if(ospell[c].splno == -1)
return(0);
n = ((int(*)(SpellFn, const char*, osp_t*))*fn)
(player, cmnd, &data, get_spell_name(data.splno), &ospell[c]);
} else {
n = ((int(*)(SpellFn))*fn) (player, cmnd, &data);
}
}
if(n || dimensionalFailure) {
player->statistics.potion();
if(object->use_output[0] && !dimensionalFailure)
player->printColor("%s\n", object->use_output);
return(endConsume(object, player));
}
return(0);
}
// drink wrapper
int cmdConsume(Player* player, cmd* cmnd) {
Object *object=0;
int n=0, match=0;
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
if(strcmp(cmnd->str[0], "eat")) {
player->print("Drink what?\n");
} else {
player->print("Eat what?\n");
}
return(0);
}
object = findObject(player, player->first_obj, cmnd);
if(!object || !cmnd->val[1]) {
for(n = 0; n < MAXWEAR; n++) {
if(!player->ready[n])
continue;
if(keyTxtEqual(player->ready[n], cmnd->str[1]))
match++;
else
continue;
if(match == cmnd->val[1] || !cmnd->val[1]) {
object = player->ready[n];
break;
}
}
}
if(!object) {
player->print("You don't have that.\n");
return(0);
}
consume(player, object, cmnd);
return(0);
}
//*********************************************************************
// cmdUseWand
//*********************************************************************
// This function allows players to zap a wand or staff at another player
// or monster.
int cmdUseWand(Player* player, cmd* cmnd) {
Object *object=0;
long i=0, t=0;
int (*fn)(SpellFn);
bool dimensionalFailure=false;
int match=0, n=0, c=0;
ctag *cp=0;
SpellData data;
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
player->print("Use what?\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CYou're blind!\n");
return(0);
}
object = findObject(player, player->first_obj, cmnd);
if(!object || !cmnd->val[1]) {
for(n = 0; n < MAXWEAR; n++) {
if(!player->ready[n])
continue;
if(keyTxtEqual(player->ready[n], cmnd->str[1]))
match++;
else
continue;
if(match == cmnd->val[1] || !cmnd->val[1]) {
object = player->ready[n];
break;
}
}
}
if(!object) {
object = findObject(player, player->getRoom()->first_obj, cmnd);
if(object && !object->flagIsSet(O_CAN_USE_FROM_FLOOR)) {
player->print("You don't have that.\n");
return(0);
}
}
if(!object) {
player->print("You don't have that.\n");
return(0);
}
if(object->getType() != WAND) {
player->print("That's not a wand or staff.\n");
return(0);
}
if(object->doRestrict(player, true))
return(0);
if(object->getShotscur() < 1) {
player->print("It's used up.\n");
return(0);
}
if( (player->getRoom()->flagIsSet(R_NO_MAGIC)) ||
(object->getMagicpower() < 1)
) {
player->print("Nothing happens.\n");
return(0);
}
i = MAX(LT(player, LT_SPELL), player->lasttime[LT_READ_SCROLL].interval + 2);
t = time(0);
if(!player->isCt() && i > t) {
player->pleaseWait(i - t);
return(0);
}
player->unhide();
cp = player->getRoom()->first_mon;
while(cp) {
if(cp->crt->flagIsSet(M_ANTI_MAGIC_AURA)) {
broadcast(NULL, player->getRoom(), "^B%M glows bright blue.", cp->crt);
player->printColor("%O sputters and smokes.\n", object);
return(0);
}
cp = cp->next_tag;
}
data.splno = object->getMagicpower() - 1;
if(data.splno < 0 || data.splno > MAXSPELL) {
player->print("Error: Bad Wand.\n");
broadcast(isCt, "^y%s has a bad wand!\n", player->name);
loge("Study: %s has a bad wand.\n", player->name);
return(0);
}
data.set(WAND, get_spell_school(data.splno), object, player);
if(!data.check(player, true))
return(0);
player->lasttime[LT_SPELL].ltime = t;
player->lasttime[LT_SPELL].interval = 3;
player->statistics.wand();
if(spell_fail(player, WAND)) {
if(!object->flagIsSet(O_CAN_USE_FROM_FLOOR))
object->decShotscur();
if(object->getShotscur() < 1 && Unique::isUnique(object)) {
player->delObj(object, true);
delete object;
}
return(0);
}
fn = get_spell_function(data.splno);
n = 0;
// check for dimensional anchor
if(hinderedByDimensionalAnchor(data.splno) && player->checkDimensionalAnchor())
dimensionalFailure = true;
// if the spell failed due to dimensional anchor, don't even run this
if(!dimensionalFailure) {
if( (int(*)(SpellFn, char*, osp_t*))fn == splOffensive ||
(int(*)(SpellFn, char*, osp_t*))fn == splMultiOffensive
) {
for(c = 0; ospell[c].splno != get_spell_num(data.splno); c++)
if(ospell[c].splno == -1)
return(0);
n = ((int(*)(SpellFn, const char*, osp_t*))*fn) (player, cmnd, &data, get_spell_name(data.splno),
&ospell[c]);
} else { // if(!object->flagIsSet(ODDICE)) // Flag isn't used best i can tell
n = ((int(*)(SpellFn))*fn) (player, cmnd, &data);
}
// else
// n = (*fn) (player, cmnd, WAND, object);
}
if(n || dimensionalFailure) {
if(object->use_output[0] && !dimensionalFailure)
player->printColor("%s\n", object->use_output);
if(!object->flagIsSet(O_CAN_USE_FROM_FLOOR))
object->decShotscur();
if(object->getShotscur() < 1 && Unique::isUnique(object)) {
player->delObj(object, true);
delete object;
}
}
return(0);
}
//*********************************************************************
// cmdRecall
//*********************************************************************
// wrapper for the useRecallPotion function
int cmdRecall(Player* player, cmd* cmnd) {
useRecallPotion(player, 1, 0);
return(0);
}
//*********************************************************************
// recallLog
//*********************************************************************
// does the logging for a lag-protect hazy
void recallLog(Player* player, bstring name, bstring cname, bstring room) {
std::ostringstream log;
log << "### " << player->name << "(L" << player->getLevel() << ") hazied (" << name << ") ";
if(cname != "")
log << "(out of bag: " << cname << ") ";
log << "due to lag protection. HP: " << player->hp.getCur() << "/" << player->hp.getMax()
<< ". Room: " << room << " to " << player->getRoom()->fullName() << ".";
broadcast(isWatcher, "^C%s", log.str().c_str());
logn("log.lprotect", "%s", log.str().c_str());
}
//*********************************************************************
// recallCheckBag
//*********************************************************************
// This handles searching through a bag for hazy, since using a hazy
// inside a bag means we need to reduce its shots. Plus, del_obj_crt
// used from consume doesnt handle deleting it properly
int recallCheckBag(Player* player, Object *cont, cmd* cmnd, int show, int log) {
Object *object=NULL;
otag *cop=0, *prev=0;
int drank=0, first=1;
bstring room = player->getRoom()->fullName(), name = "";
prev = cont->first_obj;
cop = cont->first_obj;
while(cop) {
object = cop->obj;
cop = cop->next_tag;
name = object->name;
if(object->getMagicpower() == (S_WORD_OF_RECALL+1) && object->getType() == POTION) {
if(show)
player->printColor("Recall potion found in %s: %s. Initiating auto-recall.\n", cont->name, name.c_str());
drank = consume(player, object, cmnd);
if(drank) {
if(log)
recallLog(player, name, cont->name, room);
return(1);
}
if(show)
player->print("Unable to use potion. Continuing search.\n");
}
// don't move prev if we're at the first object
if(!first)
prev = prev->next_tag;
first=0;
}
return(0);
}
//*********************************************************************
// useRecallPotion
//*********************************************************************
// used for recall command and lagprotect
int useRecallPotion(Player* player, int show, int log) {
cmd *cmnd;
Object *object=NULL;
otag *op=0;
int i=0;
bstring room = player->getRoom()->fullName(), name = "";
if(player->flagIsSet(P_ANCHOR)) {
player->print("%s will not work while you are protected by a dimensional anchor.\n",
log ? "Automatic recall" : "The recall command");
return(0);
}
// the functions we call want a command...
// so we'll give them a fake one
cmnd = new cmd;
cmnd->num = 2;
player->print("Beginning recall sequence.\n");
// do they have anything on their person?
for(i=0; i<MAXWEAR; i++) {
object = player->ready[i];
if(object) {
name = object->name;
if(object->getMagicpower() == (S_WORD_OF_RECALL+1) && object->getType() == POTION) {
if(show)
player->printColor("Recall potion found: %s. Initiating auto-recall.\n", name.c_str());
if(consume(player, object, cmnd)) {
if(log)
recallLog(player, name, "", room);
return(1);
}
if(show)
player->print("Unable to use potion. Continuing search.\n");
}
if(object->getType() == CONTAINER)
if(recallCheckBag(player, object, cmnd, show, log))
return(1);
}
}
// check through their inventory
op = player->first_obj;
while(op) {
object = op->obj;
op = op->next_tag;
name = object->name;
// is this object it?
if(object->getMagicpower() == (S_WORD_OF_RECALL+1) && object->getType() == POTION) {
if(show)
player->printColor("Recall potion found: %s. Initiating auto-recall.\n", name.c_str());
if(consume(player, object, cmnd)) {
if(log)
recallLog(player, name, "", room);
return(1);
}
if(show)
player->print("Unable to use potion. Continuing search.\n");
}
// in a container?
if(object->getType() == CONTAINER) {
if(recallCheckBag(player, object, cmnd, show, log))
return(1);
}
}
player->print("No recall potions found!\n");
return(0);
}
//*********************************************************************
// logCast
//*********************************************************************
void logCast(Creature* caster, Creature* target, bstring spell, bool dmToo) {
Player* pCaster = caster->getPlayer();
if(!pCaster || !pCaster->isStaff() || (!dmToo && pCaster->isDm()))
return;
log_immort(true, pCaster, "%s cast an %s spell on %s in room %s.\n",
caster->name, spell.c_str(), target ? target->name : "self",
caster->getRoom()->fullName().c_str());
}
//*********************************************************************
// splGeneric
//*********************************************************************
int splGeneric(Creature* player, cmd* cmnd, SpellData* spellData, const char* article, const char* spell, bstring effect, int strength, long duration) {
Creature* target=0;
if(cmnd->num == 2) {
target = player;
if(spellData->how == CAST) {
player->print("You cast %s %s spell.\n", article, spell);
broadcast(player->getSock(), player->getRoom(), "%M casts %s %s spell.", player, article, spell);
}
} 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("You don't see that player here.\n");
return(0);
}
if(checkRefusingMagic(player, target))
return(0);
if((effect == "drain-shield" || effect == "undead-ward") && target->isUndead()) {
player->print("The spell fizzles.\n%M naturally resisted your spell.\n", target);
return(0);
}
broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts %s %s spell on %N.",
player, article, spell, target);
target->print("%M casts %s on you.\n", player, spell);
player->print("You cast %s %s spell on %N.\n", article, spell, target);
}
if(target->inCombat(false))
player->smashInvis();
if( target->hasPermEffect(effect) ||
(effect == "heat-protection" && target->hasPermEffect("alwayswarm")) ||
(effect == "warmth" && target->hasPermEffect("alwayscold"))
) {
player->print("The spell didn't take hold.\n");
return(0);
}
// these spells will counteract porphyria
if((effect == "drain-shield" || effect == "undead-ward") && target->isEffected("porphyria"))
target->removeEffect("porphyria");
if(spellData->how == CAST) {
if(player->getRoom()->magicBonus())
player->print("The room's magical properties increase the power of your spell.\n");
if(!target->addEffect(effect, player, FROM_CREATURE, true, player))
return(0);
} else {
target->addEffect(effect);
}
if(strength || duration) {
EffectInfo* eff = target->getEffect(effect);
if(strength)
eff->setStrength(strength);
if(duration)
eff->setDuration(duration);
}
return(1);
}
//*********************************************************************
// cmdTransmute
//*********************************************************************
// This allows a mage to recharge a wand provided they know the
// spell of the wand and have enough gold.
int cmdTransmute(Player* player, cmd* cmnd) {
Object *object=0;
unsigned long cost=0;
if(!player->ableToDoCommand())
return(0);
if(!player->isCt()) {
if(!player->knowsSkill("transmute")) {
player->print("You haven't a clue as to how to transmute anything!\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CYou're blind!\n");
return(0);
}
if(player->ready[WIELD-1]) {
player->print("Your hands are too full to do that.\n");
return(0);
}
}
if(!player->ready[HELD-1] || player->ready[HELD-1]->getType() != WAND) {
player->print("You must hold the wand you wish to recharge.\n");
return(0);
}
object = player->ready[HELD-1];
if(object->getShotscur()) {
player->printColor("That %P still has magic in it.\n", object);
return(0);
}
if(!player->spellIsKnown(object->getMagicpower() - 1)) {
player->print("You don't know the spell of this wand.\n");
return(0);
}
if(object->getMagicpower() == (S_TELEPORT+1) || object->getMagicpower() == (S_WORD_OF_RECALL+1)) {
player->print("Your transmutation fails.\n");
return(0);
}
// TODO: SKILLS: throw in a skill level check
cost = (unsigned)(1000 * object->getShotsmax());
if(object->flagIsSet(O_NO_FIX))
cost *= 2;
if(player->coins[GOLD] < cost || cost < 1) {
player->print("You don't have enough gold.\n");
return(0);
}
player->coins.sub(cost, GOLD);
if(!player->isCt() && !dec_daily(&player->daily[DL_RCHRG])) {
player->print("You have recharged enough wands for one day.\n");
return(0);
}
if(spell_fail(player, CAST)) {
int dmg = 0;
player->print("The wand glows bright red and explodes!\n");
broadcast(player->getSock(), player->getRoom(), "A wand explodes in %s's hand!\n", player->name);
if(player->chkSave(SPL, player, -1)) {
dmg = mrand(5, 10);
player->print("You managed to avoid most of the explosion.\n");
} else
dmg = mrand(10, 20);
player->doDamage(player, dmg, CHECK_DIE);
player->checkImprove("transmute", false);
player->unequip(HELD, UNEQUIP_DELETE);
return(0);
}
// success!
object->setShotscur(object->getShotsmax());
player->printColor("You successfully recharge the %s.\n", object->name);
player->checkImprove("transmute", true);
return(0);
}
//*********************************************************************
// teleport_trap
//*********************************************************************
// This functions allows a player to be teleported via a trap.
void teleport_trap(Player* player) {
BaseRoom *newRoom=0;
if(player->isStaff())
return;
newRoom = player->teleportWhere();
player->deleteFromRoom();
player->addToRoom(newRoom);
player->doPetFollow();
player->print("Your body is violently pulled in all directions.\n");
// player->hp.getCur() -= player->hp.getMax()/2;
player->doDamage(player, player->hp.getMax()/2, NO_CHECK);
player->hp.setCur(MAX(5, player->hp.getCur()));
// This is all done so a teleport trap
// will not be abused for exploring.
player->smashInvis();
player->lasttime[LT_SPELL].ltime = time(0);
player->lasttime[LT_SPELL].interval = 120L;
player->stun(mrand(20,95));
}
//*********************************************************************
// rock_slide
//*********************************************************************
void rock_slide(Player* player) {
Player *target=0;
int dmg=0;
ctag *cp=0;
cp = player->getRoom()->first_ply;
while(cp) {
target = cp->crt->getPlayer();
cp = cp->next_tag;
if(!target)
continue;
dmg = mrand(10, 20);
if(target->getClass() == LICH)
dmg *= 2;
if(target->chkSave(LCK, target, 0))
dmg /=2;
target->printColor("Falling rocks crush you for %s%d^x damage.\n", target->customColorize("*CC:DAMAGE*"), dmg);
target->doDamage(target, dmg, NO_CHECK);
if(target->hp.getCur() < 1) {
target->print("You are crushed to death by falling rocks.\n");
target->die(ROCKS);
}
}
}
//*********************************************************************
// splBlind
//*********************************************************************
// The blind spell prevents a player or monster from seeing. The spell
// results in a penalty on attacks, and an inability look at objects
// players, rooms, or inventory. Also a player or monster cannot read.
int splBlind(Creature* player, cmd* cmnd, SpellData* spellData) {
Creature* target=0;
if(player->getClass() == BUILDER) {
player->print("You cannot cast this spell.\n");
return(0);
}
// blind self
if(cmnd->num == 2) {
target = player;
if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
player->print("You are blind and can no longer see.\n");
broadcast(player->getSock(), player->getRoom(), "%M casts blindness on %sself.", player, player->himHer());
} else if(spellData->how == POTION)
player->print("Everything goes dark.\n");
// blind a monster or player
} else {
if(noPotion(player, spellData))
return(0);
target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], false);
if(!target || target == player) {
player->print("That's not here.\n");
return(0);
}
if(!player->canAttack(target))
return(0);
player->smashInvis();
target->wake("Terrible nightmares disturb your sleep!");
if(target->chkSave(SPL, player, 0) && !player->isCt()) {
target->print("%M tried to cast a blind spell on you!\n", player);
broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M tried to cast a blind spell on %N!", player, target);
player->print("Your spell fizzles.\n");
return(0);
}
if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
player->print("Blindness spell cast on %s.\n", target->name);
broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts a blindness spell on %N.", player, target);
target->print("%M casts a blindness spell on you.\n", player);
}
if(target->isMonster()) {
target->getMonster()->addEnmCrt(player);
}
}
if(target->isPlayer()) {
if(player->isCt())
target->setFlag(P_DM_BLINDED);
}
if(spellData->how == CAST && player->isPlayer())
player->getPlayer()->statistics.offensiveCast();
if(!player->isCt())
target->addEffect("blindness", 180 - (target->constitution.getCur()/10), 1, true, player);
else
target->addEffect("blindness", 600, 1);
return(1);
}
//*********************************************************************
// spell_fail
//*********************************************************************
// This function returns 1 if the casting of a spell fails, and 0 if it is
// sucessful.
int spell_fail(Creature* player, int how) {
Player *pPlayer = player->getPlayer();
int chance=0, n=0;
if(how == POTION)
return(0);
if(!pPlayer)
return(0);
n = mrand(1, 100);
if(pPlayer)
pPlayer->computeLuck();
switch (pPlayer->getClass()) {
case ASSASSIN:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 30;
break;
case BERSERKER:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5);
break;
case VAMPIRE:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 60;
break;
case CLERIC:
case DRUID:
case BARD:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 65;
break;
case FIGHTER:
if(pPlayer->getSecondClass() == MAGE)
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 75;
else
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 10;
break;
case MAGE:
case LICH:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 75;
break;
case MONK:
case WEREWOLF:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 6) + 25;
break;
case PALADIN:
case DEATHKNIGHT:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 50;
break;
case RANGER:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 4) + 56;
break;
case THIEF:
if(pPlayer->getSecondClass() == MAGE)
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 5) + 75;
else
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 6) + 22;
break;
case ROGUE:
chance = ((pPlayer->getLevel() + bonus((int) pPlayer->intelligence.getCur())) * 6) + 22;
break;
default:
return(0);
}
chance = chance * (pPlayer->getLuck() / 30);
if(n > chance) {
player->printColor("^yYour spell fails.\n");
return(1);
} else
return(0);
}
//*********************************************************************
// splJudgement
//*********************************************************************
// This function allows a DM to teleport themself or another player to jail
int splJudgement(Creature* player, cmd* cmnd, SpellData* spellData) {
Player *target=0;
Room *new_rom=0;
if(!player->isPlayer() || (!player->isCt() && spellData->how == CAST)) {
player->print("That spell does not exist.\n");
return(0);
}
if(!player->spellIsKnown(S_JUDGEMENT) && spellData->how == CAST) {
player->print("You are unable to pass judgement at this time.\n");
return(0);
}
CatRef cr;
cr.setArea("jail");
cr.id = 1;
if(!loadRoom(cr, &new_rom)) {
player->print("Spell failure.\n");
return(0);
}
// Cast judgement on yourself
if(cmnd->num == 2) {
target = player->getPlayer();
if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
player->print("You pass judgement upon yourself.\n");
broadcast(player->getSock(), player->getRoom(), "%M casts word of Judgement on %sself.", player, player->himHer());
} else if(spellData->how == POTION) {
player->print("You find yourself elsewhere.\n");
broadcast(player->getSock(), player->getRoom(), "%M drinks a potion of judgement and disapears.", player, player->himHer());
}
// Cast word of judgement 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);
}
if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) {
player->print("You pass judgemet on %N.\n", target);
target->print("%M passes judgement on you.\nYou have been judged.\n", player);
broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M passes judgement on %N.", player, target);
logCast(player, target, "judgement", true);
broadcast("The skies grow dark, and a fog begins to cover the ground.\nA shrill voice proclaims, \"Judgement has been passed upon %N\".\n", target);
}
}
target->deleteFromRoom();
target->addToRoom(new_rom);
return(1);
}
//*********************************************************************
// cmdBarkskin
//*********************************************************************
int cmdBarkskin(Player *player, cmd *cmnd) {
long i=0, t = time(0);
int adjustment=0;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("barkskin")) {
player->print("You don't know how to turn your skin to bark.\n");
return(0);
}
if(player->getRoom()->flagIsSet(R_ETHEREAL_PLANE)) {
player->print("That is not possible here.\n");
return(0);
}
if(player->flagIsSet(P_OUTLAW)) {
player->print("You are unable to do that right now.\n");
return(0);
}
if(player->isEffected("barkskin")) {
player->print("Your skin is already made of bark.\n");
return(0);
}
if(player->inCombat(false)) {
player->print("Not in the middle of combat.\n");
return(0);
}
i = player->lasttime[LT_BARKSKIN].ltime + player->lasttime[LT_BARKSKIN].interval;
if(i>t && !player->isCt()) {
player->pleaseWait(i-t);
return(0);
}
// TODO: SKILLS: add skill level check
player->smashInvis();
player->unhide();
broadcast(player->getSock(), player->getRoom(), "%M's skin turns to bark.", player);
adjustment = mrand(3,6);
player->addEffect("barkskin", 120, adjustment, true, player);
player->printColor("^yYour skin turns to bark.\n");
player->checkImprove("barkskin", true);
player->lasttime[LT_BARKSKIN].ltime = t;
player->lasttime[LT_BARKSKIN].interval = 600;
player->computeAC();
return(0);
}
//*********************************************************************
// cmdCommune
//*********************************************************************
int cmdCommune(Player *player, cmd *cmnd) {
long i=0, t = time(0), first_exit=0;
int chance=0;
Room* uRoom=0;
AreaRoom* aRoom=0;
xtag *xp=0;
ctag *cp=0;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("commune")) {
player->print("You don't know how to commune with nature.\n");
return(0);
}
if(!player->getRoom()->isOutdoors()) {
player->print("You can only commune with nature while outdoors.\n");
return(0);
}
i = player->lasttime[LT_PRAY].ltime;
if(t - i < 60L && !player->isCt()) {
player->pleaseWait(60L-t+i);
return(0);
}
int level = (int)player->getSkillLevel(("commune"));
chance = MIN(85, level * 20 + bonus((int) player->piety.getCur()));
if(player->isStaff())
chance = 100;
if(mrand(1, 100) <= chance) {
player->print("You successfully commune with nature.\n");
player->print("You sense any living creatures in your surroundings.\n");
xp = player->getRoom()->first_ext;
first_exit = 1;
while(xp) {
if(xp->ext->flagIsSet(X_DESCRIPTION_ONLY) || xp->ext->flagIsSet(X_SECRET) ||
xp->ext->isConcealed(player) || xp->ext->flagIsSet(X_DM_ONLY) ||
xp->ext->flagIsSet(X_CLOSED) || xp->ext->flagIsSet(X_NO_SEE)) {
xp = xp->next_tag;
continue;
}
if(!first_exit)
player->print("\n");
player->print("%s:\n", xp->ext->name);
first_exit = 0;
if(!Move::getRoom(player, xp->ext, &uRoom, &aRoom, true)) {
xp = xp->next_tag;
continue;
}
if(uRoom)
cp = uRoom->first_ply;
else
cp = aRoom->first_ply;
while(cp) {
if(cp->crt->isUndead() && !player->isCt()) {
cp = cp->next_tag;
continue;
}
if(cp->crt->flagIsSet(P_DM_INVIS) && !player->isDm()) {
cp = cp->next_tag;
continue;
}
if(cp->crt->isInvisible() && !player->isEffected("detect-invisible") && !player->isCt()) {
cp = cp->next_tag;
continue;
}
player->print(" %M\n", cp->crt);
cp = cp->next_tag;
}
if(uRoom)
cp = uRoom->first_mon;
else
cp = aRoom->first_mon;
while(cp) {
if(cp->crt->isUndead() && !player->isCt()) {
cp = cp->next_tag;
continue;
}
if(cp->crt->isPet() && !player->isCt()) {
cp = cp->next_tag;
continue;
}
if(cp->crt->isUndead() && !player->isCt()) {
cp = cp->next_tag;
continue;
}
if(cp->crt->isInvisible() && !player->isEffected("detect-invisible") && !player->isCt()) {
cp = cp->next_tag;
continue;
}
player->print(" %s\n", cp->crt->name);
cp = cp->next_tag;
}
xp = xp->next_tag;
}
player->checkImprove("commune", true);
player->lasttime[LT_PRAY].interval = 60L;
} else {
player->print("You failed to commune with nature.\n");
broadcast(player->getSock(), player->getRoom(), "%M tries to commune with nature.", player);
player->checkImprove("commune", false);
player->lasttime[LT_PRAY].ltime = 10L;
}
player->lasttime[LT_PRAY].ltime = t;
return(0);
}
//*********************************************************************
// cmdEndurance
//*********************************************************************
// This allows druids to speed walk like everyone used to be able to
// This has been disabled in cmd.c
int cmdEndurance(Player *player, cmd *cmnd) {
long i, t;
int chance;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("endurance")) {
player->print("You lack the training for endurance.\n");
return(0);
}
if(player->flagIsSet(P_RUNNING)) {
player->print("You are already able to run long distances.\n");
return(0);
}
i = player->lasttime[LT_ENDURANCE].ltime;
t = time(0);
if(t - i < 600L) {
player->pleaseWait(600L-t+i);
return(0);
}
int level = (int)player->getSkillLevel("endurance");
chance = MIN(85, level * 5 + bonus((int) player->dexterity.getCur())*5);
if(mrand(1, 100) <= chance) {
player->print("You are now able to run long distances.\n");
player->checkImprove("endurance", true);
broadcast(player->getSock(), player->getRoom(), "%M is now prepared to run long distances.", player);
player->setFlag(P_RUNNING);
player->lasttime[LT_ENDURANCE].ltime = t;
player->lasttime[LT_ENDURANCE].interval = 120L + 60L * (level / 5) + bonus((int) player->constitution.getCur())*30L;
} else {
player->print("You failed prepare yourself for long distance running.\n");
player->checkImprove("endurance", false);
broadcast(player->getSock(), player->getRoom(), "%M attempts to prepare for a run.",player);
player->lasttime[LT_ENDURANCE].ltime = t - 590L;
}
return(0);
}
//*********************************************************************
// isMageLich
//*********************************************************************
bool isMageLich(const Creature* creature) {
if( creature->getClass() != MAGE &&
(creature->isPlayer() && creature->getConstPlayer()->getSecondClass() != MAGE) &&
creature->getClass() != LICH &&
!creature->isCt()
) {
creature->print("The arcane nature of that spell eludes you.\n");
return(false);
}
return(true);
}
//*********************************************************************
// noPotion
//*********************************************************************
bool noPotion(Creature* player, SpellData* spellData) {
if(spellData->how == POTION) {
player->print("You can only use a potion on yourself.\n");
return(1);
}
return(0);
}