roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * monsters.cpp
 *	 Monster routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "factions.h"
#include "move.h"
#include "math.h"
#include "unique.h"

// TODO switch this strcmp to compare
bool Monster::operator <(const Monster& t) const {
    return(strcmp(this->getCName(), t.getCName()) < 0);
}
/*
 * mTypes formatted as case for use in functions below
 *
	case HUMANOID:
	case GOBLINOID:
	case MONSTROUSHUM:
	case GIANTKIN:
	case ANIMAL:
	case DIREANIMAL:
	case INSECT:
	case INSECTOID:
	case ARACHNID:
	case REPTILE:
	case DINOSAUR:
	case AUTOMATON:
	case AVIAN:
	case FISH:
	case PLANT:
	case DEMON:
	case DEVIL:
	case DRAGON:
	case BEAST:
	case MAGICALBEAST:
	case GOLEM:
	case ETHEREAL:
	case ASTRAL:
	case GASEOUS:
	case ENERGY:
	case FAERIE:
	case DEVA:
	case ELEMENTAL:
	case PUDDING:
	case SLIME:
	case UNDEAD:
 *
 */

// This should be called whenever a monster is put into the world or into a room
// If we have an ID of -1, grab the next ID from the server and assign it to this
// monster

void Monster::validateId() {
//	std::cout << "Validating ID for <" << getName() << ">" << std::endl;
	if(id.empty() || id.equals("-1")) {
		setId(gServer->getNextMonsterId());
	}
}

//*********************************************************************
//						pulseTick
//*********************************************************************

bool hearMobTick(Socket* sock) {
	if(!sock->getPlayer() || !isCt(sock))
		return(false);
	return(!sock->getPlayer()->flagIsSet(P_NO_TICK_MSG));
}

void Monster::pulseTick(long t) {
	bool ill = false;
	//bool noTick = false;
	long mainTick = LT(this, LT_TICK);
	long secTick = LT(this, LT_TICK_SECONDARY);

	int hpTickAmt = 0;
	int mpTickAmt = 0;

	ill = isEffected("petrification") || (isPoisoned() && !immuneToPoison());
//	noTick = ill;

	//BaseRoom* room = getRoom();
	// ****** Main Tick ******
	if(mainTick < t && !ill) {
		double power = 0.8;
		int hpTickTime = 60 - 5*bonus(constitution.getCur());

		if(flagIsSet(M_FAST_TICK) && !nearEnemy()) {
			hpTickTime = MAX(1,(15 - (2*bonus(constitution.getCur()))));
			power = 1.0;
		} else if(flagIsSet(M_REGENERATES) && !nearEnemy()) {
			hpTickTime /= 2;
			power = 0.9;
		} else {
			power = 0.8;
		}
		hpTickAmt = static_cast<int>( pow( static_cast<double>(hp.getMax())*0.2, power ) );
		hpTickAmt = hp.increase(hpTickAmt);

		lasttime[LT_TICK].ltime = t;
		lasttime[LT_TICK].interval = hpTickTime;

	}
	// ****** End Main Tick ******

	// ****** Secondary Tick ******
	if(secTick < t && !ill) {
		double power = 0.8;
		int mpTickTime = 60 - 5*bonus(piety.getCur());

		if(flagIsSet(M_FAST_TICK) && !nearEnemy()) {
			mpTickTime = MAX(1,(5 - (2*bonus(piety.getCur()))));
			power = 1.0;
		} else if(flagIsSet(M_REGENERATES) && !nearEnemy()) {
			mpTickTime = mpTickTime * 2 / 3;
			power = 0.9;
		} else {
			mpTickTime -= 5;
			power = 0.8;
		}

		mpTickAmt = static_cast<int>( pow( static_cast<double>(hp.getMax())*0.2, power ) );

		mpTickAmt = mp.increase(mpTickAmt);
		lasttime[LT_TICK_SECONDARY].ltime = t;
		lasttime[LT_TICK_SECONDARY].interval = mpTickTime;
	}
	// ****** End Secondary Tick ******


	if(flagIsSet(M_PERMENANT_MONSTER) && (hpTickAmt || mpTickAmt)) {
		broadcast(hearMobTick, "^y*** %M(L%d,R%s) just ticked. [%d/%dH](+%d) [%d/%dM](+%d)",
			this, level, currentLocation.room.str().c_str(),
			MIN(hp.getCur(),hp.getMax()), hp.getMax(), hpTickAmt,
			MIN(mp.getCur(),mp.getMax()), mp.getMax(), mpTickAmt);
	}

	// reset hide when mob ticks to full and no enemies are in the room
	if(!nearEnemy(this) && hp.getCur() >= hp.getMax()/4) {
		if(flagIsSet(M_WAS_HIDDEN))	// Reset mob-hide
		{
			broadcast(NULL, getRoomParent(), "%M hides in shadows.", this);
			setFlag(M_HIDDEN);
			clearFlag(M_WAS_HIDDEN);
		}
	}
	// Mobs shouldn't stay berserk after they tick full
	if(!nearEnemy(this) && hp.getCur() == hp.getMax()) {
		if(isEffected("berserk")) {
			broadcast(NULL, getRoomParent(), "^r%M's rage diminishes!", this);
			removeEffect("berserk");
			setFlag(M_WILL_BERSERK);
		}
	}
}



