/* * afflictions.cpp * Functions that handle poison/disease/curse/blind/vampirism/lycanthropy * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * 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 "effects.h" #include "commands.h" #include "magic.h" //********************************************************************* // cmdCreepingDoom //********************************************************************* int cmdCreepingDoom(Player* player, cmd* cmnd) { Creature* creature=0; Monster *mCreature=0; long i=0, t=0; int chance=0, dmg=0; player->clearFlag(P_AFK); if(!player->ableToDoCommand()) return(0); if(!player->isCt()) { if(!player->knowsSkill("creeping-doom")) { player->print("You lack the ability to strike someone with creeping doom.\n"); return(0); } if(!player->alignInOrder()) { player->print("%s requires you to be evil to do that.\n", gConfig->getDeity(player->getDeity())->getName().c_str()); return(0); } } if(cmnd->num < 2) { player->print("Strike whom with creeping doom?\n"); return(0); } creature = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], true, true); if(!creature || creature == player) { player->print("You don't see that here.\n"); return(0); } // pCreature = creature->getPlayer(); mCreature = creature->getMonster(); player->smashInvis(); player->setFishing(false); if(!player->canAttack(creature)) return(0); if(mCreature && mCreature->getType() == ARACHNID && !player->checkStaff("You cannot harm arachnids with creeping-doom.\n") ) return(0); if( creature->getDeity() == ARACHNUS && !player->checkStaff("You cannot harm followers of Arachnus with creeping-doom.\n") ) return(0); double level = player->getSkillLevel("creeping-doom"); i = LT(player, LT_SMOTHER); t = time(0); if(i > t && !player->isCt()) { player->pleaseWait(i-t); return(0); } player->lasttime[LT_SMOTHER].ltime = t; player->updateAttackTimer(); player->lasttime[LT_SMOTHER].interval = 120L; chance = ((int)(level - creature->getLevel()) * 20) + bonus((int) player->piety.getCur()) * 5 + 25; chance = MIN(chance, 80); dmg = mrand((int)(level*2), (int)(level*3)); if(mCreature) mCreature->addEnmCrt(player); if(!creature->isCt()) { if(mrand(1, 100) > chance) { player->print("You failed to strike %N with creeping doom.\n", creature); broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M tried to strike %N with creeping doom!", player, creature); creature->print("%M tried to strike you with creeping doom!\n", player); player->checkImprove("creeping-doom", false); return(0); } if(creature->chkSave(BRE, player, 0)) { player->printColor("^yYour creeping-doom strike was interrupted!\n"); creature->print("You manage to partially avoid %N's creeping-doom strike.\n", player); dmg /= 2; } } player->printColor("You struck %N with creeping doom for %s%d^x damage.\n", creature, player->customColorize("*CC:DAMAGE*"), dmg); player->checkImprove("creeping-doom", true); player->statistics.attackDamage(dmg, "creeping-doom"); creature->printColor("%M struck you with creeping doom for %s%d^x damage!\n", player, creature->customColorize("*CC:DAMAGE*"), dmg); broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M struck %N with creeping doom!", player, creature); // if they didn't die from the damage, curse them if(!player->doDamage(creature, dmg, CHECK_DIE)) creature->addEffect("creeping-doom", 200, (int)level, true, player); return(0); } //******************************************************************** // cmdPoison //******************************************************************** // the Arachnus ability to inflict enemies with poison int cmdPoison(Player* player, cmd* cmnd) { Creature* creature=0; Monster *mCreature=0; long i=0, t=0; int chance=0; unsigned int dur=0; player->clearFlag(P_AFK); if(!player->ableToDoCommand()) return(0); if(!player->isCt()) { if(!player->knowsSkill("poison")) { if(player->getClass() == ASSASSIN || player->getSecondClass() == ASSASSIN) { player->print("Assassins must use envenom to poison people.\n"); } else { player->print("You lack the ability to poison people.\n"); } return(0); } if(!player->alignInOrder()) { player->print("%s requires you to be evil to do that.\n", gConfig->getDeity(player->getDeity())->getName().c_str()); return(0); } } if(cmnd->num < 2) { player->print("Poison whom?\n"); return(0); } creature = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], true, true); if(!creature || creature == player) { player->print("You don't see that here.\n"); return(0); } // pCreature = creature->getPlayer(); mCreature = creature->getMonster(); player->smashInvis(); player->setFishing(false); if(!player->canAttack(creature)) return(0); if(mCreature && mCreature->getType() == ARACHNID && !player->checkStaff("You cannot poison arachnids with this ability.\n") ) return(0); if( creature->getDeity() == ARACHNUS && !player->checkStaff("You cannot poison followers of Arachnus with this ability.\n") ) return(0); double level = player->getSkillLevel("poison"); i = LT(player, LT_DRAIN_LIFE); t = time(0); if(i > t && !player->isCt()) { player->pleaseWait(i-t); return(0); } player->lasttime[LT_DRAIN_LIFE].ltime = t; player->updateAttackTimer(); player->lasttime[LT_DRAIN_LIFE].interval = 120L; chance = ((int)(level - creature->getLevel()) * 20) + bonus((int) player->piety.getCur()) * 5 + 25; chance = MIN(chance, 80); dur = standardPoisonDuration((short)level, creature->constitution.getCur()); if(mCreature) mCreature->addEnmCrt(player); if(!player->isCt()) { if(mrand(1, 100) > chance || creature->isPoisoned() || creature->immuneToPoison()) { player->printColor("^GYou fail to poison %N.\n", creature); creature->printColor("^G%M tried to poison you!\n", player); broadcastGroup(false, creature, "%M tried to poison %N.\n", player, creature); broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M tried to poison %N.", player, creature); player->checkImprove("poison", false); return(0); } } player->printColor("^GYou poison %N.\n", creature); creature->printColor("^G%M poisons you!\n", player); broadcastGroup(false, creature, "^G%M poisons %N.\n", player, creature); broadcast(player->getSock(), creature->getSock(), player->getRoom(), "^G%M poisons %N.", player, creature); if(creature->chkSave(POI, player, 0)) { player->print("%N partially resists your poison.\n", creature); creature->print("You partially resists %N's poison.\n", player); level = level * 2 / 3; dur = dur * 2 / 3; } player->checkImprove("poison", true); creature->poison(player, (unsigned int)level, dur); return(0); } //******************************************************************** // poison //******************************************************************** // the calling function is responsible for announcing the poisoning void Creature::poison(Creature *enemy, unsigned int damagePerPulse, unsigned int duration) { if(immuneToPoison()) return; setPoisonedBy(""); if(enemy) { if(isPlayer()) { if(enemy->isPlayer() || enemy->isPet()) { setFlag(P_POISONED_BY_PLAYER); clearFlag(P_POISONED_BY_MONSTER); } else { setFlag(P_POISONED_BY_MONSTER); clearFlag(P_POISONED_BY_PLAYER); } if(enemy->isPet()) setPoisonedBy(enemy->following->name); else if(enemy->isMonster() && !enemy->flagIsSet(M_NO_PREFIX)) setPoisonedBy((bstring)"a " + enemy->name); else if(this == enemy) setPoisonedBy((bstring)himHer() + "self"); else setPoisonedBy(enemy->name); } else if(isMonster()) { if(enemy->isPlayer()) setPoisonedBy(enemy->name); else if(enemy->isPet()) setPoisonedBy(enemy->following->name); else if(enemy->isMonster() && !enemy->flagIsSet(M_NO_PREFIX)) setPoisonedBy((bstring)"a " + enemy->name); else setPoisonedBy(enemy->name); } } addEffect("poison", duration, damagePerPulse, false, this); } //******************************************************************** // immuneToPoison //******************************************************************** bool Creature::immuneToPoison() const { if(isUndead() || monType::noLivingVulnerabilities(type)) return(true); if(deity == ARACHNUS) return(true); // check on mobs if(isMonster()) { if(flagIsSet(M_NO_POISON)) return(true); // check on players } else { if(isStaff()) return(true); } return(false); } //********************************************************************* // isPoisoned //********************************************************************* bool Creature::isPoisoned() const { if(immuneToPoison()) return(false); return(effects.hasPoison()); } //********************************************************************* // hasPoison //********************************************************************* bool Effects::hasPoison() const { std::list<EffectInfo*>::const_iterator eIt; for(eIt = list.begin() ; eIt != list.end() ; eIt++) { if((*eIt)->isPoison()) return(true); } return(false); } //********************************************************************* // curePoison //********************************************************************* bool Creature::curePoison() { if(isPlayer()) { clearFlag(P_POISONED_BY_PLAYER); clearFlag(P_POISONED_BY_MONSTER); } removeEffect("slow-poison"); setPoisonedBy(""); return(effects.removePoison()); } //********************************************************************* // removePoison //********************************************************************* bool Effects::removePoison() { bool removed=false; // remove all effects flagged as poison std::list<EffectInfo*>::iterator eIt; EffectInfo *effect=0; for(eIt = list.begin() ; eIt != list.end() ;) { effect = (*eIt); if(effect->isPoison()) { removed = true; effect->remove(); delete effect; eIt = list.erase(eIt); } else eIt++; } return(removed); } //******************************************************************** // standardPoisonDuration //******************************************************************** unsigned int standardPoisonDuration(short level, short con) { int dur = 60 * mrand(1,3) - (60*bonus((int)con)) + level*10; if(con > 120) { // a spread between 400 (50%) and 120 (0%) resistance double percent = 1 - (con - 120) / (680 - 120); percent *= dur; dur = (int)percent; } return(MAX(60,dur)); } // low con, level 40: 1-3 mins // high con, level 40: 1 min // //******************************************************************** // disease //******************************************************************** // the calling function is responsible for announcing the diseasing void Creature::disease(Creature* enemy, unsigned int damagePerPulse) { if(immuneToDisease()) return; addPermEffect("disease", damagePerPulse, false); } //******************************************************************** // immuneToDisease //******************************************************************** bool Creature::immuneToDisease() const { // no need to call noLivingVulnerabilities if(isMonster()) return(true); if(isStaff()) return(true); if(isUndead()) return(true); return(false); } //********************************************************************* // isDiseased //********************************************************************* bool Creature::isDiseased() const { if(immuneToDisease()) return(false); return(effects.hasDisease()); } //********************************************************************* // hasDisease //********************************************************************* bool Effects::hasDisease() const { std::list<EffectInfo*>::const_iterator eIt; for(eIt = list.begin() ; eIt != list.end() ; eIt++) { if((*eIt)->isDisease()) return(true); } return(false); } //********************************************************************* // cureDisease //********************************************************************* bool Creature::cureDisease() { return(effects.removeDisease()); } //********************************************************************* // removeDisease //********************************************************************* bool Effects::removeDisease() { bool removed=false; // remove all effects flagged as disease std::list<EffectInfo*>::iterator eIt; EffectInfo *effect=0; for(eIt = list.begin() ; eIt != list.end() ;) { effect = (*eIt); if(effect->isDisease()) { removed = true; effect->remove(); delete effect; eIt = list.erase(eIt); } else eIt++; } return(removed); } //********************************************************************* // removeCurse //********************************************************************* // remove curse effects bool Creature::removeCurse() { return(effects.removeCurse()); } bool Effects::removeCurse() { bool removed=false; std::list<EffectInfo*>::iterator eIt; EffectInfo *effect=0; for(eIt = list.begin() ; eIt != list.end() ;) { effect = (*eIt); if(effect->isCurse()) { removed = true; effect->remove(); delete effect; eIt = list.erase(eIt); } else eIt++; } return(removed); } //******************************************************************** // isBlind //******************************************************************** bool Creature::isBlind() const { if(isMonster() || isStaff()) return(false); return(isEffected("blindness")); } //*********************************************************************** // isNew //*********************************************************************** // these checks distinguish legacy vampires and werewolves from new ones. // once these legacy classes have been removed, these functions can just // be replaced with isEffected("vampirism") and isEffected("lycanthropy") bool Creature::isNewVampire() const { if(isPlayer() && cClass == VAMPIRE) return(false); return(isEffected("vampirism")); } bool Creature::isNewWerewolf() const { if(isPlayer() && cClass == WEREWOLF) return(false); return(isEffected("lycanthropy")); } //*********************************************************************** // makeVampire //*********************************************************************** void Creature::makeVampire() { addPermEffect("vampirism"); if(isPlayer()) { setFlag(P_CHAOTIC); // make sure the master still exists and is still a vampire Player* player = getPlayer(); if(player->getAfflictedBy() != "") { bool online = true; Player* master = gServer->findPlayer(player->getAfflictedBy()); if(!master) { loadPlayer(player->getAfflictedBy().c_str(), &master); online = false; } if(!master || !master->isEffected("vampirism")) player->setAfflictedBy(""); else { master->minions.push_back(name); master->save(online); } if(master && !online) free_crt(master); } } if(!knowsSkill("mist")) addSkill("mist", 1); if(!knowsSkill("bite")) addSkill("bite", 1); if(!knowsSkill("hypnotize")) addSkill("hypnotize", 1); } //*********************************************************************** // willBecomeVampire //*********************************************************************** bool Creature::willBecomeVampire() const { if(level < 7 || cClass == PALADIN || isEffected("lycanthropy") || isUndead()) return(false); // only evil clerics become vampires if(cClass == CLERIC && !(deity == ARAMON || deity == ARACHNUS)) return(false); if(monType::noLivingVulnerabilities(type)) return(false); return(true); } //*********************************************************************** // vampireCharm //*********************************************************************** // determines if player is a spawn of the master // if not, it does some cleanup that should have been done elsewhere // (an extra check to make sure everything is set properly) bool Creature::vampireCharmed(Player* master) { if(!isPlayer() || !master) return(false); // if they're on their way to becoming a vampire, but arent one yet, don't // mess with minion or afflictedBy fields if(isEffected("porphyria")) return(false); Player* player = getPlayer(); bool charmed = true; // we need to make sure the minion lists match up; // this won't be the case if somebody suicides and remakes if(charmed && (!isEffected("vampirism") || !master->isEffected("vampirism"))) charmed = false; if(charmed && player->getAfflictedBy() != master->name) charmed = false; if(charmed) { bool found = false; std::list<bstring>::iterator mIt; for(mIt = master->minions.begin() ; mIt != master->minions.end() && !found ; mIt++) { if(*mIt == (bstring)player->name) found = true; } if(!found) charmed = false; } // if they're not charmed, clean up these lists if(!charmed) { if(player->getAfflictedBy() == master->name) player->setAfflictedBy(""); master->minions.remove(player->name); } return(charmed); } //******************************************************************** // clearMinions //******************************************************************** void Creature::clearMinions() { if(!isPlayer()) return; Player* player = getPlayer(), *target=0; bool online = true; if(player->getAfflictedBy() != "") { target = gServer->findPlayer(player->getAfflictedBy()); if(!target) { loadPlayer(player->getAfflictedBy().c_str(), &target); online = false; } if(target) { target->minions.remove(player->name); target->save(online); } if(!online) free_crt(target); } std::list<bstring>::iterator mIt; for(mIt = player->minions.begin() ; mIt != player->minions.end() ; mIt++) { online = true; target = gServer->findPlayer(*mIt); if(!target) { loadPlayer((*mIt).c_str(), &target); online = false; } if(target) { if(target->getAfflictedBy() == (bstring)player->name) { target->setAfflictedBy(""); target->save(online); } if(!online) free_crt(target); } } } //******************************************************************** // addPorphyria //******************************************************************** bool Creature::addPorphyria(Creature *killer, int chance) { // TODO: Dom: disable porphyria until new vamps are tested return(false); /* const RaceData* rdata = gConfig->getRace(race); if(rdata) chance -= rdata->getPorphyriaResistance(); if(mrand(1,100) > chance) return(false); if(!willBecomeVampire()) return(false); if(isEffected("porphyria") || isEffected("undead-ward") || isEffected("drain-shield")) return(false); if(!killer || !killer->isNewVampire()) return(false); addEffect("porphyria", 0, 0, true, killer); // if they become a vampire, they'll be permanently charmed by their master if(killer->isPlayer()) { killer->print("You have infected %N with porphyria.\n", this); if(isPlayer()) getPlayer()->setAfflictedBy(killer->name); } else if(isPlayer()) getPlayer()->setAfflictedBy(""); return(true); */ } //******************************************************************** // sunlightDamage //******************************************************************** // return true if they died, false if they lived bool Creature::sunlightDamage() { if(isStaff() || !isNewVampire()) return(false); int dmg = hp.getMax() / 5 + mrand(1,10); unmist(); wake("Terrible nightmares disturb your sleep!"); printColor("^Y^#The searing sunlight burns your flesh!\n"); broadcast(getSock(), getRoom(), "^Y%M's flesh is burned by the sunlight!", this); // lots of damage every 5 seconds hp.decrease(dmg); if(hp.getCur() < 1) { printColor("^YYou have been disintegrated!\n"); if(isPlayer()) getPlayer()->die(SUNLIGHT); else getMonster()->mobDeath(); return(true); } return(false); } //*********************************************************************** // makeWerewolf //*********************************************************************** void Creature::makeWerewolf() { addPermEffect("lycanthropy"); if(!knowsSkill("maul")) skills["maul"] = new CrtSkill("maul", 1); if(!knowsSkill("frenzy")) skills["frenzy"] = new CrtSkill("frenzy", 1); if(!knowsSkill("howl")) skills["howl"] = new CrtSkill("howl", 1); if(!knowsSkill("claw")) skills["claw"] = new CrtSkill("claw", level * 5); } //*********************************************************************** // willBecomeWerewolf //*********************************************************************** bool Creature::willBecomeWerewolf() const { if(level < 7 || isEffected("lycanthropy") || isUndead()) return(false); if(monType::noLivingVulnerabilities(type)) return(false); return(true); } //******************************************************************** // addLycanthropy //******************************************************************** bool Creature::addLycanthropy(Creature *killer, int chance) { // TODO: Dom: disable lycanthropy until new wolves are tested return(false); /* if(mrand(1,100) > chance) return(false); if(!willBecomeWerewolf()) return(false); if(isEffected("lycanthropy")) return(false); if(!killer || !killer->isNewWerewolf()) return(false); addEffect("lycanthropy", 0, 0, true, killer); if(killer->isPlayer()) { killer->print("You have infected %N with lycanthropy.\n", this); if(isPlayer()) getPlayer()->setAfflictedBy(killer->name); } else if(isPlayer()) getPlayer()->setAfflictedBy(""); return(true); */ } //********************************************************************* // splCurePoison //********************************************************************* // This function allows a player to cast a curepoison spell on themself, // another player or a monster. It will remove any poison that is in // that player's system. int splCurePoison(Creature* player, cmd* cmnd, SpellData* spellData) { Creature* target=0; if(player->getClass() == LICH && player->getLevel() == 1 && spellData->how == CAST) { player->print("You are not experienced enough to cast that yet.\n"); return(0); } if( player->isPlayer() && player->getClass() != CLERIC && player->getClass() != PALADIN && !player->isCt() && spellData->how == CAST ) { player->print("Your class may not cast that spell.\n"); return(0); } // Curepoison self if(cmnd->num == 2) { target = player; if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("Cure-poison spell cast on yourself.\n"); player->print("You feel much better.\n"); broadcast(player->getSock(), player->getRoom(), "%M casts cure-poison on %sself.", player, player->himHer()); } else if(spellData->how == POTION && player->isPoisoned()) player->print("You feel the poison subside.\n"); else if(spellData->how == POTION) player->print("Nothing happens.\n"); // Cure a monster or player } 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("That person is not here.\n"); return(0); } if(checkRefusingMagic(player, target)) return(0); if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("Cure-poison cast on %N.\n", target); broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts cure-poison on %N.", player, target); target->print("%M casts cure-poison on you.\nYou feel much better.\n", player); } } if(target->inCombat(false)) player->smashInvis(); target->curePoison(); return(1); } //********************************************************************* // splSlowPoison //********************************************************************* int splSlowPoison(Creature* player, cmd* cmnd, SpellData* spellData) { Creature* target=0; if(player->getClass() == LICH && player->getLevel() == 1 && spellData->how == CAST) { player->print("You are not experienced enough to cast that yet.\n"); return(0); } // slow_poison self if(cmnd->num == 2) { target = player; if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("Slow-poison spell cast on yourself.\n"); broadcast(player->getSock(), player->getRoom(), "%M casts slow-poison on %sself.", player, player->himHer()); if(!player->isPoisoned()) { player->print("Nothing happens.\n"); return(0); } player->print("You feel the poison subside somewhat.\n"); } else if(spellData->how == POTION && player->isPoisoned()) player->print("You feel the poison subside somewhat.\n"); else if(spellData->how == POTION) player->print("Nothing happens.\n"); // Cure a monster or player } else { if(noPotion(player, spellData)) return(0); cmnd->str[2][0] = up(cmnd->str[2][0]); // monsters are now valid targets target = player->getRoom()->findCreature(player, cmnd->str[2], cmnd->val[2], false); if(!target) { player->print("That person is not here.\n"); return(0); } if(checkRefusingMagic(player, target)) return(0); player->print("Slow-poison cast on %N.\n", target); broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts slow-poison on %N.", player, target); target->print("%M casts slow-poison on you.\n", player); if(!target->isPoisoned()) { player->print("Nothing happens.\n"); if(player != target) target->print("Nothing happens.\n"); return(0); } target->print("You feel a little better.\n"); } if(target->inCombat(false)) player->smashInvis(); target->addPermEffect("slow-poison"); return(1); } //********************************************************************* // splCureDisease //********************************************************************* int splCureDisease(Creature* player, cmd* cmnd, SpellData* spellData) { Creature* target=0; if(player->getClass() != CLERIC && player->getClass() != PALADIN && !player->isStaff() && spellData->how == CAST) { player->print("Only clerics and paladins may cast that spell.\n"); return(0); } if(cmnd->num == 2) { target = player; if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("Cure-disease spell cast on yourself.\n"); player->print("Your fever subsides.\n"); broadcast(player->getSock(), player->getRoom(), "%M casts cure-disease on %sself.", player, player->himHer()); } else if(spellData->how == POTION && player->isDiseased()) player->print("You feel your fever subside.\n"); else if(spellData->how == POTION) player->print("Nothing happens.\n"); } 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("That's not here.\n"); return(0); } if(checkRefusingMagic(player, target)) return(0); player->print("You cure-disease cast on %N.\n", target); broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts cure-disease on %N.", player, target); target->print("%M casts cure-disease on you.\nYou feel your fever subside.\n", player); } if(target->inCombat(false)) player->smashInvis(); target->cureDisease(); return(1); } //********************************************************************* // splCureBlindness //********************************************************************* int splCureBlindness(Creature* player, cmd* cmnd, SpellData* spellData) { Creature* target=0; if(player->getClass() == LICH && player->getLevel() < 3 && spellData->how == CAST) { player->print("You are not experienced enough to cast that yet.\n"); return(0); } if(cmnd->num == 2) { target = player; if(!player->isStaff()) { if( spellData->how != POTION || !player->isEffected("blindness") || player->flagIsSet(P_DM_BLINDED) ) player->print("Nothing happens.\n"); } } 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("That's not here.\n"); return(0); } if(checkRefusingMagic(player, target)) return(0); if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("You cast cure blindess on %N.\n", target); broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts cure blindness on %N.", player, target); if(target->isPlayer()) { target->print("%M casts cure blindness on you.\n", player); if( target->isEffected("blindness") && (player->isCt() || !target->flagIsSet(P_DM_BLINDED)) ) { //target->print("You can see again.\n"); } else { target->print("Nothing happens.\n"); } } } } if(target->inCombat(false)) player->smashInvis(); if(target->isPlayer()) if(player->isCt()) target->clearFlag(P_DM_BLINDED); target->removeEffect("blindness"); return(1); } //********************************************************************* // effectPoison //********************************************************************* // these effects can be removed with cure-poison bool hearMobTick(Socket* sock); bool effectPoison(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom) { if(target->immuneToPoison()) return(false); if(target->isEffected("slow-poison") && mrand(1,100) > 50) return(true); if(target->isPlayer() && target->flagIsSet(P_POISONED_BY_PLAYER) && target->inCombat()) return(true); if(target->isMonster() && (target->flagIsSet(M_WILL_POISON) || target->isPet()) && mrand(1,100) > 50 ) return(true); switch(action) { case EFFECT_COMPUTE: break; case EFFECT_APPLY: break; case EFFECT_UNAPPLY: break; case EFFECT_PULSE: if(effect->getName() == "poison") { // generic poison target->wake("Terrible nightmares disturb your sleep!"); target->printColor("^r^#Poison courses through your veins.\n"); broadcast(target->getSock(), target->getRoom(), "Poison courses through %N's veins.", target); int dmg = effect->getStrength() + mrand(1,3); if(target->constitution.getCur() > 120) { // a spread between 400 (50%) and 120 (0%) resistance double percent = 1 - (target->constitution.getCur() - 120) / (680 - 120); percent *= dmg; dmg = (int)percent; } if(target->isMonster()) broadcast(hearMobTick, "^y*** Poison courses through %N's body (L%d,R%s).", target, target->getLevel(), target->getRoom()->fullName().c_str()); dmg = MAX(1,dmg); target->hp.decrease(dmg); if(target->isMonster() && target->getPoisonedBy() != "") { const Player* player = gServer->findPlayer(target->getPoisonedBy()); if(player) { // everybody but arachnus gets half experience for poison damage int exp = dmg; if(player->getDeity() != ARACHNUS) exp /= 2; target->getMonster()->addEnmDmg(player, exp); } } // mark them for death if(target->hp.getCur() < 1) { broadcast(target->getSock(), target->getRoom(), "%M drops dead from poison.", target); target->setDeathType(POISON_GENERAL); if(target->isPlayer()) { if(target->flagIsSet(P_POISONED_BY_PLAYER)) { target->setDeathType(POISON_PLAYER); } else if(target->flagIsSet(P_POISONED_BY_MONSTER)) { target->setDeathType(POISON_MONSTER); } } return(false); } } break; default: break; } return(true); } //********************************************************************* // effectDisease //********************************************************************* // these effects can be removed with cure-disease bool effectDisease(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom) { switch(action) { case EFFECT_COMPUTE: break; case EFFECT_APPLY: if(effect->getName() == "lycanthropy") { // can they even be effected by lycanthropy? if(!target->willBecomeWerewolf()) return(false); effect->setStrength(1); // 90 minutes until lycanthropy takes effect effect->setDuration(90 * 60); } break; case EFFECT_UNAPPLY: break; case EFFECT_PULSE: if(effect->getName() == "lycanthropy") { // permanent lycanthropy has no pulse effects if(effect->getDuration() == -1) return(true); // if they let their disease run too low, it'll become permanent if(effect->getDuration() <= 20) effect->setDuration(-1); } else if(effect->getName() == "disease") { // generic disease target->wake("Terrible nightmares disturb your sleep!"); target->printColor("^#^rFever grips your mind.\n"); target->printColor("^bYou feel nauseous.\n"); broadcast(target->getSock(), target->getRoom(), "Fever grips %N.", target); target->updateAttackTimer(true, dice(1,6,3)); int dmg = effect->getStrength() + mrand(1,3); if(target->constitution.getCur() > 120) { // a spread between 400 (50%) and 120 (0%) resistance double percent = 1 - (target->constitution.getCur() - 120) / (680 - 120); percent *= dmg; dmg = (int)percent; } dmg = MAX(1,dmg); target->hp.decrease(dmg); // mark them for death if(target->hp.getCur() < 1) { broadcast(target->getSock(), target->getRoom(), "%M dies from disease.", target); target->setDeathType(DISEASE); return(false); } } break; default: break; } return(true); } //********************************************************************* // effectCurse //********************************************************************* // these effects can be removed with remove-curse bool effectCurse(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom) { // how many minutes until porphyria takes effect int minutes = 90; switch(action) { case EFFECT_COMPUTE: break; case EFFECT_APPLY: if(effect->getName() == "porphyria") { // can they even be effected by porphyria? if(!target->willBecomeVampire()) return(false); effect->setStrength(1); effect->setDuration(minutes * 60); } break; case EFFECT_UNAPPLY: break; case EFFECT_PULSE: if(effect->getName() == "porphyria") { Player* pTarget = target->getPlayer(); bool remove = false; // over 90 minutes, max will get up to about 270, so range is 1-27 at worst int dmg = mrand(1, MAX(2, effect->getStrength()/10)); target->wake("Terrible nightmares disturb your sleep!"); // player-inflicted porphyria does less damage if(pTarget && pTarget->getAfflictedBy() != "") dmg /= 2; dmg = MAX(1, dmg); // pulse once every 20 seconds, base 50% chance to take damage // if a player is the cause, make it happen less often if(mrand(0,1)) { effect->setStrength(effect->getStrength() + 1); switch(mrand(1,4)) { case 1: target->printColor("^rBlood sputters through your veins.\n"); break; case 2: target->printColor("^rBlood drains from your head.\n"); break; case 3: target->printColor("^rFever grips your mind.\n"); break; default: target->printColor("^WA sharp headache strikes you.\n"); break; } target->printColor("You take %s%d^x damage.\n", target->customColorize("*CC:DAMAGE*"), dmg); target->hp.decrease(dmg); } // 3 pulses every minute // death by porphyria will also trigger if( (effect->getStrength() > (minutes * 3) && !target->getRoom()->isSunlight()) || (pTarget && pTarget->hp.getCur() < 1) ) { remove = true; if(target->willBecomeVampire()) { target->makeVampire(); if(pTarget) { pTarget->printColor("^rYour body shudders, your limbs go as cold as ice!\n"); pTarget->printColor("^rFangs suddenly grow, replacing your teeth and sending blood running down your throat!\n"); pTarget->printColor("^R^#You have been turned into a vampire.\n"); } broadcast(target->getSock(), target->getRoom(), "^r%M shudders and convulses!", pTarget); broadcast(target->getSock(), target->getRoom(), "^rFangs grow from %N's teeth!", pTarget); } } if(target->hp.getCur() < 1) { broadcast(target->getSock(), target->getRoom(), "%M dies from poyphyria.", target); target->setDeathType(DISEASE); return(false); } if(remove) return(false); else { // porphyria should never actually wear off; if they are staying in // the sunlight to avoid becoming a vampire, just prolong it if(effect->getDuration() <= 20) effect->setDuration(effect->getDuration() + 20); } } else if(effect->getName() == "wounded") { target->wake("Terrible nightmares disturb your sleep!"); int dmg = mrand(1+target->hp.getMax()/30, 1+target->hp.getMax()/20); target->printColor("^RYour wounds fester and bleed for %s%d^R damage.\n", target->customColorize("*CC:DAMAGE*"), dmg); broadcast(target->getSock(), target->getRoom(), "%M's wounds fester and bleed.", target); target->hp.decrease(dmg); // mark them for death if(target->hp.getCur() < 1) { broadcast(target->getSock(), target->getRoom(), "%M dies from cursed wounds.", target); target->setDeathType(WOUNDED); return(false); } } else if(effect->getName() == "creeping-doom") { if(target->isMonster() && target->getMonster()->getType() == ARACHNID) return(true); if(target->getDeity() == ARACHNUS) return(true); int dmg = effect->getStrength() / 2 + mrand(1,3); target->printColor("^DCursed spiders crawl all over your body and bite you for %s%d^D damage.\n", target->customColorize("*CC:DAMAGE*"), dmg); broadcast(target->getSock(), target->getRoom(), "Cursed spiders crawl all over %N.", target); target->hp.decrease(dmg); // mark them for death if(target->hp.getCur() < 1) { broadcast(target->getSock(), target->getRoom(), "Cursed spiders devour %N!", target); target->setDeathType(CREEPING_DOOM); return(false); } } break; default: break; } return(true); } //********************************************************************* // isCurse //********************************************************************* bool EffectInfo::isCurse() const { return(myEffect->effFn == effectCurse); } //********************************************************************* // isDisease //********************************************************************* bool EffectInfo::isDisease() const { // lycanthropy is only a disease if it's not permanent - once permanent, it's // considered an afflication and can't be removed by cure-disease if(myEffect->name == "lycanthropy" && duration == -1) return(false); if(myEffect->effFn == effectDisease) return(true); return(false); } //********************************************************************* // isPoison //********************************************************************* bool EffectInfo::isPoison() const { return(myEffect->effFn == effectPoison); } //********************************************************************* // splCurse //********************************************************************* // This function allows a player to curse a item in their inventory int splCurse(Creature* player, cmd* cmnd, SpellData* spellData) { Object *object=0; if(spellData->how == CAST && player->getClass() != MAGE && player->getClass() != LICH && !player->isStaff()) { player->print("Only mages and liches can curse.\n"); return(0); } if(cmnd->num < 3) { player->print("Curse what?\n"); return(0); } object = findObject(player, player->first_obj, cmnd, 2); if(!object) { player->print("You don't have that in your inventory.\n"); return(0); } if(object->flagIsSet(O_CURSED)) { player->print("That object is already cursed.\n"); return(1); } object->setFlag(O_CURSED); player->printColor("%O glows darkly.\n", object); broadcast(player->getSock(), player->getRoom(), "%M places a curse on %1P.", player, object); return(1); } //********************************************************************* // splRemoveCurse //********************************************************************* // This function allows a player to remove a curse on all the items // in their inventory or on another player's inventory int splRemoveCurse(Creature* player, cmd* cmnd, SpellData* spellData) { Creature* target=0; otag* op=0; int i=0; bool equipment=true; if(player->getClass() == LICH && player->getLevel() < 4 && spellData->how == CAST) { player->print("You are not experienced enough to cast that yet.\n"); return(0); } // Cast remove-curse on self if(cmnd->num == 2) { target = player; if(spellData->how == CAST || spellData->how == SCROLL || spellData->how == WAND) { player->print("Remove-curse spell cast.\n"); broadcast(player->getSock(), player->getRoom(), "%M casts remove-curse on %sself.", player, player->himHer()); } else if(spellData->how == POTION) player->print("You feel relieved of burdens.\n"); // Cast remove-curse 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); } // this purposely does not check the refuse list if(target->flagIsSet(P_LINKDEAD) && target->isPlayer()) { player->print("%M doesn't want that cast on them right now.\n", target); return(0); } player->print("Remove-curse cast on %N.\n", target); target->print("%M casts a remove-curse spell on you.\n", player); broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M casts remove-curse on %N.", player, target); } if(target->inCombat(false)) player->smashInvis(); // remove all effects flagged as cursed if(target->removeCurse()) equipment = false; // if the remove-curse spell didn't remove a curse on the player, // it wants to remove a curse on their equipment if(equipment) { for(i=0; i<MAXWEAR; i++) { if(target->ready[i]) { target->ready[i]->clearFlag(O_CURSED); target->ready[i]->remDarkness(); } } if(target->flagIsSet(P_DARKNESS)) { op = target->first_obj; while(op) { op->obj->remDarkness(); op = op->next_tag; } player->print("The aura of darkness around you dissipates.\n"); broadcast(player->getSock(), player->getRoom(), "The aura of darkness around %N dissipates.", player); target->clearFlag(P_DARKNESS); } } return(1); }