diff --git a/data/scripts/talkactions/player/emote_spell.lua b/data/scripts/talkactions/player/emote_spell.lua new file mode 100644 index 00000000000..c86157740c5 --- /dev/null +++ b/data/scripts/talkactions/player/emote_spell.lua @@ -0,0 +1,20 @@ +-- Usage talkaction: "!emote on" or "!emote off" +local emoteSpell = TalkAction("!emote") + +function emoteSpell.onSay(player, words, param) + if param == "" then + player:sendCancelMessage("You need to specify on/off param.") + return false + end + if param == "on" then + player:setStorageValue(STORAGEVALUE_EMOTE, 1) + player:sendTextMessage(MESSAGE_INFO_DESCR, "You activated emoted spells") + elseif param == "off" then + player:setStorageValue(STORAGEVALUE_EMOTE, 0) + player:sendTextMessage(MESSAGE_INFO_DESCR, "You desactivated emoted spells") + end + return true +end + +emoteSpell:separator(" ") +emoteSpell:register() diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 2be4a190738..9a3ca4d3168 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6240,6 +6240,70 @@ std::pair, std::map>> P return std::make_pair(itemVector, lockerItems); } +bool Player::saySpell( + SpeakClasses type, + const std::string& text, + bool ghostMode, + SpectatorHashSet* spectatorsPtr/* = nullptr*/, + const Position* pos/* = nullptr*/) +{ + if (text.empty()) { + SPDLOG_DEBUG("{} - Spell text is empty for player {}", __FUNCTION__, getName()); + return false; + } + + if (!pos) { + pos = &getPosition(); + } + + SpectatorHashSet spectators; + + if (!spectatorsPtr || spectatorsPtr->empty()) { + // This somewhat complex construct ensures that the cached SpectatorHashSet + // is used if available and if it can be used, else a local vector is + // used (hopefully the compiler will optimize away the construction of + // the temporary when it's not used). + if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) { + g_game().map.getSpectators(spectators, *pos, false, false, + Map::maxClientViewportX, Map::maxClientViewportX, + Map::maxClientViewportY, Map::maxClientViewportY); + } else { + g_game().map.getSpectators(spectators, *pos, true, false, 18, 18, 14, 14); + } + } else { + spectators = (*spectatorsPtr); + } + + int32_t valueEmote = 0; + // Send to client + for (Creature* spectator : spectators) { + if (Player* tmpPlayer = spectator->getPlayer()) { + tmpPlayer->getStorageValue(STORAGEVALUE_EMOTE, valueEmote); + if (!ghostMode || tmpPlayer->canSeeCreature(this)) { + if (valueEmote == 1) { + tmpPlayer->sendCreatureSay(this, TALKTYPE_MONSTER_SAY, text, pos); + } else { + tmpPlayer->sendCreatureSay(this, TALKTYPE_SPELL_USE, text, pos); + } + } + } + } + + // Execute lua event method + for (Creature* spectator : spectators) { + auto tmpPlayer = spectator->getPlayer(); + if (!tmpPlayer) { + continue; + } + + tmpPlayer->onCreatureSay(this, type, text); + if (this != tmpPlayer) { + g_events().eventCreatureOnHear(tmpPlayer, this, text, type); + } + } + return true; +} + /******************************************************************************* * Interfaces ******************************************************************************/ @@ -6257,4 +6321,3 @@ error_t Player::GetAccountInterface(account::Account* account) { account = account_; return account::ERROR_NO; } - diff --git a/src/creatures/players/player.h b/src/creatures/players/player.h index 85f35c4dfa0..2f010ad3795 100644 --- a/src/creatures/players/player.h +++ b/src/creatures/players/player.h @@ -2089,6 +2089,14 @@ class Player final : public Creature, public Cylinder std::pair, std::map>> requestLockerItems(DepotLocker *depotLocker, bool sendToClient = false, uint8_t tier = 0) const; + bool saySpell( + SpeakClasses type, + const std::string& text, + bool ghostMode, + SpectatorHashSet* spectatorsPtr = nullptr, + const Position* pos = nullptr + ); + private: std::forward_list getMuteConditions() const; diff --git a/src/game/game.cpp b/src/game/game.cpp index 9caf4ca73b6..2f885a95abd 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5005,10 +5005,10 @@ bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& player->cancelPush(); } - if (!g_configManager().getBoolean(EMOTE_SPELLS)) { - return internalCreatureSay(player, TALKTYPE_SPELL_USE, words, false); - } else { + if (g_configManager().getBoolean(EMOTE_SPELLS)) { return internalCreatureSay(player, TALKTYPE_MONSTER_SAY, words, false); + } else { + return player->saySpell(type, words, false); } } else if (result == TALKACTION_FAILED) { diff --git a/src/lua/functions/core/game/lua_enums.hpp b/src/lua/functions/core/game/lua_enums.hpp index 9ec45b4c13b..2ffa86f0f21 100644 --- a/src/lua/functions/core/game/lua_enums.hpp +++ b/src/lua/functions/core/game/lua_enums.hpp @@ -1038,6 +1038,7 @@ class LuaEnums final : LuaScriptInterface { registerEnum(L, LIGHT_STATE_NIGHT); registerEnum(L, LIGHT_STATE_SUNSET); registerEnum(L, LIGHT_STATE_SUNRISE); + registerEnum(L, STORAGEVALUE_EMOTE); // Webhook default colors registerEnum(L, WEBHOOK_COLOR_ONLINE);