//*********************************************************************
//						beneficialCaster
//*********************************************************************
// The following function scans the room and casts various beneficial spells on
// any particular players which need them. It is to be used for monsters in
// clinics and such to make them more interactive. The chance to cast is random.

void Monster::beneficialCaster() {
	int		chance=0, heal=0;


	if(mp.getCur() < 2)
		return;

	for(Player* ply : getRoomParent()->players) {

		if(	ply->flagIsSet(P_DM_INVIS) ||
			ply->flagIsSet(P_HIDDEN) ||
			ply->isEffected("petrification") ||
			!canSee(ply)
		)
			continue;

		// mobs won'twont cast on people in combat
		if(ply->inCombat())
			continue;
		if(!Faction::willBeneCast(ply, primeFaction))
			continue;

		if(ply->flagIsSet(P_DOCTOR_KILLER)) {
			if(mrand(1,100)<=2) {
				broadcast(ply->getSock(), ply->getRoomParent(), "%M spits, \"Doctor killer!\" at %N.", this, ply);
				ply->print("%M spits, \"Doctor killer!\" at you.\n", this);
			}
			continue;
		}

		if(spellIsKnown(S_SLOW_POISON)) {

			chance = mrand(1,100);
			if((mp.getCur() >= 8) && (chance < 10) && !ply->isEffected("slow-poison") && ply->isPoisoned()) {
				if(ply->immuneToPoison())
					continue;

				if(ply->isEffected("slow-poison"))
					continue;


				ply->print("%M casts a slow-poison spell on you.\n", this);
				broadcast(ply->getSock(), ply->getSock(), getRoomParent(),
					"%M casts a slow-poison spell on %N.", this, ply);
				mp.decrease(8);

				ply->addPermEffect("slow-poison");
				continue;
			}
		}

		if(spellIsKnown(S_CURE_DISEASE)) {

			chance = mrand(1,100);
			if((mp.getCur() >= 12) && (chance < 10) && (ply->isDiseased())) {
				if(ply->immuneToDisease())
					continue;

				ply->print("%M casts a cure-disease spell on you.\n", this);

				broadcast(ply->getSock(), ply->getSock(), getRoomParent(), "%M casts a cure-disease spell on %N.",
					this, ply);
				mp.decrease(12);

				ply->cureDisease();
				continue;
			}
		}

		if(ply->getClass() == LICH)
			continue;
		if(ply->flagIsSet(P_POISONED_BY_PLAYER) && mrand(1,100) >= 3)
			continue;

		if(spellIsKnown(S_VIGOR)) {
			chance = mrand(1,100);
			if((mp.getCur() >= 2) && (chance <= 5) && (ply->hp.getCur() < ply->hp.getMax())) {

				ply->print("%M casts a vigor spell on you.\n", this);

				broadcast(ply->getSock(), ply->getSock(), getRoomParent(), "%M casts a vigor spell on %N.",
					this, ply);
				mp.decrease(2);
				heal = mrand(1,6) + bonus((int) piety.getCur());
				heal = MAX(1, heal);

				if(cClass == CLERIC && clan != 12) {
					heal *= 2;
					heal += (level / 2);
				}
				if(	(cClass == CLERIC && clan == 11) ||
					(cClass == PALADIN)
				)
					heal += mrand(1,4);
				ply->hp.increase(heal);
				heal = 0;
				continue;
			}
		}

		if(spellIsKnown(S_MEND_WOUNDS)) {

			chance = mrand(1,100);
			if((mp.getCur() >= 4) && (chance <= 5) && (ply->hp.getCur() < (ply->hp.getMax()/2))) {

				ply->print("%M casts a mend-wounds spell on you.\n", this);

				broadcast(ply->getSock(), ply->getSock(), getRoomParent(), "%M casts a mend-wounds spell on %N.",
					this, ply);
				mp.decrease(4);
				heal = mrand(6,9) + bonus((int) piety.getCur());
				heal = MAX(1, heal);
				if(cClass == CLERIC && clan != 12) {
					heal *= 2;
					heal += (level / 2);
				}
				if(	(cClass == CLERIC && clan == 11) ||
					(cClass == PALADIN)
				)
					heal += mrand(1,4);
				ply->hp.increase(heal);
				heal = 0;
				continue;
			}
		}

		if(spellIsKnown(S_HEAL)) {

			chance = mrand(1,100);
			if((mp.getCur() >= 25) && (chance < 3) && (cClass == CLERIC && clan != 12) &&
			        (ply->hp.getCur() < (ply->hp.getMax()/4)) && (ply->getLevel() >= 5)) {

				ply->print("%M casts a heal spell on you.\n", this);

				broadcast(ply->getSock(), ply->getSock(), getRoomParent(), "%M casts a heal spell on %N.", this, ply);
				mp.decrease(25);

				ply->hp.restore();
				continue;
			}
		}
	}
}


