/*
* 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-2012 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 "calendar.h"
#include "guilds.h"
#include "property.h"
// C++ includes
#include <sstream>
#include <iomanip>
#include <locale>
//********************************************************************
// canSee
//********************************************************************
bool Creature::canSeeRoom(const BaseRoom* room, bool p) const {
if(isStaff())
return(true);
const Player* player = getAsConstPlayer();
// 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());
// if they can't see, maybe someone else in the room has light for them
if(!normal_sight) {
for(Player* ply : room->players) {
if(ply->getAsPlayer()->getLight()) {
normal_sight = true;
break;
}
}
}
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);
}
bool Creature::canSee(const MudObject* target, bool skip) const {
if(!this || !target)
return(false);
if(target->isCreature()) {
const Creature* cTarget = target->getAsConstCreature();
if(cTarget->isPlayer()) {
if(cTarget->isDm() && cTarget->flagIsSet(P_DM_INVIS) && !isDm())
return(false);
if(cTarget->isCt() && cTarget->flagIsSet(P_DM_INVIS) && !isCt())
return(false);
if(cTarget->isStaff() && cTarget->flagIsSet(P_DM_INVIS) && !isStaff())
return(false);
if(target->isEffected("incognito") && (getClass() < cTarget->getClass()) && getParent() != cTarget->getParent())
return(false);
if(!skip) {
if(cTarget->isInvisible() && !isEffected("detect-invisible") && !isStaff())
return(false);
if(target->isEffected("mist") && !isEffected("true-sight") && !isStaff())
return(false);
}
} else {
if(!skip) {
if(cTarget->isInvisible() && !isEffected("detect-invisible") && !isStaff())
return(false);
}
}
} // End Creature
if(target->isExit()) {
const Exit* exit = target->getAsConstExit();
if(isStaff())
return(true);
// handle NoSee right away
if(exit->flagIsSet(X_NO_SEE))
return(false);
if(exit->isEffected("invisibility") && !isEffected("detect-invisible"))
return(false);
}
if(target->isObject()) {
const Object* object = target->getAsConstObject();
if(isStaff())
return(true);
if(object->isEffected("invisibility") && (this && !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, bool blinking) 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(!blinking && 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_STAFF_ONLY) || exit->flagIsSet(X_NO_SEE)) {
if(p) checkStaff("You cannot go that way.\n");
if(!staff) return(false);
}
if(exit->flagIsSet(X_LOOK_ONLY)) {
if(p) checkStaff("You may only look through that exit.\n");
if(!staff) return(false);
}
if(!blinking && 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( getConstRoomParent()->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^x.\n", tollcost(this->getAsConstPlayer(), exit, 0), exit->getCName());
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) && isEffected("mist")) {
if(p) checkStaff("You may not go that way in mist form.\n");
if(!staff) return(false);
}
if(exit->flagIsSet(X_MIST_ONLY) && !isEffected("mist")) {
if(p) checkStaff(getAsConstPlayer()->canMistNow() ? "You must turn to mist before you can go that way.\n" : "You cannot fit through that exit.\n");
if(!staff) return(false);
}
const Monster* guard = getConstRoomParent()->getGuardingExit(exit, getAsConstPlayer());
if(guard) {
if(p) checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you go there" : "blocks your exit");
if(!staff) return(false);
}
// we are a monster
} else {
if(exit->flagIsSet(X_MIST_ONLY))
return(false);
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 UniqueRoom* 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 = getAsConstMonster();
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) && isEffected("mist")) {
if(p) checkStaff("You may not enter there in mist form.\n");
if(!staff) return(false);
}
if(!staff && !flagIsSet(P_PTESTER) && room->isConstruction()) {
if(p) print("Off the map in that direction.\n");
return(false);
}
if(!staff && room->flagIsSet(R_SHOP_STORAGE)) {
if(p) print("Off the map in that direction.\n");
return(false);
}
if(!Property::canEnter(getAsConstPlayer(), 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;
for(Object *obj : objects) {
if(!obj->flagIsSet(O_WEIGHTLESS_CONTAINER))
n += obj->getActualWeight();
}
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() {
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;
if(isCt())
return(0);
for(Object *obj : objects) {
total += obj->getActualBulk();
}
for(i=0; i<MAXWEAR; i++)
if(ready[i])
total += (ready[i]->getActualBulk()/2);
max = getMaxBulk();
return(n + getTotalBulk() > max);
}
//********************************************************************
// getTotalBulk
//********************************************************************
int Creature::getTotalBulk() const {
int n=0, i=0;
for(Object *obj : objects) {
n += obj->getActualBulk();
}
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 can't 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 &&
!isStaff() &&
!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(object->getType() == WEAPON) {
if(object->getWeaponType() == "none") {
if(!all)
printColor("You lack the skills to wield %P.\n", object);
return(false);
}
if(isStaff())
return(true);
// if(!knowsSkill(object->getWeaponType())) {
if(!knowsSkill(object->getWeaponCategory())) {
if(!all)
print("You are not skilled with %s.\n", gConfig->getSkillDisplayName(object->getWeaponType()).c_str());
return(false);
}
}
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->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;
Object *object3=0;
if(isMonster())
return(0);
for(Object *object : objects) {
if(object->getType() == CONTAINER) {
for(Object *insideObject : object->objects) {
if(insideObject->getType() == SCROLL || insideObject->getType() == POTION || insideObject->getType() == SONGSCROLL) {
continue;
}
if(insideObject->getShotsCur() < 1 || insideObject->flagIsSet(O_NO_PAWN)) {
continue;
}
if(insideObject->value[GOLD] < 20) {
continue;
}
total += tMIN<unsigned long>(MAXPAWN,insideObject->value[GOLD]/2);
total = tMAX<long>(0,tMIN<long>(2000000000,total));
}
}
if(object->getType() == SCROLL || object->getType() == POTION || object->getType() == SONGSCROLL) {
continue;
}
if( (object->getShotsCur() < 1 && object->getType() != CONTAINER) ||
object->flagIsSet(O_NO_PAWN) )
{
continue;
}
if(object->value[GOLD] < 20) {
continue;
}
total += tMIN<unsigned long>(MAXPAWN,object->value[GOLD]/2);
total = tMAX<long>(0,tMIN<long>(2000000000,total));
}
for(a=0;a<MAXWEAR;a++) {
if(!ready[a])
continue;
object3 = ready[a];
if(object3->getType() == CONTAINER) {
for(Object *insideObject : object3->objects) {
if(insideObject->getType() == SCROLL || insideObject->getType() == POTION || insideObject->getType() == SONGSCROLL) {
continue;
}
if(insideObject->getShotsCur() < 1 || insideObject->flagIsSet(O_NO_PAWN)) {
continue;
}
if(insideObject->value[GOLD] < 20) {
continue;
}
total += tMIN<unsigned long>(MAXPAWN,insideObject->value[GOLD]);
total = tMAX<long>(0,tMIN<long>(2000000000,total));
}
}
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+=tMIN<unsigned long>(MAXPAWN,object3->value[GOLD]/2);
total = tMAX<long>(0,tMIN<long>(2000000000,total));
}
return(total);
}
//********************************************************************
// 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, false);
obj[n]->setFlag(O_WORN);
n++;
copy->ready[i] = 0;
}
}
if(copy->getName().empty())
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);
copy->setId("-1");
delete copy;
return(0);
}
//*********************************************************************
// ableToDoCommand
//*********************************************************************
bool Creature::ableToDoCommand(const 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);
}
//*********************************************************************
// 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 {
// people just logging in
if(!getParent())
return(false);
const Monster* mThis = getAsConstMonster();
if(mThis) {
for(Player* ply : getParent()->players) {
if(mThis->isEnemy(ply))
return(true);
}
for(Monster* mons : getParent()->monsters) {
if(mThis->isEnemy(mons))
return(true);
}
} else {
for(Monster* mons : getParent()->monsters) {
if(mons->isEnemy(this) && (!target || mons != target) && (countPets || !mons->isPet()))
return(true);
}
}
return(false);
}
bool Creature::convertFlag(int flag) {
if(flagIsSet(flag)) {
clearFlag(flag);
return(true);
}
return(false);
}
//*********************************************************************
// 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)");
if(viewer)
flags |= viewer->displayFlags();
const Player* pThis = getAsConstPlayer();
// 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()->getName();
} else
crtStr << pThis->getAlias()->getName();
}
// 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( isEffected("mist") && !(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 << getName();
if( flagIsSet(P_DM_INVIS ) )
crtStr << " (+)";
// Invis
else if(isInvisible())
crtStr << " (*)";
// Misted
else if(isEffected("mist"))
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 << getName();
} else
crtStr << getName();
} else if(num == 1) {
if(flagIsSet(M_NO_PREFIX))
crtStr << "";
else {
ch = low(getName()[0]);
if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
crtStr << "an ";
else
crtStr << "a ";
}
crtStr << getName();
} else if(plural != "") {
// use new plural code - on monster
crtStr << int_to_text(num);
crtStr << " " ;
crtStr << plural;
} else {
char tempStr[2056];
strcpy(tempStr, int_to_text(num));
strcat(tempStr, " ");
strcat(tempStr, getCName());
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 {
tempStr[strlen(tempStr)] = 's';
}
crtStr << tempStr;
}
// Target is magic, and viewer has detect magic on
if((flags & MAG) && mFlagIsSet(M_CAN_CAST))
crtStr << " (M)";
}
toReturn = crtStr.str();
if(flags & CAP) {
int pos = 0;
// don't capitalize colors
while(toReturn[pos] == '^') pos += 2;
toReturn[pos] = up(toReturn[pos]);
}
return(toReturn);
}
//*********************************************************************
// inSameRoom
//*********************************************************************
bool Creature::inSameRoom(Creature* target) {
return(target && target->getRoomParent() == getRoomParent());
}
//*********************************************************************
// getLTLeft
//*********************************************************************
// gets the time left on a LT
long Creature::getLTLeft(int myLT, long t) {
if(t == -1)
t = time(0);
long i = lasttime[myLT].ltime + lasttime[myLT].interval;
if(i > t)
return(i-t);
else
return(0);
}
//*********************************************************************
// setLastTime
//*********************************************************************
// Sets a LT
void Creature::setLastTime(int myLT, long t, long interval) {
lasttime[myLT].interval = tMAX(interval, getLTLeft(myLT, t));
lasttime[myLT].ltime = t;
}
//*********************************************************************
// unApplyTongues
//*********************************************************************
void Creature::unApplyTongues() {
Player* pTarget = getAsPlayer();
if(pTarget) {
if(!pTarget->languageIsKnown(LUNKNOWN + pTarget->current_language)) {
bstring selfStr = "";
int i;
selfStr.append("You can no longer speak");
selfStr.append(get_language_adj(pTarget->current_language));
selfStr.append ("\n");
if(pTarget->languageIsKnown(LUNKNOWN+LCOMMON)) {
pTarget->current_language = LCOMMON;
} else {
// oh no, they don't know common; time to find a language they do know
for(i=0; i<LANGUAGE_COUNT; i++) {
if(pTarget->languageIsKnown(LUNKNOWN+i)) {
pTarget->current_language = i;
break;
}
}
}
}
}
}