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/
/*
 * command2.cpp
 *	 Command handling/parsing 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 "commands.h"
#include "move.h"
#include "factions.h"
#include "unique.h"

#define STONE_SCROLL_INDEX		10

//*********************************************************************
//						stoneScroll
//*********************************************************************

int stoneScroll(Player* player, cmd* cmnd) {
	char	filename[80];
	sprintf(filename, "%s/stone_scroll", Path::Sign);

	if(!strncmp(cmnd->str[2], "class", strlen(cmnd->str[2])))
		strcat(filename, "_class");
	else if(!strncmp(cmnd->str[2], "race", strlen(cmnd->str[2])))
		strcat(filename, "_race");
	else if(!strncmp(cmnd->str[2], "deity", strlen(cmnd->str[2])))
		strcat(filename, "_deity");
	else if(!strncmp(cmnd->str[2], "breakdown", strlen(cmnd->str[2])))
		strcat(filename, "_breakdown");
	strcat(filename, ".txt");
	viewLoginFile(player->getSock(), filename);
	return(0);
}

//*********************************************************************
//						lookAtExit
//*********************************************************************

bool doScout(Player* player, const Exit* exit);

void lookAtExit(Player* player, Exit* exit) {
	// blindness is already handled
	
	player->printColor("You look at the %s^x exit.\n", exit->getCName());

	if(exit->getDescription() != "")
		player->printColor("%s\n", exit->getDescription().c_str());
	// the owner of the portal gets more information
	if(exit->flagIsSet(X_PORTAL) && exit->getPassPhrase() == player->getName())
		player->print("%s %s may pass through it.\n", intToText(exit->getKey(), true).c_str(), exit->getKey() == 1 ? "person" : "people");
	if(getDir(exit->getCName()) == NoDirection && exit->getDirection() != NoDirection)
		player->print("It leads %s.\n", getDirName(exit->getDirection()).c_str());
	
	if(exit->flagIsSet(X_CLOSED))
		player->print("It is closed.\n");
	if(exit->flagIsSet(X_LOCKED))
		player->print("It is locked.\n");
	if(exit->isWall("wall-of-fire"))
		player->print("It is blocked by a wall of fire.\n");
	if(exit->isWall("wall-of-force"))
		player->print("It is blocked by a wall of force.\n");
	if(exit->isWall("wall-of-thorns"))
		player->print("It is blocked by a wall of thorns.\n");

	const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
	if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you look there" : "won't let you look there"))
		return;

	if(exit->flagIsSet(X_CAN_LOOK) || exit->flagIsSet(X_LOOK_ONLY)) {
		if(	player->getRoomParent()->isEffected("dense-fog") &&
			!player->checkStaff("This room is filled with a dense fog.\nYou cannot look to the %s^x effectively.\n", exit->getCName())
		)
			return;

		doScout(player, exit);
		return;
	}
}

//*********************************************************************
//						cmdLook
//*********************************************************************

int cmdLook(Player* player, cmd* cmnd) {
	MudObject* target=0;
	int flags = player->displayFlags();

	if(!player->ableToDoCommand())
		return(0);
	if(player->isBlind()) {
		player->printColor("^CYou're blind!\n");
		return(0);
	}

	if(!cmnd || cmnd->num < 2) {
		display_rom(player);
		return(0);
	}

	target = player->findTarget(FIND_OBJ_INVENTORY | FIND_OBJ_EQUIPMENT | FIND_OBJ_ROOM |
		FIND_MON_ROOM | FIND_PLY_ROOM | FIND_EXIT, flags, cmnd->str[1], cmnd->val[1]);

	if(target) {
	    if(target->isCreature()) {
			player->displayCreature(target->getAsCreature());
	    } else if(target->isObject()) {
	        Object* obj = dynamic_cast<Object*>(target);
            if(cmnd->num == 3 && obj->info.id == STONE_SCROLL_INDEX && obj->info.isArea("misc"))
                stoneScroll(player, cmnd);
            else
                displayObject(player, obj);
	    } else if(target->isExit()) {
	        lookAtExit(player, target->getAsExit());
	    } else {
			player->print("You get all confused looking at it.\n");
		}
	} else {
		player->print("You don't see that here.\n");
	}

	return(0);
}

//*********************************************************************
//						getGuardingExit
//*********************************************************************

Monster* BaseRoom::getGuardingExit(const Exit* exit, const Player* player) const {
	if(!exit->flagIsSet(X_PASSIVE_GUARD))
		return(0);

	for(Monster* mons : monsters) {
        if(!mons->flagIsSet(M_PASSIVE_EXIT_GUARD))
            continue;
        if(player && mons->flagIsSet(M_FACTION_NO_GUARD) && Faction::willLetThrough(player, mons->getAsMonster()->getPrimeFaction()))
            continue;

        return(mons);
	}

	return(0);
}

//*********************************************************************
//						cmdThrow
//*********************************************************************

bool storageProperty(const Player* player, const Object* object, Property **p);

int cmdThrow(Creature* creature, cmd* cmnd) {
	Object*	object=0;
	Creature* victim=0;
	const Monster* guard=0;
	Exit*	exit=0;
	BaseRoom *room=0, *newRoom=0;
	Property* p=0;
	Player* player = creature->getAsPlayer(), *pVictim=0;

	if(!creature->ableToDoCommand())
		return(0);
	if(!creature->checkAttackTimer())
		return(0);
	if(creature->isBlind()) {
		creature->printColor("^CYou can't do that. You're blind!\n");
		return(0);
	}

	object = creature->findObject(creature, cmnd, 1);
	room = creature->getRoomParent();

	if(!object) {
		creature->print("You don't have that in your inventory.\n");
		return(0);
	}


	// throw checks

	if(object->flagIsSet(O_KEEP)) {
		player->printColor("%O is currently in safe keeping.\nYou must unkeep it to throw it.\n", object);
		return(0);
	}

	if(	object->flagIsSet(O_NO_DROP) &&
		!player->checkStaff("You cannot throw that.\n"))
	{
		return(0);
	}

	for(Object *obj : object->objects) {
		if(	obj->flagIsSet(O_NO_DROP) &&
			!player->checkStaff("You cannot throw that. It contains %P.\n", obj) )
		{
			return(0);
		}
	}

	if(	object->getActualWeight() > creature->strength.getCur()/3 &&
		!creature->checkStaff("%O is too heavy for you to throw!\n", object)
	)
		return(0);

	// are they allowed to throw objects in this storage room?
	if(!storageProperty(player, object, &p))
		return(0);
	
	// done with throw checks


	exit = findExit(creature, cmnd, 2, room);

	if(!exit)
		victim = room->findCreature(creature, cmnd, 2);

	if(exit) {
		if(creature->inCombat() && !creature->checkStaff("Not while you are in combat!\n"))
			return(0);

		creature->printColor("You throw %P to the %s^x.\n", object, exit->getCName());
		broadcast(creature->getSock(), room, "%M throws %P to the %s^x.",
			creature, object, exit->getCName());
		creature->delObj(object);

		guard = room->getGuardingExit(exit, player);

		// don't bother getting rooms if exit is closed or guarded
		bool loadExit = (!exit->flagIsSet(X_CLOSED) && !exit->flagIsSet(X_PORTAL) && !guard);
		if(loadExit)
			Move::getRoom(creature, exit, &newRoom, false, &exit->target.mapmarker);

		room->wake("Loud noises disturb your sleep.", true);

		// closed, or off map, or being guarded
		if(!loadExit || (!newRoom)) {
			if(guard)
				broadcast(0, room, "%M knocks %P to the ground.", guard, object);
			else
				broadcast(0, room, "%O hits the %s^x and falls to the ground.",
					object, exit->getCName());
			finishDropObject(object, room, creature);
		} else {
			room = newRoom;

			broadcast(0, room, "%1O comes flying into the room.", object);
			room->wake("Loud noises disturb your sleep.", true);
			finishDropObject(object, room, creature, false, false, true);

			if(newRoom->isAreaRoom() && newRoom->getAsAreaRoom()->canDelete())
				newRoom->getAsAreaRoom()->area->remove(newRoom->getAsAreaRoom());
		}

		creature->updateAttackTimer(true, DEFAULT_WEAPON_DELAY);

	} else if(victim) {

		pVictim = victim->getAsPlayer();
		if(!creature->canAttack(victim))
			return(0);

		if(creature->vampireCharmed(pVictim) || (victim->hasCharm(creature->getCName()) && creature->pFlagIsSet(P_CHARMED))) {
			creature->print("You like %N too much to do that.\n", victim);
			return(0);
		}

		creature->updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
		creature->smashInvis();
		creature->unhide();

		if(player)
			player->statistics.swing();
		creature->printColor("You throw %P at %N.\n", object, victim);
		victim->printColor("%M throws %P at you.\n", creature, object);
		broadcast(creature->getSock(), victim->getSock(), room,
			"%M throws %P at %N.", creature, object, victim);
		creature->delObj(object);

		if(!pVictim)
			victim->getAsMonster()->addEnemy(creature);

		if(pVictim && victim->isEffected("mist")) {
			pVictim->statistics.wasMissed();
			if(player)
				player->statistics.miss();
			broadcast(victim->getSock(), room, "%O passes right through %N.", object, victim);
			victim->printColor("%O passes right through you.\n", object);
		} else {
			int skillLevel = (int)creature->getSkillGained("thrown");
			AttackResult result = creature->getAttackResult(victim, 0, NO_FUMBLE, skillLevel);

			if(pVictim && victim->flagIsSet(P_UNCONSCIOUS))
				result = ATTACK_HIT;

			if(result == ATTACK_DODGE) {
				if(pVictim)
					pVictim->statistics.dodge();
				if(player)
					player->statistics.miss();
				victim->printColor("You dodge out of the way.\n");
				broadcast(victim->getSock(), room, "%M dodges out of the way.", victim);
				creature->checkImprove("thrown", false);
			} else if(result == ATTACK_MISS || result == ATTACK_BLOCK || result == ATTACK_PARRY) {
				if(pVictim)
					pVictim->statistics.wasMissed();
				if(player)
					player->statistics.miss();
				victim->printColor("You knock %P to the ground.\n", object);
				broadcast(victim->getSock(), room, "%M knocks %P to the ground.", victim, object);
				creature->checkImprove("thrown", false);
			} else {

				Damage damage;
				damage.set(1);
				if(object->getActualWeight() > 1)
					damage.set(mrand(1,6));

				victim->modifyDamage(creature, PHYSICAL, damage);
				if(pVictim)
					pVictim->statistics.wasHit();
				if(player) {
					player->statistics.hit();
					player->statistics.attackDamage(damage.get(), Statistics::damageWith(player, object));
				}

				creature->printColor("You hit %N for %s%d^x damage.\n", victim, creature->customColorize("*CC:DAMAGE*").c_str(), damage.get());
				victim->printColor("%M hit you%s for %s%d^x damage!\n", creature,
					victim->isBrittle() ? "r brittle body" : "", victim->customColorize("*CC:DAMAGE*").c_str(), damage.get());
				broadcastGroup(false, victim, "^M%M^x hit ^M%N^x for *CC:DAMAGE*%d^x damage, %s%s\n", creature,
					victim, damage.get(), victim->heShe(), victim->getStatusStr(damage.get()));

				creature->doDamage(victim, damage.get(), CHECK_DIE);
				creature->checkImprove("thrown", true);
			}

		}

		finishDropObject(object, room, creature);
		room->wake("Loud noises disturb your sleep.", true);

	} else {
		creature->printColor("Throw %P at what?\n", object);
	}
	return(0);
}

//*********************************************************************
//						cmdKnock
//*********************************************************************

int cmdKnock(Creature* creature, cmd* cmnd) {
	BaseRoom* targetRoom=0;
	Exit	*exit=0;

	if(cmnd->num < 2) {
		creature->print("Knock on what exit?\n");
		return(0);
	}

	lowercize(cmnd->str[1], 0);
	exit = findExit(creature, cmnd);
	if(!exit) {
		creature->print("Knock on what exit?\n");
		return(0);
	}

	if(!exit->flagIsSet(X_CLOSED)) {
		creature->print("That exit is not closed!\n");
		return(0);
	}

	creature->getParent()->wake("You awaken suddenly!", true);
	creature->printColor("You knock on the %s^x.\n", exit->getCName());
	broadcast(creature->getSock(), creature->getRoomParent(), "%M knocks on the %s^x.", creature, exit->getCName());

	// change the meaning of exit
	exit = exit->getReturnExit(creature->getRoomParent(), &targetRoom);
	targetRoom->wake("You awaken suddenly!", true);

	if(exit)
		broadcast(0, targetRoom, "You hear someone knocking on the %s.", exit);
	else
		broadcast(0, targetRoom, "You hear the sound of someone knocking.");
	return(0);
}

//*********************************************************************
//						cmdPrepare
//*********************************************************************

int cmdPrepare(Player* player, cmd* cmnd) {
	if(cmnd->num < 2)
		return(cmdPrepareForTraps(player, cmnd));
	return(cmdPrepareObject(player, cmnd));
}


//*********************************************************************
//						cmdBreak
//*********************************************************************

int cmdBreak(Player* player, cmd* cmnd) {
	int		duration=0;
	CatRef	newloc;
	int  	chance=0, dmg=0, xpgain=0, splvl=0;
	float	mtbf=0, shots=0, shotsmax=0;
	UniqueRoom *new_rom=0;
	char	item[80];
	Object	*object=0;

	player->clearFlag(P_AFK);

	if(cmnd->num < 2) {
		player->print("Break what?\n");
		return(0);
	}


	if(!player->checkAttackTimer())
		return(0);

	object = player->findObject(player, cmnd, 1);

	if(!object) {
		player->print("You don't have that in your inventory.\n");
		return(0);
	}

	if(	object->flagIsSet(O_BROKEN_BY_CMD) ||
		object->flagIsSet(O_NO_BREAK) ||
		object->getType() == KEY ||
		object->getSpecial() ||
		object->getType() == MONEY ||
		(object->getShotsCur() < 1 && object->getType() != CONTAINER)
	) {
		player->print("You can't break that.\n");
		return(0);
	}



	if(object->getType() == CONTAINER && object->getShotsMax() < 1) {
		player->print("You can't break that.\n");
		return(0);
	}

	if((object->getType() == WAND || (object->getType() < 5 && object->flagIsSet(O_WEAPON_CASTS) && object->getMagicpower())) &&
			player->getRoomParent()->isPkSafe() && !player->isCt()) {
		player->print("You really shouldn't do that in here. Someone could get hurt.\n");
		return(0);
	}

	chance = player->strength.getCur()*4;
	if(player->isDm())
		player->print("Chance(str): +%d%\n", player->strength.getCur()*4);

	shots = (float)object->getShotsCur();
	shotsmax = (float)object->getShotsMax();

	mtbf = shots/shotsmax;
	mtbf *=100;
	mtbf = (100 - mtbf);
	chance += (int)mtbf;

	if(player->isDm())
		player->print("Chance(shotslost): +%f%\n", mtbf);

	chance -= object->getAdjustment()*10;
	if(player->isDm())
		player->print("Chance(adj): -%d%\n", object->getAdjustment()*10);

	chance -= object->getWeight()/10;
	if(player->isDm())
		player->print("Chance(wgt): -%d%\n",object->getWeight()/10 );

	chance = MAX(1, chance);


	if(object->getQuestnum())
		chance = 0;

	if(player->isDm()) {
		player->print("Chance: %d%\n", chance);
		//	chance = 101;
	}



	if(object->getType() == CONTAINER && !object->objects.empty()) {
		player->print("You have to dump its contents out first!\n");
		return(0);
	}

	if(mrand(1,100) <= chance) {

		object->setFlag(O_BROKEN_BY_CMD);
		if(object->compass) {
			delete object->compass;
			object->compass = 0;
		}

		if(!(object->getType() == POISON && player->getClass() == PALADIN)) {
			player->printColor("You manage to break %P.\n", object);
			broadcast(player->getSock(), player->getParent(), "%M breaks %P.", player, object);
		} else {
			player->printColor("You dispose of the vile %P.\n", object);
			broadcast(player->getSock(), player->getParent(), "%M neatly disposes of the vile %P.", player, object);
		}


		if(object->getType() == POISON || object->getType() == POTION)
			strcpy(item, "used up ");
		else
			strcpy(item, "broken ");
		object->setName(bstring(item) + object->getName());

		strncpy(object->key[2],"broken",20);


		if(object->getType() == CONTAINER) {
			object->setShotsMax(0);
			object->setType(MISC);
		}

		object->setShotsCur(0);
		object->setFlag(O_NO_FIX);
		player->updateAttackTimer(true, 60);


		if(	(player->getClass() == BERSERKER || player->isCt()) && object->getType() > 5 &&
			(shots > 0.0) && object->value[GOLD] > 100
		) {
			xpgain = (int)mtbf+(object->getAdjustment()*10)+mrand(1,50);
			if(object->getType() == WAND)
				xpgain *= 3;

			if(!player->halftolevel()) {
				player->print("You %s %d experience for your deed.\n", gConfig->isAprilFools() ? "lose" : "gain", xpgain);
				player->addExperience(xpgain);
			}
		}

		if(	(object->getType() == WAND || (object->getType() < 5 && object->flagIsSet(O_WEAPON_CASTS) && object->getMagicpower())) &&
			(shots > 0.0) && (mrand(1,100) <= 50)
		) {
			splvl = get_spell_lvl(object->getMagicpower()-1);
			if(player->isCt())
				player->print("Spell level: %d.\n", splvl);

			if(splvl >= 4) {
				player->printColor("%O explodes in a retributive strike!\n", object);
				broadcast(player->getSock(), player->getParent(), "%O explodes in a retributive strike!", object);

				player->delObj(object, true);
				delete object;
			} else {
				player->printColor("%O explodes!\n", object);
				broadcast(player->getSock(), player->getParent(), "%O explodes!", object);
				broadcast(player->getSock(), player->getParent(), "%M is engulfed by magical energy!", player);

				player->delObj(object, true);
				delete object;
			}

			broadcast(player->getSock(), player->getParent(), "%M is engulfed by a magical vortex!", player);

			dmg = mrand(1,5);
			dmg += MAX(5,(mrand(splvl*2, splvl*6)))+((int)mtbf/2);

			if(player->chkSave(BRE, player, 0))
				dmg /= 2;

			player->printColor("You take %s%d^x damage from the release of magical energy!\n", player->customColorize("*CC:DAMAGE*").c_str(), dmg);

			if(splvl >= 4) {
			    PlayerSet::iterator pIt = player->getRoomParent()->players.begin();
			    PlayerSet::iterator pEnd = player->getRoomParent()->players.end();

			    Player* ply=0;
				while(pIt != pEnd) {
				    ply = (*pIt++);
					if(ply != player && !ply->inCombat()) {
						dmg = mrand(splvl*4, splvl*8);

						if(ply->chkSave(BRE, ply, 0))
							dmg /= 2;

						ply->printColor("You take %s%d^x damage from the magical explosion!\n", ply->customColorize("*CC:DAMAGE*").c_str(), dmg);

						if(player->doDamage(ply, dmg, CHECK_DIE)) {
							continue;
						}
					}
				}
			}

			if(splvl >= 3)
				player->getRoomParent()->scatterObjects();

			player->hp.decrease(dmg);
			if(player->hp.getCur() < 1) {
				player->die(EXPLOSION);
				return(0);
			}

			if(splvl >= 3) {
				if(mrand(1,100) <= 50) {
					newloc = getEtherealTravelRoom();
					if(player->inJail())
						newloc = player->getUniqueRoomParent()->info;
					else if(player->getRoomParent()->flagIsSet(R_ETHEREAL_PLANE))
						newloc.id = 50;

					if(!loadRoom(newloc, &new_rom))
						return(0);

					if(!player->getRoomParent()->flagIsSet(R_ETHEREAL_PLANE)) {
						broadcast(player->getSock(), player->getParent(), "%M was blown from this universe by the explosion!", player);
						player->print("You are blown into an alternate dimension.\n");
					}
					player->deleteFromRoom();
					player->addToRoom(new_rom);
					player->doPetFollow();
				}
			}

			return(0);
		}


		if(object->getType() == POISON && (player->getClass() == PALADIN || player->isCt())) {
			xpgain = object->getEffectStrength()*15;
			// no super fast leveling by breaking poison over and over.
			if(player->getLevel() < 9)
				xpgain = MIN(xpgain, player->getLevel()*10);

			if(!player->halftolevel()) {
				player->print("You %s %d experience for your deed.\n", gConfig->isAprilFools() ? "lose" : "gain", xpgain);
				player->addExperience(xpgain);
			}
		}

	} else {
		if(!(object->getType() == POISON && player->getClass() == PALADIN)) {
			player->printColor("You were unable to break %P.\n", object);
			broadcast(player->getSock(), player->getParent(), "%M tried to break %P.", player, object);
		} else {
			player->printColor("You were unable to properly dispose of the %P.\n", object);
			broadcast(player->getSock(), player->getParent(), "%M tried to dispose of the %P.", player, object);
			chance = 30 - (bonus((int)player->dexterity.getCur()));

			if(player->immuneToPoison())
				chance = 101;

			if(mrand(1,100) <= chance) {
				player->printColor("^r^#You accidentally poisoned yourself!\n");
				broadcast(player->getSock(), player->getParent(), "%M accidentally poisoned %sself!",
					player, player->himHer());

				duration = standardPoisonDuration(object->getEffectDuration(), player->constitution.getCur());

				if(player->chkSave(POI, player, -1))
					duration = duration * 2 / 3;

				player->poison(player, object->getEffectStrength(), duration);
			}
		}
		player->updateAttackTimer(true, 40);
	}
	return(0);
}