//*********************************************************************
//						mobDeathScream
//*********************************************************************

int Monster::mobDeathScream() {
	long	i=0, t=0, n=0;
	int		death=0;
	Creature* target=0;

	i = lasttime[LT_BERSERK].ltime;
	t = time(0);

	if(t - i < 30L) // Mob has to wait 30 seconds.
		return(0);


	lasttime[LT_DEATH_SCREAM].ltime = t;
	lasttime[LT_DEATH_SCREAM].interval = 30L;

	if(hp.getCur() <= (hp.getMax()/5)) {
		clearFlag(M_DEATH_SCREAM);			// Mobs will not scream at lower then 20% hp.
		return(0);
	}


	broadcast(NULL, getRoomParent(), "%M bellows out a deadly ear-splitting scream!!", this);

	PlayerSet::iterator pIt = getRoomParent()->players.begin();
	PlayerSet::iterator pEnd = getRoomParent()->players.end();
	while(pIt != pEnd) {
	    target = (*pIt++);
		n = mrand(1,15) + level; // Damage done.

		if(target->isEffected("resist-magic"))
			n /= 2;

		target->printColor("^yHarmful vibrations double you over in pain for ^Y%d^y damage!\n", n);

		if(n > (level+11))
			target->stun(mrand(4, 6));


		if(target->isEffected("berserk")) {
			n = n - (n / 5);
			n = MAX(1, n);
		}

		if(isUndead() && target->isEffected("undead-ward"))
			n /= 2;

		if(target->flagIsSet(P_DM_INVIS))
			n=0;

		if(doDamage(target, n, CHECK_DIE)) {
			death = 1;
			continue;
		}

	}

	return(!!death);
}


//*********************************************************************
//						possibleEnemy
//*********************************************************************
// This function looks for possible enemies in a room. It is specifically
// designed for fast wandering, hunting mobs....like inquisitors. -- TC

bool Monster::possibleEnemy() {
	bool	possible=0, nodetect=0;

	ASSERTLOG(this);

	if(	!flagIsSet(M_STEAL_ALWAYS) &&
		!flagIsSet(M_AGGRESSIVE) &&
		!flagIsSet(M_AGGRESSIVE_GOOD) &&
		!flagIsSet(M_AGGRESSIVE_EVIL)
	)
		return(0);

	for(Player* ply : getRoomParent()->players) {
		if(flagIsSet(M_AGGRESSIVE))
			possible = true;
		if(flagIsSet(M_STEAL_ALWAYS))
			possible = true;
		if(flagIsSet(M_AGGRESSIVE_GOOD) && ply->getAlignment() > 99)
			possible = true;
		if(flagIsSet(M_AGGRESSIVE_EVIL) && ply->getAlignment() < -99)
			possible = true;

		if(ply->flagIsSet(P_HIDDEN))
			nodetect = true;
		if(!canSee(ply))
			nodetect = true;

		if(nodetect)
			possible = false;

		if(possible)
			return(true);
	}

	return(possible);
}

//*********************************************************************
//						castSpell
//*********************************************************************
// This function allows monsters to cast spells at players.

