/* * creatures.cpp * Functions that work on creatures etc * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell * Contributions by Tim Callahan, Jonathan Hseu * Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman * */ // Mud Includes #include "mud.h" #include "version.h" #include "commands.h" #include "effects.h" #include "specials.h" #include "calendar.h" #include "quests.h" #include "guilds.h" #include "property.h" // C++ includes #include <sstream> #include <iomanip> #include <locale> //********************************************************************* // Damage //********************************************************************* Damage::Damage() { reset(); } int Damage::get() const { return(damage); } int Damage::getBonus() const { return(bonus); } int Damage::getDrain() const { return(drain); } int Damage::getReflected() const { return(reflected); } int Damage::getDoubleReflected() const { return(doubleReflected); } int Damage::getPhysicalReflected() const { return(physicalReflected); } ReflectedDamageType Damage::getPhysicalReflectedType() const { return(physicalReflectedType); } int Damage::getPhysicalBonusReflected() const { return(physicalBonusReflected); } void Damage::add(int d) { damage += d; } void Damage::set(int d) { damage = d; } void Damage::includeBonus(int fraction) { if(!fraction) fraction = 1; damage += bonus / fraction; physicalReflected += physicalBonusReflected / fraction; } void Damage::setBonus(int b) { bonus = b; } void Damage::setDrain(int d) { drain = d; } void Damage::setBonus(Damage dmg) { setBonus(dmg.get()); physicalBonusReflected = dmg.getPhysicalReflected(); } void Damage::setReflected(int r) { reflected = r; } void Damage::setDoubleReflected(int r) { doubleReflected = r; } void Damage::setPhysicalReflected(int r) { physicalReflected = r; } void Damage::setPhysicalReflectedType(ReflectedDamageType type) { physicalReflectedType = type; } //********************************************************************* // reset //********************************************************************* void Damage::reset() { damage = bonus = drain = reflected = doubleReflected = physicalReflected = physicalBonusReflected = 0; physicalReflectedType = REFLECTED_NONE; } //********************************************************************* // StringStatistic //********************************************************************* StringStatistic::StringStatistic() { reset(); } void StringStatistic::save(xmlNodePtr rootNode, bstring nodeName) const { if(!value && name == "") return; xmlNodePtr curNode = xml::newStringChild(rootNode, nodeName); xml::saveNonZeroNum(curNode, "Value", value); xml::saveNonNullString(curNode, "Name", name); } void StringStatistic::load(xmlNodePtr curNode) { xmlNodePtr childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Value")) xml::copyToNum(value, childNode); else if(NODE_NAME(childNode, "Name")) xml::copyToBString(name, childNode); childNode = childNode->next; } } void StringStatistic::update(unsigned long num, bstring with) { if(num > value) { value = num; name = stripColor(with); } } void StringStatistic::reset() { value = 0; name = ""; } //********************************************************************* // Statistics //********************************************************************* Statistics::Statistics() { track = true; numPkWon = numPkIn = 0; reset(); } void Statistics::reset() { numSwings = numHits = numMisses = numFumbles = numDodges = numCriticals = numTimesHit = numTimesMissed = numTimesFled = numCasts = numOffensiveCasts = numHealingCasts = numKills = numDeaths = numThefts = numAttemptedThefts = numSaves = numAttemptedSaves = numRecalls = numLagouts = numWandsUsed = numPotionsDrank = numItemsCrafted = numFishCaught = numCombosOpened = mostGroup = numPkIn = numPkWon = 0; mostMonster.reset(); mostAttackDamage.reset(); mostMagicDamage.reset(); long t = time(0); start = ctime(&t); start = start.trim(); } void Statistics::display(const Player* viewer, bool death) { std::ostringstream oStr; // set left aligned oStr.setf(std::ios::left, std::ios::adjustfield); oStr.imbue(std::locale("")); if(death) { // if death = true, player will always be the owner oStr << "^WGeneral player statistics:^x\n" << " Level: ^C" << parent->getLevel() << "^x\n" << " Total Experience: ^C" << parent->getExperience() << "^x\n" << " Time Played: ^C" << parent->getTimePlayed() << "^x\n" << "\n"; } oStr << "Tracking statistics since: ^C" << start << "\n"; if( numSwings || numDodges || numTimesHit || numTimesMissed || numDeaths || numKills || numPkIn ) oStr << "^WCombat^x\n"; if(numSwings) { oStr << " Swings: ^C" << numSwings << "^x\n"; if(numHits) oStr << " Hits: ^C" << numHits << " (" << (numHits * 100 / numSwings) << "%)^x\n"; if(numMisses) oStr << " Misses: ^C" << numMisses << " (" << (numMisses * 100 / numSwings) << "%)^x\n"; if(numFumbles) oStr << " Fumbles: ^C" << numFumbles << " (" << (numFumbles * 100 / numSwings) << "%)^x\n"; if(numCriticals) oStr << " Criticals: ^C" << numCriticals << " (" << (numCriticals * 100 / numSwings) << "%)^x\n"; } if(numDodges) oStr << " Dodges: ^C" << numDodges << "^x\n"; if(numTimesHit) oStr << " Times Hit: ^C" << numTimesHit << "^x\n"; if(numTimesMissed) oStr << " Times Missed: ^C" << numTimesMissed << "^x\n"; // Don't want to see this info after all //if(numTimesFled) // oStr << " Times Fled: ^C" << numTimesFled << "^x\n"; if(numDeaths) oStr << " Deaths: ^C" << numDeaths << "^x\n"; if(numKills) oStr << " Kills: ^C" << numKills << "^x\n"; if(numPkIn) oStr << " Player kills: ^C" << numPkWon << "/" << numPkIn << " (" << (numPkWon * 100 / numPkIn) << "%)^x\n"; if( numCasts || numOffensiveCasts || numHealingCasts || numWandsUsed || numPotionsDrank ) oStr << "\n^WMagic^x\n"; if(numHealingCasts) oStr << " Spells cast: ^C" << numCasts << "^x\n"; if(numCasts) { if(numOffensiveCasts) oStr << " Offensive Spells: ^C" << numOffensiveCasts << " (" << (numOffensiveCasts * 100 / numCasts) << "%)^x\n"; if(numHealingCasts) oStr << " Healing Spells: ^C" << numHealingCasts << " (" << (numHealingCasts * 100 / numCasts) << "%)^x\n"; } if(numWandsUsed) oStr << " Wands used: ^C" << numWandsUsed << "^x\n"; if(numPotionsDrank) oStr << " Potions used: ^C" << numPotionsDrank << "^x\n"; if( mostGroup || mostMonster.value || mostAttackDamage.value || mostMagicDamage.value ) oStr << "\n^WMost / Largest^x\n"; if(mostGroup) oStr << " Largest group: ^C" << mostGroup << "^x\n"; if(mostMonster.value) oStr << " Toughest monster killed: ^C" << mostMonster.name << "^x\n"; if(mostAttackDamage.value) oStr << " Most damage in one attack: ^C" << mostAttackDamage.value << " with " << mostAttackDamage.name << "^x\n"; if(mostMagicDamage.value) oStr << " Most damage in one spell: ^C" << mostMagicDamage.value << " with " << mostMagicDamage.name << "^x\n"; int rooms = parent->numDiscoveredRooms(); if( (numThefts && numAttemptedThefts) || (numSaves && numAttemptedSaves) || numRecalls || numLagouts || numFishCaught || numItemsCrafted || numCombosOpened || rooms ) oStr << "\n^WOther^x\n"; if(numThefts && numAttemptedThefts) oStr << " Items stolen: ^C" << numThefts << "/" << numAttemptedThefts << " (" << (numThefts * 100 / numAttemptedThefts) << "%)^x\n"; if(numSaves && numAttemptedSaves) oStr << " Saving throws made: ^C" << numSaves << "/" << numAttemptedSaves << " (" << (numSaves * 100 / numAttemptedSaves) << "%)^x\n"; if(numRecalls) oStr << " Hazies/word-of-recalls used: ^C" << numRecalls << "^x\n"; if(numLagouts) oStr << " Lagouts: ^C" << numLagouts << "^x\n"; if(numFishCaught) oStr << " Fish caught: ^C" << numFishCaught << "^x\n"; if(numItemsCrafted) oStr << " Items crafted: ^C" << numItemsCrafted << "^x\n"; if(numCombosOpened) oStr << " Combinations opened: ^C" << numCombosOpened << "^x\n"; if(rooms) oStr << " Special rooms discovered: ^C" << rooms << "^x\n"; viewer->printColor("%s\n", oStr.str().c_str()); } unsigned long Statistics::calcToughness(const Monster* monster) { unsigned long t = monster->hp.getMax(); t += monster->getAttackPower(); t += monster->getWeaponSkill(); t += monster->getDefenseSkill(); // level is weighted t += monster->getLevel() * 2; // only a percentage of their mana counts t += (monster->mp.getMax() * monster->getCastChance() / 100); return(t); } bstring Statistics::damageWith(const Player* player, const Object* weapon) { if(weapon) return(weapon->getObjStr(NULL, INV | MAG, 1)); return((bstring)"your " + player->getUnarmedWeaponSkill() + "s"); } unsigned long Statistics::pkDemographics() const { if(numPkIn == numPkWon) return(0); return(pkRank()); } bstring Statistics::getTime() { return(start); } void Statistics::save(xmlNodePtr rootNode, bstring nodeName) const { xmlNodePtr curNode = xml::newStringChild(rootNode, nodeName); xml::newNumChild(curNode, "Track", track); xml::saveNonNullString(curNode, "Start", start); xml::saveNonZeroNum(curNode, "NumSwings", numSwings); xml::saveNonZeroNum(curNode, "NumHits", numHits); xml::saveNonZeroNum(curNode, "NumMisses", numMisses); xml::saveNonZeroNum(curNode, "NumFumbles", numFumbles); xml::saveNonZeroNum(curNode, "NumDodges", numDodges); xml::saveNonZeroNum(curNode, "NumCriticals", numCriticals); xml::saveNonZeroNum(curNode, "NumTimesHit", numTimesHit); xml::saveNonZeroNum(curNode, "NumTimesMissed", numTimesMissed); xml::saveNonZeroNum(curNode, "NumTimesFled", numTimesFled); xml::saveNonZeroNum(curNode, "NumPkIn", numPkIn); xml::saveNonZeroNum(curNode, "NumPkWon", numPkWon); xml::saveNonZeroNum(curNode, "NumCasts", numCasts); xml::saveNonZeroNum(curNode, "NumOffensiveCasts", numOffensiveCasts); xml::saveNonZeroNum(curNode, "NumHealingCasts", numHealingCasts); xml::saveNonZeroNum(curNode, "NumKills", numKills); xml::saveNonZeroNum(curNode, "NumDeaths", numDeaths); xml::saveNonZeroNum(curNode, "NumThefts", numThefts); xml::saveNonZeroNum(curNode, "NumAttemptedThefts", numAttemptedThefts); xml::saveNonZeroNum(curNode, "NumSaves", numSaves); xml::saveNonZeroNum(curNode, "NumAttemptedSaves", numAttemptedSaves); xml::saveNonZeroNum(curNode, "NumRecalls", numRecalls); xml::saveNonZeroNum(curNode, "NumLagouts", numLagouts); xml::saveNonZeroNum(curNode, "NumWandsUsed", numWandsUsed); xml::saveNonZeroNum(curNode, "NumPotionsDrank", numPotionsDrank); xml::saveNonZeroNum(curNode, "NumFishCaught", numFishCaught); xml::saveNonZeroNum(curNode, "NumItemsCrafted", numItemsCrafted); xml::saveNonZeroNum(curNode, "NumCombosOpened", numCombosOpened); xml::saveNonZeroNum(curNode, "MostGroup", mostGroup); mostMonster.save(curNode, "MostMonster"); mostAttackDamage.save(curNode, "MostAttackDamage"); mostMagicDamage.save(curNode, "MostMagicDamage"); } void Statistics::load(xmlNodePtr curNode) { xmlNodePtr childNode = curNode->children; while(childNode) { if(NODE_NAME(childNode, "Track")) xml::copyToBool(track, childNode); else if(NODE_NAME(childNode, "Start")) xml::copyToBString(start, childNode); else if(NODE_NAME(childNode, "NumSwings")) xml::copyToNum(numSwings, childNode); else if(NODE_NAME(childNode, "NumHits")) xml::copyToNum(numHits, childNode); else if(NODE_NAME(childNode, "NumMisses")) xml::copyToNum(numMisses, childNode); else if(NODE_NAME(childNode, "NumFumbles")) xml::copyToNum(numFumbles, childNode); else if(NODE_NAME(childNode, "NumDodges")) xml::copyToNum(numDodges, childNode); else if(NODE_NAME(childNode, "NumCriticals")) xml::copyToNum(numCriticals, childNode); else if(NODE_NAME(childNode, "NumTimesHit")) xml::copyToNum(numTimesHit, childNode); else if(NODE_NAME(childNode, "NumTimesMissed")) xml::copyToNum(numTimesMissed, childNode); else if(NODE_NAME(childNode, "NumTimesFled")) xml::copyToNum(numTimesFled, childNode); else if(NODE_NAME(childNode, "NumPkIn")) xml::copyToNum(numPkIn, childNode); else if(NODE_NAME(childNode, "NumPkWon")) xml::copyToNum(numPkWon, childNode); else if(NODE_NAME(childNode, "NumCasts")) xml::copyToNum(numCasts, childNode); else if(NODE_NAME(childNode, "NumOffensiveCasts")) xml::copyToNum(numOffensiveCasts, childNode); else if(NODE_NAME(childNode, "NumHealingCasts")) xml::copyToNum(numHealingCasts, childNode); else if(NODE_NAME(childNode, "NumKills")) xml::copyToNum(numKills, childNode); else if(NODE_NAME(childNode, "NumDeaths")) xml::copyToNum(numDeaths, childNode); else if(NODE_NAME(childNode, "NumThefts")) xml::copyToNum(numThefts, childNode); else if(NODE_NAME(childNode, "NumAttemptedThefts")) xml::copyToNum(numAttemptedThefts, childNode); else if(NODE_NAME(childNode, "NumSaves")) xml::copyToNum(numSaves, childNode); else if(NODE_NAME(childNode, "NumAttemptedSaves")) xml::copyToNum(numAttemptedSaves, childNode); else if(NODE_NAME(childNode, "NumRecalls")) xml::copyToNum(numRecalls, childNode); else if(NODE_NAME(childNode, "NumLagouts")) xml::copyToNum(numLagouts, childNode); else if(NODE_NAME(childNode, "NumWandsUsed")) xml::copyToNum(numWandsUsed, childNode); else if(NODE_NAME(childNode, "NumPotionsDrank")) xml::copyToNum(numPotionsDrank, childNode); else if(NODE_NAME(childNode, "NumFishCaught")) xml::copyToNum(numFishCaught, childNode); else if(NODE_NAME(childNode, "NumItemsCrafted")) xml::copyToNum(numItemsCrafted, childNode); else if(NODE_NAME(childNode, "NumCombosOpened")) xml::copyToNum(numCombosOpened, childNode); else if(NODE_NAME(childNode, "MostGroup")) xml::copyToNum(mostGroup, childNode); else if(NODE_NAME(childNode, "MostMonster")) mostMonster.load(childNode); else if(NODE_NAME(childNode, "MostAttackDamage")) mostAttackDamage.load(childNode); else if(NODE_NAME(childNode, "MostMagicDamage")) mostMagicDamage.load(childNode); childNode = childNode->next; } } void Statistics::swing() { if(track) numSwings++; } void Statistics::hit() { if(track) numHits++; } void Statistics::miss() { if(track) numMisses++; } void Statistics::fumble() { if(track) numFumbles++; } void Statistics::dodge() { if(track) numDodges++; } void Statistics::critical() { if(track) numCriticals++; } void Statistics::wasHit() { if(track) numTimesHit++; } void Statistics::wasMissed() { if(track) numTimesMissed++; } void Statistics::flee() { if(track) numTimesFled++; } // because these two stats are older, they're always tracked void Statistics::winPk() { numPkIn++; numPkWon++; } void Statistics::losePk() { numPkIn++; } void Statistics::cast() { if(track) numCasts++; } void Statistics::offensiveCast() { if(track) numOffensiveCasts++; } void Statistics::healingCast() { if(track) numHealingCasts++; } void Statistics::kill() { if(track) numKills++; } void Statistics::die() { if(track) numDeaths++; } void Statistics::steal() { if(track) numThefts++; } void Statistics::attemptSteal() { if(track) numAttemptedThefts++; } void Statistics::save() { if(track) numSaves++; } void Statistics::attemptSave() { if(track) numAttemptedSaves++; } void Statistics::recall() { if(track) numRecalls++; } void Statistics::lagout() { if(track) numLagouts++; } void Statistics::wand() { if(track) numWandsUsed++; } void Statistics::potion() { if(track) numPotionsDrank++; } void Statistics::fish() { if(track) numFishCaught++; } void Statistics::craft() { if(track) numItemsCrafted++; } void Statistics::combo() { if(track) numCombosOpened++; } void Statistics::group(unsigned long num) { if(track) mostGroup = MAX(num, mostGroup); } void Statistics::monster(const Monster* monster) { if(!track || monster->isPet()) return; mostMonster.update(calcToughness(monster), monster->name); } void Statistics::attackDamage(unsigned long num, bstring with) { if(track) mostAttackDamage.update(num, with); } void Statistics::magicDamage(unsigned long num, bstring with) { if(track) mostMagicDamage.update(num, with); } void Statistics::setParent(Player* player) { parent = player; } //********************************************************************* // pkRank //********************************************************************* // This function returns the rank of the player based on how many // pkills they has been in, and how many they have won unsigned long Statistics::pkRank() const { if(!numPkWon || !numPkIn) return(0); return(numPkWon * 100 / numPkIn); } unsigned long Statistics::getPkin() const { return(numPkIn); } unsigned long Statistics::getPkwon() const { return(numPkWon); } // remove when all players are up to 2.42i void Statistics::setPkin(unsigned long p) { numPkIn = p; } void Statistics::setPkwon(unsigned long p) { numPkWon = p; } //********************************************************************* // creature get //********************************************************************* Location Creature::getLocation() { Location l; if(parent_rom) l.room = room; if(area_room) l.mapmarker = area_room->mapmarker; return(l); } unsigned short Creature::getClass() const { return(cClass); } unsigned short Creature::getLevel() const { return(level); } short Creature::getAlignment() const { return(alignment); } unsigned int Creature::getArmor() const { return(armor); } unsigned long Creature::getExperience() const { return(experience); } unsigned short Creature::getClan() const { return(clan); } mType Creature::getType() const { return(type); } unsigned short Creature::getRace() const { return(race); } unsigned short Creature::getDeity() const { return(deity); } Size Creature::getSize() const { return(size); } unsigned int Creature::getAttackPower() const { return(attackPower); } bstring Creature::getDescription() const { return(description); } bstring Creature::getVersion() const { return(version); } unsigned short Creature::getPoisonDuration() const { return(poison_dur); } unsigned short Creature::getPoisonDamage() const { return(poison_dmg); } Sex Creature::getSex() const { if(isPlayer()) { if(flagIsSet(P_SEXLESS)) return(SEX_NONE); if(flagIsSet(P_MALE)) return(SEX_MALE); return(SEX_FEMALE); } else { if(flagIsSet(M_SEXLESS)) return(SEX_NONE); if(flagIsSet(M_MALE)) return(SEX_MALE); return(SEX_FEMALE); } } unsigned long Creature::getRealm(Realm r) const { return(realm[r-1]); } bstring Creature::getPoisonedBy() const { return(poisonedBy); } bool Creature::inJail() const { if(isStaff()) return(false); if(!parent_rom) return(false); return(parent_rom->flagIsSet(R_JAIL)); } //********************************************************************* // creature set //********************************************************************* void Creature::addExperience(unsigned long e) { setExperience(experience + e); } void Creature::subExperience(unsigned long e) { setExperience(e > experience ? 0 : experience - e); } void Creature::setExperience(unsigned long e) { experience = MIN(2100000000, e); } void Creature::setClass(unsigned short c) { c = MIN(CLASS_COUNT-1, c); if(cClass == VAMPIRE && c != VAMPIRE) removeEffect("vampirism"); else if(cClass == WEREWOLF && c != WEREWOLF) removeEffect("lycanthropy"); if(cClass != VAMPIRE && c == VAMPIRE) { if(!isEffected("vampirism")) { addPermEffect("vampirism"); if(isPlayer()) getPlayer()->makeVampire(); } } else if(cClass != WEREWOLF && c == WEREWOLF) { if(!isEffected("lycanthropy")) { addPermEffect("lycanthropy"); if(isPlayer()) getPlayer()->makeWerewolf(); } } cClass = c; } void Creature::setClan(unsigned short c) { clan = c; } void Creature::setLevel(unsigned short l, bool isDm) { level = MAX(1, MIN(l, isDm ? 127 : MAXALVL)); } void Creature::setAlignment(short a) { alignment = MAX(-1000, MIN(1000, a)); } void Creature::subAlignment(unsigned short a) { setAlignment(alignment - a); } void Creature::setArmor(unsigned int a) { armor = MAX(MIN(a, MAX_ARMOR), 0); } void Creature::setAttackPower(unsigned int a) { attackPower = MIN(1500, a); } void Creature::setDeity(unsigned short d) { deity = MIN(d, DEITY_COUNT-1); } void Creature::setRace(unsigned short r) { race = MIN(gConfig->raceCount()-1, r); } void Creature::setSize(Size s) { size = MAX(NO_SIZE, MIN(MAX_SIZE, s)); } void Creature::setType(unsigned short t) { type = (mType)MIN(MAX_MOB_TYPES-1, t); } void Creature::setType(mType t) { type = t; } void Creature::setDescription(const bstring& desc) { description = desc; if(isMonster()) description.Replace("*CR*", "\n"); } void Creature::setVersion(bstring v) { version = v == "" ? VERSION : v; } void Creature::setVersion(xmlNodePtr rootNode) { xml::copyPropToBString(version, rootNode, "Version"); } void Creature::setPoisonDuration(unsigned short d) { poison_dur = d; } void Creature::setPoisonDamage(unsigned short d) { poison_dmg = d; } void Creature::setSex(Sex sex) { if(isPlayer()) { if(sex == SEX_FEMALE) { clearFlag(P_SEXLESS); clearFlag(P_MALE); } else if(sex == SEX_MALE) { clearFlag(P_SEXLESS); setFlag(P_MALE); } else { setFlag(P_SEXLESS); clearFlag(P_MALE); } } else { if(sex == SEX_FEMALE) { clearFlag(M_SEXLESS); clearFlag(M_MALE); } else if(sex == SEX_MALE) { clearFlag(M_SEXLESS); setFlag(M_MALE); } else { setFlag(M_SEXLESS); clearFlag(M_MALE); } } } void Creature::setDeathType(DeathType d) { deathtype = d; } void Creature::setRealm(unsigned long num, Realm r) { realm[r-1] = MIN(10000000, num); } void Creature::addRealm(unsigned long num, Realm r) { setRealm(getRealm(r) + num, r); } void Creature::subRealm(unsigned long num, Realm r) { setRealm(num > getRealm(r) ? 0 : getRealm(r) - num, r); } void Creature::setPoisonedBy(const bstring& p) { poisonedBy = p; } //********************************************************************* // monster get //********************************************************************* unsigned short Monster::getMobTrade() const { return(mobTrade); } int Monster::getSkillLevel() const { return(skillLevel); } unsigned int Monster::getMaxLevel() const { return(maxLevel); } unsigned short Monster::getNumWander() const { return(numwander); } unsigned short Monster::getLoadAggro() const { return(loadAggro); } unsigned short Monster::getUpdateAggro() const { return(updateAggro); } unsigned short Monster::getCastChance() const { return(cast); } unsigned short Monster::getMagicResistance() const { return(magicResistance); } bstring Monster::getPrimeFaction() const { return(primeFaction); } bstring Monster::getTalk() const { return(talk); } //********************************************************************* // monster set //********************************************************************* void Monster::setMaxLevel(unsigned short l) { maxLevel = MAX(1, MIN(l, MAXALVL)); } void Monster::setCastChance(unsigned short c) { cast = MAX(0, MIN(c, 100)); } void Monster::setMagicResistance(unsigned short m) { magicResistance = MAX(0, MIN(100, m)); } void Monster::setLoadAggro(unsigned short a) { loadAggro = MAX(0, MIN(a, 99)); } void Monster::setUpdateAggro(unsigned short a) { updateAggro = MAX(1, MIN(a, 99)); } void Monster::setNumWander(unsigned short n) { numwander = MAX(0, MIN(6, n)); } void Monster::setSkillLevel(int l) { skillLevel = MAX(0, MIN(100, l)); } void Monster::setMobTrade(unsigned short t) { mobTrade = MAX(0, MIN(MOBTRADE_COUNT-1, t)); } void Monster::setPrimeFaction(bstring f) { primeFaction = f; } void Monster::setTalk(bstring t) { talk = t; talk.Replace("*CR*", "\n"); } //********************************************************************* // player get //********************************************************************* unsigned short Player::getSecondClass() const { return(cClass2); } unsigned short Player::getGuild() const { return(guild); } unsigned short Player::getGuildRank() const { return(guildRank); } unsigned short Player::getActualLevel() const { return(actual_level); } unsigned short Player::getNegativeLevels() const { return(negativeLevels); } unsigned short Player::getWimpy() const { return(wimpy); } unsigned short Player::getTickDamage() const { return(tickDmg); } unsigned short Player::getWarnings() const { return(warnings); } unsigned long Player::getLostExperience() const { return(lostExperience); } unsigned short Player::getPkin() const { return(pkin); } unsigned short Player::getPkwon() const { return(pkwon); } short Player::getLuck() const { return(luck); } unsigned short Player::getWeaponTrains() const { return(weaponTrains); } long Player::getLastLogin() const { return(lastLogin); } long Player::getLastInterest() const { return(lastInterest); } bstring Player::getLastPassword() const { return(lastPassword); } bstring Player::getAfflictedBy() const { return(afflictedBy); } bstring Player::getLastCommunicate() const { return(lastCommunicate); } bstring Player::getLastCommand() const { return(lastCommand); } bstring Player::getSurname() const { return(surname); } bstring Player::getForum() const { return(forum); } long Player::getCreated() const { return(created); } bstring Player::getCreatedStr() const { bstring str; if(created) { str = ctime(&created); str.trim(); } else str = oldCreated; return(str); } Monster* Player::getAlias() const { return(alias_crt); } cDay* Player::getBirthday() const { return(birthday); } bstring Player::getAnchorAlias(int i) const { return(anchor[i] ? anchor[i]->getAlias() : ""); } bstring Player::getAnchorRoomName(int i) const { return(anchor[i] ? anchor[i]->getRoomName() : ""); } const Anchor* Player::getAnchor(int i) const { return(anchor[i]); } bool Player::hasAnchor(int i) const { return(!!anchor[i]); } bool Player::isAnchor(int i, const BaseRoom* room) const { return(anchor[i]->is(room)); } unsigned short Player::getThirst() const { return(thirst); } int Player::numDiscoveredRooms() const { return(roomExp.size()); } int Player::getUniqueObjId() const { return(uniqueObjId); } //********************************************************************* // player set //********************************************************************* void Player::setTickDamage(unsigned short t) { tickDmg = t; } void Player::setWarnings(unsigned short w) { warnings = w; } void Player::addWarnings(unsigned short w) { setWarnings(w + warnings); } void Player::subWarnings(unsigned short w) { setWarnings(w > warnings ? 0 : warnings - w); } void Player::setWimpy(unsigned short w) { wimpy = w; } void Player::setActualLevel(unsigned short l) { actual_level = MAX(1, MIN(l, MAXALVL)); } void Player::setSecondClass(unsigned short c) { cClass2 = MAX(0, MIN(CLASS_COUNT - 1, c)); } void Player::setGuild(unsigned short g) { guild = g; } void Player::setGuildRank(unsigned short g) { guildRank = g; } void Player::setNegativeLevels(unsigned short l) { negativeLevels = MAX(0, MIN(exp_to_lev(experience), l)); } void Player::setLuck(int l) { luck = l; } void Player::setWeaponTrains(unsigned short t) { weaponTrains = t; } void Player::subWeaponTrains(unsigned short t) { setWeaponTrains(t > weaponTrains ? 0 : weaponTrains - t); } void Player::setLostExperience(unsigned long e) { lostExperience = e; } void Player::setLastPassword(bstring p) { lastPassword = p; } void Player::setAfflictedBy(bstring a) { afflictedBy = a; } void Player::setLastLogin(long l) { lastLogin = MAX(0, l); } void Player::setLastInterest(long l) { lastInterest = MAX(0, l); } void Player::setLastCommunicate(bstring c) { lastCommunicate = c; } void Player::setLastCommand(bstring c) { lastCommand = c; lastCommand.trim(); } void Player::setCreated() { created = time(0); } void Player::setSurname(bstring s) { surname = s.left(20); } void Player::setForum(bstring f) { forum = f; } void Player::setAlias(Monster* m) { alias_crt = m; } void Player::setBirthday() { const Calendar* calendar = gConfig->getCalendar(); if(birthday) delete birthday; birthday = new cDay; birthday->setYear(calendar->getCurYear() - gConfig->getRace(getRace())->getStartAge()); birthday->setDay(calendar->getCurDay()); birthday->setMonth(calendar->getCurMonth()); } void Player::delAnchor(int i) { if(anchor[i]) { delete anchor[i]; anchor[i] = 0; } } void Player::setAnchor(int i, bstring a) { delAnchor(i); anchor[i] = new Anchor(a, this); } void Player::setThirst(unsigned short t) { thirst = t; } void Player::setCustomColor(CustomColor i, char c) { customColors[i] = c; } Monster::Monster() { reset(); } Player::Player() { reset(); mySock = NULL; // initial flags for new characters setFlag(P_NO_AUTO_WEAR); } Player* Creature::getMaster() { return((isPet() ? following : this)->getPlayer()); } const Player* Creature::getConstMaster() const { return((isPet() ? following : this)->getConstPlayer()); } bool Creature::isPlayer() const { return(type == PLAYER); } bool Creature::isMonster() const { return(type != PLAYER); } // Resets all common structure elements of creature void Creature::crtReset() { moReset(); myTarget = NULL; Creature* targeter; foreach(targeter, targetingThis) { targeter->clearTarget(false); } // Clear out any skills/factions/etc crtDestroy(); zero(name, sizeof(name)); zero(key, sizeof(key)); poisonedBy = description = ""; version = "0.00"; fd = -1; level = cClass = race = alignment = experience = temp_experience = clan = 0; size = NO_SIZE; type = PLAYER; deathtype = DT_NONE; int i; coins.zero(); zero(realm, sizeof(realm)); zero(flags, sizeof(flags)); zero(spells, sizeof(spells)); zero(quests, sizeof(quests)); zero(languages, sizeof(languages)); questnum = 0; for(i=0; i<MAXWEAR; i++) ready[i] = 0; following = 0; first_fol = 0; first_obj = 0; first_enm = 0; first_tlk = 0; parent_rom = 0; area_room = 0; current_language = 0; afterProf = 0; memset(movetype, 0, sizeof(movetype)); poison_dur = poison_dmg = 0; for(i=0; i<6; i++) proficiency[i] = 0; deity = 0; memset(spellTimer, 0, sizeof(spellTimer)); armor = 0; for(i=0; i<21; i++) misc[i] = 0; } void Monster::reset() { // Call Creature::reset first crtReset(); int i; info.clear(); memset(attack, 0, sizeof(attack)); memset(aggroString, 0, sizeof(aggroString)); talk = ""; baseRealm = NO_REALM; defenseSkill = weaponSkill = attackPower = 0; numwander = loadAggro = 0; memset(last_mod, 0, sizeof(last_mod)); mobTrade = 0; skillLevel = 0; maxLevel = 0; memset(ttalk, 0, sizeof(ttalk)); for(i=0; i<NUM_RESCUE; i++) rescue[i].clear(); updateAggro = 0; cast = 0; magicResistance = 0; jail.clear(); primeFaction = ""; memset(cClassAggro, 0, sizeof(cClassAggro)); memset(raceAggro, 0, sizeof(raceAggro)); memset(deityAggro, 0, sizeof(deityAggro)); std::list<TalkResponse*>::iterator tIt; for(tIt = responses.begin() ; tIt != responses.end() ; tIt++) { delete (*tIt); } responses.clear(); } void Player::reset() { // Call Creature::reset first crtReset(); cClass2 = wimpy = 0; barkskin = 0; weaponTrains = 0; bank.zero(); created = 0; oldCreated = surname = lastCommand = lastCommunicate = password = title = tempTitle = ""; lastPassword = afflictedBy = forum = ""; tickDmg = lostExperience = pkwon = pkin = lastLogin = lastInterest = fishing = uniqueObjId = 0; memset(songs, 0, sizeof(songs)); guild = guildRank = cClass2 = 0; int i; actual_level = warnings = 0; for(i=0; i<MAX_DIMEN_ANCHORS; i++) anchor[i] = 0; negativeLevels = 0; birthday = NULL; first_charm = 0; luck = 0; ansi = 0; alias_crt = 0; scared_of = 0; for(i=0; i<5; i++) tnum[i] = 0; timeout = 0; thirst = 0; resetCustomColors(); lastPawn = 0; roomExp.clear(); lore.clear(); recipes.clear(); std::map<int, QuestCompletion*>::iterator qIt; for(qIt = questsInProgress.begin() ; qIt != questsInProgress.end() ; qIt++) { delete (*qIt).second; } questsInProgress.clear(); questsCompleted.clear(); } Creature::Creature() { hooks.setParent(this); } void Creature::CopyCommon(const Creature& cr) { int i=0; moCopy(cr); description = cr.description; for(i=0; i<3; i++) { strcpy(key[i], cr.key[i]); } fd = cr.fd; level = cr.level; type = cr.type; cClass = cr.cClass; race = cr.race; alignment = cr.alignment; experience = cr.experience; temp_experience = cr.temp_experience; poisonedBy = cr.poisonedBy; damage = cr.damage; clan = cr.clan; questnum = cr.questnum; size = cr.size; for(Realm r = MIN_REALM; r<MAX_REALM; r = (Realm)((int)r + 1)) setRealm(cr.getRealm(r), r); for(i=0; i<CRT_FLAG_ARRAY_SIZE; i++) flags[i] = cr.flags[i]; poison_dur = cr.poison_dur; poison_dmg = cr.poison_dmg; strength = cr.strength; dexterity = cr.dexterity; constitution = cr.constitution; intelligence = cr.intelligence; piety = cr.piety; hp = cr.hp; mp = cr.mp; pp = cr.pp; rp = cr.rp; hpTic = cr.hpTic; mpTic = cr.mpTic; ppTic = cr.ppTic; rpTic = cr.rpTic; armor = cr.armor; current_language = cr.current_language; deity = cr.deity; for(i=0; i<MAXWEAR; i++) ready[i] = cr.ready[i]; following = cr.following; first_fol = cr.first_fol; first_obj = cr.first_obj; first_enm = cr.first_enm; first_tlk = cr.first_tlk; room = cr.room; parent_rom = cr.parent_rom; area_room = cr.area_room; for(i=0; i<6; i++) saves[i] = cr.saves[i]; for(i=0; i<16; i++) languages[i] = cr.languages[i]; for(i=0; i<32; i++) { spells[i] = cr.spells[i]; quests[i] = cr.quests[i]; } coins.set(cr.coins); for(i=0; i<6; i++) proficiency[i] = cr.proficiency[i]; for(i=0; i<20; i++) daily[i] = cr.daily[i]; for(i=0; i<128; i++) lasttime[i] = cr.lasttime[i]; for(i=0; i<16; i++) spellTimer[i] = cr.spellTimer[i]; factions = cr.factions; //skills = cr.skills; CrtSkill* skill; CrtSkill* crSkill; std::map<bstring, CrtSkill*>::const_iterator csIt; for(csIt = cr.skills.begin() ; csIt != cr.skills.end() ; csIt++) { crSkill = (*csIt).second; skill = new CrtSkill; (*skill) = (*crSkill); skills[crSkill->getName()] = skill; } effects.copy(&cr.effects, this); std::list<bstring>::const_iterator mIt; for(mIt = cr.minions.begin() ; mIt != cr.minions.end() ; mIt++) { minions.push_back(*mIt); } SpecialAttack* attack; std::list<SpecialAttack*>::const_iterator sIt; for(sIt = cr.specials.begin() ; sIt != cr.specials.end() ; sIt++) { attack = new SpecialAttack(); (*attack) = *(*sIt); specials.push_back(attack); } attackPower = cr.attackPower; } void Player::doCopy(const Player& cr) { // We want a copy of what we're getting, so clear out anything that was here before reset(); // Copy anything in common with the base class CopyCommon(cr); // Now copy player specfic stuff int i=0; created = cr.created; oldCreated = cr.oldCreated; title = cr.title; password = cr.getPassword(); surname = cr.surname; forum = cr.forum; bank.set(cr.bank); lastPassword = cr.lastPassword; lastCommand = cr.lastCommand; lastCommunicate = cr.lastCommunicate; afflictedBy = cr.afflictedBy; lastLogin = cr.lastLogin; lastInterest = cr.lastInterest; tempTitle = cr.tempTitle; std::list<CatRef>::const_iterator it; if(!cr.roomExp.empty()) { for(it = cr.roomExp.begin() ; it != cr.roomExp.end() ; it++) { roomExp.push_back(*it); } } if(!cr.lore.empty()) { for(it = cr.lore.begin() ; it != cr.lore.end() ; it++) { lore.push_back(*it); } } std::list<int>::const_iterator rt; if(!cr.recipes.empty()) { for(rt = cr.recipes.begin() ; rt != cr.recipes.end() ; rt++) { recipes.push_back(*rt); } } cClass2 = cr.cClass2; wimpy = cr.wimpy; tickDmg = cr.tickDmg; lostExperience = cr.lostExperience; pkwon = cr.pkwon; pkin = cr.pkin; bound = cr.bound; previousRoom = cr.previousRoom; statistics = cr.statistics; for(i=0; i<32; i++) songs[i] = cr.songs[i]; guild = cr.guild; guildRank = cr.guildRank; cClass2 = cr.cClass2; focus = cr.focus; negativeLevels = cr.negativeLevels; actual_level = cr.actual_level; warnings = cr.warnings; for(i=0; i<3; i++) strcpy(movetype[i], cr.movetype[i]); for(i=0; i<MAX_DIMEN_ANCHORS; i++) { if(cr.anchor[i]) { anchor[i] = new Anchor; *anchor[i] = *cr.anchor[i]; } } for(i=0; i<MAX_BUILDER_RANGE; i++) { bRange[i] = cr.bRange[i]; } for(i=0; i<21; i++) misc[i] = cr.misc[i]; if(cr.birthday) { birthday = new cDay; *(birthday) = *(cr.birthday); } strcpy(customColors, cr.customColors); thirst = cr.thirst; weaponTrains = cr.weaponTrains; attackTimer = cr.attackTimer; // TODO: Look at this and make sure it's doing what i want it to do std::pair<int, QuestCompletion*> p; QuestCompletion *oldQuest; QuestCompletion *newQuest; foreach(p, cr.questsInProgress) { oldQuest = p.second; newQuest = new QuestCompletion(*(oldQuest)); //*(newQuest) = *(oldQuest); questsInProgress[p.first] = newQuest; } foreach(const int & q, cr.questsCompleted) { questsCompleted.push_back(q); } } void Monster::doCopy(const Monster& cr) { // We want a copy of what we're getting, so clear out anything that was here before reset(); // Copy anything in common with the base class CopyCommon(cr); // Now do monster specific copies int i; strcpy(aggroString, cr.aggroString); talk = cr.talk; strcpy(last_mod, cr.last_mod); strcpy(ttalk, cr.ttalk); for(i=0; i<3; i++) strcpy(attack[i], cr.attack[i]); for(i=0; i<NUM_RESCUE; i++) rescue[i] = cr.rescue[i]; baseRealm = cr.baseRealm; mobTrade = cr.mobTrade; skillLevel = cr.skillLevel; maxLevel = cr.maxLevel; jail = cr.jail; cast = cr.cast; loadAggro = cr.loadAggro; info = cr.info; updateAggro = cr.updateAggro; numwander = cr.numwander; for(i=0; i<10; i++) carry[i] = cr.carry[i]; primeFaction = cr.primeFaction; magicResistance = cr.magicResistance; for(i=0; i<NUM_ASSIST_MOB; i++) assist_mob[i] = cr.assist_mob[i]; for(i=0; i<NUM_ENEMY_MOB; i++) enemy_mob[i] = cr.enemy_mob[i]; for(i=0; i<3; i++) { strcpy(movetype[i], cr.movetype[i]); } for(i=0; i<4; i++) { cClassAggro[i] = cr.cClassAggro[i]; raceAggro[i] = cr.raceAggro[i]; deityAggro[i] = cr.deityAggro[i]; } for(i=0; i<21; i++) misc[i] = cr.misc[i]; defenseSkill = cr.defenseSkill; weaponSkill = cr.weaponSkill; foreach(TalkResponse* response, cr.responses) { responses.push_back(new TalkResponse(*response)); } } Monster::Monster(Monster& cr) { doCopy(cr); } Monster::Monster(const Monster& cr) { doCopy(cr); } Monster& Monster::operator=(const Monster& cr) { doCopy(cr); return(*this); } Player::Player(Player& cr) { doCopy(cr); } Player::Player(const Player& cr) { doCopy(cr); } Player& Player::operator=(const Player& cr) { doCopy(cr); return(*this); } // Things all subclasses must destroy void Creature::crtDestroy() { moDestroy(); clearTarget(); Creature* targeter; foreach(targeter, targetingThis) { targeter->clearTarget(false); } factions.clear(); CrtSkill* skill; std::map<bstring, CrtSkill*>::iterator csIt; for(csIt = skills.begin() ; csIt != skills.end() ; csIt++) { skill = (*csIt).second; delete skill; } skills.clear(); effects.removeAll(); minions.clear(); SpecialAttack* attack; std::list<SpecialAttack*>::iterator sIt; for(sIt = specials.begin() ; sIt != specials.end() ; sIt++) { attack = (*sIt); delete attack; (*sIt) = NULL; } specials.clear(); } Monster::~Monster() { crtDestroy(); } Player::~Player() { crtDestroy(); int i = 0; if(birthday) { delete birthday; birthday = NULL; } for(i=0; i<MAX_DIMEN_ANCHORS; i++) if(anchor[i]) delete anchor[i]; std::pair<int, QuestCompletion*> p; QuestCompletion *quest; foreach(p, questsInProgress) { quest = p.second; delete quest; } questsInProgress.clear(); questsCompleted.clear(); setLastPawn(0); } bool Creature::isPet() const { if(isPlayer()) return(false); return(flagIsSet(M_PET) && following); } bool Creature::isPublicWatcher() const { if(isMonster()) return(false); return(flagIsSet(P_WATCHER) && !flagIsSet(P_SECRET_WATCHER)); } bool Creature::isWatcher() const { if(isMonster()) return(false); return(isCt() || flagIsSet(P_WATCHER)); } bool Creature::isStaff() const { if(isMonster()) return(false); return(cClass >= BUILDER); } bool Creature::isCt() const { if(isMonster()) return(false); return(cClass >= CARETAKER); } bool Creature::isDm() const { if(isMonster()) return(false); return(cClass == DUNGEONMASTER); } bool Creature::isAdm() const { if(isMonster()) return(false); return((!strcmp(name, "Bane") || !strcmp(name, "Dominus") || !strcmp(name, "Ocelot"))); } bool Creature::flagIsSet(int flag) const { return(flags[flag/8] & 1<<(flag%8)); } void Creature::setFlag(int flag) { flags[flag/8] |= 1<<(flag%8); if(flag == P_NO_TRACK_STATS && isPlayer()) getPlayer()->statistics.track = false; } void Creature::clearFlag(int flag) { flags[flag/8] &= ~(1<<(flag%8)); if(flag == P_NO_TRACK_STATS && isPlayer()) getPlayer()->statistics.track = true; } bool Creature::toggleFlag(int flag) { if(flagIsSet(flag)) clearFlag(flag); else setFlag(flag); return(flagIsSet(flag)); } bool Creature::languageIsKnown(int lang) const { return(languages[lang/8] & 1<<(lang%8)); } void Creature::learnLanguage(int lang) { languages[lang/8] |= 1<<(lang%8); } void Creature::forgetLanguage(int lang) { languages[lang/8] &= ~(1<<(lang%8)); } bool Creature::spellIsKnown(int spell) const { return(spells[spell/8] & 1<<(spell%8)); } void Creature::learnSpell(int spell) { spells[spell/8] |= 1<<(spell%8); } void Creature::forgetSpell(int spell) { spells[spell/8] &= ~(1<<(spell%8)); } bool Player::questIsSet(int quest) const { return(quests[quest/8] & 1<<(quest%8)); } void Player::setQuest(int quest) { quests[quest/8] |= 1<<(quest%8); } void Player::clearQuest(int quest) { quests[quest/8] &= ~(1<<(quest%8)); } bool Player::songIsKnown(int song) const { return(songs[song/8] & 1<<(song%8)); } void Player::learnSong(int song) { songs[song/8] |= 1<<(song%8); } void Player::forgetSong(int song) { songs[song/8] &= ~(1<<(song%8)); } //********************************************************************* // isRace //********************************************************************* // lets us determine race equality with the presence of subraces bool isRace(int subRace, int mainRace) { return(mainRace == subRace || gConfig->getRace(subRace)->getParentRace() == mainRace); } bool Creature::isRace(int r) const { return(::isRace(race, r)); } const char *Creature::hisHer() const { Sex sex = getSex(); if(sex == SEX_FEMALE) return("her"); else if(sex == SEX_MALE) return("his"); return("its"); } const char *Creature::himHer() const { Sex sex = getSex(); if(sex == SEX_FEMALE) return("her"); else if(sex == SEX_MALE) return("him"); return("it"); } const char *Creature::heShe() const { Sex sex = getSex(); if(sex == SEX_FEMALE) return("she"); else if(sex == SEX_MALE) return("he"); return("it"); } const char *Creature::upHisHer() const { Sex sex = getSex(); if(sex == SEX_FEMALE) return("Her"); else if(sex == SEX_MALE) return("His"); return("Its"); } const char *Creature::upHeShe() const { Sex sex = getSex(); if(sex == SEX_FEMALE) return("She"); else if(sex == SEX_MALE) return("He"); return("It"); } //******************************************************************** // getClassString //******************************************************************** bstring Player::getClassString() const { std::ostringstream cStr; cStr << get_class_string(cClass); if(cClass2) cStr << "/" << get_class_string(cClass2); return(cStr.str()); } //******************************************************************** // canSee //******************************************************************** bool Creature::canSee(const BaseRoom* room, bool p) const { if(isStaff()) return(true); const Player* player = getConstPlayer(); // blind people can't see anything if(isBlind()) { if(p) { printColor("^CYou're blind!^x\n"); printColor("^yIt's too dark to see.^x\n"); } return(false); } // magic vision overcomes all darkness if(isEffected("infravision")) return(true); // magic dark can only be overcome by magic vision if(room->isMagicDark()) { if(p) { printColor("^CIt's unnaturally dark here.\n"); printColor("^yIt's too dark to see.\n"); } return(false); } // room is affected by normal darkness if(room->isNormalDark()) { // there are several sources of normal vision bool normal_sight = gConfig->getRace(race)->hasInfravision() || isUndead() || isEffected("lycanthropy") || (player && player->getLight()); ctag* cp=0; // if they cant see, maybe someone else in the room has light for them if(!normal_sight) { cp = room->first_ply; while(cp) { if(cp->crt->getPlayer()->getLight()) { normal_sight = true; break; } cp = cp->next_tag; } } if(!normal_sight) { if(p) printColor("^yIt's too dark to see.\n"); return(false); } } // not any form of dark, then they can see return(true); } //******************************************************************** // canSee //******************************************************************** // This only handles total invisibility - the creature cannot see the target // at all. This does not handle concealment, such as being hidden. // This function can be called on an empty creature to check to see if // an object can be seen by anyone. bool Creature::canSee(const Object* object) const { if(!object) return(false); if(this && isStaff()) return(true); if(object->flagIsSet(O_INVISIBLE) && (this && !isEffected("detect-invisible"))) return(false); return(true); } //******************************************************************** // canSee //******************************************************************** // This only handles total invisibility - the creature cannot see the target // at all. This does not handle concealment, such as being hidden. bool Creature::canSee(const Exit *exit) const { if(!exit) return(false); if(isStaff()) return(true); // handle NoSee right away if(exit->flagIsSet(X_NO_SEE)) return(false); if(exit->flagIsSet(X_INVISIBLE) && !isEffected("detect-invisible")) return(false); return(true); } //******************************************************************** // canSee //******************************************************************** // This only handles total invisibility - the creature cannot see the target // at all. This does not handle concealment, such as being hidden. // The skip boolean skips the invis/mist checks. In effect, it only runs // staff invisibility. This is used in group and groupline. bool Creature::canSee(const Creature* target, bool skip) const { if(!target) return(false); // we are a player if(isPlayer()) { if(target->isPlayer()) { if(target->isDm() && target->flagIsSet(P_DM_INVIS) && !isDm()) return(false); if(target->isCt() && target->flagIsSet(P_DM_INVIS) && !isCt()) return(false); if(target->isStaff() && target->flagIsSet(P_DM_INVIS) && !isStaff()) return(false); if(target->flagIsSet(P_INCOGNITO) && !isCt() && getRoom() != target->getRoom()) return(false); if(!skip) { if(target->isInvisible() && !isEffected("detect-invisible") && !isStaff()) return(false); if(target->flagIsSet(P_MISTED) && !isEffected("true-sight") && !isStaff()) return(false); } } else { if(!skip) { if(target->isInvisible() && !isEffected("detect-invisible") && !isStaff()) return(false); } } // we are a monster } else { if(target->isPlayer()) { if(target->isStaff() && target->flagIsSet(P_DM_INVIS)) return(false); if(!skip) { if(target->isInvisible() && !isEffected("detect-invisible")) return(false); if(target->flagIsSet(P_MISTED) && !isEffected("true-sight")) return(false); } } else { if(!skip) { if(target->isInvisible() && !isEffected("detect-invisible")) return(false); } } } return(true); } //******************************************************************** // canEnter //******************************************************************** // Determines if a player/monster can enter the exit provided. If p is not 0, // it will also display a reason why. This only handles absolutely-unable-to- // enter. M_BLOCK_EXIT is not handled because players can still enter those // exits under certain circumstances (ie, flee). // // Pets are handled differently. They obey special rules for rooms because // returning false here would prevent the player from entering the room. bool Creature::canEnter(const Exit *exit, bool p) const { const Calendar* calendar=0; int staff = isStaff(); if(!exit) return(false); if(isMonster()) p = false; if(exit->target.mapmarker.getArea()) { Area *area = gConfig->getArea(exit->target.mapmarker.getArea()); if(!area) return(false); // y coords are stored upside down in the array // impassable terrain if(!area->canPass(this, &exit->target.mapmarker, true)) { if(p) checkStaff("You can't go there!\n"); if(!staff) return(false); } } // pets can go anywhere if(isPet()) return(true); if(exit->isWall("wall-of-force")) { if(p) checkStaff("^sA wall of force blocks passage in that direction.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_CLOSED)) { if(p) checkStaff("You have to open it first.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_LOCKED)) { if(p) checkStaff("It's locked.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_DM_ONLY) || exit->flagIsSet(X_NO_SEE)) { if(p) checkStaff("You cannot go that way.\n"); if(!staff) return(false); } if(exit->getSize() && size > exit->getSize()) { if(p) checkStaff("Only %s and smaller creatures may go there.\n", getSizeName(exit->getSize()).c_str()); if(!staff) return(false); } if( (exit->flagIsSet(X_MALE_ONLY) && getSex() != SEX_MALE) || (exit->flagIsSet(X_FEMALE_ONLY) && getSex() != SEX_FEMALE) ) { if(p) checkStaff("Your sex prevents you from going that way.\n"); if(!staff) return(false); } if( (exit->flagIsSet(X_NIGHT_ONLY) && isDay()) || (exit->flagIsSet(X_DAY_ONLY) && !isDay()) ) { if(p) checkStaff("You may not go that way %s.\n", isDay() ? "during the day" : "at night"); if(!staff) return(false); } if( getRoom()->getTollkeeper() && (exit->flagIsSet(X_TOLL_TO_PASS) || exit->flagIsSet(X_LEVEL_BASED_TOLL)) ) { if(p) checkStaff("You must pay a toll of %lu gold coins to go through the %s.\n", tollcost(this->getConstPlayer(), exit, 0), exit->name); if(!staff) return(false); } if(exit->clanRestrict(this)) { if(p) checkStaff("Your allegiance prevents you from going that way.\n"); if(!staff) return(false); } if(exit->classRestrict(this)) { if(p) checkStaff("Your class prevents you from going that way.\n"); if(!staff) return(false); } if(exit->raceRestrict(this)) { if(p) checkStaff("Your race prevents you from going that way.\n"); if(!staff) return(false); } if(exit->alignRestrict(this)) { if(p) checkStaff("Your alignment prevents you from going that way.\n"); if(!staff) return(false); } calendar = gConfig->getCalendar(); if(exit->flagIsSet(X_WINTER) && calendar->whatSeason() == WINTER) { if(p) checkStaff("Heavy winter snows prevent you from going that way.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_SPRING) && calendar->whatSeason() == SPRING) { if(p) checkStaff("Spring floods prevent you from going that way.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_SUMMER) && calendar->whatSeason() == SUMMER) { if(p) checkStaff("The summer heat prevent you from going that way.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_AUTUMN) && calendar->whatSeason() == AUTUMN) { if(p) checkStaff("You cannot go that way in autumn.\n"); if(!staff) return(false); } // we are a player if(isPlayer()) { if(exit->flagIsSet(X_NAKED) && getWeight()) { if(p) checkStaff("You cannot bring anything through that exit.\n"); if(!staff) return(false); } if(!canSee(exit)) { if(p) checkStaff("You don't see that exit.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_NEEDS_FLY) && !isEffected("fly")) { if(p) checkStaff("You must fly to go that way.\n"); if(!staff) return(false); } if(exit->flagIsSet(X_NO_MIST) && flagIsSet(P_MISTED)) { if(p) checkStaff("You may not go that way in mist form.\n"); if(!staff) return(false); } Monster* guard = getRoom()->getGuardingExit(exit); if(guard) { if(p) checkStaff("%M blocks your exit.\n", guard); if(!staff) return(false); } // we are a monster } else { if(exit->flagIsSet(X_NO_WANDER)) return(false); if(exit->flagIsSet(X_TO_STORAGE_ROOM)) return(false); if(exit->flagIsSet(X_TO_BOUND_ROOM)) return(false); } return(true); } //******************************************************************** // canEnter //******************************************************************** bool Creature::canEnter(const Room* room, bool p) const { // staff may always go anywhere bool staff = isStaff(); if(!room) { if(p) print("Off the map in that direction.\n"); if(staff) printColor("^eThat room does not exist.\n"); return(false); } // special rules for pets if(isPet()) { const Monster* mThis = getConstMonster(); if( room->isUnderwater() && mThis->getBaseRealm() != WATER && !isEffected("breathe-water") && !doesntBreathe() ) return(false); return(true); } if(isMonster()) p = false; if(size && room->getSize() && size > room->getSize()) { if(p) checkStaff("Only %s and smaller creatures may go there.\n", getSizeName(room->getSize()).c_str()); if(!staff) return(false); } // we are a player if(isPlayer()) { if(room->flagIsSet(R_SHOP_STORAGE)) { if(p) checkStaff("You cannot enter a shop's storage room.\n"); if(!staff) return(false); } if(room->getLowLevel() > level) { if(p) checkStaff("You must be at least level %d to go there.\n", room->getLowLevel()); if(!staff) return(false); } if(level > room->getHighLevel() && room->getHighLevel()) { if(p) checkStaff("Only players under level %d may go there.\n", room->getHighLevel()+1); if(!staff) return(false); } if(room->deityRestrict(this)) { if(p) checkStaff("Only members of the proper faith may go there.\n"); if(!staff) return(false); } if(room->isFull()) { if(p) checkStaff("That room is full.\n"); if(!staff) return(false); } if(room->flagIsSet(R_NO_MIST) && flagIsSet(P_MISTED)) { if(p) checkStaff("You may not enter there in mist form.\n"); if(!staff) return(false); } if(!staff && room->flagIsSet(R_CONSTRUCTION)) { print("Off the map in that direction.\n"); return(false); } if(!Property::canEnter(getConstPlayer(), room, p)) return(false); // we are a monster } else { // no rules for monsters } return(true); } //******************************************************************** // getWeight //******************************************************************** // This function calculates the total weight that a player (or monster) // is carrying in their inventory. int Creature::getWeight() const { int i=0, n=0; otag *op=0; op = first_obj; while(op) { if(!op->obj->flagIsSet(O_WEIGHTLESS_CONTAINER)) n += op->obj->getActualWeight(); op = op->next_tag; } for(i=0; i<MAXWEAR; i++) if(ready[i]) n += ready[i]->getActualWeight(); return(n); } //******************************************************************** // maxWeight //******************************************************************** // This function returns the maximum weight a player can be allowed to // hold in their inventory. int Creature::maxWeight() const { int n = 20 + strength.getCur(); if(cClass == BERSERKER || isCt()) n += level*10; return(n); } //******************************************************************** // tooBulky //******************************************************************** bool Creature::tooBulky(int n) const { int total=0, i=0, max=0; otag *op=0; if(isCt()) return(0); op = first_obj; while(op) { total += op->obj->getActualBulk(); op = op->next_tag; } for(i=0; i<MAXWEAR; i++) if(ready[i]) total += (ready[i]->getActualBulk()/2); max = getMaxBulk(); if(flagIsSet(P_DEEPPOCKETS)) max *= 2; return(n + getTotalBulk() > max); } //******************************************************************** // getTotalBulk //******************************************************************** int Creature::getTotalBulk() const { otag *op=0; int n=0, i=0; op = first_obj; while(op) { n += op->obj->getActualBulk(); op = op->next_tag; } for(i=0; i<MAXWEAR; i++) if(ready[i]) n += (ready[i]->getActualBulk()/2); return(n); } //******************************************************************** // getMaxBulk //******************************************************************** int Creature::getMaxBulk() const { switch(size) { case SIZE_FINE: return(10); case SIZE_DIMINUTIVE: return(30); case SIZE_TINY: return(60); case SIZE_SMALL: return(105); case SIZE_LARGE: return(185); case SIZE_HUGE: return(215); case SIZE_GARGANTUAN: return(245); case SIZE_COLOSSAL: return(275); case SIZE_MEDIUM: default: return(140); } } //******************************************************************** // willFit //******************************************************************** bool Creature::willFit(const Object* object) const { if(isStaff()) return(true); if(size && object->getSize()) { if(size == object->getSize()) return(true); // rings must be exact size if(object->getWearflag() == FINGER) return(false); // weapons and shields can be +- 1 category if( ( object->getType() == WEAPON || (object->getType() == ARMOR && object->getWearflag() == SHIELD) ) && abs(size - object->getSize()) == 1 ) return(true); // other armor can only be larger if(object->getType() == ARMOR && size - object->getSize() == -1) return(true); // if its wrong size, it cant be used return(false); } return(true); } //******************************************************************** // canWear //******************************************************************** bool Player::canWear(const Object* object, bool all) const { ASSERTLOG(object); if(!object->getWearflag() || object->getWearflag() == WIELD || object->getWearflag() == HELD) { if(!all) print("You can't wear that.\n"); return(false); } bstring armorType = object->getArmorType(); if((object->getType() == ARMOR && !object->isLightArmor() && (object->getWearflag() == BODY || object->getWearflag() == ARMS || object->getWearflag() == HANDS)) && (ready[HELD-1] && ready[HELD-1]->getType() < 6) ) { printColor("You can't wear %P and also use a second weapon.\n", object); printColor("%O would hinder your movement too much.\n", object); return(false); } if(object->getWearflag() != FINGER && object->getWearflag() != SHIELD && object->getType() == ARMOR && !knowsSkill(armorType)) { if(!all) printColor("You can't use %P; you lack the ability to wear %s armor.\n", object, armorType.c_str()); return(false); } if(object->getWearflag() == FINGER && ready[FINGER1-1] && ready[FINGER2-1] && ready[FINGER3-1] && ready[FINGER4-1] && ready[FINGER5-1] && ready[FINGER6-1] && ready[FINGER7-1] && ready[FINGER8-1]) { if(!all) print("You don't have any more fingers left.\n"); return(false); } if(object->getWearflag() != FINGER && ready[object->getWearflag()-1]) { if(!all) { printColor("You cannot wear %1P.\nYou're already wearing %1P.\n", object, ready[object->getWearflag()-1]); } return(false); } if(object->isHeavyArmor() && inCombat() && checkHeavyRestrict("")) { if(!all) { printColor("^RYou're too busy fighting to fumble around with heavy armor right now!^x\n"); } return(false); } return(true); } //******************************************************************** // canUse //******************************************************************** bool Player::canUse(Object* object, bool all) { ASSERTLOG(object); if(!willFit(object)) { if(!all) printColor("%O isn't the right size for you.\n", object); return(false); } if( size && object->getSize() && size != object->getSize() && object->getWearflag() != HELD-1 ) { if(!all) printColor("Using %P is awkward due to its size.\n", object); } if(object->getType() == WEAPON) { if(object->getWeaponType() == "none") { if(!all) printColor("You lack the skills to wield %P.\n", object); return(false); } if(!knowsSkill(object->getWeaponType())) { if(!all) print("You are not skilled with %s.\n", gConfig->getSkillDisplayName(object->getWeaponType()).c_str()); return(false); } } if(isEffected("lycanthropy") && flagIsSet(P_FRENZY)) { if(!all) print("You cannot do that while frenzied.\n"); return(false); } if(object->getShotscur() < 1 && object->getType() != WAND) { if(!all) print("You can't. It's broken.\n"); return(false); } if(object->doRestrict(this, !all)) return(false); return(true); } //******************************************************************** // canWield //******************************************************************** bool Creature::canWield(const Object* object, int n) const { int wielding=0, holding=0, shield=0, second=0; if(ready[WIELD-1]) wielding = 1; if(ready[HELD-1]) holding = 1; if(ready[HELD-1] && ready[HELD-1]->getType() < ARMOR) second = 1; if(ready[SHIELD-1]) shield = 1; switch (n) { case HOLDOBJ: if(second) { print("You're wielding a weapon in your off hand. You can't hold that.\n"); return(false); } if(holding) { print("You're already holding something.\n"); return(false); } if(shield && !ready[SHIELD-1]->flagIsSet(O_SMALL_SHIELD)) { print("Your shield is being held by your off hand. You can't hold that.\n"); return(false); } if(wielding && ready[WIELD-1]->needsTwoHands()) { printColor("You're using both hands to wield %P!\n", ready[WIELD-1]); return(false); } break; case WIELDOBJ: if(holding && object->needsTwoHands()) { printColor("You need both hands to wield %P.\nYou have %P in your off hand.\n", object, ready[HELD-1]); return(false); } if(shield && !ready[SHIELD-1]->flagIsSet(O_SMALL_SHIELD) && object->needsTwoHands()) { printColor("You need both hands to wield %P.\n%O uses your off hand.\n", object, ready[SHIELD-1]); return(false); } if(wielding) { printColor("You're already wielding %P.\n", ready[WIELD-1]); return(false); } if(cClass == CLERIC) { bstring objCategory = object->getWeaponCategory(); bstring objType = object->getWeaponType(); switch(deity) { case CERIS: if(objCategory != "crushing" && objCategory != "ranged" && objType != "polearm") { print("%s clerics may only use crushing, pole, and ranged weapons.\n", gConfig->getDeity(deity)->getName().c_str()); return(false); } break; case KAMIRA: if(objCategory == "ranged") { print("%s clerics may not use ranged weapons.\n", gConfig->getDeity(deity)->getName().c_str()); return(false); } break; } } break; case SECONDOBJ: if(second) { printColor("You're already wielding %P in your off hand.\n", ready[HELD-1]); return(false); } if(holding) { printColor("You're already holding %P in your off hand.\n", ready[HELD-1]); return(false); } if(shield && !ready[SHIELD-1]->flagIsSet(O_SMALL_SHIELD)) { printColor("Your shield is being held by your off hand. You can't second %P.\n", object); return(false); } if(object->needsTwoHands()) { printColor("%O requires two hands and cannot be used as a second weapon.\n", object); return(false); } if(wielding && ready[WIELD-1]->needsTwoHands()) { printColor("You're using both hands to wield %P!\n", ready[WIELD-1]); return(false); } if(cClass == RANGER) { if(ready[BODY-1] && !ready[BODY-1]->isLightArmor()) { printColor("You must remove %P to wield a second weapon.\nIt is too heavy.\n", ready[BODY-1]); return(false); } if(ready[ARMS-1] && !ready[ARMS-1]->isLightArmor()) { printColor("You must remove %P to wield a second weapon.\nIt is too heavy.\n", ready[ARMS-1]); return(false); } if(ready[HANDS-1] && !ready[HANDS-1]->isLightArmor()) { printColor("You must remove %P to wield a second weapon.\nIt is too heavy.\n", ready[HANDS-1]); return(false); } } break; case SHIELDOBJ: if(shield) { print("You're already wearing a shield.\n"); return(false); } if(holding && !object->flagIsSet(O_SMALL_SHIELD)) { printColor("You are using your shield arm to hold %P.\n", ready[HELD-1]); return(false); } if(wielding && !object->flagIsSet(O_SMALL_SHIELD) && ready[WIELD-1]->needsTwoHands()) { printColor("You're using both arms to wield %P!\n", ready[WIELD-1]); return(false); } break; }// end switch return(true); } //******************************************************************** // getInventoryValue //******************************************************************** unsigned long Creature::getInventoryValue() const { int a=0; long total=0; otag *op=0, *cop=0; Object *object=0, *object2=0, *object3=0; if(isMonster()) return(0); op = first_obj; while(op) { object = op->obj; if(object->getType() == CONTAINER) { cop = object->first_obj; while(cop) { object2 = cop->obj; if(object2->getType() == SCROLL || object2->getType() == POTION || object2->getType() == SONGSCROLL) { cop = cop->next_tag; continue; } if(object2->getShotscur() < 1 || object2->flagIsSet(O_NO_PAWN)) { cop = cop->next_tag; continue; } if(object2->value[GOLD] < 20) { cop = cop->next_tag; continue; } total += MIN(MAXPAWN,object2->value[GOLD]/2); total = MAX(0,MIN(2000000000,total)); cop = cop->next_tag; } } if(object->getType() == SCROLL || object->getType() == POTION || object->getType() == SONGSCROLL) { op = op->next_tag; continue; } if((object->getShotscur() < 1 && object->getType() != CONTAINER) || object->flagIsSet(O_NO_PAWN)) { op = op->next_tag; continue; } if(object->value[GOLD] < 20) { op = op->next_tag; continue; } total += MIN(MAXPAWN,object->value[GOLD]/2); total = MAX(0,MIN(2000000000,total)); op = op->next_tag; } for(a=0;a<MAXWEAR;a++) { if(!ready[a]) continue; object3 = ready[a]; if(object3->getType() == CONTAINER) { cop = object3->first_obj; while(cop) { object2 = cop->obj; if(cop->obj->getType() == SCROLL || cop->obj->getType() == POTION || cop->obj->getType() == SONGSCROLL) { cop = cop->next_tag; continue; } if(cop->obj->getShotscur() < 1 || cop->obj->flagIsSet(O_NO_PAWN)) { cop = cop->next_tag; continue; } if(cop->obj->value[GOLD] < 20) { cop = cop->next_tag; continue; } total += MIN(MAXPAWN,cop->obj->value[GOLD]); total = MAX(0,MIN(2000000000,total)); cop = cop->next_tag; } } if(object3->getType() == SCROLL || object3->getType()== POTION || object3->getType() == SONGSCROLL) continue; if((object3->getShotscur() < 1 && object3->getType() != CONTAINER) || object3->flagIsSet(O_NO_DROP) || object3->flagIsSet(O_NO_PAWN)) continue; if(object3->value[GOLD] < 20) continue; total+=MIN(MAXPAWN,object3->value[GOLD]/2); total = MAX(0,MIN(2000000000,total)); } return(total); } //******************************************************************** // isBrittle //******************************************************************** bool Creature::isBrittle() const { return(isPlayer() && cClass == LICH); } //******************************************************************** // isUndead //******************************************************************** bool Creature::isUndead() const { if(cClass == LICH) return(true); if(isEffected("vampirism")) return(true); if(isMonster()) { if(monType::isUndead(type)) return(true); if(flagIsSet(M_UNDEAD)) return(true); } return(false); } //******************************************************************** // save //******************************************************************** // This function saves a player when the game decides to. It does // not issue a message that the player was saved. This should be called // instead of write_ply becuase it handles saving worn items properly // // This function saves a player's char. Since items need to be un-readied // before a player can be saved to a file, this function makes a duplicate // of the player, unreadies everything on the duplicate, and then saves // the duplicate to the file. Afterwards, the duplicate is freed from // memory. int Player::save(bool updateTime, LoadType saveType) { Player* copy=0; Object *obj[MAXWEAR]; int i=0, n=0; // having an update time option, which should be false for offline // operations, prevents aging of chars and keeps last login accurate if(updateTime) { lastLogin = time(0); lasttime[LT_AGE].interval += (lastLogin - lasttime[LT_AGE].ltime); lasttime[LT_AGE].ltime = lastLogin; } copy = new Player; if(!copy) merror("save", FATAL); *copy = *this; for(i=0; i<MAXWEAR; i++) { if(copy->ready[i]) { obj[n] = copy->unequip(i+1, UNEQUIP_ADD_TO_INVENTORY, false); obj[n]->setFlag(O_WORN); n++; copy->ready[i] = 0; } } if(!copy->name[0]) return(1); copy->checkDarkness(); if(copy->saveToFile(saveType) < 0) printf("*** ERROR: saveXml!\n"); for(i=0; i<n; i++) copy->delObj(obj[i], false, false, true, false); delete copy; return(0); } //********************************************************************* // ableToDoCommand //********************************************************************* bool Creature::ableToDoCommand(cmd* cmnd) const { if(isMonster()) return(true); if(flagIsSet(P_BRAINDEAD)) { print("You are brain-dead. You can't do that.\n"); return(false); } // unconscious has some special rules if(flagIsSet(P_UNCONSCIOUS)) { // if they're sleeping, let them do only a few things if(cmnd && flagIsSet(P_SLEEPING) && ( cmnd->myCommand->getName() == "wake" || cmnd->myCommand->getName() == "snore" || cmnd->myCommand->getName() == "murmur" || cmnd->myCommand->getName() == "dream" || cmnd->myCommand->getName() == "rollover" ) ) return(true); if(flagIsSet(P_SLEEPING)) print("You can't do that while sleeping.\n"); else print("How can you do that while unconscious?\n"); return(false); } return(true); } //********************************************************************* // wake //********************************************************************* void Creature::wake(bstring str, bool noise) { if(isMonster()) return; if(flagIsSet(P_SLEEPING)) { // can't wake sleeping people with noise if(noise && isEffected("deafness")) return; if(str != "") { printColor("%s\n", str.c_str()); broadcast(getSock(), getRoom(), "%M wakes up.", this); } clearFlag(P_SLEEPING); clearFlag(P_UNCONSCIOUS); unhide(); } } //********************************************************************* // isUnconscious //********************************************************************* // unconsciousness provides some special protection in combat for which // sleeping people do not qualify. use this function when being unconscious // will help bool Creature::isUnconscious() const { if(isMonster()) return(false); return(flagIsSet(P_UNCONSCIOUS) && !flagIsSet(P_SLEEPING)); } //********************************************************************* // knockUnconscious //********************************************************************* void Creature::knockUnconscious(long duration) { if(isMonster()) return; setFlag(P_UNCONSCIOUS); clearFlag(P_SLEEPING); lasttime[LT_UNCONSCIOUS].ltime = time(0); lasttime[LT_UNCONSCIOUS].interval = duration; } //********************************************************************* // isBraindead //********************************************************************* bool Creature::isBraindead() const { if(isMonster()) return(false); return(flagIsSet(P_BRAINDEAD)); } //********************************************************************* // inCombat //********************************************************************* // we don't care if they are fighting people in other rooms - // we only care if they're fighting someone in THIS room bool Creature::inCombat(bool countPets) const { return(inCombat(0, countPets)); } // target is used when you want to check for a player being in combat // with a mob BESIDES the one pointed to by creature. bool Creature::inCombat(const Creature* target, bool countPets) const { BaseRoom* room = getRoom(); // people just logging in if(!room) return(false); const Monster* mThis = getConstMonster(); ctag *cp=0; if(mThis) { cp = room->first_ply; while(cp) { if(mThis->isEnmCrt(cp->crt->name)) return(true); cp = cp->next_tag; } cp = room->first_mon; while(cp) { if(mThis->isEnmCrt(cp->crt->name)) return(true); cp = cp->next_tag; } } else { cp = room->first_mon; while(cp) { if( cp->crt->getConstMonster()->isEnmCrt(name) && (!target || cp->crt != target) && (countPets || !cp->crt->isPet()) ) return(true); cp = cp->next_tag; } } return(false); } BaseRoom *Creature::getRoom() const { if(parent_rom) return(parent_rom); return(area_room); } bstring Creature::fullName() const { const Player *player = getConstPlayer(); bstring str = name; if(player && player->flagIsSet(P_CHOSEN_SURNAME)) { str += " "; str += player->getSurname(); } return(str); } void Creature::unmist() { if(isMonster()) return; if(!flagIsSet(P_MISTED)) return; clearFlag(P_MISTED); clearFlag(P_SNEAK_WHILE_MISTED); unhide(); printColor("^mYou return to your natural form.\n"); if(getRoom()) broadcast(getSock(), getRoom(), "%M reforms.", this); } bool Creature::unhide(bool show) { if(isMonster()) { if(!flagIsSet(M_HIDDEN)) return(false); clearFlag(M_HIDDEN); setFlag(M_WAS_HIDDEN); } else { if(!flagIsSet(P_HIDDEN)) return(false); clearFlag(P_HIDDEN); if(show) printColor("^cYou have become unhidden.\n"); } return(true); } Realm Monster::getBaseRealm() const { return(baseRealm); } void Monster::setBaseRealm(Realm toSet) { baseRealm = toSet; } void Creature::convertOldFlags() { } bool Creature::convertFlag(int flag) { if(flagIsSet(flag)) { clearFlag(flag); return(true); } return(false); } //********************************************************************* // hasMp //********************************************************************* bool Creature::hasMp() const { return(mp.getMax() != 0 && cClass != BERSERKER && cClass != LICH); } //********************************************************************* // getCrtStr //********************************************************************* bstring Creature::getCrtStr(const Creature* viewer, int flags, int num) const { std::ostringstream crtStr; bstring toReturn = ""; char ch; int mobNum=0; // char *str; if(!this) return("(ERROR: NULL CRT)"); flags |= viewer->displayFlags(); const Player* pThis = getConstPlayer(); // Player if(isPlayer()) { // Target is possessing a monster -- Show the monsters name if invis if(flagIsSet(P_ALIASING) && flagIsSet(P_DM_INVIS)) { if(!pThis->getAlias()->flagIsSet(M_NO_PREFIX)) { crtStr << "A " << pThis->getAlias()->name; } else crtStr << pThis->getAlias()->name; } // Target is a dm, is dm invis, and viewer is not a dm OR // Target is a ct, is dm invis, and viewer is not a dm or ct OR // Target is staff less than a ct and is dm invis, viewier is less than a builder else if((cClass == DUNGEONMASTER && flagIsSet(P_DM_INVIS) && !(flags & ISDM) ) || (cClass == CARETAKER && (flagIsSet(P_DM_INVIS) && !(flags & ISDM) && !(flags & ISCT))) || (flagIsSet(P_DM_INVIS) && !(flags & ISDM) && !(flags & ISCT) && !(flags & ISBD)) ) { crtStr << "Someone"; } // Target is misted, viewer can't detect mist, or isn't staff else if( flagIsSet(P_MISTED) && !(flags & MIST) && !(flags & ISDM) && !(flags & ISCT) && !(flags & ISBD)) { crtStr << "A light mist"; } // Target is invisible and viewer doesn't have detect-invis or isn't staff else if(isInvisible() && !(flags & INV) && !(flags & ISDM) && !(flags & ISCT) && !(flags & ISBD)) { crtStr << "Someone"; } // Can be seen else { crtStr << name; if( flagIsSet(P_DM_INVIS ) ) crtStr << " (+)"; // Invis else if(isInvisible()) crtStr << " (*)"; // Misted else if(flagIsSet(P_MISTED)) crtStr << " (m)"; } toReturn = crtStr.str(); return(toReturn); } // Monster // Target is a monster, is invisible, and viewer doesn't have detect-invis or is not staff if(isMonster() && isInvisible() && !(flags & INV) && !(flags & ISDM) && !(flags & ISCT) && !(flags & ISBD)) { crtStr << "Something"; } else { if(num == 0) { if(!flagIsSet(M_NO_PREFIX)) { crtStr << "the "; if(!(flags & NONUM)) { mobNum = ((Monster*)this)->getNumMobs(); if(mobNum>1) { crtStr << getOrdinal(mobNum).c_str(); crtStr << " "; } } crtStr << name; } else crtStr << name; } else if(num == 1) { if(flagIsSet(M_NO_PREFIX)) crtStr << ""; else { ch = low(name[0]); if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') crtStr << "an "; else crtStr << "a "; } crtStr << name; } else { char tempStr[2056]; strcpy(tempStr, int_to_text(num)); strcat(tempStr, " "); strcat(tempStr, name); if(flagIsSet(M_MAN_TO_MEN)) { tempStr[strlen(tempStr)-2] = 'e'; } else if(!flagIsSet(M_NO_S_ON_PLURAL)) { tempStr[strlen(tempStr)+1] = 0; tempStr[strlen(tempStr)+2] = 0; if(tempStr[strlen(tempStr)-1] == 's' || tempStr[strlen(tempStr)-1] == 'x') { tempStr[strlen(tempStr)] = 'e'; tempStr[strlen(tempStr)] = 's'; } else { if(flagIsSet(M_F_TO_VE_ON_PLURAL)) { strcat(tempStr, "s"); tempStr[strlen(tempStr)-2] = 'v'; tempStr[strlen(tempStr)-1] = 'e'; tempStr[strlen(tempStr)] = 's'; } else if(flagIsSet(M_Y_TO_IE_ON_PLURAL)) { strcat(tempStr, "s"); tempStr[strlen(tempStr)-2] = 'i'; tempStr[strlen(tempStr)-1] = 'e'; tempStr[strlen(tempStr)] = 's'; } else tempStr[strlen(tempStr)] = 's'; } } crtStr << tempStr; } if(flagIsSet(M_IRREGULAR_PLURAL) && num > 1) { int found; FILE *plural; char pform[80]; char sform[80]; char pfile[80]; found = 0; sprintf(pfile, "%s/plurals.txt", GAMEPATH); plural = fopen(pfile, "r"); if(plural != NULL) { while(!found && !(feof (plural))) { fflush(plural); // get singular form fgets(sform, sizeof(sform), plural); sform[strlen(sform)-1] = 0; if(sform[strlen(sform)-1] == '\r') sform[strlen(sform)-1] = 0; fflush(plural); // get plural form fgets(pform, sizeof(pform), plural); pform[strlen(pform)-1] = 0; if(pform[strlen(pform)-1] == '\r') pform[strlen(pform)-1] = 0; if(strcmp(name, sform) == 0) { if(num < 21) crtStr << int_to_text(num); crtStr << " " ; crtStr << pform; found = 1; } } fclose(plural); } } // Target is magic, and viewer has detect magic on if((flags & MAG) && (isMonster()) && (flagIsSet(M_CAN_CAST))) crtStr << " (M)"; } toReturn = crtStr.str(); if(flags & CAP) toReturn[0] = up(toReturn[0]); return(toReturn); } //********************************************************************* // setSkill //********************************************************************* bool Creature::setSkill(const bstring skillStr, int gained) { if(!gConfig->skillExists(skillStr)) return(false); CrtSkill* crSkill = getSkill(skillStr); //= new CrtSkill(skillStr, cmnd->val[4]); if(crSkill) crSkill->setGained(gained); else { crSkill = new CrtSkill(skillStr, gained); skills[skillStr] = crSkill; } return(true); } //********************************************************************* // isGroupLeader //********************************************************************* // Are we a group leader against the given monster bool Creature::isGroupLeader(Monster* target) { if(isMonster()) return(false); ctag *cp = first_fol; Creature *crt; while(cp) { crt = cp->crt; if(crt && crt->getsGroupExperience(target)) return(true); cp = cp->next_tag; } return(false); } //********************************************************************* // getsGroupExperience //********************************************************************* bool Creature::getsGroupExperience(Monster* target) { // We can get exp if we're a player... return(isPlayer() && // And idle less than 2 minutes getPlayer()->getIdle() < 120 && // And we haven't abused group exp !flagIsSet(P_GROUP_EXP_ABUSE) && // And we're not a DM invis person !flagIsSet(P_DM_INVIS) && // And we are visible! (No more clerics/mages sitting invis leeching) !isInvisible() && // No invis players !flagIsSet(P_HIDDEN) && // No mists either !flagIsSet(P_MISTED) && // no self-declared AFK people !flagIsSet(P_AFK) && // no unconscious people !flagIsSet(P_UNCONSCIOUS) && // no statues !isEffected("petrification") && // and they're on the enemy list target->isEnmCrt(this->name) ); } //********************************************************************* // doesntBreathe //********************************************************************* bool Creature::doesntBreathe() const { return( (monType::noLivingVulnerabilities(type) || isUndead()) && !isEffected("vampirism") // vampires breathe (and can drown) ); } //********************************************************************* // immuneCriticals //********************************************************************* bool Creature::immuneCriticals() const { return(monType::immuneCriticals(type)); } //********************************************************************* // getName //********************************************************************* const char* Creature::getName() { return(name); } //********************************************************************* // getSock //********************************************************************* Socket* Creature::getSock() const { return(NULL); } //********************************************************************* // getSexName //********************************************************************* bstring getSexName(Sex sex) { if(sex == SEX_FEMALE) return("Female"); if(sex == SEX_MALE) return("Male"); if(sex == SEX_NONE) return("Genderless"); return("Unknown"); } //********************************************************************* // getDisplayRace //********************************************************************* unsigned short Creature::getDisplayRace() const { if(isMonster() || !isEffected("illusion")) return(race); return(getEffect("illusion")->getExtra()); } //********************************************************************* // canSpeak //********************************************************************* bool Creature::canSpeak() const { if(isStaff()) return(true); if(isEffected("silence")) return(false); if(getRoom()->isEffected("globe-of-silence")) return(false); return(true); } Creature* Creature::addTarget(Creature* toTarget) { if(!toTarget) return(NULL); // We've already got them targetted! if(toTarget == myTarget) return(myTarget); clearTarget(); toTarget->addTargetingThis(this); myTarget = toTarget; Player* ply = getPlayer(); if(ply) { ply->printColor("You are now targeting %s.\n", myTarget->getName()); } return(myTarget); } void Creature::addTargetingThis(Creature* targeter) { ASSERTLOG(targeter); Player* ply = getPlayer(); if(ply) { ply->printColor("%s is now targeting you!\n", targeter->getName()); } targetingThis.push_back(targeter); } void Creature::clearTarget(bool clearTargetsList) { if(!myTarget) return; Player* ply = getPlayer(); if(ply) { ply->printColor("You are no longer targeting %s!\n", myTarget->getName()); } if(clearTargetsList) myTarget->clearTargetingThis(this); myTarget = NULL; return; } void Creature::clearTargetingThis(Creature* targeter) { ASSERTLOG(targeter); Player* ply = getPlayer(); if(ply) { ply->printColor("%s is no longer targeting you!\n", targeter->getName()); } targetingThis.remove(targeter); } int cmdTarget(Player* player, cmd* cmnd) { if(cmnd->num < 2) { player->print("You are targetting: "); if(player->myTarget) player->print("%s\n", player->myTarget->getName()); else player->print("Noone!\n"); //if(player->isCt()) { { player->print("People targeting you: "); Creature* targetter; int numTargets = 0; foreach(targetter, player->targetingThis) { if(numTargets++ != 0) player->print(", "); player->print("%s", targetter->getName()); } if(numTargets == 0) player->print("Nobody!"); player->print("\n"); } return(0); } if(!strcasecmp(cmnd->str[1], "-c")) { player->print("Clearing target.\n"); player->clearTarget(); return(0); } lowercize(cmnd->str[1], 1); Creature* toTarget = player->getRoom()->findCreature(player, cmnd); if(!toTarget) { player->print("You don't see that here.\n"); return(0); } player->addTarget(toTarget); return(0); } Creature* Creature::getTarget() { return(myTarget); } bool Creature::hasAttackableTarget() { return(getTarget() && getTarget() != this && inSameRoom(getTarget()) && canSee(getTarget())); } bool Creature::inSameRoom(Creature* target) { return(target && target->getRoom() == getRoom()); }