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/
/*
 * SkillCommand.cpp
 *   Skills that can be performed as a command
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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"

int getFindWhere(TargetType targetType) {
    switch(targetType) {
        case TARGET_MONSTER:
            return(FIND_MON_ROOM);
        case TARGET_PLAYER:
            return(FIND_PLY_ROOM);
        case TARGET_CREATURE:
            return(FIND_MON_ROOM | FIND_PLY_ROOM);
        case TARGET_OBJECT:
            return(FIND_OBJ_EQUIPMENT | FIND_OBJ_INVENTORY | FIND_OBJ_ROOM);
        case TARGET_OBJECT_CREATURE:
            return(FIND_OBJ_EQUIPMENT | FIND_OBJ_INVENTORY | FIND_OBJ_ROOM | FIND_MON_ROOM | FIND_PLY_ROOM);
        case TARGET_EXIT:
            return(FIND_EXIT);
        case TARGET_MUDOBJECT:
            return(FIND_MON_ROOM | FIND_PLY_ROOM | FIND_OBJ_EQUIPMENT | FIND_OBJ_INVENTORY | FIND_OBJ_ROOM | FIND_EXIT);
        default:
            break;
    }
    return(0);
}
int cmdSkill(Creature* creature, cmd* cmnd) {
    Player *player = creature->getAsPlayer();

    *creature << "Attempting to use the " << cmnd->myCommand->getName() << " skill\n";

    if(!creature->ableToDoCommand(cmnd))
        return(0);

    bstring str = cmnd->myCommand->getName();
    SkillCommand* skillCmd = dynamic_cast<SkillCommand*>(cmnd->myCommand);
    if(!skillCmd) {
        *creature << "Invalid skill!\n";
        return(0);
    }
    Skill* skill = creature->getSkill(cmnd->myCommand->getName());
    if(!skill) {
        *creature << "You don't know how to " << cmnd->myCommand->getName() << "\n";
        return(0);
    }
    if(!skillCmd->checkResources(creature))
        return(0);

    MudObject* target = NULL;
    Container* parent = creature->getParent();
    TargetType targetType = skillCmd->getTargetType();
    int findFlags = 0;

    if(skillCmd->getTargetType() != TARGET_NONE) {
        bstring toFind = cmnd->str[1];
        int num = cmnd->val[1];
        target = creature->findTarget(getFindWhere(skillCmd->getTargetType()), 0, toFind, num);

        // Assumption: If it's offensive, it's usable on creatures
        if(!target && skillCmd->isOffensive() && creature->hasAttackableTarget())
            target = creature->getTarget();

        if(!target) {
            *creature << "You can't find '" << toFind << "'.\n";
            return(0);
        }
    } else {
        target = creature;
    }

    if(skillCmd->isOffensive()) {
        if(target->isCreature()) {
            if(!creature->canAttack(target->getAsCreature(), false)) {
                return(0);
            }
        }
    }

    if(skillCmd->hasCooldown()) {
        if(!skill->checkTimer(creature, true))
            return(0);
        skill->updateTimer();
    }

    if(skillCmd->getUsesAttackTimer()) {
        if(!creature->checkAttackTimer())
            return(0);

        creature->updateAttackTimer();
    }
    *creature << "You attempt to " << skillCmd->getName() << " " << target << "\n";

    bool result = skillCmd->runScript(creature, target, skill);

    if(skillCmd->hasCooldown()) {
        if(result)
            skill->modifyDelay(skillCmd->getCooldown());
        else
            skill->modifyDelay(skillCmd->getFailCooldown());
    }
    // TODO: Track last skill for future combos

    return(0);
}
//*********************************************************************
//                      checkAttackTimer
//*********************************************************************
// Return: true if the attack timer has expired, false if not

bool Skill::checkTimer(Creature* creature, bool displayFail) {
    long i;
    if(!((i = timer.getTimeLeft()) == 0)) {
        if(displayFail)
            creature->pleaseWait(i/10.0);
//        if(!creature->isDm())
            return(false);
    }
    return(true);
}
//*********************************************************************
//                      UpdateTimer
//*********************************************************************

void Skill::updateTimer(bool setDelay, int delay) {
    if(!setDelay) {
        timer.update();
    } else {
        timer.update(delay);
    }
}

//*********************************************************************
//                      modifyDelay
//*********************************************************************

void Skill::modifyDelay(int amt) {
    timer.modifyDelay(amt);
}

//*********************************************************************
//                      setAttackDelay
//*********************************************************************

void Skill::setDelay(int newDelay) {
    timer.setDelay(newDelay);
}

bool SkillCommand::getUsesAttackTimer() const {
    return(usesAttackTimer);
}
bool SkillCommand::hasCooldown() const {
    return(cooldown != 0);
}
int SkillCommand::getCooldown() const {
    return(cooldown);
}
int SkillCommand::getFailCooldown() const {
    return(failCooldown);
}


bool SkillCommand::runScript(Creature* actor, MudObject* target, Skill* skill) {
    try {
        object localNamespace( (handle<>(PyDict_New())));

        object skillModule( (handle<>(PyImport_ImportModule("skillLib"))) );

        localNamespace["skillLib"] = skillModule;

        localNamespace["skill"] = ptr(skill);
        localNamespace["skillCmd"] = ptr(this);

        // Default retVal is true
        localNamespace["retVal"] = true;
        addMudObjectToDictionary(localNamespace, "actor", actor);
        addMudObjectToDictionary(localNamespace, "target", target);


        gServer->runPython(pyScript, localNamespace);

        bool retVal = extract<bool>(localNamespace["retVal"]);
        //std::cout << "runScript returning: " << retVal << std::endl;
        return(retVal);
    }
    catch( error_already_set) {
        gServer->handlePythonError();
    }
    return(false);
}
TargetType SkillCommand::getTargetType() const {
    return(targetType);
}

bool SkillCommand::isOffensive() const { return(offensive); }

//********************************************************************************
// CheckResource
//********************************************************************************
// Checks that the given resource type has sufficient resources left

bool Creature::checkResource(ResourceType resType, int resCost) {
    bool retVal = true;
    switch(resType) {
    case RES_NONE:
        retVal = true;
        break;
    case RES_GOLD:
        retVal = coins[GOLD] >= resCost;
        break;
    case RES_MANA:
        retVal = mp.getCur() >= resCost;
        break;
    case RES_HIT_POINTS:
        retVal = hp.getCur() >= resCost;
        break;
    case RES_FOCUS:
        if(!getAsPlayer())
            retVal = false;
        else
            retVal = getAsPlayer()->focus.getCur() >= resCost;
        break;
    case RES_ENERGY:
        // no energy for now
        retVal = false;
        break;
    default:
        // Unknown resource, we don't have it
        retVal = false;
        break;
    }
    return(retVal);
}
//********************************************************************************
// SubResource
//********************************************************************************
// Removes resCost of given resource; does not check for sufficient resources,
// as it assumes that has been checked elsewhere
void Creature::subResource(ResourceType resType, int resCost) {
    switch(resType) {
        case RES_NONE:
            return;
        case RES_GOLD:
            //return(coins[GOLD] >= resCost);
            coins.sub(resCost, GOLD);
            return;
        case RES_MANA:
            mp.decrease(resCost);
            return;
        case RES_HIT_POINTS:
            hp.decrease(resCost);
            return;
        case RES_FOCUS:
            if(!getAsPlayer())
                return;
            getAsPlayer()->focus.decrease(resCost);
            return;
        case RES_ENERGY:
            // no energy for now
            return;
        default:
            // Unknown resource, we don't have it
            return;
        }
}

//********************************************************************************
// GetResourceName
//********************************************************************************
bstring getResourceName(ResourceType resType) {
    switch(resType) {
        case RES_NONE:
        default:
            return("(Unknown)");
        case RES_GOLD:
            return("gold coins");
        case RES_MANA:
            return("magic points");
        case RES_HIT_POINTS:
            return("hit points");
        case RES_FOCUS:
            return("focus");
        case RES_ENERGY:
            return("energy");
        }
}
//********************************************************************
//* Check Resources
//********************************************************************
// Returns: True  - Sufficient resources
//          False - Insufficient resources

bool SkillCommand::checkResources(Creature* creature) {
    for(SkillCost& res : resources) {
        if(!creature->checkResource(res.resource, res.cost)) {
            bstring failMsg = bstring("You need to have at least ") + res.cost + " " + getResourceName(res.resource) + ".\n";
            return(creature->checkStaff(failMsg.c_str()));
        }
    }
    return(true);
}
void SkillCommand::subResources(Creature* creature) {
    for(SkillCost& res : resources) {
        creature->subResource(res.resource, res.cost);
    }
}

int SkillCommand::execute(Creature* player, cmd* cmnd) {
    return((fn)(player, cmnd));
}