int Monster::castSpell(Creature *target) {
	Creature *temp_ptr=0;
	cmd		cmnd;
	int		i=0, spl=0, c=0;
	int		known[20], knowctr=0;
	int		(*fn)(SpellFn);
	bstring enemy;
	int		realm=0, n=0;

	zero(known, sizeof(known));
	for(i=0; i<MAXSPELL; i++) {
		if(knowctr > 19)
			break;
		if(spellIsKnown(i))
			known[knowctr++] = i;
	}

	if(!knowctr)
		return(0);

	spl = known[mrand(0, knowctr-1)];

	// pets are smarter
	if(isPet()) {
		// Pets only cast offensive spells in combat
		if((int(*)(SpellFn, char*, osp_t*)) get_spell_function(spl) != splOffensive) {
			realm = proficiency[1];
			if(realm == CONJUREBARD || realm == CONJUREMAGE || realm == CONJUREANIM)
				realm = getRandomRealm();
			spl = ospell[(realm-1)].splno;
		}

		if(	(int(*)(SpellFn, char*, osp_t*)) get_spell_function(spl) == splOffensive &&
			get_spell_lvl(spl) < 5) {
			// They will cast higher level offensive spells if they have the mana
			for(i=1;i <knowctr; i++) {
				if(	get_spell_lvl(known[i-1]) > get_spell_lvl(spl) &&
					(int(*)(SpellFn, char*, osp_t*))get_spell_function(known[i-1]) == splOffensive
				) {
					for(c=0; ospell[c].splno != get_spell_num(known[i-1]); c++)
						if(ospell[c].splno == -1) {
							return(13);
						}
					if(mp.getCur() >= ospell[c].mp)
						spl = known[i-1];
				}
			}
		}
	}

	int splNo = get_spell_num(spl);
	if(	(int(*)(SpellFn, char*, osp_t*))get_spell_function(spl) == splOffensive ||
		splNo == S_TELEPORT ||
		splNo == S_STUN ||
		splNo == S_WORD_OF_RECALL ||
		splNo == S_DRAIN_EXP ||
		splNo == S_BLINDNESS ||
		splNo == S_SILENCE ||
		splNo == S_FEAR ||
		splNo == S_DISPEL_MAGIC ||
		splNo == S_CANCEL_MAGIC ||
		splNo == S_ANNUL_MAGIC ||
		splNo == S_SCARE ||
		splNo == S_HOLD_PERSON ||
		splNo == S_ENTANGLE ||
		splNo == S_ETHREAL_TRAVEL ||
		splNo == S_DISINTEGRATE ||
		splNo == S_MAGIC_MISSILE ||
		splNo == S_SLOW ||
		splNo == S_WEAKNESS ||
		splNo == S_DAMNATION ||
		splNo == S_FEEBLEMIND ||
		splNo == S_ENFEEBLEMENT ||
		(splNo == S_HEAL && target->getClass() == LICH) ||
		(splNo == S_DISPEL_EVIL && target->getAdjustedAlignment() < NEUTRAL) ||
		(splNo == S_DISPEL_GOOD && target->getAdjustedAlignment() > NEUTRAL) ||
		splNo == S_JUDGEMENT ||
		splNo == S_DEAFNESS ||
		splNo == S_SAP_LIFE ||
		splNo == S_LIFETAP ||
		splNo == S_LIFEDRAW ||
		splNo == S_DRAW_SPIRIT ||
		splNo == S_SIPHON_LIFE ||
		splNo == S_SPIRIT_STRIKE ||
		splNo == S_SOULSTEAL ||
		splNo == S_TOUCH_OF_KESH
	) {
		// we have the exact pointer
		enemy = target->getName();
		n = 1;
		do {
			temp_ptr = getRoomParent()->findCreature(this, enemy, n, false);
			if(!temp_ptr) {
				// something is seriously wrong here
				return(12);
			}

			// move to the next
			n++;

			// keep going till we find the exact match
		} while( target != temp_ptr );

		// we were one too many here
		n--;

		// at this point we know which number is the exact match
		strcpy(cmnd.str[2], target->getCName());
		cmnd.val[2] = n;
		cmnd.num = 3;
	} else {
		// cast on self
		cmnd.num = 2;
	}
	fn = get_spell_function(spl);

	target->printColor("^r");

	SpellData data;
	data.set(CAST, get_spell_school(spl), get_spell_domain(spl), 0, this);
	data.splno = spl;

	if((int(*)(SpellFn, char*, osp_t*))fn == splOffensive) {
		for(c=0; ospell[c].splno != get_spell_num(spl); c++)
			if(ospell[c].splno == -1)
				return(13);
		i = ((int(*)(SpellFn, const char*, osp_t*))*fn)(this, &cmnd, &data, get_spell_name(spl), &ospell[c]);
	} else {
		i = ((int(*)(SpellFn))*fn)(this, &cmnd, &data);
	}

	castDelay(PET_CAST_DELAY);
	return(i);
}

//*********************************************************************
//						petCaster
//*********************************************************************

