/*
* 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-2009 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 "unique.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->parent_rom || Faction::willDoBusinessWith(player, player->parent_rom->getFaction());
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->getRoom();
Object* object=0;
otag *op = player->first_obj,*oprev=0;
while(op) {
oprev = op;
op = op->next_tag;
object = oprev->obj;
if(delete_drop_obj(room, object, factionCanRecycle) || !canDrop(player, object, 0) || object->flagIsSet(O_STARTING))
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->value.set(player->coins[GOLD], GOLD);
player->coins.sub(player->coins[GOLD], GOLD);
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->name);
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
//********************************************************************
// adjustAlignment
//********************************************************************
void Player::adjustAlignment(Monster *victim) {
int adjust = victim->getAlignment() / 8;
if(victim->getAlignment() < 0 && victim->getAlignment() > -8)
adjust = -1;
if(victim->getAlignment() > 0 && victim->getAlignment() < 8)
adjust = 1;
bool toGood = adjust < 0;
bool toEvil = adjust > 0;
bool toNeutral = ((alignment > 0 && toEvil) ||
(alignment < 0 && toGood) );
// handle all the move-away-from-neutral cases
if(!toNeutral) {
// if they're gradius, an anti-gradius race can only help their alignment.
// same goes with the paly/dk war.
if(deity == GRADIUS) {
if(victim->isAntiGradius())
adjust = 0;
if(cClass == PALADIN && victim->getClass() == DEATHKNIGHT)
adjust = 0;
}
// werewolves and vampires can always kill each other
if(isEffected("vampirism") && victim->isEffected("lycanthropy"))
adjust = 0;
if(isEffected("lycanthropy") && victim->isEffected("vampirism"))
adjust = 0;
if(deity == CERIS && victim->isUndead())
adjust /= 2;
}
// paladin / deathknight war
// gradius paladins are taken care of above
if(deity != GRADIUS) {
if(cClass == PALADIN && victim->getClass() == DEATHKNIGHT && toEvil)
adjust = 0;
if(cClass == DEATHKNIGHT && victim->getClass() == PALADIN && toGood)
adjust = 0;
}
alignment -= adjust;
alignment = MAX(-1000, MIN(1000, alignment));
}
//********************************************************************
// dropCorpse
//********************************************************************
// Parameters: <killer> The creature attacking
// Handles the dropping of items from creatures
void Monster::dropCorpse(Creature *killer) {
BaseRoom* room = getRoom();
bstring str = "", carry = "";
otag *op=0;
Object *object=0;
Player* player=0;
Player* pMaster = isPet() ? following->getPlayer() : 0;
bool destroy = room->flagIsSet(R_DESTROYS_ITEMS);
if(killer)
player = killer->getMaster();
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)
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 = listObjects(player, first_obj, true);
op = first_obj;
while(op) {
object = op->obj;
op = op->next_tag;
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->nameCoin("gold", object->value[GOLD]);
object->addToRoom(room);
if(player) {
if(str != "")
str += ", ";
str += object->name;
}
}
if(player && str != "") {
carry = crt_str(this, 0, CAP | INV);
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 = getPlayer();
Monster* mVictim = getMonster();
Player* pKiller = killer->getPlayer();
Monster* mKiller = killer->getMonster();
bool duel = induel(pVictim, pKiller);
if(pKiller) {
pKiller->statistics.kill();
if(mVictim)
pKiller->statistics.monster(mVictim);
if(pKiller->hasCharm(name))
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);
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);
}
// 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.", name);
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.", name, killer);
killer->delEnmCrt(name);
clearAsEnemy();
clearEnemyPlayer();
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, getRoom()->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->following)
master = killer->following->getPlayer();
else {
broadcast(::isCt, "^y*** Pet %s has no master and is trying to kill a player. Room %s",
killer->name, killer->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.", name, master->name, killer->name);
else {
if(level > 2)
broadcast("### Sadly, %s was killed by %s's %s.", name, master->name, killer->name);
else {
broadcast(::isCt, "^y### Sadly, %s was killed by %s's %s.", name, master->name, killer->name);
print("### Sadly, %s was killed by %s's %s.", name, master->name, killer->name);
}
}
killer->delEnmCrt(name);
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->following) {
petKiller = killer;
pKiller = killer->following->getPlayer();
} else {
broadcast(::isCt, "^y*** Pet %s has no master and is trying to kill a mob. Room %s",
killer->name, killer->room.str().c_str());
return;
}
broadcast(NULL, pKiller->getRoom(), "%M's %s killed %N.", pKiller, petKiller->name, 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->getRoom(), "%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(following != killer) {
killer->print("You killed %N.\n", this);
broadcast(killer->getSock(), killer->getRoom(), "%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();
// freeTarget now means: do I (calling function) need to free the target?
freeTarget = !freeTarget;
}
//********************************************************************
// finishMobDeath
//********************************************************************
void Monster::finishMobDeath() {
deleteFromRoom();
gServer->delActive(this);
free_crt(this);
}
//********************************************************************
// dieToPlay
//********************************************************************
// 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->getRoom(), "%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.", name, deathstring, killer->name);
else// if(level > 2)
broadcast("### Sadly, %s was %s by %1N.", name, 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, getRoom()->fullName().c_str());
}
if(reset)
resetPlayer(killer);
}
//********************************************************************
// isGuildKill
//********************************************************************
bool Player::isGuildKill(const Player *killer) const {
if(induel(killer, this))
return(false);
if(killer->getLevel() < 7 || level < 7)
return(false);
if(!killer->getGuild() || killer->getGuild() > gConfig->maxGuilds || guild > gConfig->maxGuilds)
return(false);
if(!(killer->getGuild() && killer->getGuildRank() >= GUILD_PEON) ||
!(guild && guildRank >= GUILD_PEON) ||
(killer->isStaff()) )
return(false);
return(true);
}
//********************************************************************
// 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->pkillsIn ++;
killerGuild->pkillsWon++;
thisGuild->pkillsIn++;
killerGuild->bank.add(coins);
guildBankLog(killer->getGuild(), "Guild PKILL: %s by %s [Balance: %s]\n",
coins.str().c_str(), killer->name, killerGuild->bank.str().c_str());
coins.zero();
}
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));
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));
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);
killer->checkLevel();
experience -= penalty;
checkLevel();
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->pkillsIn ++;
killerGuild->pkillsWon ++;
thisGuild->pkillsIn ++;
}
}
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);
killer->checkLevel();
experience -= penalty;
checkLevel();
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);
experience -= expLoss;
if(experience <= 0)
experience = 0;
if(!killer->flagIsSet(P_OUTLAW)) {
killer->print("You have vanquished %s.\n", name);
killer->print("You %s %d for your heroic deed.\n", gConfig->isAprilFools() ? "lose" : "gain", expGain);
killer->addExperience(expGain);
killer->checkLevel();
}
}
return(1);
}
//********************************************************************
// checkDoctorKill
//********************************************************************
// Parameters: <player>
// <killer> The creature attacking
// Checks for doctor killers
void Creature::checkDoctorKill(Creature *victim) {
if(!strcmp(victim->name, "doctor")) {
if( (isPlayer() && !isStaff()) ||
(isMonster() && isPet() && !following->isStaff())
) {
Creature* target = isPlayer() ? this : following;
target->setFlag(P_DOCTOR_KILLER);
if(!target->flagIsSet(P_DOCTOR_KILLER))
broadcast("### %s is a doctor this!", target->name);
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() {
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(parent_rom && parent_rom->getHighLevel() && level == parent_rom->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",
name, n, level, getRoom()->fullName().c_str());
if(!flagIsSet(P_DM_INVIS))
broadcast("### %s just releveled to %s!", name, int_to_text(n));
while(level < n)
upLevel();
return(1); // Relevel
}
// TODO: 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;
// be nice to lowbies
if(!dropAll || level < 4) {
dropWeapons(true);
} else {
// 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();
// don't make them lose all their inventory in a drop-destroy room
if(!getRoom()->flagIsSet(R_DESTROYS_ITEMS)) {
// 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]->name;
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) {
Limited::deleteOwner(this, temp);
temp->addToRoom(getRoom());
}
}
}
// reset these wear locations
ready[WIELD-1] = rMain;
ready[HELD-1] = rHeld;
if(dropString != "") {
if(killerSock)
killerSock->print("%s dropped: %s.\n", name, 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(getRoom()->flagIsSet(R_DESTROYS_ITEMS))
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->following->isDm() &&
(killer->following->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;
}
strcpy(partName, name);
sprintf(body_part->name, "%s's %s", partName, part);
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(getRoom());
}
}
}
//********************************************************************
// logDeath
//********************************************************************
void Player::logDeath(Creature *killer) {
char file[16], killerName[80];
Player* pKiller = killer->getPlayer();
if(!killer->isStaff())
statistics.die();
strcpy(killerName, "");
if(killer->isPet())
sprintf(killerName, "%s's %s.", killer->following->name, killer->name);
else
strcpy(killerName, killer->name);
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", name, level,
killer->name, killer->getLevel(), killer->getRoom()->fullName().c_str());
}
if(pKiller && pKiller->isStaff()) {
log_immort(false, pKiller, "%s(%d) was killed by %s(%d) in room %s.\n", name, level,
pKiller->name, pKiller->getLevel(), pKiller->getRoom()->fullName().c_str());
}
}
//********************************************************************
// 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->getPlayer();
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.", name, killer->name);
}
// a hardcore player about to die doesnt need to worry about room movement
if(isHardcore() && !killer->isStaff())
same = true;
if(!same) {
deleteFromRoom();
addToRoom(newRoom);
doPetFollow();
}
//doClearPetEnemy(this, killer->name);
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(), getRoom(), "%s is knocked unconscious!", name);
updateAttackTimer(true, 300);
pKiller->delDueling(name);
delDueling(killer->name);
} else if(isHardcore() && !killer->isStaff()) {
hardcoreDeath(this);
} else {
courageous();
}
}
//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 solo=0, logType=0;
ctag *fp=0;
Creature *leader=0, *pet=0;
BaseRoom* room = killer->getRoom();
char file[80], killerString[1024];
char logStr[2096];
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->isPlayer() && killer->flagIsSet(P_BUGGED) ) {
sprintf(file, "%s/%s", BUGPATH, killer->name);
logType = 3;
} else if(killer->isPlayer() && killer->flagIsSet(P_KILLS_LOGGED) ) {
sprintf(file, "%s/%s.kills", BUGPATH, killer->name);
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()) {
solo = 1;
// we have the leader of the pet
if(killer->isPet())
leader = killer->following;
else
leader = killer;
// see if they have a leader or followers
if(leader->following)
solo = 0;
// we always need to check for a pet
fp = leader->first_fol;
while(fp) {
if(fp->crt != leader) {
if(fp->crt->isPet() && fp->crt->following == leader)
pet = fp->crt;
if(!(fp->crt->isPet() && fp->crt->following == leader))
solo = 0;
}
fp = fp->next_tag;
}
}
if(killer->isPet() || pet) {
sprintf(killerString, "%s and %s %s",
leader->name,
leader->hisHer(),
pet->name);
} else {
sprintf(killerString, "%s", killer->name);
}
switch(logType) {
case 1: // unsaved mob
sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s for %lu experience.",
name, 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.",
name, level, killerString, killer->getLevel(), room->fullName().c_str(), experience);
break;
case 2: // Mob is a perm
// 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(leader->following)
leader = leader->following;
if(!solo)
sprintf(logStr, "%s(L%d) was killed by %s(L%d) in %s(L%d)'s group in room %s.",
name, level, killerString, killer->getLevel(),
leader->name, leader->getLevel(), room->fullName().c_str());
else
sprintf(logStr, "%s(L%d) was killed by %s(L%d) in room %s [SOLO].",
name, 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.", name, leader->name, leader->hisHer());
else
broadcast(wantsPermDeaths, "^m### Sadly, %s was killed by %s.", name, 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.",
name, 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->following->isStaff()))
return;
if(level >= 7)
addEffect("death-sickness");
if(level < 10) {
// Under level 10, 10% exp loss
xploss = ((float)experience / 10.0);
lostExperience += (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);
lostExperience += (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) {
etag *ep=0;
long expGain = 0;
Monster* pet=0;
Player* player=0, *follower=0;
Player* leader=0;
if(isPet())
return;
if(flagIsSet(M_PERMENANT_MONSTER))
diePermCrt();
if(killer) {
if(killer->isPet())
player = killer->following->getPlayer();
else
player = killer->getPlayer();
if(player) {
if(player->isGroupLeader(this) && player->flagIsSet(P_XP_DIVIDE))
leader = player;
else if(player->following && player->following->flagIsSet(P_XP_DIVIDE) && player->inSameRoom(player->following))
leader = player->following->getPlayer();
}
std::list<Player*> groupExpList;
// If we have a leader, means we've got group exp set, so lets distribute it
if(leader) {
// Split exp evenly amounsgt the group
int numGroupMembers=0, totalGroupLevel=0, totalGroupDamage=0;
Creature* crt=0;
ctag* cp = leader->first_fol;
// Calculate how many people are in the group, see how much damage they have done
// and remove them from the enemy list
if( isEnmCrt(leader->name) &&
inSameRoom(leader) &&
leader->getsGroupExperience(this)
) {
numGroupMembers++;
totalGroupLevel += leader->getLevel();
totalGroupDamage += delEnmCrt(leader->name);
groupExpList.push_back(leader);
}
while(cp) {
crt = cp->crt;
cp = cp->next_tag;
if(isEnmCrt(crt->name) && inSameRoom(crt)) {
if(crt->getsGroupExperience(this)) {
// Group member
follower = crt->getPlayer();
numGroupMembers ++;
totalGroupLevel += follower->getLevel();
totalGroupDamage += delEnmCrt(follower->name);
groupExpList.push_back(follower);
pet = follower->getPet();
if(pet)
totalGroupDamage += delEnmCrt(pet->name, pet->following->name);
} else if(crt->isPet()) {
// Leader's pet
totalGroupDamage += delEnmCrt(crt->name, crt->following->name);
}
}
}
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.
foreach(Player * ply, groupExpList) {
if(ply) {
expGain = (long)(((float)ply->getLevel()/(float)totalGroupLevel) * adjustedExp);
expGain = MAX(1, expGain);
ply->gainExperience(this, killer, expGain, true);
}
}
}
}
// Now handle everyone else on the list
ep = first_enm;
while(ep) {
pet = 0;
if(ep->owner[0]) {
player = gServer->findPlayer(ep->owner);
if(!player)
player = gServer->findPlayer(ep->enemy);
if(player) {
// take monster off of pet enemy list
pet = player->getPet();
if(pet)
pet->delEnmCrt(name);
}
} else {
player = gServer->findPlayer(ep->enemy);
}
if(player) {
expGain = (experience * ep->damage) / MAX(hp.getMax(), 1);
expGain = MIN(MAX(0,expGain), experience);
if(pet)
pet->gainExperience(this, killer, expGain);
else
player->gainExperience(this, killer, expGain);
// Adjust monk/wolf ac and thac0 for extreme aligment
// **************************************************
player->alignAdjustAcThaco();
// **************************************************
}
ep = ep->next_tag;
}
}
//********************************************************************
// adjustExperience
//********************************************************************
void Creature::adjustExperience(Monster* victim, int& expAmount, int& holidayExp) {
Player* player;
if(isPet())
player = following->getPlayer();
else
player = getPlayer();
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->following->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->following, killer->name, 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);
experience += expAmount;
experience += holidayExp;
checkLevel();
if(victim->flagIsSet(M_WILL_BE_LOGGED))
logn("log.mdeath", "%s was killed by %s, for %d experience.\n", victim->name, name, expAmount);
ctag* fp = first_fol;
while(fp) {
if(fp->crt->isPet()) {
fp->crt->getMonster()->delEnmCrt(victim->name);
break;
}
fp = fp->next_tag;
}
}
//********************************************************************
// gainExperience
//********************************************************************
void Monster::gainExperience(Monster* victim, Creature* killer, int expAmount, bool groupExp) {
Creature* master = following;
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->name, 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);
master->addExperience(holidayExp);
delEnmCrt(victim->name);
}
//********************************************************************
// 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 = getRoom();
Player *pVictim = getPlayer();
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;
ctag *rcp;
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);
rcp = room->first_mon;
while(rcp) {
rcp->crt->getMonster()->delEnmCrt(pVictim->name);
rcp = rcp->next_tag;
}
killer->delEnmCrt(pVictim->name);
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.", name);
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.", name);
logn("log.death", "%s was poisoned to death by %s.\n", name, poisonedBy.c_str());
break;
case CREEPING_DOOM:
removeEffect("creeping-doom", false);
sprintf(deathStr, "### Sadly, %s was killed by cursed spiders.", name);
logn("log.death", "%s was poisoned to death by %s.\n", name, poisonedBy.c_str());
break;
default:
break;
}
if(poisonedBy != "") {
switch(dt) {
case POISON_PLAYER:
sprintf(deathStr, "### Sadly, %s was poisoned to death by %s.", name, poisonedBy.c_str());
break;
case CREEPING_DOOM:
sprintf(deathStr, "### Sadly, %s was killed by %s's cursed spiders.", name, 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.", name, poisonedBy.c_str());
logn("log.death", "%s was poisoned to death by %s.\n", name, poisonedBy.c_str());
death = "poison";
break;
case POISON_GENERAL:
sprintf(deathStr, "### Sadly, %s was poisoned to death.", name);
logn("log.death", "%s was poisoned to death.\n", name);
death = "poison";
break;
case FALL:
sprintf(deathStr, "### Sadly, %s fell to %s death.", name, hisHer());
logn("log.death", "%s was killed by a fall.\n", name);
death = "a fall";
break;
case PETRIFIED:
sprintf(deathStr, "### Sadly, %s was turned to stone.\n### %s statue crumbles and breaks.",
name, upHisHer());
// all inventory, equipment, and gold are destroyed
lose_all(this);
coins.set(0, GOLD);
printColor("^rAll your possessions were lost!\n");
if(level >= 10) {
if(loadObject(STATUE_OBJ, &statue)) {
sprintf(statue->name, "broken statue of %s", name);
statue->description = name;
statue->description += " is forever frozen in stone.";
strncpy(statue->key[0], "broken", 20);
strncpy(statue->key[1], "statue", 20);
strncpy(statue->key[2], name, 20);
statue->setWeight(100 + getWeight());
statue->setBulk(50);
statue->setFlag(O_NO_BREAK);
statue->addToRoom(getRoom());
}
}
logn("log.death", "%s died from petrification.\n", name);
death = "petrification";
removeEffect("petrification", false);
break;
case DISEASE:
sprintf(deathStr, "### Sadly, %s died from disease.", name);
logn("log.death", "%s was killed by disease.\n", name);
death = "disease";
break;
case WOUNDED:
logn("log.death", "%s was killed by festering wounds.\n", name);
sprintf(deathStr, "### Sadly, %s has bled to death.", name);
death = "festering wounds";
break;
case ELVEN_ARCHERS:
logn("log.death", "%s was shot to death by elven archers.\n", name);
sprintf(deathStr, "### Sadly, %s was shot to death by elven archers.", name);
death = "elven archers";
break;
case DEADLY_MOSS:
logn("log.death", "%s was choked to death by deadly underdark moss.\n", name);
sprintf(deathStr, "### Sadly, %s was choked to death by deadly underdark moss.", name);
death = "deadly underdark moss";
case PIERCER:
sprintf(deathStr, "### Sadly, %s was impaled to death by a piercer.", name);
logn("log.death", "%s was killed by a piercer.\n", name);
death = "a piercer";
break;
case SMOTHER:
sprintf(deathStr, "### Sadly, %s was engulfed by the earth.", name);
logn("log.death", "%s was killed by an earth damage room.\n", name);
death = "engulfing earth";
break;
case FROZE:
sprintf(deathStr, "### Sadly, %s froze to death.", name);
logn("log.death", "%s was killed by an air damage room.\n", name);
death = "hypothermia";
break;
case LIGHTNING:
sprintf(deathStr, "### Sadly, %s was blasted to bits by electricity.", name);
logn("log.death", "%s was killed by an electricity damage room.\n", name);
death = "electricity";
break;
case WINDBATTERED:
sprintf(deathStr, "### Sadly, %s was ripped apart by the wind.", name);
logn("log.death", "%s was killed by an air damage room.\n", name);
death = "battering winds";
break;
case BURNED:
sprintf(deathStr, "### Sadly, %s was burned alive.", name);
logn("log.death", "%s was killed by a fire damage room or effect.\n", name);
death = "a raging fire";
break;
case THORNS:
sprintf(deathStr, "### Sadly, %s was killed by a wall of thorns.", name);
logn("log.death", "%s was killed by a wall of thorns.\n", name);
death = "a wall of thorns";
break;
case DROWNED:
sprintf(deathStr, "### Sadly, %s drowned.", name);
logn("log.death", "%s was killed by drowning.\n", name);
death = "drowning";
break;
case DRAINED:
sprintf(deathStr, "### Sadly, %s's life force was completely drained.", name);
logn("log.death", "%s was killed by a pharm room.\n", name);
death = "life-drain";
break;
case ZAPPED:
sprintf(deathStr, "### Sadly, %s was zapped to death.", name);
logn("log.death", "%s was killed by a combo lock.\n", name);
death = "a fatal shock";
break;
case SHOCKED:
sprintf(deathStr, "### Sadly, %s was shocked to death.", name);
logn("log.death", "%s was killed by shocking.\n", name);
death = "a fatal shock";
break;
case PIT:
sprintf(deathStr, "### Sadly, %s fell into a pit and died.", name);
logn("log.death", "%s was killed by a pit trap.\n", name);
death = "a fall";
break;
case BLOCK:
sprintf(deathStr, "### Sadly, %s was crushed to death by a giant stone block.", name);
logn("log.death", "%s was killed by a stone block trap.\n", name);
death = "a giant stone block";
break;
case DART:
sprintf(deathStr, "### Sadly, %s was killed by a poisoned dart.", name);
logn("log.death", "%s was killed by a poison dart trap.\n", name);
death = "a poisoned dart";
break;
case ARROW:
sprintf(deathStr, "### Sadly, %s was killed by a flight of arrows.", name);
logn("log.death", "%s was killed by an arrow trap.\n", name);
death = "a flight of arrows";
break;
case SPIKED_PIT:
sprintf(deathStr, "### Sadly, %s fell into a pit and was impaled by spikes.", name);
logn("log.death", "%s was killed by a spiked pit trap.\n", name);
death = "a fall";
break;
case FIRE_TRAP:
sprintf(deathStr, "### Sadly, %s was engulfed by flames and died.", name);
logn("log.death", "%s was killed by a fire trap.\n", name);
death = "a raging fire";
break;
case FROST:
sprintf(deathStr, "### Sadly, %s was frozen alive, and died.", name);
logn("log.death", "%s was killed by a frost trap.\n", name);
death = "hypothermia";
break;
case ELECTRICITY:
sprintf(deathStr, "### Sadly, %s was killed by electrocution.", name);
logn("log.death", "%s was killed by an electricity trap.\n", name);
death = "electricity";
break;
case ACID:
sprintf(deathStr, "### Sadly, %s was dissolved by acid.", name);
logn("log.death", "%s was killed by an acid trap.\n", name);
death = "acid";
break;
case ROCKS:
sprintf(deathStr, "### Sadly, %s was crushed to death in a rockslide.", name);
logn("log.death", "%s was killed by a rockslide trap.\n", name);
death = "a rockslide";
break;
case ICICLE_TRAP:
sprintf(deathStr, "### Sadly, %s was impaled by a giant icicle.", name);
logn("log.death", "%s was killed by a falling icicle trap.\n", name);
death = "a giant icicle";
break;
case SPEAR:
sprintf(deathStr, "### Sadly, %s was killed by a giant spear.", name);
logn("log.death", "%s was killed by a spear trap.\n", name);
death = "a spear";
break;
case CROSSBOW_TRAP:
sprintf(deathStr, "### Sadly, %s was killed by a crossbow trap.", name);
logn("log.death", "%s was killed by a crossbow trap.\n", name);
death = "a crossbow bolt";
break;
case VINES:
sprintf(deathStr, "### Sadly, %s was ripped apart by crawling vines.", name);
logn("log.death", "%s was killed by deadly vines.\n", name);
death = "deadly vines";
break;
case COLDWATER:
sprintf(deathStr, "### Sadly, %s died from hypothermia.", name);
logn("log.death", "%s was killed from hypothermia.\n", name);
death = "hypothermia";
break;
case EXPLODED:
sprintf(deathStr, "### Sadly, %s exploded.", name);
logn("log.death", "%s was killed by exploding.\n", name);
death = "self-combustion";
break;
case SPLAT:
sprintf(deathStr, "### Sadly, %s tumbled to %s death. (SPLAT!)", name, hisHer());
logn("log.death", "%s tumbled to %s death.\n", name, hisHer());
death = "a fall";
break;
case BOLTS:
sprintf(deathStr, "### Sadly, %s was blasted to death by energy bolts.", name);
logn("log.death", "%s was killed by energy bolts.\n", name);
death = "energy bolts";
break;
case BONES:
sprintf(deathStr, "### Sadly, %s was crushed to death under an avalanche of bones.", name);
logn("log.death", "%s was killed by a bone avalanche.\n", name);
death = "an avalanche of bones";
break;
case EXPLOSION:
sprintf(deathStr, "### Sadly, %s was vaporized in a magical explosion.", name);
logn("log.death", "%s was killed by an exploding wand.\n", name);
death = "an exploding wand";
break;
case SUNLIGHT:
sprintf(deathStr, "### Sadly, %s was disintegrated by sunlight.", name);
logn("log.death", "%s was disintegrated by sunlight.\n", name);
death = "sunlight";
break;
default:
sprintf(deathStr, "### Sadly, %s died.", name);
logn("log.death", "%s was killed by %s.\n", name, name);
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);
lostExperience += (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);
lostExperience += (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, getRoom()->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() {
ctag *cp = getRoom()->first_mon;
while(cp) {
if(cp->crt->isPet())
cp->crt->getMonster()->delEnmCrt(name);
cp = cp->next_tag;
}
}
//********************************************************************
// cleanFollow
//********************************************************************
void Monster::cleanFollow(Creature *killer) {
if(isMonster() && following) {
Player* player = following->getPlayer();
// 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(following != killer) {
player->printColor("^r%M's body has been destroyed.\n", this);
if(killer)
broadcast(killer->getSock(), getRoom(), "%M was killed by %N.", this, killer);
}
doStopFollowing(this, FALSE);
}
}
//*********************************************************************
// 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(bool noDrop) {
BaseRoom *room = getRoom();
Object* weapon;
bool dropMain=false, dropSec=false, fumbleMain=false, fumbleSec=false;
bool jump=false;
if(isStaff())
return(false);
noDrop = room->flagIsSet(R_DESTROYS_ITEMS);
// check main weapons
weapon = ready[WIELD-1];
if(weapon && !weapon->flagIsSet(O_CURSED)) {
if(weapon->flagIsSet(O_NO_DROP) || weapon->flagIsSet(O_STARTING) || noDrop) {
// Add to inventory
unequip(WIELD);
fumbleMain = true;
} else {
dropMain = true;
// Unequip, and add to the room
unequip(WIELD, UNEQUIP_NOTHING, false);
Limited::deleteOwner(this, weapon);
weapon->addToRoom(room);
}
}
// 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) || noDrop) {
fumbleSec = true;
// Add to inventory
unequip(HELD);
} else {
dropSec = true;
// Unequip and add to the room
unequip(HELD, UNEQUIP_NOTHING, false);
Limited::deleteOwner(this, weapon);
weapon->addToRoom(room);
}
}
}
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]->name);
// checkDarkness(), computeAttackPower(), computeAC() should be handled outside this function
return(true);
}
return(false);
}
//********************************************************************
// effectDeathSickness
//********************************************************************
bool effectDeathSickness(EffectInfo *effect, Creature* target, EffectAction action, void* applier, ApplyFrom aFrom) {
Player* pTarget = target->getPlayer();
// Only on players
if(!pTarget)
return(false);
switch(action) {
case EFFECT_COMPUTE:
{
int duration = 0;
int strength = 1;
// Two minutes per level
duration = pTarget->getLevel() * 2 * 60;
// We'll start the strength out at 100 and reduce it each pulse
strength = 100;
effect->setStrength(strength);
effect->setDuration(duration);
}
break;
case EFFECT_APPLY:
break;
case EFFECT_UNAPPLY:
break;
case EFFECT_PULSE:
{
int strength = effect->getStrength();
int duration = effect->getDuration();
if(!target->isEffected("petrification") && mrand(1,100) < (strength/2)) {
target->wake("^DA strong urge to vomit wakes you!");
target->printColor("^GYour death-sickness causes you to vomit. EWWW.\n");
target->unhide();
// Stun then half of the time for 0-2 seconds
if(mrand(1,100) < 50) {
target->stun(mrand(0,2));
target->printColor("^DYou become disoriented.\n");
}
if(target->getRoom())
broadcast(target->getSock(), target->getRoom(), "^G%M vomits all over the ground.", target);
}
int newStrength = 0;
if(strength != 0)
newStrength = (int)nearbyint((double)strength - (((double)strength/(double)duration)*20.0));
if(strength > 75 && newStrength <= 75) {
pTarget->printColor("^cYou feel a little better.\n");
if(target->getRoom())
broadcast(target->getSock(), target->getRoom(), "^c%M looks a little better.", target);
} else if(strength > 50 && newStrength <= 50) {
pTarget->printColor("^cYou feel better.\n");
if(target->getRoom())
broadcast(target->getSock(), target->getRoom(), "^c%M looks better.", target);
} else if(strength > 25 && newStrength <= 25) {
pTarget->printColor("^cYou are nearly recovered.\n");
if(target->getRoom())
broadcast(target->getSock(), target->getRoom(), "^c%M looks nearly recovered.", target);
}
strength = MIN(MAX(newStrength,0), 100);
//target->print("DEATH SICKNESS: New Strength %d, Duration %d.\n", strength, duration);
effect->setStrength(strength);
}
break;
default:
break;
}
return(true);
}