/* * undead.cpp * Functions for undead players. * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * 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" //********************************************************************* // cmdBite //********************************************************************* int cmdBite(Player* player, cmd* cmnd) { Creature* target=0; Player *pTarget=0; long i=0, t=0; int chance=0, dmgnum=0; Damage damage; if(!player->ableToDoCommand() || !player->checkAttackTimer()) return(0); if((!player->knowsSkill("bite") || !player->isEffected("vampirism")) && !player->isStaff()) { player->print("You lack the fangs!\n"); return(0); } if(isDay() && !player->isCt()) { player->print("You may only bite people during the night.\n"); return(0); } if(player->inCombat() && !player->checkStaff("Not while you're in combat.\n")) return(0); if(!(target = player->findVictim(cmnd, 1, true, false, "Bite what?\n", "You don't see that here.\n"))) return(0); pTarget = target->getAsPlayer(); if(!player->canAttack(target)) return(0); if(player->isBlind()) { player->printColor("^CYou're blind!\n"); return(0); } if(target->isEffected("drain-shield") && target->isMonster()) { player->print("Your bite would be ineffective.\n"); return(0); } if( target->isUndead() && !player->checkStaff("That won't work on the undead.\n") ) { if(target->isMonster()) target->getAsMonster()->addEnemy(player); return(0); } i = player->lasttime[LT_PLAYER_BITE].ltime; t = time(0); if((t - i < 30L) && !player->isDm()) { player->pleaseWait(30L-t+i); return(0); } player->smashInvis(); player->interruptDelayedActions(); if(pTarget) { } else { target->getAsMonster()->addEnemy(player); // Activates lag protection. if(player->flagIsSet(P_LAG_PROTECTION_SET)) player->setFlag(P_LAG_PROTECTION_ACTIVE); } player->updateAttackTimer(true, DEFAULT_WEAPON_DELAY); player->lasttime[LT_PLAYER_BITE].ltime = t; player->lasttime[LT_READ_SCROLL].interval = 3L; player->lasttime[LT_READ_SCROLL].ltime = t; player->lasttime[LT_PLAYER_BITE].interval = 30L; chance = 35 + ((int)((player->getSkillLevel("bite") - target->getLevel()) * 20) + bonus((int) player->strength.getCur()) * 5); chance = MIN(chance, 85); if(target->isEffected("drain-shield")) chance /= 2; if(player->isDm()) chance = 100; if(mrand(1, 100) > chance) { player->print("%s eludes your bite.\n", target->upHeShe()); player->checkImprove("bite", false); target->print("%M tried to bite you!\n",player); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M tried to bite %N.", player, target); return(0); } damage.set(mrand((int)(player->getSkillLevel("bite")*3), (int)(player->getSkillLevel("bite")*4))); target->modifyDamage(player, NEGATIVE_ENERGY, damage); dmgnum = damage.get(); damage.set(MIN(damage.get(), target->hp.getCur() + 1)); if(damage.get() < 1) damage.set(1); player->printColor("You bite %N for %s%d^x damage.\n", target, player->customColorize("*CC:DAMAGE*").c_str(), dmgnum); player->checkImprove("bite", true); if(player->hp.getCur() < player->hp.getMax() && damage.getDrain()) { player->print("The blood of %N revitalizes your strength.\n", target); player->hp.increase(damage.getDrain()); } if(player->getClass() == CARETAKER) log_immort(false,player, "%s bites %s.\n", player->getCName(), target->getCName()); target->printColor("%M bites you for %s%d^x damage.\n", player, target->customColorize("*CC:DAMAGE*").c_str(), dmgnum); target->stun((mrand(5, 8) + bonus((int) player->strength.getCur()))); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M bites %N!", player, target); broadcastGroup(false, target, "^M%M^x bites ^M%N^x for *CC:DAMAGE*%d^x damage, %s%s\n", player, target, dmgnum, target->heShe(), target->getStatusStr(dmgnum)); player->statistics.attackDamage(dmgnum, "bite"); if(player->doDamage(target, damage.get(), CHECK_DIE)) { if(player->getClass() == CARETAKER) log_immort(true, player, "%s killed %s with a bite.\n", player->getCName(), target->getCName()); } else { if(!induel(player, pTarget)) { // 5% chance to get porphyria when bitten by a vampire target->addPorphyria(player, 5); } } return(0); } //********************************************************************* // `Mist //********************************************************************* int cmdMist(Player* player, cmd* cmnd) { long i=0, t=0; t = time(0); i = player->getLTAttack() > LT(player, LT_SPELL) ? player->getLTAttack() : LT(player, LT_SPELL); if(LT(player, LT_MIST) > i) i = LT(player, LT_MIST); if(!player->ableToDoCommand()) return(0); if((!player->knowsSkill("mist") || !player->isEffected("vampirism")) && !player->isStaff()) { player->print("You are unable to turn to mist.\n"); return(0); } if(player->getRoomParent()->flagIsSet(R_ETHEREAL_PLANE)) { player->print("That is not possible here.\n"); return(0); } if(t < i) { player->pleaseWait(i-t); return(0); } if(!player->canMistNow()) { player->print("You may only turn to mist during the night.\n"); return(0); } if(player->flagIsSet(P_OUTLAW)) { player->print("You are unable to mist right now.\n"); return(0); } if(player->getRoomParent()->flagIsSet(R_DISPERSE_MIST)) { player->print("Swirling vapors prevent you from entering mist form.\n"); return(0); } if(player->getGroup()) { player->print("You can't do that while in a group.\n"); return(0); } if(player->isEffected("mist")) { player->print("You are already a mist.\n"); return(0); } if(player->inCombat()) { player->print("Not in the middle of combat.\n"); return(0); } broadcast(player->getSock(), player->getParent(), "%M turns to mist.", player); player->addEffect("mist", -1); player->print("You turn to mist.\n"); //player->checkImprove("mist", true); player->clearFlag(P_SITTING); player->interruptDelayedActions(); player->lasttime[LT_MIST].ltime = time(0); player->lasttime[LT_MIST].interval = 3L; return(0); } //********************************************************************* // canMistNow //********************************************************************* // whether or not the player can currently turn to mist bool Player::canMistNow() const { if(isStaff()) return(true); // only people who know how to do it can mist if((!knowsSkill("mist") || !isEffected("vampirism"))) return(false); // does the room let them mist anyway? // BUG: callin flagIsSet() with possibly invalid getRoom() if(getConstRoomParent() && (getConstRoomParent()->flagIsSet(R_VAMPIRE_COVEN) || getConstRoomParent()->flagIsSet(R_CAN_ALWAYS_MIST))) return(true); // otherwise, not during the day if(isDay()) return(false); return(true); } //********************************************************************* // cmdUnmist //********************************************************************* int cmdUnmist(Player* player, cmd* cmnd) { if(player->isEffected("mist")) { player->unmist(); } else { if(!player->isEffected("vampirism") && !player->isStaff()) return(0); player->print("You are not in mist form.\n"); } return(0); } //********************************************************************* // cmdHypnotize //********************************************************************* int cmdHypnotize(Player* player, cmd* cmnd) { Creature* target=0; int dur=0, chance=0; long i=0, t=0; player->clearFlag(P_AFK); if(!player->ableToDoCommand()) return(0); if(!player->knowsSkill("hypnotize")) { player->print("You lack the training to hypnotize anyone!\n"); return(0); } if(!player->isEffected("vampirism") && !player->isStaff()) { player->print("Only vampires may hypnotize people.\n"); return(0); } i = LT(player, LT_HYPNOTIZE); t = time(0); if(i > t && !player->isDm()) { player->pleaseWait(i-t); return(0); } dur = 300 + mrand(1, 30) * 10 + bonus((int) player->constitution.getCur()) * 30 + (int)(player->getSkillLevel("hypnotize") * 5); if(!(target = player->findVictim(cmnd, 1, true, false, "Hypnotize whom?\n", "You don't see that here.\n"))) return(0); if(!target->canSee(player) && !player->checkStaff("Your victim must see you to be hypnotized.\n")) return(0); if(!player->canAttack(target)) return(0); if(target->isMonster()) { if(target->getAsMonster()->isEnemy(player)) { player->print("Not while you are already fighting %s.\n", target->himHer()); return(0); } } if( target->isPlayer() && ( player->vampireCharmed(target->getAsPlayer()) || (target->hasCharm(player->getName()) && player->flagIsSet(P_CHARMED)) )) { player->print("But they are already your good friend!\n"); return(0); } if(target->isPlayer() && target->getClass() == RANGER && target->getLevel() > player->getLevel()) { player->print("%M is immune to your hypnotizing gaze.\n", target); return(0); } if(target->isMonster() && (target->flagIsSet(M_NO_CHARM) || (target->intelligence.getCur() < 3)) ) { player->print("Your hypnotism fails.\n"); return(0); } if( target->isUndead() && !player->checkStaff("You cannot hypnotize undead beings.\n") ) return(0); player->smashInvis(); player->lasttime[LT_HYPNOTIZE].ltime = t; player->lasttime[LT_HYPNOTIZE].interval = 600L; chance = MIN(90, 40 + (int)(player->getSkillLevel("hypnotize") - target->getLevel()) * 20 + 4 * bonus((int) player->intelligence.getCur())); if(target->flagIsSet(M_PERMENANT_MONSTER)) chance-=25; if(player->isDm()) chance = 101; if(mrand(1, 100) > chance && !player->isCt()) { player->print("You fail to hypnotize %N.\n", target); player->checkImprove("hypnotize", false); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M attempts to hypnotize %N.",player, target); if(target->isMonster()) { target->getAsMonster()->addEnemy(player); if(player->flagIsSet(P_LAG_PROTECTION_SET)) { // Activates lag protection. player->setFlag(P_LAG_PROTECTION_ACTIVE); } return(0); } target->printColor("^m%M tried to hypnotize you.\n", player); return(0); } if( (target->isPlayer() && target->isEffected("resist-magic")) || (target->isMonster() && target->isEffected("resist-magic")) ) dur /= 2; if(!player->isCt()) { if(target->chkSave(MEN, player,0)) { player->print("%M avoided your hypnotizing gaze.\n", target); player->checkImprove("hypnotize", false); target->print("You avoided %s's hypnotizing gaze.\n", player->getCName()); return(0); } } log_immort(false,player, "%s hypnotizes %s.\n", player->getCName(), target->getCName()); player->print("You hypnotize %N.\n", target); player->checkImprove("hypnotize", true); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M hypnotizes %N!", player, target); target->print("%M hypnotizes you.\n", player); player->addCharm(target); target->lasttime[LT_CHARMED].ltime = time(0); target->lasttime[LT_CHARMED].interval = dur; if(target->isPlayer()) target->setFlag(P_CHARMED); else target->setFlag(M_CHARMED); return(0); } //********************************************************************* // cmdRegenerate //********************************************************************* // This command is for liches. int cmdRegenerate(Player* player, cmd* cmnd) { int chance=0, xtra=0, pasthalf=0; long i=0, t=0; if(!player->ableToDoCommand()) return(0); player->clearFlag(P_AFK); if(!player->knowsSkill("regenerate")) { player->print("You lack the training to regenerate.\n"); return(0); } if(player->inCombat() && !player->checkStaff("You are too busy to draw from the life energy around you.\n")) return(0); if(!player->isCt()) { if(player->hp.getCur() > player->hp.getMax() * 3 / 4) { player->printColor("^cYou cannot regenerate when past 3/4 of your life force.\n"); return(0); } i = player->lasttime[LT_REGENERATE].ltime + player->lasttime[LT_REGENERATE].interval; t = time(0); if(i > t) { player->pleaseWait(i-t); return(0); } } if( player->getAlignment() > 100 && !player->checkStaff("There isn't enough evil in your soul to regenerate.\n") ) return(0); int level = (int)player->getSkillLevel("regenerate"); // get a base number: // 10 = -36.66 // 120 = 0 // 400 = 93.3 chance = (player->constitution.getCur() - 120) / 3; chance = MIN(85, level * 4 + chance); if(player->isCt()) chance = 101; if(mrand(1, 100) <= chance) { player->print("You feel the evil in your soul rebuilding.\n"); player->checkImprove("regenerate", true); broadcast(player->getSock(), player->getParent(), "%M regenerates.", player); for(Player* ply : player->getRoomParent()->players) { if(!ply->isUndead()) ply->print("You shiver from a sudden deathly coldness.\n"); } player->hp.increase(mrand(level+4,level*(5/2)+5)); // Prevents players from avoiding the 75% hp limit to allow regenerate // by casting on themselves to get below half. A lich may never regenerate // past 75% their max HP. This totally evens out their ticktime with mages if(player->hp.getCur() > player->hp.getMax() * 3 / 4) { player->hp.setCur((player->hp.getMax() * 3 / 4) + 1); pasthalf = 1; } // Extra regeneration from being extremely evil. if(player->getAlignment() <= -250 && !pasthalf) { if(player->getAlignment() <= -400) xtra = mrand(8,10); else xtra = mrand(4,6); player->print("Your life force regenerates %d more due to your extreme evil.\n",xtra); player->hp.increase(xtra); } player->lasttime[LT_REGENERATE].ltime = t; player->lasttime[LT_REGENERATE].interval = 120L; } else { player->print("You fail to regenerate.\n"); player->checkImprove("regenerate", false); broadcast(player->getSock(), player->getParent(), "%M tried to regenerate.", player); player->lasttime[LT_REGENERATE].ltime = t; player->lasttime[LT_REGENERATE].interval = 10L; } return(0); } //********************************************************************* // cmdDrainLife //********************************************************************* // This allows a lich to drain hp from someone else and add half the // damage to their own int cmdDrainLife(Player* player, cmd* cmnd) { Creature* target=0; Player *pTarget=0; long i=0, t=0; int chance=0; Damage damage; if(!player->ableToDoCommand()) return(0); if(!player->knowsSkill("drain")) { player->print("You haven't learned how to drain someone's life.\n"); return(0); } if(player->inCombat()) { player->print("Not while you're already in combat.\n"); return(0); } if(!(target = player->findVictim(cmnd, 1, true, false, "Drain whom?\n", "You don't see that here.\n"))) return(0); pTarget = target->getAsPlayer(); player->smashInvis(); player->interruptDelayedActions(); if(!player->canAttack(target)) return(0); if( target->isUndead() && !player->checkStaff("You may only drain the life of the living.\n") ) return(0); if(!pTarget) { // Activates lag protection. if(player->flagIsSet(P_LAG_PROTECTION_SET)) player->setFlag(P_LAG_PROTECTION_ACTIVE); if(target->isPet()) { if(!player->flagIsSet(P_CHAOTIC) && !player->isCt() && !target->getPlayerMaster()->flagIsSet(P_OUTLAW)) { player->print("Sorry, you're lawful.\n"); return(0); } if(!target->getPlayerMaster()->flagIsSet(P_CHAOTIC) && !player->isCt() && !target->getPlayerMaster()->flagIsSet(P_OUTLAW)) { player->print("Sorry, that creature is lawful.\n"); return(0); } if(player->getRoomParent()->isPkSafe() && !target->getPlayerMaster()->flagIsSet(P_OUTLAW)) { player->print("No killing allowed in this room.\n"); return(0); } } } i = LT(player, LT_DRAIN_LIFE); t = time(0); if(i > t && !player->isDm()) { player->pleaseWait(i-t); return(0); } if(target->isMonster()) target->getAsMonster()->addEnemy(player); player->lasttime[LT_DRAIN_LIFE].ltime = t; player->updateAttackTimer(true, DEFAULT_WEAPON_DELAY); player->lasttime[LT_DRAIN_LIFE].interval = 120L; int level = (int)player->getSkillLevel("drain"); chance = (level - target->getLevel()) * 20 + bonus((int) player->constitution.getCur()) * 5 + 25; chance = MIN(chance, 80); if(target->isEffected("drain-shield")) chance /= 2; if(mrand(1, 100) > chance) { player->print("You failed to drain %N's life.\n", target); player->checkImprove("drain", false); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M tried to drain %N's life.", player, target); target->print("%M tried drain your life!\n", player); return(0); } damage.set(mrand(level * 2, level * 4)); if(target->isEffected("drain-shield")) damage.set(mrand(1, level)); // Berserked barbarians have more energy to drain if(pTarget && pTarget->isEffected("berserk")) damage.set(damage.get() + (damage.get() / 5)); damage.set(MIN(damage.get(), target->hp.getCur() + 1)); if(damage.get() < 1) damage.set(1); if(!target->isCt()) { if(target->chkSave(DEA, player, 0)) { player->printColor("^yYour life-drain failed!\n"); target->print("%M failed to drain your life.\n", player); return(0); } } target->modifyDamage(player, NEGATIVE_ENERGY, damage); // drain heals us if(!target->isEffected("drain-shield")) player->hp.increase(damage.get() / 2); player->printColor("You drained %N for %s%d^x damage.\n", target, player->customColorize("*CC:DAMAGE*").c_str(), damage.get()); player->checkImprove("drain", true); player->statistics.attackDamage(damage.get(), "drain-life"); if(target->isEffected("drain-shield")) player->print("%M resisted your drain!\n", target); target->printColor("%M drained you for %s%d^x damage.\n", player, target->customColorize("*CC:DAMAGE*").c_str(), damage.get()); broadcast(player->getSock(), target->getSock(), player->getParent(), "%M drained %N's life.", player, target); if(player->doDamage(target, damage.get(), CHECK_DIE)) { if(player->getClass() == CARETAKER && pTarget) log_immort(true, player, "%s killed %s with the drain.\n", player->getCName(), target->getCName()); } return(0); }