/* * conjuration.cpp * Conjuration spells * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-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 "effects.h" //********************************************************************* // petTalkDesc //********************************************************************* void petTalkDesc(Monster* pet, Creature* owner) { bstring name = owner->getName(), desc = ""; if(owner->flagIsSet(P_DM_INVIS)) name = "Someone"; desc = "It's wearing a tag that says, '"; desc += name; desc += "'s, hands off!'."; pet->setDescription(desc); desc = "I serve only "; desc += name; desc += "!"; pet->setTalk(desc); pet->escapeText(); } //********************************************************************* // getPetTitle //********************************************************************* int getPetTitle(int how, int skLevel, bool weaker, bool undead) { int title=0, num=0; if(how == CAST || how == SKILL || how == WAND) { title = (skLevel + 2) / 3; if(weaker) { num = skLevel / 2; if(num < 1) num = 1; title = (num + 2) / 3; } else { title = (skLevel + 2) / 3; } if(title > 10) title = 10; } else { title = (undead ? 3 : 1); } return(MAX(1, title)); } //********************************************************************* // conjure //********************************************************************* int conjureCmd(Player* player, cmd* cmnd) { SpellData data; data.set(SKILL, CONJURATION, NO_DOMAIN, 0, player); if(!data.check(player)) return(0); return(conjure(player, cmnd, &data)); } int conjure(Creature* player, cmd* cmnd, SpellData* spellData) { Player* pPlayer = player->getAsPlayer(); if(!pPlayer) return(0); Monster *target=0; int title=0, mp=0, realm=0, level=0, spells=0, chance=0, sRealm=0; int buff=0, hp_percent=0, mp_percent=0, a=0, rnum=0, cClass=0, skLevel=0; int interval=0, len=0, n=0, x=0, hplow=0,hphigh=0, mplow=0, mphigh=0; time_t t, i; const char *delem; // BUG: Name is not long enough for "schizophrenic serial killer clown" char *s, *p, name[80]; delem = " "; if(!player->ableToDoCommand()) return(0); if(player->noPotion( spellData)) return(0); if(spellData->how == SKILL && !player->knowsSkill("conjure")) { player->print("The conjuring of elementals escapes you.\n"); return(0); } /* if(spellData->how == SKILL) { skLevel = (int)player->getSkillLevel("conjure"); } else { skLevel = player->getLevel(); } */ // TODO: conjure/animate is no longer a skill until progression // has been fixed skLevel = player->getLevel(); if( spellData->how == SKILL && player->getClass() == CLERIC && player->getDeity() == GRADIUS && ( player->getAdjustedAlignment() == BLOODRED || player->getAdjustedAlignment() == ROYALBLUE ) ) { player->print("Your alignment is out of harmony.\n"); return(0); } if(player->getClass() == BUILDER) { player->print("You cannot conjure pets.\n"); return(0); } if(player->hasPet() && !player->checkStaff("Only one conjuration at a time!\n")) return(0); if(spellData->object) { level = spellData->object->getQuality()/10; } else { level = skLevel; } title = getPetTitle(spellData->how, level, spellData->how == WAND && player->getClass() != DRUID && !(player->getClass() == CLERIC && player->getDeity() == GRADIUS), false); mp = 4 * title; if(spellData->how == CAST && !player->checkMp(mp)) return(0); t = time(0); i = LT(player, LT_INVOKE); if(!player->isCt()) { if( (i > t) && (spellData->how != WAND) ) { player->pleaseWait(i-t); return(0); } if(spellData->how == CAST) { if(player->spellFail( spellData->how)) { player->subMp(mp); return(0); } } } if( player->getClass() == DRUID || (spellData->how == SKILL && !(player->getClass() == CLERIC && player->getDeity() == GRADIUS)) || player->isDm() ) { if( (cmnd->num < 3 && (spellData->how == CAST || spellData->how == WAND)) || (cmnd->num < 2 && spellData->how == SKILL) ) { player->print("Conjure what kind of elemental (earth, air, water, fire, electricity, cold)?\n"); return(0); } s = cmnd->str[spellData->how == SKILL ? 1 : 2]; len = strlen(s); if(!strncasecmp(s, "earth", len)) realm = EARTH; else if(!strncasecmp(s, "air", len)) realm = WIND; else if(!strncasecmp(s, "fire", len)) realm = FIRE; else if(!strncasecmp(s, "water", len)) realm = WATER; else if(!strncasecmp(s, "cold", len)) realm = COLD; else if(!strncasecmp(s, "electricity", len)) realm = ELEC; else if(player->isDm() && !strncasecmp(s, "mage", len)) realm = CONJUREMAGE; else if(player->isDm() && !strncasecmp(s, "bard", len)) realm = CONJUREBARD; else { player->print("Conjure what kind of elemental (earth, air, water, fire, electricity, cold)?\n"); return(0); } } else { if(player->getClass() == CLERIC && player->getDeity() == GRADIUS) realm = EARTH; else if(player->getClass() == BARD) realm = CONJUREBARD; else realm = CONJUREMAGE; } // 0 = weak, 1 = normal, 2 = buff buff = mrand(1,3) - 1; target = new Monster; if(!target) { player->print("Cannot allocate memory for target.\n"); merror("conjure", NONFATAL); return(PROMPT); } // Only level 30 titles int titleIdx = MIN(29, title); if(realm == CONJUREBARD) { target->setName(bardConjureTitles[buff][titleIdx-1]); strncpy(name, bardConjureTitles[buff][titleIdx-1], 79); } else if(realm == CONJUREMAGE) { target->setName(mageConjureTitles[buff][titleIdx-1]); strncpy(name, mageConjureTitles[buff][titleIdx-1], 79); } else { target->setName(conjureTitles[realm-1][buff][titleIdx-1]); strncpy(name, conjureTitles[realm-1][buff][titleIdx-1], 79); } name[79] = '\0'; if(spellData->object) { level = spellData->object->getQuality()/10; } else { level = skLevel; } switch(buff) { case 0: level -= 3; break; case 1: level -= 2; break; case 2: level -= mrand(0,1); break; } level = MAX(1, MIN(MAXALVL, level)); p = strtok(name, delem); if(p) strncpy(target->key[0], p, 19); p = strtok(NULL, delem); if(p) strncpy(target->key[1], p, 19); p = strtok(NULL, delem); if(p) strncpy(target->key[2], p, 19); // if(realm == CONJUREBARD) { // level = title * 12 / 7 + mrand(1,4) - 2; // level = MAX(1,MIN(skLevel-3, level)); // } else if(realm == CONJUREMAGE) { // level = title * 6 / 4 + mrand(1,4) - 2; // } else { // level = title * 2 + mrand(1,4) - 2; // } target->setType(MONSTER); bool combatPet = false; if(realm == CONJUREBARD) { chance = mrand(1,100); if(chance > 50) cClass = mrand(1,13); if(cClass) { switch(cClass) { case 1: target->setClass(ASSASSIN); break; case 2: target->setClass(BERSERKER); combatPet = true; break; case 3: target->setClass(CLERIC); break; case 4: target->setClass(FIGHTER); combatPet = true; break; case 5: target->setClass(MAGE); break; case 6: target->setClass(PALADIN); break; case 7: target->setClass(RANGER); break; case 8: target->setClass(THIEF); break; case 9: target->setClass(PUREBLOOD); break; case 10: target->setClass(MONK); break; case 11: target->setClass(DEATHKNIGHT); break; case 12: target->setClass(WEREWOLF); break; case 13: target->setClass(ROGUE); break; } } //if(target->getClass() == CLERIC && mrand(1,100) > 50) // target->learnSpell(S_HEAL); if(target->getClass() == MAGE && mrand(1,100) > 50) target->learnSpell(S_RESIST_MAGIC); } target->setLevel(MIN(level, skLevel+1)); level--; target->strength.setInitial(conjureStats[buff][level].str*10); target->dexterity.setInitial(conjureStats[buff][level].dex*10); target->constitution.setInitial(conjureStats[buff][level].con*10); target->intelligence.setInitial(conjureStats[buff][level].intel*10); target->piety.setInitial(conjureStats[buff][level].pie*10); target->strength.restore(); target->dexterity.restore(); target->constitution.restore(); target->intelligence.restore(); target->piety.restore(); // This will be adjusted in 2.50, for now just str*2 target->setAttackPower(target->strength.getCur()*2); // There are as many variations of elementals on the elemental // planes as there are people on the Prime Material. Therefore, // the elementals summoned have varying hp and mp stats. -TC if(player->getClass() == CLERIC && player->getDeity() == GRADIUS) { hp_percent = 8; mp_percent = 4; } else { hp_percent = 6; mp_percent = 8; } hphigh = conjureStats[buff][level].hp; hplow = (conjureStats[buff][level].hp * hp_percent) / 10; target->hp.setInitial(mrand(hplow, hphigh)); target->hp.restore(); if(!combatPet) { mphigh = conjureStats[buff][level].mp; mplow = (conjureStats[buff][level].mp * mp_percent) / 10; target->mp.setInitial(MAX(10,mrand(mplow, mphigh))); target->mp.restore(); } //target->hp.getMax() = target->hp.getCur() = conjureStats[buff][level].hp; // target->mp.getMax() = target->mp.getCur() = conjureStats[buff][level].mp; target->setArmor(conjureStats[buff][level].armor); int skill = ((target->getLevel() - (combatPet ? 0 : 1)) * 10) + (buff * 5); target->setDefenseSkill(skill); target->setWeaponSkill(skill); target->setExperience(0); target->damage.setNumber(conjureStats[buff][level].ndice); target->damage.setSides(conjureStats[buff][level].sdice); target->damage.setPlus(conjureStats[buff][level].pdice); target->first_tlk = 0; target->setParent(NULL); for(n=0; n<20; n++) target->ready[n] = 0; if(!combatPet) { for(Realm r = MIN_REALM; r<MAX_REALM; r = (Realm)((int)r + 1)) target->setRealm(mrand((conjureStats[buff][level].realms*3)/4, conjureStats[buff][level].realms), r); } target->lasttime[LT_TICK].ltime = target->lasttime[LT_TICK_SECONDARY].ltime = target->lasttime[LT_TICK_HARMFUL].ltime = time(0); target->lasttime[LT_TICK].interval = target->lasttime[LT_TICK_SECONDARY].interval = 60; target->lasttime[LT_TICK_HARMFUL].interval = 30; target->getMobSave(); if(!combatPet) { if(player->getDeity() == GRADIUS || realm == CONJUREBARD) target->setCastChance(mrand(5,10)); // cast precent else target->setCastChance(mrand(20,50)); // cast precent target->proficiency[1] = realm; target->setFlag(M_CAST_PRECENT); target->setFlag(M_CAN_CAST); target->learnSpell(S_VIGOR); if(target->getLevel() > 10 && player->getDeity() != GRADIUS) target->learnSpell(S_MEND_WOUNDS); if(target->getLevel() > 7 && player->getDeity() != GRADIUS && player->getClass() != DRUID) target->learnSpell(S_CURE_POISON); switch(buff) { case 0: // Wimpy mob -- get 1-2 spells spells = mrand(1, 2); break; case 1: // Medium mob -- get 1-4 spells spells = mrand(2, 4); break; case 2: // Buff mob -- get 2-5 spells spells = mrand(2,5); break; } // Give higher level pets higher chance for more spells if(target->getLevel() > 25) spells += 2; else if(target->getLevel() > 16) spells += 1; if(target->getLevel() < 3) spells = 1; else if(target->getLevel() < 7) spells = MIN(spells, 2); else if(target->getLevel() < 12) spells = MIN(spells, 3); else if(target->getLevel() < 16) spells = MIN(spells, 4); else spells = MIN(spells, 5); } // // Druid and gradius pets only // if(realm != CONJUREBARD && realm != CONJUREMAGE) { // // // TODO: Add realm based attacks // // ie: earth - smother, water - ?? fire - ?? // } //Gradius Earth Pets if(player->getDeity() == GRADIUS) { int enchantedOnlyChance = 0; int bashChance = 0; int circleChance = 0; if(target->getLevel() <= 12) enchantedOnlyChance = 50; else enchantedOnlyChance = 101; if(target->getLevel() >= 10) bashChance = 50; if(target->getLevel() >= 16) circleChance = 75; else if(target->getLevel() >= 13) circleChance = 50; if(combatPet) { bashChance += 25; circleChance += 25; } if(mrand(1,100) <= enchantedOnlyChance) target->setFlag(M_ENCHANTED_WEAPONS_ONLY); if(mrand(1, 100) <= bashChance) target->addSpecial("bash"); if(mrand(1, 100) <= circleChance) target->addSpecial("circle"); if(target->getLevel() >= 10) { int numResist = mrand(1,3); for(a=0;a<numResist;a++) { rnum = mrand(1,5); switch(rnum) { case 1: target->addEffect("resist-slashing"); break; case 2: target->addEffect("resist-piercing"); break; case 3: target->addEffect("resist-crushing"); break; case 4: target->addEffect("resist-ranged"); break; case 5: target->addEffect("resist-chopping"); break; } } } if(target->getLevel() >= 13) { if(mrand(1,100) <= 50) { target->setFlag(M_PLUS_TWO); target->clearFlag(M_ENCHANTED_WEAPONS_ONLY); } target->setFlag(M_REGENERATES); if(mrand(1,100) <= 15) target->addSpecial("smother"); } if(target->getLevel() >= 16) { target->setFlag(M_PLUS_TWO); if(mrand(1,100) <= 20) { target->setFlag(M_PLUS_THREE); target->clearFlag(M_PLUS_TWO); target->clearFlag(M_ENCHANTED_WEAPONS_ONLY); } target->setFlag(M_REGENERATES); if(mrand(1,100) <= 30) target->addSpecial("smother"); if(mrand(1,100) <= 15) target->addSpecial("trample"); target->addPermEffect("immune-earth"); } if(target->getLevel() >= 19) { target->setFlag(M_PLUS_TWO); if(mrand(1,100) <= 40) { target->setFlag(M_PLUS_THREE); target->clearFlag(M_PLUS_TWO); target->clearFlag(M_ENCHANTED_WEAPONS_ONLY); } target->setFlag(M_REGENERATES); if(mrand(1,100) <= 40) target->addSpecial("smother"); if(mrand(1,100) <= 25) target->addSpecial("trample"); target->addPermEffect("immune-earth"); target->setFlag(M_NO_LEVEL_ONE); target->setFlag(M_RESIST_STUN_SPELL); } } if(!combatPet) { sRealm = realm; for(x=0;x<spells;x++) { if(realm == CONJUREBARD || realm == CONJUREMAGE) sRealm = getRandomRealm(); target->learnSpell(getOffensiveSpell((Realm)sRealm, x)); // TODO: fix bad cast } } player->print("You conjure a %s.\n", target->getCName()); player->checkImprove("conjure", true); broadcast(player->getSock(), player->getParent(), "%M conjures a %s.", player, target->getCName()); if(!combatPet) { chance = mrand(1,100); if(chance > 50) target->learnSpell(S_DETECT_INVISIBILITY); if(chance > 90) target->learnSpell(S_TRUE_SIGHT); } // add it to the room, make it active, and make it follow the summoner target->updateAttackTimer(true, DEFAULT_WEAPON_DELAY); target->lasttime[LT_MOB_THIEF].ltime = t; target->addToRoom(player->getRoomParent()); gServer->addActive(target); player->addPet(target); // addFollower(player, target, FALSE); petTalkDesc(target, player); target->setFlag(M_PET); if(!combatPet) { if(realm < MAX_REALM) target->setBaseRealm((Realm)realm); // TODO: fix bad cast } // find out how long it's going to last and create all the timeouts x = player->piety.getCur(); if(player->getClass() == DRUID && player->constitution.getCur() > player->piety.getCur()) x = player->constitution.getCur(); x = bonus((int) x)*60L; interval = (60L*mrand(2,4)) + (x >= 0 ? x : 0); //+ 60 * title; target->lasttime[LT_INVOKE].ltime = t; target->lasttime[LT_INVOKE].interval = interval; player->lasttime[LT_INVOKE].ltime = t; player->lasttime[LT_INVOKE].interval = 600L; // pets use LT_SPELL target->lasttime[LT_SPELL].ltime = t; target->lasttime[LT_SPELL].interval = 3; if(player->isCt()) player->lasttime[LT_INVOKE].interval = 6L; if(spellData->how == CAST) player->mp.decrease(mp); if(spellData->how == SKILL) return(PROMPT); else return(1); } //********************************************************************* // splDenseFog //********************************************************************* int splDenseFog(Creature* player, cmd* cmnd, SpellData* spellData) { int strength = 10; long duration = 600; if(player->noPotion( spellData)) return(0); if(!player->isCt()) { if(player->getRoomParent()->isUnderwater()) { player->print("Water currents prevent you from casting that spell.\n"); return(0); } } player->print("You cast a dense fog spell.\n"); broadcast(player->getSock(), player->getParent(), "%M casts a dense fog spell.", player); if(player->getRoomParent()->hasPermEffect("dense-fog")) { player->print("The spell didn't take hold.\n"); return(0); } if(spellData->how == CAST) { if(player->getRoomParent()->magicBonus()) player->print("The room's magical properties increase the power of your spell.\n"); } player->getRoomParent()->addEffect("dense-fog", duration, strength, player, true, player); return(1); } //********************************************************************* // splToxicCloud //********************************************************************* int splToxicCloud(Creature* player, cmd* cmnd, SpellData* spellData) { int strength = spellData->level; long duration = 600; if(player->noPotion( spellData)) return(0); if(!player->isCt()) { if(player->getRoomParent()->isPkSafe()) { player->print("That spell is not allowed here.\n"); return(0); } if(player->getRoomParent()->isUnderwater()) { player->print("Water currents prevent you from casting that spell.\n"); return(0); } } player->print("You cast a toxic cloud spell.\n"); broadcast(player->getSock(), player->getParent(), "%M casts a toxic cloud spell.", player); if(player->getRoomParent()->hasPermEffect("toxic-cloud")) { player->print("The spell didn't take hold.\n"); return(0); } if(spellData->how == CAST) { if(player->getRoomParent()->magicBonus()) player->print("The room's magical properties increase the power of your spell.\n"); } player->getRoomParent()->addEffect("toxic-cloud", duration, strength, player, true, player); return(1); } //********************************************************************* // splWallOfFire //********************************************************************* int splWallOfFire(Creature* player, cmd* cmnd, SpellData* spellData) { Exit *exit=0; int strength = spellData->level; long duration = 300; if(player->noPotion( spellData)) return(0); if(!player->isCt()) { if(player->getRoomParent()->isPkSafe()) { player->print("That spell is not allowed here.\n"); return(0); } if(player->getRoomParent()->isUnderwater()) { player->print("Water currents prevent you from casting that spell.\n"); return(0); } } if(cmnd->num > 2) exit = findExit(player, cmnd, 2); if(!exit) { player->print("Cast a wall of fire on which exit?\n"); return(0); } player->printColor("You cast a wall of fire spell on the %s^x.\n", exit->getCName()); broadcast(player->getSock(), player->getParent(), "%M casts a wall of fire spell on the %s^x.", player, exit->getCName()); if(exit->hasPermEffect("wall-of-fire")) { player->print("The spell didn't take hold.\n"); return(0); } if(spellData->how == CAST) { if(player->getRoomParent()->magicBonus()) player->print("The room's magical properties increase the power of your spell.\n"); } exit->addEffectReturnExit("wall-of-fire", duration, strength, player); return(1); } //********************************************************************* // splWallOfForce //********************************************************************* int splWallOfForce(Creature* player, cmd* cmnd, SpellData* spellData) { Exit *exit=0; int strength = spellData->level; long duration = 300; if(player->noPotion( spellData)) return(0); if(cmnd->num > 2) exit = findExit(player, cmnd, 2); if(!exit) { player->print("Cast a wall of force on which exit?\n"); return(0); } player->printColor("You cast a wall of force spell on the %s^x.\n", exit->getCName()); broadcast(player->getSock(), player->getParent(), "%M casts a wall of force spell on the %s^x.", player, exit->getCName()); if(exit->hasPermEffect("wall-of-force")) { player->print("The spell didn't take hold.\n"); return(0); } if(spellData->how == CAST) { if(player->getRoomParent()->magicBonus()) player->print("The room's magical properties increase the power of your spell.\n"); } exit->addEffectReturnExit("wall-of-force", duration, strength, player); return(1); } //********************************************************************* // splWallOfThorns //********************************************************************* int splWallOfThorns(Creature* player, cmd* cmnd, SpellData* spellData) { Exit *exit=0; int strength = spellData->level; long duration = 300; if(player->noPotion( spellData)) return(0); if(player->getRoomParent()->isPkSafe() && !player->isCt()) { player->print("That spell is not allowed here.\n"); return(0); } if(cmnd->num > 2) exit = findExit(player, cmnd, 2); if(!exit) { player->print("Cast a wall of thorns on which exit?\n"); return(0); } player->printColor("You cast a wall of thorns spell on the %s^x.\n", exit->getCName()); broadcast(player->getSock(), player->getParent(), "%M casts a wall of thorns spell on the %s^x.", player, exit->getCName()); if(exit->hasPermEffect("wall-of-thorns")) { player->print("The spell didn't take hold.\n"); return(0); } if(spellData->how == CAST) { if(player->getRoomParent()->magicBonus()) player->print("The room's magical properties increase the power of your spell.\n"); } exit->addEffectReturnExit("wall-of-thorns", duration, strength, player); return(1); } //********************************************************************* // bringDownTheWall //********************************************************************* void bringDownTheWall(EffectInfo* effect, BaseRoom* room, Exit* exit) { if(!effect) return; BaseRoom* targetRoom=0; bstring name = effect->getName(); if(effect->isPermanent()) { // fake being removed Effect* ef = effect->getEffect(); room->effectEcho(ef->getRoomAddStr(), exit); // extra of 2 means a 2 pulse (21-40 seconds) duration effect->setExtra(2); exit = exit->getReturnExit(room, &targetRoom); if(exit) { effect = exit->getEffect(name); if(effect) { targetRoom->effectEcho(ef->getRoomAddStr(), exit); effect->setExtra(2); } } } else { exit->removeEffectReturnExit(name, room); } }