/* * die.cpp * New death code * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * 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 * */ // Mud includes #include "mud.h" #include "die.h" #include "factions.h" #include "effects.h" #include "commands.h" #include "guilds.h" #include "bank.h" #include "unique.h" #include "web.h" #include <math.h> //******************************************************************** // isHardcore //******************************************************************** // Return: true if player is flagged as Hardcore (death = permanent) bool Player::isHardcore() const { return(flagIsSet(P_HARDCORE) && !isStaff()); } //******************************************************************** // hardcoreDeath //******************************************************************** bool canDrop(const Player* player, const Object* object, const Property* p); bool delete_drop_obj(const BaseRoom* room, const Object* object, bool factionCanRecycle); void hardcoreDeath(Player* player) { if(!player->isHardcore()) return; bool factionCanRecycle = !player->inUniqueRoom() || Faction::willDoBusinessWith(player, player->getUniqueRoomParent()->getFaction()); player->hooks.execute("preHardcoreDeath"); for(int i=0; i<MAXWEAR; i++) { if(player->ready[i] && (!(player->ready[i]->flagIsSet(O_CURSED) && player->ready[i]->getShotsCur() > 0))) { player->ready[i]->clearFlag(O_WORN); player->doRemove(i); } } player->computeAC(); player->computeAttackPower(); BaseRoom* room = player->getRoomParent(); Object* object=0; ObjectSet::iterator it; for( it = player->objects.begin() ; it != player->objects.end() ; ) { object = (*it++); if(delete_drop_obj(room, object, factionCanRecycle) || !canDrop(player, object, 0) || object->flagIsSet(O_STARTING)) { delete object; continue; } player->delObj(object, false, true, true, false); object->addToRoom(room); } player->checkDarkness(); if(player->coins[GOLD]) { object=0; loadObject(MONEY_OBJ, &object); object->nameCoin("gold", player->coins[GOLD]); object->setDroppedBy(player, "HardcoreDeath"); object->value.set(player->coins[GOLD], GOLD); player->coins.sub(player->coins[GOLD], GOLD); gServer->logGold(GOLD_OUT, player, object->value, NULL, "HardcoreDeath"); object->addToRoom(room); } player->printColor("\n\n ^YYou have died.\n\n"); player->printColor("^RAs a hardcore character, this death is permanent.\n"); player->print("\n\n"); player->statistics.display(player, true); player->print("\n"); broadcast("^#^R### %s's soul is lost forever.", player->getCName()); player->hooks.execute("postHardcoreDeath"); deletePlayer(player); } //******************************************************************** // isHoliday //******************************************************************** // if a string is returned, player will get bonus exp bstring isHoliday() { bstring str = gConfig->getMonthDay(); // see if today is a holiday if(str == "Oct 31") return("Happy Halloween!"); if(str == "Dec 24" || str == "Dec 25") return("Merry Christmas!"); if(str == "Dec 31" || str == "Jan 1") return("Happy New Year!"); return(""); } // TODO: Add in perm killing code //******************************************************************** // dropCorpse //******************************************************************** // Parameters: <killer> The creature attacking // Handles the dropping of items from creatures void Monster::dropCorpse(Creature *killer) { BaseRoom* room = getRoomParent(); bstring str = "", carry = ""; Object *object=0; Player* player=0; Player* pMaster = isPet() ? getPlayerMaster() : 0; bool destroy = room->isDropDestroy(); if(killer) player = killer->getPlayerMaster(); killDarkmetal(); killUniques(); // Drop weapons also if(ready[WIELD-1]) { Lore::remove(pMaster, ready[WIELD-1], true); if(destroy) { unequip(WIELD, UNEQUIP_DELETE); } else { if(player) player->printColor("%M dropped their weapon: %1P.\n", this, ready[WIELD - 1]); Object* drop = unequip(WIELD, UNEQUIP_NOTHING, false); if(drop) { if(drop->flagIsSet(O_JUST_LOADED)) drop->setDroppedBy(this, "MobDeath"); drop->addToRoom(room); } } } // list all on death // check for player: for mob killing pet, player is null, and then we don't // care about listing items dropped if(!destroy && player) str = this->listObjects(player, true); ObjectSet::iterator it; for( it = objects.begin() ; it != objects.end() ; ) { object = (*it++); if(object->flagIsSet(O_JUST_LOADED)) object->setDroppedBy(this, "MobDeath"); Lore::remove(pMaster, object, true); delObj(object, false, false, true, false); if(destroy) delete object; else if(!flagIsSet(M_TRADES) || !object->info.id) { object->clearFlag(O_BODYPART); object->addToRoom(room); } } checkDarkness(); if(!destroy) { if(!coins.isZero()) { loadObject(MONEY_OBJ, &object); object->value.set(coins); object->setDroppedBy(this, "MobDeath"); object->nameCoin("gold", object->value[GOLD]); object->addToRoom(room); if(player) { if(str != "") str += ", "; str += object->getName(); } } if(player && str != "") { carry = getCrtStr(player, CAP | INV, 0); carry += " was carrying: "; carry += str; carry += ".\n"; player->printColor("%s", carry.c_str()); if(!player->flagIsSet(P_DM_INVIS)) broadcastGroup(true, killer, "%s", carry.c_str()); } } } //******************************************************************** // die //******************************************************************** // wrapper for die void Creature::die(Creature *killer) { bool freeTarget=true; die(killer, freeTarget); } //******************************************************************** // die //******************************************************************** // Parameters: <killer> The creature attacking // Handles the death of people void Creature::die(Creature *killer, bool &freeTarget) { Player* pVictim = getAsPlayer(); Monster* mVictim = getAsMonster(); Player* pKiller = killer->getAsPlayer(); Monster* mKiller = killer->getAsMonster(); bool duel = induel(pVictim, pKiller); if(pKiller) { pKiller->statistics.kill(); if(mVictim) pKiller->statistics.monster(mVictim); if(pKiller->hasCharm(getName())) pKiller->delCharm(this); } if(pVictim) freeTarget = false; // Any time a level 7+ player dies, they are auto backed up. if(pVictim && pVictim->getLevel() >= 7 && !duel) pVictim->save(true, LS_BACKUP); Hooks::run(killer, "preKill", this, "preDeath", duel); if(mKiller && mKiller->isPet() && pVictim) { pVictim->dieToPet(mKiller); } else if(mKiller && mKiller->isPet() && mVictim) { mVictim->dieToPet(mKiller, freeTarget); } else if(mKiller && mVictim) { mVictim->dieToMonster(mKiller, freeTarget); } else if(mKiller && pVictim) { pVictim->dieToMonster(mKiller); } else if(pKiller && mVictim) { mVictim->dieToPlayer(pKiller, freeTarget); } else if(pKiller && pVictim) { pVictim->dieToPlayer(pKiller); } if(pVictim) { pVictim->clearFocus(); } // 10% chance to get porphyria/lycanthropy when killed by a vampire/werewolf if(pVictim && !duel) { pVictim->addPorphyria(killer, 10); pVictim->addLycanthropy(killer, 10); } } //******************************************************************** // dieToMonster //******************************************************************** // Parameters: <killer> The creature attacking // Handles monsters killing players void Player::dieToMonster(Monster *killer) { // Some sanity checks here -- Should never fail these ASSERTLOG( !killer->isPet() ); // no real penalties for staff dying if(isStaff()) { printColor("^r*** You just died ***\n"); broadcast(::isStaff, "^G### Sadly, %s just died.", getCName()); clearAsEnemy(); hp.restore(); mp.restore(); return; } print("%M killed you!\n", killer); if(flagIsSet(P_OUTLAW) && killer->getLevel() > 10) clearFlag(P_OUTLAW); setFlag(P_KILLED_BY_MOB); // Stop level 13+ players from suiciding right after dieing if(level >= 13) { lasttime[LT_MOBDEATH].ltime = time(0); lasttime[LT_MOBDEATH].interval = 60*60*24L; setFlag(P_NO_SUICIDE); } unsigned long oldxp = experience; unsigned short oldlvl = level; broadcast("### Sadly, %s was killed by %1N.", getCName(), killer); killer->clearEnemy(this); clearAsEnemy(); gServer->clearAsEnemy(this); loseExperience(killer); dropEquipment(false); logDeath(killer); // more info for staff broadcast(::isCt, "^rOld Exp: %u Lost Exp: %d, Old Lvl: %u, Lvl: %u, Room: %s", oldxp, (int)(oldxp - experience), oldlvl, level, getRoomParent()->fullName().c_str()); resetPlayer(killer); } //******************************************************************** // dieToPet //******************************************************************** // Parameters: <killer> The creature attacking // Handles pets killing players void Player::dieToPet(Monster *killer) { Player *master=0; if(killer->getMaster()) master = killer->getMaster()->getAsPlayer(); else { broadcast(::isCt, "^y*** Pet %s has no master and is trying to kill a player. Room %s", killer->getCName(), killer->currentLocation.room.str().c_str()); return; } bool dueling = induel(this, master); if(dueling) broadcast("^R### Sadly, %s was killed by %s's %s in a duel.", getCName(), master->getCName(), killer->getCName()); else { if(level > 2) broadcast("### Sadly, %s was killed by %s's %s.", getCName(), master->getCName(), killer->getCName()); else { broadcast(::isCt, "^y### Sadly, %s was killed by %s's %s.", getCName(), master->getCName(), killer->getCName()); print("### Sadly, %s was killed by %s's %s.", getCName(), master->getCName(), killer->getCName()); } } killer->clearEnemy(this); getPkilled(master, dueling); } //******************************************************************** // dieToPet //******************************************************************** // Parameters: <killer> The creature attacking // Handles pets killing monsters void Monster::dieToPet(Monster *killer, bool &freeTarget) { Creature* petKiller=0; Player* pKiller=0; logDeath(killer); if(killer->getMaster()) { petKiller = killer; pKiller = killer->getMaster()->getAsPlayer(); } else { broadcast(::isCt, "^y*** Pet %s has no master and is trying to kill a mob. Room %s", killer->getCName(), killer->currentLocation.room.str().c_str()); return; } broadcast(NULL, pKiller->getRoomParent(), "%M's %s killed %N.", pKiller, petKiller->getCName(), this); mobDeath(pKiller, freeTarget); } //******************************************************************** // dieToMonster //******************************************************************** // Parameters: <this> The creature being attacked // <killer> The creature attacking // Handles monsters killing monsters void Monster::dieToMonster(Monster *killer, bool &freeTarget) { if(this != killer) broadcast(NULL, killer->getRoomParent(), "%M killed %N.", killer, this); mobDeath(killer, freeTarget); } //******************************************************************** // dieToPlayer //******************************************************************** // Parameters: <killer> The creature attacking // Handles players killing monsters void Monster::dieToPlayer(Player *killer, bool &freeTarget) { logDeath(killer); if(getMaster() != killer) { killer->print("You killed %N.\n", this); broadcast(killer->getSock(), killer->getRoomParent(), "%M killed %N.", killer, this); } mobDeath(killer, freeTarget); } //******************************************************************** // mobDeath //******************************************************************** // Common routine for all mob's dying. void Monster::mobDeath(Creature *killer) { bool freeTarget=true; mobDeath(killer, freeTarget); } void Monster::mobDeath(Creature *killer, bool &freeTarget) { if(killer) killer->checkDoctorKill(this); distributeExperience(killer); dropCorpse(killer); cleanFollow(killer); clearAsEnemy(); // does the calling function want us to free the target? if(freeTarget) finishMobDeath(killer); // freeTarget now means: do I (calling function) need to free the target? freeTarget = !freeTarget; } //******************************************************************** // finishMobDeath //******************************************************************** void Monster::finishMobDeath(Creature *killer) { //if(killer) // a null killer is valid, so the called function should handle that properly Hooks::run(killer, "postKill", this, "postDeath", "0"); deleteFromRoom(); gServer->delActive(this); free_crt(this); } //******************************************************************** // dieToPlayer //******************************************************************** // Parameters: <killer> The creature attacking // Handles players killing players void Player::dieToPlayer(Player *killer) { char deathstring[80]; bool dueling = induel(this, killer); killer->print("You killed %N.\n", this); print("%M killed you.\n", killer); broadcast(killer->getSock(), getSock(), killer->getRoomParent(), "%M killed %N.", killer, this); if(killer->isEffected("lycanthropy") && killer->getLevel() >= 13) strcpy(deathstring, "eaten"); else if(killer->getClass() == ASSASSIN && killer->getLevel() >= 13) strcpy(deathstring, "assassinated"); else strcpy(deathstring, "killed"); if(dueling) broadcast("^R### Sadly, %s was %s by %s in a duel.", getCName(), deathstring, killer->getCName()); else// if(level > 2) broadcast("### Sadly, %s was %s by %1N.", getCName(), deathstring, killer); //else { // broadcast(::isWatcher, "^C### Sadly, %s was %s by %1N.", name, deathstring, killer); // print("### Sadly, %s was %s by %1N.\n", name, deathstring, killer); //} getPkilled(killer, dueling); } //******************************************************************** // getPkilled //******************************************************************** void Player::getPkilled(Player *killer, bool dueling, bool reset) { unsigned long oldxp = experience; unsigned short oldlvl = level; if(isGuildKill(killer)) guildKill(killer); if(isGodKill(killer)) godKill(killer); else if(isClanKill(killer)) clanKill(killer); // make the pets stop attacking the players clearAsPetEnemy(); killer->clearAsPetEnemy(); if(!dueling) { updatePkill(killer); dropBodyPart(killer); if(!killer->isStaff()) dropEquipment(true, killer->getSock()); logDeath(killer); // more info for staff broadcast(::isCt, "^rOld Exp: %u Lost Exp: %d, Old Lvl: %u, Lvl: %u, Room: %s", oldxp, (int)(oldxp - experience), oldlvl, level, getRoomParent()->fullName().c_str()); } if(reset) resetPlayer(killer); } //******************************************************************** // isGodKill //******************************************************************** bool Player::isGodKill(const Player *killer) const { if(induel(killer, this)) return(false); if(killer->getLevel() < 7 || level < 7) return(false); if(!killer->getDeity() || !deity) return(false); if(killer->isStaff()) return(false); return(true); } //******************************************************************** // isClanKill //******************************************************************** bool Player::isClanKill(const Player *killer) const { if(flagIsSet(P_OUTLAW_WILL_LOSE_XP)) return(true); if(induel(killer, this)) return(false); if(killer->getLevel() < 7 || level < 7) return(false); if(clan && killer->getClan()) return(true); // Dk vs Paly, Paly vs Dk, or Dk vs Dk if( (killer->getClass() == DEATHKNIGHT && cClass == PALADIN) || (killer->getClass() == DEATHKNIGHT && (cClass == PALADIN || cClass == DEATHKNIGHT)) ) return(true); if( ((killer->getClass() == CLERIC && killer->flagIsSet(P_CHAOTIC) && killer->getLevel() >= 7) && flagIsSet(P_PLEDGED)) || ((cClass == CLERIC && flagIsSet(P_CHAOTIC) && level >= 7) && killer->flagIsSet(P_PLEDGED)) ) return(true); return(false); } //******************************************************************** // guildKill //******************************************************************** int Player::guildKill(Player *killer) { int bns=0, penalty=0, levelDiff=0; Guild *killerGuild, *thisGuild; int same=0, base=0; long total=0; killerGuild = gConfig->getGuild(killer->getGuild()); thisGuild = gConfig->getGuild(guild); base = level * 50; levelDiff = level - killer->getLevel(); if(levelDiff < 0) bns += -5 * level * levelDiff; else bns += levelDiff * 50; if(guildRank == GUILD_MASTER) bns *= 3; else if(guildRank == GUILD_BANKER) bns *= 2; else if(guildRank == GUILD_OFFICER) bns = (bns*3)/2; total = MAX(1, mrand((base + bns)/2, base + bns)); if(killer->halftolevel()) total = 0; if(killer->getSecondClass()) total = total * 3 / 4; penalty = MIN(mrand(1000,1500), (bns*3)/2); if(killer->getLevel() > level + 6) penalty = 100; if(killerGuild == thisGuild) { total = -1000; penalty = 1000; same = 1; } else { killerGuild->incPkillsIn(); killerGuild->incPkillsWon(); thisGuild->incPkillsIn(); killerGuild->bank.add(coins); Bank::guildLog(killer->getGuild(), "Guild PKILL: %s by %s [Balance: %s]\n", coins.str().c_str(), killer->getCName(), killerGuild->bank.str().c_str()); coins.zero(); gConfig->saveGuilds(); } if(!same) { killer->printColor("^gYou have defeated a member of a rival guild!\n"); killer->print("Your guild honors you with %d experience.\n", abs(total)); if(coins[GOLD]) killer->printColor("You grab %N's coins and put them in your guild bank.^x\n", this); printColor("^gYou have been defeated by a member of a rival guild!\n"); print("Your guild shames you by taking %d experience.\n", abs(penalty)); if(coins[GOLD]) printColor("%M grabs all of your coins!\n", killer); } else { killer->printColor("^gYou have shamelessly defeated a member of your own guild!\n"); killer->printColor("Your guild penalizes you for %d experience.\n", abs(total)); printColor("^gYou have been defeated during senseless guild infighting!\n"); printColor("Your guild penalizes you for %d experience.\n", abs(penalty)); } killer->addExperience(total); subExperience(penalty); return(1); } //******************************************************************** // godKill //******************************************************************** int Player::godKill(Player *killer) { int bns=0, penalty=0, levelDiff=0; int same=0, base=0; Guild *killerGuild, *thisGuild; long total=0; killerGuild = gConfig->getGuild(killer->getGuild()); thisGuild = gConfig->getGuild(guild); base = level * 50; levelDiff = level - killer->getLevel(); if(levelDiff < 0) bns += 5 * level * levelDiff; else bns += levelDiff * 50; total = MAX(1, mrand((base + bns)/2, base + bns)); if(killer->halftolevel()) total = 0; if(killer->getSecondClass()) total = total * 3 / 4; penalty = MIN(mrand(1000,1500), (bns*3)/2); if(killer->getLevel() > level + 6) penalty = 100; if(killer->getDeity() == deity) { total = -50; penalty = 50; same = 1; } if(killerGuild != 0 && thisGuild != 0) { if(killerGuild != thisGuild) { killerGuild->incPkillsIn(); killerGuild->incPkillsWon(); thisGuild->incPkillsIn(); } } if(!same) { killer->printColor("^cYou have defeated a member of an enemy religion!\n"); killer->printColor("Your order honors you with %d experience.\n", abs(total)); printColor("^cYou have been defeated by a member of a rival religion!\n"); printColor("Your order shames you by taking %d experience.\n", abs(penalty)); } else { killer->printColor("^cYou have shamelessly defeated a member of your own religion!\n"); killer->printColor("Your order penalizes you for %d experience.\n", abs(total)); printColor("^cYou have been defeated during senseless religious infighting!\n"); printColor("Your order penalizes you for %d experience.\n", abs(penalty)); } killer->addExperience(total); subExperience(penalty); return(1); } //******************************************************************** // clanKill //******************************************************************** int Player::clanKill(Player *killer) { bool penalty = false; // Do we give a penalty for killing this person? int expGain = 0, expLoss = 0; // Paly's shouldn't be killing each other if(cClass == PALADIN && killer->getClass() == PALADIN && deity == killer->getDeity()) penalty = true; // Clan members should not be killing members of their own clan if(clan == killer->getClan()) penalty = true; // Never a penalty for killing an outlaw! if(flagIsSet(P_OUTLAW_WILL_LOSE_XP)) penalty = false; // No penalty if(!penalty) { expGain = (level * killer->getLevel()) * 10; expLoss = expGain * 2; if(killer->halftolevel()) expGain = 0; if(killer->getSecondClass()) expGain = expGain * 3 / 4; print("You have been bested!\nYou lose %d experience.\n", expLoss); subExperience(expLoss); if(!killer->flagIsSet(P_OUTLAW)) { killer->print("You have vanquished %s.\n", getCName()); killer->print("You %s %d experience for your heroic deed.\n", gConfig->isAprilFools() ? "lose" : "gain", expGain); killer->addExperience(expGain); } } return(1); } //******************************************************************** // checkDoctorKill //******************************************************************** // Parameters: <player> // <killer> The creature attacking // Checks for doctor killers void Creature::checkDoctorKill(Creature *victim) { if(victim->getName() == "doctor") { if( (isPlayer() && !isStaff()) || (isMonster() && isPet() && !getMaster()->isStaff())) { Creature* target = isPlayer() ? this : getMaster(); target->setFlag(P_DOCTOR_KILLER); if(!target->flagIsSet(P_DOCTOR_KILLER)) broadcast("### %s is a doctor this!", target->getCName()); target->lasttime[LT_KILL_DOCTOR].ltime = time(0); target->lasttime[LT_KILL_DOCTOR].interval = 72000L; } } } //******************************************************************** // checkLevel //******************************************************************** // Parameters: <player> // Checks if a player has deleveled or releveled //void doTrain(Player* player) { // // //} int Player::checkLevel() { // Staff doesn't delevel or relevel if(isCt()) return(0); int n = exp_to_lev(experience); // De-Level! if(level > n) { print("You have deleveled to level %s!\n", int_to_text(n)); while(level > n) downLevel(); negativeLevels = 0; return(-1); // Delevel } // Re-Level! if(level < n && level < actual_level && n <= actual_level && !negativeLevels) { if(inUniqueRoom() && getUniqueRoomParent()->getHighLevel() && level == getUniqueRoomParent()->getHighLevel()) { print("You have enough experience to relevel, but cannot do so in this room.\n"); return(0); } print("You have releveled to level %s!\n", int_to_text(n)); logn("log.relevel", "%s just releveled to level %d from level %d in room %s.\n", getCName(), n, level, getRoomParent()->fullName().c_str()); if(!isStaff()) broadcast("### %s just releveled to %s!", getCName(), int_to_text(n)); while(level < n) upLevel(); return(1); // Relevel } // OCEANCREST: Dom: HC //if(isHardcore() && level < n && !negativeLevels) { // doTrain(this); // return(2); // Level //} return(0); // No change } //******************************************************************** // updatePkill //******************************************************************** void Player::updatePkill(Player *killer) { // No pkill changes if either party is staff if(isStaff() || killer->isStaff()) return; statistics.losePk(); killer->statistics.winPk(); } //******************************************************************** // dropEquipment //******************************************************************** // Parameters: <killer> The creature attacking // Function to make players drop the equipment they are currently wearing // via a pkill void Player::dropEquipment(bool dropAll, Socket* killerSock) { bstring dropString = ""; int i=0; // dropping weapons is handled separately from equipment, but we still need their // names for dropping later Object* main = ready[WIELD-1]; Object* held = ready[HELD-1]; dropWeapons(); // be nice to lowbies if(dropAll && level > 3) { // don't make them lose all their inventory in a drop-destroy room if(!getRoomParent()->isDropDestroy()) { // we reassign these for the purposes of having them printed in the list; // they will be reset afterwards Object* rMain = ready[WIELD-1]; Object* rHeld = ready[HELD-1]; ready[WIELD-1] = main; ready[HELD-1] = held; for(i=0; i<MAXWEAR; i++) { if( !ready[i] || ready[i]->flagIsSet(O_CURSED) || ready[i]->flagIsSet(O_NO_DROP) || ready[i]->flagIsSet(O_STARTING) ) continue; // 20% chance each item will be dropped (ignore this chance for wielded items // as we want them added to the list of stuff dropped) if((i != WIELD-1 && i != HELD-1) && mrand(1,5) > 1) continue; if(dropString != "") dropString += ", "; dropString += ready[i]->getName(); if(i != WIELD-1 && i != HELD-1) { // I is wearloc-1, so add one to it Object* temp = unequip(i+1, UNEQUIP_NOTHING, false); if(temp) finishDropObject(temp, getRoomParent(), this); } } // reset these wear locations ready[WIELD-1] = rMain; ready[HELD-1] = rHeld; if(dropString != "") { if(killerSock) killerSock->printColor("%s dropped: %s.\n", getCName(), dropString.c_str()); print("You dropped your inventory where you died!\n"); } } } checkDarkness(); computeAC(); computeAttackPower(); } //******************************************************************** // dropBodyPart //******************************************************************** // Parameters: <killer> The creature attacking // Makes a player drop a body part void Player::dropBodyPart(Player *killer) { ASSERTLOG( killer->isPlayer()); if(getRoomParent()->isDropDestroy()) return; bool nopart = false; int dueling = 0, num = 0; Object *body_part; char part[12], partName[25]; dueling = induel(this,killer); if(mrand (1,100) > 5 && !killer->isDm() && !flagIsSet(P_OUTLAW)) nopart = true; if(level <= 4 && !killer->isDm() && !flagIsSet(P_OUTLAW)) nopart = true; if( isStaff() || ( !killer->isPet() && !killer->isDm() && (killer->getLevel() > level + 5) && !flagIsSet(P_OUTLAW)) || ( killer->isPet() && !killer->getMaster()->isDm() && (killer->getMaster()->getLevel() > level + 5) && !flagIsSet(P_OUTLAW) ) ) { nopart = true; } if(!nopart && !dueling) { if(loadObject(BODYPART_OBJ, &body_part)) { num = mrand(1,14); switch(num) { case 1: strcpy(part, "skull"); break; case 2: strcpy(part, "head"); break; case 3: strcpy(part, "ear"); break; case 4: strcpy(part, "scalp"); break; case 5: strcpy(part, "hand"); break; case 6: strcpy(part, "spleen"); break; case 7: strcpy(part, "nose"); break; case 8: strcpy(part, "arm"); break; case 9: strcpy(part, "foot"); break; case 10: strcpy(part, "spine"); break; case 11: strcpy(part, "heart"); break; case 12: strcpy(part, "brain"); break; case 13: strcpy(part, "eyeball"); break; case 14: if(isEffected("vampirism")) strcpy(part, "fangs"); else strcpy(part, "teeth"); break; default: strcpy(part, "skull"); break; } body_part->setName(bstring(getName() + "'s " + part).toLower()); lowercize(partName, 0); strncpy(body_part->key[0], partName, 20); strncpy(body_part->key[1], part, 20); strncpy(body_part->key[2], part, 20); body_part->setAdjustment(mrand(1,2)); if(mrand(1,100) == 1) body_part->setAdjustment(3); body_part->addToRoom(getRoomParent()); } } } //******************************************************************** // logDeath //******************************************************************** void Player::logDeath(Creature *killer) { char file[16], killerName[80]; Player* pKiller = killer->getAsPlayer(); if(!killer->isStaff()) statistics.die(); strcpy(killerName, ""); if(killer->isPet()) sprintf(killerName, "%s's %s.", killer->getMaster()->getCName(), killer->getCName()); else strcpy(killerName, killer->getCName()); if(pKiller) strcpy(file, "log.pkill"); else strcpy(file, "log.death"); if(!(!pKiller && killer->flagIsSet(M_NO_EXP_LOSS))) { logn(file, "%s(%d) was killed by %s(%d) in room %s.\n", getCName(), level, killer->getCName(), killer->getLevel(), killer->getRoomParent()->fullName().c_str()); } if(pKiller && pKiller->isStaff()) { log_immort(false, pKiller, "%s(%d) was killed by %s(%d) in room %s.\n", getCName(), level, pKiller->getCName(), pKiller->getLevel(), pKiller->getRoomParent()->fullName().c_str()); } updateRecentActivity(); } //******************************************************************** // resetPlayer //******************************************************************** // Parameters: <killer> The creature attacking // Reset a player after death -- Move them to limbo, broadcast // they were killed, clear deterimental effects, etc void Player::resetPlayer(Creature *killer) { int duel=0; bool same=false; Player* pKiller = killer->getAsPlayer(); BaseRoom *newRoom = getLimboRoom().loadRoom(this); duel = induel(this, pKiller); if( inJail() || duel || !newRoom ) same = true; if(cClass == BUILDER) { same = true; print("*** You died ***\n"); broadcast(::isStaff, "^G### Sadly, %s was killed by %s.", getCName(), killer->getCName()); } // a hardcore player about to die doesnt need to worry about room movement if(isHardcore() && !killer->isStaff()) same = true; if(!same) { Hooks::run(killer, "postKillPreLimbo", this, "postDeathPreLimbo"); deleteFromRoom(); addToRoom(newRoom); doPetFollow(); } //doClearPetEnemy(this, killer->getCName()); curePoison(); cureDisease(); tickDmg = 0; removeCurse(); removeEffect("hold-person"); removeEffect("petrification"); removeEffect("confusion"); removeEffect("drunkenness"); unhide(); if(killer->isPlayer() || duel) { hp.setCur( MAX(1, MAX(hp.getMax()/2, hp.getCur()))); mp.setCur(MAX(mp.getCur(),(mp.getMax())/10)); } else { hp.restore(); mp.restore(); } if(duel) { setFlag(P_DIED_IN_DUEL); knockUnconscious(10); broadcast(getSock(), getRoomParent(), "%s is knocked unconscious!", getCName()); updateAttackTimer(true, 300); pKiller->delDueling(getName()); delDueling(killer->getName()); } else if(isHardcore() && !killer->isStaff()) { hardcoreDeath(this); // the player is invalid after this return; } else { courageous(); } killer->hooks.execute("postKill", this, duel); hooks.execute("postDeath", killer, duel, same); } //void clearMobEnemies(Creature *monster) { // etag *ep=0; // // if(monster->isPlayer()) // return; // // ep = monster->first_enm; // while(ep) { // del_enm_crt(ep->enemy, monster); // ep = ep->next_tag; // } //} //******************************************************************** // hearMobDeath //******************************************************************** bool hearMobDeath(Socket* sock) { if(!sock->getPlayer() || !isCt(sock)) return(false); return(!sock->getPlayer()->flagIsSet(P_NO_DEATH_MSG)); } //******************************************************************** // logDeath //******************************************************************** void Monster::logDeath(Creature *killer) { int logType=0; Creature *leader=0, *pet=0; BaseRoom* room = killer->getRoomParent(); char file[80], killerString[1024]; char logStr[2096]; bool solo = true; strcpy(file, ""); strcpy(killerString, ""); if(flagIsSet(M_WILL_BE_LOGGED)) { strcpy(file, "log.mdeath"); logType = 1; } else if(flagIsSet(M_PERMENANT_MONSTER) && (killer->isPlayer() || killer->isPet())) { strcpy(file, "log.perm"); logType = 2; } else if(killer->pFlagIsSet(P_BUGGED) ) { sprintf(file, "%s/%s", Path::BugLog, killer->getCName()); logType = 3; } else if(killer->pFlagIsSet(P_KILLS_LOGGED) ) { sprintf(file, "%s/%s.kills", Path::BugLog, killer->getCName()); logType = 4; } else return; //Mob's death not logged // There are 4 cases for perm broadcast, otherwise we do single broadcast // (which includes the pet message). // group broadcast (aka solo = 1) // killer is pet // pet's leader has a player follower // pet's leader is following // killer is a player // killer has a player follower // killer is following if(killer->isPlayer() || killer->isPet()) { // we have the leader of the pet if(killer->isPet()) leader = killer->getMaster(); else leader = killer; // see if they are in a group if(leader->getGroup()) solo = false; } if(killer->isPet() || leader->hasPet()) { if(leader->pets.size() == 1) { pet = leader->pets.front(); sprintf(killerString, "%s and %s %s", leader->getCName(), leader->hisHer(), pet->getCName()); } else { sprintf(killerString, "%s and %s pets", leader->getCName(), leader->hisHer()); } } else { sprintf(killerString, "%s", killer->getCName()); } switch(logType) { case 1: // unsaved mob sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s for %lu experience.", getCName(), level, killerString, killer->getLevel(), room->fullName().c_str(), experience); broadcast(hearMobDeath, "^g*** %s(L%d) was killed by %s(L%d) in room %s for %lu experience.", getCName(), level, killerString, killer->getLevel(), room->fullName().c_str(), experience); break; case 2: // Mob is a perm { Group* group = leader->getGroup(); // if the killer was a pet, leader is pointing to the pet's leader // make it point to the leader of the pet's leader if(group) leader = group->getLeader(); if(!solo) sprintf(logStr, "%s(L%d) was killed by %s(L%d) in %s(L%d)'s group in room %s.", getCName(), level, killerString, killer->getLevel(), leader->getCName(), leader->getLevel(), room->fullName().c_str()); else sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s [SOLO].", getCName(), level, killerString, killer->getLevel(), room->fullName().c_str()); if(!killer->isStaff() && flagIsSet(M_NO_PREFIX)) { if(!solo) broadcast(wantsPermDeaths, "^m### Sadly, %s was killed by %s and %s followers.", getCName(), leader->getCName(), leader->hisHer()); else broadcast(wantsPermDeaths, "^m### Sadly, %s was killed by %s.", getCName(), killerString); } } break; case 3: // bugged player case 4: // all player's kills logged sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s for %lu experience%s.", getCName(), level, killerString, killer->getLevel(), room->fullName().c_str(), experience, solo == 1 ? "[SOLO]" : ""); break; } logn(file, "%s\n", logStr); // I don't want to see bugged kill logs...too much spam if((killer->isPlayer() || killer->isPet()) && !killer->isCt() && logType != 4) broadcast(hearMobDeath, "^y*** %s", logStr); } //******************************************************************** // loseExperience //******************************************************************** // Parameters: <killer> The creature attacking // Handles experience/realms loss & death effects void Player::loseExperience(Monster *killer) { float xploss=0.0; long n=0; int count=0; // No exp loss from possesed monsters if(killer->flagIsSet(M_DM_FOLLOW)) return; if(killer->flagIsSet(M_NO_EXP_LOSS) || (killer->isPet() && killer->getMaster()->isStaff())) return; if(level >= 7) addEffect("death-sickness"); if(level < 10) { // Under level 10, 10% exp loss xploss = ((float)experience / 10.0); statistics.experienceLost((long)xploss); experience -= (long)xploss; } else { // Level 10 and over, 2% exp loss with a minimum of 10k xploss = MAX((long)( (float)experience * 0.02), 10000); print("You have lost %ld experience.\n", (long)xploss); statistics.experienceLost((long)xploss); experience -= (long)xploss; } n = level - exp_to_lev(experience); if(n > 1) { if(level < (MAXALVL+2)) experience = needed_exp[level-3]; else experience = (long)((needed_exp[MAXALVL-1]*(level-2))); } checkLevel(); // n = exp_to_lev(experience); // while(level > n) // down_level(this); for(count=0; count < 6; count++) { // Random loss to saving throws due to death. if(mrand(1,100) <= 25) { saves[count].chance -= 1; saves[count].gained -= 1; saves[count].chance = MAX(1, saves[count].chance); saves[count].gained = MAX(1, saves[count].gained); } } } //******************************************************************** // giveExperience //******************************************************************** // Parameters: <killer> The creature attacking // Handles experience gain on monster death void Monster::distributeExperience(Creature *killer) { long expGain = 0; Player* player=0; if(isPet()) return; if(flagIsSet(M_PERMENANT_MONSTER)) diePermCrt(); if(killer) { Group* group = NULL; if(killer->isPet()) player = killer->getMaster()->getAsPlayer(); else player = killer->getAsPlayer(); if(player) { group = player->getGroup(); } std::map<Player*, int> expList; // See if the group has experience split turned on if(group && group->flagIsSet(GROUP_SPLIT_EXPERIENCE) && group->getNumPlyInSameRoom(player) >= 1) { // Split exp evenly amongst the group int numGroupMembers=0, totalGroupLevel=0, totalGroupDamage=0; long n = 0; // Calculate how many people are in the group, see how much damage they have done // and remove them from the enemy list Player* groupMember; for(Creature* crt : group->members ) { if(isEnemy(crt) && inSameRoom(crt)) { if(crt->getsGroupExperience(this)) { // Group member groupMember = crt->getAsPlayer(); numGroupMembers++; totalGroupLevel += groupMember->getLevel(); n = clearEnemy(groupMember); totalGroupDamage += n; expList[groupMember] += n; } else if(crt->isPet() && (crt->getMaster()->getsGroupExperience(this) || expList.find(crt->getPlayerMaster()) != expList.end() )) { // If the master gets group experience, or was calculated earlier to get group experience (They're in expList already) // then count the pet as the same n = clearEnemy(crt); totalGroupDamage += n; expList[crt->getPlayerMaster()] += n; } } } float xpPercent = (float)MIN(totalGroupDamage, hp.getMax())/(float)hp.getMax(); long adjustedExp = (long)((xpPercent*experience) * (1.0 + (.25*numGroupMembers))); // Exp is split amoungst the group based on their level, // since we split it evenly, in this case pets do NOT give their master // any extra experience. int averageEffort = totalGroupDamage / tMAX<int>(expList.size(), 1); std::cout << "GROUP EXP: TGD:" << totalGroupDamage << " Num:" << expList.size() << " AVG EFF:" << averageEffort << std::endl; for(std::pair<Player*, int> p : expList) { Player* ply = p.first; int effort = p.second; if(ply) { expGain = (long)(((float)ply->getLevel()/(float)totalGroupLevel) * adjustedExp); // ply->printColor("You contributed ^m%d^x effort. Average effort was ^m%d^x.\n", effort, averageEffort); // Adjust based on reduced effort if(effort < (averageEffort/2)) { ply->printColor("You receive reduced experience because you contributed less than half of the average effort.\n"); expGain *= (((float)effort)/totalGroupDamage); } expGain = MAX(1, expGain); ply->gainExperience(this, killer, expGain, true); } } } } // Now handle everyone else on the list std::map<Player*, int> expList; ThreatSet::iterator tIt = threatTable->threatSet.begin(); ThreatEntry* threat = 0; Creature* crt = 0; while(tIt != threatTable->threatSet.end()) { // Iterate it because we will be invaliding this iterator threat = (*tIt++); crt = gServer->lookupCrtId(threat->getUid()); if(!crt) continue; if(crt->isPet()) expList[crt->getPlayerMaster()] += clearEnemy(crt); else if(crt->isPlayer()) expList[crt->getAsPlayer()] += clearEnemy(crt); } for(std::pair<Player*, int> p : expList) { Player* ply = p.first; if(!ply) { std::cout << "Distribute Experience: NULL Player found" << std::endl; continue; } int effort = p.second; expGain = (experience * effort) / MAX(hp.getMax(), 1); expGain = MIN(MAX(0,expGain), experience); ply->gainExperience(this, killer, expGain); // TODO: Why is this here? // Adjust monk/wolf ac and thac0 for extreme aligment // ************************************************** ply->alignAdjustAcThaco(); // ************************************************** } } //******************************************************************** // adjustExperience //******************************************************************** void Creature::adjustExperience(Monster* victim, int& expAmount, int& holidayExp) { Player* player; if(isPet()) player = getMaster()->getAsPlayer(); else player = getAsPlayer(); if(player->halftolevel()) { expAmount = 0; holidayExp = 0; return; } if(player->getRace() == HUMAN && expAmount) expAmount += MAX(mrand(4,6),expAmount/3/10); if(player->getSecondClass()) { // Penalty is 12.5% at level 30 and above if(player->level >= 30) expAmount = (expAmount*7)/8; else // and 25% below 30 expAmount = (expAmount*3)/4; } // // All experience is multiplied by 3/4 for a multi-classed player // if(player->getSecondClass()) // expAmount = (expAmount*3)/4; int levelDiff = abs((int)player->getLevel() - (int)victim->getLevel()); float multiplier=1.0; if(levelDiff <= 5) multiplier = 1.0; // 6 - 8 levels = 90% else if(levelDiff >= 6 && levelDiff <= 8) { multiplier = 0.9; } // 8 - 10 = 70% else if(levelDiff >= 8 && levelDiff <= 10 ) { multiplier = 0.70; } // 10 - 15 = 50% else if(levelDiff >= 10 && levelDiff <= 15) { multiplier = 0.50; } // 15 - 25 = 25% else if(levelDiff >= 15 && levelDiff <= 25) { multiplier = 0.25; } // 26+ = 10% else multiplier = 0.10; if(multiplier < 1.0) { // player->printColor("^YExp Adjustment: %d%% (%d level difference) %d -> %d\n", (int)(multiplier*100), levelDiff, expAmount, (int)(expAmount*multiplier)); expAmount = (int)(expAmount * multiplier); expAmount = MAX(1, expAmount); } // Add in holiday experience bstring holidayStr = isHoliday(); if(holidayStr != "") holidayExp = MAX(1, (int)(expAmount * 0.1)); if(victim->flagIsSet(M_PERMENANT_MONSTER)) holidayExp = 0; } //******************************************************************** // gainExperience //******************************************************************** void Player::gainExperience(Monster* victim, Creature* killer, int expAmount, bool groupExp) { int holidayExp = 0; adjustExperience(victim, expAmount, holidayExp); bool notlocal = false, af = gConfig->isAprilFools(); if(!inSameRoom(victim)) notlocal = true; bstring holidayStr = isHoliday(); if( groupExp || this == killer || !killer || !(notlocal && !(killer->isPlayer() && killer->flagIsSet(P_DM_INVIS)) && !(killer->isPet() && killer->getMaster()->flagIsSet(P_DM_INVIS)))) { printColor("You %s ^y%d^x %sexperience for the death of %N.\n", af ? "lose" : "gain", expAmount, groupExp ? "group " : "", victim); } else { if(killer->isPet()) print("%M's %s %s you %d experience for the death of %N.\n", killer->getMaster(), killer->getCName(), af ? "cost" : "gained", expAmount, victim); else print("%M %s you %d experience for the death of %N.\n", killer, af ? "cost" : "gained", expAmount, victim); } if(holidayExp > 0) printColor("%s You %s an extra ^y%d^x experience!\n", holidayStr.c_str(), af ? "lost" : "gained", holidayExp); adjustFactionStanding(victim->factions); adjustAlignment(victim); updateMobKills(victim); statistics.experience(expAmount, victim->getName()); addExperience(expAmount + holidayExp); if(victim->flagIsSet(M_WILL_BE_LOGGED)) logn("log.mdeath", "%s was killed by %s, for %d experience.\n", victim->getCName(), getCName(), expAmount); for(Monster* pet : pets) { pet->clearEnemy(victim); } } //******************************************************************** // gainExperience //******************************************************************** void Monster::gainExperience(Monster* victim, Creature* killer, int expAmount, bool groupExp) { Creature* master = getMaster(); if(!master || !isPet()) return; bool af = gConfig->isAprilFools(); int holidayExp = 0; adjustExperience(victim, expAmount, holidayExp); master->printColor("Your %s %s you ^y%d^x experience for the death of %N.\n", this->getCName(), af ? "cost" : "earned", expAmount, victim); bstring holidayStr = isHoliday(); if(holidayExp > 0) master->printColor("%s You %s an extra ^y%d^x experience!\n", holidayStr.c_str(), af ? "lost" : "gained", holidayExp); master->addExperience(expAmount + holidayExp); clearEnemy(victim); } //******************************************************************** // checkDie //******************************************************************** // wrapper for checkDie // Return: true if they died, false if they didn't bool Creature::checkDie(Creature *killer) { bool freeTarget=true; return(checkDie(killer, freeTarget)); } //******************************************************************** // checkDie //******************************************************************** // Parameters: <killer> The creature attacking // If the victim has 0 hp or less, kill them off // Return: true if they died, false if they didn't bool Creature::checkDie(Creature *killer, bool &freeTarget) { if(hp.getCur() < 1) { die(killer, freeTarget); return(true); } return(false); } //******************************************************************** // checkDieRobJail //******************************************************************** // wrapper for checkDieRobJail int Creature::checkDieRobJail(Monster *killer) { bool freeTarget=true; return(checkDieRobJail(killer, freeTarget)); } //******************************************************************** // checkDieRobJail //******************************************************************** // Parameters: <killer> The creature attacking // Similar to checkDie, but this function will see if the attacker // is a mugger, or a jailer and handle appropriately. // Return Value: 1 <Death> // 2 <Unconscious/Jail/Mug> // 0 <Nothing> int Creature::checkDieRobJail(Monster *killer, bool &freeTarget) { BaseRoom* room = getRoomParent(); Player *pVictim = getAsPlayer(); ASSERTLOG(killer->isMonster()); // Less than 1 hp & not police/greedy monster or it's monster, then die if(hp.getCur() < 1 && ((!killer->flagIsSet(M_POLICE) && ! killer->flagIsSet(M_GREEDY)) || isMonster() ) ) { die(killer, freeTarget); return(1); } // Police/Greedy & less than 1/10th hp & pVictim else if((killer->flagIsSet(M_GREEDY) || killer->flagIsSet(M_POLICE)) && pVictim && pVictim->hp.getCur() < pVictim->hp.getMax()/10) { freeTarget = false; pVictim->printColor("^r%M knocks you unconscious.\n",killer); pVictim->knockUnconscious(39); broadcast(pVictim->getSock(), room, "%M knocked %N unconscious.", killer, pVictim); pVictim->hp.setCur( pVictim->hp.getMax()/20); pVictim->clearAsEnemy(); killer->clearEnemy(pVictim); if(killer->flagIsSet(M_POLICE)) { broadcast(pVictim->getSock(), room, "%M picks %N up and hauls %s off.",killer, pVictim, pVictim->himHer()); if(!killer->jail.id && !pVictim->isStaff()) broadcast("### Unfortunately, %N was hauled off to jail by %N.",pVictim, killer); killer->toJail(pVictim); return(2); } if(killer->flagIsSet(M_GREEDY)) { broadcast(pVictim->getSock(), room, "%M rummages through %N's inventory.", killer, pVictim); broadcast("### Unfortunately, %N was mugged by %N.", pVictim, killer); killer->grabCoins(pVictim); return(2); } return(2); } freeTarget = false; return(0); } //******************************************************************** // die //******************************************************************** // Parameters: <killer> The creature attacking // This function is called when a player dies to something other then a // player or a monster. -- TC void Player::die(DeathType dt) { bstring death = ""; Player* killer=0; Object* statue=0; char deathStr[2048]; unsigned long oldxp=0; int n=0; unsigned short oldlvl=0; bool killedByPlayer = dt == POISON_PLAYER || dt == CREEPING_DOOM; deathtype = DT_NONE; statistics.die(); if(isStaff()) { printColor("^r*** You just died ***\n"); broadcast(::isStaff, "^G### Sadly, %s just died.", getCName()); clearAsEnemy(); hp.restore(); mp.restore(); return; } // Any time a level 7+ this dies, they are backed up. if(level >= 7) save(true, LS_BACKUP); if(!killedByPlayer) { setFlag(P_KILLED_BY_MOB); if(level >= 13) { lasttime[LT_MOBDEATH].ltime = time(0); lasttime[LT_MOBDEATH].interval = 60*60*24L; setFlag(P_NO_SUICIDE); } } switch(dt) { case POISON_PLAYER: case CREEPING_DOOM: // these are player-caused death types switch(dt) { case POISON_PLAYER: sprintf(deathStr, "### Sadly, %s was poisoned to death.", getCName()); logn("log.death", "%s was poisoned to death by %s.\n", getCName(), poisonedBy.c_str()); break; case CREEPING_DOOM: removeEffect("creeping-doom", false); sprintf(deathStr, "### Sadly, %s was killed by cursed spiders.", getCName()); logn("log.death", "%s was poisoned to death by %s.\n", getCName(), poisonedBy.c_str()); break; default: break; } if(poisonedBy != "") { switch(dt) { case POISON_PLAYER: sprintf(deathStr, "### Sadly, %s was poisoned to death by %s.", getCName(), poisonedBy.c_str()); break; case CREEPING_DOOM: sprintf(deathStr, "### Sadly, %s was killed by %s's cursed spiders.", getCName(), poisonedBy.c_str()); break; default: break; } killer = gServer->findPlayer(poisonedBy.c_str()); if(killer) getPkilled(killer, false, false); } break; case POISON_MONSTER: sprintf(deathStr, "### Sadly, %s was poisoned to death by %s.", getCName(), poisonedBy.c_str()); logn("log.death", "%s was poisoned to death by %s.\n", getCName(), poisonedBy.c_str()); death = "poison"; break; case POISON_GENERAL: sprintf(deathStr, "### Sadly, %s was poisoned to death.", getCName()); logn("log.death", "%s was poisoned to death.\n", getCName()); death = "poison"; break; case FALL: sprintf(deathStr, "### Sadly, %s fell to %s death.", getCName(), hisHer()); logn("log.death", "%s was killed by a fall.\n", getCName()); death = "a fall"; break; case PETRIFIED: sprintf(deathStr, "### Sadly, %s was turned to stone.\n### %s statue crumbles and breaks.", getCName(), upHisHer()); // all inventory, equipment, and gold are destroyed lose_all(this, true, "petrification"); coins.set(0, GOLD); printColor("^rAll your possessions were lost!\n"); if(level >= 10) { if(loadObject(STATUE_OBJ, &statue)) { statue->setName("broken statue of " + getName()); statue->description = getName(); statue->description += " is forever frozen in stone."; strncpy(statue->key[0], "broken", 20); strncpy(statue->key[1], "statue", 20); strncpy(statue->key[2], getCName(), 20); statue->setWeight(100 + getWeight()); statue->setBulk(50); statue->setFlag(O_NO_BREAK); statue->addToRoom(getRoomParent()); } } logn("log.death", "%s died from petrification.\n", getCName()); death = "petrification"; removeEffect("petrification", false); break; case DISEASE: sprintf(deathStr, "### Sadly, %s died from disease.", getCName()); logn("log.death", "%s was killed by disease.\n", getCName()); death = "disease"; break; case WOUNDED: logn("log.death", "%s was killed by festering wounds.\n", getCName()); sprintf(deathStr, "### Sadly, %s has bled to death.", getCName()); death = "festering wounds"; break; case ELVEN_ARCHERS: logn("log.death", "%s was shot to death by elven archers.\n", getCName()); sprintf(deathStr, "### Sadly, %s was shot to death by elven archers.", getCName()); death = "elven archers"; break; case DEADLY_MOSS: logn("log.death", "%s was choked to death by deadly underdark moss.\n", getCName()); sprintf(deathStr, "### Sadly, %s was choked to death by deadly underdark moss.", getCName()); death = "deadly underdark moss"; case PIERCER: sprintf(deathStr, "### Sadly, %s was impaled to death by a piercer.", getCName()); logn("log.death", "%s was killed by a piercer.\n", getCName()); death = "a piercer"; break; case SMOTHER: sprintf(deathStr, "### Sadly, %s was engulfed by the earth.", getCName()); logn("log.death", "%s was killed by an earth damage room.\n", getCName()); death = "engulfing earth"; break; case FROZE: sprintf(deathStr, "### Sadly, %s froze to death.", getCName()); logn("log.death", "%s was killed by an air damage room.\n", getCName()); death = "hypothermia"; break; case LIGHTNING: sprintf(deathStr, "### Sadly, %s was blasted to bits by electricity.", getCName()); logn("log.death", "%s was killed by an electricity damage room.\n", getCName()); death = "electricity"; break; case WINDBATTERED: sprintf(deathStr, "### Sadly, %s was ripped apart by the wind.", getCName()); logn("log.death", "%s was killed by an air damage room.\n", getCName()); death = "battering winds"; break; case BURNED: sprintf(deathStr, "### Sadly, %s was burned alive.", getCName()); logn("log.death", "%s was killed by a fire damage room or effect.\n", getCName()); death = "a raging fire"; break; case THORNS: sprintf(deathStr, "### Sadly, %s was killed by a wall of thorns.", getCName()); logn("log.death", "%s was killed by a wall of thorns.\n", getCName()); death = "a wall of thorns"; break; case DROWNED: sprintf(deathStr, "### Sadly, %s drowned.", getCName()); logn("log.death", "%s was killed by drowning.\n", getCName()); death = "drowning"; break; case DRAINED: sprintf(deathStr, "### Sadly, %s's life force was completely drained.", getCName()); logn("log.death", "%s was killed by a pharm room.\n", getCName()); death = "life-drain"; break; case ZAPPED: sprintf(deathStr, "### Sadly, %s was zapped to death.", getCName()); logn("log.death", "%s was killed by a combo lock.\n", getCName()); death = "a fatal shock"; break; case SHOCKED: sprintf(deathStr, "### Sadly, %s was shocked to death.", getCName()); logn("log.death", "%s was killed by shocking.\n", getCName()); death = "a fatal shock"; break; case PIT: sprintf(deathStr, "### Sadly, %s fell into a pit and died.", getCName()); logn("log.death", "%s was killed by a pit trap.\n", getCName()); death = "a fall"; break; case BLOCK: sprintf(deathStr, "### Sadly, %s was crushed to death by a giant stone block.", getCName()); logn("log.death", "%s was killed by a stone block trap.\n", getCName()); death = "a giant stone block"; break; case DART: sprintf(deathStr, "### Sadly, %s was killed by a poisoned dart.", getCName()); logn("log.death", "%s was killed by a poison dart trap.\n", getCName()); death = "a poisoned dart"; break; case ARROW: sprintf(deathStr, "### Sadly, %s was killed by a flight of arrows.", getCName()); logn("log.death", "%s was killed by an arrow trap.\n", getCName()); death = "a flight of arrows"; break; case SPIKED_PIT: sprintf(deathStr, "### Sadly, %s fell into a pit and was impaled by spikes.", getCName()); logn("log.death", "%s was killed by a spiked pit trap.\n", getCName()); death = "a fall"; break; case FIRE_TRAP: sprintf(deathStr, "### Sadly, %s was engulfed by flames and died.", getCName()); logn("log.death", "%s was killed by a fire trap.\n", getCName()); death = "a raging fire"; break; case FROST: sprintf(deathStr, "### Sadly, %s was frozen alive, and died.", getCName()); logn("log.death", "%s was killed by a frost trap.\n", getCName()); death = "hypothermia"; break; case ELECTRICITY: sprintf(deathStr, "### Sadly, %s was killed by electrocution.", getCName()); logn("log.death", "%s was killed by an electricity trap.\n", getCName()); death = "electricity"; break; case ACID: sprintf(deathStr, "### Sadly, %s was dissolved by acid.", getCName()); logn("log.death", "%s was killed by an acid trap.\n", getCName()); death = "acid"; break; case ROCKS: sprintf(deathStr, "### Sadly, %s was crushed to death in a rockslide.", getCName()); logn("log.death", "%s was killed by a rockslide trap.\n", getCName()); death = "a rockslide"; break; case ICICLE_TRAP: sprintf(deathStr, "### Sadly, %s was impaled by a giant icicle.", getCName()); logn("log.death", "%s was killed by a falling icicle trap.\n", getCName()); death = "a giant icicle"; break; case SPEAR: sprintf(deathStr, "### Sadly, %s was killed by a giant spear.", getCName()); logn("log.death", "%s was killed by a spear trap.\n", getCName()); death = "a spear"; break; case CROSSBOW_TRAP: sprintf(deathStr, "### Sadly, %s was killed by a crossbow trap.", getCName()); logn("log.death", "%s was killed by a crossbow trap.\n", getCName()); death = "a crossbow bolt"; break; case VINES: sprintf(deathStr, "### Sadly, %s was ripped apart by crawling vines.", getCName()); logn("log.death", "%s was killed by deadly vines.\n", getCName()); death = "deadly vines"; break; case COLDWATER: sprintf(deathStr, "### Sadly, %s died from hypothermia.", getCName()); logn("log.death", "%s was killed from hypothermia.\n", getCName()); death = "hypothermia"; break; case EXPLODED: sprintf(deathStr, "### Sadly, %s exploded.", getCName()); logn("log.death", "%s was killed by exploding.\n", getCName()); death = "self-combustion"; break; case SPLAT: sprintf(deathStr, "### Sadly, %s tumbled to %s death. (SPLAT!)", getCName(), hisHer()); logn("log.death", "%s tumbled to %s death.\n", getCName(), hisHer()); death = "a fall"; break; case BOLTS: sprintf(deathStr, "### Sadly, %s was blasted to death by energy bolts.", getCName()); logn("log.death", "%s was killed by energy bolts.\n", getCName()); death = "energy bolts"; break; case BONES: sprintf(deathStr, "### Sadly, %s was crushed to death under an avalanche of bones.", getCName()); logn("log.death", "%s was killed by a bone avalanche.\n", getCName()); death = "an avalanche of bones"; break; case EXPLOSION: sprintf(deathStr, "### Sadly, %s was vaporized in a magical explosion.", getCName()); logn("log.death", "%s was killed by a magical explosion.\n", getCName()); death = "a magical explosion"; break; case SUNLIGHT: sprintf(deathStr, "### Sadly, %s was disintegrated by sunlight.", getCName()); logn("log.death", "%s was disintegrated by sunlight.\n", getCName()); death = "sunlight"; break; default: sprintf(deathStr, "### Sadly, %s died.", getCName()); logn("log.death", "%s was killed by %s.\n", getCName(), getCName()); death = "misfortune"; break; } //if(level > 2) // broadcast(deathStr); //else { // broadcast(::isWatcher, "^C%s", deathStr); // print(deathStr); //} broadcast(deathStr); oldxp = experience; oldlvl = level; if(!killedByPlayer) { cureDisease(); removeCurse(); } if(!killedByPlayer || dt == POISON_PLAYER) curePoison(); // only drop all if killed by player dropEquipment(killedByPlayer && (!killer || !killer->isStaff()), killer ? killer->getSock() : 0); /* if(ready[WIELD - 1] && !ready[WIELD - 1]->flagIsSet(O_CURSED)) unequip(WIELD); if( ready[HELD - 1] && ready[HELD - 1]->getWearflag() == WIELD && !ready[HELD - 1]->flagIsSet(O_CURSED) ) { Object* held = unequip(HELD, UNEQUIP_NOTHING); if(held) { held->addToRoom(getRoom()); held->tempPerm(); } } Object* temp=0; for(i=0; i<MAXWEAR; i++) { if(ready[i] && !ready[i]->flagIsSet(O_CURSED)) { if(!killedByPlayer) { // i is wearloc-1 so add 1 unequip(i+1); } else { temp = unequip(i+1, UNEQUIP_NOTHING); if(temp) temp->addToRoom(getRoom()); } } } */ checkDarkness(); computeAC(); computeAttackPower(); courageous(); int xploss = 0; if(!killedByPlayer) { if(level >= 5) addEffect("death-sickness"); if(level < 10) { // Under level 10, 10% exp loss xploss = (int)((float)experience / 10.0); } else { // Level 10 and over, 2% exp loss with a minimum of 10k xploss = MAX((long)( (float)experience * 0.02), 10000); print("You have lost %ld experience.\n", (long)xploss); } subExperience((long)xploss); } n = level - exp_to_lev(experience); if(n > 1) { if(level < (MAXALVL+2)) experience = needed_exp[level-3]; else experience = (long)((needed_exp[MAXALVL-1]*(level-2))); } if(level >= 7) logn("log.death", "L:%dXP:%dE:%luA:%luF:%luW:%luE:%luC:%lu\n", oldlvl, oldxp, getRealm(EARTH), getRealm(WIND), getRealm(FIRE), getRealm(WATER), getRealm(ELEC), getRealm(COLD)); if(!killedByPlayer) { n = exp_to_lev(experience); while(level > n) downLevel(); negativeLevels = 0; } hp.restore(); mp.restore(); unhide(); // more info for staff broadcast(::isCt, "^rOld Exp: %u Lost Exp: %d, Old Lvl: %u, Lvl: %u, Room: %s", oldxp, xploss, oldlvl, level, getRoomParent()->fullName().c_str()); if(isHardcore()) { hardcoreDeath(this); // if you die in jail, you stay in jail } else if(!inJail()) { BaseRoom *newRoom = getLimboRoom().loadRoom(this); if(newRoom) { deleteFromRoom(); addToRoom(newRoom); doPetFollow(); } } } //******************************************************************** // clearAsPetEnemy //******************************************************************** void Creature::clearAsPetEnemy() { for(Monster* mons : getRoomParent()->monsters) { if(mons->isPet()) mons->clearEnemy(this); } } //******************************************************************** // cleanFollow //******************************************************************** void Monster::cleanFollow(Creature *killer) { if(isMonster() && getMaster()) { Player* player = getMaster()->getAsPlayer(); // This should fix the bug with having a dm's pet killed while possessing a mob if(flagIsSet(M_DM_FOLLOW)) { player->setAlias(0); player->clearFlag(P_ALIASING); } if(getMaster() != killer) { player->printColor("^r%M's body has been destroyed.\n", this); if(killer) broadcast(killer->getSock(), getRoomParent(), "%M was killed by %N.", this, killer); } removeFromGroup(false); player->delPet(this); } } //********************************************************************* // dropWeapons //********************************************************************* // handle dropping of weapons when you flee or die // Return: true if any weapons were dropped, false if none were dropped bool Player::dropWeapons() { BaseRoom* room = getRoomParent(); Object* weapon; bool dropMain=false, dropSec=false, fumbleMain=false, fumbleSec=false; bool dropDestroy = room->isDropDestroy(); bool jump=false; if(isStaff()) return(false); // check main weapons weapon = ready[WIELD-1]; if(weapon && !weapon->flagIsSet(O_CURSED)) { if(weapon->flagIsSet(O_NO_DROP) || weapon->flagIsSet(O_STARTING) || dropDestroy) { // Add to inventory unequip(WIELD); fumbleMain = true; } else { dropMain = true; // Unequip, and add to the room weapon = unequip(WIELD, UNEQUIP_NOTHING, false); finishDropObject(weapon, room, this); } } // check secondary weapons weapon = ready[HELD-1]; if(weapon && weapon->getWearflag() == WIELD) { if(weapon->flagIsSet(O_CURSED)) { if(!ready[WIELD-1]) { ready[WIELD-1] = ready[HELD-1]; ready[HELD-1] = 0; jump = true; } } else { if(weapon->flagIsSet(O_NO_DROP) || weapon->flagIsSet(O_STARTING) || dropDestroy) { fumbleSec = true; // Add to inventory unequip(HELD); } else { dropSec = true; // Unequip and add to the room weapon = unequip(HELD, UNEQUIP_NOTHING, false); finishDropObject(weapon, room, this); } } } if(dropMain || dropSec || fumbleMain || fumbleSec) { // only fumble if(!dropMain && !dropSec) if(fumbleMain && fumbleSec) print("You fumble your weapons.\n"); else print("You fumble your %sweapon.\n", fumbleSec ? "secondary " : ""); // only drop else if(!fumbleMain && !fumbleSec) if(dropMain && dropSec) print("You drop your weapons.\n"); else print("You drop your %sweapon.\n", dropSec ? "secondary " : ""); // one of each else print("You drop your %s weapon and fumble your %s weapon.\n", dropMain ? "main" : "secondary", fumbleMain ? "main" : "secondary"); if(jump) print("%s%s jumped to your primary hand! It's cursed!\n", !ready[WIELD-1]->flagIsSet(O_NO_PREFIX) ? "The " : "", ready[WIELD-1]->getCName()); // checkDarkness(), computeAttackPower(), computeAC() should be handled outside this function return(true); } return(false); } //********************************************************************