/* * players.cpp * 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 * */ #include "mud.h" #include "effects.h" #include "calendar.h" //#include <libxml/xmlmemory.h> //#include <libxml/parser.h> #include <sstream> char statStr[][5] = { "NONE", "STR", "DEX", "CON", "INT", "PTY", "CHA" }; char saveStr[][5] = { "LCK", "POI", "DEA", "BRE", "MEN", "SPL" }; int findDeity(bstring &deity); int findStat(bstring &stat) { for(int i = 0 ; i < 7 ; i++) { if(stat == statStr[i]) { return(i); } } return(-1); } int findSave(bstring &save) { for(int i = 0 ; i < 6 ; i++) { if(save == saveStr[i]) { return(i); } } return(-1); } SkillGain::SkillGain(xmlNodePtr rootNode) { load(rootNode); } SkillGain::~SkillGain() { deities.clear(); } void SkillGain::load(xmlNodePtr rootNode) { xmlNodePtr curNode = rootNode->children; while(curNode) { if(NODE_NAME(curNode, "Name")) { xml::copyToBString(skillName, curNode); } else if(NODE_NAME(curNode, "Gained")) { xml::copyToNum(gainLevel, curNode); } else if(NODE_NAME(curNode, "Deity")) { bstring deityStr; xml::copyToBString(deityStr, curNode); int deity = gConfig->deitytoNum(deityStr); if(deity != -1) { deities[deity] = true; } } curNode = curNode->next; } } PlayerClass::~PlayerClass() { std::list<SkillGain*>::iterator bsIt; std::map<int, LevelGain*>::iterator lgIt; std::map<int, PlayerTitle*>::iterator tt; SkillGain* sGain; LevelGain* lGain; for(bsIt = baseSkills.begin() ; bsIt != baseSkills.end() ; bsIt++) { sGain = (*bsIt); delete sGain; } baseSkills.clear(); for(lgIt = levels.begin() ; lgIt != levels.end() ; lgIt++) { lGain = (*lgIt).second; delete lGain; } levels.clear(); for(tt = titles.begin() ; tt != titles.end() ; tt++) { PlayerTitle* p = (*tt).second; delete p; } titles.clear(); } PlayerClass::PlayerClass(xmlNodePtr rootNode) { needDeity = false; numProf = 1; unarmedWeaponSkill = "bare-hand"; load(rootNode); } void PlayerClass::load(xmlNodePtr rootNode) { // // Stuff gained on each additional level // std::map<int, LevelGain*> levels; id = xml::getIntProp(rootNode, "id"); xml::copyPropToBString(name, rootNode, "Name"); xmlNodePtr curNode = rootNode->children; while(curNode) { if(NODE_NAME(curNode, "Title")) { titles[1] = new PlayerTitle; titles[1]->load(curNode); } else if(NODE_NAME(curNode, "UnarmedWeaponSkill")) xml::copyToBString(unarmedWeaponSkill, curNode); else if(NODE_NAME(curNode, "NumProfs")) xml::copyToNum(numProf, curNode); else if(NODE_NAME(curNode, "NeedsDeity")) { int i=0; xml::copyToNum(i, curNode); if(i) needDeity = true; } else if(NODE_NAME(curNode, "Base")) { xmlNodePtr baseNode = curNode->children; while(baseNode) { if(NODE_NAME(baseNode, "Stats")) { xmlNodePtr statNode = baseNode->children; while(statNode) { if(NODE_NAME(statNode, "Hp")) { xml::copyToNum(baseHp, statNode); } else if(NODE_NAME(statNode, "Mp")) { xml::copyToNum(baseMp, statNode); } statNode = statNode->next; } } else if(NODE_NAME(baseNode, "Dice")) damage.load(baseNode); else if(NODE_NAME(baseNode, "Skills")) { xmlNodePtr skillNode = baseNode->children; while(skillNode) { if(NODE_NAME(skillNode, "Skill")) { SkillGain* skillGain = new SkillGain(skillNode); baseSkills.push_back(skillGain); } skillNode = skillNode->next; } } baseNode = baseNode->next; } } else if(NODE_NAME(curNode, "Levels")) { xmlNodePtr levelNode = curNode->children; int lvl; while(levelNode) { if(NODE_NAME(levelNode, "Level")) { lvl = xml::getIntProp(levelNode, "Num"); if(lvl > 0) { levels[lvl] = new LevelGain(levelNode); } xmlNodePtr childNode = levelNode->children; while(childNode) { if(NODE_NAME(childNode, "Title")) { titles[lvl] = new PlayerTitle; titles[lvl]->load(childNode); } childNode = childNode->next; } } levelNode = levelNode->next; } } curNode = curNode->next; } } int PlayerClass::getId() const { return(id); } bstring PlayerClass::getName() const { return(name); } bstring PlayerClass::getUnarmedWeaponSkill() const { return(unarmedWeaponSkill); } LevelGain::LevelGain(xmlNodePtr rootNode) { load(rootNode); } LevelGain::~LevelGain() { std::list<SkillGain*>::iterator sgIt; SkillGain* sGain; for(sgIt = skills.begin() ; sgIt != skills.end() ; sgIt++) { sGain = (*sgIt); delete sGain; } skills.clear(); } void LevelGain::load(xmlNodePtr rootNode) { xmlNodePtr curNode = rootNode->children; bstring temp; while(curNode) { if(NODE_NAME(curNode, "HpGain")) { xml::copyToNum(hp, curNode); } else if(NODE_NAME(curNode, "MpGain")) { xml::copyToNum(mp, curNode); } else if(NODE_NAME(curNode, "Stat")) { xml::copyToBString(temp, curNode); stat = findStat(temp); } else if(NODE_NAME(curNode, "Save")) { xml::copyToBString(temp, curNode); save = findSave(temp); } else if(NODE_NAME(curNode, "Skills")) { xmlNodePtr skillNode = curNode->children; while(skillNode) { if(NODE_NAME(skillNode, "Skill")) { SkillGain* skillGain = new SkillGain(skillNode); skills.push_back(skillGain); } skillNode = skillNode->next; } } curNode = curNode->next; } } bool Config::loadClasses() { xmlDocPtr xmlDoc; xmlNodePtr curNode; xmlDoc = xml::loadFile(CONFPATH "classes.xml", "Classes"); if(xmlDoc == NULL) return(false); curNode = xmlDocGetRootElement(xmlDoc); curNode = curNode->children; while(curNode && xmlIsBlankNode(curNode)) curNode = curNode->next; if(curNode == 0) { xmlFreeDoc(xmlDoc); return(false); } clearClasses(); bstring className = ""; while(curNode != NULL) { if(NODE_NAME(curNode, "Class")) { xml::copyPropToBString( className, curNode, "Name"); if(className != "") { if(classes.find(className) == classes.end()) { //printf("\n\tLoaded %s", className.c_str()); classes[className] = new PlayerClass(curNode); } else { printf("Error: Duplicate class: %s\n", className.c_str()); } } } curNode = curNode->next; } xmlFreeDoc(xmlDoc); xmlCleanupParser(); //printf("\n"); return(true); } void Config::clearClasses() { std::map<bstring, PlayerClass*>::iterator pcIt; for(pcIt = classes.begin() ; pcIt != classes.end() ; pcIt++) { //printf("Erasing class %s\n", ((*pcIt).first).c_str()); PlayerClass* pClass = (*pcIt).second; delete pClass; } classes.clear(); } int dmShowClasses(Player* admin, cmd* cmnd) { bool more = admin->isDm() && cmnd->num > 1 && !strcmp(cmnd->str[1], "more"); bool all = admin->isDm() && cmnd->num > 1 && !strcmp(cmnd->str[1], "all"); std::map<bstring, PlayerClass*>::iterator cIt; PlayerClass* pClass; SkillGain* sGain; bstring tmp; admin->printColor("Displaying Classes:%s\n", admin->isDm() ? " Type ^y*classlist more^x to view more, ^y*classlist all^x to view all information." : ""); for(cIt = gConfig->classes.begin() ; cIt != gConfig->classes.end() ; cIt++) { pClass = (*cIt).second; admin->printColor("Id: ^c%-2d^x Name: ^c%s\n", pClass->getId(), (*cIt).first.c_str()); if(more || all) { std::ostringstream cStr; std::map<int, PlayerTitle*>::iterator tt; //cStr << "Class: " << (*cIt).first << "\n"; cStr << " Base: " << pClass->getBaseHp() << "hp, " << pClass->getBaseMp() << "mp, " << "Dice: " << pClass->damage.str() << "\n" << " NumProfs: " << pClass->numProfs() << " NeedsDeity: " << (pClass->needsDeity() ? "^gYes" : "^rNo") << "^x\n"; std::list<SkillGain*>::const_iterator sgIt; if(all) { cStr << " Skills: "; for(sgIt = pClass->getSkillBegin() ; sgIt != pClass->getSkillEnd() ; sgIt++) { sGain = *sgIt; cStr << gConfig->getSkillDisplayName(sGain->getName()) << "(" << sGain->getGained() << ") "; } std::map<int, LevelGain*>::const_iterator lgIt; cStr << "\n Levels: "; for(lgIt = pClass->getLevelBegin() ; lgIt != pClass->getLevelEnd() ; lgIt ++) { LevelGain* lGain = (*lgIt).second; int lvl = (*lgIt).first; if(!lGain) { cStr << "\n ERROR: No level gain information found for " << pClass->getName() << " : " << lvl << "."; continue; } cStr << "\n " << lvl << ": " << lGain->getHp() << "HP, " << lGain->getMp() << "MP, St:" << lGain->getStatStr() << ", Sa:" << lGain->getSaveStr(); if(lGain->hasSkills()) { cStr << "\n Skills:"; for(sgIt = lGain->getSkillBegin() ; sgIt != lGain->getSkillEnd() ; sgIt++) { sGain = *sgIt; cStr << "\n " << gConfig->getSkillDisplayName(sGain->getName()) << "(" << sGain->getGained() << ") "; } } } cStr << "\n Titles: "; for(tt = pClass->titles.begin() ; tt != pClass->titles.end(); tt++) { PlayerTitle* title = (*tt).second; cStr << "\n Level: ^c" << (*tt).first << "^x" << " Male: ^c" << title->getTitle(true) << "^x" << " Female: ^c" << title->getTitle(false) << "^x"; } } cStr << "\n"; tmp = cStr.str(); admin->printColor("%s", tmp.c_str()); gServer->processOutput(); } } return(0); } bool LevelGain::hasSkills() { return(!skills.empty()); } std::list<SkillGain*>::const_iterator PlayerClass::getSkillBegin() { return(baseSkills.begin()); } std::list<SkillGain*>::const_iterator PlayerClass::getSkillEnd() { return(baseSkills.end()); } std::map<int, LevelGain*>::const_iterator PlayerClass::getLevelBegin() { return(levels.begin()); } std::map<int, LevelGain*>::const_iterator PlayerClass::getLevelEnd() { return(levels.end()); } short PlayerClass::getBaseHp() { return(baseHp); } short PlayerClass::getBaseMp() { return(baseMp); } bool PlayerClass::needsDeity() { return(needDeity); } short PlayerClass::numProfs() { return(numProf); } int SkillGain::getGained() { return(gainLevel); } bstring SkillGain::getName() const { return(skillName); } std::list<SkillGain*>::const_iterator LevelGain::getSkillBegin() { return(skills.begin()); } std::list<SkillGain*>::const_iterator LevelGain::getSkillEnd() { return(skills.end()); } bstring LevelGain::getStatStr() { return(statStr[stat]); } bstring LevelGain::getSaveStr() { return(saveStr[save]); } int LevelGain::getStat() { return(stat); } int LevelGain::getSave() { return(save); } int LevelGain::getHp() { return(hp); } int LevelGain::getMp() { return(mp); } LevelGain* PlayerClass::getLevelGain(int lvl) { return(levels[lvl]); } //********************************************************************* // deityIsAllowed //********************************************************************* bool SkillGain::deityIsAllowed(int deity) { if(deities.find(deity) != deities.end()) return(true); else return(false); } //********************************************************************* // hasDeities //********************************************************************* bool SkillGain::hasDeities() { return(!this->deities.empty()); } //********************************************************************* // pulseTick //********************************************************************* // return true if they were destroyed, false if they still exist void Player::pulseTick(long t) { bool ill = false; bool noTick = false; long mainTick = LT(this, LT_TICK); long secTick = LT(this, LT_TICK_SECONDARY); long badTick = LT(this, LT_TICK_HARMFUL); bool vamp = isNewVampire() && getRoom()->isSunlight(); bool hardcore = isHardcore(); if(!getSock()) return; EffectInfo* deathSickness = getEffect("death-sickness"); ill = isPoisoned() || isDiseased() || isEffected("wounded") || isEffected("petrification"); noTick = (vamp || isEffected("petrification") || isEffected("wounded")); BaseRoom* room = getRoom(); // ****** Main Tick ****** if(mainTick < t && !flagIsSet(P_NO_TICK_HP) && !noTick) { int hpTickAmt = 0; if(cClass != LICH) { hpTickAmt = MAX(1, 3 + bonus(constitution.getCur()) + (cClass == BERSERKER ? 2:0)); if(!ill && !deathSickness) { if(!flagIsSet(P_BLOODSAC)) { if(flagIsSet(P_SITTING)) hpTickAmt += 1; else if(flagIsSet(P_SLEEPING)) hpTickAmt += 3; } if(room && room->flagIsSet(R_FAST_HEAL)) hpTickAmt += 3; } } else if(cClass == LICH && !flagIsSet(P_NO_TICK_MP)) { hpTickAmt = MAX(1, 4+(constitution.getCur() > 170 ? 1:0)); if(!ill && !deathSickness) { if(flagIsSet(P_SITTING)) hpTickAmt += 1; if(room && room->flagIsSet(R_FAST_HEAL)) hpTickAmt += 2; } } hp.increase(hpTickAmt, flagIsSet(P_BLOODSAC)); if(!ill && !deathSickness) hp.increase(getHpTickBonus(), flagIsSet(P_BLOODSAC));; lasttime[LT_TICK].ltime = t; lasttime[LT_TICK].interval = 45 - 5*bonus((int)constitution.getCur()); if(getRoom()->flagIsSet(R_FAST_HEAL)) lasttime[LT_TICK].interval /= 2; if(deathSickness && mrand(1,100) < 50) { lasttime[LT_TICK].interval += 10; } } // ****** End Main Tick ****** // ****** Secondary Tick ****** // It's time to tick and (they don't have NO_MP_TICK set OR they're a pure fighter) and they're not a lich if(secTick < t) { if(!flagIsSet(P_NO_TICK_MP) && cClass != LICH && !noTick && !(cClass == FIGHTER && !cClass2 && flagIsSet(P_PTESTER)) ) { // Non fighters handle here int mpTickAmt = 0; if(cClass == MAGE || (cClass2 == MAGE && (cClass == FIGHTER || cClass == THIEF)) ) mpTickAmt += 2; mpTickAmt += MAX(1, 2+(intelligence.getCur() > 170 ? 1:0)); if(!ill && !deathSickness) { if(flagIsSet(P_SITTING)) mpTickAmt += 1; else if(flagIsSet(P_SLEEPING)) mpTickAmt += 3; if(room && room->flagIsSet(R_FAST_HEAL)) mpTickAmt += 2; } mp.increase(mpTickAmt); if(!ill && !deathSickness) mp.increase(getMpTickBonus()); lasttime[LT_TICK_SECONDARY].ltime = t; // MP Ticking is now based on piety lasttime[LT_TICK_SECONDARY].interval = 45 - 5*bonus((int)piety.getCur()); if(getRoom()->flagIsSet(R_FAST_HEAL)) lasttime[LT_TICK_SECONDARY].interval /= 2; if(deathSickness && mrand(1,100) < 50) lasttime[LT_TICK_SECONDARY].interval += 10; } else if(cClass == FIGHTER && !cClass2 && flagIsSet(P_PTESTER)) { // Pure fighters handled here if(!inCombat()) decreaseFocus(); // Set secondary tick timer here for fighters lasttime[LT_TICK_SECONDARY].ltime = t; // More constitution keeps them focused longer lasttime[LT_TICK_SECONDARY].interval = 20 + 2*bonus((int)constitution.getCur()); // Stay focus longer if in a fast heal room if(getRoom()->flagIsSet(R_FAST_HEAL) && !deathSickness) lasttime[LT_TICK_SECONDARY].interval += 10; else if(deathSickness) lasttime[LT_TICK_SECONDARY].interval -= 10; } } // ****** End Secondary Tick ****** // ****** Bad Tick ****** // Poison and room harms and such if(badTick < t) { // Handle DoT effects // a hardcore death will invalidate us if(doDoTs() && hardcore) { zero(this, sizeof(this)); return; } // a hardcore death will invalidate us if(doPlayerHarmRooms() && hardcore) { zero(this, sizeof(this)); return; } // Set bad tick timer here lasttime[LT_TICK_HARMFUL].ltime = t; if(flagIsSet(P_OUTLAW) && room && room->flagIsSet(R_OUTLAW_SAFE)) { // Outlaws hiding in an outlaw safe room get hurt more often lasttime[LT_TICK_HARMFUL].interval = 20; } else { // If you have higher piety, you get hurt less often if(cClass != LICH) lasttime[LT_TICK_HARMFUL].interval = 45 + 5*bonus((int)piety.getCur()); else lasttime[LT_TICK_HARMFUL].interval = 45 + 5*bonus((int)constitution.getCur()); if(isEffected("vampirism") && isDay()) lasttime[LT_TICK_HARMFUL].interval -= 15; } } // ****** End Bad Tick ****** // a hardcore death will invalidate us if(vamp && sunlightDamage() && hardcore) { zero(this, sizeof(this)); return; } } //********************************************************************* // getHpTickBonus //********************************************************************* int Player::getHpTickBonus() const { int bonus = 0; switch(cClass) { case FIGHTER: if(!cClass2 || cClass2 == THIEF) bonus = level/6; break; // More or less pure fighter classes - fall through cases case ASSASSIN: case BERSERKER: case WEREWOLF: case ROGUE: case MONK: case THIEF: bonus = level/6; break; // Now handle the hybrids case RANGER: case PALADIN: case DEATHKNIGHT: case BARD: case VAMPIRE: bonus = (level+4)/10; break; // And then the pure casters case MAGE: case CLERIC: case DRUID: bonus = 0; break; // Lich is a special case, using hp as mp...so give them a bonus here case LICH: bonus = level/6; break; default: bonus = 0; break; } return(bonus); } //********************************************************************* // getMpTickBonus //********************************************************************* int Player::getMpTickBonus() const { int bonus = 0; switch(cClass) { case FIGHTER: if(cClass2 && cClass2 == MAGE) bonus = level/6; break; // More or less pure fighter classes - fall through cases case ASSASSIN: case BERSERKER: case WEREWOLF: case ROGUE: case MONK: case THIEF: bonus = 0; break; // Now handle the hybrids case RANGER: case PALADIN: case DEATHKNIGHT: case BARD: case VAMPIRE: bonus = (level+4)/10; break; // And then the pure casters case MAGE: case CLERIC: case DRUID: bonus = level/6; break; // Lich is a special case, using hp as mp...so no bonus here case LICH: bonus = 0; break; default: bonus = 0; break; } return(bonus); } //********************************************************************* // doDoTs //********************************************************************* // return true if they died, false if they lived bool Player::doDoTs() { if(isStaff()) return(false); if(doPetrificationDmg()) return(true); return(false); } //********************************************************************* // winterProtection //********************************************************************* double Object::winterProtection() const { double percent = 0; if(subType != "cloth" && subType != "leather") return(0); switch(wearflag) { case BODY: percent = 1; break; case ARMS: case LEGS: case NECK: case HANDS: case HEAD: case FEET: case FACE: percent = getLocationModifier() * 2; break; default: break; } if(subType == "cloth") percent /= 2; return(percent); } double Player::winterProtection() const { double percent = 0; int i=0; if(cClass == LICH || isEffected("lycantrhopy") || race == MINOTAUR) return(1); // how much protection do this person's equipment provide them? for(i=0; i<MAXWEAR; i++) if(ready[i]) percent += ready[i]->winterProtection(); return(MAX(0, MIN(1, percent))); } //********************************************************************* // doPlayerHarmRooms //********************************************************************* // return true if they died, false if they lived bool Player::doPlayerHarmRooms() { DeathType dt; bool prot = true; // gives us a range of 10-18 dmg int dmg = 15 - (constitution.getCur() - 150)/50; BaseRoom* room = getRoom(); // Poison rooms if(room->flagIsSet(R_POISONS) && !isPoisoned() && !immuneToPoison()) { wake("Terrible nightmares disturb your sleep!"); printColor("^#^GThe toxic air poisoned you.\n"); if(!isStaff()) { poison(0, 15, 210 + standardPoisonDuration(15, constitution.getCur())); poisonedBy = "noxious fumes"; } } // Stun rooms if(room->flagIsSet(R_STUN)) { wake("You awaken suddenly!"); print("The room starts to spin around you.\nYou feel confused.\n"); if(!isStaff()) { updateAttackTimer(true, MAX(dice(2,6,0), 6)); stun(mrand(3,9)); } } // Mp Drain rooms if(room->flagIsSet(R_DRAIN_MANA) && !isStaff()) { wake("Terrible nightmares disturb your sleep!"); if(cClass != LICH) mp.decrease(MIN(mp.getCur(),6)); if(cClass == LICH) { hp.decrease(MIN(hp.getCur(), 6)); if(hp.getCur() < 1) die(DRAINED); } } if( room->flagIsSet(R_PLAYER_HARM) || // these flags don't need PHARM set to work room->flagIsSet(R_DEADLY_VINES) || room->flagIsSet(R_WINTER_COLD) || room->flagIsSet(R_DESERT_HARM) || room->flagIsSet(R_ICY_WATER) ) { if( ( room->flagIsSet(R_FIRE_BONUS) || (room->flagIsSet(R_DESERT_HARM) && isDay()) ) && !isEffected("heat-protection") && !isEffected("alwayswarm") ) { wake("You awaken suddenly"); printColor("^rThe searing heat burns your flesh.\n"); dt = BURNED; prot = false; } else if( !isEffected("breathe-water") && !doesntBreathe() && ( room->flagIsSet(R_WATER_BONUS) ||( room->flagIsSet(R_UNDER_WATER_BONUS) || (area_room && area_room->isWater() && !isEffected("fly")) ) ) ) { wake("You awaken suddenly!"); printColor("^bWater fills your lungs.\n"); dt = DROWNED; prot = false; } else if(room->flagIsSet(R_ICY_WATER) && cClass != LICH && !isEffected("warmth") && !isEffected("alwayscold")) { wake("You awaken suddenly!"); printColor("^CThe ice cold water freezes your blood.\n"); dt = COLDWATER; prot = false; } else if(room->flagIsSet(R_EARTH_BONUS) && !isEffected("earth-shield")) { wake("You awaken suddenly!"); printColor("^YThe earth swells up around you and smothers you.\n"); dt = SMOTHER; prot = false; } else if(room->flagIsSet(R_ELEC_BONUS) && !isEffected("static-field")) { wake("You awaken suddenly!"); printColor("^WZAP! You are stuck by bolts of static electricity!\n"); dt = LIGHTNING; prot = false; } else if(room->flagIsSet(R_AIR_BONUS) && !isEffected("fly")) { wake("You awaken suddenly!"); printColor("^yTurbulent winds batter and thrash you about.\n"); dt = WINDBATTERED; prot = false; } else if( ( room->flagIsSet(R_COLD_BONUS) || (room->flagIsSet(R_DESERT_HARM) && !isDay() && cClass != LICH) || (room->isWinter() && cClass != LICH) ) && !isEffected("warmth") && !isEffected("alwayscold") ) { prot = false; // winter cold is not as severe and is easier to protect against if(room->flagIsSet(R_WINTER_COLD)) { dmg -= 5; dmg -= (int)(dmg * winterProtection()); if(!dmg) prot = true; } if(!prot) { wake("You awaken suddenly!"); printColor("^bThe freezing temperature chills you to the bone.\n"); dt = FROZE; } } else if(room->flagIsSet(R_DEADLY_VINES)) { if(cClass != DRUID && !isStaff() && deity != LINOTHAN && !isEffected("pass-without-trace")) { wake("You awaken suddenly!"); printColor("^gLiving vines reach out and crush the air from your lungs.\n"); dt = VINES; prot = false; } else { printColor("^gLiving vines crawl dangerously close to you, but ignore you.\n"); prot = true; } } else if(!room->flagIsSet(R_AIR_BONUS) && !room->flagIsSet(R_EARTH_BONUS) && !room->flagIsSet(R_FIRE_BONUS) && !room->flagIsSet(R_WATER_BONUS) && !room->flagIsSet(R_POISONS) && !room->flagIsSet(R_STUN) && !room->flagIsSet(R_DRAIN_MANA) && !room->flagIsSet(R_COLD_BONUS) && !room->flagIsSet(R_ELEC_BONUS) && !room->flagIsSet(R_AIR_BONUS) && !room->flagIsSet(R_DEADLY_VINES) && !room->flagIsSet(R_WINTER_COLD) && !room->flagIsSet(R_DESERT_HARM) && !room->flagIsSet(R_ICY_WATER) ) { wake("You awaken suddenly!"); printColor("^MAn invisible force saps your life force.\n"); dt = DRAINED; prot = false; } } if(isStaff()) prot = true; // Not protected if(!prot) { hp.decrease(MAX(1,dmg)); if(hp.getCur() < 1) { die(dt); return(true); } } if(flagIsSet(P_OUTLAW) && room->flagIsSet(R_OUTLAW_SAFE)) { wake("Terrible nightmares disturb your sleep!"); printColor("^#^rEnergy bolts blast you!\n"); printColor("Don't stay in this room!\n"); hp.decrease(hp.getMax()/4); // lasttime[LT_TICK].ltime = t; // lasttime[LT_TICK].interval = 20L; if(hp.getCur() < 1) { print("You are blown away by energy bolts. You died!\n"); broadcast(getSock(), getRoom(), "%M was blown to bits by energy bolts. %s died.", this, heShe()); die(BOLTS); return(true); } } return(false); } //********************************************************************* // getIdle //********************************************************************* time_t Player::getIdle() { return(time(0) - mySock->ltime); } //********************************************************************** // chooseItem //********************************************************************** // This function randomly chooses an item that the player pointed to // by the first argument is wearing. int Player::chooseItem() { char checklist[MAXWEAR]; int numwear=0, i; for(i=0; i<MAXWEAR; i++) { checklist[i] = 0; if(i==WIELD-1 || i==HELD-1) continue; if(ready[i]) checklist[numwear++] = i+1; } if(!numwear) return(0); i = mrand(0, numwear-1); return(checklist[i]); } //********************************************************************** // resetObjectIds //********************************************************************** int doSetObjectIds(otag* op, int id) { while(op) { op->obj->setUniqueId(id++); id = doSetObjectIds(op->obj->first_obj, id); op = op->next_tag; } return(id); } void Player::resetObjectIds() { uniqueObjId = doSetObjectIds(first_obj, 0); for(int i=0; i<MAXWEAR; i++) { if(ready[i]) { ready[i]->setUniqueId(uniqueObjId++); uniqueObjId = doSetObjectIds(ready[i]->first_obj, uniqueObjId); } } } //********************************************************************** // setObjectId //********************************************************************** void Player::setObjectId(Object* object) { // don't let unique IDs get out of hand if(uniqueObjId > 1000000) resetObjectIds(); object->setUniqueId(uniqueObjId++); uniqueObjId = doSetObjectIds(object->first_obj, uniqueObjId); }