Postado Março 19, 2019 6 anos 1 hora atrás, poko360 disse: @WooX pegou nao =c Eu entendi errado o que você queria, tinha entendido que era pra players não poder atacar summons. Testa agora. Spoiler //////////////////////////////////////////////////////////////////////// // 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 "combat.h" #include "tools.h" #include "game.h" #include "configmanager.h" #include "creature.h" #include "player.h" #include "weapons.h" extern Game g_game; extern Weapons* g_weapons; extern ConfigManager g_config; Combat::Combat() { params.valueCallback = NULL; params.tileCallback = NULL; params.targetCallback = NULL; area = NULL; formulaType = FORMULA_UNDEFINED; mina = minb = maxa = maxb = minl = maxl = minm = maxm = minc = maxc = 0; } Combat::~Combat() { for(std::list<const Condition*>::iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it) delete (*it); params.conditionList.clear(); delete area; delete params.valueCallback; delete params.tileCallback; delete params.targetCallback; } bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, int32_t& max) const { if(creature) { if(creature->getCombatValues(min, max)) return true; if(Player* player = creature->getPlayer()) { if(params.valueCallback) { params.valueCallback->getMinMaxValues(player, min, max, params.useCharges); return true; } min = max = 0; switch(formulaType) { case FORMULA_LEVELMAGIC: { min = (int32_t)((player->getLevel() / minl + player->getMagicLevel() * minm) * 1. * mina + minb); max = (int32_t)((player->getLevel() / maxl + player->getMagicLevel() * maxm) * 1. * maxa + maxb); if(minc && std::abs(min) < std::abs(minc)) min = minc; if(maxc && std::abs(max) < std::abs(maxc)) max = maxc; player->increaseCombatValues(min, max, params.useCharges, true); return true; } case FORMULA_SKILL: { Item* item = player->getWeapon(false); if(const Weapon* weapon = g_weapons->getWeapon(item)) { max = (int32_t)(weapon->getWeaponDamage(player, target, item, true) * maxa + maxb); if(params.useCharges && item->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES)) g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getCharges()) - 1)); } else max = (int32_t)maxb; min = (int32_t)minb; if(maxc && std::abs(max) < std::abs(maxc)) max = maxc; return true; } case FORMULA_VALUE: { min = (int32_t)minb; max = (int32_t)maxb; return true; } default: break; } return false; } } if(formulaType != FORMULA_VALUE) return false; min = (int32_t)mina; max = (int32_t)maxa; return true; } void Combat::getCombatArea(const Position& centerPos, const Position& targetPos, const CombatArea* area, std::list<Tile*>& list) { if(area) area->getList(centerPos, targetPos, list); else if(targetPos.z < MAP_MAX_LAYERS) { Tile* tile = g_game.getTile(targetPos); if(!tile) { tile = new StaticTile(targetPos.x, targetPos.y, targetPos.z); g_game.setTile(tile); } list.push_back(tile); } } CombatType_t Combat::ConditionToDamageType(ConditionType_t type) { switch(type) { case CONDITION_FIRE: return COMBAT_FIREDAMAGE; case CONDITION_ENERGY: return COMBAT_ENERGYDAMAGE; case CONDITION_POISON: return COMBAT_EARTHDAMAGE; case CONDITION_FREEZING: return COMBAT_ICEDAMAGE; case CONDITION_DAZZLED: return COMBAT_HOLYDAMAGE; case CONDITION_CURSED: return COMBAT_DEATHDAMAGE; case CONDITION_DROWN: return COMBAT_DROWNDAMAGE; case CONDITION_PHYSICAL: return COMBAT_PHYSICALDAMAGE; default: break; } return COMBAT_NONE; } ConditionType_t Combat::DamageToConditionType(CombatType_t type) { switch(type) { case COMBAT_FIREDAMAGE: return CONDITION_FIRE; case COMBAT_ENERGYDAMAGE: return CONDITION_ENERGY; case COMBAT_EARTHDAMAGE: return CONDITION_POISON; case COMBAT_ICEDAMAGE: return CONDITION_FREEZING; case COMBAT_HOLYDAMAGE: return CONDITION_DAZZLED; case COMBAT_DEATHDAMAGE: return CONDITION_CURSED; case COMBAT_PHYSICALDAMAGE: return CONDITION_PHYSICAL; default: break; } return CONDITION_NONE; } ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive) { if(tile->hasProperty(BLOCKPROJECTILE) || tile->floorChange() || tile->getTeleportItem()) return RET_NOTENOUGHROOM; if(caster) { bool success = true; CreatureEventList combatAreaEvents = const_cast<Creature*>(caster)->getCreatureEvents(CREATURE_EVENT_COMBAT_AREA); for(CreatureEventList::iterator it = combatAreaEvents.begin(); it != combatAreaEvents.end(); ++it) { if(!(*it)->executeCombatArea(const_cast<Creature*>(caster), const_cast<Tile*>(tile), isAggressive) && success) success = false; } if(!success) return RET_NOTPOSSIBLE; if(caster->getPosition().z < tile->getPosition().z) return RET_FIRSTGODOWNSTAIRS; if(caster->getPosition().z > tile->getPosition().z) return RET_FIRSTGOUPSTAIRS; if(!isAggressive) return RET_NOERROR; const Player* player = caster->getPlayer(); if(player && player->hasFlag(PlayerFlag_IgnoreProtectionZone)) return RET_NOERROR; } return isAggressive && tile->hasFlag(TILESTATE_PROTECTIONZONE) ? RET_ACTIONNOTPERMITTEDINPROTECTIONZONE : RET_NOERROR; } ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target) { if(!attacker) return RET_NOERROR; bool success = true; CreatureEventList combatEvents = const_cast<Creature*>(attacker)->getCreatureEvents(CREATURE_EVENT_COMBAT); for(CreatureEventList::iterator it = combatEvents.begin(); it != combatEvents.end(); ++it) { if(!(*it)->executeCombat(const_cast<Creature*>(attacker), const_cast<Creature*>(target)) && success) success = false; } if(!success) return RET_NOTPOSSIBLE; bool checkZones = false; if(const Player* targetPlayer = target->getPlayer()) { if(!targetPlayer->isAttackable()) return RET_YOUMAYNOTATTACKTHISPLAYER; const Player* attackerPlayer = NULL; if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster())) { checkZones = true; if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target) && !attackerPlayer->isEnemy(targetPlayer, true) ) || isProtected(const_cast<Player*>(attackerPlayer), const_cast<Player*>(targetPlayer)) || (g_config.getBool(ConfigManager::CANNOT_ATTACK_SAME_LOOKFEET) && attackerPlayer->getDefaultOutfit().lookFeet == targetPlayer->getDefaultOutfit().lookFeet) || !attackerPlayer->canSeeCreature(targetPlayer)) return RET_YOUMAYNOTATTACKTHISPLAYER; } } else if(target->getMonster()) { if(attacker->getMonster() && target->isPlayerSummon()) return RET_YOUMAYNOTATTACKTHISCREATURE; if(!target->isAttackable()) return RET_YOUMAYNOTATTACKTHISCREATURE; const Player* attackerPlayer = NULL; if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster())) { if(attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) return RET_YOUMAYNOTATTACKTHISCREATURE; if(target->isPlayerSummon()) { checkZones = true; if(g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target) && !attackerPlayer->isEnemy(target->getPlayerMaster(), true)) return RET_YOUMAYNOTATTACKTHISCREATURE; } } } return checkZones && (target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) || (attacker->getTile()->hasFlag(TILESTATE_OPTIONALZONE) && !target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) && !target->getTile()->hasFlag(TILESTATE_PROTECTIONZONE))) ? RET_ACTIONNOTPERMITTEDINANOPVPZONE : RET_NOERROR; } ReturnValue Combat::canTargetCreature(const Player* player, const Creature* target) { if(player == target) return RET_YOUMAYNOTATTACKTHISPLAYER; Player* tmpPlayer = const_cast<Player*>(player); CreatureEventList targetEvents = tmpPlayer->getCreatureEvents(CREATURE_EVENT_TARGET); bool deny = false; for(CreatureEventList::iterator it = targetEvents.begin(); it != targetEvents.end(); ++it) { if(!(*it)->executeTarget(tmpPlayer, const_cast<Creature*>(target))) deny = true; } if(deny) return RET_DONTSHOWMESSAGE; if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone)) { if(player->getZone() == ZONE_PROTECTION) return RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE; if(target->getZone() == ZONE_PROTECTION) return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE; if(target->getPlayer() || target->isPlayerSummon()) { if(player->getZone() == ZONE_OPTIONAL) return RET_ACTIONNOTPERMITTEDINANOPVPZONE; if(target->getZone() == ZONE_OPTIONAL) return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE; } } if(player->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable() || target->isPlayerSummon()) return target->getPlayer() ? RET_YOUMAYNOTATTACKTHISPLAYER : RET_YOUMAYNOTATTACKTHISCREATURE; if(target->getPlayer() && !Combat::isInPvpZone(player, target) && player->getSkullType(target->getPlayer()) == SKULL_NONE) { if(player->getSecureMode() == SECUREMODE_ON) return RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS; if(player->getSkull() == SKULL_BLACK) return RET_YOUMAYNOTATTACKTHISPLAYER; } return Combat::canDoCombat(player, target); } bool Combat::isInPvpZone(const Creature* attacker, const Creature* target) { return attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE; } bool Combat::isProtected(Player* attacker, Player* target) { if(attacker->hasFlag(PlayerFlag_CannotAttackPlayer) || !target->isAttackable()) return true; if(attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE && g_config.getBool(ConfigManager::PVP_TILE_IGNORE_PROTECTION)) return false; if(attacker->hasCustomFlag(PlayerCustomFlag_IsProtected) || target->hasCustomFlag(PlayerCustomFlag_IsProtected)) return true; uint32_t protectionLevel = g_config.getNumber(ConfigManager::PROTECTION_LEVEL); if(target->getLevel() < protectionLevel || attacker->getLevel() < protectionLevel) return true; if(!attacker->getVocation()->isAttackable() || !target->getVocation()->isAttackable()) return true; return attacker->checkLoginDelay(target->getID()); } void Combat::setPlayerCombatValues(formulaType_t _type, double _mina, double _minb, double _maxa, double _maxb, double _minl, double _maxl, double _minm, double _maxm, int32_t _minc, int32_t _maxc) { formulaType = _type; mina = _mina; minb = _minb; maxa = _maxa; maxb = _maxb; minl = _minl; maxl = _maxl; minm = _minm; maxm = _maxm; minc = _minc; maxc = _maxc; } bool Combat::setParam(CombatParam_t param, uint32_t value) { switch(param) { case COMBATPARAM_COMBATTYPE: params.combatType = (CombatType_t)value; return true; case COMBATPARAM_EFFECT: params.effects.impact = (MagicEffect_t)value; return true; case COMBATPARAM_DISTANCEEFFECT: params.effects.distance = (ShootEffect_t)value; return true; case COMBATPARAM_BLOCKEDBYARMOR: params.blockedByArmor = (value != 0); return true; case COMBATPARAM_BLOCKEDBYSHIELD: params.blockedByShield = (value != 0); return true; case COMBATPARAM_TARGETCASTERORTOPMOST: params.targetCasterOrTopMost = (value != 0); return true; case COMBATPARAM_TARGETPLAYERSORSUMMONS: params.targetPlayersOrSummons = (value != 0); return true; case COMBATPARAM_DIFFERENTAREADAMAGE: params.differentAreaDamage = (value != 0); return true; case COMBATPARAM_CREATEITEM: params.itemId = value; return true; case COMBATPARAM_AGGRESSIVE: params.isAggressive = (value != 0); return true; case COMBATPARAM_DISPEL: params.dispelType = (ConditionType_t)value; return true; case COMBATPARAM_USECHARGES: params.useCharges = (value != 0); return true; case COMBATPARAM_HITEFFECT: params.effects.hit = (MagicEffect_t)value; return true; case COMBATPARAM_HITCOLOR: params.effects.color = (Color_t)value; return true; default: break; } return false; } bool Combat::setCallback(CallBackParam_t key) { switch(key) { case CALLBACKPARAM_LEVELMAGICVALUE: { delete params.valueCallback; params.valueCallback = new ValueCallback(FORMULA_LEVELMAGIC); return true; } case CALLBACKPARAM_SKILLVALUE: { delete params.valueCallback; params.valueCallback = new ValueCallback(FORMULA_SKILL); return true; } case CALLBACKPARAM_TARGETTILECALLBACK: { delete params.tileCallback; params.tileCallback = new TileCallback(); break; } case CALLBACKPARAM_TARGETCREATURECALLBACK: { delete params.targetCallback; params.targetCallback = new TargetCallback(); break; } default: std::clog << "Combat::setCallback - Unknown callback type: " << (uint32_t)key << std::endl; break; } return false; } CallBack* Combat::getCallback(CallBackParam_t key) { switch(key) { case CALLBACKPARAM_LEVELMAGICVALUE: case CALLBACKPARAM_SKILLVALUE: return params.valueCallback; case CALLBACKPARAM_TARGETTILECALLBACK: return params.tileCallback; case CALLBACKPARAM_TARGETCREATURECALLBACK: return params.targetCallback; default: break; } return NULL; } bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data) { int32_t change = 0; if(Combat2Var* var = (Combat2Var*)data) { change = var->change; if(!change) change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL); } if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor)) return false; if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK) change = change / 2; if(!g_game.combatChangeHealth(params.combatType, caster, target, change, params.effects.hit, params.effects.color)) return false; CombatConditionFunc(caster, target, params, NULL); CombatDispelFunc(caster, target, params, NULL); return true; } bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, void* data) { int32_t change = 0; if(Combat2Var* var = (Combat2Var*)data) { change = var->change; if(!change) change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL); } if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK) change = change / 2; if(!g_game.combatChangeMana(caster, target, change)) return false; CombatConditionFunc(caster, target, params, NULL); CombatDispelFunc(caster, target, params, NULL); return true; } bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, void*) { if(params.conditionList.empty()) return false; bool result = true; for(std::list<const Condition*>::const_iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it) { if(caster != target && target->isImmune((*it)->getType())) continue; Condition* tmp = (*it)->clone(); if(caster) tmp->setParam(CONDITIONPARAM_OWNER, caster->getID()); //TODO: infight condition until all aggressive conditions has ended if(!target->addCombatCondition(tmp) && result) result = false; } return result; } bool Combat::CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, void*) { if(!target->hasCondition(params.dispelType)) return false; target->removeCondition(caster, params.dispelType); return true; } bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void*) { CombatConditionFunc(caster, target, params, NULL); CombatDispelFunc(caster, target, params, NULL); return true; } void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params) { if(params.itemId) { Player* player = NULL; if(caster) { if(caster->getPlayer()) player = caster->getPlayer(); else if(caster->isPlayerSummon()) player = caster->getPlayerMaster(); } uint32_t itemId = params.itemId; if(player) { bool pzLock = true; if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !tile->hasFlag( TILESTATE_HARDCOREZONE)) || tile->hasFlag(TILESTATE_OPTIONALZONE)) { switch(itemId) { case ITEM_FIREFIELD: itemId = ITEM_FIREFIELD_SAFE; break; case ITEM_POISONFIELD: itemId = ITEM_POISONFIELD_SAFE; break; case ITEM_ENERGYFIELD: itemId = ITEM_ENERGYFIELD_SAFE; break; case ITEM_MAGICWALL: itemId = ITEM_MAGICWALL_SAFE; break; case ITEM_WILDGROWTH: itemId = ITEM_WILDGROWTH_SAFE; break; default: break; } } else if(params.isAggressive && !Item::items[itemId].blockPathFind) pzLock = true; player->addInFightTicks(pzLock); } if(Item* item = Item::CreateItem(itemId)) { if(caster) item->setOwner(caster->getID()); if(g_game.internalAddItem(caster, tile, item) == RET_NOERROR) g_game.startDecay(item); else delete item; } } if(params.tileCallback) params.tileCallback->onTileCombat(caster, tile); if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost() || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS))) g_game.addMagicEffect(list, tile->getPosition(), params.effects.impact); } void Combat::postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params) { if(caster && params.effects.distance != SHOOT_EFFECT_NONE) addDistanceEffect(caster, caster->getPosition(), pos, params.effects.distance); } void Combat::addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, ShootEffect_t effect) { if(effect == SHOOT_EFFECT_WEAPONTYPE) { switch(caster->getWeaponType()) { case WEAPON_AXE: effect = SHOOT_EFFECT_WHIRLWINDAXE; break; case WEAPON_SWORD: effect = SHOOT_EFFECT_WHIRLWINDSWORD; break; case WEAPON_CLUB: effect = SHOOT_EFFECT_WHIRLWINDCLUB; break; case WEAPON_FIST: effect = SHOOT_EFFECT_LARGEROCK; break; default: effect = SHOOT_EFFECT_NONE; break; } } if(caster && effect != SHOOT_EFFECT_NONE) g_game.addDistanceEffect(fromPos, toPos, effect); } void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* area, const CombatParams& params, COMBATFUNC func, void* data) { std::list<Tile*> tileList; if(caster) getCombatArea(caster->getPosition(), pos, area, tileList); else getCombatArea(pos, pos, area, tileList); Combat2Var* var = (Combat2Var*)data; if(var && !params.differentAreaDamage) var->change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL); uint32_t maxX = 0, maxY = 0, diff; //calculate the max viewable range for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it) { diff = std::abs((*it)->getPosition().x - pos.x); if(diff > maxX) maxX = diff; diff = std::abs((*it)->getPosition().y - pos.y); if(diff > maxY) maxY = diff; } SpectatorVec list; g_game.getSpectators(list, pos, false, true, maxX + Map::maxViewportX, maxX + Map::maxViewportX, maxY + Map::maxViewportY, maxY + Map::maxViewportY); Tile* tile = NULL; for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it) { if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR) continue; bool skip = true; if(CreatureVector* creatures = tile->getCreatures()) { for(CreatureVector::iterator cit = creatures->begin(), cend = creatures->end(); skip && cit != cend; ++cit) { if(params.targetPlayersOrSummons && !(*cit)->getPlayer() && !(*cit)->isPlayerSummon()) continue; if(params.targetCasterOrTopMost) { if(caster && caster->getTile() == tile) { if((*cit) == caster) skip = false; } else if((*cit) == tile->getTopCreature()) skip = false; if(skip) continue; } if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR)) { func(caster, (*cit), params, (void*)var); if(params.targetCallback) params.targetCallback->onTargetCombat(caster, (*cit)); } } } combatTileEffects(list, caster, tile, params); } postCombatEffects(caster, pos, params); } void Combat::doCombat(Creature* caster, Creature* target) const { //target combat callback function if(params.combatType != COMBAT_NONE) { int32_t minChange = 0, maxChange = 0; getMinMaxValues(caster, target, minChange, maxChange); if(params.combatType != COMBAT_MANADRAIN) doCombatHealth(caster, target, minChange, maxChange, params); else doCombatMana(caster, target, minChange, maxChange, params); } else doCombatDefault(caster, target, params); } void Combat::doCombat(Creature* caster, const Position& pos) const { //area combat callback function if(params.combatType != COMBAT_NONE) { int32_t minChange = 0, maxChange = 0; getMinMaxValues(caster, NULL, minChange, maxChange); if(params.combatType != COMBAT_MANADRAIN) doCombatHealth(caster, pos, area, minChange, maxChange, params); else doCombatMana(caster, pos, area, minChange, maxChange, params); } else CombatFunc(caster, pos, area, params, CombatNullFunc, NULL); } void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params) { if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) return; Combat2Var var; var.minChange = minChange; var.maxChange = maxChange; CombatHealthFunc(caster, target, params, (void*)&var); if(params.targetCallback) params.targetCallback->onTargetCombat(caster, target); if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost() || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS))) g_game.addMagicEffect(target->getPosition(), params.effects.impact); if(caster && params.effects.distance != SHOOT_EFFECT_NONE) addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance); } void Combat::doCombatHealth(Creature* caster, const Position& pos, const CombatArea* area, int32_t minChange, int32_t maxChange, const CombatParams& params) { Combat2Var var; var.minChange = minChange; var.maxChange = maxChange; CombatFunc(caster, pos, area, params, CombatHealthFunc, (void*)&var); } void Combat::doCombatMana(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params) { if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) return; Combat2Var var; var.minChange = minChange; var.maxChange = maxChange; CombatManaFunc(caster, target, params, (void*)&var); if(params.targetCallback) params.targetCallback->onTargetCombat(caster, target); if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost() || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS))) g_game.addMagicEffect(target->getPosition(), params.effects.impact); if(caster && params.effects.distance != SHOOT_EFFECT_NONE) addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance); } void Combat::doCombatMana(Creature* caster, const Position& pos, const CombatArea* area, int32_t minChange, int32_t maxChange, const CombatParams& params) { Combat2Var var; var.minChange = minChange; var.maxChange = maxChange; CombatFunc(caster, pos, area, params, CombatManaFunc, (void*)&var); } void Combat::doCombatCondition(Creature* caster, const Position& pos, const CombatArea* area, const CombatParams& params) { CombatFunc(caster, pos, area, params, CombatConditionFunc, NULL); } void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params) { if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) return; CombatConditionFunc(caster, target, params, NULL); if(params.targetCallback) params.targetCallback->onTargetCombat(caster, target); if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost() || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS))) g_game.addMagicEffect(target->getPosition(), params.effects.impact); if(caster && params.effects.distance != SHOOT_EFFECT_NONE) addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance); } void Combat::doCombatDispel(Creature* caster, const Position& pos, const CombatArea* area, const CombatParams& params) { CombatFunc(caster, pos, area, params, CombatDispelFunc, NULL); } void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params) { if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) return; CombatDispelFunc(caster, target, params, NULL); if(params.targetCallback) params.targetCallback->onTargetCombat(caster, target); if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost() || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS))) g_game.addMagicEffect(target->getPosition(), params.effects.impact); if(caster && params.effects.distance != SHOOT_EFFECT_NONE) addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance); } void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params) { if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) return; const SpectatorVec& list = g_game.getSpectators(target->getTile()->getPosition()); CombatNullFunc(caster, target, params, NULL); combatTileEffects(list, caster, target->getTile(), params); if(params.targetCallback) params.targetCallback->onTargetCombat(caster, target); if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost() || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS))) g_game.addMagicEffect(target->getPosition(), params.effects.impact); if(caster && params.effects.distance != SHOOT_EFFECT_NONE) addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance); } //********************************************************** void ValueCallback::getMinMaxValues(Player* player, int32_t& min, int32_t& max, bool useCharges) const { //"onGetPlayerMinMaxValues"(cid, ...) if(!m_interface->reserveEnv()) { std::clog << "[Error - ValueCallback::getMinMaxValues] Callstack overflow." << std::endl; return; } ScriptEnviroment* env = m_interface->getEnv(); if(!env->setCallbackId(m_scriptId, m_interface)) return; m_interface->pushFunction(m_scriptId); lua_State* L = m_interface->getState(); lua_pushnumber(L, env->addThing(player)); int32_t parameters = 1; switch(type) { case FORMULA_LEVELMAGIC: { //"onGetPlayerMinMaxValues"(cid, level, magLevel) lua_pushnumber(L, player->getLevel()); lua_pushnumber(L, player->getMagicLevel()); parameters += 2; break; } case FORMULA_SKILL: { //"onGetPlayerMinMaxValues"(cid, level, skill, attack, factor) lua_pushnumber(L, player->getLevel()); if(Item* weapon = player->getWeapon(false)) { lua_pushnumber(L, player->getWeaponSkill(weapon)); if(useCharges && weapon->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES)) g_game.transformItem(weapon, weapon->getID(), std::max(0, weapon->getCharges() - 1)); lua_pushnumber(L, weapon->getAttack() + weapon->getExtraAttack()); } else { lua_pushnumber(L, player->getSkill(SKILL_FIST, SKILL_LEVEL)); lua_pushnumber(L, g_config.getNumber(ConfigManager::FIST_BASE_ATTACK)); } lua_pushnumber(L, player->getAttackFactor()); parameters += 4; break; } default: { std::clog << "[Warning - ValueCallback::getMinMaxValues] Unknown callback type" << std::endl; return; } } int32_t params = lua_gettop(L); if(!lua_pcall(L, parameters, 2, 0)) { min = LuaInterface::popNumber(L); max = LuaInterface::popNumber(L); player->increaseCombatValues(min, max, useCharges, type != FORMULA_SKILL); } else LuaInterface::error(NULL, std::string(LuaInterface::popString(L))); if((lua_gettop(L) + parameters + 1) != params) LuaInterface::error(__FUNCTION__, "Stack size changed!"); env->resetCallback(); m_interface->releaseEnv(); } //********************************************************** void TileCallback::onTileCombat(Creature* creature, Tile* tile) const { //"onTileCombat"(cid, pos) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(!env->setCallbackId(m_scriptId, m_interface)) return; m_interface->pushFunction(m_scriptId); lua_State* L = m_interface->getState(); lua_pushnumber(L, creature ? env->addThing(creature) : 0); m_interface->pushPosition(L, tile->getPosition(), 0); m_interface->callFunction(2); env->resetCallback(); m_interface->releaseEnv(); } else std::clog << "[Error - TileCallback::onTileCombat] Call stack overflow." << std::endl; } //********************************************************** void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const { //"onTargetCombat"(cid, target) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(!env->setCallbackId(m_scriptId, m_interface)) return; uint32_t cid = 0; if(creature) cid = env->addThing(creature); m_interface->pushFunction(m_scriptId); lua_State* L = m_interface->getState(); lua_pushnumber(L, cid); lua_pushnumber(L, env->addThing(target)); int32_t size = lua_gettop(L); if(lua_pcall(L, 2, 0 /*nReturnValues*/, 0) != 0) LuaInterface::error(NULL, std::string(LuaInterface::popString(L))); if((lua_gettop(L) + 2 /*nParams*/ + 1) != size) LuaInterface::error(__FUNCTION__, "Stack size changed!"); env->resetCallback(); m_interface->releaseEnv(); } else { std::clog << "[Error - TargetCallback::onTargetCombat] Call stack overflow." << std::endl; return; } } //********************************************************** void CombatArea::clear() { for(CombatAreas::iterator it = areas.begin(); it != areas.end(); ++it) delete it->second; areas.clear(); } CombatArea::CombatArea(const CombatArea& rhs) { hasExtArea = rhs.hasExtArea; for(CombatAreas::const_iterator it = rhs.areas.begin(); it != rhs.areas.end(); ++it) areas[it->first] = new MatrixArea(*it->second); } bool CombatArea::getList(const Position& centerPos, const Position& targetPos, std::list<Tile*>& list) const { Tile* tile = g_game.getTile(targetPos); const MatrixArea* area = getArea(centerPos, targetPos); if(!area) return false; uint16_t tmpX = targetPos.x, tmpY = targetPos.y, centerY = 0, centerX = 0; size_t cols = area->getCols(), rows = area->getRows(); area->getCenter(centerY, centerX); tmpX -= centerX; tmpY -= centerY; for(size_t y = 0; y < rows; ++y) { for(size_t x = 0; x < cols; ++x) { if(area->getValue(y, x) != 0) { if(targetPos.z < MAP_MAX_LAYERS && g_game.isSightClear(targetPos, Position(tmpX, tmpY, targetPos.z), true)) { if(!(tile = g_game.getTile(tmpX, tmpY, targetPos.z))) { tile = new StaticTile(tmpX, tmpY, targetPos.z); g_game.setTile(tile); } list.push_back(tile); } } tmpX++; } tmpX -= cols; tmpY++; } return true; } void CombatArea::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const { uint16_t centerY, centerX; input->getCenter(centerY, centerX); if(op == MATRIXOPERATION_COPY) { for(uint32_t y = 0; y < input->getRows(); ++y) { for(uint32_t x = 0; x < input->getCols(); ++x) (*output)[y][x] = (*input)[y][x]; } output->setCenter(centerY, centerX); } else if(op == MATRIXOPERATION_MIRROR) { for(uint32_t y = 0; y < input->getRows(); ++y) { int32_t rx = 0; for(int32_t x = input->getCols() - 1; x >= 0; --x) (*output)[y][rx++] = (*input)[y][x]; } output->setCenter(centerY, (input->getRows() - 1) - centerX); } else if(op == MATRIXOPERATION_FLIP) { for(uint32_t x = 0; x < input->getCols(); ++x) { int32_t ry = 0; for(int32_t y = input->getRows() - 1; y >= 0; --y) (*output)[ry++][x] = (*input)[y][x]; } output->setCenter((input->getCols() - 1) - centerY, centerX); } else //rotation { uint16_t centerX, centerY; input->getCenter(centerY, centerX); int32_t rotateCenterX = (output->getCols() / 2) - 1, rotateCenterY = (output->getRows() / 2) - 1, angle = 0; switch(op) { case MATRIXOPERATION_ROTATE90: angle = 90; break; case MATRIXOPERATION_ROTATE180: angle = 180; break; case MATRIXOPERATION_ROTATE270: angle = 270; break; default: angle = 0; break; } double angleRad = 3.1416 * angle / 180.0; float a = std::cos(angleRad), b = -std::sin(angleRad); float c = std::sin(angleRad), d = std::cos(angleRad); for(int32_t x = 0; x < (long)input->getCols(); ++x) { for(int32_t y = 0; y < (long)input->getRows(); ++y) { //calculate new coordinates using rotation center int32_t newX = x - centerX, newY = y - centerY, rotatedX = round(newX * a + newY * b), rotatedY = round(newX * c + newY * d); //write in the output matrix using rotated coordinates (*output)[rotatedY + rotateCenterY][rotatedX + rotateCenterX] = (*input)[y][x]; } } output->setCenter(rotateCenterY, rotateCenterX); } } MatrixArea* CombatArea::createArea(const std::list<uint32_t>& list, uint32_t rows) { uint32_t cols = list.size() / rows; MatrixArea* area = new MatrixArea(rows, cols); uint16_t x = 0, y = 0; for(std::list<uint32_t>::const_iterator it = list.begin(); it != list.end(); ++it) { if(*it == 1 || *it == 3) area->setValue(y, x, true); if(*it == 2 || *it == 3) area->setCenter(y, x); ++x; if(cols != x) continue; x = 0; ++y; } return area; } void CombatArea::setupArea(const std::list<uint32_t>& list, uint32_t rows) { //NORTH MatrixArea* area = createArea(list, rows); areas[NORTH] = area; uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2; //SOUTH MatrixArea* southArea = new MatrixArea(maxOutput, maxOutput); copyArea(area, southArea, MATRIXOPERATION_ROTATE180); areas[SOUTH] = southArea; //EAST MatrixArea* eastArea = new MatrixArea(maxOutput, maxOutput); copyArea(area, eastArea, MATRIXOPERATION_ROTATE90); areas[EAST] = eastArea; //WEST MatrixArea* westArea = new MatrixArea(maxOutput, maxOutput); copyArea(area, westArea, MATRIXOPERATION_ROTATE270); areas[WEST] = westArea; } void CombatArea::setupArea(int32_t length, int32_t spread) { std::list<uint32_t> list; uint32_t rows = length; int32_t cols = 1; if(spread != 0) cols = ((length - length % spread) / spread) * 2 + 1; int32_t colSpread = cols; for(uint32_t y = 1; y <= rows; ++y) { int32_t mincol = cols - colSpread + 1, maxcol = cols - (cols - colSpread); for(int32_t x = 1; x <= cols; ++x) { if(y == rows && x == ((cols - cols % 2) / 2) + 1) list.push_back(3); else if(x >= mincol && x <= maxcol) list.push_back(1); else list.push_back(0); } if(spread > 0 && y % spread == 0) --colSpread; } setupArea(list, rows); } void CombatArea::setupArea(int32_t radius) { int32_t area[13][13] = { {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0}, {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0}, {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0}, {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0}, {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0}, {8, 7, 6, 5, 4, 2, 1, 2, 4, 5, 6, 7, 8}, {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0}, {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0}, {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0}, {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0}, {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0} }; std::list<uint32_t> list; for(int32_t y = 0; y < 13; ++y) { for(int32_t x = 0; x < 13; ++x) { if(area[y][x] == 1) list.push_back(3); else if(area[y][x] > 0 && area[y][x] <= radius) list.push_back(1); else list.push_back(0); } } setupArea(list, 13); } void CombatArea::setupExtArea(const std::list<uint32_t>& list, uint32_t rows) { if(list.empty()) return; //NORTH-WEST MatrixArea* area = createArea(list, rows); areas[NORTHWEST] = area; uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2; //NORTH-EAST MatrixArea* neArea = new MatrixArea(maxOutput, maxOutput); copyArea(area, neArea, MATRIXOPERATION_MIRROR); areas[NORTHEAST] = neArea; //SOUTH-WEST MatrixArea* swArea = new MatrixArea(maxOutput, maxOutput); copyArea(area, swArea, MATRIXOPERATION_FLIP); areas[SOUTHWEST] = swArea; //SOUTH-EAST MatrixArea* seArea = new MatrixArea(maxOutput, maxOutput); copyArea(swArea, seArea, MATRIXOPERATION_MIRROR); areas[SOUTHEAST] = seArea; hasExtArea = true; } //********************************************************** bool MagicField::isBlocking(const Creature* creature) const { if(!isUnstepable()) return Item::isBlocking(creature); if(!creature || !creature->getPlayer()) return true; uint32_t ownerId = getOwner(); if(!ownerId) return false; if(Creature* owner = g_game.getCreatureByID(ownerId)) return creature->getPlayer()->getGuildEmblem(owner) != EMBLEM_NONE; return false; } void MagicField::onStepInField(Creature* creature, bool purposeful/* = true*/) { if(!purposeful) return; const ItemType& it = items[id]; if(!it.condition) return; uint32_t ownerId = getOwner(); Condition* condition = it.condition->clone(); if(ownerId && !getTile()->hasFlag(TILESTATE_HARDCOREZONE)) { if(Creature* owner = g_game.getCreatureByID(ownerId)) { bool harmful = true; if((g_game.getWorldType() == WORLDTYPE_OPTIONAL || getTile()->hasFlag(TILESTATE_OPTIONALZONE)) && (owner->getPlayer() || owner->isPlayerSummon())) harmful = false; else if(Player* targetPlayer = creature->getPlayer()) { if(owner->getPlayer() && Combat::isProtected(owner->getPlayer(), targetPlayer)) harmful = false; } if(!harmful || (OTSYS_TIME() - createTime) <= (uint32_t)g_config.getNumber( ConfigManager::FIELD_OWNERSHIP) || creature->hasBeenAttacked(ownerId)) condition->setParam(CONDITIONPARAM_OWNER, ownerId); } } creature->addCondition(condition); } Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito. Aristóteles
Postado Março 19, 2019 6 anos Autor @WooX ainda n funfou os monstro ainda tao pegando target no summon
Postado Março 19, 2019 6 anos 38 minutos atrás, poko360 disse: @WooX ainda n funfou os monstro ainda tao pegando target no summon Esqueci dessa parte, pra isso vou precisar que me envie também monster.cpp Mas o dano parou de pegar nos summons né? Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito. Aristóteles
Postado Março 19, 2019 6 anos Autor o dano pode deixar normal (pra pegar no summon), tipo eu so queria que o monstro nao peguasse target nele, mas o dano pode deixar normal monster.cpp Spoiler //////////////////////////////////////////////////////////////////////// // 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 "monster.h" #include "spawn.h" #include "monsters.h" #include "spells.h" #include "combat.h" #include "configmanager.h" #include "game.h" extern Game g_game; extern ConfigManager g_config; extern Monsters g_monsters; AutoList<Monster>Monster::autoList; #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t Monster::monsterCount = 0; #endif Monster* Monster::createMonster(MonsterType* mType) { return new Monster(mType); } Monster* Monster::createMonster(const std::string& name) { MonsterType* mType = g_monsters.getMonsterType(name); if(!mType) return NULL; return createMonster(mType); } Monster::Monster(MonsterType* _mType): Creature() { isIdle = true; isMasterInRange = false; teleportToMaster = false; mType = _mType; spawn = NULL; raid = NULL; defaultOutfit = mType->outfit; currentOutfit = mType->outfit; double multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_HEALTH); health = (int32_t)(mType->health * multiplier); healthMax = (int32_t)(mType->healthMax * multiplier); baseSpeed = mType->baseSpeed; internalLight.level = mType->lightLevel; internalLight.color = mType->lightColor; setSkull(mType->skull); setShield(mType->partyShield); setEmblem(mType->guildEmblem); hideName = mType->hideName, hideHealth = mType->hideHealth; minCombatValue = 0; maxCombatValue = 0; targetTicks = 0; targetChangeTicks = 0; targetChangeCooldown = 0; attackTicks = 0; defenseTicks = 0; yellTicks = 0; extraMeleeAttack = false; targetPlayers = true; //MS // register creature events for(StringVec::iterator it = mType->scriptList.begin(); it != mType->scriptList.end(); ++it) { if(!registerCreatureEvent(*it)) std::clog << "[Warning - Monster::Monster] Unknown event name - " << *it << std::endl; } #ifdef __ENABLE_SERVER_DIAGNOSTIC__ monsterCount++; #endif } Monster::~Monster() { clearTargetList(); clearFriendList(); namelist.clear(); //MS #ifdef __ENABLE_SERVER_DIAGNOSTIC__ monsterCount--; #endif if(raid) { raid->unRef(); raid = NULL; } } void Monster::setAttackPlayer(bool state) //MS { targetPlayers = state; if(attackedCreature && attackedCreature->getPlayer() && !targetPlayers) { setFollowCreature(NULL); setAttackedCreature(NULL); onAttackedCreatureDisappear(true); } updateTargetList(); } void Monster::addAttackList(std::string name) //MS { namelist.push_back(name); updateTargetList(); } void Monster::onAttackedCreature(Creature* target) { Creature::onAttackedCreature(target); if(isSummon()) master->onSummonAttackedCreature(this, target); } void Monster::onAttackedCreatureDisappear(bool) { #ifdef __DEBUG__ std::clog << "Attacked creature disappeared." << std::endl; #endif attackTicks = 0; extraMeleeAttack = true; } void Monster::onAttackedCreatureDrain(Creature* target, int32_t points) { Creature::onAttackedCreatureDrain(target, points); if(isSummon()) master->onSummonAttackedCreatureDrain(this, target, points); } void Monster::onCreatureAppear(const Creature* creature) { Creature::onCreatureAppear(creature); if(creature == this) { //We just spawned lets look around to see who is there. if(isSummon()) isMasterInRange = canSee(master->getPosition()); updateTargetList(); updateIdleStatus(); } else onCreatureEnter(const_cast<Creature*>(creature)); } void Monster::onCreatureDisappear(const Creature* creature, bool isLogout) { Creature::onCreatureDisappear(creature, isLogout); if(creature == this) { if(spawn) spawn->startEvent(); setIdle(true); } else onCreatureLeave(const_cast<Creature*>(creature)); } void Monster::onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); if(creature == this) { if(isSummon()) isMasterInRange = canSee(master->getPosition()); updateTargetList(); updateIdleStatus(); } else { bool canSeeNewPos = canSee(newPos), canSeeOldPos = canSee(oldPos); if(canSeeNewPos && !canSeeOldPos) onCreatureEnter(const_cast<Creature*>(creature)); else if(!canSeeNewPos && canSeeOldPos) onCreatureLeave(const_cast<Creature*>(creature)); if(isSummon() && master == creature && canSeeNewPos) //Turn the summon on again isMasterInRange = true; updateIdleStatus(); if(!followCreature && !isSummon() && isOpponent(creature)) //we have no target lets try pick this one selectTarget(const_cast<Creature*>(creature)); } } void Monster::updateTargetList() { CreatureList::iterator it; for(it = friendList.begin(); it != friendList.end();) { if((*it)->getHealth() <= 0 || !isFriend(*it) || !canSee((*it)->getPosition())) //MS { (*it)->unRef(); it = friendList.erase(it); } else ++it; } for(it = targetList.begin(); it != targetList.end();) { if((*it)->getHealth() <= 0 || !isOpponent(*it) || !canSee((*it)->getPosition())) //MS { (*it)->unRef(); it = targetList.erase(it); } else ++it; } const SpectatorVec& list = g_game.getSpectators(getPosition()); for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { if((*it) != this && canSee((*it)->getPosition())) onCreatureFound(*it); } } void Monster::clearTargetList() { for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it) (*it)->unRef(); targetList.clear(); } void Monster::clearFriendList() { for(CreatureList::iterator it = friendList.begin(); it != friendList.end(); ++it) (*it)->unRef(); friendList.clear(); } void Monster::onCreatureFound(Creature* creature, bool pushFront /*= false*/) { if(isFriend(creature)) { assert(creature != this); if(std::find(friendList.begin(), friendList.end(), creature) == friendList.end()) { creature->addRef(); friendList.push_back(creature); } } if(isOpponent(creature)) { assert(creature != this); if(std::find(targetList.begin(), targetList.end(), creature) == targetList.end()) { creature->addRef(); if(pushFront) targetList.push_front(creature); else targetList.push_back(creature); } } updateIdleStatus(); } void Monster::onCreatureEnter(Creature* creature) { if(master == creature) //Turn the summon on again { isMasterInRange = true; updateIdleStatus(); } onCreatureFound(creature, true); } bool Monster::isFriend(const Creature* creature) { if(creature->getMonster() == this) return true; NameList::iterator it = std::find(namelist.begin(), namelist.end(), creature->getName()); //MS if(it != namelist.end()) return false; if(creature->getPlayer() && !targetPlayers) return true; if(!isSummon() || !master->getPlayer()) return creature->getMonster() && !creature->isSummon(); const Player* tmpPlayer = NULL; if(creature->getPlayer()) tmpPlayer = creature->getPlayer(); else if(creature->getPlayerMaster()) tmpPlayer = creature->getPlayerMaster(); const Player* masterPlayer = master->getPlayer(); return tmpPlayer && (tmpPlayer == masterPlayer || masterPlayer->isPartner(tmpPlayer) || masterPlayer->isAlly(tmpPlayer)); } bool Monster::isOpponent(const Creature* creature) { if(creature->getMonster() == this) return false; NameList::iterator it = std::find(namelist.begin(), namelist.end(), creature->getName()); if(it != namelist.end()) return true; if(creature->getPlayer() && !targetPlayers) return false; return (isSummon() && master->getPlayer() && creature != master) || ((creature->getPlayer() && !creature->getPlayer()->hasFlag(PlayerFlag_IgnoredByMonsters)) || (creature->getMaster() && creature->getPlayerMaster())); } bool Monster::doTeleportToMaster() { const Position& tmp = getPosition(); if(g_game.internalTeleport(this, g_game.getClosestFreeTile(this, master->getPosition(), true), true) != RET_NOERROR) return false; g_game.addMagicEffect(tmp, MAGIC_EFFECT_POFF); g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_TELEPORT); return true; } void Monster::onCreatureLeave(Creature* creature) { #ifdef __DEBUG__ std::clog << "onCreatureLeave - " << creature->getName() << std::endl; #endif if(isSummon() && master == creature) { if(!g_config.getBool(ConfigManager::TELEPORT_SUMMONS) && (!master->getPlayer() || !g_config.getBool(ConfigManager::TELEPORT_PLAYER_SUMMONS))) { //Turn the monster off until its master comes back isMasterInRange = false; updateIdleStatus(); } else if(!doTeleportToMaster()) teleportToMaster = true; } //update friendList if(isFriend(creature)) { CreatureList::iterator it = std::find(friendList.begin(), friendList.end(), creature); if(it != friendList.end()) { (*it)->unRef(); friendList.erase(it); } #ifdef __DEBUG__ else std::clog << "Monster: " << creature->getName() << " not found in the friendList." << std::endl; #endif } //update targetList if(isOpponent(creature)) { CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); if(it != targetList.end()) { (*it)->unRef(); targetList.erase(it); if(targetList.empty()) updateIdleStatus(); } #ifdef __DEBUG__ else std::clog << "Player: " << creature->getName() << " not found in the targetList." << std::endl; #endif } } bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAULT*/) { #ifdef __DEBUG__ std::clog << "Searching target... " << std::endl; #endif std::list<Creature*> resultList; const Position& myPos = getPosition(); for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it) { if(followCreature != (*it) && isTarget(*it) && (searchType == TARGETSEARCH_RANDOM || canUseAttack(myPos, *it))) resultList.push_back(*it); } switch(searchType) { case TARGETSEARCH_NEAREST: { Creature* target = NULL; int32_t range = -1; for(CreatureList::iterator it = resultList.begin(); it != resultList.end(); ++it) { int32_t tmp = std::max(std::abs(myPos.x - (*it)->getPosition().x), std::abs(myPos.y - (*it)->getPosition().y)); if(range >= 0 && tmp >= range) continue; target = *it; range = tmp; } if(target && selectTarget(target)) return target; break; } default: { if(!resultList.empty()) { CreatureList::iterator it = resultList.begin(); std::advance(it, random_range(0, resultList.size() - 1)); #ifdef __DEBUG__ std::clog << "Selecting target " << (*it)->getName() << std::endl; #endif return selectTarget(*it); } if(searchType == TARGETSEARCH_ATTACKRANGE) return false; break; } } //lets just pick the first target in the list for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it) { if(followCreature == (*it) || !selectTarget(*it)) continue; #ifdef __DEBUG__ /*std::clog << "Selecting target " << (*it)->getName() << std::endl;*/ // Caused a strange crash, will look at it later #endif return true; } return false; } void Monster::onFollowCreatureComplete(const Creature* creature) { if(!creature) return; CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); if(it != targetList.end()) { Creature* target = (*it); targetList.erase(it); if(hasFollowPath) //push target we have found a path to the front targetList.push_front(target); else if(!isSummon()) //push target we have not found a path to the back targetList.push_back(target); else //Since we removed the creature from the targetList (and not put it back) we have to release it too target->unRef(); } } BlockType_t Monster::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool /*reflect = true*/) { BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor); if(!damage) return blockType; int32_t elementMod = 0; ElementMap::iterator it = mType->elementMap.find(combatType); if(it != mType->elementMap.end()) elementMod = it->second; if(!elementMod) return blockType; damage = (int32_t)std::ceil(damage * ((float)(100 - elementMod) / 100)); if(damage > 0) return blockType; damage = 0; blockType = BLOCK_DEFENSE; return blockType; } bool Monster::isTarget(Creature* creature) { return (!creature->isRemoved() && creature->isAttackable() && creature->getZone() != ZONE_PROTECTION && canSeeCreature(creature) && creature->getPosition().z == getPosition().z); } bool Monster::selectTarget(Creature* creature) { #ifdef __DEBUG__ std::clog << "Selecting target... " << std::endl; #endif if(!isTarget(creature)) return false; CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); if(it == targetList.end()) { //Target not found in our target list. #ifdef __DEBUG__ std::clog << "Target not found in targetList." << std::endl; #endif return false; } if((isHostile() || isSummon()) && setAttackedCreature(creature) && !isSummon()) Dispatcher::getInstance().addTask(createTask( boost::bind(&Game::checkCreatureAttack, &g_game, getID()))); return setFollowCreature(creature, true); } void Monster::setIdle(bool _idle) { if(isRemoved() || getHealth() <= 0) return; isIdle = _idle; if(isIdle) { onIdleStatus(); clearTargetList(); clearFriendList(); g_game.removeCreatureCheck(this); } else g_game.addCreatureCheck(this); } void Monster::updateIdleStatus() { bool idle = false; if(conditions.empty()) { if(isSummon()) { if((!isMasterInRange && !teleportToMaster) || (master->getMonster() && master->getMonster()->getIdleStatus())) idle = true; } else if(targetList.empty() && targetPlayers) idle = true; } setIdle(idle); } void Monster::onAddCondition(ConditionType_t type, bool hadCondition) { Creature::onAddCondition(type, hadCondition); //the walkCache need to be updated if the monster becomes "resistent" to the damage, see Tile::__queryAdd() if(type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) updateMapCache(); updateIdleStatus(); } void Monster::onEndCondition(ConditionType_t type) { Creature::onEndCondition(type); //the walkCache need to be updated if the monster loose the "resistent" to the damage, see Tile::__queryAdd() if(type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) updateMapCache(); updateIdleStatus(); } void Monster::onThink(uint32_t interval) { Creature::onThink(interval); if(despawn()) { g_game.removeCreature(this, true); setIdle(true); return; } updateIdleStatus(); if(isIdle) return; if(teleportToMaster && doTeleportToMaster()) teleportToMaster = false; addEventWalk(); if(isSummon()) { if(!attackedCreature) { if(master && master->getAttackedCreature()) //This happens if the monster is summoned during combat selectTarget(master->getAttackedCreature()); else if(master != followCreature) //Our master has not ordered us to attack anything, lets follow him around instead. setFollowCreature(master); } else if(attackedCreature == this) setFollowCreature(NULL); else if(followCreature != attackedCreature) //This happens just after a master orders an attack, so lets follow it aswell. setFollowCreature(attackedCreature); } else if(!targetList.empty()) { if(!followCreature || !hasFollowPath) searchTarget(); else if(isFleeing() && attackedCreature && !canUseAttack(getPosition(), attackedCreature)) searchTarget(TARGETSEARCH_ATTACKRANGE); } onThinkTarget(interval); onThinkYell(interval); onThinkDefense(interval); } void Monster::doAttacking(uint32_t interval) { if(!attackedCreature || (isSummon() && attackedCreature == this)) return; bool updateLook = true; resetTicks = interval; attackTicks += interval; const Position& myPos = getPosition(); for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it) { if(!attackedCreature || attackedCreature->isRemoved()) break; const Position& targetPos = attackedCreature->getPosition(); if(it->isMelee && isFleeing()) continue; bool inRange = false; if(canUseSpell(myPos, targetPos, *it, interval, inRange)) { if(it->chance >= (uint32_t)random_range(1, 100)) { if(updateLook) { updateLookDirection(); updateLook = false; } double multiplier; if(maxCombatValue > 0) //defense multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_DEFENSE); else //attack multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_ATTACK); minCombatValue = (int32_t)(it->minCombatValue * multiplier); maxCombatValue = (int32_t)(it->maxCombatValue * multiplier); it->spell->castSpell(this, attackedCreature); if(it->isMelee) extraMeleeAttack = false; #ifdef __DEBUG__ static uint64_t prevTicks = OTSYS_TIME(); std::clog << "doAttacking ticks: " << OTSYS_TIME() - prevTicks << std::endl; prevTicks = OTSYS_TIME(); #endif } } if(!inRange && it->isMelee) //melee swing out of reach extraMeleeAttack = true; } if(updateLook) updateLookDirection(); if(resetTicks) attackTicks = 0; } bool Monster::canUseAttack(const Position& pos, const Creature* target) const { if(!isHostile()) return true; const Position& targetPos = target->getPosition(); for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it) { if((*it).range != 0 && std::max(std::abs(pos.x - targetPos.x), std::abs(pos.y - targetPos.y)) <= (int32_t)(*it).range) return g_game.isSightClear(pos, targetPos, true); } return false; } bool Monster::canUseSpell(const Position& pos, const Position& targetPos, const spellBlock_t& sb, uint32_t interval, bool& inRange) { inRange = true; if(!sb.isMelee || !extraMeleeAttack) { if(sb.speed > attackTicks) { resetTicks = false; return false; } if(attackTicks % sb.speed >= interval) //already used this spell for this round return false; } if(!sb.range || std::max(std::abs(pos.x - targetPos.x), std::abs(pos.y - targetPos.y)) <= (int32_t)sb.range) return true; inRange = false; return false; } void Monster::onThinkTarget(uint32_t interval) { if(isSummon() || mType->changeTargetSpeed <= 0) return; bool canChangeTarget = true; if(targetChangeCooldown > 0) { targetChangeCooldown -= interval; if(targetChangeCooldown <= 0) { targetChangeCooldown = 0; targetChangeTicks = (uint32_t)mType->changeTargetSpeed; } else canChangeTarget = false; } if(!canChangeTarget) return; targetChangeTicks += interval; if(targetChangeTicks < (uint32_t)mType->changeTargetSpeed) return; targetChangeTicks = 0; targetChangeCooldown = (uint32_t)mType->changeTargetSpeed; if(mType->changeTargetChance < random_range(1, 100)) return; if(mType->targetDistance <= 1) searchTarget(TARGETSEARCH_RANDOM); else searchTarget(TARGETSEARCH_NEAREST); } void Monster::onThinkDefense(uint32_t interval) { resetTicks = true; defenseTicks += interval; for(SpellList::iterator it = mType->spellDefenseList.begin(); it != mType->spellDefenseList.end(); ++it) { if(it->speed > defenseTicks) { if(resetTicks) resetTicks = false; continue; } if(defenseTicks % it->speed >= interval) //already used this spell for this round continue; if((it->chance >= (uint32_t)random_range(1, 100))) { minCombatValue = it->minCombatValue; maxCombatValue = it->maxCombatValue; it->spell->castSpell(this, this); } } if(!isSummon()) { if(mType->maxSummons < 0 || (int32_t)summons.size() < mType->maxSummons) { for(SummonList::iterator it = mType->summonList.begin(); it != mType->summonList.end(); ++it) { if((int32_t)summons.size() >= mType->maxSummons) break; if(it->interval > defenseTicks) { if(resetTicks) resetTicks = false; continue; } if(defenseTicks % it->interval >= interval) continue; uint32_t typeCount = 0; for(CreatureList::iterator cit = summons.begin(); cit != summons.end(); ++cit) { if(!(*cit)->isRemoved() && (*cit)->getMonster() && (*cit)->getMonster()->getName() == it->name) typeCount++; } if(typeCount >= it->amount) continue; if((it->chance >= (uint32_t)random_range(1, 100))) { if(Monster* summon = Monster::createMonster(it->name)) { addSummon(summon); if(g_game.placeCreature(summon, getPosition())) g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_BLUE); else removeSummon(summon); } } } } } if(resetTicks) defenseTicks = 0; } void Monster::onThinkYell(uint32_t interval) { if(mType->yellSpeedTicks <= 0) return; yellTicks += interval; if(yellTicks < mType->yellSpeedTicks) return; yellTicks = 0; if(mType->voiceVector.empty() || (mType->yellChance < (uint32_t)random_range(1, 100))) return; const voiceBlock_t& vb = mType->voiceVector[random_range(0, mType->voiceVector.size() - 1)]; if(vb.yellText) g_game.internalCreatureSay(this, SPEAK_MONSTER_YELL, vb.text, false); else g_game.internalCreatureSay(this, SPEAK_MONSTER_SAY, vb.text, false); } bool Monster::pushItem(Item* item, int32_t radius) { const Position& centerPos = item->getPosition(); PairVector pairVector; pairVector.push_back(PositionPair(-1, -1)); pairVector.push_back(PositionPair(-1, 0)); pairVector.push_back(PositionPair(-1, 1)); pairVector.push_back(PositionPair(0, -1)); pairVector.push_back(PositionPair(0, 1)); pairVector.push_back(PositionPair(1, -1)); pairVector.push_back(PositionPair(1, 0)); pairVector.push_back(PositionPair(1, 1)); std::random_shuffle(pairVector.begin(), pairVector.end()); Position tryPos; for(int32_t n = 1; n <= radius; ++n) { for(PairVector::iterator it = pairVector.begin(); it != pairVector.end(); ++it) { int32_t dx = it->first * n, dy = it->second * n; tryPos = centerPos; tryPos.x = tryPos.x + dx; tryPos.y = tryPos.y + dy; Tile* tile = g_game.getTile(tryPos); if(tile && g_game.canThrowObjectTo(centerPos, tryPos) && g_game.internalMoveItem(this, item->getParent(), tile, INDEX_WHEREEVER, item, item->getItemCount(), NULL) == RET_NOERROR) return true; } } return false; } void Monster::pushItems(Tile* tile) { TileItemVector* items = tile->getItemList(); if(!items) return; //We cannot use iterators here since we can push the item to another tile //which will invalidate the iterator. //start from the end to minimize the amount of traffic int32_t moveCount = 0, removeCount = 0, downItemsSize = tile->getDownItemCount(); Item* item = NULL; for(int32_t i = downItemsSize - 1; i >= 0; --i) { assert(i >= 0 && i < downItemsSize); if((item = items->at(i)) && item->hasProperty(MOVEABLE) && (item->hasProperty(BLOCKPATH) || item->hasProperty(BLOCKSOLID))) { if(moveCount < 20 && pushItem(item, 1)) moveCount++; else if(g_game.internalRemoveItem(this, item) == RET_NOERROR) ++removeCount; } } if(removeCount > 0) g_game.addMagicEffect(tile->getPosition(), MAGIC_EFFECT_POFF); } bool Monster::pushCreature(Creature* creature) { DirVector dirVector; dirVector.push_back(NORTH); dirVector.push_back(SOUTH); dirVector.push_back(WEST); dirVector.push_back(EAST); std::random_shuffle(dirVector.begin(), dirVector.end()); Position monsterPos = creature->getPosition(); Tile* tile = NULL; for(DirVector::iterator it = dirVector.begin(); it != dirVector.end(); ++it) { if((tile = g_game.getTile(Spells::getCasterPosition(creature, (*it)))) && !tile->hasProperty( BLOCKPATH) && g_game.internalMoveCreature(creature, (*it)) == RET_NOERROR) return true; } return false; } void Monster::pushCreatures(Tile* tile) { CreatureVector* creatures = tile->getCreatures(); if(!creatures) return; bool effect = false; Monster* monster = NULL; for(uint32_t i = 0; i < creatures->size();) { if((monster = creatures->at(i)->getMonster()) && monster->isPushable()) { if(pushCreature(monster)) continue; monster->setDropLoot(LOOT_DROP_NONE); monster->changeHealth(-monster->getHealth()); if(!effect) effect = true; } ++i; } if(effect) g_game.addMagicEffect(tile->getPosition(), MAGIC_EFFECT_BLOCKHIT); } bool Monster::getNextStep(Direction& dir, uint32_t& flags) { if(isIdle || getHealth() <= 0) { //we dont have anyone watching might aswell stop walking eventWalk = 0; return false; } bool result = false; if((!followCreature || !hasFollowPath) && !isSummon()) { if(followCreature || getTimeSinceLastMove() > 1000) //choose a random direction result = getRandomStep(getPosition(), dir); } else if(isSummon() || followCreature) { result = Creature::getNextStep(dir, flags); if(!result) { //target dancing if(attackedCreature && attackedCreature == followCreature) { if(isFleeing()) result = getDanceStep(getPosition(), dir, false, false); else if(mType->staticAttackChance < (uint32_t)random_range(1, 100)) result = getDanceStep(getPosition(), dir); } } else flags |= FLAG_PATHFINDING; } if(result && (canPushItems() || canPushCreatures())) { if(Tile* tile = g_game.getTile(Spells::getCasterPosition(this, dir))) { if(canPushItems()) pushItems(tile); if(canPushCreatures()) pushCreatures(tile); } #ifdef __DEBUG__ else std::clog << "[Warning - Monster::getNextStep] no tile found." << std::endl; #endif } return result; } bool Monster::getRandomStep(const Position& creaturePos, Direction& dir) { DirVector dirVector; dirVector.push_back(NORTH); dirVector.push_back(SOUTH); dirVector.push_back(WEST); dirVector.push_back(EAST); std::random_shuffle(dirVector.begin(), dirVector.end()); for(DirVector::iterator it = dirVector.begin(); it != dirVector.end(); ++it) { if(!canWalkTo(creaturePos, *it)) continue; dir = *it; return true; } return false; } bool Monster::getDanceStep(const Position& creaturePos, Direction& dir, bool keepAttack /*= true*/, bool keepDistance /*= true*/) { assert(attackedCreature); bool canDoAttackNow = canUseAttack(creaturePos, attackedCreature); const Position& centerPos = attackedCreature->getPosition(); uint32_t tmpDist, centerToDist = std::max(std::abs(creaturePos.x - centerPos.x), std::abs(creaturePos.y - centerPos.y)); DirVector dirVector; if(!keepDistance || creaturePos.y - centerPos.y >= 0) { tmpDist = std::max(std::abs((creaturePos.x) - centerPos.x), std::abs((creaturePos.y - 1) - centerPos.y)); if(tmpDist == centerToDist && canWalkTo(creaturePos, NORTH)) { bool result = true; if(keepAttack) result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x, creaturePos.y - 1, creaturePos.z), attackedCreature)); if(result) dirVector.push_back(NORTH); } } if(!keepDistance || creaturePos.y - centerPos.y <= 0) { tmpDist = std::max(std::abs((creaturePos.x) - centerPos.x), std::abs((creaturePos.y + 1) - centerPos.y)); if(tmpDist == centerToDist && canWalkTo(creaturePos, SOUTH)) { bool result = true; if(keepAttack) result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x, creaturePos.y + 1, creaturePos.z), attackedCreature)); if(result) dirVector.push_back(SOUTH); } } if(!keepDistance || creaturePos.x - centerPos.x >= 0) { tmpDist = std::max(std::abs((creaturePos.x + 1) - centerPos.x), std::abs((creaturePos.y) - centerPos.y)); if(tmpDist == centerToDist && canWalkTo(creaturePos, EAST)) { bool result = true; if(keepAttack) result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x + 1, creaturePos.y, creaturePos.z), attackedCreature)); if(result) dirVector.push_back(EAST); } } if(!keepDistance || creaturePos.x - centerPos.x <= 0) { tmpDist = std::max(std::abs((creaturePos.x - 1) - centerPos.x), std::abs((creaturePos.y) - centerPos.y)); if(tmpDist == centerToDist && canWalkTo(creaturePos, WEST)) { bool result = true; if(keepAttack) result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x - 1, creaturePos.y, creaturePos.z), attackedCreature)); if(result) dirVector.push_back(WEST); } } if(dirVector.empty()) return false; std::random_shuffle(dirVector.begin(), dirVector.end()); dir = dirVector[random_range(0, dirVector.size() - 1)]; return true; } bool Monster::isInSpawnRange(const Position& toPos) { return masterRadius == -1 || !inDespawnRange(toPos); } bool Monster::canWalkTo(Position pos, Direction dir) { if(getNoMove()) return false; switch(dir) { case NORTH: pos.y += -1; break; case WEST: pos.x += -1; break; case EAST: pos.x += 1; break; case SOUTH: pos.y += 1; break; default: break; } if(!isInSpawnRange(pos) || !getWalkCache(pos)) return false; Tile* tile = g_game.getTile(pos); if(!tile || g_game.isSwimmingPool(NULL, getTile(), false) != g_game.isSwimmingPool(NULL, tile, false)) // prevent monsters entering/exiting to swimming pool return false; return !tile->getTopVisibleCreature(this) && tile->__queryAdd( 0, this, 1, FLAG_PATHFINDING) == RET_NOERROR; } bool Monster::onDeath() { if(!Creature::onDeath()) return false; destroySummons(); clearTargetList(); clearFriendList(); setAttackedCreature(NULL); onIdleStatus(); if(raid) { raid->unRef(); raid = NULL; } g_game.removeCreature(this, false); return true; } Item* Monster::createCorpse(DeathList deathList) { Item* corpse = Creature::createCorpse(deathList); if(!corpse) return NULL; if(mType->corpseUnique) corpse->setUniqueId(mType->corpseUnique); if(mType->corpseAction) corpse->setActionId(mType->corpseAction, false); if(deathList[0].isNameKill()) return corpse; Creature* _owner = deathList[0].getKillerCreature(); if(deathList.size() > 1 && deathList[1].getDamage() > deathList[0].getDamage()) _owner = deathList[1].getKillerCreature(); if(!_owner) return corpse; Player* owner = NULL; if(_owner->getPlayer()) owner = _owner->getPlayer(); else if(_owner->isPlayerSummon()) owner = _owner->getPlayerMaster(); if(!owner) return corpse; uint64_t stamina = g_config.getNumber(ConfigManager::STAMINA_DESTROY_LOOT); if(stamina && owner->getStamina() <= (stamina * STAMINA_MULTIPLIER)) lootDrop = LOOT_DROP_NONE; corpse->setCorpseOwner(owner->getGUID()); return corpse; } bool Monster::inDespawnRange(const Position& pos) { if(!spawn || mType->isLureable) return false; int32_t radius = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRADIUS); if(!radius) return false; if(!Spawns::getInstance()->isInZone(masterPosition, radius, pos)) return true; int32_t range = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRANGE); if(!range) return false; return std::abs(pos.z - masterPosition.z) > range; } bool Monster::despawn() { return inDespawnRange(getPosition()); } bool Monster::getCombatValues(int32_t& min, int32_t& max) { if(!minCombatValue && !maxCombatValue) return false; double multiplier; if(maxCombatValue > 0) //defense multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_DEFENSE); else //attack multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_ATTACK); min = (int32_t)(minCombatValue * multiplier); max = (int32_t)(maxCombatValue * multiplier); return true; } void Monster::updateLookDirection() { Direction newDir = getDirection(); if(attackedCreature) { const Position& pos = getPosition(); const Position& attackedCreaturePos = attackedCreature->getPosition(); int32_t dx = attackedCreaturePos.x - pos.x, dy = attackedCreaturePos.y - pos.y; if(std::abs(dx) > std::abs(dy)) { //look EAST/WEST if(dx < 0) newDir = WEST; else newDir = EAST; } else if(std::abs(dx) < std::abs(dy)) { //look NORTH/SOUTH if(dy < 0) newDir = NORTH; else newDir = SOUTH; } else if(dx < 0 && dy < 0) { if(getDirection() == SOUTH) newDir = WEST; else if(getDirection() == EAST) newDir = NORTH; } else if(dx < 0 && dy > 0) { if(getDirection() == NORTH) newDir = WEST; else if(getDirection() == EAST) newDir = SOUTH; } else if(dx > 0 && dy < 0) { if(getDirection() == SOUTH) newDir = EAST; else if(getDirection() == WEST) newDir = NORTH; } else if(getDirection() == NORTH) newDir = EAST; else if(getDirection() == WEST) newDir = SOUTH; } g_game.internalCreatureTurn(this, newDir); } void Monster::dropLoot(Container* corpse) { if(corpse && lootDrop == LOOT_DROP_FULL) mType->dropLoot(corpse); } bool Monster::isImmune(CombatType_t type) const { ElementMap::const_iterator it = mType->elementMap.find(type); if(it == mType->elementMap.end()) return Creature::isImmune(type); return it->second >= 100; } void Monster::setNormalCreatureLight() { internalLight.level = mType->lightLevel; internalLight.color = mType->lightColor; } void Monster::drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage) { Creature::drainHealth(attacker, combatType, damage); if(isInvisible()) removeCondition(CONDITION_INVISIBLE); } void Monster::changeHealth(int32_t healthChange) { //In case a player with ignore flag set attacks the monster setIdle(false); Creature::changeHealth(healthChange); } bool Monster::challengeCreature(Creature* creature) { if(isSummon() || !selectTarget(creature)) return false; targetChangeCooldown = 8000; targetChangeTicks = 0; return true; } bool Monster::convinceCreature(Creature* creature) { Player* player = creature->getPlayer(); if(player && !player->hasFlag(PlayerFlag_CanConvinceAll) && !mType->isConvinceable) return false; Creature* oldMaster = NULL; if(isSummon()) oldMaster = master; if(oldMaster) { if(oldMaster->getPlayer() || oldMaster == creature) return false; oldMaster->removeSummon(this); } setFollowCreature(NULL); setAttackedCreature(NULL); destroySummons(); creature->addSummon(this); updateTargetList(); updateIdleStatus(); //Notify surrounding about the change SpectatorVec list; g_game.getSpectators(list, getPosition(), false, true); g_game.getSpectators(list, creature->getPosition(), true, true); isMasterInRange = true; for(SpectatorVec::iterator it = list.begin(); it != list.end(); ++it) (*it)->onCreatureConvinced(creature, this); if(spawn) { spawn->removeMonster(this); spawn = NULL; masterRadius = -1; } if(raid) { raid->unRef(); raid = NULL; } return true; } void Monster::onCreatureConvinced(const Creature* convincer, const Creature* creature) { if(convincer == this || (!isFriend(creature) && !isOpponent(creature))) return; updateTargetList(); updateIdleStatus(); } void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const { Creature::getPathSearchParams(creature, fpp); fpp.minTargetDist = 1; fpp.maxTargetDist = mType->targetDistance; if(isSummon()) { if(master == creature) { fpp.maxTargetDist = 2; fpp.fullPathSearch = true; } else if(mType->targetDistance <= 1) fpp.fullPathSearch = true; else fpp.fullPathSearch = !canUseAttack(getPosition(), creature); } else if(isFleeing()) { //Distance should be higher than the client view range (Map::maxClientViewportX/Map::maxClientViewportY) fpp.maxTargetDist = Map::maxViewportX; fpp.clearSight = fpp.fullPathSearch = false; fpp.keepDistance = true; } else if(mType->targetDistance <= 1) fpp.fullPathSearch = true; else fpp.fullPathSearch = !canUseAttack(getPosition(), creature); } Editado Março 19, 2019 6 anos por poko360 (veja o histórico de edições)
Postado Março 21, 2019 6 anos Solução Em 19/03/2019 em 17:36, poko360 disse: o dano pode deixar normal (pra pegar no summon), tipo eu so queria que o monstro nao peguasse target nele, mas o dano pode deixar normal Volta seu combat.cpp para o original pro dano voltar a pegar. Pra não pegar target nos summons faz essa alteração. Em monster.cpp onde está: bool Monster::isTarget(Creature* creature) { return (!creature->isRemoved() && creature->isAttackable() && creature->getZone() != ZONE_PROTECTION && canSeeCreature(creature) && creature->getPosition().z == getPosition().z); } Muda para: bool Monster::isTarget(Creature* creature) { return (!creature->isRemoved() && creature->isAttackable() && !creature->isPlayerSummon() && creature->getZone() != ZONE_PROTECTION && canSeeCreature(creature) && creature->getPosition().z == getPosition().z); } Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito. Aristóteles
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.