/*
* player.c
* Player routines.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* 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
*
*/
// C includes
#include <math.h>
#include <arpa/telnet.h> // IAC
// Mud includes
#include "mud.h"
#include "guilds.h"
#include "login.h"
#include "commands.h"
#include "move.h"
#include "effects.h"
#include "clans.h"
#include "property.h"
#include "unique.h"
#include "magic.h"
#include <iomanip>
#include <locale>
//********************************************************************
// fixLts
//********************************************************************
void Creature::fixLts() {
long tdiff=0, t = time(0);
int i=0;
if(isPet())
tdiff = t - following->lasttime[LT_AGE].ltime;
else
tdiff = t - lasttime[LT_AGE].ltime;
for(i=0; i<MAX_LT; i++) {
if(i == LT_JAILED)
continue;
lasttime[i].ltime += tdiff;
lasttime[i].ltime = MIN(t, lasttime[i].ltime);
}
}
//********************************************************************
// init
//********************************************************************
// This function initializes a player's stats and loads up the room he
// should be in. This should only be called when the player first
// logs on.
void Player::init() {
char file[80], str[50], watchers[128];
BaseRoom *newRoom=0;
Room *uRoom=0;
ctag *cp;
long t = time(0);
int watch=0;
statistics.setParent(this);
// always make sure size matches up with race
if(size == NO_SIZE) {
size = gConfig->getRace(race)->getSize();
EffectInfo *e=0;
if(isEffected("enlarge"))
e = getEffect("enlarge");
else if(isEffected("reduce"))
e = getEffect("reduce");
if(e)
changeSize(0, e->getStrength(), e->getName() == "enlarge");
if(!size)
size = SIZE_MEDIUM;
}
setFlag(P_KILL_AGGROS);
clearFlag(P_LINKDEAD);
clearFlag(P_SPYING);
clearFlag(P_READING_FILE);
clearFlag(P_AFK);
clearFlag(P_DIED_IN_DUEL);
clearFlag(P_VIEW_ZONES);
if(cClass == CLERIC && level >= 7 && flagIsSet(P_CHAOTIC))
setFlag(P_PLEDGED);
if(level <= ALIGNMENT_LEVEL && !flagIsSet(P_CHOSEN_ALIGNMENT))
clearFlag(P_CHAOTIC);
if(level > ALIGNMENT_LEVEL)
setFlag(P_CHOSEN_ALIGNMENT);
if((cClass == CLERIC && level < 7) || (cClass == CLERIC && !flagIsSet(P_CHAOTIC)))
clearFlag(P_PLEDGED);
setFlag(P_SECURITY_CHECK_OK);
if(isdm(name))
cClass = DUNGEONMASTER;
else if(isDm())
cClass = CARETAKER;
/* change this later
strength.getCur() = strength.max;
dexterity.getCur() = dexterity.max;
constitution.getCur() = constitution.max;
intelligence.getCur() = intelligence.max;
piety.getCur() = piety.max;
*/
if(!isStaff()) {
daily[DL_ENCHA].max = 3;
daily[DL_FHEAL].max = MAX(3, 3 + (level) / 3);
daily[DL_TRACK].max = MAX(3, 3 + (level) / 3);
daily[DL_DEFEC].max = 1;
// daily[DL_TELEP].max = MAX(3, MAX(2, level/4));
daily[DL_TELEP].max = 3;
if(level < 13)
daily[DL_TELEP].max = 1;
// daily[DL_ETRVL].max = MAX(3, MAX(2, level/4));
daily[DL_RCHRG].max = MAX(7, level / 2);
daily[DL_HANDS].max = 3;
daily[DL_RESURRECT].max = 1;
daily[DL_SILENCE].max = 3;
daily[DL_HARM].max = 2;
daily[DL_SCARES].max = 3;
} else {
daily[DL_DEFEC].max = 100;
}
if(!gServer->isRebooting())
initBuilder();
if(flagIsSet(P_MISTED) && isDay())
unmist();
if(!isStaff()) {
clearFlag(P_DM_INVIS);
clearFlag(P_INCOGNITO);
} else {
// staff logs on with dmInvis
setFlag(P_DM_INVIS);
}
if(isDm())
clearFlag(P_BUGGED);
//if((race != KATARAN && race != TROLL))
learnLanguage(LCOMMON); // All races speak common but troll and kataran.
if(!current_language) {
initLanguages();
current_language = LCOMMON;
setFlag(P_LANGUAGE_COLORS);
}
// Paladins get auto know-aur
if(cClass==PALADIN)
addPermEffect("know-aura");
// Mages get auto d-m
if((cClass==MAGE || cClass2 == MAGE) &&
!(cClass == MAGE && cClass2 == ASSASSIN)) {
addPermEffect("detect-magic");
}
if(cClass == DRUID && level >= 7)
addPermEffect("pass-without-trace");
if(cClass == THIEF && cClass2 == MAGE)
addPermEffect("detect-magic");
if((cClass == MAGE || cClass2 == MAGE) && level >= 7)
learnSpell(S_ARMOR);
if(cClass == CLERIC && deity == CERIS && level >= 13)
learnSpell(S_REJUVENATE);
// Werewolves get auto Detect-Invisibility at level 7
if(isEffected("lycanthropy") && level >= 7)
addPermEffect("detect-invisible");
// Rangers level 13+ can detect misted vampires.
if(cClass == RANGER && level >= 13)
addPermEffect("true-sight");
// clear ignore flags
clearFlag(P_IGNORE_CLASS_SEND);
clearFlag(P_IGNORE_RACE_SEND);
clearFlag(P_NO_BROADCASTS);
clearFlag(P_IGNORE_NEWBIE_SEND);
clearFlag(P_IGNORE_GOSSIP);
clearFlag(P_IGNORE_CLAN);
clearFlag(P_NO_TELLS);
if(!flagIsSet(P_OUTLAW))
setFlag(P_NO_SUMMON);
clearFlag(P_LAG_PROTECTION_OPERATING);
clearFlag(P_ALIASING);
if(!isStaff())
setFlag(P_PROMPT);
strcpy(str, "");
lasttime[LT_PLAYER_SAVE].ltime = t;
lasttime[LT_PLAYER_SAVE].interval = SAVEINTERVAL;
// SEARCH FOR ME
lasttime[LT_TICK].ltime = t+30;
lasttime[LT_TICK_SECONDARY].ltime = t+30;
lasttime[LT_TICK_HARMFUL].ltime = t+30;
// spam check
lasttime[LT_PLAYER_SEND].ltime = t;
lasttime[LT_PLAYER_SEND].misc = 0;
// load up parent_rom for the broadcast below, but don't add the
// player to the room or the messages will be out of order
if(!area_room) {
Property *p = gConfig->getProperty(room);
if( p &&
p->getType() == PROP_STORAGE &&
!p->isOwner(name) &&
!p->isPartialOwner(name)
) {
// default to bound location
Location l = bound;
// but go to previous room, if possible
if(previousRoom.room.id || previousRoom.mapmarker.getArea())
l = previousRoom;
if(l.room.id)
room = l.room;
else
gConfig->areaInit(this, l.mapmarker);
}
}
// area_room might get set by areaInit, so check again
if(!area_room) {
if(!loadRoom(room, &uRoom)) {
loge("%s: %s (%s) Attempted logon to bad or missing room!\n", name,
getSock()->getHostname().c_str(), room.str().c_str());
// NOTE: Using ::isCt to use the global function, not the local function
broadcast(::isCt, "^y%s: %s (%s) Attempted logon to bad or missing room (normal)!", name,
getSock()->getHostname().c_str(), room.str().c_str());
newRoom = abortFindRoom(this, "init_ply");
uRoom = newRoom->getUniqueRoom();
}
if(uRoom && !isStaff() && !gServer->isRebooting()) {
if( ( uRoom->flagIsSet(R_LOG_INTO_TRAP_ROOM) ||
uRoom->flagIsSet(R_SHOP_STORAGE)
) &&
uRoom->getTrapExit().id &&
!loadRoom(uRoom->getTrapExit(), &uRoom)
) {
broadcast(::isCt, "^y%s: %s (%s) Attempted logon to bad or missing room!", name,
getSock()->getHostname().c_str(), uRoom->getTrapExit().str().c_str());
newRoom = abortFindRoom(this, "init_ply");
uRoom = newRoom->getUniqueRoom();
}
if( uRoom &&
( uRoom->isFull() ||
uRoom->flagIsSet(R_NO_LOGIN) ||
(!isStaff() && uRoom->flagIsSet(R_CONSTRUCTION))
)
) {
newRoom = getRecallRoom().loadRoom(this);
if(!newRoom) {
broadcast(::isCt, "^y%s: %s (%s) Attempted logon to bad or missing room!", name,
getSock()->getHostname().c_str(), getRecallRoom().str().c_str());
newRoom = abortFindRoom(this, "init_ply");
}
uRoom = newRoom->getUniqueRoom();
}
}
if(uRoom) {
uRoom->killMortalObjects();
// this gets assigned just for the sake of broadcast_login;
// it doesnt actually do anything
parent_rom = uRoom;
} else {
area_room = newRoom->getAreaRoom();
}
}
// str[0] = 0;
if(!isDm()) {
loge("%s(L:%d) (%s) %s. Room - %s (Port-%d)\n", name, level,
getSock()->getHostname().c_str(), gServer->isRebooting() ? "reloaded" : "logged on",
getRoom()->fullName().c_str(), Port);
}
if(isStaff())
logn("log.imm", "%s (%s) %s.\n",
name, getSock()->getHostname().c_str(),
gServer->isRebooting() ? "reloaded" : "logged on");
// broadcast
if(!gServer->isRebooting()) {
defineColors();
broadcast_login(this, 1);
}
checkDarkness();
// don't do the actual adding until after broadcast
if(area_room)
addToRoom(area_room);
else
addToRoom(uRoom);
fixLts();
// pet code
cp = first_fol;
while(cp) {
Monster* pet = cp->crt->getMonster();
pet->following = this;
pet->fixLts();
pet->updateAttackTimer();
pet->lasttime[LT_SPELL].interval = 3;
pet->lasttime[LT_SPELL].ltime =
pet->lasttime[LT_MOB_THIEF].ltime = t;
pet->addToRoom(getRoom());
gServer->addActive(pet);
cp = cp->next_tag;
}
if(!gServer->isRebooting())
bug("%s logged into room %s.\n", name, getRoom()->fullName().c_str());
wearCursed();
if(!flagIsSet(P_NO_AUTO_WEAR))
wearAll(this, true);
computeLuck();
update();
Socket* sock = getSock();
if(!gServer->isRebooting()) {
sprintf(file, "%snews.txt", DOCPATH);
viewLoginFile(sock, file);
sprintf(file, "%snewbie_news.txt", DOCPATH);
viewLoginFile(sock, file);
if(isCt()) {
sprintf(file, "%snews.txt", DMHSUBDIR);
viewLoginFile(sock, file);
}
if(isStaff() && strcmp(name, "Bane")) {
sprintf(file, "%snews.txt", BHSUBDIR);
viewLoginFile(sock, file);
}
if(isCt() || flagIsSet(P_WATCHER)) {
sprintf(file, "%swatcher_news.txt", DMHSUBDIR);
viewLoginFile(sock, file);
}
sprintf(file, "%slatest_post.txt", DOCPATH);
viewLoginFile(sock, file, false);
hasNewMudmail();
}
if(!gServer->isRebooting()) {
strcpy(watchers, "");
printColor("^yWatchers currently online: ");
std::pair<bstring, Player*> p;
Player* ply;
foreach(p, gServer->players) {
ply = p.second;
if(!ply->isConnected())
continue;
if(!ply->isPublicWatcher())
continue;
if(!canSee(ply))
continue;
strcat(watchers, ply->name);
strcat(watchers, ", ");
watch++;
}
if(watch) {
watchers[strlen(watchers) - 2] = '.';
watchers[strlen(watchers) - 1] = 0;
printColor("%s\n", watchers);
} else
printColor("None.\n");
}
if(isCt())
showGuildsNeedingApproval(this);
if(hp.getCur() < 0)
hp.setCur(1);
// only players and builders are effected
if(!isCt()) {
// players cant set eavesdropper flag anymore
clearFlag(P_EAVESDROPPER);
clearFlag(P_SUPER_EAVESDROPPER);
// DM/CT only flag
clearFlag(P_NO_COMPUTE);
setMonkDice();
if(lasttime[LT_AGE].ltime > time(0) ) {
lasttime[LT_AGE].ltime = time(0);
lasttime[LT_AGE].interval = 0;
broadcast(::isCt, "^yPlayer %s had negative age and is now validated.\n", name);
logn("log.validate", "Player %s had negative age and is now validated.\n", name);
}
}
// Make sure everything is in order with their guild.
if(guild) {
if(!gConfig->getGuild(guild)) {
if(guildRank >= GUILD_PEON)
print("You are now guildless because your guild has been disbanded.\n");
guild = guildRank = 0;
} else if(!gConfig->getGuild(guild)->isMember(name)) {
guild = guildRank = 0;
}
}
if(actual_level < level)
actual_level = MAX(level, actual_level);
killDarkmetal();
if(weaponTrains != 0)
print("You have %d weapon train(s) available!\n", weaponTrains);
computeInterest(t, true);
resetObjectIds();
}
//*********************************************************************
// uninit
//*********************************************************************
// This function de-initializes a player who has left the game. This
// is called whenever a player quits or disconnects, right before save
// is called.
void Player::uninit() {
Creature* target=0;
ctag *cp=0, *prev=0;
int i=0;
long t=0;
char str[50];
// Save storage rooms
if(parent_rom && parent_rom->info.isArea("stor"))
gConfig->resaveRoom(parent_rom->info);
courageous();
clearMaybeDueling();
cp = first_fol;
while(cp) {
// pet code
if(cp->crt->isPet()) {
Monster* pet = cp->crt->getMonster();
gServer->delActive(pet);
pet->deleteFromRoom();
free_crt(pet);
cp->crt = 0;
cp = cp->next_tag;
continue;
}
cp->crt->following = 0;
if(cp->crt->isPlayer() && !gServer->isRebooting())
cp->crt->print("You stop following %s.\n", name);
prev = cp->next_tag;
cp->crt = NULL;
cp = prev;
}
while(first_fol && first_fol->crt == NULL) {
prev = first_fol;
first_fol = first_fol->next_tag;
delete prev;
}
cp = first_fol;
while(cp) {
if(cp->next_tag == NULL)
break;
if(cp->next_tag->crt == NULL) {
prev = cp->next_tag;
cp->next_tag = cp->next_tag->next_tag;
delete prev;
continue;
}
cp = cp->next_tag;
}
if(following) {
target = following;
cp = target->first_fol;
if(cp->crt == this) {
target->first_fol = cp->next_tag;
delete cp;
} else
while(cp) {
if(cp->crt == this) {
prev->next_tag = cp->next_tag;
delete cp;
break;
}
prev = cp;
cp = cp->next_tag;
}
following = 0;
if(!isStaff() && !gServer->isRebooting())
target->print("%s stops following you.\n", name);
}
for(i=0; i<MAXWEAR; i++) {
if(ready[i]) {
// i is wearloc-1, so add 1
unequip(i+1);
}
}
if(!gServer->isRebooting())
broadcast_login(this, 0);
if(parent_rom || area_room)
deleteFromRoom();
t = time(0);
strcpy(str, (char *)ctime(&t));
str[strlen(str)-1] = 0;
if(!isDm() && !gServer->isRebooting())
loge("%s logged off.\n", name);
// Clean up the old "Extra" struct
etag *crm, *ctemp;
crm = first_charm;
while(crm) {
ctemp = crm;
crm = crm->next_tag;
delete ctemp;
}
first_charm = 0;
if(alias_crt) {
alias_crt->clearFlag(M_DM_FOLLOW);
alias_crt = 0;
}
}
//*********************************************************************
// courageous
//*********************************************************************
void Player::courageous() {
int **scared;
scared = &(scared_of);
if(*scared) {
free(*scared);
*scared = NULL;
}
}
//*********************************************************************
// checkTempEnchant
//*********************************************************************
void Player::checkTempEnchant( Object* object) {
long i=0, t=0;
if(object && object->flagIsSet(O_TEMP_ENCHANT)) {
t = time(0);
i = LT(object, LT_ENCHA);
if(i < t) {
object->setArmor(MAX(0, object->getArmor() - object->getAdjustment()));
object->setAdjustment(0);
object->clearFlag(O_TEMP_ENCHANT);
object->clearFlag(O_RANDOM_ENCHANT);
if(isEffected("detect-magic"))
printColor("The enchantment on your %s fades.\n", object->name);
}
}
}
//*********************************************************************
// checkEnvenom
//*********************************************************************
void Player::checkEnvenom( Object* object) {
long i=0, t=0;
if(object && object->flagIsSet(O_ENVENOMED)) {
t = time(0);
i = LT(object, LT_ENVEN);
if(i < t) {
object->clearFlag(O_ENVENOMED);
object->clearEffect();
printColor("The poison on your %s deludes.\n", object->name);
}
}
}
//*********************************************************************
// checkInventory
//*********************************************************************
// Check inventory for temp enchant or envenom
void Player::checkInventory( ) {
otag *op = first_obj, *cop=0;
int i=0;
// Check for temp enchant items carried/inventory/in containers
while(op) {
if(op->obj->getType() == CONTAINER) {
cop=op->obj->first_obj;
while(cop) {
checkTempEnchant(cop->obj);
cop=cop->next_tag;
}
}
checkTempEnchant(op->obj);
op=op->next_tag;
}
for(i=0; i<MAXWEAR; i++) {
if(i==(HELD-1) && ready[i] && ready[i]->getType()==CONTAINER) {
cop=ready[i]->first_obj;
while(cop) {
checkTempEnchant(cop->obj);
cop=cop->next_tag;
}
}
checkTempEnchant(ready[i]);
}
}
//*********************************************************************
// checkEffectsWearingOff
//*********************************************************************
void Player::checkEffectsWearingOff() {
long t = time(0);
int staff = isStaff();
// Added P_STUNNED and LT_PLAYER_STUNNED stun for dodge code.
if(flagIsSet(P_STUNNED)) {
if(t > LT(this, LT_PLAYER_STUNNED)) {
clearFlag(P_STUNNED);
}
}
if(flagIsSet(P_FRENZY)) {
if(t > LT(this, LT_FRENZY)) {
loseFrenzy();
}
}
if(flagIsSet(P_FOCUSED) && (cClass == MONK || staff)) {
if(t > LT(this, LT_FOCUS)) {
printColor("^cYou lose your concentration.\n");
clearFlag(P_FOCUSED);
//computeAC();
computeAttackPower();
}
}
if(flagIsSet(P_MISTBANE)) {
if(t > LT(this, LT_FOCUS)) {
printColor("^bYour mistbane is ended.\n");
clearFlag(P_MISTBANE);
}
}
if(flagIsSet(P_OUTLAW)) {
if(t > LT(this, LT_OUTLAW)) {
printColor("^yYou are no longer an outlaw.\n");
clearFlag(P_OUTLAW);
clearFlag(P_OUTLAW_WILL_BE_ATTACKED);
setFlag(P_NO_SUMMON);
clearFlag(P_OUTLAW_WILL_LOSE_XP);
clearFlag(P_NO_GET_ALL);
}
}
// only force them to wake from sleeping unconsciousness when they're
// completely healed.
if(flagIsSet(P_UNCONSCIOUS)) {
if( ( !flagIsSet(P_SLEEPING) &&
t > LT(this, LT_UNCONSCIOUS)
) ||
( flagIsSet(P_SLEEPING) && (
(hp.getCur() >= hp.getMax() && mp.getCur() >= mp.getMax()) ||
(cClass == VAMPIRE && !getRoom()->vampCanSleep(getSock()))
) )
) {
printColor("^cYou wake up.\n");
clearFlag(P_UNCONSCIOUS);
wake();
clearFlag(P_DIED_IN_DUEL);
broadcast(getSock(), getRoom(), "%M wakes up.", this);
}
}
if(flagIsSet(P_RUNNING)) {
if(t > LT(this, LT_ENDURANCE)) {
clearFlag(P_RUNNING);
}
}
if(flagIsSet(P_PRAYED)) {
if(t > LT(this, LT_PRAY)) {
losePray();
}
}
if(flagIsSet(P_BLOODSAC)) {
if(t > LT(this, LT_BLOOD_SACRIFICE)) {
printColor("^mYour demonic power leaves you.\n");
clearFlag(P_BLOODSAC);
hp.setCur(hp.getMax());
}
}
if(flagIsSet(P_NO_SUICIDE)) {
if(t > LT(this, LT_MOBDEATH)) {
printColor("^yYour cooling-off period has ended.\n");
clearFlag(P_NO_SUICIDE);
}
}
if(flagIsSet(P_ANCHOR)) {
if(t > LT(this, LT_ANCHOR)) {
printColor("^cYou are no longer magically anchored.\n");
clearFlag(P_ANCHOR);
}
}
if(flagIsSet(P_BERSERKED)) {
if(t > LT(this, LT_BERSERK) || (LT(this, LT_BERSERK) - t <=0)) {
loseRage();
}
}
if(flagIsSet(P_HIDDEN) && !staff) {
if(t - lasttime[LT_HIDE].ltime > 300L) {
printColor("^cShifting shadows expose you.\n");
unhide(false);
}
}
if(flagIsSet(P_FREE_ACTION)) {
if(t > LT(this, LT_FREE_ACTION)) {
printColor("^c^#You no longer magically move freely.\n");
clearFlag(P_FREE_ACTION);
computeAC();
computeAttackPower();
}
}
if(flagIsSet(P_NO_PKILL)) {
if(t > LT(this, LT_NO_PKILL)) {
printColor("^c^#You can now be pkilled again.\n");
clearFlag(P_NO_PKILL);
}
}
if(flagIsSet(P_DOCTOR_KILLER)) {
if(t > LT(this, LT_KILL_DOCTOR) || staff) {
printColor("^y^#The doctors have forgiven you.\n");
clearFlag(P_DOCTOR_KILLER);
}
}
if(flagIsSet(P_NO_TICK_MP)) {
if(t > LT(this, LT_NOMPTICK) || staff) {
printColor("^cYour magical vitality has returned.\n");
clearFlag(P_NO_TICK_MP);
}
}
if(flagIsSet(P_NO_TICK_HP)) {
if(t > LT(this, LT_NOHPTICK) || staff) {
printColor("^gYou now heal normally again.\n");
clearFlag(P_NO_TICK_HP);
}
}
if(flagIsSet(P_CANT_BROADCAST)) {
if(t > LT(this, LT_NO_BROADCAST)) {
printColor("^rYou can broadcast again, now don't abuse it this time.\n");
clearFlag(P_CANT_BROADCAST);
}
}
if(flagIsSet(P_MISTED)) {
if(isDay() && !staff)
unmist();
}
if(flagIsSet(P_CHARMED)) {
if(t > LT(this, LT_CHARMED) || staff) {
printColor("^yYou are again in control of your actions.\n");
clearFlag(P_CHARMED);
}
}
if(negativeLevels) {
if(t > LT(this, LT_LEVEL_DRAIN) || staff) {
long expTemp=0;
negativeLevels--;
if(negativeLevels) {
printColor("^WYou have regained a lost level.\n");
expTemp = experience;
upLevel();
experience = expTemp;
lasttime[LT_LEVEL_DRAIN].ltime = t;
lasttime[LT_LEVEL_DRAIN].interval = 60L + 5*bonus((int)constitution.getCur());
} else {
printColor("^WYou have recovered all your lost levels.\n");
expTemp = experience;
upLevel();
experience = expTemp;
}
}
}
if(t > LT(this, LT_JAILED) && flagIsSet(P_JAILED)) {
printColor("^rA demonic jailer just arrived.\n");
printColor("The demonic jailer says, \"You have been released from your torment.\"\n");
printColor("The demonic jailer casts word of recall on you.\n");
broadcast(getSock(), getRoom(), "A demonic jailer just arrived.\nThe demonic jailer casts word of recall on %s.", name);
broadcast(getSock(), getRoom(), "The demonic jailer sneers evilly and spits on you.\nThe demonic jailer vanishes.");
broadcast("^R### Cackling demons shove %s from the Dungeon of Despair.", name);
doRecall();
clearFlag(P_JAILED);
}
if( t > LT(this, LT_JAILED) &&
parent_rom && parent_rom->flagIsSet(R_MOB_JAIL) &&
!staff
) {
printColor("A jailer just arrived.\n");
printColor("The jailer says, \"You're free to go...get out!\"\n");
printColor("The jailer opens the cell door and shoves you out.\n");
printColor("The jailer goes back to napping.\n");
doRecall();
}
}
//*********************************************************************
// doPetrificationDmg
//*********************************************************************
bool Creature::doPetrificationDmg() {
if(!isEffected("petrification"))
return(false);
wake("Terrible nightmares disturb your sleep!");
printColor("^c^#Petrification spreads toward your heart.\n");
hp.decrease(MAX(1,(hp.getMax()/15 - bonus((int)constitution.getCur()))));
if(hp.getCur() < 1) {
Player* pThis = getPlayer();
if(pThis)
pThis->die(PETRIFIED);
else
die(this);
return(true);
}
return(false);
}
//*********************************************************************
// update
//*********************************************************************
// This function checks up on all a player's time-expiring flags to see
// if some of them have expired. If so, flags are set accordingly.
void Player::update() {
BaseRoom* room=0;
long t=0;
int item=0;
bool fighting = inCombat();
t = time(0);
lasttime[LT_AGE].interval += (t - lasttime[LT_AGE].ltime);
lasttime[LT_AGE].ltime = t;
checkInventory();
if(flagIsSet(P_LAG_PROTECTION_SET) && flagIsSet(P_LAG_PROTECTION_ACTIVE) && level > 1) {
// Suspends lag protect if this not in battle.
if(!fighting)
clearFlag(P_LAG_PROTECTION_ACTIVE);
}
// All mobs will not attack a petrified opponent.
if(isEffected("petrification"))
clearAsEnemy();
if(flagIsSet(P_UNIQUE_TO_DECAY) && !fighting) {
gConfig->uniqueDecay(this);
clearFlag(P_UNIQUE_TO_DECAY);
}
checkEffectsWearingOff();
if(isDiseased() && immuneToDisease())
cureDisease();
if(isPoisoned() && immuneToPoison())
curePoison();
//pulseEffects(t);
if(mp.getCur() < 0)
mp.setCur(0);
room = getRoom();
if(room && !flagIsSet(P_LINKDEAD))
doRoomHarms(room, this);
if(t > LT(this, LT_PLAYER_SAVE)) {
lasttime[LT_PLAYER_SAVE].ltime = t;
cmdSave(this, 0);
}
item = getLight();
if(item && item != MAXWEAR+1) {
if(ready[item-1]->getType() == LIGHTSOURCE) {
ready[item-1]->decShotscur();
if(ready[item-1]->getShotscur() < 1) {
print("Your %s died out.\n", ready[item-1]->name);
broadcast(getSock(), room, "%M's %s died out.", this, ready[item-1]->name);
}
}
}
// Fix funky stuff
setMonkDice();
computeAC();
computeAttackPower();
}
//*********************************************************************
// checkWeaponSkillGain
//*********************************************************************
void Player::checkWeaponSkillGain() {
int numWeapons = 0;
// Everyone gets a new weapon skill every title
if(((level+2)%3) == 0)
numWeapons ++;
switch(cClass) {
case FIGHTER:
if(!cClass2) {
if(level%4 == 0)
numWeapons++;
} else {
// Mutli fighters get weapon skills like other fighting classes
if(level%8 == 8)
numWeapons++;
}
break;
case BERSERKER:
if(level%6)
numWeapons++;
break;
case THIEF:
case RANGER:
case ROGUE:
case BARD:
case PALADIN:
case DEATHKNIGHT:
case ASSASSIN:
if(level/8)
numWeapons++;
break;
case CLERIC:
case DRUID:
case VAMPIRE:
if(cClass2) {
// Cle/Ass
if(level/8)
numWeapons++;
} else {
if(level/12)
numWeapons++;
}
break;
case MAGE:
if(cClass2) {
if(level/12)
numWeapons++;
}
break;
default:
break;
}
if(numWeapons != 0) {
weaponTrains += numWeapons;
print("You can now learn %d more weapon skill(s)!\n", numWeapons);
}
}
//*********************************************************************
// upLevel
//*********************************************************************
// This function should be called whenever a player goes up a level.
// It raises there hit points and magic points appropriately, and if
// it is initializing a new character, it sets up the character.
// TODO: Make this function use the information loaded from classes.xml for hp/mp/saves etc
void Player::upLevel() {
int a=0;
int switchNum=0;
bool relevel=false;
if(isStaff()) {
level++;
return;
}
PlayerClass *pClass = gConfig->classes[getClassString()];
const RaceData* rData = gConfig->getRace(race);
LevelGain *lGain = 0;
if(level == actual_level)
actual_level++;
else
relevel = true;
level++;
// Check for level info
if(!pClass) {
print("Error: Can't find your class!\n");
if(!isStaff()) {
bstring errorStr = "Error: Can't find class: " + getClassString();
merror(errorStr.c_str(), FATAL);
}
return;
} else {
print("Checking Leveling Information for [%s:%d].\n", pClass->getName().c_str(), level);
lGain = pClass->getLevelGain(level);
if(!lGain && level != 1) {
print("Error: Can't find any information for your level!\n");
if(!isStaff()) {
bstring errorStr = "Error: Can't find level info for " + getClassString() + level;
merror(errorStr.c_str(), FATAL);
}
return;
}
}
if(!relevel) {
// Only check weapon gains on a real level
checkWeaponSkillGain();
}
if(level == 1) {
hp.setMax(pClass->getBaseHp());
if(cClass == FIGHTER && flagIsSet(P_PTESTER))
focus.setMax(2);
else if(cClass != BERSERKER && cClass != LICH)
mp.setMax(pClass->getBaseMp());
// TODO: Dom: HC: hardcore characters get double at level 1
//if(isHardcore()) {
// hp.setMax(hp.getMax()*2);
// mp.setMax(mp.getMax()*2);
//}
hp.restore();
mp.restore();
damage = pClass->damage;
if(!rData) {
print("Error: Can't find your race, no saving throws for you!\n");
} else {
saves[POI].chance = rData->getSave(POI);
saves[DEA].chance = rData->getSave(DEA);
saves[BRE].chance = rData->getSave(BRE);
saves[MEN].chance = rData->getSave(MEN);
saves[SPL].chance = rData->getSave(SPL);
setFlag(P_SAVING_THROWSPELL_LEARN);
checkSkillsGain(rData->getSkillBegin(), rData->getSkillEnd());
}
// Base Skills
if(!pClass) {
print("Error: Can't find your class, no skills for you!\n");
} else {
checkSkillsGain(pClass->getSkillBegin(), pClass->getSkillEnd());
}
} else {
hp.increaseMax(lGain->getHp());
if(cClass == FIGHTER && flagIsSet(P_PTESTER))
focus.increaseMax(2);
else if(cClass != BERSERKER && cClass != LICH)
mp.increaseMax(lGain->getMp());
// idx = (level - 2) % 10;
switchNum = lGain->getStat();
// if(!cClass2)
// switchNum = level_cycle[(int) cClass][idx];
// else
// switchNum = multiStatCycle[getMultiClassID(cClass, cClass2)][idx];
switch(switchNum) {
case STR:
strength.addCur(10);
strength.addMax(10);
print("You have become stronger.\n");
break;
case DEX:
dexterity.addCur(10);
dexterity.addMax(10);
print("You have become more dextrous.\n");
break;
case CON:
constitution.addCur(10);
constitution.addMax(10);
print("You have become healthier.\n");
break;
case INT:
intelligence.addCur(10);
intelligence.addMax(10);
print("You have become more intelligent.\n");
break;
case PTY:
piety.addCur(10);
piety.addMax(10);
print("You have become more pious.\n");
break;
}
switchNum=0;
switchNum = lGain->getSave();
// idx = (level - 2) % 10;
// if(!cClass2)
// switchNum = saving_throw_cycle[(int) cClass][idx];
// else
// switchNum = multiSaveCycle[getMultiClassID(cClass, cClass2)][idx];
switch (switchNum) {
case POI:
saves[POI].chance += 3;
print("You are now more resistant to poison.\n");
break;
case DEA:
saves[DEA].chance += 3;
print("You are now more able to resist traps and death.\n");
break;
case BRE:
saves[BRE].chance += 3;
print("You are now more resistant to breath weapons and explosions.\n");
break;
case MEN:
saves[MEN].chance += 3;
print("You are now more resistant to mind dominating attacks.\n");
break;
case SPL:
saves[SPL].chance += 3;
print("You are now more resistant to magical spells.\n");
break;
}
if(!relevel) {
// Saving throw bug fix: Spells and mental saving throws will now be
// properly reset so they can increase like the other ones -Bane
for(a=POI; a<= SPL;a++)
saves[a].gained = 0;
}
// Give out skills here
if(lGain->hasSkills()) {
print("Ok you should have some skills, seeing what they are.\n");
checkSkillsGain(lGain->getSkillBegin(), lGain->getSkillEnd());
} else {
print("No skills for you at this level.\n");
}
}
// Make constitution actualy worth something
if(cClass != LICH) {
if(cClass == BERSERKER && constitution.getCur() >= 70)
hp.increaseMax(1);
if(constitution.getCur() >= 130)
hp.increaseMax(1);
if(constitution.getCur() >= 210)
hp.increaseMax(1);
if(constitution.getCur() >= 250)
hp.increaseMax(1);
}
if(!negativeLevels) {
hp.restore();
mp.restore();
}
if(cClass == LICH && !relevel) {
if(level == 7)
print("Your body has deteriorated slightly.\n");
if(level == 13)
print("Your body has decayed immensely.\n");
if(level == 19)
print("What's left of your flesh hangs on your bones.\n");
if(level == 25)
print("You are now nothing but a dried up and brittle set of bones.\n");
}
if((cClass == MAGE || cClass2 == MAGE) && level == 7 && !relevel) {
print("You have learned the armor spell.\n");
learnSpell(S_ARMOR);
}
if(cClass == CLERIC && level >= 13 &&
deity == CERIS && !spellIsKnown(S_REJUVENATE)) {
print("%s has granted you the rejuvinate spell.\n", gConfig->getDeity(deity)->getName().c_str());
learnSpell(S_REJUVENATE);
}
if( cClass == CLERIC &&
level >= 19 &&
deity == CERIS &&
!spellIsKnown(S_RESURRECT)
) {
print("%s has granted you the resurrect spell.\n", gConfig->getDeity(deity)->getName().c_str());
learnSpell(S_RESURRECT);
}
if( cClass == CLERIC &&
!cClass2 &&
level >= 22 &&
deity == ARAMON &&
!spellIsKnown(S_BLOODFUSION)
) {
print("%s has granted you the bloodfusion spell.\n", gConfig->getDeity(deity)->getName().c_str());
learnSpell(S_BLOODFUSION);
}
updateGuild(this, GUILD_LEVEL);
update();
// getting [custom] as a title lets you pick a new one,
// even if you have already picked one.
if(!relevel) {
}
/* if(cClass == BARD)
pick_song(player);*/
}
//*********************************************************************
// downLevel
//*********************************************************************
// This function is called when a player loses a level due to dying or
// for some other reason. The appropriate stats are downgraded.
void Player::downLevel() {
if(isStaff()) {
level--;
return;
}
int switchNum=0;
PlayerClass *pClass = gConfig->classes[getClassString()];
LevelGain *lGain = 0;
// Check for level info
if(!pClass) {
print("Error: Can't find your class!\n");
if(!isStaff()) {
return;
//bstring errorStr = "Error: Can't find class: " + getClassString();
//merror(errorStr.c_str(), FATAL);
}
return;
} else {
//print("Checking Leveling Information for [%s:%d].\n", pClass->getName().c_str(), level);
lGain = pClass->getLevelGain(level);
if(!lGain) {
return;
//print("Error: Can't find any information for your level!\n");
//bstring errorStr = "Error: Can't find level info for " + getClassString() + level;
//merror(errorStr.c_str(), FATAL);
}
}
level--;
hp.decreaseMax(lGain->getHp());
if(cClass == FIGHTER && flagIsSet(P_PTESTER))
focus.decreaseMax(2);
else if(cClass != BERSERKER && cClass != LICH)
mp.decreaseMax(lGain->getMp());
// if(!cClass2) {
// hp.decreaseMax(class_stats[(int) cClass].hp);
//
// if(cClass == FIGHTER && flagIsSet(P_PTESTER))
// focus.decreaseMax(2);
// else if(cClass != BERSERKER && cClass != LICH)
// mp.decreaseMax(class_stats[(int) cClass].mp);
//
// } else {
// hp.decreaseMax(multiHpMpAdj[getMultiClassID(cClass, cClass2)][0]);
// mp.decreaseMax(multiHpMpAdj[getMultiClassID(cClass, cClass2)][1]);
// }
//
//
// if(((level % 2) !=0) && cClass == LICH) // liches lose a HP at every odd level.
// hp.decreaseMax(1);
// Make constitution actualy worth something
if(cClass != LICH) {
if(cClass == BERSERKER && constitution.getCur() >= 70)
hp.decreaseMax(1);
if(constitution.getCur() >= 130)
hp.decreaseMax(1);
if(constitution.getCur() >= 210)
hp.decreaseMax(1);
if(constitution.getCur() >= 250)
hp.decreaseMax(1);
}
hp.restore();
if(cClass != LICH)
mp.restore();
// idx = (level - 1) % 10;
// if(!cClass2)
// switchNum = level_cycle[(int) cClass][idx];
// else
// switchNum = multiStatCycle[getMultiClassID(cClass, cClass2)][idx];
switchNum = lGain->getStat();
switch (switchNum) {
case STR:
strength.addCur(-10);
strength.addMax(-10);
print("You have lost strength.\n");
break;
case DEX:
dexterity.addCur(-10);
dexterity.addMax(-10);
print("You have lost dexterity.\n");
break;
case CON:
constitution.addCur(-10);
constitution.addMax(-10);
print("You have lost constitution.\n");
break;
case INT:
intelligence.addCur(-10);
intelligence.addMax(-10);
print("You have lost intelligence.\n");
break;
case PTY:
piety.addCur(-10);
piety.addMax(-10);
print("You have lost piety.\n");
break;
}
// if(isCt() || flagIsSet(P_PTESTER)) {
// idx = (level - 1) % 10;
// if(!cClass2)
// switchNum = saving_throw_cycle[(int) cClass][idx];
// else
// switchNum = multiSaveCycle[getMultiClassID(cClass, cClass2)][idx];
switchNum = lGain->getSave();
switch (switchNum) {
case POI:
saves[POI].chance -= 3;
if(!negativeLevels)
saves[POI].gained = 0;
print("You are now less resistant to poison.\n");
break;
case DEA:
saves[DEA].chance -= 3;
if(!negativeLevels)
saves[DEA].gained = 0;
print("You are now less able to resist traps and death.\n");
break;
case BRE:
saves[BRE].chance -= 3;
if(!negativeLevels)
saves[BRE].gained = 0;
print("You are now less resistant to breath weapons and explosions.\n");
break;
case MEN:
saves[MEN].chance -= 3;
if(!negativeLevels)
saves[MEN].gained = 0;
print("You are now less resistant to mind dominating attacks.\n");
break;
case SPL:
saves[SPL].chance -= 3;
if(!negativeLevels)
saves[SPL].gained = 0;
print("You are now less resistant to magical spells.\n");
break;
}
// }
updateGuild(this, GUILD_DIE);
setMonkDice();
}
//*********************************************************************
// addObj
//*********************************************************************
// This function adds the object pointer to by the first parameter to
// the inventory of the player pointed to by the second parameter.
void Creature::addObj(Object* object, bool resetUniqueId) {
otag *op=0, *temp=0, *prev=0;
Player* pPlayer = getPlayer();
if(resetUniqueId && pPlayer)
pPlayer->setObjectId(object);
hooks.execute("beforeAddObject", object);
object->hooks.execute("beforeAddObject", this);
object->parent_crt = this;
object->parent_obj = 0;
object->parent_room = 0;
object->clearFlag(O_JUST_LOADED);
// players have big inventories; to keep the mud from searching them when it
// doesnt need to, record a flag on the player
if(pPlayer && object->flagIsSet(O_DARKMETAL))
setFlag(P_DARKMETAL);
if(object->flagIsSet(O_DARKNESS))
setFlag(pPlayer ? P_DARKNESS : M_DARKNESS);
op = new otag;
if(!op)
merror("add_obj_crt", FATAL);
op->obj = object;
op->next_tag = 0;
if(!first_obj) {
first_obj = op;
} else {
temp = first_obj;
if( strcmp(temp->obj->cmpName(), object->cmpName()) > 0 ||
(!strcmp(temp->obj->name, object->name) &&
temp->obj->getAdjustment() > object->getAdjustment())
) {
op->next_tag = temp;
first_obj = op;
} else {
while(temp) {
if( strcmp(temp->obj->cmpName(), object->cmpName()) > 0 ||
(!strcmp(temp->obj->name, object->name) &&
temp->obj->getAdjustment() > object->getAdjustment())
)
break;
prev = temp;
temp = temp->next_tag;
}
op->next_tag = prev->next_tag;
prev->next_tag = op;
}
}
if(pPlayer)
pPlayer->updateItems(object);
hooks.execute("afterAddObject", object);
object->hooks.execute("afterAddObject", this);
killDarkmetal();
}
//*********************************************************************
// delObj
//*********************************************************************
// This function removes the object pointer to by the first parameter
// from the player pointed to by the second. This does NOT DELETE THE
// OBJECT. You will have to do that yourself, if desired.
// we put in a choice to do darkmetal or not so we wont have
// recursion problems
// if you pass false to darkness, you MUST run checkDarkness() unless
// you are certain the item you are deleted isn't flagged O_DARKNESS
void Creature::finishDelObj(Object* object, bool breakUnique, bool removeUnique, bool darkmetal, bool darkness, bool keep) {
if(darkmetal)
killDarkmetal();
if(breakUnique || removeUnique) {
Player* player = getMaster();
if(player) {
if(breakUnique)
Limited::remove(player, object);
else if(removeUnique)
Limited::deleteOwner(player, object);
}
}
if(darkness)
checkDarkness();
if(!keep)
object->clearFlag(O_KEEP);
hooks.execute("afterDeleteFromObject", object);
object->hooks.execute("afterDeleteFromObject", this);
}
void Creature::delObj(Object* object, bool breakUnique, bool removeUnique, bool darkmetal, bool darkness, bool keep) {
otag *temp=0, *prev=0;
hooks.execute("beforeDeleteFromObject", object);
object->hooks.execute("beforeDeleteFromObject", this);
// dont run checkDarkness if this isnt a dark item
if(!object->flagIsSet(O_DARKNESS))
darkness = false;
object->clearFlag(O_BEING_PREPARED);
object->clearFlag(O_HIDDEN);
object->clearFlag(O_JUST_LOADED);
// if it doesnt have a parent_crt, it's either being worn or is in a bag
if(!object->parent_crt) {
// the object is being worn
if(object->getWearflag() && ready[object->getWearflag()-1] == object) {
ready[object->getWearflag()-1] = 0;
finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
} else {
// the object is in a bag somewhere
// problem is, we don't know which bag
temp = first_obj;
while(temp) {
if(temp->obj->getType() == CONTAINER) {
prev = temp->obj->first_obj;
while(prev) {
if(prev->obj == object) {
del_obj_obj(object, temp->obj);
finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
return;
}
prev = prev->next_tag;
}
}
temp = temp->next_tag;
}
// not in their inventory? they must be wearing a bag
for(int i=0; i < MAXWEAR; i++) {
if(!ready[i])
continue;
if(ready[i]->getType() == CONTAINER) {
prev = ready[i]->first_obj;
while(prev) {
if(prev->obj == object) {
del_obj_obj(object, ready[i]);
finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
return;
}
prev = prev->next_tag;
}
}
}
}
return;
}
object->parent_crt = 0;
if(first_obj->obj == object) {
temp = first_obj->next_tag;
delete first_obj;
first_obj = temp;
finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
return;
}
prev = first_obj;
temp = prev->next_tag;
while(temp) {
if(temp->obj == object) {
prev->next_tag = temp->next_tag;
delete temp;
finishDelObj(object, breakUnique, removeUnique, darkmetal, darkness, keep);
return;
}
prev = temp;
temp = temp->next_tag;
}
}
//*********************************************************************
// computeAC
//*********************************************************************
// This function computes a player's armor class by
// examining their stats and the items they are holding.
void Player::computeAC() {
int ac=0, i;
// Monks are a little more impervious to damage than other classes due to
// their practice in meditation
if(cClass == MONK)
ac += level * 10;
// Wolves have a tough skin that grows tougher as they level up
if(isEffected("lycanthropy"))
ac += level * 20;
// Vamps have a higher armor at night
if(isEffected("vampirism") && !isDay())
ac += level * 5 / 2;
for(i=0; i<MAXWEAR; i++) {
if(ready[i] && ready[i]->getType() == ARMOR) {
ac += ready[i]->getArmor();
// penalty for wearing armor of the wrong size
if(size && ready[i]->getSize() && size != ready[i]->getSize())
ac -= ready[i]->getArmor()/2;
if(ready[i]->getWearflag() < FINGER || ready[i]->getWearflag() > HELD)
ac += (int)(ready[i]->getAdjustment()*4.4);
}
}
if((cClass == DRUID || isCt()) && isEffected("barkskin")) {
EffectInfo* barkskin = getEffect("barkskin");
ac += (int)(((level+bonus(constitution.getCur())) * barkskin->getStrength())*4.4);
}
if(isEffected("armor"))
ac += 200 + 200 * level /30;
// TODO: Possibly add in +armor for higher constitution?
if(flagIsSet(P_BERSERKED))
ac -= 100;
if(isEffected("fortitude"))
ac += 100;
if(isEffected("weakness"))
ac -= 100;
armor = MAX(0, ac);
}
//*********************************************************************
// alignAdjustAcThaco
//*********************************************************************
// This function will be called whenever alignment changes,
// primarily in combat after killing a mob. It is initially
// designed to be only for monks and werewolves, but it can
// be applied to any class by altering the function. -TC
void Player::alignAdjustAcThaco() {
if(cClass != MONK && cClass != WEREWOLF)
return;
computeAC();
computeAttackPower();
}
//*********************************************************************
// getArmorWeight
//*********************************************************************
int Player::getArmorWeight() const {
int weight=0;
for(int i=0; i<MAXWEAR; i++) {
if( ready[i] && ready[i]->getType() == ARMOR &&
( (ready[i]->getWearflag() < FINGER) ||
(ready[i]->getWearflag() > HELD)
)
)
weight += ready[i]->getActualWeight();
}
return(weight);
}
//*********************************************************************
// mprofic
//*********************************************************************
// This function returns the magical realm proficiency as a percentage
int mprofic(const Creature* player, int index) {
const Player *pPlayer = player->getConstPlayer();
long prof_array[12];
int i=0, n=0, prof=0;
switch(player->getClass()) {
case MAGE:
if(pPlayer && (pPlayer->getSecondClass() == ASSASSIN || pPlayer->getSecondClass() == THIEF)) {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 4092L;
prof_array[3] = 8192L;
prof_array[4] = 16384L;
prof_array[5] = 32768L;
prof_array[6] = 70536L;
prof_array[7] = 119000L;
prof_array[8] = 226410L;
prof_array[9] = 709410L;
prof_array[10] = 2973307L;
prof_array[11] = 500000000L;
} else {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 2048L;
prof_array[3] = 4096L;
prof_array[4] = 8192L;
prof_array[5] = 16384L;
prof_array[6] = 35768L;
prof_array[7] = 85536L;
prof_array[8] = 140000L;
prof_array[9] = 459410L;
prof_array[10] = 2073306L;
prof_array[11] = 500000000L;
}
break;
case LICH:
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 2048L;
prof_array[3] = 4096L;
prof_array[4] = 8192L;
prof_array[5] = 16384L;
prof_array[6] = 35768L;
prof_array[7] = 85536L;
prof_array[8] = 140000L;
prof_array[9] = 459410L;
prof_array[10] = 2073306L;
prof_array[11] = 500000000L;
break;
case CLERIC:
if(pPlayer && (pPlayer->getSecondClass() == ASSASSIN || pPlayer->getSecondClass() == FIGHTER)) {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 8192L;
prof_array[3] = 16384L;
prof_array[4] = 32768L;
prof_array[5] = 65536L;
prof_array[6] = 105000L;
prof_array[7] = 165410L;
prof_array[8] = 287306L;
prof_array[9] = 809410L;
prof_array[10] = 3538232L;
prof_array[11] = 500000000L;
} else {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 4092L;
prof_array[3] = 8192L;
prof_array[4] = 16384L;
prof_array[5] = 32768L;
prof_array[6] = 70536L;
prof_array[7] = 119000L;
prof_array[8] = 226410L;
prof_array[9] = 709410L;
prof_array[10] = 2973307L;
prof_array[11] = 500000000L;
}
break;
case THIEF:
if(pPlayer && pPlayer->getSecondClass() == MAGE) {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 8192L;
prof_array[3] = 16384L;
prof_array[4] = 32768L;
prof_array[5] = 65536L;
prof_array[6] = 105000L;
prof_array[7] = 165410L;
prof_array[8] = 287306L;
prof_array[9] = 809410L;
prof_array[10] = 3538232L;
prof_array[11] = 500000000L;
} else {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 40000L;
prof_array[3] = 80000L;
prof_array[4] = 120000L;
prof_array[5] = 160000L;
prof_array[6] = 205000L;
prof_array[7] = 222000L;
prof_array[8] = 380000L;
prof_array[9] = 965410L;
prof_array[10] = 5495000;
prof_array[11] = 500000000L;
}
break;
case FIGHTER:
if(pPlayer && pPlayer->getSecondClass() == MAGE) {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 8192L;
prof_array[3] = 16384L;
prof_array[4] = 32768L;
prof_array[5] = 65536L;
prof_array[6] = 105000L;
prof_array[7] = 165410L;
prof_array[8] = 287306L;
prof_array[9] = 809410L;
prof_array[10] = 3538232L;
prof_array[11] = 500000000L;
} else {
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 40000L;
prof_array[3] = 80000L;
prof_array[4] = 120000L;
prof_array[5] = 160000L;
prof_array[6] = 205000L;
prof_array[7] = 222000L;
prof_array[8] = 380000L;
prof_array[9] = 965410L;
prof_array[10] = 5495000;
prof_array[11] = 500000000L;
}
break;
case PALADIN:
case BARD:
case VAMPIRE:
case DRUID:
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 4092L;
prof_array[3] = 8192L;
prof_array[4] = 16384L;
prof_array[5] = 32768L;
prof_array[6] = 70536L;
prof_array[7] = 119000L;
prof_array[8] = 226410L;
prof_array[9] = 709410L;
prof_array[10] = 2973307L;
prof_array[11] = 500000000L;
break;
case DEATHKNIGHT:
case MONK:
case RANGER:
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 8192L;
prof_array[3] = 16384L;
prof_array[4] = 32768L;
prof_array[5] = 65536L;
prof_array[6] = 105000L;
prof_array[7] = 165410L;
prof_array[8] = 287306L;
prof_array[9] = 809410L;
prof_array[10] = 3538232L;
prof_array[11] = 500000000L;
break;
default:
prof_array[0] = 0L;
prof_array[1] = 1024L;
prof_array[2] = 40000L;
prof_array[3] = 80000L;
prof_array[4] = 120000L;
prof_array[5] = 160000L;
prof_array[6] = 205000L;
prof_array[7] = 222000L;
prof_array[8] = 380000L;
prof_array[9] = 965410L;
prof_array[10] = 5495000;
prof_array[11] = 500000000L;
break;
}
n = player->getRealm((Realm)index);
for(i=0; i<11; i++)
if(n < prof_array[i+1]) {
prof = 10*i;
break;
}
prof += ((n - prof_array[i])*10) / (prof_array[i+1] - prof_array[i]);
return(prof);
}
//*********************************************************************
// getFallBonus
//*********************************************************************
// This function computes a player's bonus (or susceptibility) to falling
// while climbing.
int Player::getFallBonus() const {
int fall = bonus((int)dexterity.getCur())*5;
for(int j=0; j<MAXWEAR; j++)
if(ready[j])
if(ready[j]->flagIsSet(O_CLIMBING_GEAR))
fall += ready[j]->damage.getPlus()*3;
return(fall);
}
//*********************************************************************
// lowest_piety
//*********************************************************************
// This function finds the player with the lowest piety in a given room.
// The pointer to that player is returned. In the case of a tie, one of
// them is randomly chosen.
Player* lowest_piety(BaseRoom* room, bool invis) {
Creature* player=0;
ctag *cp = room->first_ply;
int totalpiety=0, pick=0;
if(!cp)
return(0);
while(cp) {
if(cp->crt->flagIsSet(P_HIDDEN) ||
(cp->crt->isInvisible() && !invis) ||
cp->crt->flagIsSet(P_DM_INVIS) ) {
cp = cp->next_tag;
continue;
}
totalpiety += MAX(1, (25 - cp->crt->piety.getCur()));
cp = cp->next_tag;
}
if(!totalpiety)
return(0);
pick = mrand(1, totalpiety);
cp = room->first_ply;
totalpiety = 0;
while(cp) {
if(cp->crt->flagIsSet(P_HIDDEN) ||
(cp->crt->isInvisible() && !invis) ||
cp->crt->flagIsSet(P_DM_INVIS) ) {
cp = cp->next_tag;
continue;
}
totalpiety += MAX(1, (25 - cp->crt->piety.getCur()));
if(totalpiety >= pick) {
player = cp->crt;
break;
}
cp = cp->next_tag;
}
return(player->getPlayer());
}
//*********************************************************************
// hasLight
//*********************************************************************
// This function returns true if the player in the first parameter is
// holding or wearing anything that generates light.
int Player::getLight() const {
int i=0, light=0;
for(i = 0; i < MAXWEAR; i++) {
if (!ready[i])
continue;
if (ready[i]->flagIsSet(O_LIGHT_SOURCE)) {
if ((ready[i]->getType() == LIGHTSOURCE &&
ready[i]->getShotscur() > 0) ||
ready[i]->getType() != LIGHTSOURCE) {
light = 1;
break;
}
}
}
if(light)
return (i + 1);
return(0);
}
//***********************************************************************
// sendPrompt
//***********************************************************************
// This function returns the prompt that the player should be seeing
void Player::sendPrompt() {
bstring toPrint;
if(fd < 0)
return;
// no prompt in this situation
if(flagIsSet(P_SPYING) || flagIsSet(P_READING_FILE))
return;
if(flagIsSet(P_PROMPT) || flagIsSet(P_ALIASING)) {
std::ostringstream promptStr;
if(!flagIsSet(P_NO_EXTRA_COLOR))
promptStr << alignColor();
if(flagIsSet(P_ALIASING) && alias_crt) {
promptStr << "(" << alias_crt->hp.getCur() << " H " << alias_crt->mp.getCur() << " M): ";
} else {
promptStr << "(" << hp.getCur() << " H";
if(cClass != LICH && cClass != BERSERKER
&& (cClass != FIGHTER || !flagIsSet(P_PTESTER)))
promptStr << " " << mp.getCur() << " M";
else if(cClass == FIGHTER && flagIsSet(P_PTESTER))
promptStr << " " << focus.getCur() << " F";
if(flagIsSet(P_SHOW_XP_IN_PROMPT))
promptStr << " " << expToLevel(true);
promptStr << "):^x ";
}
toPrint = promptStr.str();
} else
toPrint = ": ";
if(flagIsSet(P_AFK))
toPrint += "^r[AFK]^x ";
if(flagIsSet(P_NEWLINE_AFTER_PROMPT))
toPrint += "\n";
if(getSock()->getEor() == 1) {
unsigned char eor_str[] = {IAC, EOR, '\0' };
toPrint += eor_str;
}
toPrint = colorize((char*)toPrint.c_str(), flagIsSet(P_ANSI_COLOR));
mySock->write(toPrint);
}
//*********************************************************************
// computeLuck
//*********************************************************************
// This sets the luck value for a given player
int Player::computeLuck() {
int num=0, alg=0, con=0, smrt=0;
alg = abs(alignment);
alg = alg + 1;
alg /= 10;
// alignment only matters for these classes
if(cClass != PALADIN && cClass != CLERIC && cClass != DEATHKNIGHT && cClass != LICH)
alg = 0;
if( !alg ||
(cClass == PALADIN && deity != GRADIUS && getAdjustedAlignment() > NEUTRAL) ||
(cClass == DEATHKNIGHT && getAdjustedAlignment() < NEUTRAL) ||
(cClass == LICH && alignment <= -500) ||
(cClass == CLERIC && (deity == ENOCH || deity == LINOTHAN || deity == KAMIRA) && getAdjustedAlignment() >= LIGHTBLUE) ||
(cClass == CLERIC && (deity == ARAMON || deity == ARACHNUS) && getAdjustedAlignment() <= PINKISH)
)
alg = 1;
if(cClass != LICH) // Balances mages with liches for luck.
con = constitution.getCur()/10;
else
con = piety.getCur()/10;
smrt = intelligence.getCur()/10;
num = 100*(smrt+con);
num /= alg;
if(ready[HELD-1] && ready[HELD-1]->flagIsSet(O_LUCKY))
num += ready[HELD-1]->damage.getPlus();
// Carrying around alot of gold isn't very lucky!
if(!isStaff())
num -= (coins[GOLD] / 20000);
num = MAX(1, MIN(99, num));
luck = num;
return(num);
}
//*********************************************************************
// getStatusStr
//*********************************************************************
// returns a status string that describes the hp condition of the creature
const char* Creature::getStatusStr(int dmg) const {
int health = hp.getCur() - dmg;
if(health < 1)
return "'s dead!";
switch(MIN(health * 10 / (hp.getMax() ? hp.getMax() : 1), 10)) {
case 10:
return("'s unharmed.");
case 9:
return("'s relatively unscathed.");
case 8:
return("'s a little battered.");
case 7:
return("'s getting bruised.");
case 6:
return("'s noticeably bleeding.");
case 5:
return("'s having some trouble.");
case 4:
return(" doesn't look too good.");
case 3:
return("'s beginning to stagger.");
case 2:
return(" has some nasty wounds.");
case 1:
return(" isn't going to last much longer.");
case 0:
return(" is about to die!");
}
return("");
}
//*********************************************************************
// checkForSpam
//*********************************************************************
bool Player::checkForSpam() {
int t = time(0);
if(!isCt()) {
if(lasttime[LT_PLAYER_SEND].ltime == t) {
// 4 in one second is spam
if(++lasttime[LT_PLAYER_SEND].misc > 3) {
silenceSpammer();
return(true);
}
} else if(lasttime[LT_PLAYER_SEND].ltime + 1 == t) {
// 6 in 2 seconds is spam
if(++lasttime[LT_PLAYER_SEND].misc > 5) {
silenceSpammer();
return(true);
}
} else if(lasttime[LT_PLAYER_SEND].ltime + 2 == t) {
// 7 in 3 seconds is spam
if(++lasttime[LT_PLAYER_SEND].misc > 6 ) {
silenceSpammer();
return(true);
}
} else {
// reset spam counter
lasttime[LT_PLAYER_SEND].ltime = t;
lasttime[LT_PLAYER_SEND].misc = 1;
}
}
return(false);
}
//*********************************************************************
// silenceSpammer
//*********************************************************************
void Player::silenceSpammer() {
if(isEffected("silence")) {
// already spamming
EffectInfo* silenceEffect = getEffect("silence");
silenceEffect->setDuration(silenceEffect->getDuration() + 300);
printColor("^rYou have been given an additional 5 minutes of silence for spamming again!\n");
} else {
// first spam
addEffect("silence", 120, 1);
printColor("^rYou have been silenced for 2 minutes for spamming!\n");
broadcast(getSock(), getRoom(), "%s has been silenced for spamming!\n",name);
}
}
//*********************************************************************
// setMonkDice
//*********************************************************************
void Player::setMonkDice() {
int nLevel = MAX(0, MIN(level, MAXALVL));
// reset monk dice?
if(cClass == MONK) {
damage = monk_dice[nLevel];
} else if(cClass == WEREWOLF) {
damage = wolf_dice[nLevel];
}
}
//*********************************************************************
// getMultiClassID
//*********************************************************************
int getMultiClassID(char cls, char cls2) {
int id=0;
if(!cls2)
return(0);
switch(cls) {
case FIGHTER:
switch(cls2) {
case MAGE:
id = 1;
break;
case THIEF:
id = 2;
break;
}
break;
case CLERIC:
switch(cls2) {
case ASSASSIN:
id = 3;
break;
case FIGHTER:
id = 6;
break;
}
break;
case MAGE:
switch(cls2) {
case ASSASSIN:
id = 7;
break;
case THIEF:
id = 4;
break;
}
break;
case THIEF:
id = 5; // thief/mage is only thief-first combo
break;
}
return(id);
}
//*********************************************************************
// isOutdoors
//*********************************************************************
bool isOutdoors(Socket* sock) {
if(sock && sock->getPlayer())
return(sock->getPlayer()->getRoom()->isOutdoors());
return(false);
}
//*********************************************************************
// initLanguages
//*********************************************************************
void Player::initLanguages() {
switch(race) {
case DWARF:
learnLanguage(LDWARVEN);
learnLanguage(LGNOMISH);
learnLanguage(LKOBOLD);
learnLanguage(LGOBLINOID);
learnLanguage(LORCISH);
break;
case ELF:
learnLanguage(LELVEN);
learnLanguage(LGNOMISH);
learnLanguage(LORCISH);
break;
case HALFELF:
learnLanguage(LELVEN);
learnLanguage(LHALFLING);
break;
case HALFLING:
learnLanguage(LHALFLING);
learnLanguage(LGNOMISH);
break;
case ORC:
learnLanguage(LORCISH);
learnLanguage(LGIANTKIN);
break;
case HALFGIANT:
learnLanguage(LGIANTKIN);
learnLanguage(LOGRISH);
learnLanguage(LTROLL);
break;
case GNOME:
learnLanguage(LGNOMISH);
learnLanguage(LDWARVEN);
learnLanguage(LGOBLINOID);
learnLanguage(LKOBOLD);
break;
case TROLL:
learnLanguage(LTROLL);
break;
case HALFORC:
learnLanguage(LORCISH);
break;
case OGRE:
learnLanguage(LOGRISH);
learnLanguage(LGIANTKIN);
break;
case DARKELF:
learnLanguage(LDARKELVEN);
learnLanguage(LORCISH);
learnLanguage(LELVEN);
learnLanguage(LDWARVEN);
learnLanguage(LGNOMISH);
learnLanguage(LKOBOLD);
learnLanguage(LGOBLINOID);
break;
case GOBLIN:
learnLanguage(LGOBLINOID);
learnLanguage(LORCISH);
break;
case MINOTAUR:
learnLanguage(LMINOTAUR);
break;
case SERAPH:
learnLanguage(LELVEN);
learnLanguage(LCELESTIAL);
learnLanguage(LINFERNAL);
learnLanguage(LGNOMISH);
learnLanguage(LHALFLING);
learnLanguage(LABYSSAL);
break;
case KOBOLD:
learnLanguage(LKOBOLD);
learnLanguage(LGNOMISH);
learnLanguage(LDARKELVEN);
learnLanguage(LOGRISH);
learnLanguage(LGIANTKIN);
break;
case CAMBION:
learnLanguage(LINFERNAL);
learnLanguage(LDARKELVEN);
learnLanguage(LELVEN);
learnLanguage(LCELESTIAL);
learnLanguage(LORCISH);
learnLanguage(LGIANTKIN);
learnLanguage(LGOBLINOID);
break;
case BARBARIAN:
learnLanguage(LBARBARIAN);
break;
case KATARAN:
learnLanguage(LKATARAN);
break;
case TIEFLING:
learnLanguage(LHALFLING);
learnLanguage(LINFERNAL);
learnLanguage(LABYSSAL);
learnLanguage(LORCISH);
learnLanguage(LGOBLINOID);
learnLanguage(LTIEFLING);
break;
} // End switch.
switch(cClass) {
case THIEF:
case ASSASSIN:
case BARD:
case ROGUE:
learnLanguage(LTHIEFCANT);
break;
case DRUID:
case RANGER:
learnLanguage(LDRUIDIC);
break;
case WEREWOLF:
learnLanguage(LWOLFEN);
break;
case MAGE:
case LICH:
learnLanguage(LARCANIC);
break;
case CLERIC:
switch(deity) {
case ARAMON:
learnLanguage(LINFERNAL);
break;
case ENOCH:
learnLanguage(LCELESTIAL);
break;
case ARES:
learnLanguage(LBARBARIAN);
break;
case KAMIRA:
learnLanguage(LTHIEFCANT);
break;
}
break;
}
return;
}
//*********************************************************************
// doRecall
//*********************************************************************
void Player::doRecall(int roomNum) {
Room *new_rom=0;
BaseRoom *newRoom=0;
if(roomNum == -1) {
newRoom = recallWhere();
} else {
if(!loadRoom(roomNum, &new_rom))
newRoom = abortFindRoom(this, "doRecall");
else
newRoom = new_rom;
}
courageous();
deleteFromRoom();
addToRoom(newRoom);
doPetFollow();
}
//*********************************************************************
// recallWhere
//*********************************************************************
// Because of ethereal plane, we don't always know where we're going to
// recall to. We need a function to figure out where we are going.
BaseRoom* Creature::recallWhere() {
// A builder should never get this far, but let's not chance it.
// Only continue if they can't load the perm_low_room.
if(cClass == BUILDER) {
Room* uRoom=0;
if(loadRoom(ROOM_BUILDER_PERM_LOW, &uRoom))
return(uRoom);
}
if( getRoom()->flagIsSet(R_ETHEREAL_PLANE) &&
(mrand(1,100) <= 50)
) {
return(teleportWhere());
}
BaseRoom* room = getRecallRoom().loadRoom(getPlayer());
// uh oh!
if(!room)
return(abortFindRoom(this, "recallWhere"));
return(room);
}
//*********************************************************************
// teleportWhere
//*********************************************************************
// Loops through rooms and finds us a place we can teleport to.
// This function will always return a room or it will crash trying to.
BaseRoom* Creature::teleportWhere() {
Room *uRoom=0;
BaseRoom *newRoom=0;
AreaRoom* aRoom=0;
const CatRefInfo* cri = gConfig->getCatRefInfo(getRoom());
int i=0, zone = cri ? cri->teleportZone : 0;
Area *area=0;
Location l;
bool found = false;
// A builder should never get this far, but let's not chance it.
// Only continue if they can't load the perm_low_room.
if(cClass == BUILDER) {
if(loadRoom(ROOM_BUILDER_PERM_LOW, &uRoom))
return(uRoom);
}
do {
if(i>250)
return(abortFindRoom(this, "teleportWhere"));
cri = gConfig->getRandomCatRefInfo(zone);
// if this fails, we have nowhere to teleport to
if(!cri)
return(getRoom());
// special area used to signify overland map
if(cri->area == "area") {
area = gConfig->getArea(cri->id);
l.mapmarker.set(area->id, mrand(0, area->width), mrand(0, area->height), mrand(0, area->depth));
if(area->canPass(0, &l.mapmarker, true)) {
//area->adjustCoords(&mapmarker.x, &mapmarker.y, &mapmarker.z);
// don't bother sending a creature because we've already done
// canPass check here
//aRoom = area->loadRoom(0, &mapmarker, false);
if(Move::getRoom(this, 0, &uRoom, &aRoom, false, &l.mapmarker)) {
if(uRoom)
newRoom = uRoom;
else
newRoom = aRoom;
found = true;
}
}
} else {
l.room.setArea(cri->area);
// if misc, first 1000 rooms are off-limits
l.room.id = mrand(l.room.isArea("misc") ? 1000 : 1, cri->teleportWeight);
found = canPortHere(loadRoom(l.room, &uRoom), uRoom);
if(found)
newRoom = uRoom;
}
i++;
} while(!found);
if(!newRoom)
return(abortFindRoom(this, "teleportWhere"));
return(newRoom);
}
//*********************************************************************
// canPortHere
//*********************************************************************
// A player, the return value from loadRoom, and a room will determine if the
// player is allowed to teleport to the room we're given.
bool Creature::canPortHere(bool loadRoom, const Room *room) const {
if(!loadRoom)
return(false);
if(room->flagIsSet(R_NO_TELEPORT) ||
room->flagIsSet(R_CONSTRUCTION) ||
room->flagIsSet(R_SHOP_STORAGE) ||
room->flagIsSet(R_JAIL) ||
room->flagIsSet(R_IS_STORAGE_ROOM) ||
room->flagIsSet(R_ETHEREAL_PLANE)
)
return(false);
if(size && room->getSize() && size > room->getSize())
return(false);
if(room->isFull())
return(false);
if(room->deityRestrict(this))
return(false);
if(room->getLowLevel() > level)
return(false);
if(room->getHighLevel() && level > room->getHighLevel())
return(false);
// artificial limits for the misc area
if(room->info.isArea("misc") && room->info.id <= 1000)
return(false);
if(!room->first_ext)
return(false);
return(true);
}
//*********************************************************************
// checkSkillsGain
//*********************************************************************
// setToLevel: Set skill level to player level - 1, otherwise set to whatever the skill gain tells us to
void Creature::checkSkillsGain(std::list<SkillGain*>::const_iterator begin, std::list<SkillGain*>::const_iterator end, bool setToLevel) {
SkillGain *sGain=0;
std::list<SkillGain*>::const_iterator sgIt;
for(sgIt = begin ; sgIt != end ; sgIt++) {
sGain = (*sgIt);
if(sGain->getName() == "")
continue;
if(!sGain->hasDeities() || sGain->deityIsAllowed(deity)) {
if(!knowsSkill(sGain->getName())) {
if(setToLevel)
addSkill(sGain->getName(), (level-1)*10);
else
addSkill(sGain->getName(), sGain->getGained());
print("You have learned the fine art of '%s'.\n", gConfig->getSkillDisplayName(sGain->getName()).c_str());
}
}
}
}
//*********************************************************************
// loseRage
//*********************************************************************
void Player::loseRage() {
if(!flagIsSet(P_BERSERKED))
return;
printColor("^rYour rage diminishes.^x\n");
clearFlag(P_BERSERKED);
if(cClass == CLERIC && deity == ARES)
strength.decrease(30);
else
strength.decrease(50);
computeAC();
computeAttackPower();
}
//*********************************************************************
// losePray
//*********************************************************************
void Player::losePray() {
if(!flagIsSet(P_PRAYED))
return;
if(cClass != DEATHKNIGHT) {
printColor("^yYou feel less pious.\n");
piety.decrease(50);
} else {
printColor("^rYour demonic strength leaves you.\n");
strength.decrease(30);
computeAC();
computeAttackPower();
}
clearFlag(P_PRAYED);
}
//*********************************************************************
// loseFrenzy
//*********************************************************************
void Player::loseFrenzy() {
if(!flagIsSet(P_FRENZY))
return;
printColor("^gYou feel slower.\n");
clearFlag(P_FRENZY);
dexterity.decrease(50);
}
//*********************************************************************
// halftolevel
//*********************************************************************
// The following function will return true if a player is more then halfway in experience
// to their next level, causing them to not gain exp until they go level. If they are
// not yet half way, it will return false. Staff and players under level 5 are exempt.
bool Player::halftolevel() const {
if(isStaff())
return(false);
// can save up xp until level 5.
if(experience <= gConfig->expNeeded(5))
return(false);
if(experience >= gConfig->expNeeded(level) + (((unsigned)(gConfig->expNeeded(level+1) - gConfig->expNeeded(level)))/2)) {
print("You have enough experience to train.\nYou are half way to the following level.\n");
print("You must go train in order to gain further experience.\n");
return(true);
}
return(false);
}
//*********************************************************************
// getSock
//*********************************************************************
Socket* Player::getSock() const {
if(!this)
return(null);
return(mySock);
}
//*********************************************************************
// setSock
//*********************************************************************
void Player::setSock(Socket* pSock) {
mySock = pSock;
}
//*********************************************************************
// getVision
//*********************************************************************
int Player::getVision() const {
if(isStaff())
return(MAX_VISION);
int vision = 8;
if(race == ELF)
vision++;
if(race == DARKELF) {
if(isDay())
vision--;
else
vision++;
}
if(isEffected("farsight"))
vision *= 2;
return(MIN(MAX_VISION, vision));
}
//*********************************************************************
// getSneakChance
//*********************************************************************
// determines out percentage chance of being able to sneak
int Player::getSneakChance() const {
int sLvl = (int)getSkillLevel("sneak");
if(isStaff())
return(101);
// this is the base chance for most classes
int chance = MIN(70, 5 + 2 * sLvl + 3 * bonus((int) dexterity.getCur()));
switch(cClass) {
case THIEF:
if(cClass2 == MAGE)
MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) dexterity.getCur()));
else
chance = MIN(90, 5 + 8 * sLvl + 3 * bonus((int) dexterity.getCur()));
break;
case ASSASSIN:
chance = MIN(90, 5 + 8 * sLvl + 3 * bonus((int) dexterity.getCur()));
break;
case CLERIC:
if(cClass2 == ASSASSIN)
chance = MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) dexterity.getCur()));
else if(deity == KAMIRA || deity == ARACHNUS)
chance = MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) piety.getCur()));
break;
case FIGHTER:
if(cClass2 == THIEF)
chance = MIN(90, 5 + 8 * MAX(1,sLvl-2) + 3 * bonus((int) dexterity.getCur()));
break;
case MAGE:
if(cClass2 == THIEF || cClass2 == ASSASSIN)
chance = MIN(90, 5 + 8 * MAX(1,sLvl-3) + 3 * bonus((int) dexterity.getCur()));
break;
case DRUID:
if(getRoom()->isForest())
chance = MIN(95 , 5 + 10 * sLvl + 3 * bonus((int) dexterity.getCur()));
break;
case RANGER:
if(getRoom()->isForest())
chance = MIN(95 , 5 + 10 * sLvl + 3 * bonus((int) dexterity.getCur()));
else
chance = MIN(83, 5 + 8 * sLvl + 3 * bonus((int) dexterity.getCur()));
case ROGUE:
chance = MIN(85, 5 + 7 * sLvl + 3 * bonus((int) dexterity.getCur()));
break;
default:
break;
}
if(isBlind())
chance = MIN(20, chance);
if(isEffected("camouflage")) {
if(getRoom()->isOutdoors())
chance += 15;
if(cClass == DRUID && getRoom()->isForest())
chance += 5;
}
return(MIN(99, chance));
}
//*********************************************************************
// breakObject
//*********************************************************************
// breaks a worn object and readds it to the player's inventory
bool Player::breakObject(Object* object, int loc) {
if(!object)
return(false);
if(object->getShotscur() < 1) {
printColor("Your %s is broken.\n", object->name);
broadcast(getSock(), getRoom(), "%M broke %s %s.", this, hisHer(), object->name);
if(object->compass) {
delete object->compass;
object->compass = 0;
}
object->clearFlag(O_WORN);
object->parent_crt = this;
Limited::remove(this, object);
if(object->getAdjustment())
object->setAdjustment(0);
if(object->flagIsSet(O_TEMP_ENCHANT)) {
object->damage.setPlus(0);
object->clearFlag(O_TEMP_ENCHANT);
object->clearFlag(O_RANDOM_ENCHANT);
if(isEffected("detect-magic"))
print("The enchantment on your %s fades.\n", object);
}
if(object->flagIsSet(O_ENVENOMED)) {
object->clearFlag(O_ENVENOMED);
object->clearEffect();
print("The poison on your %s becomes useless.\n", object);
}
if(loc != -1) {
unequip(loc);
} else {
broadcast(::isDm, "^g>>> BreakObject: BadLoc (Loc:%d) %'s %s", loc, name, object->name);
if(ready[WIELD-1] == object) {
unequip(WIELD);
} else if(ready[HELD-1] == object) {
unequip(HELD);
}
}
return(true);
}
return(false);
}
//********************************************************************
// getWhoString
//********************************************************************
bstring Player::getWhoString(bool whois, bool color, bool ignoreIllusion) const {
std::ostringstream whoStr;
if(whois) {
whoStr << "^bWhois for [" << name << "]\n";
whoStr << "----------------------------------------------------------------------------------\n";
}
whoStr << (color ? "^x[^c" : "[") << std::setw(2) << level
<< ":" << std::setw(4) << bstring(getShortClassName(this)).left(4)
<< (color ? "^x] " : "] ");
if(isHardcore())
whoStr << (color ? "^y" : "") << "H ";
else if(flagIsSet(P_OUTLAW))
whoStr << (color ? "^r" : "") << "O ";
else if( (flagIsSet(P_NO_PKILL) || flagIsSet(P_DIED_IN_DUEL) ||
getRoom()->flagIsSet(R_SAFE_ROOM)) &&
(flagIsSet(P_CHAOTIC) || clan || cClass == CLERIC) )
whoStr << (color ? "^y" : "") << "N ";
else if(flagIsSet(P_CHAOTIC)) // Chaotic
whoStr << (color ? "^y" : "") << "C ";
else // Lawful
whoStr << (color ? "^y" : "") << "L ";
if(color)
whoStr << (isPublicWatcher() ? "^w" : "^g");
whoStr << fullName() << " the ";
if(whois)
whoStr << getSexName(getSex()) << " ";
whoStr << gConfig->getRace(getDisplayRace())->getAdjective();
// will they see through the illusion?
if(ignoreIllusion && getDisplayRace() != getRace())
whoStr << " (" << gConfig->getRace(getRace())->getAdjective() << ")";
whoStr << " " << getTitle();
if(whois)
whoStr << " (Age:" << getAge() << ")";
if( flagIsSet(P_DM_INVIS) ||
flagIsSet(P_INCOGNITO) ||
isInvisible() ||
flagIsSet(P_MISTED) ||
(flagIsSet(P_LINKDEAD) && !isPublicWatcher())
) {
if(color)
whoStr << " ^w";
if(flagIsSet(P_DM_INVIS))
whoStr << "[+]";
if(flagIsSet(P_INCOGNITO) )
whoStr << "[g]";
if(isInvisible() )
whoStr << "[*]";
if(flagIsSet(P_MISTED) )
whoStr << "[m]";
if(flagIsSet(P_LINKDEAD) && !isPublicWatcher() )
whoStr << "[l]";
}
if(flagIsSet(P_AFK))
whoStr << (color ? "^R" : "") << " [AFK]";
if(deity) {
whoStr << (color ? "^r" : "") << " (" << gConfig->getDeity(deity)->getName() << ")";
} else if(clan) {
whoStr << (color ? "^r" : "") << " (" << gConfig->getClan(clan)->getName() << ")";
}
if(guild && guildRank >= GUILD_PEON) {
whoStr << (color ? "^y" : "") << " [" << getGuildName(guild) << "]";
}
whoStr << (color ? "^x" : "") << "\n";
return(whoStr.str());
}
//********************************************************************
// titlePunctuation
//********************************************************************
bool titlePunctuation(char c) {
switch(c) {
case ' ':
case '/':
case '\'':
case '-':
return(true);
default:
return(false);
}
}
//********************************************************************
// cmdTitle
//********************************************************************
int cmdTitle(Player* player, cmd* cmnd) {
double punctuation=0;
bstring title = getFullstrText(cmnd->fullstr, 1);
if(!player->flagIsSet(P_CAN_CHOOSE_CUSTOM_TITLE)) {
player->print("You cannot currently choose a custom title.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Please enter a title.\n");
return(0);
}
for(size_t i = 0; i<title.length(); i++) {
if(!titlePunctuation(title[i]) && !isalpha(title[i])) {
player->print("Sorry, ""%c"" cannot be used in your title.\n", title[i]);
return(0);
}
if(titlePunctuation(title[i]))
punctuation++;
}
if(title.length() < 4) {
player->print("Title must be atleast 4 characters.\n");
return(0);
}
punctuation = punctuation / (float)title.length();
if(punctuation > 0.25) {
player->print("This title has too much punctuation.\n");
return(0);
}
player->print("Your new title: %s\n", title.c_str());
player->print("Is this acceptable? (Y/N)\n");
player->setTempTitle(title);
player->getSock()->setState(CON_CONFIRM_TITLE);
return(0);
}
//*********************************************************************
// doTitle
//*********************************************************************
void doTitle(Socket* sock, char *str) {
Player* player = sock->getPlayer();
if(low(str[0]) == 'y') {
player->print("You are now known as %s the %s.\n", player->name, player->getTempTitle().c_str());
if(!player->isStaff())
broadcast("^y%s the %s is now known as %s the %s.", player->name,
player->getTitle().c_str(), player->name, player->getTempTitle().c_str());
player->setTitle(player->getTempTitle());
player->clearFlag(P_CAN_CHOOSE_CUSTOM_TITLE);
} else
player->print("Aborted.\n");
player->setTempTitle("");
sock->setState(CON_PLAYING);
}
//*********************************************************************
// getBound
//*********************************************************************
Location Player::getBound() {
if(bound != getLimboRoom() && bound != getRecallRoom())
return(bound);
else {
// we hardcode highport in case of failure
const StartLoc* location = gConfig->getStartLoc("highport");
bind(location);
if(location)
return(location->getBind());
}
// too many errors to handle!
Location cr;
cr.room.id = ROOM_BOUND_FAILURE;
return(cr);
}
//*********************************************************************
// expToLevel
//*********************************************************************
unsigned long Player::expToLevel() const {
return(experience > gConfig->expNeeded(level) ? 0 : gConfig->expNeeded(level) - experience);
}
bstring Player::expToLevel(bool addX) const {
if(level < MAXALVL) {
std::ostringstream oStr;
oStr.imbue(std::locale(isStaff() ? "C" : ""));
oStr << expToLevel();
if(addX)
oStr << "X";
return(oStr.str());
}
return("infinity");
}
//*********************************************************************
// exists
//*********************************************************************
bool Player::exists(bstring name) {
char file[80];
sprintf(file, "%s/%s.xml", PLAYERPATH, name.c_str());
return(file_exists(file));
}
//*********************************************************************
// inList functions
//*********************************************************************
bool Player::inList(const std::list<bstring>* list, bstring name) const {
std::list<bstring>::const_iterator it;
for(it = list->begin(); it != list->end() ; it++) {
if((*it) == name)
return(true);
}
return(false);
}
bool Player::isIgnoring(bstring name) const {
return(inList(&ignoring, name));
}
bool Player::isGagging(bstring name) const {
return(inList(&gagging, name));
}
bool Player::isRefusing(bstring name) const {
return(inList(&refusing, name));
}
bool Player::isDueling(bstring name) const {
return(inList(&dueling, name));
}
bool Player::isWatching(bstring name) const {
return(inList(&watching, name));
}
//*********************************************************************
// showList
//*********************************************************************
bstring Player::showList(const std::list<bstring>* list) const {
std::ostringstream oStr;
std::list<bstring>::const_iterator it;
bool initial=false;
for(it = list->begin(); it != list->end() ; it++) {
if(initial)
oStr << ", ";
initial = true;
oStr << (*it);
}
if(!initial)
oStr << "No one";
oStr << ".";
return(oStr.str());
}
bstring Player::showIgnoring() const {
return(showList(&ignoring));
}
bstring Player::showGagging() const {
return(showList(&gagging));
}
bstring Player::showRefusing() const {
return(showList(&refusing));
}
bstring Player::showDueling() const {
return(showList(&dueling));
}
bstring Player::showWatching() const {
return(showList(&watching));
}
//*********************************************************************
// addList functions
//*********************************************************************
void Player::addList(std::list<bstring>* list, bstring name) {
list->push_back(name);
}
void Player::addIgnoring(bstring name) {
addList(&ignoring, name);
}
void Player::addGagging(bstring name) {
addList(&gagging, name);
}
void Player::addRefusing(bstring name) {
addList(&refusing, name);
}
void Player::addDueling(bstring name) {
delList(&maybeDueling, name);
// if they aren't dueling us, add us to their maybe dueling list
Player* player = gServer->findPlayer(name);
if(player && !player->isDueling(name))
player->addMaybeDueling(this->name);
addList(&dueling, name);
}
void Player::addMaybeDueling(bstring name) {
addList(&maybeDueling, name);
}
void Player::addWatching(bstring name) {
addList(&watching, name);
}
//*********************************************************************
// delList functions
//*********************************************************************
void Player::delList(std::list<bstring>* list, bstring name) {
std::list<bstring>::iterator it;
for(it = list->begin(); it != list->end() ; it++) {
if((*it) == name) {
list->erase(it);
return;
}
}
}
void Player::delIgnoring(bstring name) {
delList(&ignoring, name);
}
void Player::delGagging(bstring name) {
delList(&gagging, name);
}
void Player::delRefusing(bstring name) {
delList(&refusing, name);
}
void Player::delDueling(bstring name) {
delList(&dueling, name);
}
void Player::delWatching(bstring name) {
delList(&watching, name);
}
//*********************************************************************
// clear list functions
//*********************************************************************
void Player::clearIgnoring() {
ignoring.clear();
}
void Player::clearGagging() {
gagging.clear();
}
void Player::clearRefusing() {
refusing.clear();
}
void Player::clearDueling() {
dueling.clear();
}
void Player::clearMaybeDueling() {
std::list<bstring>::iterator it;
Player* player=0;
for(it = maybeDueling.begin(); it != maybeDueling.end() ; it++) {
player = gServer->findPlayer(*it);
if(!player)
continue;
player->delDueling(name);
}
maybeDueling.clear();
}
void Player::clearWatching() {
watching.clear();
}
//*********************************************************************
// renamePlayerFiles
//*********************************************************************
void renamePlayerFiles(char *old_name, char *new_name) {
char file[80], file2[80];
sprintf(file, "%s/%s.xml", PLAYERPATH, old_name);
unlink(file);
sprintf(file, "%s/%s.txt", POSTPATH, old_name);
sprintf(file2, "%s/%s.txt", POSTPATH, new_name);
rename(file, file2);
sprintf(file, "%s/%s.txt", HISTPATH, old_name);
sprintf(file2, "%s/%s.txt", HISTPATH, new_name);
rename(file, file2);
sprintf(file, "%s/%s.txt", BANKDIR, old_name);
sprintf(file2, "%s/%s.txt", BANKDIR, new_name);
rename(file, file2);
}
//*********************************************************************
// checkHeavyRestrict
//*********************************************************************
bool Player::checkHeavyRestrict(const bstring& skill) const {
// If we aren't one of the classes that can use heavy armor, but with restrictions
// immediately return false
if(! ((getClass() == FIGHTER && getSecondClass() == THIEF) ||
(getClass() == RANGER)) )
return(false);
// Allows us to do a blank check and see if the player's class is one of the heavy armor
// restricted classes.
if(skill == "")
return(true);
bool mediumOK = (getClass() == RANGER);
for(int i = 0 ; i < MAXWEAR ; i++) {
if(ready[i] && (ready[i]->isHeavyArmor() || (!mediumOK && ready[i]->isMediumArmor())) ) {
printColor("You can't ^W%s^x while wearing heavy armor!\n", skill.c_str());
printColor("^W%O^x would hinder your movement too much!\n", ready[i]);
return(true);
}
}
return(false);
}