bool Monster::petCaster() {
	Player *master = getPlayerMaster();
	int		heal=0;
	long	i=0, t = time(0);

	if(!isPet() || !master || !inSameRoom(master))
		return(false);

	i = LT(this, LT_SPELL);
	if(t < i)
		return(false);

	if(mp.getCur() < 2 || mrand(1,100) > 33 || getRoomParent()->flagIsSet(R_NO_MAGIC))
		return(false);

	if(	spellIsKnown(S_HEAL) &&
		master->hp.getCur() <= (master->hp.getMax()/8) &&
		mp.getCur() >= 25 &&
		mrand(1,100) > 50
	) {
		mp.decrease(25);

		master->print("%M casts a heal spell on you.\n", this);
		broadcast(master->getSock(), getRoomParent(),
			"%M casts a heal spell on %N.", this, master);
		master->hp.restore();
		castDelay(PET_CAST_DELAY);
		return(true);
	}

	if(	spellIsKnown(S_MEND_WOUNDS) &&
		master->hp.getCur() <= (master->hp.getMax()/2) &&
		mp.getCur() >= 4 &&
		mrand(1,100) > 25
	) {
		master->print("%M casts a mend-wounds spell on you.\n", this);
		broadcast((Socket*)NULL, master->getSock(), getRoomParent(),
			"%M casts a mend-wounds spell on %N.", this, master);

		mp.decrease(4);

		heal = MAX(bonus((int) intelligence.getCur()), bonus((int) piety.getCur())) +
			   ((cClass == CLERIC) ?
				level + mrand(1, 1 + level / 2) : 0) +
			   (cClass == PALADIN ?
				level / 2 + mrand(1, 1 + level / 3) : 0) +
			   ((isEffected("vampirism") || cClass == BARD || cClass == DEATHKNIGHT) ?
				mrand(1, 1 + level / 5) + 2 : 0) +
			   ((isEffected("lycanthropy") || cClass == MONK) ? level / 5 +
				mrand(1, 1 + level / 7) + 2 : 0) +
			   dice(2, 7, 0);

				this->doHeal(master, heal);
		castDelay(PET_CAST_DELAY);
		return(true);
	}

	if(	spellIsKnown(S_VIGOR) &&
		master->hp.getCur() < master->hp.getMax() &&
		mp.getCur() >= 2
	) {
		master->print("%M casts a vigor spell on you.\n", this);
		broadcast((Socket*)NULL, master->getSock(), getRoomParent(),
			"%M casts a vigor spell on %N.", this, master);
		mp.decrease(2);

		heal = MAX(bonus((int) intelligence.getCur()),bonus((int) piety.getCur())) +
			   ((cClass == CLERIC) ? level / 2 +
				mrand(1, 1 + level / 2) : 0) +
			   (cClass == PALADIN ?
				level / 3 + mrand(1, 1 + level / 4) : 0) +
			   ((isEffected("vampirism") || cClass == BARD || cClass == DEATHKNIGHT) ?
				mrand(1, 1 + level / 5) : 0) +
			   ((isEffected("lycanthropy") || cClass == MONK) ? level/5 +
				mrand(1,1+level/7) : 0)
			   + mrand(1, 8);

				this->doHeal(master, heal);
		castDelay(PET_CAST_DELAY);
		return(true);
	}

	// After all possible spells have been checked on the players...check themselves
	if(	spellIsKnown(S_MEND_WOUNDS) &&
		hp.getCur() <= (hp.getMax()/2) &&
		mp.getCur() >= 4 &&
		mrand(1,100) > 25
	) {
		broadcast(NULL, getRoomParent(), "%M casts a mend-wounds spell on %sself.", this, himHer());
		mp.decrease(4);

		heal = MAX(bonus((int) intelligence.getCur()), bonus((int) piety.getCur())) +
			   ((cClass == CLERIC) ?
				level + mrand(1, 1 + level / 2) : 0) +
			   (cClass == PALADIN ?
				level / 2 + mrand(1, 1 + level / 3) : 0) +
			   ((isEffected("vampirism") || cClass == BARD || cClass == DEATHKNIGHT) ?
				mrand(1, 1 + level / 5) + 2 : 0) +
			   ((isEffected("lycanthropy") || cClass == MONK) ? level / 5 +
				mrand(1, 1 + level / 7) + 2 : 0) +
			   dice(2, 7, 0);

				doHeal(this, heal);
		castDelay(PET_CAST_DELAY);
		return(true);
	}

	if(	spellIsKnown(S_VIGOR) &&
		hp.getCur() < hp.getMax() &&
		mp.getCur() >= 2
	) {

		broadcast(NULL, getRoomParent(), "%M casts a vigor spell on %sself.", this, himHer());
		mp.decrease(2);

		heal = MAX(bonus((int) intelligence.getCur()),bonus((int) piety.getCur())) +
			   ((cClass == CLERIC) ? level / 2 +
				mrand(1, 1 + level / 2) : 0) +
			   (cClass == PALADIN ?
				level / 3 + mrand(1, 1 + level / 4) : 0) +
			   ((isEffected("vampirism") || cClass == BARD || cClass == DEATHKNIGHT) ?
				mrand(1, 1 + level / 5) : 0) +
			   ((isEffected("lycanthropy") || cClass == MONK) ? level/5 +
				mrand(1,1+level/7) : 0)
			   + mrand(1, 8);

		doHeal(this, heal);
		castDelay(PET_CAST_DELAY);
		return(true);
	}

	return(false);
}


//*********************************************************************
//						checkAssist
//*********************************************************************
// See if this monster will assist another monster,
// or if they will be assisted by someone else

bool hearMobAggro(Socket* sock) {
	if(!sock->getPlayer() || !isCt(sock))
		return(false);
	return(!sock->getPlayer()->flagIsSet(P_NO_AGGRO_MSG));
}

bool Monster::checkAssist() {
	int	i=0, assist=0;

	Creature *crt=0;

	for(i=0; i<NUM_ASSIST_MOB; i++) {
		if(assist_mob[i].id) {
			assist = 1;
			break;
		}
	}

	if(assist) {
		for(Monster* mons : getRoomParent()->monsters) {
			if(mons == this)
				continue;

			if(mons->hasEnemy() && willAssist(mons)) {
			    crt = mons->getTarget(false);

			    crt = enm_in_group(crt);

				if(crt && inSameRoom(crt) && !isEnemy(crt)) {
					// if assisting, don't let them swing right away
					updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
					addEnemy(crt, true);

					broadcast(hearMobAggro, "^y*** %s(R:%s) added %s to %s attack list (same room).",
						getCName(), getRoomParent()->fullName().c_str(), crt->getCName(), hisHer());

					setFlag(M_ALWAYS_ACTIVE);
					crt = 0;
				}
			}
		}
	}


	if(flagIsSet(M_WILL_BE_ASSISTED) || primeFaction != "") {
		Creature* target=0;
		if(hasEnemy()) {
			target = getTarget();
			if(target)
				target = target->getAsPlayer();
			if(target) {
			    for(Monster* mons : target->getRoomParent()->monsters) {
					if(mons == this)
						continue;
					if(mons->hasEnemy() || !mons->canSee(target))
						continue;

					if(	(mons->flagIsSet(M_WILL_ASSIST) && flagIsSet(M_WILL_BE_ASSISTED)) ||
						(mons->flagIsSet(M_FACTION_ASSIST) && primeFaction == mons->getPrimeFaction()))
					{
						target->printColor("^r%M quickly assists %N!\n", mons, this);

						mons->setFlag(M_ALWAYS_ACTIVE);

						mons->updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
						mons->getAsMonster()->addEnemy(enm_in_group(target), true);
					}
				}
			}
		}
	}
	return(true);
}

