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