diff --git a/data/libs/functions/boss_lever.lua b/data/libs/functions/boss_lever.lua index b95bf7211b0..cbecfc26f8c 100644 --- a/data/libs/functions/boss_lever.lua +++ b/data/libs/functions/boss_lever.lua @@ -144,6 +144,7 @@ end ---@param player Player ---@return boolean function BossLever:onUse(player) + local monsterName = MonsterType(self.name):getName() local isParticipant = false for _, v in ipairs(self.playerPositions) do if Position(v.pos) == player:getPosition() then @@ -161,7 +162,7 @@ function BossLever:onUse(player) local zone = self:getZone() if zone:countPlayers(IgnoredByMonsters) > 0 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. self.name .. ".") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. monsterName .. ".") return true end diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 1e6d637a158..7bf72496bb7 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -693,7 +693,7 @@ void Player::addSkillAdvance(skills_t skill, uint64_t count) { } g_events().eventPlayerOnGainSkillTries(static_self_cast(), skill, count); - g_callbacks().executeCallback(EventCallback_t::playerOnGainSkillTries, &EventCallback::playerOnGainSkillTries, getPlayer(), skill, count); + g_callbacks().executeCallback(EventCallback_t::playerOnGainSkillTries, &EventCallback::playerOnGainSkillTries, getPlayer(), std::ref(skill), std::ref(count)); if (count == 0) { return; } @@ -2247,6 +2247,8 @@ void Player::onThink(uint32_t interval) { // Wheel of destiny major spells wheel()->onThink(); + + g_callbacks().executeCallback(EventCallback_t::playerOnThink, &EventCallback::playerOnThink, getPlayer(), interval); } uint32_t Player::isMuted() const { @@ -2378,7 +2380,7 @@ void Player::addExperience(std::shared_ptr target, uint64_t exp, bool return; } - g_callbacks().executeCallback(EventCallback_t::playerOnGainExperience, &EventCallback::playerOnGainExperience, getPlayer(), target, exp, rawExp); + g_callbacks().executeCallback(EventCallback_t::playerOnGainExperience, &EventCallback::playerOnGainExperience, getPlayer(), target, std::ref(exp), std::ref(rawExp)); g_events().eventPlayerOnGainExperience(static_self_cast(), target, exp, rawExp); if (exp == 0) { @@ -2493,7 +2495,7 @@ void Player::removeExperience(uint64_t exp, bool sendText /* = false*/) { } g_events().eventPlayerOnLoseExperience(static_self_cast(), exp); - g_callbacks().executeCallback(EventCallback_t::playerOnLoseExperience, &EventCallback::playerOnLoseExperience, getPlayer(), exp); + g_callbacks().executeCallback(EventCallback_t::playerOnLoseExperience, &EventCallback::playerOnLoseExperience, getPlayer(), std::ref(exp)); if (exp == 0) { return; } @@ -5572,15 +5574,15 @@ int32_t Player::getMagicShieldCapacityPercent(bool useCharges) const { return result; } -int32_t Player::getReflectPercent(CombatType_t combat, bool useCharges) const { - int32_t result = reflectPercent[combatTypeToIndex(combat)]; - for (const auto &item : getEquippedItems()) { +double_t Player::getReflectPercent(CombatType_t combat, bool useCharges) const { + double_t result = reflectPercent[combatTypeToIndex(combat)]; + for (const auto item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { continue; } - int32_t reflectPercent = itemType.abilities->reflectPercent[combatTypeToIndex(combat)]; + double_t reflectPercent = itemType.abilities->reflectPercent[combatTypeToIndex(combat)]; if (reflectPercent != 0) { result += reflectPercent; uint16_t charges = item->getCharges(); @@ -6000,7 +6002,7 @@ bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries) { oldPercentToNextLevel = static_cast(manaSpent * 100) / nextReqMana; g_events().eventPlayerOnGainSkillTries(static_self_cast(), SKILL_MAGLEVEL, tries); - g_callbacks().executeCallback(EventCallback_t::playerOnGainSkillTries, &EventCallback::playerOnGainSkillTries, getPlayer(), SKILL_MAGLEVEL, tries); + g_callbacks().executeCallback(EventCallback_t::playerOnGainSkillTries, &EventCallback::playerOnGainSkillTries, getPlayer(), SKILL_MAGLEVEL, std::ref(tries)); uint32_t currMagLevel = magLevel; while ((manaSpent + tries) >= nextReqMana) { diff --git a/src/game/game.cpp b/src/game/game.cpp index 718aefbe1e8..bf48e252dce 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1444,6 +1444,10 @@ ReturnValue Game::internalMoveCreature(std::shared_ptr creature, Direc return RETURNVALUE_NOTPOSSIBLE; } + if (creature->getBaseSpeed() == 0 && flags != FLAG_IGNORENOTMOVABLE) { + return RETURNVALUE_NOTMOVABLE; + } + creature->setLastPosition(creature->getPosition()); const Position ¤tPos = creature->getPosition(); Position destPos = getNextPosition(direction, currentPos); @@ -7084,7 +7088,7 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt if (!isEvent) { g_events().eventCreatureOnDrainHealth(target, attacker, damage.primary.type, damage.primary.value, damage.secondary.type, damage.secondary.value, message.primary.color, message.secondary.color); - g_callbacks().executeCallback(EventCallback_t::creatureOnDrainHealth, &EventCallback::creatureOnDrainHealth, target, attacker, damage.primary.type, damage.primary.value, damage.secondary.type, damage.secondary.value, message.primary.color, message.secondary.color); + g_callbacks().executeCallback(EventCallback_t::creatureOnDrainHealth, &EventCallback::creatureOnDrainHealth, target, attacker, std::ref(damage.primary.type), std::ref(damage.primary.value), std::ref(damage.secondary.type), std::ref(damage.secondary.value), std::ref(message.primary.color), std::ref(message.secondary.color)); } if (damage.origin != ORIGIN_NONE && attacker && damage.primary.type != COMBAT_HEALING) { damage.primary.value *= attacker->getBuff(BUFF_DAMAGEDEALT) / 100.; @@ -7279,7 +7283,7 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt return true; } else if (realDamage >= targetHealth) { for (const auto &creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { - if (!creatureEvent->executeOnPrepareDeath(target, attacker)) { + if (!creatureEvent->executeOnPrepareDeath(target, attacker, std::ref(realDamage))) { return false; } } diff --git a/src/lua/callbacks/callbacks_definitions.hpp b/src/lua/callbacks/callbacks_definitions.hpp index 3b8016f5f5b..6c19cc809c9 100644 --- a/src/lua/callbacks/callbacks_definitions.hpp +++ b/src/lua/callbacks/callbacks_definitions.hpp @@ -25,6 +25,7 @@ enum class EventCallback_t : uint16_t { creatureOnTargetCombat, creatureOnHear, creatureOnDrainHealth, + creatureOnCombat, // Party partyOnJoin, partyOnLeave, @@ -57,6 +58,7 @@ enum class EventCallback_t : uint16_t { playerOnInventoryUpdate, playerOnRotateItem, playerOnWalk, + playerOnThink, // Monster monsterOnDropLoot, monsterPostDropLoot, @@ -68,4 +70,5 @@ enum class EventCallback_t : uint16_t { zoneBeforeCreatureLeave, zoneAfterCreatureEnter, zoneAfterCreatureLeave, + mapOnLoad, }; diff --git a/src/lua/callbacks/event_callback.hpp b/src/lua/callbacks/event_callback.hpp index 9141235a028..9e65480c246 100644 --- a/src/lua/callbacks/event_callback.hpp +++ b/src/lua/callbacks/event_callback.hpp @@ -35,13 +35,27 @@ class EventCallback : public Script { private: EventCallback_t m_callbackType = EventCallback_t::none; ///< The type of the event callback. std::string m_scriptTypeName; ///< The name associated with the script type. + std::string m_callbackName; ///< The name of the callback. + bool m_skipDuplicationCheck = false; ///< Whether the callback is silent error for already registered log error. public: /** * @brief Constructor that initializes the EventCallback with a given script interface. * @param scriptInterface Pointer to the LuaScriptInterface object. */ - explicit EventCallback(LuaScriptInterface* scriptInterface); + explicit EventCallback(LuaScriptInterface* scriptInterface, const std::string &callbackName, bool silentAlreadyRegistered); + + /** + * @brief Retrieves the callback name. + * @return The callback name as a string. + */ + std::string getName() const; + + /** + * @brief Retrieves the skip registration status of the callback. + * @return True if the callback is true for skip duplication check and register again the event, false otherwise. + */ + bool skipDuplicationCheck() const; /** * @brief Retrieves the script type name. @@ -84,6 +98,7 @@ class EventCallback : public Script { ReturnValue creatureOnTargetCombat(std::shared_ptr creature, std::shared_ptr target) const; void creatureOnHear(std::shared_ptr creature, std::shared_ptr speaker, const std::string &words, SpeakClasses type) const; void creatureOnDrainHealth(std::shared_ptr creature, std::shared_ptr attacker, CombatType_t &typePrimary, int32_t &damagePrimary, CombatType_t &typeSecondary, int32_t &damageSecondary, TextColor_t &colorPrimary, TextColor_t &colorSecondary) const; + void creatureOnCombat(std::shared_ptr attacker, std::shared_ptr target, CombatDamage &damage) const; // Party bool partyOnJoin(std::shared_ptr party, std::shared_ptr player) const; @@ -117,6 +132,7 @@ class EventCallback : public Script { void playerOnInventoryUpdate(std::shared_ptr player, std::shared_ptr item, Slots_t slot, bool equip) const; bool playerOnRotateItem(std::shared_ptr player, std::shared_ptr item, const Position &position) const; void playerOnWalk(std::shared_ptr player, Direction &dir) const; + void playerOnThink(std::shared_ptr player, uint32_t interval) const; // Monster void monsterOnDropLoot(std::shared_ptr monster, std::shared_ptr corpse) const; @@ -132,7 +148,5 @@ class EventCallback : public Script { void zoneAfterCreatureEnter(std::shared_ptr zone, std::shared_ptr creature) const; void zoneAfterCreatureLeave(std::shared_ptr zone, std::shared_ptr creature) const; - /** - * @note here end the lua binder functions } - */ + void mapOnLoad(const std::string &mapFullPath) const; }; diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 9cc5434abd5..d6c182bfa2a 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -2270,10 +2270,23 @@ int PlayerFunctions::luaPlayerGetParty(lua_State* L) { } int PlayerFunctions::luaPlayerAddOutfit(lua_State* L) { - // player:addOutfit(lookType) + // player:addOutfit(lookType or name, addon = 0) std::shared_ptr player = getUserdataShared(L, 1); if (player) { - player->addOutfit(getNumber(L, 2), 0); + auto addon = getNumber(L, 3, 0); + if (lua_isnumber(L, 2)) { + player->addOutfit(getNumber(L, 2), addon); + } else if (lua_isstring(L, 2)) { + const std::string &outfitName = getString(L, 2); + const auto &outfit = Outfits::getInstance().getOutfitByName(player->getSex(), outfitName); + if (!outfit) { + reportErrorFunc("Outfit not found"); + return 1; + } + + player->addOutfit(outfit->lookType, addon); + } + pushBoolean(L, true); } else { lua_pushnil(L); @@ -4355,3 +4368,16 @@ int PlayerFunctions::luaPlayerSetCurrentTitle(lua_State* L) { pushBoolean(L, true); return 1; } + +int PlayerFunctions::luaPlayerSendCreatureAppear(lua_State* L) { + auto player = getUserdataShared(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + bool isLogin = getBoolean(L, 2, false); + player->sendCreatureAppear(player, player->getPosition(), isLogin); + pushBoolean(L, true); + return 1; +} diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 4d89a86d27f..e9114a708e7 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -372,6 +372,8 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "getTitles", PlayerFunctions::luaPlayerGetTitles); registerMethod(L, "Player", "setCurrentTitle", PlayerFunctions::luaPlayerSetCurrentTitle); + registerMethod(L, "Player", "sendCreatureAppear", PlayerFunctions::luaPlayerSendCreatureAppear); + GroupFunctions::init(L); GuildFunctions::init(L); MountFunctions::init(L); @@ -732,5 +734,7 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerGetTitles(lua_State* L); static int luaPlayerSetCurrentTitle(lua_State* L); + static int luaPlayerSendCreatureAppear(lua_State* L); + friend class CreatureFunctions; };