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/
/*
 * delayedAction.cpp
 *   Delayed action queue
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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"


//*********************************************************************
//						Delayed Action Queue
//*********************************************************************

// TODO: optomize this by making the queue a sorted list based on completion.
// That way you only have to check the front of the list to see if anything needs to be
// done - no more looping!
// std::map<long, action>


//*********************************************************************
//						removeDelayedActions
//*********************************************************************
// If the creature's delayedActionQueue is not cleared first, it will have invalid pointers.
// If the creature is immediately going out of memory (like in free_crt), that's fine.

bool Server::removeDelayedActions(MudObject* target, bool interruptOnly) {
	std::list<DelayedAction>::iterator it;
	bool found = false;

	for(it = delayedActionQueue.begin(); it != delayedActionQueue.end(); ) {
		if(!(*it).target) {
			// this must be a bug
			found = true;
			it = delayedActionQueue.erase(it);
			broadcast(isCt, "^rA delayed action without a target was discovered and removed from the queue.");
		} else if((*it).target == target) {
			// should we remove this action?
			if(!interruptOnly || (*it).canInterrupt) {
				// if we're just interrupting, we won't remove-all at the end
				if(interruptOnly)
					target->removeDelayedAction(&(*it));

				found = true;
				it = delayedActionQueue.erase(it);
			} else {
				it++;
			}
		} else {
			it++;
		}
	}

	// if we aren't just interrupting, remove all
	if(!interruptOnly)
		target->clearDelayedActions();
	return(found);
}

//*********************************************************************
//						parseDelayedActions
//*********************************************************************
// this function is called once a second from Server::updateGame

void Server::parseDelayedActions(long t) {
	std::list<DelayedAction>::iterator it;

	for(it = delayedActionQueue.begin(); it != delayedActionQueue.end(); ) {
		if((*it).whenFinished <= t) {
			if((*it).target) {
				// exectue the callback function
				((*it).callback) (&(*it));
				(*it).target->removeDelayedAction(&(*it));
			}
			it = delayedActionQueue.erase(it);
		} else
			it++;
	}
}


//*********************************************************************
//						addDelayedAction
//*********************************************************************
// This is called by anywhere in the game that wants to provide a delayed action to the player.
// If adding the action interrupts what the player is currently doing, that's up to the
// calling code to take care of.

void Server::addDelayedAction(void (*callback)(DelayedActionFn), MudObject* target, cmd* cmnd, DelayedActionType type, long howLong, bool canInterrupt) {
	switch(type) {
		case ActionFish:
		case ActionSearch:
		case ActionTrack:
		case ActionStudy:
			// these actions are player-only
			if(!target->getAsConstPlayer())
				return;
			break;
		default:
			break;
	}

	DelayedAction action = DelayedAction(callback, target, cmnd, type, time(0) + howLong, canInterrupt);

	delayedActionQueue.push_back(action);
	target->addDelayedAction(&action);
}

//*********************************************************************
//						addDelayedScript
//*********************************************************************

void Server::addDelayedScript(void (*callback)(DelayedActionFn), MudObject* target, bstring script, long howLong, bool canInterrupt) {
	DelayedAction action = DelayedAction(callback, target, script, time(0) + howLong, canInterrupt);

	delayedActionQueue.push_back(action);
	target->addDelayedAction(&action);
}


//*********************************************************************
//						hasAction
//*********************************************************************
// this will inform the calling function that a particular delayed action is in the queue

bool Server::hasAction(const MudObject* target, DelayedActionType type) {
	std::list<DelayedAction>::const_iterator it;

	for(it = delayedActionQueue.begin(); it != delayedActionQueue.end(); it++) {
		if((*it).target == target && (*it).type == type)
			return(true);
	}

	return(false);
}

//*********************************************************************
//						delayedActionStrings
//*********************************************************************
// called from Player::score

bstring Server::delayedActionStrings(const MudObject* target) {
	std::ostringstream oStr;
	std::list<DelayedAction>::const_iterator it;

	for(it = delayedActionQueue.begin(); it != delayedActionQueue.end(); it++) {
		if((*it).target == target) {
			switch((*it).type) {
				case ActionFish:
					oStr << " ^C*Fishing*";
					break;
				case ActionSearch:
					oStr << " ^C*Searching*";
					break;
				case ActionTrack:
					oStr << " ^C*Tracking*";
					break;
				case ActionStudy:
					oStr << " ^Y*Studying*";
					break;
				default:
					break;
			}
		}
	}

	return(oStr.str());
}


//*********************************************************************
//						interruptDelayedActions
//*********************************************************************
// Called whenever the player does something to make them cancel their delayed action queue.

void MudObject::interruptDelayedActions() {
	if(delayedActionQueue.size()) {
		// true means we are only removing interrupt-able actions
		if(gServer->removeDelayedActions(this, true)) {
			const Creature* creature = getAsConstCreature();
			if(creature)
				creature->print("You stop what you are doing.\n");
		}
	}
}


//*********************************************************************
//						addDelayedAction
//*********************************************************************
// ONLY to be called from Server::addDelayedAction

void MudObject::addDelayedAction(DelayedAction* action) {
	delayedActionQueue.push_back(action);
}


//*********************************************************************
//						removeDelayedAction
//*********************************************************************
// ONLY to be called from Server::parseDelayedActions and Server::removeDelayedActions

void MudObject::removeDelayedAction(DelayedAction* action) {
	std::list<DelayedAction*>::iterator it;

	for(it = delayedActionQueue.begin(); it != delayedActionQueue.end(); it++) {
		if((*it) == action) {
			delayedActionQueue.erase(it);
			return;
		}
	}
}


//*********************************************************************
//						clearDelayedActions
//*********************************************************************
// ONLY to be called from Server::removeDelayedActions

void MudObject::clearDelayedActions() {
	delayedActionQueue.clear();
}


//*********************************************************************
//						doDelayedAction
//*********************************************************************

void doDelayedAction(const DelayedAction* action) {
	Creature* creature = action->target->getAsCreature();
	if(!creature)
		return;
	// we need a non-const command
	cmd cmnd;
	cmnd = action->cmnd;
	cmdProcess(creature, &cmnd);
}

//*********************************************************************
//						delayedAction
//*********************************************************************

void stripBadChars(bstring str);
void Creature::delayedAction(bstring action, int delay, MudObject* target) {
	cmd cmnd;

	cmnd.fullstr = action;
	if(target) {
	    cmnd.fullstr += bstring(" ") + target->getName();
	}

	stripBadChars(cmnd.fullstr); // removes '.' and '/'
	lowercize(cmnd.fullstr, 0);
	parse(cmnd.fullstr, &cmnd);

	gServer->addDelayedAction(doDelayedAction, this, &cmnd, ActionScript, delay);
}

//*********************************************************************
//						doDelayedScript
//*********************************************************************

void doDelayedScript(const DelayedAction* action) {
	Creature* creature = action->target->getAsCreature();
	if(!creature)
		return;
	gServer->runPython(action->script, "", creature);
}

//*********************************************************************
//						delayedScript
//*********************************************************************

void Creature::delayedScript(bstring script, int delay) {
	gServer->addDelayedScript(doDelayedScript, this, script, delay);
}