/* * 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); }