//*********************************************************************
//						willAssist
//*********************************************************************

bool Monster::willAssist(const Monster *victim) const {

	if(!victim->info.id)
		return(false);

	for(int i=0; i<NUM_ASSIST_MOB; i++) {
		if(assist_mob[i] == victim->info)
			return(true);
	}

	return(false);
}

//*********************************************************************
//						checkScavange
//*********************************************************************

void Monster::checkScavange(long t) {
	BaseRoom* room = getRoomParent();
	Object* object=0;
	long i=0;

	if(room->flagIsSet(R_SHOP_STORAGE))
		return;

	if(flagIsSet(M_SCAVANGER)) {
		i = lasttime[LT_MON_SCAVANGE].ltime;
		if(	t - i > 20 &&
			mrand(1, 100) <= 15 &&
			!room->objects.empty() &&
			canScavange((*room->objects.begin())) &&
			!((*room->objects.begin())->getType() == WEAPON && flagIsSet(M_WILL_WIELD)))
		{
			object = (*room->objects.begin());
			object->deleteFromRoom();

			setFlag(M_HAS_SCAVANGED);
			broadcast(NULL, room, "%M picked up %1P.", this, object);

			// Object is gold
			if(!object->info.id) {
				coins.add(object->value);
				delete object;
			} else
				addObj(object);
		}
		if(t - i > 20)
			lasttime[LT_MON_SCAVANGE].ltime = t;
	}

	// thief code
	if(flagIsSet(M_TAKE_LOOT) || flagIsSet(M_STREET_SWEEPER)) {
		i = lasttime[LT_MOB_THIEF].ltime;
		if(t - i > 5) {
			Object	*hide_obj = NULL;
			char	buf[2048], *s = buf;
			int		buflen = 0;
			int		namelen;


			lasttime[LT_MOB_THIEF].ltime = t;
			buf[0] = 0;
			ObjectSet::iterator it;
			for(it = room->objects.begin() ; it != room->objects.end() ; ) {
				object = (*it++);
				if((flagIsSet(M_STREET_SWEEPER) ||
					(object->getType() == MONEY || object->value[GOLD] >= 100)) &&
						canScavange(object) )
				{
					if(getWeight() + object->getActualWeight() > maxWeight()) {
						if(!hide_obj && !flagIsSet(M_STREET_SWEEPER)) {
							hide_obj = object;
						}
						continue;
					}
					namelen = object->getName().length();
					if(buflen + namelen + 2 >= 2048)
						break;
					strcpy(s, object->getCName());
					s += namelen;
					strcpy(s, "^x, ");
					s += 4;
					buflen += namelen + 4;
					object->deleteFromRoom();
					if(object->getType() == MONEY || !object->info.id) {
						coins.add(object->value);
						delete object;
					} else {
						addObj(object);
					}
				}
			}
			if(buflen) {
				buf[buflen - 2] = 0;
				broadcast(NULL, room, "%M picked up %s.", this, buf);
			}
			if(hide_obj) {
				object = hide_obj;
				broadcast(getSock(), room, "%M attempts to hide %1P.", this, object);
				if(mrand(1, 100) <= level * 10) {
					object->setFlag(O_HIDDEN);
				}
			}
		}
	}

}

//*********************************************************************
//						canScavange
//*********************************************************************

bool Monster::canScavange(Object* object) {
    return( !object->flagIsSet(O_NO_TAKE) &&
    		!object->flagIsSet(O_SCENERY) &&
    		!object->flagIsSet(O_HIDDEN) &&
    		!object->flagIsSet(O_PERM_INV_ITEM) &&
    		!object->flagIsSet(O_PERM_ITEM) &&
    		!Unique::is(object)
    );
}

// TODO: Put this in the config/server class
static int Mobilechance = 30;

//*********************************************************************
//						checkWander
//*********************************************************************
// See if the monster will go mobile or wander away
// Return Value: 1 - Move to the next monster
//				 2 - Free this monster and start again at start of active list

