diff --git a/src/creature.cpp b/src/creature.cpp index 8228063e48..09cf1047e8 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -26,7 +26,7 @@ Creature::Creature() { onIdleStatus(); } Creature::~Creature() { for (Creature* summon : summons) { - summon->setAttackedCreature(nullptr); + summon->removeAttackedCreature(); summon->removeMaster(); } @@ -338,12 +338,12 @@ void Creature::onRemoveCreature(Creature* creature, bool) { onCreatureDisappear( void Creature::onCreatureDisappear(const Creature* creature, bool isLogout) { if (attackedCreature == creature) { - setAttackedCreature(nullptr); + removeAttackedCreature(); onAttackedCreatureDisappear(isLogout); } if (followCreature == creature) { - setFollowCreature(nullptr); + removeFollowCreature(); onFollowCreatureDisappear(isLogout); } } @@ -721,26 +721,42 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3 return blockType; } -bool Creature::setAttackedCreature(Creature* creature) +void Creature::setAttackedCreature(Creature* creature) { - if (creature) { - const Position& creaturePos = creature->getPosition(); - if (creaturePos.z != getPosition().z || !canSee(creaturePos)) { - attackedCreature = nullptr; - return false; - } + if (isAttackingCreature(creature)) { + return; + } - attackedCreature = creature; - onAttackedCreature(attackedCreature); - attackedCreature->onAttacked(); - } else { - attackedCreature = nullptr; + if (!canAttackCreature(creature)) { + removeAttackedCreature(); + return; } + attackedCreature = creature; + onAttackedCreature(attackedCreature); + attackedCreature->onAttacked(); + for (Creature* summon : summons) { summon->setAttackedCreature(creature); } - return true; +} + +void Creature::removeAttackedCreature() +{ + attackedCreature = nullptr; + + for (Creature* summon : summons) { + summon->removeAttackedCreature(); + } +} + +bool Creature::canAttackCreature(Creature* creature) +{ + const auto& creaturePos = creature->getPosition(); + if (creaturePos.z != getPosition().z) { + return false; + } + return canSee(creaturePos); } void Creature::getPathSearchParams(const Creature*, FindPathParams& fpp) const @@ -752,37 +768,50 @@ void Creature::getPathSearchParams(const Creature*, FindPathParams& fpp) const fpp.maxTargetDist = 1; } -bool Creature::setFollowCreature(Creature* creature) +void Creature::setFollowCreature(Creature* creature) { - if (creature) { - if (followCreature == creature) { - return true; - } + if (isFollowingCreature(creature)) { + return; + } - const Position& creaturePos = creature->getPosition(); - if (creaturePos.z != getPosition().z || !canSee(creaturePos)) { - followCreature = nullptr; - return false; - } + if (!canFollowCreature(creature)) { + removeFollowCreature(); + return; + } - if (!listWalkDir.empty()) { - listWalkDir.clear(); - onWalkAborted(); - } + followCreature = creature; + onFollowCreature(creature); +} - hasFollowPath = false; - forceUpdateFollowPath = false; - followCreature = creature; - isUpdatingPath = true; - } else { - isUpdatingPath = false; - followCreature = nullptr; +void Creature::removeFollowCreature() +{ + followCreature = nullptr; + onUnfollowCreature(); +} + +bool Creature::canFollowCreature(Creature* creature) +{ + const auto& creaturePos = creature->getPosition(); + if (creaturePos.z != getPosition().z) { + return false; } + return canSee(creaturePos); +} - onFollowCreature(creature); - return true; +void Creature::onFollowCreature(const Creature*) +{ + if (!listWalkDir.empty()) { + listWalkDir.clear(); + onWalkAborted(); + } + + hasFollowPath = false; + forceUpdateFollowPath = false; + isUpdatingPath = true; } +void Creature::onUnfollowCreature() { isUpdatingPath = false; } + double Creature::getDamageRatio(Creature* attacker) const { uint32_t totalDamage = 0; diff --git a/src/creature.h b/src/creature.h index 305087587d..3e3a8c2117 100644 --- a/src/creature.h +++ b/src/creature.h @@ -190,14 +190,22 @@ class Creature : virtual public Thing // follow functions Creature* getFollowCreature() const { return followCreature; } - virtual bool setFollowCreature(Creature* creature); + virtual void setFollowCreature(Creature* creature); + virtual void removeFollowCreature(); + virtual bool canFollowCreature(Creature* creature); + virtual bool isFollowingCreature(Creature* creature) { return followCreature == creature; } // follow events - virtual void onFollowCreature(const Creature*) {} + virtual void onFollowCreature(const Creature*); + virtual void onUnfollowCreature(); // combat functions Creature* getAttackedCreature() { return attackedCreature; } - virtual bool setAttackedCreature(Creature* creature); + virtual void setAttackedCreature(Creature* creature); + virtual void removeAttackedCreature(); + virtual bool canAttackCreature(Creature* creature); + virtual bool isAttackingCreature(Creature* creature) { return attackedCreature == creature; } + virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, bool checkDefense = false, bool checkArmor = false, bool field = false, bool ignoreResistances = false); diff --git a/src/game.cpp b/src/game.cpp index 6cf2a74eae..41ae78ddf3 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3231,14 +3231,14 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) } if (player->getAttackedCreature() && creatureId == 0) { - player->setAttackedCreature(nullptr); + player->removeAttackedCreature(); player->sendCancelTarget(); return; } Creature* attackCreature = getCreatureByID(creatureId); if (!attackCreature) { - player->setAttackedCreature(nullptr); + player->removeAttackedCreature(); player->sendCancelTarget(); return; } @@ -3247,11 +3247,12 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); player->sendCancelTarget(); - player->setAttackedCreature(nullptr); + player->removeAttackedCreature(); return; } player->setAttackedCreature(attackCreature); + g_dispatcher.addTask([this, id = player->getID()]() { updateCreatureWalk(id); }); } @@ -3262,9 +3263,15 @@ void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) return; } - player->setAttackedCreature(nullptr); + player->removeAttackedCreature(); + + if (Creature* followCreature = getCreatureByID(creatureId)) { + player->setFollowCreature(followCreature); + } else { + player->removeFollowCreature(); + } + g_dispatcher.addTask([this, id = player->getID()]() { updateCreatureWalk(id); }); - player->setFollowCreature(getCreatureByID(creatureId)); } void Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, bool chaseMode, bool secureMode) diff --git a/src/luascript.cpp b/src/luascript.cpp index af0f799623..269a2699f0 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -8115,11 +8115,20 @@ int LuaScriptInterface::luaCreatureGetTarget(lua_State* L) int LuaScriptInterface::luaCreatureSetTarget(lua_State* L) { // creature:setTarget(target) - Creature* creature = tfs::lua::getUserdata(L, 1); - if (creature) { - tfs::lua::pushBoolean(L, creature->setAttackedCreature(tfs::lua::getCreature(L, 2))); - } else { + auto creature = tfs::lua::getUserdata(L, 1); + if (!creature) { lua_pushnil(L); + + return 1; + } + + auto target = tfs::lua::getCreature(L, 2); + if (target) { + creature->setAttackedCreature(target); + tfs::lua::pushBoolean(L, creature->canAttackCreature(target)); + } else { + creature->removeAttackedCreature(); + tfs::lua::pushBoolean(L, true); } return 1; } @@ -8146,11 +8155,19 @@ int LuaScriptInterface::luaCreatureGetFollowCreature(lua_State* L) int LuaScriptInterface::luaCreatureSetFollowCreature(lua_State* L) { // creature:setFollowCreature(followedCreature) - Creature* creature = tfs::lua::getUserdata(L, 1); - if (creature) { - tfs::lua::pushBoolean(L, creature->setFollowCreature(tfs::lua::getCreature(L, 2))); - } else { + auto creature = tfs::lua::getUserdata(L, 1); + if (!creature) { lua_pushnil(L); + return 1; + } + + auto followedCreature = tfs::lua::getCreature(L, 2); + if (followedCreature) { + creature->setFollowCreature(followedCreature); + tfs::lua::pushBoolean(L, creature->canFollowCreature(followedCreature)); + } else { + creature->removeFollowCreature(); + tfs::lua::pushBoolean(L, true); } return 1; } diff --git a/src/monster.cpp b/src/monster.cpp index c737cf8436..983b0088bd 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -679,11 +679,22 @@ bool Monster::selectTarget(Creature* creature) } if (isHostile() || isSummon()) { - if (setAttackedCreature(creature) && !isSummon()) { - g_dispatcher.addTask([id = getID()]() { g_game.checkCreatureAttack(id); }); + if (canAttackCreature(creature)) { + setAttackedCreature(creature); + + if (isHostile()) { + g_dispatcher.addTask([id = getID()]() { g_game.checkCreatureAttack(id); }); + } + } else { + removeAttackedCreature(); } } - return setFollowCreature(creature); + + if (isFollowingCreature(creature) || canFollowCreature(creature)) { + setFollowCreature(creature); + return true; + } + return false; } void Monster::setIdle(bool idle) @@ -787,7 +798,7 @@ void Monster::onThink(uint32_t interval) setFollowCreature(getMaster()); } } else if (attackedCreature == this) { - setFollowCreature(nullptr); + removeFollowCreature(); } else if (followCreature != attackedCreature) { // This happens just after a master orders an attack, so lets follow it as well. setFollowCreature(attackedCreature); @@ -1852,7 +1863,7 @@ bool Monster::canWalkTo(Position pos, Direction direction) const void Monster::death(Creature*) { - setAttackedCreature(nullptr); + removeAttackedCreature(); for (Creature* summon : summons) { summon->changeHealth(-summon->getHealth()); diff --git a/src/npc.cpp b/src/npc.cpp index 0fb0f4a2e7..add4658be9 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -897,7 +897,14 @@ int NpcScriptInterface::luaActionFollow(lua_State* L) return 1; } - tfs::lua::pushBoolean(L, npc->setFollowCreature(tfs::lua::getPlayer(L, 1))); + auto followedPlayer = tfs::lua::getPlayer(L, 1); + if (followedPlayer) { + npc->setFollowCreature(followedPlayer); + tfs::lua::pushBoolean(L, npc->canFollowCreature(followedPlayer)); + } else { + npc->removeFollowCreature(); + tfs::lua::pushBoolean(L, true); + } return 1; } diff --git a/src/player.cpp b/src/player.cpp index 86b6f4a032..23fb219cf1 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -881,7 +881,7 @@ void Player::sendPing() int64_t noPongTime = timeNow - lastPong; if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) { - setAttackedCreature(nullptr); + removeAttackedCreature(); } int32_t noPongKickTime = vocation->getNoPongKickTime(); @@ -1192,7 +1192,7 @@ void Player::onChangeZone(ZoneType_t zone) { if (zone == ZONE_PROTECTION) { if (attackedCreature && !hasFlag(PlayerFlag_IgnoreProtectionZone)) { - setAttackedCreature(nullptr); + removeAttackedCreature(); onAttackedCreatureDisappear(false); } @@ -1216,13 +1216,13 @@ void Player::onAttackedCreatureChangeZone(ZoneType_t zone) { if (zone == ZONE_PROTECTION) { if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) { - setAttackedCreature(nullptr); + removeAttackedCreature(); onAttackedCreatureDisappear(false); } } else if (zone == ZONE_NOPVP) { if (attackedCreature->getPlayer()) { if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) { - setAttackedCreature(nullptr); + removeAttackedCreature(); onAttackedCreatureDisappear(false); } } @@ -1230,7 +1230,7 @@ void Player::onAttackedCreatureChangeZone(ZoneType_t zone) // attackedCreature can leave a pvp zone if not pzlocked if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) { if (attackedCreature->getPlayer()) { - setAttackedCreature(nullptr); + removeAttackedCreature(); onAttackedCreatureDisappear(false); } } @@ -1251,7 +1251,7 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) lastLogout = time(nullptr); if (eventWalk != 0) { - setFollowCreature(nullptr); + removeFollowCreature(); } if (tradePartner) { @@ -3315,40 +3315,57 @@ void Player::internalAddThing(uint32_t index, Thing* thing) } } -bool Player::setFollowCreature(Creature* creature) +void Player::setFollowCreature(Creature* creature) { - if (!Creature::setFollowCreature(creature)) { - setFollowCreature(nullptr); - setAttackedCreature(nullptr); + if (isFollowingCreature(creature)) { + return; + } - sendCancelMessage(RETURNVALUE_THEREISNOWAY); + if (!canFollowCreature(creature)) { + removeFollowCreature(); + removeAttackedCreature(); sendCancelTarget(); + sendCancelMessage(RETURNVALUE_THEREISNOWAY); stopWalk(); - return false; + return; } - return true; + + Creature::setFollowCreature(creature); } -bool Player::setAttackedCreature(Creature* creature) +void Player::setAttackedCreature(Creature* creature) { - if (!Creature::setAttackedCreature(creature)) { + if (isAttackingCreature(creature)) { + return; + } + + if (!canAttackCreature(creature)) { + removeAttackedCreature(); sendCancelTarget(); - return false; + return; } - if (chaseMode && creature) { + Creature::setAttackedCreature(creature); + + if (chaseMode) { if (followCreature != creature) { // chase opponent setFollowCreature(creature); } } else if (followCreature) { - setFollowCreature(nullptr); + removeFollowCreature(); } - if (creature) { - g_dispatcher.addTask([id = getID()]() { g_game.checkCreatureAttack(id); }); + g_dispatcher.addTask([id = getID()]() { g_game.checkCreatureAttack(id); }); +} + +void Player::removeAttackedCreature() +{ + Creature::removeAttackedCreature(); + + if (followCreature) { + removeFollowCreature(); } - return true; } void Player::goToFollowCreature() @@ -3434,11 +3451,11 @@ uint64_t Player::getGainedExperience(Creature* attacker) const return 0; } -void Player::onFollowCreature(const Creature* creature) +void Player::onUnfollowCreature() { - if (!creature) { - stopWalk(); - } + Creature::onUnfollowCreature(); + + stopWalk(); } void Player::setChaseMode(bool mode) @@ -3453,7 +3470,7 @@ void Player::setChaseMode(bool mode) setFollowCreature(attackedCreature); } } else if (attackedCreature) { - setFollowCreature(nullptr); + removeFollowCreature(); cancelNextWalk = true; } } diff --git a/src/player.h b/src/player.h index 969a860457..4f8ccc03ff 100644 --- a/src/player.h +++ b/src/player.h @@ -406,11 +406,11 @@ class Player final : public Creature, public Cylinder bool editVIP(uint32_t vipGuid, const std::string& description, uint32_t icon, bool notify); // follow functions - bool setFollowCreature(Creature* creature) override; + void setFollowCreature(Creature* creature) override; void goToFollowCreature() override; // follow events - void onFollowCreature(const Creature* creature) override; + void onUnfollowCreature() override; // walk events void onWalk(Direction& dir) override; @@ -428,7 +428,8 @@ class Player final : public Creature, public Cylinder void setSecureMode(bool mode) { secureMode = mode; } // combat functions - bool setAttackedCreature(Creature* creature) override; + void setAttackedCreature(Creature* creature) override; + void removeAttackedCreature() override; bool isImmune(CombatType_t type) const override; bool isImmune(ConditionType_t type) const override; bool hasShield() const;