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