diff --git a/data-canary/scripts/lib/register_npc_type.lua b/data-canary/scripts/lib/register_npc_type.lua index bf23e53951e..84c642b9c48 100644 --- a/data-canary/scripts/lib/register_npc_type.lua +++ b/data-canary/scripts/lib/register_npc_type.lua @@ -25,19 +25,6 @@ registerNpcType.description = function(npcType, mask) end end -registerNpcType.speechBubble = function(npcType, mask) - local speechBubble = npcType:speechBubble() - if mask.speechBubble then - npcType:speechBubble(mask.speechBubble) - elseif speechBubble == 3 then - npcType:speechBubble(4) - elseif speechBubble < 1 then - npcType:speechBubble(1) - else - npcType:speechBubble(2) - end -end - registerNpcType.outfit = function(npcType, mask) if mask.outfit then npcType:outfit(mask.outfit) @@ -206,3 +193,9 @@ registerNpcType.currency = function(npcType, mask) npcType:currency(mask.currency) end end + +registerNpcType.speechBubble = function(npcType, mask) + if mask.speechBubble then + npcType:speechBubble(mask.speechBubble) + end +end diff --git a/data-otservbr-global/scripts/lib/register_npc_type.lua b/data-otservbr-global/scripts/lib/register_npc_type.lua index bf23e53951e..84c642b9c48 100644 --- a/data-otservbr-global/scripts/lib/register_npc_type.lua +++ b/data-otservbr-global/scripts/lib/register_npc_type.lua @@ -25,19 +25,6 @@ registerNpcType.description = function(npcType, mask) end end -registerNpcType.speechBubble = function(npcType, mask) - local speechBubble = npcType:speechBubble() - if mask.speechBubble then - npcType:speechBubble(mask.speechBubble) - elseif speechBubble == 3 then - npcType:speechBubble(4) - elseif speechBubble < 1 then - npcType:speechBubble(1) - else - npcType:speechBubble(2) - end -end - registerNpcType.outfit = function(npcType, mask) if mask.outfit then npcType:outfit(mask.outfit) @@ -206,3 +193,9 @@ registerNpcType.currency = function(npcType, mask) npcType:currency(mask.currency) end end + +registerNpcType.speechBubble = function(npcType, mask) + if mask.speechBubble then + npcType:speechBubble(mask.speechBubble) + end +end diff --git a/data/items/items.xml b/data/items/items.xml index 0b12e55ca6e..0d1241cd6a7 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -10650,7 +10650,10 @@ - + + + + @@ -13126,43 +13129,70 @@ - + - + + + + - + + + + + + + - + - + + + + - + + + + - + + + + - + + + + - + + + + + + + @@ -13561,7 +13591,11 @@ - + + + + + @@ -13720,8 +13754,12 @@ - + + + + + @@ -18442,6 +18480,7 @@ + @@ -18450,6 +18489,7 @@ + @@ -18468,10 +18508,13 @@ - + + + + @@ -18926,10 +18969,13 @@ - + + + + @@ -47740,8 +47786,12 @@ - + + + + + @@ -47764,8 +47814,19 @@ - + + + + + + + + + + + + @@ -50208,6 +50269,7 @@ + diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 29ccdfa63e3..8faa76b6367 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -343,6 +343,8 @@ ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target) { } } } + } else if (target && target->getNpc()) { + return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; } if (g_game().getWorldType() == WORLD_TYPE_NO_PVP) { diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 031d557f863..b032d42e9ee 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1247,7 +1247,7 @@ bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange) { } if (!creature->isAttackable() || Combat::canDoCombat(attacker, creature) != RETURNVALUE_NOERROR) { - if (!creature->isInGhostMode()) { + if (!creature->isInGhostMode() && !creature->getNpc()) { g_game().addMagicEffect(creature->getPosition(), CONST_ME_POFF); } return false; diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 6b4185970fc..e9ee0c46211 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -223,6 +223,7 @@ enum SpeechBubble_t { SPEECHBUBBLE_TRADE = 2, SPEECHBUBBLE_QUEST = 3, SPEECHBUBBLE_QUESTTRADER = 4, + SPEECHBUBBLE_HIRELING = 7, }; enum MarketAction_t { diff --git a/src/creatures/npcs/npcs.cpp b/src/creatures/npcs/npcs.cpp index 649ba6cd166..4f80f3b5c43 100644 --- a/src/creatures/npcs/npcs.cpp +++ b/src/creatures/npcs/npcs.cpp @@ -95,6 +95,8 @@ void NpcType::loadShop(NpcType* npcType, ShopBlock shopBlock) { } else { npcType->info.shopItemVector.push_back(shopBlock); } + + info.speechBubble = SPEECHBUBBLE_TRADE; } bool Npcs::load(bool loadLibs /* = true*/, bool loadNpcs /* = true*/, bool reloading /* = false*/) const { diff --git a/src/creatures/npcs/npcs.h b/src/creatures/npcs/npcs.h index e30cb4b23cc..92bf372f272 100644 --- a/src/creatures/npcs/npcs.h +++ b/src/creatures/npcs/npcs.h @@ -32,7 +32,7 @@ class NpcType { LightInfo light = {}; - uint8_t speechBubble; + uint8_t speechBubble = SPEECHBUBBLE_NORMAL; uint16_t currencyId = ITEM_GOLD_COIN; diff --git a/src/items/items.cpp b/src/items/items.cpp index c658e178b57..cc73998f1c0 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -169,25 +169,18 @@ bool Items::loadFromXml() { } for (auto itemNode : doc.child("items").children()) { - pugi::xml_attribute idAttribute = itemNode.attribute("id"); - if (idAttribute) { + if (auto idAttribute = itemNode.attribute("id"); idAttribute) { parseItemNode(itemNode, pugi::cast(idAttribute.value())); continue; } - pugi::xml_attribute fromIdAttribute = itemNode.attribute("fromid"); + auto fromIdAttribute = itemNode.attribute("fromid"); if (!fromIdAttribute) { - if (idAttribute) { - SPDLOG_WARN("[Items::loadFromXml] - " - "No item id: {} found", - idAttribute.value()); - } else { - SPDLOG_WARN("[Items::loadFromXml] - No item id found"); - } + SPDLOG_WARN("[Items::loadFromXml] - No item id found, use id or fromid"); continue; } - pugi::xml_attribute toIdAttribute = itemNode.attribute("toid"); + auto toIdAttribute = itemNode.attribute("toid"); if (!toIdAttribute) { SPDLOG_WARN("[Items::loadFromXml] - " "tag fromid: {} without toid", diff --git a/src/items/tile.cpp b/src/items/tile.cpp index 737cb46cff0..99e3bdd36fb 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -465,9 +465,17 @@ void Tile::onUpdateTile(const SpectatorHashSet &spectators) { } ReturnValue Tile::queryAdd(int32_t, const Thing &thing, uint32_t, uint32_t tileFlags, Creature*) const { + if (hasBitSet(FLAG_NOLIMIT, tileFlags)) { + return RETURNVALUE_NOERROR; + } + if (const Creature* creature = thing.getCreature()) { - if (hasBitSet(FLAG_NOLIMIT, tileFlags)) { - return RETURNVALUE_NOERROR; + + if (creature->getNpc()) { + ReturnValue returnValue = checkNpcCanWalkIntoTile(); + if (returnValue != RETURNVALUE_NOERROR) { + return returnValue; + } } if (hasBitSet(FLAG_PATHFINDING, tileFlags) && hasFlag(TILESTATE_FLOORCHANGE | TILESTATE_TELEPORT)) { @@ -525,9 +533,8 @@ ReturnValue Tile::queryAdd(int32_t, const Thing &thing, uint32_t, uint32_t tileF } } - MagicField* field = getFieldItem(); - if (field && !field->isBlocking() && field->getDamage() != 0) { - CombatType_t combatType = field->getCombatType(); + if (hasHarmfulField()) { + CombatType_t combatType = getFieldItem()->getCombatType(); // There is 3 options for a monster to enter a magic field // 1) Monster is immune @@ -557,7 +564,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing &thing, uint32_t, uint32_t tileF } } - if (hasBitSet(FLAG_PATHFINDING, tileFlags) && hasFlag(TILESTATE_MAGICFIELD) && !fieldIsUnharmable()) { + if (hasBitSet(FLAG_PATHFINDING, tileFlags) && hasHarmfulField()) { return RETURNVALUE_NOTPOSSIBLE; } @@ -633,10 +640,6 @@ ReturnValue Tile::queryAdd(int32_t, const Thing &thing, uint32_t, uint32_t tileF return RETURNVALUE_NOTPOSSIBLE; } - if (hasBitSet(FLAG_NOLIMIT, tileFlags)) { - return RETURNVALUE_NOERROR; - } - bool itemIsHangable = item->isHangable(); if (ground == nullptr && !itemIsHangable) { return RETURNVALUE_NOTPOSSIBLE; @@ -700,9 +703,16 @@ ReturnValue Tile::queryAdd(int32_t, const Thing &thing, uint32_t, uint32_t tileF return RETURNVALUE_NOERROR; } -bool Tile::fieldIsUnharmable() const { - uint16_t fieldId = getFieldItem()->getID(); - return fieldId == ITEM_FIREFIELD_PVP_SMALL || fieldId == ITEM_FIREFIELD_PERSISTENT_SMALL; +ReturnValue Tile::checkNpcCanWalkIntoTile() const { + if (hasHarmfulField()) { + return RETURNVALUE_NOTPOSSIBLE; + } else { + return RETURNVALUE_NOERROR; + } +} + +bool Tile::hasHarmfulField() const { + return hasFlag(TILESTATE_MAGICFIELD) && getFieldItem() && !getFieldItem()->isBlocking() && getFieldItem()->getDamage() > 0; } ReturnValue Tile::queryMaxCount(int32_t, const Thing &, uint32_t count, uint32_t &maxQueryCount, uint32_t) const { diff --git a/src/items/tile.h b/src/items/tile.h index d5127d809ac..65d2148f282 100644 --- a/src/items/tile.h +++ b/src/items/tile.h @@ -246,7 +246,8 @@ class Tile : public Cylinder { void setTileFlags(const Item* item); void resetTileFlags(const Item* item); - bool fieldIsUnharmable() const; + bool hasHarmfulField() const; + ReturnValue checkNpcCanWalkIntoTile() const; protected: Item* ground = nullptr; diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index 7d8591ac382..64a8aa56d08 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -955,6 +955,7 @@ void LuaEnums::initSpeechBubbleEnums(lua_State* L) { registerEnum(L, SPEECHBUBBLE_TRADE); registerEnum(L, SPEECHBUBBLE_QUEST); registerEnum(L, SPEECHBUBBLE_QUESTTRADER); + registerEnum(L, SPEECHBUBBLE_HIRELING); } // Use with player:addMapMark diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index e427488ef76..5c6fcb5763a 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3208,7 +3208,7 @@ int PlayerFunctions::luaPlayerGetFaction(lua_State* L) { return 1; } -int PlayerFunctions::luaPlayerIsUIExhausted(lua_State *L) { +int PlayerFunctions::luaPlayerIsUIExhausted(lua_State* L) { // player:isUIExhausted() Player* player = getUserdata(L, 1); if (!player) { @@ -3222,7 +3222,7 @@ int PlayerFunctions::luaPlayerIsUIExhausted(lua_State *L) { return 1; } -int PlayerFunctions::luaPlayerUpdateUIExhausted(lua_State *L) { +int PlayerFunctions::luaPlayerUpdateUIExhausted(lua_State* L) { // player:updateUIExhausted(exhaustionTime = 250) Player* player = getUserdata(L, 1); if (!player) { diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 67081defe6c..cf5a63e694f 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -562,8 +562,8 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerGetForgeSlivers(lua_State* L); static int luaPlayerGetForgeCores(lua_State* L); - static int luaPlayerIsUIExhausted(lua_State* L); - static int luaPlayerUpdateUIExhausted(lua_State* L); + static int luaPlayerIsUIExhausted(lua_State* L); + static int luaPlayerUpdateUIExhausted(lua_State* L); static int luaPlayerSetFaction(lua_State* L); static int luaPlayerGetFaction(lua_State* L);