Postado Agosto 15, 2015 10 anos Autor spells.cpp //////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include "const.h" #include <libxml/xmlmemory.h> #include <libxml/parser.h> #include "spells.h" #include "tools.h" #include "house.h" #include "housetile.h" #include "combat.h" #include "monsters.h" #include "configmanager.h" #include "game.h" extern Game g_game; extern Spells* g_spells; extern Monsters g_monsters; extern ConfigManager g_config; Spells::Spells(): m_interface("Spell Interface") { m_interface.initState(); } ReturnValue Spells::onPlayerSay(Player* player, const std::string& words) { std::string reWords = words; trimString(reWords); InstantSpell* instantSpell = getInstantSpell(reWords); if(!instantSpell) return RET_NOTPOSSIBLE; size_t size = instantSpell->getWords().length(); std::string param = reWords.substr(size, reWords.length() - size), reParam = ""; if(instantSpell->getHasParam() && !param.empty() && param[0] == ' ') { size_t quote = param.find('"', 1); if(quote != std::string::npos) { size_t tmp = param.find('"', quote + 1); if(tmp == std::string::npos) tmp = param.length(); reParam = param.substr(quote + 1, tmp - quote - 1); } else if(param.find(' ', 1) == std::string::npos) reParam = param.substr(1, param.length()); trimString(reParam); } Position pos = player->getPosition(); if(!instantSpell->castInstant(player, reParam)) return RET_NEEDEXCHANGE; SpeakClasses type = SPEAK_SAY; if(g_config.getBool(ConfigManager::EMOTE_SPELLS)) type = SPEAK_MONSTER_SAY; if(!g_config.getBool(ConfigManager::SPELL_NAME_INSTEAD_WORDS)) { if(g_config.getBool(ConfigManager::UNIFIED_SPELLS)) { reWords = instantSpell->getWords(); if(instantSpell->getHasParam()) reWords += " \"" + param + "\""; } return g_game.internalCreatureSay(player, type, reWords, player->isGhost()) ? RET_NOERROR : RET_NOTPOSSIBLE; } std::string ret = instantSpell->getName(); if(param.length()) { trimString(param); size_t tmp = 0, rtmp = param.length(); if(param[0] == '"') tmp = 1; if(param[rtmp] == '"') rtmp -= 1; ret += ": " + param.substr(tmp, rtmp); } return g_game.internalCreatureSay(player, type, ret, player->isGhost(), NULL, &pos) ? RET_NOERROR : RET_NOTPOSSIBLE; } void Spells::clear() { for(RunesMap::iterator rit = runes.begin(); rit != runes.end(); ++rit) delete rit->second; runes.clear(); for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) delete it->second; instants.clear(); m_interface.reInitState(); } Event* Spells::getEvent(const std::string& nodeName) { std::string tmpNodeName = asLowerCaseString(nodeName); if(tmpNodeName == "rune") return new RuneSpell(&m_interface); if(tmpNodeName == "instant") return new InstantSpell(&m_interface); if(tmpNodeName == "conjure") return new ConjureSpell(&m_interface); return NULL; } bool Spells::registerEvent(Event* event, xmlNodePtr, bool override) { if(InstantSpell* instant = dynamic_cast<InstantSpell*>(event)) { InstantsMap::iterator it = instants.find(instant->getWords()); if(it == instants.end()) { instants[instant->getWords()] = instant; return true; } if(override) { delete it->second; it->second = instant; return true; } std::clog << "[Warning - Spells::registerEvent] Duplicate registered instant spell with words: " << instant->getWords() << std::endl; return false; } if(RuneSpell* rune = dynamic_cast<RuneSpell*>(event)) { RunesMap::iterator it = runes.find(rune->getRuneItemId()); if(it == runes.end()) { runes[rune->getRuneItemId()] = rune; return true; } if(override) { delete it->second; it->second = rune; return true; } std::clog << "[Warning - Spells::registerEvent] Duplicate registered rune with id: " << rune->getRuneItemId() << std::endl; return false; } return false; } Spell* Spells::getSpellByName(const std::string& name) { Spell* spell; if((spell = getRuneSpellByName(name)) || (spell = getInstantSpellByName(name))) return spell; return NULL; } RuneSpell* Spells::getRuneSpell(uint32_t id) { RunesMap::iterator it = runes.find(id); if(it != runes.end()) return it->second; return NULL; } RuneSpell* Spells::getRuneSpellByName(const std::string& name) { for(RunesMap::iterator it = runes.begin(); it != runes.end(); ++it) { if(strcasecmp(it->second->getName().c_str(), name.c_str()) == 0) return it->second; } return NULL; } InstantSpell* Spells::getInstantSpell(const std::string& words) { InstantSpell* result = NULL; for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { InstantSpell* instantSpell = it->second; if(strncasecmp(instantSpell->getWords().c_str(), words.c_str(), instantSpell->getWords().length()) == 0) { if(!result || instantSpell->getWords().length() > result->getWords().length()) result = instantSpell; } } if(result && words.length() > result->getWords().length()) { std::string param = words.substr(result->getWords().length(), words.length()); if(param[0] != ' ' || (param.length() > 1 && (!result->getHasParam() || param.find(' ', 1) != std::string::npos) && param[1] != '"')) return NULL; } return result; } uint32_t Spells::getInstantSpellCount(const Player* player) { uint32_t count = 0; for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { if(it->second->canCast(player)) ++count; } return count; } InstantSpell* Spells::getInstantSpellByIndex(const Player* player, uint32_t index) { uint32_t count = 0; for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { InstantSpell* instantSpell = it->second; if(!instantSpell->canCast(player)) continue; if(count == index) return instantSpell; ++count; } return NULL; } InstantSpell* Spells::getInstantSpellByName(const std::string& name) { std::string tmpName = asLowerCaseString(name); for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { if(tmpName == asLowerCaseString(it->second->getName())) return it->second; } return NULL; } Position Spells::getCasterPosition(Creature* creature, Direction dir) { return getNextPosition(dir, creature->getPosition()); } bool BaseSpell::castSpell(Creature* creature) { if(!creature) return false; bool success = true; CreatureEventList castEvents = creature->getCreatureEvents(CREATURE_EVENT_CAST); for(CreatureEventList::iterator it = castEvents.begin(); it != castEvents.end(); ++it) { if(!(*it)->executeCast(creature) && success) success = false; } return success; } bool BaseSpell::castSpell(Creature* creature, Creature* target) { if(!creature || !target) return false; bool success = true; CreatureEventList castEvents = creature->getCreatureEvents(CREATURE_EVENT_CAST); for(CreatureEventList::iterator it = castEvents.begin(); it != castEvents.end(); ++it) { if(!(*it)->executeCast(creature, target) && success) success = false; } return success; } CombatSpell::CombatSpell(Combat* _combat, bool _needTarget, bool _needDirection) : Event(&g_spells->getInterface()) { combat =_combat; needTarget = _needTarget; needDirection = _needDirection; } CombatSpell::~CombatSpell() { if(combat) delete combat; } bool CombatSpell::loadScriptCombat() { if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); combat = env->getCombatObject(env->getLastCombatId()); env->resetCallback(); m_interface->releaseEnv(); } return combat != NULL; } bool CombatSpell::castSpell(Creature* creature) { if(!BaseSpell::castSpell(creature)) return false; if(isScripted()) { LuaVariant var; var.type = VARIANT_POSITION; if(needDirection) var.pos = Spells::getCasterPosition(creature, creature->getDirection()); else var.pos = creature->getPosition(); return executeCastSpell(creature, var); } Position pos; if(needDirection) pos = Spells::getCasterPosition(creature, creature->getDirection()); else pos = creature->getPosition(); combat->doCombat(creature, pos); return true; } bool CombatSpell::castSpell(Creature* creature, Creature* target) { if(!BaseSpell::castSpell(creature, target)) return false; if(isScripted()) { LuaVariant var; if(combat->hasArea()) { var.type = VARIANT_POSITION; if(needTarget) var.pos = target->getPosition(); else if(needDirection) var.pos = Spells::getCasterPosition(creature, creature->getDirection()); else var.pos = creature->getPosition(); } else { var.type = VARIANT_NUMBER; var.number = target->getID(); } return executeCastSpell(creature, var); } if(combat->hasArea()) { if(!needTarget) return castSpell(creature); combat->doCombat(creature, target->getPosition()); } else combat->doCombat(creature, target); return true; } bool CombatSpell::executeCastSpell(Creature* creature, const LuaVariant& var) { //onCastSpell(cid, var) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(creature->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamVariant(scriptstream, "var", var); scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onCastSpell - %s", creature->getName().c_str()); env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(creature->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); m_interface->pushVariant(L, var); bool result = m_interface->callFunction(2); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - CombatSpell::executeCastSpell] Call stack overflow." << std::endl; return false; } } Spell::Spell() { level = 0; magLevel = 0; mana = 0; manaPercent = 0; soul = 0; range = -1; exhaustion = 1000; needTarget = false; needWeapon = false; selfTarget = false; blockingSolid = false; blockingCreature = false; enabled = true; premium = false; isAggressive = true; learnable = false; } bool Spell::configureSpell(xmlNodePtr p) { int32_t intValue; std::string strValue; if(readXMLString(p, "name", strValue)) { name = strValue; const char* reservedList[] = { "melee", "physical", "poison", "earth", "fire", "ice", "freeze", "energy", "drown", "death", "curse", "holy", "lifedrain", "manadrain", "healing", "speed", "outfit", "invisible", "drunk", "firefield", "poisonfield", "energyfield", "firecondition", "poisoncondition", "energycondition", "drowncondition", "freezecondition", "cursecondition" }; for(uint32_t i = 0; i < sizeof(reservedList) / sizeof(const char*); ++i) { if(!strcasecmp(reservedList[i], name.c_str())) { std::clog << "Error: [Spell::configureSpell] Spell is using a reserved name: " << reservedList[i] << std::endl; return false; } } } else { std::clog << "Error: [Spell::configureSpell] Spell without name." << std::endl; return false; } if(readXMLInteger(p, "lvl", intValue) || readXMLInteger(p, "level", intValue)) level = intValue; if(readXMLInteger(p, "maglv", intValue) || readXMLInteger(p, "magiclevel", intValue)) magLevel = intValue; if(readXMLInteger(p, "mana", intValue)) mana = intValue; if(readXMLInteger(p, "manapercent", intValue)) manaPercent = intValue; if(readXMLInteger(p, "soul", intValue)) soul = intValue; if(readXMLInteger(p, "exhaustion", intValue)) exhaustion = intValue; if(readXMLString(p, "enabled", strValue)) enabled = booleanString(strValue); if(readXMLString(p, "prem", strValue) || readXMLString(p, "premium", strValue)) premium = booleanString(strValue); if(readXMLString(p, "needtarget", strValue)) needTarget = booleanString(strValue); if(readXMLString(p, "needweapon", strValue)) needWeapon = booleanString(strValue); if(readXMLString(p, "selftarget", strValue)) selfTarget = booleanString(strValue); if(readXMLString(p, "needlearn", strValue)) learnable = booleanString(strValue); if(readXMLInteger(p, "range", intValue)) range = intValue; if(readXMLString(p, "blocking", strValue)) blockingCreature = blockingSolid = booleanString(strValue); if(readXMLString(p, "blocktype", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); if(tmpStrValue == "all") blockingCreature = blockingSolid = true; else if(tmpStrValue == "solid") blockingSolid = true; else if(tmpStrValue == "creature") blockingCreature = true; else std::clog << "[Warning - Spell::configureSpell] Blocktype \"" <<strValue << "\" does not exist." << std::endl; } if(readXMLString(p, "aggressive", strValue)) isAggressive = booleanString(strValue); std::string error = ""; xmlNodePtr vocationNode = p->children; while(vocationNode) { if(!parseVocationNode(vocationNode, vocSpellMap, vocStringVec, error)) std::clog << "[Warning - Spell::configureSpell] " << error << std::endl; vocationNode = vocationNode->next; } return true; } bool Spell::checkSpell(Player* player) const { if(player->hasFlag(PlayerFlag_CannotUseSpells)) return false; if(player->hasFlag(PlayerFlag_IgnoreSpellCheck)) return true; if(!isEnabled()) return false; bool exhausted = false; if(isAggressive) { if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone) && player->getZone() == ZONE_PROTECTION) { player->sendCancelMessage(RET_ACTIONNOTPERMITTEDINPROTECTIONZONE); return false; } if(player->hasCondition(CONDITION_EXHAUST, EXHAUST_COMBAT)) exhausted = true; } else if(player->hasCondition(CONDITION_EXHAUST, EXHAUST_HEALING)) exhausted = true; if(exhausted && !player->hasFlag(PlayerFlag_HasNoExhaustion)) { player->sendCancelMessage(RET_YOUAREEXHAUSTED); if(isInstant()) g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(isPremium() && !player->isPremium()) { player->sendCancelMessage(RET_YOUNEEDPREMIUMACCOUNT); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if((int32_t)player->getLevel() < level) { player->sendCancelMessage(RET_NOTENOUGHLEVEL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if((int32_t)player->getMagicLevel() < magLevel) { player->sendCancelMessage(RET_NOTENOUGHMAGICLEVEL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(player->getMana() < getManaCost(player) && !player->hasFlag(PlayerFlag_HasInfiniteMana)) { player->sendCancelMessage(RET_NOTENOUGHMANA); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(player->getSoul() < soul && !player->hasFlag(PlayerFlag_HasInfiniteSoul)) { player->sendCancelMessage(RET_NOTENOUGHSOUL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(isInstant() && isLearnable() && !player->hasLearnedInstantSpell(getName())) { player->sendCancelMessage(RET_YOUNEEDTOLEARNTHISSPELL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!vocSpellMap.empty()) { if(vocSpellMap.find(player->getVocationId()) == vocSpellMap.end()) { player->sendCancelMessage(RET_YOURVOCATIONCANNOTUSETHISSPELL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } } if(needWeapon) { switch(player->getWeaponType()) { case WEAPON_SWORD: case WEAPON_CLUB: case WEAPON_AXE: case WEAPON_FIST: break; default: { player->sendCancelMessage(RET_YOUNEEDAWEAPONTOUSETHISSPELL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } } } return true; } bool Spell::checkInstantSpell(Player* player, Creature* creature) { if(!checkSpell(player)) return false; const Position& toPos = creature->getPosition(); const Position& playerPos = player->getPosition(); if(playerPos.z > toPos.z) { player->sendCancelMessage(RET_FIRSTGOUPSTAIRS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(playerPos.z < toPos.z) { player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Tile* tile = g_game.getTile(toPos); if(!tile) { tile = new StaticTile(toPos.x, toPos.y, toPos.z); g_game.setTile(tile); } ReturnValue ret; if((ret = Combat::canDoCombat(player, tile, isAggressive)) != RET_NOERROR) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(blockingCreature && creature) { player->sendCancelMessage(RET_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(blockingSolid && tile->hasProperty(BLOCKSOLID)) { player->sendCancelMessage(RET_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!needTarget) { if(!isAggressive || player->getSkull() != SKULL_BLACK) return true; player->sendCancelMessage(RET_YOUMAYNOTCASTAREAONBLACKSKULL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!creature) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Player* targetPlayer = creature->getPlayer(); if(!isAggressive || !targetPlayer || Combat::isInPvpZone(player, targetPlayer) || player->getSkullType(targetPlayer) != SKULL_NONE) return true; if(player->getSecureMode() == SECUREMODE_ON) { player->sendCancelMessage(RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(player->getSkull() == SKULL_BLACK) { player->sendCancelMessage(RET_YOUMAYNOTATTACKTHISPLAYER); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } return true; } bool Spell::checkInstantSpell(Player* player, const Position& toPos) { if(!checkSpell(player)) return false; if(toPos.x == 0xFFFF) return true; const Position& playerPos = player->getPosition(); if(playerPos.z > toPos.z) { player->sendCancelMessage(RET_FIRSTGOUPSTAIRS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(playerPos.z < toPos.z) { player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Tile* tile = g_game.getTile(toPos); if(!tile) { tile = new StaticTile(toPos.x, toPos.y, toPos.z); g_game.setTile(tile); } ReturnValue ret; if((ret = Combat::canDoCombat(player, tile, isAggressive)) != RET_NOERROR) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(blockingCreature && tile->getTopVisibleCreature(player)) { player->sendCancelMessage(RET_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(blockingSolid && tile->hasProperty(BLOCKSOLID)) { player->sendCancelMessage(RET_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(player->getSkull() == SKULL_BLACK && isAggressive && range == -1) //-1 is (usually?) an area spell { player->sendCancelMessage(RET_YOUMAYNOTCASTAREAONBLACKSKULL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } return true; } bool Spell::checkRuneSpell(Player* player, const Position& toPos) { if(!checkSpell(player)) return false; if(toPos.x == 0xFFFF) return true; const Position& playerPos = player->getPosition(); if(playerPos.z > toPos.z) { player->sendCancelMessage(RET_FIRSTGOUPSTAIRS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(playerPos.z < toPos.z) { player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Tile* tile = g_game.getTile(toPos); if(!tile) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(range != -1 && !g_game.canThrowObjectTo(playerPos, toPos, true, range, range)) { player->sendCancelMessage(RET_DESTINATIONOUTOFREACH); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } ReturnValue ret; if((ret = Combat::canDoCombat(player, tile, isAggressive)) != RET_NOERROR) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Creature* targetCreature = tile->getTopVisibleCreature(player); if(blockingCreature && targetCreature) { player->sendCancelMessage(RET_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(blockingSolid && tile->hasProperty(BLOCKSOLID)) { player->sendCancelMessage(RET_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!needTarget) { if(!isAggressive || player->getSkull() != SKULL_BLACK) return true; player->sendCancelMessage(RET_YOUMAYNOTCASTAREAONBLACKSKULL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!targetCreature) { player->sendCancelMessage(RET_CANONLYUSETHISRUNEONCREATURES); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Player* targetPlayer = targetCreature->getPlayer(); if(!isAggressive || !targetPlayer || Combat::isInPvpZone(player, targetPlayer) || player->getSkullType(targetPlayer) != SKULL_NONE) return true; if(player->getSecureMode() == SECUREMODE_ON) { player->sendCancelMessage(RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(player->getSkull() == SKULL_BLACK) { player->sendCancelMessage(RET_YOUMAYNOTATTACKTHISPLAYER); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } return true; } void Spell::postSpell(Player* player) const { if(!player->hasFlag(PlayerFlag_HasNoExhaustion) && exhaustion > 0) player->addExhaust(exhaustion, isAggressive ? EXHAUST_COMBAT : EXHAUST_HEALING); if(isAggressive && !player->hasFlag(PlayerFlag_NotGainInFight)) player->addInFightTicks(false); postSpell(player, (uint32_t)getManaCost(player), (uint32_t)getSoulCost()); } void Spell::postSpell(Player* player, uint32_t manaCost, uint32_t soulCost) const { if(manaCost > 0) { player->changeMana(-(int32_t)manaCost); if(!player->hasFlag(PlayerFlag_NotGainMana) && (player->getZone() != ZONE_HARDCORE || g_config.getBool(ConfigManager::PVPZONE_ADDMANASPENT))) player->addManaSpent(manaCost); } if(soulCost > 0) player->changeSoul(-(int32_t)soulCost); } int32_t Spell::getManaCost(const Player* player) const { if(player && manaPercent) return (int32_t)std::floor(double(player->getMaxMana() * manaPercent) / 100); return mana; } ReturnValue Spell::CreateIllusion(Creature* creature, const Outfit_t& outfit, int32_t time) { ConditionOutfit* outfitCondition = new ConditionOutfit(CONDITIONID_COMBAT, CONDITION_OUTFIT, time, false, 0); if(!outfitCondition) return RET_NOTPOSSIBLE; outfitCondition->addOutfit(outfit); creature->addCondition(outfitCondition); return RET_NOERROR; } ReturnValue Spell::CreateIllusion(Creature* creature, const std::string& name, int32_t time) { uint32_t mId = g_monsters.getIdByName(name); if(!mId) return RET_CREATUREDOESNOTEXIST; const MonsterType* mType = g_monsters.getMonsterType(mId); if(!mType) return RET_CREATUREDOESNOTEXIST; Player* player = creature->getPlayer(); if(player && !player->hasFlag(PlayerFlag_CanIllusionAll) && !mType->isIllusionable) return RET_NOTPOSSIBLE; return CreateIllusion(creature, mType->outfit, time); } ReturnValue Spell::CreateIllusion(Creature* creature, uint32_t itemId, int32_t time) { const ItemType& it = Item::items[itemId]; if(!it.id) return RET_NOTPOSSIBLE; Outfit_t outfit; outfit.lookTypeEx = itemId; return CreateIllusion(creature, outfit, time); } InstantSpell::InstantSpell(LuaInterface* _interface) : TalkAction(_interface) { needDirection = false; hasParam = false; checkLineOfSight = true; casterTargetOrDirection = false; limitRange = 0; function = NULL; } bool InstantSpell::configureEvent(xmlNodePtr p) { if(!Spell::configureSpell(p)) return false; if(!TalkAction::configureEvent(p)) return false; std::string strValue; if(readXMLString(p, "param", strValue) || readXMLString(p, "params", strValue)) hasParam = booleanString(strValue); if(readXMLString(p, "direction", strValue)) needDirection = booleanString(strValue); if(readXMLString(p, "casterTargetOrDirection", strValue)) casterTargetOrDirection = booleanString(strValue); if(readXMLString(p, "blockwalls", strValue)) checkLineOfSight = booleanString(strValue); int32_t intValue; if(readXMLInteger(p, "limitRange", intValue)) limitRange = intValue; return true; } bool InstantSpell::loadFunction(const std::string& functionName) { std::string tmpFunctionName = asLowerCaseString(functionName); if(tmpFunctionName == "summonmonster") function = SummonMonster; else if(tmpFunctionName == "searchplayer") { isAggressive = false; function = SearchPlayer; } else if(tmpFunctionName == "levitate") { isAggressive = false; function = Levitate; } else if(tmpFunctionName == "illusion") { isAggressive = false; function = Illusion; } else { std::clog << "[Warning - InstantSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } m_scripted = EVENT_SCRIPT_FALSE; return true; } bool InstantSpell::castInstant(Player* player, const std::string& param) { LuaVariant var; if(selfTarget) { var.type = VARIANT_NUMBER; var.number = player->getID(); if(!checkInstantSpell(player, player)) return false; } else if(needTarget || casterTargetOrDirection) { Creature* target = NULL; if(hasParam) { Player* targetPlayer = NULL; ReturnValue ret = g_game.getPlayerByNameWildcard(param, targetPlayer); target = targetPlayer; if(limitRange && target && !Position::areInRange(Position(limitRange, limitRange, 0), target->getPosition(), player->getPosition())) target = NULL; if((!target || target->getHealth() <= 0) && !casterTargetOrDirection) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } } else { target = player->getAttackedCreature(); if(limitRange && target && !Position::areInRange(Position(limitRange, limitRange, 0), target->getPosition(), player->getPosition())) target = NULL; if((!target || target->getHealth() <= 0) && !casterTargetOrDirection) { player->sendCancelMessage(RET_YOUCANONLYUSEITONCREATURES); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } } if(target) { bool canSee = player->canSeeCreature(target); if(!canSee || !canThrowSpell(player, target)) { player->sendCancelMessage(canSee ? RET_CREATUREISNOTREACHABLE : RET_PLAYERWITHTHISNAMEISNOTONLINE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } var.type = VARIANT_NUMBER; var.number = target->getID(); if(!checkInstantSpell(player, target)) return false; } else { var.type = VARIANT_POSITION; var.pos = Spells::getCasterPosition(player, player->getDirection()); if(!checkInstantSpell(player, var.pos)) return false; } } else if(hasParam) { var.type = VARIANT_STRING; var.text = param; if(!checkSpell(player)) return false; } else { var.type = VARIANT_POSITION; if(needDirection) var.pos = Spells::getCasterPosition(player, player->getDirection()); else var.pos = player->getPosition(); if(!checkInstantSpell(player, var.pos)) return false; } if(!internalCastSpell(player, var)) return false; Spell::postSpell(player); return true; } bool InstantSpell::canThrowSpell(const Creature* creature, const Creature* target) const { const Position& fromPos = creature->getPosition(); const Position& toPos = target->getPosition(); return (!(fromPos.z != toPos.z || (range == -1 && !g_game.canThrowObjectTo(fromPos, toPos, checkLineOfSight)) || (range != -1 && !g_game.canThrowObjectTo(fromPos, toPos, checkLineOfSight, range, range)))); } bool InstantSpell::castSpell(Creature* creature) { if(!BaseSpell::castSpell(creature)) return false; LuaVariant var; if(casterTargetOrDirection) { Creature* target = creature->getAttackedCreature(); if(target && target->getHealth() > 0) { if(!creature->canSeeCreature(target) || !canThrowSpell(creature, target)) return false; var.type = VARIANT_NUMBER; var.number = target->getID(); return internalCastSpell(creature, var); } return false; } if(needDirection) { var.type = VARIANT_POSITION; var.pos = Spells::getCasterPosition(creature, creature->getDirection()); } else { var.type = VARIANT_POSITION; var.pos = creature->getPosition(); } return internalCastSpell(creature, var); } bool InstantSpell::castSpell(Creature* creature, Creature* target) { if(!BaseSpell::castSpell(creature, target)) return false; if(!needTarget) return castSpell(creature); LuaVariant var; var.type = VARIANT_NUMBER; var.number = target->getID(); return internalCastSpell(creature, var); } bool InstantSpell::internalCastSpell(Creature* creature, const LuaVariant& var) { if(isScripted()) return executeCastSpell(creature, var); return function ? function(this, creature, var.text) : false; } bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) { //onCastSpell(cid, var) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(creature->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamVariant(scriptstream, "var", var); scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onCastSpell - %s", creature->getName().c_str()); env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(creature->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); m_interface->pushVariant(L, var); bool result = m_interface->callFunction(2); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - InstantSpell::executeCastSpell] Call stack overflow." << std::endl; return false; } } bool InstantSpell::SearchPlayer(const InstantSpell*, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player || player->isRemoved()) return false; Player* targetPlayer = NULL; ReturnValue ret = g_game.getPlayerByNameWildcard(param, targetPlayer); if(ret != RET_NOERROR || !targetPlayer || targetPlayer->isRemoved()) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(targetPlayer->hasCustomFlag(PlayerCustomFlag_NotSearchable) && !player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)) { player->sendCancelMessage(RET_PLAYERWITHTHISNAMEISNOTONLINE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } std::stringstream ss; ss << targetPlayer->getName() << " " << g_game.getSearchString(player->getPosition(), targetPlayer->getPosition(), true, true) << "."; player->sendTextMessage(MSG_INFO_DESCR, ss.str().c_str()); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_BLUE); return true; } bool InstantSpell::SummonMonster(const InstantSpell* spell, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player) return false; MonsterType* mType = g_monsters.getMonsterType(param); if(!mType) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } int32_t manaCost = (int32_t)(mType->manaCost * g_config.getDouble(ConfigManager::RATE_MONSTER_MANA)); if(!player->hasFlag(PlayerFlag_CanSummonAll)) { if(player->getSkull() == SKULL_BLACK) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!mType->isSummonable) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(player->getMana() < manaCost) { player->sendCancelMessage(RET_NOTENOUGHMANA); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if((int32_t)player->getSummonCount() >= g_config.getNumber(ConfigManager::MAX_PLAYER_SUMMONS)) { player->sendCancel("You cannot summon more creatures."); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } } ReturnValue ret = g_game.placeSummon(creature, param); if(ret == RET_NOERROR) { spell->postSpell(player, (uint32_t)manaCost, (uint32_t)spell->getSoulCost()); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_BLUE); return true; } player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } bool InstantSpell::Levitate(const InstantSpell*, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player) return false; uint16_t floor = 7; ReturnValue ret = RET_NOERROR; const Position& position = creature->getPosition(); Position destination = Spells::getCasterPosition(creature, creature->getDirection()); std::string tmpParam = asLowerCaseString(param); if(tmpParam == "up") floor = 8; else if(tmpParam != "down") ret = RET_NOTPOSSIBLE; if(ret == RET_NOERROR) { ret = RET_NOTPOSSIBLE; if(position.z != floor) { Tile* tmpTile = NULL; if(floor != 7) { tmpTile = g_game.getTile(position.x, position.y, position.z - 1); destination.z--; } else { tmpTile = g_game.getTile(destination); destination.z++; } if(!tmpTile || (!tmpTile->ground && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID))) { Tile* tile = player->getTile(); tmpTile = g_game.getTile(destination); if(tile && tmpTile && tmpTile->ground && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID) && !tmpTile->floorChange() && tile->hasFlag(TILESTATE_HOUSE) == tmpTile->hasFlag(TILESTATE_HOUSE) && tile->hasFlag(TILESTATE_PROTECTIONZONE) == tmpTile->hasFlag(TILESTATE_PROTECTIONZONE)) ret = g_game.internalMoveCreature(NULL, player, tile, tmpTile, FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE); } } } if(ret == RET_NOERROR) { g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_TELEPORT, player->isGhost()); return true; } player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF, player->isGhost()); return false; } bool InstantSpell::Illusion(const InstantSpell*, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player) return false; ReturnValue ret = CreateIllusion(creature, param, 60000); if(ret == RET_NOERROR) { g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return (ret == RET_NOERROR); } bool InstantSpell::canCast(const Player* player) const { if(player->hasFlag(PlayerFlag_CannotUseSpells)) return false; if(player->hasFlag(PlayerFlag_IgnoreSpellCheck) || (!isLearnable() && (vocSpellMap.empty() || vocSpellMap.find(player->getVocationId()) != vocSpellMap.end()))) return true; return player->hasLearnedInstantSpell(getName()); } ConjureSpell::ConjureSpell(LuaInterface* _interface): InstantSpell(_interface) { isAggressive = false; conjureId = 0; conjureCount = 1; conjureReagentId = 0; } bool ConjureSpell::configureEvent(xmlNodePtr p) { if(!InstantSpell::configureEvent(p)) return false; int32_t intValue; if(readXMLInteger(p, "conjureId", intValue)) conjureId = intValue; if(readXMLInteger(p, "conjureCount", intValue)) conjureCount = intValue; else if(conjureId != 0) { //load the default charge from items.xml const ItemType& it = Item::items[conjureId]; if(it.charges != 0) conjureCount = it.charges; } if(readXMLInteger(p, "reagentId", intValue)) conjureReagentId = intValue; return true; } bool ConjureSpell::loadFunction(const std::string& functionName) { std::string tmpFunctionName = asLowerCaseString(functionName); if(tmpFunctionName == "conjureitem" || tmpFunctionName == "conjurerune") function = ConjureItem; else { std::clog << "[Warning - ConjureSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } m_scripted = EVENT_SCRIPT_FALSE; return true; } ReturnValue ConjureSpell::internalConjureItem(Player* player, uint32_t conjureId, uint32_t conjureCount, bool transform/* = false*/, uint32_t reagentId/* = 0*/) { if(!transform) { Item* newItem = Item::CreateItem(conjureId, conjureCount); if(!newItem) return RET_NOTPOSSIBLE; ReturnValue ret = g_game.internalPlayerAddItem(player, player, newItem, true); if(ret != RET_NOERROR) delete newItem; return ret; } if(!reagentId) return RET_NOTPOSSIBLE; std::list<Container*> containers; Item *item = NULL, *fromItem = NULL; for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) { if(!(item = player->getInventoryItem((slots_t)i))) continue; if(!fromItem && item->getID() == reagentId) fromItem = item; else if(Container* container = item->getContainer()) containers.push_back(container); } if(!fromItem) { for(std::list<Container*>::iterator cit = containers.begin(); cit != containers.end(); ++cit) { for(ItemList::const_reverse_iterator it = (*cit)->getReversedItems(); it != (*cit)->getReversedEnd(); ++it) { if((*it)->getID() == reagentId) { fromItem = (*it); break; } if(Container* tmp = (*it)->getContainer()) containers.push_back(tmp); } } } if(!fromItem) return RET_YOUNEEDAMAGICITEMTOCASTSPELL; item = Item::CreateItem(conjureId, conjureCount); ReturnValue ret = g_game.internalPlayerAddItem(NULL, player, item, false); if(ret != RET_NOERROR) return ret; g_game.transformItem(fromItem, reagentId, fromItem->getItemCount() - 1); g_game.startDecay(item); return RET_NOERROR; } bool ConjureSpell::ConjureItem(const ConjureSpell* spell, Creature* creature, const std::string&) { Player* player = creature->getPlayer(); if(!player) return false; if(!player->hasFlag(PlayerFlag_IgnoreSpellCheck) && player->getZone() == ZONE_HARDCORE) { player->sendCancelMessage(RET_CANNOTCONJUREITEMHERE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } ReturnValue result = RET_NOTPOSSIBLE; if(spell->getReagentId() != 0) { if((result = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount(), true, spell->getReagentId())) == RET_NOERROR) { spell->postSpell(player); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } } else if((result = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount())) == RET_NOERROR) { spell->postSpell(player); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } player->sendCancelMessage(result); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } bool ConjureSpell::castInstant(Player* player, const std::string& param) { if(!checkSpell(player)) return false; if(!isScripted()) return function ? function(this, player, param) : false; LuaVariant var; var.type = VARIANT_STRING; var.text = param; return executeCastSpell(player, var); } RuneSpell::RuneSpell(LuaInterface* _interface): Action(_interface) { runeId = 0; function = NULL; hasCharges = allowFarUse = true; } bool RuneSpell::configureEvent(xmlNodePtr p) { if(!Spell::configureSpell(p)) return false; if(!Action::configureEvent(p)) return false; int32_t intValue; if(readXMLInteger(p, "id", intValue)) runeId = intValue; else { std::clog << "Error: [RuneSpell::configureSpell] Rune spell without id." << std::endl; return false; } std::string strValue; if(readXMLString(p, "charges", strValue)) hasCharges = booleanString(strValue); ItemType& it = Item::items.getItemType(runeId); if(level && level != it.runeLevel) it.runeLevel = level; if(magLevel && magLevel != it.runeMagLevel) it.runeMagLevel = magLevel; it.vocationString = parseVocationString(vocStringVec); return true; } bool RuneSpell::loadFunction(const std::string& functionName) { std::string tmpFunctionName = asLowerCaseString(functionName); if(tmpFunctionName == "chameleon") function = Illusion; else if(tmpFunctionName == "convince") function = Convince; else { std::clog << "[Warning - RuneSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } m_scripted = EVENT_SCRIPT_FALSE; return true; } bool RuneSpell::Illusion(const RuneSpell*, Creature* creature, Item*, const Position&, const Position& posTo) { Player* player = creature->getPlayer(); if(!player) return false; Thing* thing = g_game.internalGetThing(player, posTo, 0, 0, STACKPOS_MOVE); if(!thing) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Item* illusionItem = thing->getItem(); if(!illusionItem || !illusionItem->isMovable()) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } ReturnValue ret = CreateIllusion(creature, illusionItem->getID(), 60000); if(ret == RET_NOERROR) { g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } bool RuneSpell::Convince(const RuneSpell* spell, Creature* creature, Item*, const Position&, const Position& posTo) { Player* player = creature->getPlayer(); if(!player) return false; if(!player->hasFlag(PlayerFlag_CanConvinceAll)) { if(player->getSkull() == SKULL_BLACK) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if((int32_t)player->getSummonCount() >= g_config.getNumber(ConfigManager::MAX_PLAYER_SUMMONS)) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } } Thing* thing = g_game.internalGetThing(player, posTo, 0, 0, STACKPOS_LOOK); if(!thing) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } Creature* convinceCreature = thing->getCreature(); if(!convinceCreature) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } int32_t manaCost = 0; if(Monster* monster = convinceCreature->getMonster()) manaCost = (int32_t)(monster->getManaCost() * g_config.getDouble(ConfigManager::RATE_MONSTER_MANA)); if(!player->hasFlag(PlayerFlag_HasInfiniteMana) && player->getMana() < manaCost) { player->sendCancelMessage(RET_NOTENOUGHMANA); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } if(!convinceCreature->convinceCreature(creature)) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } spell->postSpell(player, (uint32_t)manaCost, (uint32_t)spell->getSoulCost()); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } ReturnValue RuneSpell::canExecuteAction(const Player* player, const Position& toPos) { if(player->hasFlag(PlayerFlag_CannotUseSpells)) return RET_CANNOTUSETHISOBJECT; ReturnValue ret = Action::canExecuteAction(player, toPos); if(ret != RET_NOERROR) return ret; if(toPos.x == 0xFFFF) { if(needTarget) return RET_CANONLYUSETHISRUNEONCREATURES; if(!selfTarget) return RET_NOTENOUGHROOM; } return RET_NOERROR; } bool RuneSpell::executeUse(Player* player, Item* item, const PositionEx& posFrom, const PositionEx& posTo, bool, uint32_t creatureId) { if(!checkRuneSpell(player, posTo)) return false; bool result = false; if(isScripted()) { LuaVariant var; if(creatureId && needTarget) { var.type = VARIANT_NUMBER; var.number = creatureId; } else { var.type = VARIANT_POSITION; var.pos = posTo; } result = internalCastSpell(player, var); } else if(function) result = function(this, player, item, posFrom, posTo); if(result) { Spell::postSpell(player); if(hasCharges && item && g_config.getBool(ConfigManager::REMOVE_RUNE_CHARGES)) g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getItemCount()) - 1)); } return result; } bool RuneSpell::castSpell(Creature* creature) { if(!BaseSpell::castSpell(creature)) return false; LuaVariant var; var.type = VARIANT_NUMBER; var.number = creature->getID(); return internalCastSpell(creature, var); } bool RuneSpell::castSpell(Creature* creature, Creature* target) { if(!BaseSpell::castSpell(creature, target)) return false; LuaVariant var; var.type = VARIANT_NUMBER; var.number = target->getID(); return internalCastSpell(creature, var); } bool RuneSpell::internalCastSpell(Creature* creature, const LuaVariant& var) { return isScripted() ? executeCastSpell(creature, var) : false; } bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) { //onCastSpell(cid, var) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(creature->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamVariant(scriptstream, "var", var); scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onCastSpell - %s", creature->getName().c_str()); env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(creature->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); m_interface->pushVariant(L, var); bool result = m_interface->callFunction(2); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - RuneSpell::executeCastSpell] Call stack overflow." << std::endl; return false; } } spells.h //////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #ifndef __SPELLS__ #define __SPELLS__ #include "otsystem.h" #include "enums.h" #include "player.h" #include "luascript.h" #include "baseevents.h" #include "actions.h" #include "talkaction.h" class InstantSpell; class ConjureSpell; class RuneSpell; class Spell; typedef std::map<uint32_t, RuneSpell*> RunesMap; typedef std::map<std::string, InstantSpell*> InstantsMap; class Spells : public BaseEvents { public: Spells(); virtual ~Spells() {clear();} Spell* getSpellByName(const std::string& name); RuneSpell* getRuneSpell(uint32_t id); RuneSpell* getRuneSpellByName(const std::string& name); InstantSpell* getInstantSpell(const std::string& words); InstantSpell* getInstantSpellByName(const std::string& name); InstantSpell* getInstantSpellByIndex(const Player* player, uint32_t index); uint32_t getInstantSpellCount(const Player* player); ReturnValue onPlayerSay(Player* player, const std::string& words); virtual std::string getScriptBaseName() const {return "spells";} static Position getCasterPosition(Creature* creature, Direction dir); protected: virtual void clear(); virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); virtual LuaInterface& getInterface() {return m_interface;} LuaInterface m_interface; RunesMap runes; InstantsMap instants; friend class CombatSpell; }; typedef bool (InstantSpellFunction)(const InstantSpell* spell, Creature* creature, const std::string& param); typedef bool (ConjureSpellFunction)(const ConjureSpell* spell, Creature* creature, const std::string& param); typedef bool (RuneSpellFunction)(const RuneSpell* spell, Creature* creature, Item* item, const Position& posFrom, const Position& posTo); class BaseSpell { public: BaseSpell() {} virtual ~BaseSpell() {} virtual bool castSpell(Creature* creature); virtual bool castSpell(Creature* creature, Creature* target); }; class CombatSpell : public Event, public BaseSpell { public: CombatSpell(Combat* _combat, bool _needTarget, bool _needDirection); virtual ~CombatSpell(); virtual bool castSpell(Creature* creature); virtual bool castSpell(Creature* creature, Creature* target); virtual bool configureEvent(xmlNodePtr) {return true;} //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var); bool loadScriptCombat(); Combat* getCombat() {return combat;} protected: virtual std::string getScriptEventName() const {return "onCastSpell";} virtual std::string getScriptEventParams() const {return "cid, var";} bool needDirection; bool needTarget; Combat* combat; }; class Spell : public BaseSpell { public: Spell(); virtual ~Spell() {} bool configureSpell(xmlNodePtr xmlspell); const std::string& getName() const {return name;} void postSpell(Player* player) const; void postSpell(Player* player, uint32_t manaCost, uint32_t soulCost) const; int32_t getManaCost(const Player* player) const; int32_t getSoulCost() const {return soul;} uint32_t getLevel() const {return level;} int32_t getMagicLevel() const {return magLevel;} int32_t getMana() const {return mana;} int32_t getManaPercent() const {return manaPercent;} uint32_t getExhaustion() const {return exhaustion;} bool isEnabled() const {return enabled;} bool isPremium() const {return premium;} virtual bool isInstant() const = 0; bool isLearnable() const {return learnable;} static ReturnValue CreateIllusion(Creature* creature, const Outfit_t& outfit, int32_t time); static ReturnValue CreateIllusion(Creature* creature, const std::string& name, int32_t time); static ReturnValue CreateIllusion(Creature* creature, uint32_t itemId, int32_t time); protected: bool checkSpell(Player* player) const; bool checkInstantSpell(Player* player, Creature* creature); bool checkInstantSpell(Player* player, const Position& toPos); bool checkRuneSpell(Player* player, const Position& toPos); int32_t level; int32_t magLevel; bool premium; bool learnable; bool enabled; int32_t mana; int32_t manaPercent; int32_t soul; int32_t range; uint32_t exhaustion; bool needTarget; bool needWeapon; bool blockingSolid; bool blockingCreature; bool selfTarget; bool isAggressive; VocationMap vocSpellMap; typedef std::vector<std::string> VocStringVec; VocStringVec vocStringVec; private: std::string name; }; class InstantSpell : public TalkAction, public Spell { public: InstantSpell(LuaInterface* _interface); virtual ~InstantSpell() {} virtual bool configureEvent(xmlNodePtr p); virtual bool loadFunction(const std::string& functionName); virtual bool castInstant(Player* player, const std::string& param); virtual bool castSpell(Creature* creature); virtual bool castSpell(Creature* creature, Creature* target); //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var); virtual bool isInstant() const {return true;} bool getHasParam() const {return hasParam;} bool canCast(const Player* player) const; bool canThrowSpell(const Creature* creature, const Creature* target) const; protected: virtual std::string getScriptEventName() const {return "onCastSpell";} virtual std::string getScriptEventParams() const {return "cid, var";} static InstantSpellFunction SearchPlayer; static InstantSpellFunction SummonMonster; static InstantSpellFunction Levitate; static InstantSpellFunction Illusion; bool internalCastSpell(Creature* creature, const LuaVariant& var); bool needDirection; bool hasParam; bool checkLineOfSight; bool casterTargetOrDirection; uint8_t limitRange; InstantSpellFunction* function; }; class ConjureSpell : public InstantSpell { public: ConjureSpell(LuaInterface* _interface); virtual ~ConjureSpell() {} virtual bool configureEvent(xmlNodePtr p); virtual bool loadFunction(const std::string& functionName); virtual bool castInstant(Player* player, const std::string& param); virtual bool castSpell(Creature*) {return false;} virtual bool castSpell(Creature*, Creature*) {return false;} uint32_t getConjureId() const {return conjureId;} uint32_t getConjureCount() const {return conjureCount;} uint32_t getReagentId() const {return conjureReagentId;} protected: virtual std::string getScriptEventName() const {return "onCastSpell";} virtual std::string getScriptEventParams() const {return "cid, var";} static ReturnValue internalConjureItem(Player* player, uint32_t conjureId, uint32_t conjureCount, bool transform = false, uint32_t reagentId = 0); static ConjureSpellFunction ConjureItem; bool internalCastSpell(Creature* creature, const LuaVariant& var); Position getCasterPosition(Creature* creature); ConjureSpellFunction* function; uint32_t conjureId; uint32_t conjureCount; uint32_t conjureReagentId; }; class RuneSpell : public Action, public Spell { public: RuneSpell(LuaInterface* _interface); virtual ~RuneSpell() {} virtual bool configureEvent(xmlNodePtr p); virtual bool loadFunction(const std::string& functionName); virtual ReturnValue canExecuteAction(const Player* player, const Position& toPos); virtual bool hasOwnErrorHandler() {return true;} virtual bool executeUse(Player* player, Item* item, const PositionEx& posFrom, const PositionEx& posTo, bool extendedUse, uint32_t creatureId); virtual bool castSpell(Creature* creature); virtual bool castSpell(Creature* creature, Creature* target); //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var); virtual bool isInstant() const {return false;} uint32_t getRuneItemId(){return runeId;} protected: virtual std::string getScriptEventName() const {return "onCastSpell";} virtual std::string getScriptEventParams() const {return "cid, var";} static RuneSpellFunction Illusion; static RuneSpellFunction Convince; bool internalCastSpell(Creature* creature, const LuaVariant& var); bool hasCharges; uint32_t runeId; RuneSpellFunction* function; }; #endif vocation.h //////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #ifndef __OTSERV_VOCATION__ #define __OTSERV_VOCATION__ #include "otsystem.h" #include "enums.h" enum multiplier_t { MULTIPLIER_FIRST = 0, MULTIPLIER_MELEE = MULTIPLIER_FIRST, MULTIPLIER_DISTANCE = 1, MULTIPLIER_DEFENSE = 2, MULTIPLIER_MAGICDEFENSE = 3, MULTIPLIER_ARMOR = 4, MULTIPLIER_MAGIC = 5, MULTIPLIER_HEALING = 6, MULTIPLIER_WAND = 7, MULTIPLIER_MANA = 8, MULTIPLIER_LAST = MULTIPLIER_MANA }; enum gain_t { GAIN_FIRST = 0, GAIN_HEALTH = GAIN_FIRST, GAIN_MANA = 1, GAIN_SOUL = 2, GAIN_LAST = GAIN_SOUL }; class Vocation { public: virtual ~Vocation(); Vocation() {reset();} Vocation(uint32_t _id): id(_id) {reset();} void reset(); uint32_t getId() const {return id;} void setId(int32_t v) {id = v;} uint32_t getFromVocation() const {return fromVocation;} void setFromVocation(int32_t v) {fromVocation = v;} const std::string& getName() const {return name;} void setName(const std::string& v) {name = v;} const std::string& getDescription() const {return description;} void setDescription(const std::string& v) {description = v;} bool isAttackable() const {return attackable;} void setAttackable(bool v) {attackable = v;} bool isPremiumNeeded() const {return needPremium;} void setNeedPremium(bool v) {needPremium = v;} uint32_t getAttackSpeed() const {return attackSpeed;} void setAttackSpeed(uint32_t v) {attackSpeed = v;} uint32_t getBaseSpeed() const {return baseSpeed;} void setBaseSpeed(uint32_t v) {baseSpeed = v;} int32_t getLessLoss() const {return lessLoss;} void setLessLoss(int32_t v) {lessLoss = v;} int32_t getGainCap() const {return capGain;} void setGainCap(int32_t v) {capGain = v;} uint32_t getGain(gain_t type) const {return gain[type];} void setGain(gain_t type, uint32_t v) {gain[type] = v;} uint32_t getGainTicks(gain_t type) const {return gainTicks[type];} void setGainTicks(gain_t type, uint32_t v) {gainTicks[type] = v;} uint32_t getGainAmount(gain_t type) const {return gainAmount[type];} void setGainAmount(gain_t type, uint32_t v) {gainAmount[type] = v;} float getMultiplier(multiplier_t type) const {return formulaMultipliers[type];} void setMultiplier(multiplier_t type, float v) {formulaMultipliers[type] = v;} int16_t getAbsorb(CombatType_t combat) const {return absorb[combat];} void increaseAbsorb(CombatType_t combat, int16_t v) {absorb[combat] += v;} int16_t getReflect(CombatType_t combat) const; void increaseReflect(Reflect_t type, CombatType_t combat, int16_t v) {reflect[type][combat] += v;} double getExperienceMultiplier() const {return skillMultipliers[SKILL__LEVEL];} void setSkillMultiplier(skills_t s, float v) {skillMultipliers[s] = v;} void setSkillBase(skills_t s, uint32_t v) {skillBase[s] = v;} uint64_t getReqSkillTries(int32_t skill, int32_t level); uint64_t getReqMana(uint32_t magLevel); private: typedef std::map<uint32_t, uint64_t> cacheMap; cacheMap cacheSkill[SKILL_LAST + 1]; cacheMap cacheMana; bool attackable, needPremium; int32_t lessLoss, capGain; uint32_t id, fromVocation, baseSpeed, attackSpeed; std::string name, description; int16_t absorb[COMBAT_LAST + 1], reflect[REFLECT_LAST + 1][COMBAT_LAST + 1]; uint32_t gain[GAIN_LAST + 1], gainTicks[GAIN_LAST + 1], gainAmount[GAIN_LAST + 1], skillBase[SKILL_LAST + 1]; float skillMultipliers[SKILL__LAST + 1], formulaMultipliers[MULTIPLIER_LAST + 1]; }; typedef std::map<uint32_t, Vocation*> VocationsMap; class Vocations { public: virtual ~Vocations() {clear();} static Vocations* getInstance() { static Vocations instance; return &instance; } bool reload(); bool loadFromXml(); bool parseVocationNode(xmlNodePtr p); Vocation* getVocation(uint32_t vocId); int32_t getVocationId(const std::string& name); int32_t getPromotedVocation(uint32_t vocationId); VocationsMap::iterator getFirstVocation() {return vocationsMap.begin();} VocationsMap::iterator getLastVocation() {return vocationsMap.end();} private: static Vocation defVoc; VocationsMap vocationsMap; Vocations() {} void clear(); }; #endif vocation.cpp //////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include <iostream> #include <libxml/xmlmemory.h> #include <libxml/parser.h> #include "vocation.h" #include "tools.h" Vocation Vocations::defVoc = Vocation(); void Vocations::clear() { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) delete it->second; vocationsMap.clear(); } bool Vocations::reload() { clear(); return loadFromXml(); } bool Vocations::parseVocationNode(xmlNodePtr p) { std::string strValue; int32_t intValue; float floatValue; if(xmlStrcmp(p->name, (const xmlChar*)"vocation")) return false; if(!readXMLInteger(p, "id", intValue)) { std::clog << "[Error - Vocations::parseVocationNode] Missing vocation id." << std::endl; return false; } Vocation* voc = new Vocation(intValue); if(readXMLString(p, "name", strValue)) voc->setName(strValue); if(readXMLString(p, "description", strValue)) voc->setDescription(strValue); if(readXMLString(p, "needpremium", strValue)) voc->setNeedPremium(booleanString(strValue)); if(readXMLInteger(p, "gaincap", intValue) || readXMLInteger(p, "gaincapacity", intValue)) voc->setGainCap(intValue); if(readXMLInteger(p, "gainhp", intValue) || readXMLInteger(p, "gainhealth", intValue)) voc->setGain(GAIN_HEALTH, intValue); if(readXMLInteger(p, "gainmana", intValue)) voc->setGain(GAIN_MANA, intValue); if(readXMLInteger(p, "gainhpticks", intValue) || readXMLInteger(p, "gainhealthticks", intValue)) voc->setGainTicks(GAIN_HEALTH, intValue); if(readXMLInteger(p, "gainhpamount", intValue) || readXMLInteger(p, "gainhealthamount", intValue)) voc->setGainAmount(GAIN_HEALTH, intValue); if(readXMLInteger(p, "gainmanaticks", intValue)) voc->setGainTicks(GAIN_MANA, intValue); if(readXMLInteger(p, "gainmanaamount", intValue)) voc->setGainAmount(GAIN_MANA, intValue); if(readXMLFloat(p, "manamultiplier", floatValue)) voc->setMultiplier(MULTIPLIER_MANA, floatValue); if(readXMLInteger(p, "attackspeed", intValue)) voc->setAttackSpeed(intValue); if(readXMLInteger(p, "basespeed", intValue)) voc->setBaseSpeed(intValue); if(readXMLInteger(p, "soulmax", intValue)) voc->setGain(GAIN_SOUL, intValue); if(readXMLInteger(p, "gainsoulamount", intValue)) voc->setGainAmount(GAIN_SOUL, intValue); if(readXMLInteger(p, "gainsoulticks", intValue)) voc->setGainTicks(GAIN_SOUL, intValue); if(readXMLString(p, "attackable", strValue)) voc->setAttackable(booleanString(strValue)); if(readXMLInteger(p, "fromvoc", intValue) || readXMLInteger(p, "fromvocation", intValue)) voc->setFromVocation(intValue); if(readXMLInteger(p, "lessloss", intValue)) voc->setLessLoss(intValue); for(xmlNodePtr configNode = p->children; configNode; configNode = configNode->next) { if(!xmlStrcmp(configNode->name, (const xmlChar*)"skill")) { if(readXMLFloat(configNode, "fist", floatValue)) voc->setSkillMultiplier(SKILL_FIST, floatValue); if(readXMLInteger(configNode, "fistBase", intValue)) voc->setSkillBase(SKILL_FIST, intValue); if(readXMLFloat(configNode, "club", floatValue)) voc->setSkillMultiplier(SKILL_CLUB, floatValue); if(readXMLInteger(configNode, "clubBase", intValue)) voc->setSkillBase(SKILL_CLUB, intValue); if(readXMLFloat(configNode, "axe", floatValue)) voc->setSkillMultiplier(SKILL_AXE, floatValue); if(readXMLInteger(configNode, "axeBase", intValue)) voc->setSkillBase(SKILL_AXE, intValue); if(readXMLFloat(configNode, "sword", floatValue)) voc->setSkillMultiplier(SKILL_SWORD, floatValue); if(readXMLInteger(configNode, "swordBase", intValue)) voc->setSkillBase(SKILL_SWORD, intValue); if(readXMLFloat(configNode, "distance", floatValue) || readXMLFloat(configNode, "dist", floatValue)) voc->setSkillMultiplier(SKILL_DIST, floatValue); if(readXMLInteger(configNode, "distanceBase", intValue) || readXMLInteger(configNode, "distBase", intValue)) voc->setSkillBase(SKILL_DIST, intValue); if(readXMLFloat(configNode, "shielding", floatValue) || readXMLFloat(configNode, "shield", floatValue)) voc->setSkillMultiplier(SKILL_SHIELD, floatValue); if(readXMLInteger(configNode, "shieldingBase", intValue) || readXMLInteger(configNode, "shieldBase", intValue)) voc->setSkillBase(SKILL_SHIELD, intValue); if(readXMLFloat(configNode, "fishing", floatValue) || readXMLFloat(configNode, "fish", floatValue)) voc->setSkillMultiplier(SKILL_FISH, floatValue); if(readXMLInteger(configNode, "fishingBase", intValue) || readXMLInteger(configNode, "fishBase", intValue)) voc->setSkillBase(SKILL_FISH, intValue); if(readXMLFloat(configNode, "experience", floatValue) || readXMLFloat(configNode, "exp", floatValue)) voc->setSkillMultiplier(SKILL__LEVEL, floatValue); if(readXMLInteger(configNode, "id", intValue)) { skills_t skill = (skills_t)intValue; if(intValue < SKILL_FIRST || intValue >= SKILL__LAST) { std::clog << "[Error - Vocations::parseVocationNode] No valid skill id (" << intValue << ")." << std::endl; continue; } if(readXMLInteger(configNode, "base", intValue)) voc->setSkillBase(skill, intValue); if(readXMLFloat(configNode, "multiplier", floatValue)) voc->setSkillMultiplier(skill, floatValue); } } else if(!xmlStrcmp(configNode->name, (const xmlChar*)"formula")) { if(readXMLFloat(configNode, "meleeDamage", floatValue)) voc->setMultiplier(MULTIPLIER_MELEE, floatValue); if(readXMLFloat(configNode, "distDamage", floatValue) || readXMLFloat(configNode, "distanceDamage", floatValue)) voc->setMultiplier(MULTIPLIER_DISTANCE, floatValue); if(readXMLFloat(configNode, "wandDamage", floatValue) || readXMLFloat(configNode, "rodDamage", floatValue)) voc->setMultiplier(MULTIPLIER_WAND, floatValue); if(readXMLFloat(configNode, "magDamage", floatValue) || readXMLFloat(configNode, "magicDamage", floatValue)) voc->setMultiplier(MULTIPLIER_MAGIC, floatValue); if(readXMLFloat(configNode, "magHealingDamage", floatValue) || readXMLFloat(configNode, "magicHealingDamage", floatValue)) voc->setMultiplier(MULTIPLIER_HEALING, floatValue); if(readXMLFloat(configNode, "defense", floatValue)) voc->setMultiplier(MULTIPLIER_DEFENSE, floatValue); if(readXMLFloat(configNode, "magDefense", floatValue) || readXMLFloat(configNode, "magicDefense", floatValue)) voc->setMultiplier(MULTIPLIER_MAGICDEFENSE, floatValue); if(readXMLFloat(configNode, "armor", floatValue)) voc->setMultiplier(MULTIPLIER_ARMOR, floatValue); } else if(!xmlStrcmp(configNode->name, (const xmlChar*)"absorb")) { if(readXMLInteger(configNode, "percentAll", intValue)) { for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) voc->increaseAbsorb((CombatType_t)i, intValue); } if(readXMLInteger(configNode, "percentElements", intValue)) { voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue); voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue); voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue); voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue); } if(readXMLInteger(configNode, "percentMagic", intValue)) { voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue); voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue); voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue); voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue); voc->increaseAbsorb(COMBAT_HOLYDAMAGE, intValue); voc->increaseAbsorb(COMBAT_DEATHDAMAGE, intValue); } if(readXMLInteger(configNode, "percentEnergy", intValue)) voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue); if(readXMLInteger(configNode, "percentFire", intValue)) voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue); if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue)) voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue); if(readXMLInteger(configNode, "percentIce", intValue)) voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue); if(readXMLInteger(configNode, "percentHoly", intValue)) voc->increaseAbsorb(COMBAT_HOLYDAMAGE, intValue); if(readXMLInteger(configNode, "percentDeath", intValue)) voc->increaseAbsorb(COMBAT_DEATHDAMAGE, intValue); if(readXMLInteger(configNode, "percentLifeDrain", intValue)) voc->increaseAbsorb(COMBAT_LIFEDRAIN, intValue); if(readXMLInteger(configNode, "percentManaDrain", intValue)) voc->increaseAbsorb(COMBAT_MANADRAIN, intValue); if(readXMLInteger(configNode, "percentDrown", intValue)) voc->increaseAbsorb(COMBAT_DROWNDAMAGE, intValue); if(readXMLInteger(configNode, "percentPhysical", intValue)) voc->increaseAbsorb(COMBAT_PHYSICALDAMAGE, intValue); if(readXMLInteger(configNode, "percentHealing", intValue)) voc->increaseAbsorb(COMBAT_HEALING, intValue); if(readXMLInteger(configNode, "percentUndefined", intValue)) voc->increaseAbsorb(COMBAT_UNDEFINEDDAMAGE, intValue); } else if(!xmlStrcmp(configNode->name, (const xmlChar*)"reflect")) { if(readXMLInteger(configNode, "percentAll", intValue)) { for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) voc->increaseReflect(REFLECT_PERCENT, (CombatType_t)i, intValue); } if(readXMLInteger(configNode, "percentElements", intValue)) { voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue); } if(readXMLInteger(configNode, "percentMagic", intValue)) { voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_HOLYDAMAGE, intValue); voc->increaseReflect(REFLECT_PERCENT, COMBAT_DEATHDAMAGE, intValue); } if(readXMLInteger(configNode, "percentEnergy", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue); if(readXMLInteger(configNode, "percentFire", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue); if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue); if(readXMLInteger(configNode, "percentIce", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue); if(readXMLInteger(configNode, "percentHoly", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_HOLYDAMAGE, intValue); if(readXMLInteger(configNode, "percentDeath", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_DEATHDAMAGE, intValue); if(readXMLInteger(configNode, "percentLifeDrain", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_LIFEDRAIN, intValue); if(readXMLInteger(configNode, "percentManaDrain", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_MANADRAIN, intValue); if(readXMLInteger(configNode, "percentDrown", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_DROWNDAMAGE, intValue); if(readXMLInteger(configNode, "percentPhysical", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_PHYSICALDAMAGE, intValue); if(readXMLInteger(configNode, "percentHealing", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_HEALING, intValue); if(readXMLInteger(configNode, "percentUndefined", intValue)) voc->increaseReflect(REFLECT_PERCENT, COMBAT_UNDEFINEDDAMAGE, intValue); if(readXMLInteger(configNode, "chanceAll", intValue)) { for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) voc->increaseReflect(REFLECT_CHANCE, (CombatType_t)i, intValue); } if(readXMLInteger(configNode, "chanceElements", intValue)) { voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue); } if(readXMLInteger(configNode, "chanceMagic", intValue)) { voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_HOLYDAMAGE, intValue); voc->increaseReflect(REFLECT_CHANCE, COMBAT_DEATHDAMAGE, intValue); } if(readXMLInteger(configNode, "chanceEnergy", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue); if(readXMLInteger(configNode, "chanceFire", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue); if(readXMLInteger(configNode, "chancePoison", intValue) || readXMLInteger(configNode, "chanceEarth", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue); if(readXMLInteger(configNode, "chanceIce", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue); if(readXMLInteger(configNode, "chanceHoly", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_HOLYDAMAGE, intValue); if(readXMLInteger(configNode, "chanceDeath", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_DEATHDAMAGE, intValue); if(readXMLInteger(configNode, "chanceLifeDrain", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_LIFEDRAIN, intValue); if(readXMLInteger(configNode, "chanceManaDrain", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_MANADRAIN, intValue); if(readXMLInteger(configNode, "chanceDrown", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_DROWNDAMAGE, intValue); if(readXMLInteger(configNode, "chancePhysical", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_PHYSICALDAMAGE, intValue); if(readXMLInteger(configNode, "chanceHealing", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_HEALING, intValue); if(readXMLInteger(configNode, "chanceUndefined", intValue)) voc->increaseReflect(REFLECT_CHANCE, COMBAT_UNDEFINEDDAMAGE, intValue); } } vocationsMap[voc->getId()] = voc; return true; } bool Vocations::loadFromXml() { xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML,"vocations.xml").c_str()); if(!doc) { std::clog << "[Warning - Vocations::loadFromXml] Cannot load vocations file." << std::endl; std::clog << getLastXMLError() << std::endl; return false; } xmlNodePtr p, root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"vocations")) { std::clog << "[Error - Vocations::loadFromXml] Malformed vocations file." << std::endl; xmlFreeDoc(doc); return false; } for(p = root->children; p; p = p->next) parseVocationNode(p); xmlFreeDoc(doc); return true; } Vocation* Vocations::getVocation(uint32_t vocId) { VocationsMap::iterator it = vocationsMap.find(vocId); if(it != vocationsMap.end()) return it->second; std::clog << "[Warning - Vocations::getVocation] Vocation " << vocId << " not found." << std::endl; return &Vocations::defVoc; } int32_t Vocations::getVocationId(const std::string& name) { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { if(!strcasecmp(it->second->getName().c_str(), name.c_str())) return it->first; } return -1; } int32_t Vocations::getPromotedVocation(uint32_t vocationId) { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { if(it->second->getFromVocation() == vocationId && it->first != vocationId) return it->first; } return -1; } Vocation::~Vocation() { cacheMana.clear(); for(int32_t i = SKILL_FIRST; i < SKILL_LAST; ++i) cacheSkill[i].clear(); } void Vocation::reset() { memset(absorb, 0, sizeof(absorb)); memset(reflect[REFLECT_PERCENT], 0, sizeof(reflect[REFLECT_PERCENT])); memset(reflect[REFLECT_CHANCE], 0, sizeof(reflect[REFLECT_CHANCE])); needPremium = false; attackable = true; lessLoss = fromVocation = 0; gain[GAIN_SOUL] = 100; gainTicks[GAIN_SOUL] = 120; baseSpeed = 220; attackSpeed = 1500; name = description = ""; gainAmount[GAIN_HEALTH] = gainAmount[GAIN_MANA] = gainAmount[GAIN_SOUL] = 1; gain[GAIN_HEALTH] = gain[GAIN_MANA] = capGain = 5; gainTicks[GAIN_HEALTH] = gainTicks[GAIN_MANA] = 6; skillBase[SKILL_SHIELD] = 100; skillBase[SKILL_DIST] = 30; skillBase[SKILL_FISH] = 20; for(int32_t i = SKILL_FIST; i < SKILL_DIST; i++) skillBase[i] = 50; skillMultipliers[SKILL_FIST] = 1.5f; skillMultipliers[SKILL_FISH] = 1.1f; skillMultipliers[SKILL__LEVEL] = 1.0f; for(int32_t i = SKILL_CLUB; i < SKILL_FISH; i++) skillMultipliers[i] = 2.0f; formulaMultipliers[MULTIPLIER_MANA] = 4.0f; for(int32_t i = MULTIPLIER_FIRST; i < MULTIPLIER_LAST; i++) formulaMultipliers[i] = 1.0f; } int16_t Vocation::getReflect(CombatType_t combat) const { if(reflect[REFLECT_CHANCE][combat] < random_range(0, 100)) return reflect[REFLECT_PERCENT][combat]; return 0; } uint64_t Vocation::getReqSkillTries(int32_t skill, int32_t level) { if(skill < SKILL_FIRST || skill > SKILL_LAST) return 0; cacheMap& skillMap = cacheSkill[skill]; cacheMap::iterator it = skillMap.find(level); if(it != cacheSkill[skill].end()) return it->second; skillMap[level] = (uint64_t)(skillBase[skill] * std::pow(skillMultipliers[skill], (level - 11))); return skillMap[level]; } uint64_t Vocation::getReqMana(uint32_t magLevel) { cacheMap::iterator it = cacheMana.find(magLevel); if(it != cacheMana.end()) return it->second; cacheMana[magLevel] = (uint64_t)(1600 * std::pow(formulaMultipliers[MULTIPLIER_MANA], (float)(magLevel - 1))); return cacheMana[magLevel]; }
Postado Agosto 17, 2015 10 anos @gabriel28, Tem como você mandar todas as suas source? assim eu compilo e mando para você! Se quiser sua dúvida tirada, mande PM com os links, e não com a dúvida (outros podem ter a mesma dúvida, e o fórum serve para ser usado). Tópicos: [FAQ] BBCODE [LIB] Constant [RME] Administrando bordas. [TALK] Broadcast Editável. [TALK] Sugest. [TALK] Checkpoint. [MOVE] Pântano pegajoso. [ACTION] Piggy Bank. (Cassino). [GLOBAL] Uptime Ad. [C0DE] Consertando 'Invalid Password' [PROGRAM] Quest Maker
Postado Agosto 18, 2015 10 anos Autor Ta ai o link: https://mega.nz/#!pgh2QKhB!zliN619BQgFSamkcWuP-ner207SR4YIboDAgnqnGckU Conseguiu Caronte? Editado Agosto 20, 2015 10 anos por gabriel28 (veja o histórico de edições)
Participe da conversa
Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.