int Monster::checkWander(long t) {
	int n=0, wanderchance=0;
	BaseRoom* room = getRoomParent();
	long i = lasttime[LT_MON_WANDER].ltime;

			// If we're a mobile monster
	if( flagIsSet(M_MOBILE_MONSTER) &&
		// if fast wander, mobile even if perm.
		(!flagIsSet(M_PERMENANT_MONSTER) || flagIsSet(M_FAST_WANDER)) &&
		// can fast-wander if enemies are not nearby
		(flagIsSet(M_FAST_WANDER) ? !nearEnemy() :
			// if not fast-wander, can mobile if no enemies
			!hasEnemy()) &&
		// can't wander if following someone
		!flagIsSet(M_DM_FOLLOW) && !getMaster() &&
		// or if they're set no wander
		!flagIsSet(M_NO_WANDER))
	{
		// Fast wander can wander once a second
		if((flagIsSet(M_FAST_WANDER) && t - i > 1) ||
			// Non fast wander has a 20% chance after 20 seconds
			(t - i > 20 && mrand(1, 100) > 20))
		{
			// If we're a fast wanderer, see if there's anyone in the room we
			// might want to stick around and attack. If there isn't we can
			// wander, otherwise don't move.
			if(!flagIsSet(M_FAST_WANDER) || (flagIsSet(M_FAST_WANDER) && !possibleEnemy()))
				n = mobileCrt();
			else
				n = 0;

			if(!n)
				clearFlag(M_MOBILE_MONSTER);

			if(n)
				return(1);
		}
		return(0);
	}


	// If we're chasing a shoplifter, we don't have an enemy in the room,
	// and sufficient time has passed
	if(	flagIsSet(M_ATTACKING_SHOPLIFTER) &&
		!nearEnemy() &&
		(t - i > 60 && mrand(1, 100) <= (!flagIsSet(M_PERMENANT_MONSTER) ? 40:30))
	) {
		// If we have a mobile chance or we're a fast wanderer
		if((mrand(1, 100) < Mobilechance || flagIsSet(M_FAST_WANDER))) {
			// We can go looking for the guy
			setFlag(M_MOBILE_MONSTER);
		} else if(!flagIsSet(M_CHASING_SOMEONE)) {
			// If we're nto chasing someone
			// Then we might start chasing them or let our guard down

			if(!flagIsSet(M_PERMENANT_MONSTER)) {
				broadcast(NULL, room, "%1M mutters obscenities under %s breath.", this, hisHer());
				// If we're not a perm, become a fast wander and go looking for the guy
				setFlag(M_FAST_WANDER);
				// Clear any aggressive flags so we don't go hit someone who
				// doesn't deserve it!  Chase the shoplifter down
				clearFlag(M_AGGRESSIVE_GOOD);
				clearFlag(M_AGGRESSIVE_EVIL);
				clearFlag(M_WILL_BE_AGGRESSIVE);
				clearFlag(M_WILL_ASSIST);
				setFlag(M_CHASING_SOMEONE);
				setFlag(M_OUTLAW_AGGRO);
			} else if(flagIsSet(M_ATTACKING_SHOPLIFTER)) {
				broadcast(NULL, room, "%1M lets down %s guard.", this, hisHer());
				clearFlag(M_ATTACKING_SHOPLIFTER);
				clearEnemyList();
			}
		}

		// If we're chasing someone we have a chance to wander away
		if(flagIsSet(M_CHASING_SOMEONE) && mrand(1,100) < 10) {
		    Creature* target = getTarget(false);
		    if(target)
		        broadcast(NULL, room, "%1M gives up %s search for %s and wanders away.", this, hisHer(), target->getCName());
		    else
		        broadcast(NULL, room, "%1M gives up %s search and wanders away.", this, hisHer());
			return(2);
		}
	}

	// Special case for aggressive monsters
	// If we're an aggressive non perm and haven't had any combat in 20 minutes
	if(	flagIsSet(M_AGGRESSIVE) &&
		t - lasttime[LT_AGGRO_ACTION].ltime > 1200 &&
		!flagIsSet(M_PERMENANT_MONSTER)
	) {
		// Then we've got a chance to wander away
		if(mrand(1, 100) < 5) {
			if(race || monType::isIntelligent(type))
				broadcast(NULL, room, "%1M %s away in search of someone to bully.", this, Move::getString(this).c_str());
			else
				broadcast(NULL, room, "%1M just %s away.", this, Move::getString(this).c_str());
			return(2);
		}
	}


	// Now see if we'll either wander away or if we can become a wanderer
		  // No wandering away if we have scavanged
	if(	(!flagIsSet(M_HAS_SCAVANGED) &&
		// if fast wander, wander even if perm.
		(!flagIsSet(M_PERMENANT_MONSTER) || flagIsSet(M_FAST_WANDER)) &&
		// pets and other followers don't wander
		!isPet() && !flagIsSet(M_DM_FOLLOW)))
	{
		if(	// If it's time to wander, and we have no enemey
			(t - i > 60 && (inUniqueRoom() && mrand(1, 100) <= getUniqueRoomParent()->wander.getTraffic()) && !hasEnemy()) ||
			// or we fast-wander if enemies are not nearby
			(flagIsSet(M_FAST_WANDER) && !nearEnemy()))
		{
			lasttime[LT_MON_WANDER].ltime = t;

			// Special rules if we fast wander
			if(flagIsSet(M_FAST_WANDER)) {
				// We always get the mobile creature flag set
				setFlag(M_MOBILE_MONSTER);

				// Now we've got a small chance here to wander away
				if(hasEnemy())
					wanderchance = 1;
				else
					wanderchance = 5;
					// If we're not a permenant monster
				if(	!flagIsSet(M_PERMENANT_MONSTER) &&
				    // and there is no dminvis person in the room
				    !room->dmInRoom() &&
				    // and we've got a wander chance
				    (mrand(1,100) <= wanderchance)
				) {
					broadcast(NULL, room, "%1M just %s away.", this, Move::getString(this).c_str());
					return(2);
				}
			}

			// If we're not a fast wanderer
			if(!flagIsSet(M_FAST_WANDER)) {
				// And we've got a mobile chance
				if(mrand(1, 100) < Mobilechance) {
					// Then we can wander next round
					setFlag(M_MOBILE_MONSTER);
				}
				// Otherwise if we have no enemy and we're not a perm, and there's no
				// dm invis person in the room, we can wander away
				else if(!room->dmInRoom() && !hasEnemy() && !flagIsSet(M_PERMENANT_MONSTER)) {
					// Time to wander away
					broadcast(NULL, room, "%1M just %s away.", this, Move::getString(this).c_str());
					return(2);
				}
			}
		} else if(t - i > 60)
			lasttime[LT_MON_WANDER].ltime = t;
	}
	return(0);
}

