/* * 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-2012 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 "commands.h" #include "craft.h" #include "unique.h" #include "effects.h" #include "alchemy.h" //********************************************************************* // spellShortcut //********************************************************************* 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"); } //********************************************************************* // doMpCheck //********************************************************************* int Creature::doMpCheck(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(!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) && spellFail(CAST)) { // Reduced spell fails to half mp 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); } void doCastPython(MudObject* caster, Creature* target, bstring spell, int strength) { if(!caster || !target) return; int c = 0, n = 0; SpellData data; bool found = false, offensive = false; do { if(spell == get_spell_name(c)) { data.splno = c; found = true; break; } c++; } while(get_spell_num(c) != -1); if(!found) return; int (*fn)(SpellFn); cmd cmnd; fn = get_spell_function(data.splno); if(caster->isCreature()) { data.set(CAST, get_spell_school(data.splno), get_spell_domain(data.splno), 0, caster->getAsConstCreature()); cmnd.fullstr = "cast " + spell + " " + target->getName(); } else { data.set(WAND, get_spell_school(data.splno), get_spell_domain(data.splno), caster->getAsObject(), target); cmnd.fullstr = bstring("use spell ."); caster = target; } parse(cmnd.fullstr, &cmnd); 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; n = ((int(*)(SpellFn, const char*, osp_t*))*fn) (caster->getAsCreature(), &cmnd, &data, get_spell_name(data.splno), &ospell[c]); } else { n = ((int(*)(SpellFn))*fn) (caster->getAsCreature(), &cmnd, &data); } } //********************************************************************* // 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; Player* player = creature->getPlayerMaster(); bool offensive=false, self = (!player || player == creature); Creature* listen = (player ? player : creature); SpellData data; if(player) { player->clearFlag(P_AFK); player->interruptDelayedActions(); 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->isStaff()) { switch(creature->getCastingType()) { case Divine: if(data.domain == DOMAIN_CANNOT_CAST) { listen->print("Divine spellcasters may not cast that spell.\n"); return(CAST_RESULT_FAILURE); } break; case Arcane: if(data.school == SCHOOL_CANNOT_CAST) { listen->print("Arcane spellcasters may not cast that spell.\n"); return(CAST_RESULT_FAILURE); } break; default: if(data.school == SCHOOL_CANNOT_CAST || data.domain == DOMAIN_CANNOT_CAST) { listen->print("Your class may not cast that spell.\n"); return(CAST_RESULT_FAILURE); } break; } } if( creature->getRoomParent()->flagIsSet(R_NO_MAGIC) && !creature->checkStaff("Nothing happens.\n") ) return(CAST_RESULT_FAILURE); if( player == creature && player->pFlagIsSet(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()) { if(creature->getRoomParent()->checkAntiMagic()) { if(self) listen->printColor("^yYour spell fails.\n"); else listen->printColor("^y%M's spell fails.\n", creature); return(CAST_RESULT_SPELL_FAILURE); } } reqMp = creature->doMpCheck(data.splno); if(!reqMp) return(CAST_RESULT_CURRENT_FAILURE); fn = get_spell_function(data.splno); data.set(CAST, get_spell_school(data.splno), get_spell_domain(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() == THIEF || 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 PUREBLOOD: 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: if(player->getSecondClass() == MAGE) player->lasttime[LT_SPELL].interval = 4; else 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/domains of magic // Can't improve evocation/necromancy/destruction spells by casting on yourself MagicType castingType = player->getCastingType(); if(data.skill != "" && (!self || ( (castingType == Arcane && data.school != EVOCATION) || (castingType == Arcane && data.school != NECROMANCY) || (castingType == Divine && data.domain != DESTRUCTION) ) )) 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->getUniqueRoomParent())) { player->print("Error: room number not in any of your alotted ranges.\n"); return(0); } } target = player->getParent()->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->pFlagIsSet(P_LINKDEAD)) { 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->getCName(), target->getCName(), 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->getCName(), target->getCName(), 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->checkStaff("%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->isEffected("incognito")) { broadcast(player->getSock(), target->getSock(), player->getParent(), "%M taught %N the %s spell.", player, target, get_spell_name(splno)); } } return(0); } //********************************************************************* // studyFindObject //********************************************************************* // This function finds the object the player is trying to study // and determines if they can study it. Object* studyFindObject(Player* player, const cmd* cmnd) { Object *object=0; if(cmnd->num < 2) { player->print("Study what?\n"); return(0); } if(!player->ableToDoCommand()) return(0); if(player->isBlind()) { player->printColor("^CYou're blind!\n"); return(0); } object = player->findObject(player, cmnd, 1); if(!object) { player->print("You don't have that.\n"); return(0); } if(object->increase) { // // If this object can increase the player's skills or languages // // make sure we respect onlyOnce if(object->increase->onlyOnce) { std::list<CatRef>::const_iterator it; for(it = player->objIncrease.begin() ; it != player->objIncrease.end() ; it++) { if( *it == object->info && !player->checkStaff("You can gain no further understanding from %P.\n", object)) return(0); } } // handle objects that increase skills if(object->increase->type == SkillIncrease) { Skill* crtSkill = player->getSkill(object->increase->increase); const SkillInfo *skill = gConfig->getSkill(object->increase->increase); if(!skill) { player->printColor("The skill set on this object is not a valid skill.\n"); return(0); } if(!crtSkill && !object->increase->canAddIfNotKnown) { player->printColor("You lack the training to understand %P properly.\n", object); return(0); } // don't improve skills that don't need to improve if(crtSkill && skill->isKnownOnly()) { player->printColor("You can gain no further understanding from %P.\n", object); return(0); } // 10 skill points per level possible, can't go over that if(crtSkill && (crtSkill->getGained() + object->increase->amount) >= (player->getLevel()*10)) { player->printColor("You can currently gain no further understanding from %P.\n", object); return(0); } } else if(object->increase->type == LanguageIncrease) { int lang = atoi(object->increase->increase.c_str()); if(lang < 1 || lang > LANGUAGE_COUNT) { player->printColor("The language set on this object is not a valid language.\n"); return(0); } if(player->languageIsKnown(LUNKNOWN+lang-1)) { player->printColor("You already know how to speak %s!\n", get_language_adj(lang-1)); return(0); } } else { player->printColor("You don't understand how to use %P properly.\n", object); return(0); } } else if(object->getRecipe()) { // // If this object can teach the player a recipe // 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->getCName()); loge("Study: %s has a bad recipe.\n", player->getCName()); 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); } } else if(object->getType() == SCROLL) { // // If this object can teach the player a spell // if(object->getMagicpower() - 1 < 0 || object->getMagicpower() - 1 > MAXSPELL) { player->print("%O is unintelligible.\n", object); broadcast(isCt, "^y%s has a bad scroll: %s.\n", player->getCName(), object->getCName()); loge("Study: %s has a bad scroll: %s.\n", player->getCName(), object->getCName()); return(0); } bstring skill = ""; if(player->getCastingType() == Divine) skill = spellSkill(get_spell_domain(object->getMagicpower() - 1)); else 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); } } else if(object->getType() == SONGSCROLL) { // // If this object can teach the player a song // } else { player->print("You can't study that!\n"); return(0); } if(object->doRestrict(player, true)) return(0); return(object); } //********************************************************************* // doStudy //********************************************************************* // This function does the grunt work of studying the object. void doStudy(Player* player, Object* object, bool immediate) { player->unhide(); if(object->increase) { // // If this object can increase the player's skills or languages // // handle objects that increase skills if(object->increase->type == SkillIncrease) { Skill* crtSkill = player->getSkill(object->increase->increase); const SkillInfo *skill = gConfig->getSkill(object->increase->increase); // improve the skill bstring output = skill->getDisplayName(); player->printColor("Your understanding of ^W%s^x has improved.\n", output.c_str()); if(!crtSkill) player->addSkill(object->increase->increase, object->increase->amount); else crtSkill->improve(object->increase->amount); } else if(object->increase->type == LanguageIncrease) { int lang = atoi(object->increase->increase.c_str()); player->printColor("You learn know how to speak ^W%s^x!\n", get_language_adj(lang-1)); player->learnLanguage(lang-1); } // we only get here if it increases successfully if(object->increase->onlyOnce) player->objIncrease.push_back(object->info); } else if(object->getRecipe()) { // // If this object can teach the player a recipe // Recipe* recipe = gConfig->getRecipe(object->getRecipe()); player->printColor("You learn the art of making ^W%s^x.\n", recipe->getResultName().c_str()); player->learnRecipe(recipe); } else if(object->getType() == SCROLL) { // // If this object can teach the player a spell // player->printColor("You learn the ^W%s^x spell.\n", get_spell_name(object->getMagicpower() - 1)); player->learnSpell(object->getMagicpower() - 1); } else if(object->getType() == SONGSCROLL) { // // If this object can teach the player a song // player->printColor("You learn the song of ^W%s^x.\n", get_song_name(object->getMagicpower() - 1)); player->learnSong(object->getMagicpower() - 1); } player->printColor("%O disintegrates!\n", object); if(immediate) broadcast(player->getSock(), player->getParent(), "%M studies %1P.", player, object); else broadcast(player->getSock(), player->getParent(), "%M finishes studiying %1P.", player, object); player->delObj(object, true); delete object; } // this function is called when we are ready to finish studying the object void doStudy(const DelayedAction* action) { Player* player = action->target->getAsPlayer(); Object* object = studyFindObject(player, &action->cmnd); // nothing to study? if(!object) return; player->printColor("You finish studying %P.\n", object); doStudy(player, object, false); } //********************************************************************* // cmdStudy //********************************************************************* // This function allows a player to study a scroll, and learn the // spell / recipe / skill / language / song that is on it. int cmdStudy(Player* player, cmd* cmnd) { Object* object = studyFindObject(player, cmnd); // nothing to study? if(!object) return(0); // these types of objects take a while to study if(object->increase) { int delay; switch(object->increase->type) { case SkillIncrease: delay = 15; break; case LanguageIncrease: delay = 60; break; default: delay = 60; break; } // doStudy calls unhide, no need to do it twice player->unhide(); player->printColor("You begin studying %P.\n", object); broadcast(player->getSock(), player->getParent(), "%M begins studying %1P.", player, object); gServer->addDelayedAction(doStudy, player, cmnd, ActionStudy, delay); } else { doStudy(player, object, true); } 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; 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 = player->findObject(player, cmnd, 1); 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 = object->doSpecial(player); 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->getRoomParent()->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); } if(player->getRoomParent()->checkAntiMagic()) { player->print("Nothing happens.\n"); return(0); } 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->getCName()); loge("Readscroll: %s has a bad scroll.\n", player->getCName()); return(0); } if(player->spellFail( 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), get_spell_domain(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 Player::endConsume(Object* object, bool forceDelete) { if(object->flagIsSet(O_EATABLE) || object->getType() == HERB) { print("You eat %P.\n", object); broadcast(getSock(), getParent(), "%M eats %1P.", this, object); } else { print("Potion drank.\n"); broadcast(getSock(), getParent(), "%M drinks %1P.", this, object); } if(!forceDelete) object->decShotsCur(); if(forceDelete || object->getShotsCur() < 1) { if(object->flagIsSet(O_EATABLE) || object->getType() == HERB) printColor("You ate all of %P.\n", object); else printColor("You drank all of %P.\n", object); delObj(object, true); delete object; return(2); } return(1); } int Player::consume(Object* object, cmd* cmnd) { int c=0, splno=0, n=0; int (*fn)(SpellFn); bool dimensionalFailure=false; bool eat = (object->flagIsSet(O_EATABLE) || object->getType() == HERB), drink=object->flagIsSet(O_DRINKABLE) || object->getType() == POTION; //const bstring& effect = object->getEffect(); fn = 0; if(isStaff() && object->getType() != POTION && !strcmp(cmnd->str[0], "eat")) { broadcast(getSock(), getParent(), "%M eats %1P.", this, object); printColor("You ate %P.\n", object); delObj(object); delete object; return(0); } if(!strcmp(cmnd->str[0], "eat")) { if(!eat && !checkStaff("You may not eat that.\n")) return(0); } else { if(!drink && !checkStaff("You may not drink that.\n")) return(0); } if(object->doRestrict(this, true)) return(0); // they are eating a non-potion object if( object->getShotsCur() < 1 || (object->getMagicpower() - 1 < 0) || object->getType() != POTION) { unhide(); if(object->use_output[0]) printColor("%s\n", object->use_output); // some food can heal you if(object->flagIsSet(O_CONSUME_HEAL) && !isUndead()) hp.increase(object->damage.roll()); return(endConsume(object, true)); } if( getRoomParent()->flagIsSet(R_NO_POTION) || getRoomParent()->flagIsSet(R_LIMBO)) { if(!checkStaff("%O starts to %s before you %s it.\n", object, eat ? "get moldy" : "evaporate", eat ? "eat" : "drink")) return(0); } unhide(); // Handle Alchemy Potions if(object->isAlchemyPotion()) { if(object->consumeAlchemyPotion(this)) return(endConsume(object,this)); else return(0); } if(object->getMagicpower() - 1 < 0 || object->getMagicpower() - 1 > MAXSPELL) { print("Error: Bad Potion.\n"); broadcast(::isCt, "^y%s has a bad potion!\n", getCName()); loge("Quaff: %s has a bad potion.\n", getCName()); return(0); } splno = object->getMagicpower() - 1; fn = get_spell_function(splno); // check for dimensional anchor if(hinderedByDimensionalAnchor(splno) && 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), get_spell_domain(data.splno), object, this); 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) (this, cmnd, &data, get_spell_name(data.splno), &ospell[c]); } else { n = ((int(*)(SpellFn))*fn) (this, cmnd, &data); } } if(n || dimensionalFailure) { statistics.potion(); if(object->use_output[0] && !dimensionalFailure) printColor("%s\n", object->use_output); return(endConsume(object)); } 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 = player->findObject(player, cmnd, 1); 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); } player->consume(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; 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 = player->findObject(player, cmnd, 1); 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 = player->getRoomParent()->findObject(player, cmnd, 1); 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->getRoomParent()->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(); if(player->getRoomParent()->checkAntiMagic()) { player->printColor("%O sputters and smokes.\n", object); return(0); } 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->getCName()); loge("Study: %s has a bad wand.\n", player->getCName()); return(0); } data.set(WAND, get_spell_school(data.splno), get_spell_domain(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(player->spellFail( 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) { if((player->getLevel() <= 7 && !player->inCombat()) || player->isStaff()) player->doRecall(); else player->useRecallPotion(1, 0); return(0); } //********************************************************************* // recallLog //********************************************************************* // does the logging for a lag-protect hazy void Player::recallLog(bstring name, bstring cname, bstring room) { std::ostringstream log; log << "### " << getName() << "(L" << getLevel() << ") hazied (" << name << ") "; if(cname != "") log << "(out of bag: " << cname << ") "; log << "due to lag protection. HP: " << hp.getCur() << "/" << hp.getMax() << ". Room: " << room << " to " << getRoomParent()->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 Player::recallCheckBag(Object *cont, cmd* cmnd, int show, int log) { int drank=0; bstring room = getRoomParent()->fullName(), name = ""; for(Object* object : cont->objects) { name = object->getName(); if(object->getMagicpower() == (S_WORD_OF_RECALL+1) && object->getType() == POTION) { if(show) printColor("Recall potion found in %s: %s. Initiating auto-recall.\n", cont->getCName(), name.c_str()); drank = consume(object, cmnd); if(drank) { if(log) recallLog(name, cont->getCName(), room); return(1); } if(show) print("Unable to use potion. Continuing search.\n"); } } return(0); } //********************************************************************* // useRecallPotion //********************************************************************* // used for recall command and lagprotect int Player::useRecallPotion(int show, int log) { cmd *cmnd; Object *object=NULL; int i=0; bstring room = getRoomParent()->fullName(), name = ""; if(isEffected("anchor")) { 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; print("Beginning recall sequence.\n"); // do they have anything on their person? for(i=0; i<MAXWEAR; i++) { object = ready[i]; if(object) { name = object->getName(); if(object->getMagicpower() == (S_WORD_OF_RECALL+1) && object->getType() == POTION) { if(show) printColor("Recall potion found: %s. Initiating auto-recall.\n", name.c_str()); if(consume(object, cmnd)) { if(log) recallLog(name, "", room); return(1); } if(show) print("Unable to use potion. Continuing search.\n"); } if(object->getType() == CONTAINER) if(recallCheckBag(object, cmnd, show, log)) return(1); } } // check through their inventory for(Object* obj : objects) { object = obj; name = object->getName(); // is this object it? if(object->getMagicpower() == (S_WORD_OF_RECALL+1) && object->getType() == POTION) { if(show) printColor("Recall potion found: %s. Initiating auto-recall.\n", name.c_str()); if(consume(object, cmnd)) { if(log) recallLog(name, "", room); return(1); } if(show) print("Unable to use potion. Continuing search.\n"); } // in a container? if(object->getType() == CONTAINER) { if(recallCheckBag(object, cmnd, show, log)) return(1); } } print("No recall potions found!\n"); return(0); } //********************************************************************* // logCast //********************************************************************* void logCast(Creature* caster, Creature* target, bstring spell, bool dmToo) { Player* pCaster = caster->getAsPlayer(); if(!pCaster || !pCaster->isStaff() || (!dmToo && pCaster->isDm())) return; log_immort(true, pCaster, "%s cast an %s spell on %s in room %s.\n", caster->getCName(), spell.c_str(), target ? target->getCName() : "self", caster->getRoomParent()->fullName().c_str()); } //********************************************************************* // noCastUndead //********************************************************************* bool noCastUndead(const bstring& effect) { return(effect == "regeneration"); } //********************************************************************* // 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( noCastUndead(effect) && target->isUndead() && !player->checkStaff("You cannot cast that spell on yourself.\n") ) return(0); if(spellData->how == CAST) { player->print("You cast %s %s spell.\n", article, spell); broadcast(player->getSock(), player->getParent(), "%M casts %s %s spell.", player, article, spell); } } else { if(player->noPotion( spellData)) return(0); cmnd->str[2][0] = up(cmnd->str[2][0]); target = player->getParent()->findCreature(player, cmnd->str[2], cmnd->val[2], false); if(!target) { player->print("You don't see that player here.\n"); return(0); } if( noCastUndead(effect) && target->isUndead() && !player->checkStaff("You cannot cast that spell on the undead.\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->getParent(), "%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->getRoomParent()->magicBonus()) player->print("The room's magical properties increase the power of your spell.\n"); if(!target->addEffect(effect, duration, strength, player, true)) return(0); } else { target->addEffect(effect, duration, strength, NULL, true); } return(1); } //********************************************************************* // cmdTransmute //********************************************************************* // This allows a mage to transmute gold into magical charges for a wand 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); gServer->logGold(GOLD_OUT, player, Money(cost, GOLD), object, "Transmuate"); if(!player->isCt() && !dec_daily(&player->daily[DL_RCHRG])) { player->print("You have recharged enough wands for one day.\n"); return(0); } if(player->spellFail(CAST)) { int dmg = 0; player->print("The wand glows bright red and explodes!\n"); broadcast(player->getSock(), player->getParent(), "A wand explodes in %s's hand!\n", player->getCName()); 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->getCName()); player->checkImprove("transmute", true); player->statistics.transmute(); return(0); } //********************************************************************* // 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->getParent(), "%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(player->noPotion( spellData)) return(0); target = player->getParent()->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->getParent(), "%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->getCName()); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M casts a blindness spell on %N.", player, target); target->print("%M casts a blindness spell on you.\n", player); } if(target->isMonster()) { target->getAsMonster()->addEnemy(player); } } if(target->isPlayer()) { if(player->isCt()) target->setFlag(P_DM_BLINDED); } if(spellData->how == CAST && player->isPlayer()) player->getAsPlayer()->statistics.offensiveCast(); if(!player->isCt()) target->addEffect("blindness", 180 - (target->constitution.getCur()/10), 1, player, true, player); else target->addEffect("blindness", 600, 1, player); return(1); } //********************************************************************* // spell_fail //********************************************************************* // This function returns 1 if the casting of a spell fails, and 0 if it is // sucessful. int Creature::spellFail(int how) { Player *pPlayer = getAsPlayer(); 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 PUREBLOOD: 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) { 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; UniqueRoom *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->getAsPlayer(); if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("You pass judgement upon yourself.\n"); broadcast(player->getSock(), player->getParent(), "%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->getParent(), "%M drinks a potion of judgement and disapears.", player, player->himHer()); } // Cast word of judgement on another player } else { if(player->noPotion( spellData)) return(0); cmnd->str[2][0] = up(cmnd->str[2][0]); target = player->getParent()->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->getParent(), "%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->getRoomParent()->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->getParent(), "%M's skin turns to bark.", player); adjustment = mrand(3,6); player->addEffect("barkskin", 120, adjustment, player, 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; BaseRoom* newRoom=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->getRoomParent()->isOutdoors()) { player->print("You can only commune with nature while outdoors.\n"); return(0); } i = player->lasttime[LT_PRAY].ltime; if(t - i < 45L && !player->isCt()) { player->pleaseWait(45L-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"); first_exit = 1; for(Exit* ext : player->getRoomParent()->exits) { if( ext->flagIsSet(X_DESCRIPTION_ONLY) || ext->flagIsSet(X_SECRET) || ext->isConcealed(player) || ext->flagIsSet(X_STAFF_ONLY) || ext->flagIsSet(X_LOOK_ONLY) || ext->flagIsSet(X_CLOSED) || ext->flagIsSet(X_NO_SEE) ) { continue; } if(!first_exit) player->print("\n"); player->print("%s:\n", ext->getCName()); first_exit = 0; if(!Move::getRoom(player, ext, &newRoom, true)) { continue; } PlayerSet::iterator pIt; PlayerSet::iterator pEnd; pIt = newRoom->players.begin(); pEnd = newRoom->players.end(); Player* ply; while(pIt != pEnd) { ply = (*pIt++); if(ply->isUndead() && !player->isCt()) continue; if(ply->flagIsSet(P_DM_INVIS) && !player->isDm()) continue; if(ply->isInvisible() && !player->isEffected("detect-invisible") && !player->isCt()) continue; player->print(" %M\n", ply); } MonsterSet::iterator mIt; MonsterSet::iterator mEnd; mIt = newRoom->monsters.begin(); mEnd = newRoom->monsters.end(); Monster* mons; while(mIt != mEnd) { mons = (*mIt++); if(mons->isUndead() && !player->isCt()) continue; if(mons->isPet() && !player->isCt()) continue; if(mons->isUndead() && !player->isCt()) continue; if(mons->isInvisible() && !player->isEffected("detect-invisible") && !player->isCt()) continue; player->print(" %s\n", mons->getCName()); } } player->checkImprove("commune", true); player->lasttime[LT_PRAY].interval = 60L; } else { player->print("You failed to commune with nature.\n"); broadcast(player->getSock(), player->getParent(), "%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); } //********************************************************************* // isMageLich //********************************************************************* bool Creature::isMageLich() { if( getClass() != MAGE && (isPlayer() && getAsConstPlayer()->getSecondClass() != MAGE) && getClass() != LICH && !isCt()) { print("The arcane nature of that spell eludes you.\n"); return(false); } return(true); } //********************************************************************* // noPotion //********************************************************************* bool Creature::noPotion(SpellData* spellData) { if(spellData->how == POTION) { print("You can only use a potion on yourself.\n"); return(1); } return(0); }