//*********************************************************************
//						checkEnemyMobs
//*********************************************************************
// If we're not currently fighthing someone, see if we hate any of the
// monsters in the room and attack them!

bool Monster::checkEnemyMobs() {
	int i=0, aggroSize=0;
	BaseRoom* room = getRoomParent();

	for(i = 0 ; i<NUM_ENEMY_MOB ;i++) {
		if(enemy_mob[i].id != 0) {
			aggroSize++;
		}
	}
	// Nothing to aggro, null room, or we have a nearby enemy
	if(aggroSize == 0 || !room || nearEnemy())
		return(false);

	// 25% chance each update cycle to aggro an enemy monster
	if(mrand(1,100) > 25)
		return(false);

	// We know we have some enemies, now lets see if we have any
	// enemies in the room with us to attack!

	for(Monster* mons : room->monsters) {
        if(mons != this && isEnemyMob(mons)) {
            broadcast(NULL, room, "%M yells, \"Die!\"\n%M attacks %N!", this, this, mons);
            addEnemy(mons);
            updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
            return(true);
        }
	}

	return(false);
}

//*********************************************************************
//						isEnemyMob
//*********************************************************************

bool Monster::isEnemyMob(const Monster* target) const {
	if(target->info.id == 0)
		return(false);
	for(int i=0 ; i<NUM_ENEMY_MOB ; i++) {
		if(enemy_mob[i] == target->info)
			return(true);
	}
	return(false);
}


//*********************************************************************
//						toJail
//*********************************************************************
// Sends a guy off to jail

int Monster::toJail(Player* player) {
	UniqueRoom* room=0;
	CatRef jailroom;
	jailroom.id = 2650;
	long jailtime=0;

	if(player->isStaff())
		return(0);

	if(jail.id)
		jailroom = jail;

	if(!loadRoom(jailroom, &room)) {
		player->print("%M gets very confused.\n", this);
		return(-1);
	}

	logn("log.jail", "%s was hauled off to jail (%s) by %s.\n", player->getCName(), jailroom.str().c_str(), getCName());
	player->deleteFromRoom();
	player->addToRoom(room);
	player->doPetFollow();

	if(room->flagIsSet(R_MOB_JAIL)) {
		jailtime = (8 + 2*player->getLevel())*60;

		if(player->isCt())
			jailtime = 15L;

		player->lasttime[LT_MOB_JAILED].ltime = time(0);
		player->lasttime[LT_MOB_JAILED].interval = jailtime;

		player->print("You are sentenced to around %ld day%s hard labor!\n",
		      (((jailtime / 60)/24 > 1) ? ((jailtime / 60)/24): 1),
		      (((jailtime / 60)/24 > 1) ? "s":""));
	}

	return(1);
}

//*********************************************************************
//						grabCoins
//*********************************************************************
// steals someone's gold

int Monster::grabCoins(Player* player) {
	if(!player->coins[GOLD])
		return(0);

	unsigned long grab = mrand(1, player->coins[GOLD]), num = player->coins[GOLD];
	// never steal all gold if they have more than 100k
	grab = MIN(grab, 100000);

	gServer->logGold(GOLD_OUT, player, Money(grab, GOLD), this, "Mugging");
	coins.add(grab, GOLD);
	player->coins.sub(grab, GOLD);

	logn("log.mug", "%s was mugged by %s for %d gold.\n", player->getCName(), getCName(), grab);

	if(grab == num) {
		player->print("%M grabs all your coins!\n", this);
		return(2);
	} else {
		player->print("%M takes %d gold coins from you!\n", this, grab);
		return(1);
	}
}