From ad03b0eb3e54c946a6f68c46d2f66f98c2e0a9ca Mon Sep 17 00:00:00 2001 From: ErikasKontenis Date: Mon, 16 Sep 2019 20:38:16 +0300 Subject: [PATCH] introduce changes from streamside fork --- config.lua | 4 +- data/actions/scripts/misc/fluids.lua | 7 +- data/creaturescripts/scripts/firstitems.lua | 4 +- data/events/events.xml | 28 + data/events/scripts/creature.lua | 11 + data/events/scripts/party.lua | 35 + data/events/scripts/player.lua | 169 ++++ data/global.lua | 4 + data/lib/core/core.lua | 1 + data/lib/core/game.lua | 8 +- data/lib/core/tile.lua | 42 + data/lib/core/vocation.lua | 7 + data/movements/scripts/misc/floorchange.lua | 3 +- data/talkactions/scripts/reload.lua | 65 ++ data/talkactions/scripts/save.lua | 23 + data/talkactions/scripts/sellhouse.lua | 19 + data/talkactions/talkactions.xml | 5 +- src/CMakeLists.txt | 2 +- src/account.h | 2 +- src/actions.cpp | 2 +- src/actions.h | 2 +- src/ban.cpp | 2 +- src/ban.h | 2 +- src/baseevents.cpp | 2 +- src/baseevents.h | 2 +- src/bed.cpp | 2 +- src/bed.h | 2 +- src/behaviourdatabase.cpp | 8 +- src/behaviourdatabase.h | 2 +- src/chat.cpp | 2 +- src/chat.h | 2 +- src/combat.cpp | 213 ++--- src/combat.h | 23 +- src/commands.cpp | 2 +- src/commands.h | 2 +- src/condition.cpp | 11 +- src/condition.h | 2 +- src/configmanager.cpp | 6 +- src/configmanager.h | 5 +- src/connection.cpp | 2 +- src/connection.h | 2 +- src/const.h | 24 +- src/container.cpp | 34 +- src/container.h | 2 +- src/creature.cpp | 32 +- src/creature.h | 11 +- src/creatureevent.cpp | 91 ++- src/creatureevent.h | 6 +- src/cylinder.cpp | 2 +- src/cylinder.h | 2 +- src/database.cpp | 2 +- src/database.h | 2 +- src/databasemanager.cpp | 2 +- src/databasemanager.h | 2 +- src/databasetasks.cpp | 2 +- src/databasetasks.h | 2 +- src/definitions.h | 10 +- src/depotlocker.cpp | 2 +- src/depotlocker.h | 2 +- src/enums.h | 18 +- src/events.cpp | 827 ++++++++++++++++++++ src/events.h | 95 +++ src/fileloader.cpp | 2 +- src/fileloader.h | 2 +- src/game.cpp | 396 ++++++---- src/game.h | 13 +- src/globalevent.cpp | 2 +- src/globalevent.h | 2 +- src/groups.cpp | 2 +- src/groups.h | 2 +- src/guild.cpp | 2 +- src/guild.h | 2 +- src/house.cpp | 18 +- src/house.h | 10 +- src/housetile.cpp | 2 +- src/housetile.h | 2 +- src/ioguild.cpp | 2 +- src/ioguild.h | 2 +- src/iologindata.cpp | 2 +- src/iologindata.h | 2 +- src/iomap.cpp | 2 +- src/iomap.h | 2 +- src/iomapserialize.cpp | 2 +- src/iomapserialize.h | 2 +- src/item.cpp | 2 +- src/item.h | 2 +- src/items.cpp | 2 +- src/items.h | 2 +- src/lockfree.h | 2 +- src/luascript.cpp | 281 +++++-- src/luascript.h | 15 +- src/mailbox.cpp | 2 +- src/mailbox.h | 2 +- src/map.cpp | 2 +- src/map.h | 2 +- src/monster.cpp | 111 +-- src/monster.h | 2 +- src/monsters.cpp | 14 +- src/monsters.h | 2 +- src/movement.cpp | 2 +- src/movement.h | 2 +- src/networkmessage.cpp | 2 +- src/networkmessage.h | 2 +- src/npc.cpp | 2 +- src/npc.h | 2 +- src/otpch.cpp | 2 +- src/otpch.h | 2 +- src/otserv.cpp | 2 +- src/outputmessage.cpp | 2 +- src/outputmessage.h | 2 +- src/party.cpp | 27 +- src/party.h | 4 +- src/player.cpp | 89 ++- src/player.h | 19 +- src/position.cpp | 2 +- src/position.h | 2 +- src/protocol.cpp | 2 +- src/protocol.h | 2 +- src/protocolgame.cpp | 4 +- src/protocolgame.h | 2 +- src/protocollogin.cpp | 2 +- src/protocollogin.h | 2 +- src/protocolstatus.cpp | 2 +- src/protocolstatus.h | 2 +- src/pugicast.h | 2 +- src/raids.cpp | 2 +- src/raids.h | 2 +- src/rsa.cpp | 2 +- src/rsa.h | 2 +- src/scheduler.cpp | 2 +- src/scheduler.h | 2 +- src/script.cpp | 6 +- src/script.h | 2 +- src/scriptmanager.cpp | 12 +- src/scriptmanager.h | 2 +- src/server.cpp | 2 +- src/server.h | 2 +- src/spawn.cpp | 2 +- src/spawn.h | 2 +- src/spells.cpp | 18 +- src/spells.h | 2 +- src/talkaction.cpp | 17 +- src/talkaction.h | 5 +- src/tasks.cpp | 2 +- src/tasks.h | 2 +- src/teleport.cpp | 2 +- src/teleport.h | 2 +- src/thing.cpp | 2 +- src/thing.h | 2 +- src/thread_holder_base.h | 2 +- src/tile.cpp | 35 +- src/tile.h | 5 +- src/tools.cpp | 17 +- src/tools.h | 2 +- src/town.h | 2 +- src/vocation.cpp | 2 +- src/vocation.h | 2 +- src/waitlist.cpp | 2 +- src/waitlist.h | 2 +- src/wildcardtree.cpp | 2 +- src/wildcardtree.h | 2 +- vc14/theforgottenserver.vcxproj | 4 +- 162 files changed, 2485 insertions(+), 698 deletions(-) create mode 100644 data/events/events.xml create mode 100644 data/events/scripts/creature.lua create mode 100644 data/events/scripts/party.lua create mode 100644 data/events/scripts/player.lua create mode 100644 data/lib/core/vocation.lua create mode 100644 data/talkactions/scripts/reload.lua create mode 100644 data/talkactions/scripts/save.lua create mode 100644 data/talkactions/scripts/sellhouse.lua create mode 100644 src/events.cpp create mode 100644 src/events.h diff --git a/config.lua b/config.lua index 5bedf78..e354f60 100644 --- a/config.lua +++ b/config.lua @@ -69,7 +69,9 @@ freePremium = false kickIdlePlayerAfterMinutes = 15 maxMessageBuffer = 4 showMonsterLoot = false -queryPlayerContainers = false +blockHeight = false +dropItems = false + -- Character Rooking -- Level threshold is the level requirement to teleport players back to newbie town diff --git a/data/actions/scripts/misc/fluids.lua b/data/actions/scripts/misc/fluids.lua index 3277f38..e300dcb 100644 --- a/data/actions/scripts/misc/fluids.lua +++ b/data/actions/scripts/misc/fluids.lua @@ -2,7 +2,12 @@ local drunk = Condition(CONDITION_DRUNK) drunk:setParameter(CONDITION_PARAM_TICKS, 60000) local poison = Condition(CONDITION_POISON) -poison:setTiming(100) +poison:setParameter(CONDITION_PARAM_DELAYED, true) +poison:setParameter(CONDITION_PARAM_MINVALUE, -50) +poison:setParameter(CONDITION_PARAM_MAXVALUE, -120) +poison:setParameter(CONDITION_PARAM_STARTVALUE, -5) +poison:setParameter(CONDITION_PARAM_TICKINTERVAL, 5000) +poison:setParameter(CONDITION_PARAM_FORCEUPDATE, true) local messages = { [FLUID_WATER] = "Gulp.", diff --git a/data/creaturescripts/scripts/firstitems.lua b/data/creaturescripts/scripts/firstitems.lua index 7c837c9..0c3c75a 100644 --- a/data/creaturescripts/scripts/firstitems.lua +++ b/data/creaturescripts/scripts/firstitems.lua @@ -1,7 +1,5 @@ function onLogin(player) - if player:getLastLoginSaved() <= 0 or player:getStorageValue(30017) == 1 then - player:setStorageValue(30017, 0) -- reset storage for first items - + if player:getLastLoginSaved() <= 0 then -- Items if player:getSex() == PLAYERSEX_FEMALE then player:addItem(3562, 1, true, -1, CONST_SLOT_ARMOR) diff --git a/data/events/events.xml b/data/events/events.xml new file mode 100644 index 0000000..1b75858 --- /dev/null +++ b/data/events/events.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/events/scripts/creature.lua b/data/events/scripts/creature.lua new file mode 100644 index 0000000..ac4ff89 --- /dev/null +++ b/data/events/scripts/creature.lua @@ -0,0 +1,11 @@ +function Creature:onChangeOutfit(outfit) + return true +end + +function Creature:onAreaCombat(tile, isAggressive) + return RETURNVALUE_NOERROR +end + +function Creature:onTargetCombat(target) + return RETURNVALUE_NOERROR +end diff --git a/data/events/scripts/party.lua b/data/events/scripts/party.lua new file mode 100644 index 0000000..1a6b561 --- /dev/null +++ b/data/events/scripts/party.lua @@ -0,0 +1,35 @@ +function Party:onJoin(player) + return true +end + +function Party:onLeave(player) + return true +end + +function Party:onDisband() + return true +end + +function Party:onShareExperience(exp) + local sharedExperienceMultiplier = 1.20 --20% + local vocationsIds = {} + + local vocationId = self:getLeader():getVocation():getBase():getId() + if vocationId ~= VOCATION_NONE then + table.insert(vocationsIds, vocationId) + end + + for _, member in ipairs(self:getMembers()) do + vocationId = member:getVocation():getBase():getId() + if not table.contains(vocationsIds, vocationId) and vocationId ~= VOCATION_NONE then + table.insert(vocationsIds, vocationId) + end + end + + local size = #vocationsIds + if size > 1 then + sharedExperienceMultiplier = 1.0 + ((size * (5 * (size - 1) + 10)) / 100) + end + + return (exp * sharedExperienceMultiplier) / (#self:getMembers() + 1) +end diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua new file mode 100644 index 0000000..86262d7 --- /dev/null +++ b/data/events/scripts/player.lua @@ -0,0 +1,169 @@ +function Player:onLook(thing, position, distance) + local description = "You see " .. thing:getDescription(distance) + if self:getGroup():getAccess() then + if thing:isItem() then + description = string.format("%s\nItem ID: %d", description, thing:getId()) + + local actionId = thing:getActionId() + if actionId ~= 0 then + description = string.format("%s, Action ID: %d", description, actionId) + end + + local uniqueId = thing:getAttribute(ITEM_ATTRIBUTE_MOVEMENTID) + if uniqueId > 0 and uniqueId < 65536 then + description = string.format("%s, Movement ID: %d", description, uniqueId) + end + + local itemType = thing:getType() + + local transformEquipId = itemType:getTransformEquipId() + local transformDeEquipId = itemType:getTransformDeEquipId() + if transformEquipId ~= 0 then + description = string.format("%s\nTransforms to: %d (onEquip)", description, transformEquipId) + elseif transformDeEquipId ~= 0 then + description = string.format("%s\nTransforms to: %d (onDeEquip)", description, transformDeEquipId) + end + + local decayId = itemType:getDecayId() + if decayId ~= -1 then + description = string.format("%s\nDecays to: %d", description, decayId) + end + if thing:getAttribute(ITEM_ATTRIBUTE_DECAYSTATE) == 1 then + description = string.format("%s\nDecaying in %d minutes (%d seconds).", description, thing:getAttribute(ITEM_ATTRIBUTE_DURATION) / 1000 / 60, thing:getAttribute(ITEM_ATTRIBUTE_DURATION) / 1000) + end + elseif thing:isCreature() then + local str = "%s\nHealth: %d / %d" + if thing:isPlayer() and thing:getMaxMana() > 0 then + str = string.format("%s, Mana: %d / %d", str, thing:getMana(), thing:getMaxMana()) + end + description = string.format(str, description, thing:getHealth(), thing:getMaxHealth()) .. "." + end + + local position = thing:getPosition() + description = string.format( + "%s\nPosition: %d, %d, %d", + description, position.x, position.y, position.z + ) + + if thing:isCreature() then + if thing:isPlayer() then + description = string.format("%s\nIP: %s.", description, Game.convertIpToString(thing:getIp())) + end + end + end + self:sendTextMessage(MESSAGE_INFO_DESCR, description) +end + +function Player:onLookInBattleList(creature, distance) + local description = "You see " .. creature:getDescription(distance) + if self:getGroup():getAccess() then + local str = "%s\nHealth: %d / %d" + if creature:isPlayer() and creature:getMaxMana() > 0 then + str = string.format("%s, Mana: %d / %d", str, creature:getMana(), creature:getMaxMana()) + end + description = string.format(str, description, creature:getHealth(), creature:getMaxHealth()) .. "." + + local position = creature:getPosition() + description = string.format( + "%s\nPosition: %d, %d, %d", + description, position.x, position.y, position.z + ) + + if creature:isPlayer() then + description = string.format("%s\nIP: %s", description, Game.convertIpToString(creature:getIp())) + end + end + self:sendTextMessage(MESSAGE_INFO_DESCR, description) +end + +function Player:onLookInTrade(partner, item, distance) + self:sendTextMessage(MESSAGE_INFO_DESCR, "You see " .. item:getDescription(distance)) +end + +function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, toCylinder) + return true +end + +function Player:onItemMoved(item, count, fromPosition, toPosition, fromCylinder, toCylinder) +end + +function Player:onMoveCreature(creature, fromPosition, toPosition) + return true +end + +function Player:onReportBug(message, position, category) + if self:getAccountType() == ACCOUNT_TYPE_NORMAL then + return false + end + + local name = self:getName() + local file = io.open("data/reports/bugs/" .. name .. " report.txt", "a") + + if not file then + self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.") + return true + end + + io.output(file) + io.write("------------------------------\n") + io.write("Name: " .. name) + if category == BUG_CATEGORY_MAP then + io.write(" [Map position: " .. position.x .. ", " .. position.y .. ", " .. position.z .. "]") + end + local playerPosition = self:getPosition() + io.write(" [Player Position: " .. playerPosition.x .. ", " .. playerPosition.y .. ", " .. playerPosition.z .. "]\n") + io.write("Comment: " .. message .. "\n") + io.close(file) + + self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " .. configManager.getString(configKeys.SERVER_NAME) .. ".") + return true +end + +function Player:onTurn(direction) + return true +end + +function Player:onTradeRequest(target, item) + return true +end + +function Player:onTradeAccept(target, item, targetItem) + return true +end + +local soulCondition = Condition(CONDITION_SOUL, CONDITIONID_DEFAULT) +soulCondition:setTicks(4 * 60 * 1000) +soulCondition:setParameter(CONDITION_PARAM_SOULGAIN, 1) + +function Player:onGainExperience(source, exp, rawExp) + if not source or source:isPlayer() then + return exp + end + + -- Soul regeneration + local vocation = self:getVocation() + if self:getSoul() < vocation:getMaxSoul() and exp >= self:getLevel() then + soulCondition:setParameter(CONDITION_PARAM_SOULTICKS, vocation:getSoulGainTicks() * 1000) + self:addCondition(soulCondition) + end + + -- Apply experience stage multiplier + exp = exp * Game.getExperienceStage(self:getLevel()) + + return exp +end + +function Player:onLoseExperience(exp) + return exp +end + +function Player:onGainSkillTries(skill, tries) + if APPLY_SKILL_MULTIPLIER == false then + return tries + end + + if skill == SKILL_MAGLEVEL then + return tries * configManager.getNumber(configKeys.RATE_MAGIC) + end + return tries * configManager.getNumber(configKeys.RATE_SKILL) +end diff --git a/data/global.lua b/data/global.lua index 89a84ec..13608e1 100644 --- a/data/global.lua +++ b/data/global.lua @@ -40,4 +40,8 @@ table.contains = function(array, value) end end return false +end + +function isNumber(str) + return tonumber(str) ~= nil end \ No newline at end of file diff --git a/data/lib/core/core.lua b/data/lib/core/core.lua index 9491e80..8bc8ac0 100644 --- a/data/lib/core/core.lua +++ b/data/lib/core/core.lua @@ -9,3 +9,4 @@ dofile('data/lib/core/player.lua') dofile('data/lib/core/position.lua') dofile('data/lib/core/teleport.lua') dofile('data/lib/core/tile.lua') +dofile('data/lib/core/vocation.lua') \ No newline at end of file diff --git a/data/lib/core/game.lua b/data/lib/core/game.lua index 5bbec2b..29cacef 100644 --- a/data/lib/core/game.lua +++ b/data/lib/core/game.lua @@ -9,7 +9,7 @@ function Game.removeItemsOnMap(position) local i = 0 while i < tileCount do local tileItem = tile:getThing(i) - if tileItem and not tileItem:isCreature() and ItemType(tileItem:getId()):isMovable() then + if tileItem and tileItem:getType():isMovable() then tileItem:remove() else i = i + 1 @@ -24,10 +24,8 @@ function Game.transformItemOnMap(position, itemId, toItemId, subtype) local tile = Tile(position) local item = tile:getItemById(itemId) - if item ~= nil then - item:transform(toItemId, subtype) - item:decay() - end + item:transform(toItemId, subtype) + item:decay() return item end diff --git a/data/lib/core/tile.lua b/data/lib/core/tile.lua index c254ad7..de3072f 100644 --- a/data/lib/core/tile.lua +++ b/data/lib/core/tile.lua @@ -21,3 +21,45 @@ end function Tile.isTile(self) return true end + +function Tile.relocateTo(self, toPosition, pushMove, monsterPosition) + if self:getPosition() == toPosition then + return false + end + + if not Tile(toPosition) then + return false + end + + for i = self:getThingCount() - 1, 0, -1 do + local thing = self:getThing(i) + if thing then + if thing:isItem() then + if ItemType(thing.itemid):isMovable() then + thing:moveTo(toPosition) + end + elseif thing:isCreature() then + if monsterPosition and thing:isMonster() then + thing:teleportTo(monsterPosition, pushMove) + else + thing:teleportTo(toPosition, pushMove) + end + end + end + end + return true +end + +function Tile:getPlayers() + local players = {} + local creatures = self:getCreatures() + if (creatures) then + for i = 1, #creatures do + if (creatures[i]:isPlayer()) then + table.insert(players, creatures[i]) + end + end + end + + return players +end \ No newline at end of file diff --git a/data/lib/core/vocation.lua b/data/lib/core/vocation.lua new file mode 100644 index 0000000..33772a5 --- /dev/null +++ b/data/lib/core/vocation.lua @@ -0,0 +1,7 @@ +function Vocation.getBase(self) + local base = self + while base:getDemotion() do + base = base:getDemotion() + end + return base +end \ No newline at end of file diff --git a/data/movements/scripts/misc/floorchange.lua b/data/movements/scripts/misc/floorchange.lua index 58b54aa..6e1f365 100644 --- a/data/movements/scripts/misc/floorchange.lua +++ b/data/movements/scripts/misc/floorchange.lua @@ -81,8 +81,7 @@ function onStepIn(creature, item, position, fromPosition) return false end - doRelocate(position, relPos) - + Tile(item:getPosition()):relocateTo(relPos) if item:getId() == 293 then item:transform(294) item:decay() diff --git a/data/talkactions/scripts/reload.lua b/data/talkactions/scripts/reload.lua new file mode 100644 index 0000000..1dd9c14 --- /dev/null +++ b/data/talkactions/scripts/reload.lua @@ -0,0 +1,65 @@ +local reloadTypes = { + ["all"] = { targetType = RELOAD_TYPE_ALL, name = "all" }, + + ["action"] = { targetType = RELOAD_TYPE_ACTIONS, name = "actions" }, + ["actions"] = { targetType = RELOAD_TYPE_ACTIONS, name = "actions" }, + + ["chat"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" }, + ["channel"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" }, + ["chatchannels"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" }, + + ["config"] = { targetType = RELOAD_TYPE_CONFIG, name = "config" }, + ["configuration"] = { targetType = RELOAD_TYPE_CONFIG, name = "config" }, + + ["creaturescript"] = { targetType = RELOAD_TYPE_CREATURESCRIPTS, name = "creature scripts" }, + ["creaturescripts"] = { targetType = RELOAD_TYPE_CREATURESCRIPTS, name = "creature scripts" }, + + ["events"] = { targetType = RELOAD_TYPE_EVENTS, name = "events" }, + + ["global"] = { targetType = RELOAD_TYPE_GLOBAL, name = "global.lua" }, + + ["globalevent"] = { targetType = RELOAD_TYPE_GLOBALEVENTS, name = "globalevents" }, + ["globalevents"] = { targetType = RELOAD_TYPE_GLOBALEVENTS, name = "globalevents" }, + + ["items"] = { targetType = RELOAD_TYPE_ITEMS, name = "items" }, + + ["monster"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" }, + ["monsters"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" }, + + ["move"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" }, + ["movement"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" }, + ["movements"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" }, + + ["npc"] = { targetType = RELOAD_TYPE_NPCS, name = "npcs" }, + ["npcs"] = { targetType = RELOAD_TYPE_NPCS, name = "npcs" }, + + ["raid"] = { targetType = RELOAD_TYPE_RAIDS, name = "raids" }, + ["raids"] = { targetType = RELOAD_TYPE_RAIDS, name = "raids" }, + + ["spell"] = { targetType = RELOAD_TYPE_SPELLS, name = "spells" }, + ["spells"] = { targetType = RELOAD_TYPE_SPELLS, name = "spells" }, + + ["talk"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" }, + ["talkaction"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" }, + ["talkactions"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" } +} + +function onSay(player, words, param) + if not player:getGroup():getAccess() then + return true + end + + if player:getAccountType() < ACCOUNT_TYPE_GAMEMASTER then + return false + end + + local reloadType = reloadTypes[param and param:lower()] + if not reloadType then + player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Reload type not found.") + return false + end + + Game.reload(reloadType.targetType) + player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Reloaded %s.", reloadType.name)) + return true +end \ No newline at end of file diff --git a/data/talkactions/scripts/save.lua b/data/talkactions/scripts/save.lua new file mode 100644 index 0000000..d704c4c --- /dev/null +++ b/data/talkactions/scripts/save.lua @@ -0,0 +1,23 @@ +function onSay(player, words, param) + + local function timeSave(delay, msg) + broadcastMessage(msg, MESSAGE_STATUS_WARNING) + player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg) + addEvent(saveServer, delay) + end + + if not player:getGroup():getAccess() then + return true + end + + if player:getAccountType() < ACCOUNT_TYPE_GOD then + return false + end + + if isNumber(param) then + local delay = tonumber(param) * 1000 + timeSave(delay, "Saving server in " .. tonumber(param) .. " seconds.") + else + saveServer() + end +end \ No newline at end of file diff --git a/data/talkactions/scripts/sellhouse.lua b/data/talkactions/scripts/sellhouse.lua new file mode 100644 index 0000000..9920a8a --- /dev/null +++ b/data/talkactions/scripts/sellhouse.lua @@ -0,0 +1,19 @@ +function onSay(player, words, param) + local tradePartner = Player(param) + if not tradePartner or tradePartner == player then + player:sendCancelMessage("Trade player not found.") + return false + end + + local house = player:getTile():getHouse() + if not house then + player:sendCancelMessage("You must stand in your house to initiate the trade.") + return false + end + + local returnValue = house:startTrade(player, tradePartner) + if returnValue ~= RETURNVALUE_NOERROR then + player:sendCancelMessage(returnValue) + end + return false +end \ No newline at end of file diff --git a/data/talkactions/talkactions.xml b/data/talkactions/talkactions.xml index ad15b22..ac214b4 100644 --- a/data/talkactions/talkactions.xml +++ b/data/talkactions/talkactions.xml @@ -27,7 +27,9 @@ - + + + @@ -37,6 +39,7 @@ + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ccc19f..76c0cad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,6 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/behaviourdatabase.cpp ${CMAKE_CURRENT_LIST_DIR}/chat.cpp ${CMAKE_CURRENT_LIST_DIR}/combat.cpp - ${CMAKE_CURRENT_LIST_DIR}/commands.cpp ${CMAKE_CURRENT_LIST_DIR}/condition.cpp ${CMAKE_CURRENT_LIST_DIR}/configmanager.cpp ${CMAKE_CURRENT_LIST_DIR}/connection.cpp @@ -19,6 +18,7 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/databasemanager.cpp ${CMAKE_CURRENT_LIST_DIR}/databasetasks.cpp ${CMAKE_CURRENT_LIST_DIR}/depotlocker.cpp + ${CMAKE_CURRENT_LIST_DIR}/events.cpp ${CMAKE_CURRENT_LIST_DIR}/fileloader.cpp ${CMAKE_CURRENT_LIST_DIR}/game.cpp ${CMAKE_CURRENT_LIST_DIR}/globalevent.cpp diff --git a/src/account.h b/src/account.h index a7741f4..5bdc53a 100644 --- a/src/account.h +++ b/src/account.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/actions.cpp b/src/actions.cpp index 748bcab..439748d 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/actions.h b/src/actions.h index 02c4924..f4ae6d9 100644 --- a/src/actions.h +++ b/src/actions.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/ban.cpp b/src/ban.cpp index a531ea9..547ad29 100644 --- a/src/ban.cpp +++ b/src/ban.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/ban.h b/src/ban.h index 0ca2360..86535a6 100644 --- a/src/ban.h +++ b/src/ban.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/baseevents.cpp b/src/baseevents.cpp index f8191fc..c99243e 100644 --- a/src/baseevents.cpp +++ b/src/baseevents.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/baseevents.h b/src/baseevents.h index 3529a6a..c5b528f 100644 --- a/src/baseevents.h +++ b/src/baseevents.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/bed.cpp b/src/bed.cpp index a2f55aa..90629a9 100644 --- a/src/bed.cpp +++ b/src/bed.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/bed.h b/src/bed.h index b45486d..f1f836d 100644 --- a/src/bed.h +++ b/src/bed.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/behaviourdatabase.cpp b/src/behaviourdatabase.cpp index 7ddc588..e331abb 100644 --- a/src/behaviourdatabase.cpp +++ b/src/behaviourdatabase.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator -* Copyright (C) 2017 Alejandro Mujica +* Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -820,10 +820,6 @@ void BehaviourDatabase::checkAction(const NpcBehaviourAction* action, Player* pl } } while (amount); } else { - if (it.charges) { - data = it.charges; - } - for (int32_t i = 0; i < std::max(1, amount); i++) { Item* item = Item::CreateItem(itemId, data); if (!item) { @@ -937,7 +933,7 @@ void BehaviourDatabase::checkAction(const NpcBehaviourAction* action, Player* pl } case BEHAVIOUR_TYPE_EXPERIENCE: { int32_t experience = evaluate(action->expression, player, message); - player->addExperience(experience, true, false); + player->addExperience(nullptr, experience, false); break; } case BEHAVIOUR_TYPE_WITHDRAW: { diff --git a/src/behaviourdatabase.h b/src/behaviourdatabase.h index 73e1856..de7eca6 100644 --- a/src/behaviourdatabase.h +++ b/src/behaviourdatabase.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator -* Copyright (C) 2017 Alejandro Mujica +* Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/chat.cpp b/src/chat.cpp index bee075e..59973c8 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/chat.h b/src/chat.h index 3d7b973..e181142 100644 --- a/src/chat.h +++ b/src/chat.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/combat.cpp b/src/combat.cpp index 2e62e91..52bc2da 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -24,13 +24,16 @@ #include "game.h" #include "configmanager.h" #include "monster.h" +#include "events.h" extern Game g_game; extern ConfigManager g_config; +extern Events* g_events; CombatDamage Combat::getCombatDamage(Creature* creature) const { CombatDamage damage; + damage.origin = params.origin; damage.type = params.combatType; if (formulaType == COMBAT_FORMULA_DAMAGE) { damage.min = static_cast(mina); @@ -175,11 +178,7 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive) return RETURNVALUE_NOTENOUGHROOM; } - /*if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && tile->hasProperty(CONST_PROP_UNLAY)) { - return RETURNVALUE_NOTENOUGHROOM; - }*/ - - if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH) && tile->hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) { + if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && tile->hasProperty(CONST_PROP_UNLAY)) { return RETURNVALUE_NOTENOUGHROOM; } @@ -192,7 +191,8 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive) const Position& tilePosition = tile->getPosition(); if (casterPosition.z < tilePosition.z) { return RETURNVALUE_FIRSTGODOWNSTAIRS; - } else if (casterPosition.z > tilePosition.z) { + } + else if (casterPosition.z > tilePosition.z) { return RETURNVALUE_FIRSTGOUPSTAIRS; } @@ -208,7 +208,7 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive) return RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE; } - return RETURNVALUE_NOERROR; + return g_events->eventCreatureOnAreaCombat(caster, tile, aggressive); } bool Combat::isInPvpZone(const Creature* attacker, const Creature* target) @@ -232,74 +232,78 @@ bool Combat::isProtected(const Player* attacker, const Player* target) ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target) { - if (attacker) { - if (const Player* targetPlayer = target->getPlayer()) { - if (targetPlayer->hasFlag(PlayerFlag_CannotBeAttacked)) { + if (!attacker) { + return g_events->eventCreatureOnTargetCombat(attacker, target); + } + + if (const Player* targetPlayer = target->getPlayer()) { + if (targetPlayer->hasFlag(PlayerFlag_CannotBeAttacked)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; + } + + if (const Player* attackerPlayer = attacker->getPlayer()) { + if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } - if (const Player* attackerPlayer = attacker->getPlayer()) { - if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; - } - - if (isProtected(attackerPlayer, targetPlayer)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; - } - - //nopvp-zone - const Tile* targetPlayerTile = targetPlayer->getTile(); - if (targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE)) { - return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; - } else if (attackerPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE) && !targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE | TILESTATE_PROTECTIONZONE)) { - return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; - } + if (isProtected(attackerPlayer, targetPlayer)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } - if (attacker->isSummon()) { - if (const Player* masterAttackerPlayer = attacker->getMaster()->getPlayer()) { - if (masterAttackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; - } - - if (targetPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE)) { - return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; - } - - if (isProtected(masterAttackerPlayer, targetPlayer)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; - } - } + //nopvp-zone + const Tile* targetPlayerTile = targetPlayer->getTile(); + if (targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE)) { + return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; } - } else if (target->getMonster()) { - if (const Player* attackerPlayer = attacker->getPlayer()) { - if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; - } - - if (target->isSummon() && target->getMaster()->getPlayer() && target->getZone() == ZONE_NOPVP) { - return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; - } + else if (attackerPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE) && !targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE | TILESTATE_PROTECTIONZONE)) { + return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; } } - if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) { - if (attacker->getPlayer() || (attacker->isSummon() && attacker->getMaster()->getPlayer())) { - if (target->getPlayer()) { - if (!isInPvpZone(attacker, target)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; - } + if (attacker->isSummon()) { + if (const Player* masterAttackerPlayer = attacker->getMaster()->getPlayer()) { + if (masterAttackerPlayer->hasFlag(PlayerFlag_CannotAttackPlayer)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } - if (target->isSummon() && target->getMaster()->getPlayer()) { - if (!isInPvpZone(attacker, target)) { - return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; - } + if (targetPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE)) { + return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; + } + + if (isProtected(masterAttackerPlayer, targetPlayer)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } } } } - return RETURNVALUE_NOERROR; + else if (target->getMonster()) { + if (const Player* attackerPlayer = attacker->getPlayer()) { + if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; + } + + if (target->isSummon() && target->getMaster()->getPlayer() && target->getZone() == ZONE_NOPVP) { + return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; + } + } + } + + if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) { + if (attacker->getPlayer() || (attacker->isSummon() && attacker->getMaster()->getPlayer())) { + if (target->getPlayer()) { + if (!isInPvpZone(attacker, target)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; + } + } + + if (target->isSummon() && target->getMaster()->getPlayer()) { + if (!isInPvpZone(attacker, target)) { + return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; + } + } + } + } + return g_events->eventCreatureOnTargetCombat(attacker, target); } void Combat::setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb) @@ -422,7 +426,7 @@ CallBack* Combat::getCallback(CallBackParam_t key) return nullptr; } -bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) +void Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) { assert(data); CombatDamage damage = *data; @@ -439,18 +443,16 @@ bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatPa } if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) { - return false; + return; } if (g_game.combatChangeHealth(caster, target, damage)) { - CombatConditionFunc(caster, target, params, nullptr); + CombatConditionFunc(caster, target, params, &damage); CombatDispelFunc(caster, target, params, nullptr); } - - return true; } -bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) +void Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) { assert(data); CombatDamage damage = *data; @@ -465,16 +467,18 @@ bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatPara } } - if (g_game.combatChangeMana(caster, target, damage.value)) { + if (g_game.combatChangeMana(caster, target, damage)) { CombatConditionFunc(caster, target, params, nullptr); CombatDispelFunc(caster, target, params, nullptr); } - - return true; } -bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*) +void Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) { + if (params.origin == ORIGIN_MELEE && data && data->value == 0) { + return; + } + for (const auto& condition : params.conditionList) { if (caster == target || !target->isImmune(condition->getType())) { Condition* conditionCopy = condition->clone(); @@ -486,21 +490,17 @@ bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const Comba target->addCombatCondition(conditionCopy); } } - - return true; } -bool Combat::CombatDispelFunc(Creature*, Creature* target, const CombatParams& params, CombatDamage*) +void Combat::CombatDispelFunc(Creature*, Creature* target, const CombatParams& params, CombatDamage*) { target->removeCombatCondition(params.dispelType); - return true; } -bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*) +void Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage*) { CombatConditionFunc(caster, target, params, nullptr); CombatDispelFunc(caster, target, params, nullptr); - return true; } void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params) @@ -632,6 +632,8 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* const int32_t rangeY = maxY + Map::maxViewportY; g_game.map.getSpectators(list, pos, true, true, rangeX, rangeX, rangeY, rangeY); + postCombatEffects(caster, pos, params); + uint16_t decreasedDamage = 0; const uint16_t maximumDecreasedDamage = params.maximumDecreasedDamage; @@ -721,6 +723,8 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* continue; } + combatTileEffects(list, caster, tile, params); + if (CreatureVector* creatures = tile->getCreatures()) { const Creature* topCreature = tile->getTopCreature(); for (Creature* creature : *creatures) { @@ -746,9 +750,7 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* } } } - combatTileEffects(list, caster, tile, params); } - postCombatEffects(caster, pos, params); } void Combat::doCombat(Creature* caster, Creature* target) const @@ -834,7 +836,7 @@ bool Combat::attack(Creature* attacker, Creature* target) return false; } -bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode, bool fist) +bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode) { const Position& attackerPos = attacker->getPosition(); const Position& targetPos = target->getPosition(); @@ -859,7 +861,7 @@ bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fight uint32_t skillValue = 0; uint8_t skill = SKILL_FIST; - Combat::getAttackValue(attacker, attackValue, skillValue, skill, fist); + Combat::getAttackValue(attacker, attackValue, skillValue, skill); int32_t defense = target->getDefense(); @@ -876,6 +878,7 @@ bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fight combatDamage.type = combatParams.combatType; int32_t totalDamage = Combat::getTotalDamage(skillValue, attackValue, fightMode); combatDamage.value = totalDamage; + combatDamage.origin = ORIGIN_MELEE; bool hit = Combat::doCombatHealth(attacker, target, combatDamage, combatParams); @@ -884,7 +887,7 @@ bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fight if (poison) { int32_t randTest = rand(); - if (hit || -totalDamage > defense && (randTest == 5 * (randTest / 5))) { + if (hit || ((-totalDamage > defense) && (randTest == 5 * (randTest / 5)))) { poison = normal_random(poison / 2, poison); if (poison) { ConditionDamage* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_POISON, 0, 0)); @@ -962,9 +965,9 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight if (weapon->getWeaponType() == WEAPON_DISTANCE) { ammunition = player->getAmmunition(); if (weapon->getAmmoType() != AMMO_NONE) { - if (!ammunition || ammunition->getWeaponType() != WEAPON_AMMO || weapon->getAmmoType() != ammunition->getAmmoType()) { + if (!ammunition || weapon->getAmmoType() != ammunition->getAmmoType()) { // redirect to fist fighting - return closeAttack(attacker, target, fightMode, true); + return closeAttack(attacker, target, fightMode); } distanceEffect = ammunition->getMissileType(); @@ -991,6 +994,7 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight CombatDamage combatDamage; combatDamage.type = combatParams.combatType; combatDamage.value = Combat::getTotalDamage(skillValue, attackValue, fightMode); + combatDamage.origin = ORIGIN_RANGED; if (weapon) { hitChance = 75; // throwables and such @@ -1011,7 +1015,7 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight } } - if (ammunition && ammunition->getWeaponType() == WEAPON_AMMO && weapon->getAmmoType() != AMMO_NONE && weapon->getAmmoType() == ammunition->getAmmoType()) { + if (ammunition && weapon->getAmmoType() != AMMO_NONE && weapon->getAmmoType() == ammunition->getAmmoType()) { hitChance = 90; // bows and crossbows specialEffect = ammunition->getWeaponSpecialEffect(); attackStrength = ammunition->getAttackStrength(); @@ -1037,10 +1041,12 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight bool hit = false; - if (rand() % distance <= skillValue) { - hit = rand() % 100 <= hitChance; + int32_t random = rand(); + if (random % distance <= static_cast(skillValue)) { + hit = random % 100 <= hitChance; } + if (Player* player = attacker->getPlayer()) { if (player->getAddAttackSkill()) { switch (player->getLastAttackBlockType()) { @@ -1130,6 +1136,7 @@ bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fight CombatDamage combatDamage; combatDamage.type = combatParams.combatType; combatDamage.value = -(variation + weapon->getAttackStrength()); + combatDamage.origin = ORIGIN_RANGED; g_game.addDistanceEffect(attackerPos, targetPos, distanceEffect); Combat::doCombatHealth(attacker, target, combatDamage, combatParams); @@ -1182,13 +1189,13 @@ void Combat::circleShapeSpell(Creature* attacker, const Position& toPos, int32_t } } -void Combat::getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill, bool fist) +void Combat::getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill) { skill = SKILL_FIST; if (Player* player = creature->getPlayer()) { Item* weapon = player->getWeapon(); - if (weapon && !fist) { + if (weapon) { switch (weapon->getWeaponType()) { case WEAPON_AXE: { skill = SKILL_AXE; @@ -1243,7 +1250,7 @@ bool Combat::canUseWeapon(Player* player, Item* weapon) return false; } - if (!player->hasFlag(PlayerFlag_HasInfiniteMana) && player->getMana() < weapon->getManaConsumption()) { + if (!player->hasFlag(PlayerFlag_HasInfiniteMana) && static_cast(player->getMana()) < weapon->getManaConsumption()) { return false; } @@ -1278,14 +1285,14 @@ bool Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da } if (canCombat) { - canCombat = CombatHealthFunc(caster, target, params, &damage); - if (params.targetCallback) { - params.targetCallback->onTargetCombat(caster, target); - } - if (caster && params.distanceEffect != CONST_ANI_NONE) { addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); } + + CombatHealthFunc(caster, target, params, &damage); + if (params.targetCallback) { + params.targetCallback->onTargetCombat(caster, target); + } } return canCombat; @@ -1304,14 +1311,14 @@ void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& dama } if (canCombat) { + if (caster && params.distanceEffect != CONST_ANI_NONE) { + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); + } + CombatManaFunc(caster, target, params, &damage); if (params.targetCallback) { params.targetCallback->onTargetCombat(caster, target); } - - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); - } } } @@ -1333,14 +1340,14 @@ void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatP } if (canCombat) { + if (caster && params.distanceEffect != CONST_ANI_NONE) { + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); + } + CombatConditionFunc(caster, target, params, nullptr); if (params.targetCallback) { params.targetCallback->onTargetCombat(caster, target); } - - if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); - } } } diff --git a/src/combat.h b/src/combat.h index bc07037..a6b6b70 100644 --- a/src/combat.h +++ b/src/combat.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -73,6 +73,7 @@ struct CombatParams { ConditionType_t dispelType = CONDITION_NONE; CombatType_t combatType = COMBAT_NONE; + CombatOrigin origin = ORIGIN_SPELL; uint8_t impactEffect = CONST_ME_NONE; uint8_t distanceEffect = CONST_ANI_NONE; @@ -116,7 +117,7 @@ struct DunkenImpact : Impact void handleCreature(Creature* target) final; }; -typedef bool (*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*); +typedef void(*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*); class MatrixArea { @@ -285,12 +286,12 @@ class Combat static int32_t getTotalDamage(int32_t attackSkill, int32_t attackValue, fightMode_t fightMode); static bool attack(Creature* attacker, Creature* target); - static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode, bool fist = false); + static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode); static bool rangeAttack(Creature* attacker, Creature* target, fightMode_t fightMode); static void circleShapeSpell(Creature* attacker, const Position& toPos, int32_t range, int32_t animation, int32_t radius, DamageImpact* impact, int32_t effect); - static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill, bool fist = false); + static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill); static bool doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); static void doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); @@ -339,6 +340,10 @@ class Combat postCombatEffects(caster, pos, params); } + void setOrigin(CombatOrigin origin) { + params.origin = origin; + } + protected: static bool canUseWeapon(Player* player, Item* weapon); static void postWeaponEffects(Player* player, Item* weapon); @@ -347,11 +352,11 @@ class Combat static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, COMBATFUNC func, CombatDamage* data); - static bool CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static bool CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage); - static bool CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static bool CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static bool CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); + static void CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); + static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage); + static void CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); + static void CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); + static void CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params); CombatDamage getCombatDamage(Creature* creature) const; diff --git a/src/commands.cpp b/src/commands.cpp index 0e42920..83db35e 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/commands.h b/src/commands.h index 2be9ac1..828ef8a 100644 --- a/src/commands.h +++ b/src/commands.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/condition.cpp b/src/condition.cpp index e07d8d5..13b02cd 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -227,7 +227,7 @@ bool Condition::isPersistent() const return false; } - if (!(id == CONDITIONID_DEFAULT || id == CONDITIONID_COMBAT)) { + if (!(id == CONDITIONID_DEFAULT || id == CONDITIONID_COMBAT || conditionType == CONDITION_MUTED)) { return false; } @@ -655,7 +655,9 @@ bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interva if (internalManaTicks >= manaTicks) { internalManaTicks = 0; - creature->changeMana(manaGain); + if (Player* player = creature->getPlayer()) { + player->changeMana(manaGain); + } } } @@ -756,8 +758,6 @@ bool ConditionSoul::setParam(ConditionParam_t param, int32_t value) bool ConditionDamage::setParam(ConditionParam_t param, int32_t value) { - Condition::setParam(param, value); - switch (param) { case CONDITION_PARAM_OWNER: owner = value; @@ -927,6 +927,7 @@ bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange) } CombatDamage damage; + damage.origin = ORIGIN_CONDITION; damage.value = healthChange; damage.type = Combat::ConditionToDamageType(conditionType); diff --git a/src/condition.h b/src/condition.h index efee824..2535aa0 100644 --- a/src/condition.h +++ b/src/condition.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/configmanager.cpp b/src/configmanager.cpp index d1e60c4..2993702 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -77,7 +77,9 @@ bool ConfigManager::load() boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true); boolean[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true); boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false); - boolean[QUERY_PLAYER_CONTAINERS] = getGlobalBoolean(L, "queryPlayerContainers", false); + boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false); + boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false); + string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high"); string[SERVER_NAME] = getGlobalString(L, "serverName", ""); diff --git a/src/configmanager.h b/src/configmanager.h index f2105fe..6c14b68 100644 --- a/src/configmanager.h +++ b/src/configmanager.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -40,7 +40,8 @@ class ConfigManager CONVERT_UNSAFE_SCRIPTS, TELEPORT_NEWBIES, STACK_CUMULATIVES, - QUERY_PLAYER_CONTAINERS, + BLOCK_HEIGHT, + DROP_ITEMS, LAST_BOOLEAN_CONFIG /* this must be the last one */ }; diff --git a/src/connection.cpp b/src/connection.cpp index 5f21835..859066c 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/connection.h b/src/connection.h index 34c6ac9..b885749 100644 --- a/src/connection.h +++ b/src/connection.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/const.h b/src/const.h index afefe77..8490b1f 100644 --- a/src/const.h +++ b/src/const.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -308,6 +308,28 @@ enum PlayerFlags : uint64_t { PlayerFlag_SpecialMoveUse = static_cast(1) << 38, }; +enum ReloadTypes_t : uint8_t { + RELOAD_TYPE_ALL, + RELOAD_TYPE_ACTIONS, + RELOAD_TYPE_CHAT, + RELOAD_TYPE_COMMANDS, + RELOAD_TYPE_CONFIG, + RELOAD_TYPE_CREATURESCRIPTS, + RELOAD_TYPE_EVENTS, + RELOAD_TYPE_GLOBAL, + RELOAD_TYPE_GLOBALEVENTS, + RELOAD_TYPE_ITEMS, + RELOAD_TYPE_MONSTERS, + RELOAD_TYPE_MOUNTS, + RELOAD_TYPE_MOVEMENTS, + RELOAD_TYPE_NPCS, + RELOAD_TYPE_QUESTS, + RELOAD_TYPE_RAIDS, + RELOAD_TYPE_SPELLS, + RELOAD_TYPE_TALKACTIONS, + RELOAD_TYPE_WEAPONS, +}; + static constexpr int32_t CHANNEL_GUILD = 0x00; static constexpr int32_t CHANNEL_PARTY = 0x01; static constexpr int32_t CHANNEL_RULE_REP = 0x02; diff --git a/src/container.cpp b/src/container.cpp index e404695..f949f81 100644 --- a/src/container.cpp +++ b/src/container.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -370,7 +370,7 @@ ReturnValue Container::queryRemove(const Thing& thing, uint32_t count, uint32_t } Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** destItem, - uint32_t& flags) + uint32_t& flags) { if (index == 254 /*move up*/) { index = INDEX_WHEREEVER; @@ -386,7 +386,8 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** if (index == 255 /*add wherever*/) { index = INDEX_WHEREEVER; *destItem = nullptr; - } else if (index >= static_cast(capacity())) { + } + else if (index >= static_cast(capacity())) { /* if you have a container, maximize it to show all 20 slots then you open a bag that is inside the container you will have a bag with 8 slots @@ -403,6 +404,20 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** return this; } + if (index != INDEX_WHEREEVER) { + Item* itemFromIndex = getItemByIndex(index); + if (itemFromIndex) { + *destItem = itemFromIndex; + } + + Cylinder* subCylinder = dynamic_cast(*destItem); + if (subCylinder) { + index = INDEX_WHEREEVER; + *destItem = nullptr; + return subCylinder; + } + } + if (g_config.getBoolean(ConfigManager::STACK_CUMULATIVES)) { bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags); if (autoStack && item->isStackable() && item->getParent() != this) { @@ -419,19 +434,6 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** } } - if (index != INDEX_WHEREEVER) { - Item* itemFromIndex = getItemByIndex(index); - if (itemFromIndex) { - *destItem = itemFromIndex; - } - - Cylinder* subCylinder = dynamic_cast(*destItem); - if (subCylinder) { - index = INDEX_WHEREEVER; - *destItem = nullptr; - return subCylinder; - } - } return this; } diff --git a/src/container.h b/src/container.h index 7af5675..495c92f 100644 --- a/src/container.h +++ b/src/container.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/creature.cpp b/src/creature.cpp index 781c87d..8bd0318 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -619,7 +619,8 @@ void Creature::onDeath() if (lastHitCreature) { lastHitUnjustified = lastHitCreature->onKilledCreature(this); lastHitCreatureMaster = lastHitCreature->getMaster(); - } else { + } + else { lastHitCreatureMaster = nullptr; } @@ -639,8 +640,9 @@ void Creature::onDeath() if (attacker != this) { uint64_t gainExp = getGainedExperience(attacker); - if (Player* player = attacker->getPlayer()) { - Party* party = player->getParty(); + if (Player* attackerPlayer = attacker->getPlayer()) { + attackerPlayer->removeAttacked(getPlayer()); + Party* party = attackerPlayer->getParty(); if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) { attacker = party->getLeader(); } @@ -649,7 +651,8 @@ void Creature::onDeath() auto tmpIt = experienceMap.find(attacker); if (tmpIt == experienceMap.end()) { experienceMap[attacker] = gainExp; - } else { + } + else { tmpIt->second += gainExp; } } @@ -752,15 +755,6 @@ void Creature::changeHealth(int32_t healthChange, bool sendHealthChange/* = true } } -void Creature::changeMana(int32_t manaChange) -{ - if (manaChange > 0) { - mana += std::min(manaChange, getMaxMana() - mana); - } else { - mana = std::max(0, mana + manaChange); - } -} - void Creature::gainHealth(Creature* healer, int32_t healthGain) { changeHealth(healthGain); @@ -778,16 +772,6 @@ void Creature::drainHealth(Creature* attacker, int32_t damage) } } -void Creature::drainMana(Creature* attacker, int32_t manaLoss) -{ - onAttacked(); - changeMana(-manaLoss); - - if (attacker) { - addDamagePoints(attacker, manaLoss); - } -} - BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, bool checkDefense /* = false */, bool checkArmor /* = false */, bool /* field = false */) { diff --git a/src/creature.h b/src/creature.h index 39afb11..994556b 100644 --- a/src/creature.h +++ b/src/creature.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -220,12 +220,6 @@ class Creature : virtual public Thing virtual int32_t getMaxHealth() const { return healthMax; } - uint32_t getMana() const { - return mana; - } - virtual uint32_t getMaxMana() const { - return 0; - } const Outfit_t getCurrentOutfit() const { return currentOutfit; @@ -320,11 +314,9 @@ class Creature : virtual public Thing } virtual void changeHealth(int32_t healthChange, bool sendHealthChange = true); - virtual void changeMana(int32_t manaChange); void gainHealth(Creature* attacker, int32_t healthGain); virtual void drainHealth(Creature* attacker, int32_t damage); - virtual void drainMana(Creature* attacker, int32_t manaLoss); virtual bool challengeCreature(Creature*) { return false; @@ -494,7 +486,6 @@ class Creature : virtual public Thing uint32_t blockTicks = 0; uint32_t lastStepCost = 1; uint32_t baseSpeed = 70; - uint32_t mana = 0; uint32_t latestKillEvent = 0; int32_t varSpeed = 0; int32_t health = 1000; diff --git a/src/creatureevent.cpp b/src/creatureevent.cpp index 990fa64..eb72b28 100644 --- a/src/creatureevent.cpp +++ b/src/creatureevent.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -177,6 +177,10 @@ bool CreatureEvent::configureEvent(const pugi::xml_node& node) type = CREATURE_EVENT_KILL; } else if (tmpStr == "advance") { type = CREATURE_EVENT_ADVANCE; + } else if (tmpStr == "healthchange") { + type = CREATURE_EVENT_HEALTHCHANGE; + } else if (tmpStr == "manachange") { + type = CREATURE_EVENT_MANACHANGE; } else if (tmpStr == "extendedopcode") { type = CREATURE_EVENT_EXTENDED_OPCODE; } else { @@ -213,6 +217,12 @@ std::string CreatureEvent::getScriptEventName() const case CREATURE_EVENT_ADVANCE: return "onAdvance"; + case CREATURE_EVENT_HEALTHCHANGE: + return "onHealthChange"; + + case CREATURE_EVENT_MANACHANGE: + return "onManaChange"; + case CREATURE_EVENT_EXTENDED_OPCODE: return "onExtendedOpcode"; @@ -409,6 +419,85 @@ void CreatureEvent::executeOnKill(Creature* creature, Creature* target) scriptInterface->callVoidFunction(2); } +void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage) +{ + //onHealthChange(creature, attacker, value, type, min, max, origin) + if (!scriptInterface->reserveScriptEnv()) { + std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface->getScriptEnv(); + env->setScriptId(scriptId, scriptInterface); + + lua_State* L = scriptInterface->getLuaState(); + scriptInterface->pushFunction(scriptId); + + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + if (attacker) { + LuaScriptInterface::pushUserdata(L, attacker); + LuaScriptInterface::setCreatureMetatable(L, -1, attacker); + } + else { + lua_pushnil(L); + } + + LuaScriptInterface::pushCombatDamage(L, damage); + + if (scriptInterface->protectedCall(L, 7, 4) != 0) { + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + damage.value = std::abs(LuaScriptInterface::getNumber(L, -4)); + damage.type = LuaScriptInterface::getNumber(L, -3); + damage.min = std::abs(LuaScriptInterface::getNumber(L, -2)); + damage.max = LuaScriptInterface::getNumber(L, -1); + + lua_pop(L, 4); + if (damage.type != COMBAT_HEALING) { + damage.value = -damage.value; + } + } + + scriptInterface->resetScriptEnv(); +} + +void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage) { + //onManaChange(creature, attacker, value, type, min, max, origin) + if (!scriptInterface->reserveScriptEnv()) { + std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface->getScriptEnv(); + env->setScriptId(scriptId, scriptInterface); + + lua_State* L = scriptInterface->getLuaState(); + scriptInterface->pushFunction(scriptId); + + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + if (attacker) { + LuaScriptInterface::pushUserdata(L, attacker); + LuaScriptInterface::setCreatureMetatable(L, -1, attacker); + } + else { + lua_pushnil(L); + } + + LuaScriptInterface::pushCombatDamage(L, damage); + + if (scriptInterface->protectedCall(L, 7, 4) != 0) { + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + damage = LuaScriptInterface::getCombatDamage(L); + } + + scriptInterface->resetScriptEnv(); +} + void CreatureEvent::executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer) { //onExtendedOpcode(player, opcode, buffer) diff --git a/src/creatureevent.h b/src/creatureevent.h index 7617c46..6dbdeac 100644 --- a/src/creatureevent.h +++ b/src/creatureevent.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -33,6 +33,8 @@ enum CreatureEventType_t { CREATURE_EVENT_DEATH, CREATURE_EVENT_KILL, CREATURE_EVENT_ADVANCE, + CREATURE_EVENT_HEALTHCHANGE, + CREATURE_EVENT_MANACHANGE, CREATURE_EVENT_EXTENDED_OPCODE, // otclient additional network opcodes }; @@ -97,6 +99,8 @@ class CreatureEvent final : public Event bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified); void executeOnKill(Creature* creature, Creature* target); bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t); + void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage); + void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage); void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer); // diff --git a/src/cylinder.cpp b/src/cylinder.cpp index 9e9e289..406ca9a 100644 --- a/src/cylinder.cpp +++ b/src/cylinder.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/cylinder.h b/src/cylinder.h index b31765f..872609c 100644 --- a/src/cylinder.h +++ b/src/cylinder.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/database.cpp b/src/database.cpp index 5079e08..0dfc590 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/database.h b/src/database.h index f9ccf57..e0168ba 100644 --- a/src/database.h +++ b/src/database.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/databasemanager.cpp b/src/databasemanager.cpp index 07f2325..dc38b37 100644 --- a/src/databasemanager.cpp +++ b/src/databasemanager.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/databasemanager.h b/src/databasemanager.h index 4592687..d15f1ec 100644 --- a/src/databasemanager.h +++ b/src/databasemanager.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/databasetasks.cpp b/src/databasetasks.cpp index a741e9f..37f1176 100644 --- a/src/databasetasks.cpp +++ b/src/databasetasks.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/databasetasks.h b/src/databasetasks.h index 131a173..42b36e2 100644 --- a/src/databasetasks.h +++ b/src/databasetasks.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/definitions.h b/src/definitions.h index 2dfc2c6..4ab6c38 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -20,9 +20,9 @@ #ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963 #define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963 -static constexpr auto STATUS_SERVER_NAME = "Nostalrius"; -static constexpr auto STATUS_SERVER_VERSION = "3.0"; -static constexpr auto STATUS_SERVER_DEVELOPERS = "Alejandro Mujica"; +static constexpr auto STATUS_SERVER_NAME = "Sabrehaven"; +static constexpr auto STATUS_SERVER_VERSION = "1.0"; +static constexpr auto STATUS_SERVER_DEVELOPERS = "Sabrehaven Developers Team"; static constexpr auto CLIENT_VERSION_MIN = 772; static constexpr auto CLIENT_VERSION_MAX = 772; @@ -57,7 +57,7 @@ static constexpr auto CLIENT_VERSION_STR = "7.72"; #pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data #pragma warning(disable:4351) // new behavior: elements of array will be default initialized #pragma warning(disable:4458) // declaration hides class member -#pragma warning(disable:4018) // signed/unsigned mismatch +#pragma warning(disable:4996) // inetpton warning #endif #define strcasecmp _stricmp diff --git a/src/depotlocker.cpp b/src/depotlocker.cpp index 10f5f15..09189a1 100644 --- a/src/depotlocker.cpp +++ b/src/depotlocker.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/depotlocker.h b/src/depotlocker.h index 9d03f8b..094defc 100644 --- a/src/depotlocker.h +++ b/src/depotlocker.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/enums.h b/src/enums.h index c4a89d7..1ae3739 100644 --- a/src/enums.h +++ b/src/enums.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -345,6 +345,11 @@ enum ReturnValue { RETURNVALUE_CANONLYUSEONESHIELD, RETURNVALUE_NOPARTYMEMBERSINRANGE, RETURNVALUE_YOUARENOTTHEOWNER, + RETURNVALUE_TRADEPLAYERFARAWAY, + RETURNVALUE_YOUDONTOWNTHISHOUSE, + RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE, + RETURNVALUE_TRADEPLAYERHIGHESTBIDDER, + RETURNVALUE_YOUCANNOTTRADETHISHOUSE, }; struct Outfit_t { @@ -363,15 +368,26 @@ struct LightInfo { constexpr LightInfo(uint8_t level, uint8_t color) : level(level), color(color) {} }; +enum CombatOrigin +{ + ORIGIN_NONE, + ORIGIN_CONDITION, + ORIGIN_SPELL, + ORIGIN_MELEE, + ORIGIN_RANGED, +}; + struct CombatDamage { CombatType_t type; int32_t value; int32_t min; int32_t max; + CombatOrigin origin; CombatDamage() { + origin = ORIGIN_NONE; type = COMBAT_NONE; value = 0; min = 0; diff --git a/src/events.cpp b/src/events.cpp new file mode 100644 index 0000000..868901e --- /dev/null +++ b/src/events.cpp @@ -0,0 +1,827 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2016 Mark Samman + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "otpch.h" + +#include "events.h" +#include "tools.h" +#include "item.h" +#include "player.h" + +#include + +Events::Events() : + scriptInterface("Event Interface") +{ + clear(); + scriptInterface.initState(); +} + +void Events::clear() +{ + // Creature + creatureOnChangeOutfit = -1; + creatureOnAreaCombat = -1; + creatureOnTargetCombat = -1; + + // Party + partyOnJoin = -1; + partyOnLeave = -1; + partyOnDisband = -1; + + // Player + playerOnLook = -1; + playerOnLookInBattleList = -1; + playerOnLookInTrade = -1; + playerOnMoveItem = -1; + playerOnItemMoved = -1; + playerOnMoveCreature = -1; + playerOnTurn = -1; + playerOnTradeRequest = -1; + playerOnTradeAccept = -1; + playerOnGainExperience = -1; + playerOnLoseExperience = -1; + playerOnGainSkillTries = -1; + playerOnReportBug = -1; + +} + +bool Events::load() +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file("data/events/events.xml"); + if (!result) { + printXMLError("Error - Events::load", "data/events/events.xml", result); + return false; + } + + std::set classes; + for (auto eventNode : doc.child("events").children()) { + if (!eventNode.attribute("enabled").as_bool()) { + continue; + } + + const std::string& className = eventNode.attribute("class").as_string(); + auto res = classes.insert(className); + if (res.second) { + const std::string& lowercase = asLowerCaseString(className); + if (scriptInterface.loadFile("data/events/scripts/" + lowercase + ".lua") != 0) { + std::cout << "[Warning - Events::load] Can not load script: " << lowercase << ".lua" << std::endl; + std::cout << scriptInterface.getLastLuaError() << std::endl; + } + } + + const std::string& methodName = eventNode.attribute("method").as_string(); + const int32_t event = scriptInterface.getMetaEvent(className, methodName); + if (className == "Creature") { + if (methodName == "onChangeOutfit") { + creatureOnChangeOutfit = event; + } + else if (methodName == "onAreaCombat") { + creatureOnAreaCombat = event; + } + else if (methodName == "onTargetCombat") { + creatureOnTargetCombat = event; + } + else { + std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl; + } + } + else if (className == "Party") { + if (methodName == "onJoin") { + partyOnJoin = event; + } + else if (methodName == "onLeave") { + partyOnLeave = event; + } + else if (methodName == "onDisband") { + partyOnDisband = event; + } + else if (methodName == "onShareExperience") { + partyOnShareExperience = event; + } + else { + std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl; + } + } + else if (className == "Player") { + if (methodName == "onLook") { + playerOnLook = event; + } + else if (methodName == "onLookInBattleList") { + playerOnLookInBattleList = event; + } + else if (methodName == "onLookInTrade") { + playerOnLookInTrade = event; + } + else if (methodName == "onTradeRequest") { + playerOnTradeRequest = event; + } + else if (methodName == "onTradeAccept") { + playerOnTradeAccept = event; + } + else if (methodName == "onMoveItem") { + playerOnMoveItem = event; + } + else if (methodName == "onItemMoved") { + playerOnItemMoved = event; + } + else if (methodName == "onMoveCreature") { + playerOnMoveCreature = event; + } + else if (methodName == "onReportBug") { + playerOnReportBug = event; + } + else if (methodName == "onTurn") { + playerOnTurn = event; + } + else if (methodName == "onGainExperience") { + playerOnGainExperience = event; + } + else if (methodName == "onLoseExperience") { + playerOnLoseExperience = event; + } + else if (methodName == "onGainSkillTries") { + playerOnGainSkillTries = event; + } + else { + std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl; + } + } + else { + std::cout << "[Warning - Events::load] Unknown class: " << className << std::endl; + } + } + return true; +} + +// Creature +bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit) +{ + // Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit) + if (creatureOnChangeOutfit == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventCreatureOnChangeOutfit] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(creatureOnChangeOutfit, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(creatureOnChangeOutfit); + + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + + LuaScriptInterface::pushOutfit(L, outfit); + + return scriptInterface.callFunction(2); +} + +ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive) +{ + // Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive) + if (creatureOnAreaCombat == -1) { + return RETURNVALUE_NOERROR; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventCreatureOnAreaCombat] Call stack overflow" << std::endl; + return RETURNVALUE_NOTPOSSIBLE; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(creatureOnAreaCombat, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(creatureOnAreaCombat); + + if (creature) { + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + } + else { + lua_pushnil(L); + } + + LuaScriptInterface::pushUserdata(L, tile); + LuaScriptInterface::setMetatable(L, -1, "Tile"); + + LuaScriptInterface::pushBoolean(L, aggressive); + + ReturnValue returnValue; + if (scriptInterface.protectedCall(L, 3, 1) != 0) { + returnValue = RETURNVALUE_NOTPOSSIBLE; + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + returnValue = LuaScriptInterface::getNumber(L, -1); + lua_pop(L, 1); + } + + scriptInterface.resetScriptEnv(); + return returnValue; +} + +ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* target) +{ + // Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target) + if (creatureOnTargetCombat == -1) { + return RETURNVALUE_NOERROR; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventCreatureOnTargetCombat] Call stack overflow" << std::endl; + return RETURNVALUE_NOTPOSSIBLE; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(creatureOnTargetCombat, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(creatureOnTargetCombat); + + if (creature) { + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + } + else { + lua_pushnil(L); + } + + LuaScriptInterface::pushUserdata(L, target); + LuaScriptInterface::setCreatureMetatable(L, -1, target); + + ReturnValue returnValue; + if (scriptInterface.protectedCall(L, 2, 1) != 0) { + returnValue = RETURNVALUE_NOTPOSSIBLE; + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + returnValue = LuaScriptInterface::getNumber(L, -1); + lua_pop(L, 1); + } + + scriptInterface.resetScriptEnv(); + return returnValue; +} + +// Party +bool Events::eventPartyOnJoin(Party* party, Player* player) +{ + // Party:onJoin(player) or Party.onJoin(self, player) + if (partyOnJoin == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPartyOnJoin] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(partyOnJoin, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(partyOnJoin); + + LuaScriptInterface::pushUserdata(L, party); + LuaScriptInterface::setMetatable(L, -1, "Party"); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + return scriptInterface.callFunction(2); +} + +bool Events::eventPartyOnLeave(Party* party, Player* player) +{ + // Party:onLeave(player) or Party.onLeave(self, player) + if (partyOnLeave == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPartyOnLeave] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(partyOnLeave, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(partyOnLeave); + + LuaScriptInterface::pushUserdata(L, party); + LuaScriptInterface::setMetatable(L, -1, "Party"); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + return scriptInterface.callFunction(2); +} + +bool Events::eventPartyOnDisband(Party* party) +{ + // Party:onDisband() or Party.onDisband(self) + if (partyOnDisband == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPartyOnDisband] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(partyOnDisband, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(partyOnDisband); + + LuaScriptInterface::pushUserdata(L, party); + LuaScriptInterface::setMetatable(L, -1, "Party"); + + return scriptInterface.callFunction(1); +} + +void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) +{ + // Party:onShareExperience(exp) or Party.onShareExperience(self, exp) + if (partyOnShareExperience == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPartyOnShareExperience] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(partyOnShareExperience, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(partyOnShareExperience); + + LuaScriptInterface::pushUserdata(L, party); + LuaScriptInterface::setMetatable(L, -1, "Party"); + + lua_pushnumber(L, exp); + + if (scriptInterface.protectedCall(L, 2, 1) != 0) { + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + exp = LuaScriptInterface::getNumber(L, -1); + lua_pop(L, 1); + } + + scriptInterface.resetScriptEnv(); +} + +void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance) +{ + // Player:onLook(thing, position, distance) or Player.onLook(self, thing, position, distance) + if (playerOnLook == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnLook] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnLook, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnLook); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + if (Creature* creature = thing->getCreature()) { + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + } + else if (Item* item = thing->getItem()) { + LuaScriptInterface::pushUserdata(L, item); + LuaScriptInterface::setItemMetatable(L, -1, item); + } + else { + lua_pushnil(L); + } + + LuaScriptInterface::pushPosition(L, position, stackpos); + lua_pushnumber(L, lookDistance); + + scriptInterface.callVoidFunction(4); +} + +void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance) +{ + // Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance) + if (playerOnLookInBattleList == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnLookInBattleList] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnLookInBattleList, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnLookInBattleList); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + + lua_pushnumber(L, lookDistance); + + scriptInterface.callVoidFunction(3); +} + +void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance) +{ + // Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance) + if (playerOnLookInTrade == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnLookInTrade] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnLookInTrade, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnLookInTrade); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, partner); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, item); + LuaScriptInterface::setItemMetatable(L, -1, item); + + lua_pushnumber(L, lookDistance); + + scriptInterface.callVoidFunction(4); +} + +bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) +{ + // Player:onMoveItem(item, count, fromPosition, toPosition) or Player.onMoveItem(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) + if (playerOnMoveItem == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnMoveItem] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnMoveItem, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnMoveItem); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, item); + LuaScriptInterface::setItemMetatable(L, -1, item); + + lua_pushnumber(L, count); + LuaScriptInterface::pushPosition(L, fromPosition); + LuaScriptInterface::pushPosition(L, toPosition); + + LuaScriptInterface::pushCylinder(L, fromCylinder); + LuaScriptInterface::pushCylinder(L, toCylinder); + + return scriptInterface.callFunction(7); +} + +void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) +{ + // Player:onItemMoved(item, count, fromPosition, toPosition) or Player.onItemMoved(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) + if (playerOnItemMoved == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnItemMoved] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnItemMoved, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnItemMoved); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, item); + LuaScriptInterface::setItemMetatable(L, -1, item); + + lua_pushnumber(L, count); + LuaScriptInterface::pushPosition(L, fromPosition); + LuaScriptInterface::pushPosition(L, toPosition); + + LuaScriptInterface::pushCylinder(L, fromCylinder); + LuaScriptInterface::pushCylinder(L, toCylinder); + + scriptInterface.callVoidFunction(7); +} + +bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition) +{ + // Player:onMoveCreature(creature, fromPosition, toPosition) or Player.onMoveCreature(self, creature, fromPosition, toPosition) + if (playerOnMoveCreature == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnMoveCreature] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnMoveCreature, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnMoveCreature); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); + + LuaScriptInterface::pushPosition(L, fromPosition); + LuaScriptInterface::pushPosition(L, toPosition); + + return scriptInterface.callFunction(4); +} + +bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position) +{ + // Player:onReportBug(message, position, category) + if (playerOnReportBug == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnReportBug] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnReportBug, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnReportBug); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushString(L, message); + LuaScriptInterface::pushPosition(L, position); + + return scriptInterface.callFunction(3); +} + +bool Events::eventPlayerOnTurn(Player* player, Direction direction) +{ + // Player:onTurn(direction) or Player.onTurn(self, direction) + if (playerOnTurn == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnTurn] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnTurn, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnTurn); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + lua_pushnumber(L, direction); + + return scriptInterface.callFunction(2); +} + +bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* item) +{ + // Player:onTradeRequest(target, item) + if (playerOnTradeRequest == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnTradeRequest] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnTradeRequest, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnTradeRequest); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, target); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, item); + LuaScriptInterface::setItemMetatable(L, -1, item); + + return scriptInterface.callFunction(3); +} + +bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem) +{ + // Player:onTradeAccept(target, item, targetItem) + if (playerOnTradeAccept == -1) { + return true; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnTradeAccept] Call stack overflow" << std::endl; + return false; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnTradeAccept, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnTradeAccept); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, target); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + LuaScriptInterface::pushUserdata(L, item); + LuaScriptInterface::setItemMetatable(L, -1, item); + + LuaScriptInterface::pushUserdata(L, targetItem); + LuaScriptInterface::setItemMetatable(L, -1, targetItem); + + return scriptInterface.callFunction(4); +} + +void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp) +{ + // Player:onGainExperience(source, exp, rawExp) + // rawExp gives the original exp which is not multiplied + if (playerOnGainExperience == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnGainExperience] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnGainExperience, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnGainExperience); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + if (source) { + LuaScriptInterface::pushUserdata(L, source); + LuaScriptInterface::setCreatureMetatable(L, -1, source); + } + else { + lua_pushnil(L); + } + + lua_pushnumber(L, exp); + lua_pushnumber(L, rawExp); + + if (scriptInterface.protectedCall(L, 4, 1) != 0) { + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + exp = LuaScriptInterface::getNumber(L, -1); + lua_pop(L, 1); + } + + scriptInterface.resetScriptEnv(); +} + +void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp) +{ + // Player:onLoseExperience(exp) + if (playerOnLoseExperience == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnLoseExperience] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnLoseExperience, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnLoseExperience); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + lua_pushnumber(L, exp); + + if (scriptInterface.protectedCall(L, 2, 1) != 0) { + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + exp = LuaScriptInterface::getNumber(L, -1); + lua_pop(L, 1); + } + + scriptInterface.resetScriptEnv(); +} + +void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries) +{ + // Player:onGainSkillTries(skill, tries) + if (playerOnGainSkillTries == -1) { + return; + } + + if (!scriptInterface.reserveScriptEnv()) { + std::cout << "[Error - Events::eventPlayerOnGainSkillTries] Call stack overflow" << std::endl; + return; + } + + ScriptEnvironment* env = scriptInterface.getScriptEnv(); + env->setScriptId(playerOnGainSkillTries, &scriptInterface); + + lua_State* L = scriptInterface.getLuaState(); + scriptInterface.pushFunction(playerOnGainSkillTries); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + lua_pushnumber(L, skill); + lua_pushnumber(L, tries); + + if (scriptInterface.protectedCall(L, 3, 1) != 0) { + LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); + } + else { + tries = LuaScriptInterface::getNumber(L, -1); + lua_pop(L, 1); + } + + scriptInterface.resetScriptEnv(); +} \ No newline at end of file diff --git a/src/events.h b/src/events.h new file mode 100644 index 0000000..7582c29 --- /dev/null +++ b/src/events.h @@ -0,0 +1,95 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2016 Mark Samman + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef FS_EVENTS_H_BD444CC0EE167E5777E4C90C766B36DC +#define FS_EVENTS_H_BD444CC0EE167E5777E4C90C766B36DC + +#include "luascript.h" + +class Party; +class ItemType; +class Tile; + +class Events +{ +public: + Events(); + + void clear(); + bool load(); + + // Creature + bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit); + ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive); + ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target); + + // Party + bool eventPartyOnJoin(Party* party, Player* player); + bool eventPartyOnLeave(Party* party, Player* player); + bool eventPartyOnDisband(Party* party); + void eventPartyOnShareExperience(Party* party, uint64_t& exp); + + // Player + void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance); + void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance); + void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance); + bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); + void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); + bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition); + void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation); + bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position); + bool eventPlayerOnTurn(Player* player, Direction direction); + bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item); + bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem); + void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp); + void eventPlayerOnLoseExperience(Player* player, uint64_t& exp); + void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries); + +private: + LuaScriptInterface scriptInterface; + + // Creature + int32_t creatureOnChangeOutfit; + int32_t creatureOnAreaCombat; + int32_t creatureOnTargetCombat; + + // Party + int32_t partyOnJoin; + int32_t partyOnLeave; + int32_t partyOnDisband; + int32_t partyOnShareExperience; + + // Player + int32_t playerOnLook; + int32_t playerOnLookInBattleList; + int32_t playerOnLookInTrade; + int32_t playerOnMoveItem; + int32_t playerOnItemMoved; + int32_t playerOnMoveCreature; + int32_t playerOnReportRuleViolation; + int32_t playerOnReportBug; + int32_t playerOnTurn; + int32_t playerOnTradeRequest; + int32_t playerOnTradeAccept; + int32_t playerOnGainExperience; + int32_t playerOnLoseExperience; + int32_t playerOnGainSkillTries; +}; + +#endif \ No newline at end of file diff --git a/src/fileloader.cpp b/src/fileloader.cpp index d37955f..be25ecf 100644 --- a/src/fileloader.cpp +++ b/src/fileloader.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/fileloader.h b/src/fileloader.h index 5ce9c5b..74872b0 100644 --- a/src/fileloader.h +++ b/src/fileloader.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/game.cpp b/src/game.cpp index 1c6d3d6..1d587b6 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -22,9 +22,9 @@ #include "pugicast.h" #include "items.h" -#include "commands.h" #include "creature.h" #include "monster.h" +#include "events.h" #include "game.h" #include "actions.h" #include "iologindata.h" @@ -36,6 +36,7 @@ #include "bed.h" #include "scheduler.h" #include "databasetasks.h" +#include "movement.h" extern ConfigManager g_config; extern Actions* g_actions; @@ -44,6 +45,10 @@ extern TalkActions* g_talkActions; extern Spells* g_spells; extern Vocations g_vocations; extern GlobalEvents* g_globalEvents; +extern Events* g_events; +extern CreatureEvents* g_creatureEvents; +extern Monsters g_monsters; +extern MoveEvents* g_moveEvents; Game::~Game() { @@ -84,8 +89,6 @@ void Game::setGameState(GameState_t newState) gameState = newState; switch (newState) { case GAME_STATE_INIT: { - commands.loadFromXml(); - loadExperienceStages(); groups.load(); @@ -219,7 +222,7 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index } case STACKPOS_USEITEM: { - thing = tile->getUseItem(); + thing = tile->getUseItem(index); break; } @@ -231,7 +234,7 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index case STACKPOS_USETARGET: { thing = tile->getTopCreature(); if (!thing) { - thing = tile->getUseItem(); + thing = tile->getUseItem(index); } break; } @@ -651,6 +654,13 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po player->setNextActionTask(nullptr); + if (g_config.getBoolean(ConfigManager::BLOCK_HEIGHT)) { + if (toTile->getHeight() > 1) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + } + if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) { //need to walk to the creature first before moving it std::forward_list listDir; @@ -705,6 +715,10 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po } } + if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) { + return; + } + ReturnValue ret = internalMoveCreature(*movingCreature, *toTile); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); @@ -729,7 +743,8 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, internalCreatureTurn(creature, DIRECTION_NORTH); } } - } else { + } + else { //try go down Tile* tmpTile = map.getTile(destPos.x, destPos.y, destPos.z); if (currentPos.z != 7 && (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)))) { @@ -742,11 +757,37 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, } } + ReturnValue ret = RETURNVALUE_NOTPOSSIBLE; Tile* toTile = map.getTile(destPos); - if (!toTile) { - return RETURNVALUE_NOTPOSSIBLE; + + Tile* toPos = map.getTile(destPos.x, destPos.y, destPos.z); + Tile* fromPos = map.getTile(currentPos.x, currentPos.y, currentPos.z); + + if (g_config.getBoolean(ConfigManager::BLOCK_HEIGHT)) { + if (toTile) { + if (currentPos.z > destPos.z && toPos->getHeight() > 1); + // not possible + else if ((((toPos->getHeight() - fromPos->getHeight()) < 2)) || + (fromPos->hasHeight(3) && (currentPos.z == destPos.z)) || + ((currentPos.z < destPos.z) && (toPos->hasHeight(3) && (fromPos->getHeight() < 2)))) + ret = internalMoveCreature(*creature, *toTile, flags); + } + + if (ret != RETURNVALUE_NOERROR) { + if (Player* player = creature->getPlayer()) { + player->sendCancelMessage(ret); + player->sendCancelWalk(); + } + } + + return ret; + } + else { + if (!toTile) { + return RETURNVALUE_NOTPOSSIBLE; + } + return internalMoveCreature(*creature, *toTile, flags); } - return internalMoveCreature(*creature, *toTile, flags); } ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/) @@ -964,6 +1005,10 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, return; } + if (!g_events->eventPlayerOnMoveItem(player, item, count, fromPos, toPos, fromCylinder, toCylinder)) { + return; + } + uint8_t toIndex = 0; if (toPos.x == 0xFFFF) { if (toPos.y & 0x40) { @@ -976,6 +1021,8 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); + } else { + g_events->eventPlayerOnItemMoved(player, item, count, fromPos, toPos, fromCylinder, toCylinder); } } @@ -2316,6 +2363,10 @@ void Game::playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t st return; } + if (!g_events->eventPlayerOnTradeRequest(player, tradePartner, tradeItem)) { + return; + } + internalStartTrade(player, tradePartner, tradeItem); } @@ -2379,6 +2430,11 @@ void Game::playerAcceptTrade(uint32_t playerId) Item* tradeItem1 = player->tradeItem; Item* tradeItem2 = tradePartner->tradeItem; + if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, tradeItem1, tradeItem2)) { + internalCloseTrade(player); + return; + } + player->setTradeState(TRADE_TRANSFER); tradePartner->setTradeState(TRADE_TRANSFER); @@ -2508,10 +2564,8 @@ void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t int32_t lookDistance = std::max(Position::getDistanceX(playerPosition, tradeItemPosition), Position::getDistanceY(playerPosition, tradeItemPosition)); - std::stringstream ss; if (index == 0) { - ss << "You see " << tradeItem->getDescription(lookDistance); - player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); + g_events->eventPlayerOnLookInTrade(player, tradePartner, tradeItem, lookDistance); return; } @@ -2531,8 +2585,7 @@ void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t } if (--index == 0) { - ss << "You see " << item->getDescription(lookDistance); - player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); + g_events->eventPlayerOnLookInTrade(player, tradePartner, item, lookDistance); return; } } @@ -2624,52 +2677,7 @@ void Game::playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos lookDistance = -1; } - std::ostringstream ss; - ss << "You see " << thing->getDescription(lookDistance); - - if (player->isAccessPlayer()) { - Item* item = thing->getItem(); - if (item) { - ss << std::endl << "ItemID: [" << item->getID() << ']'; - - uint16_t actionId = item->getActionId(); - if (actionId != 0) { - ss << ", ActionID: [" << actionId << ']'; - } - - uint16_t movementID = item->getMovementId(); - if (movementID != 0) { - ss << ", MovementID: [" << movementID << ']'; - } - - ss << '.'; - const ItemType& it = Item::items[item->getID()]; - - if (it.transformEquipTo) { - ss << std::endl << "TransformTo: [" << it.transformEquipTo << "] (onEquip)."; - } else if (it.transformDeEquipTo) { - ss << std::endl << "TransformTo: [" << it.transformDeEquipTo << "] (onDeEquip)."; - } - - if (it.decayTo != -1) { - ss << std::endl << "DecayTo: [" << it.decayTo << "]."; - } - } - - if (const Creature* creature = thing->getCreature()) { - ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << ']'; - - if (creature->getMaxMana() > 0) { - ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << ']'; - } - - ss << '.'; - } - - ss << std::endl << "Position: [X: " << thingPos.x << "] [Y: " << thingPos.y << "] [Z: " << thingPos.getZ() << "]."; - } - - player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); + g_events->eventPlayerOnLook(player, pos, thing, stackPos, lookDistance); } void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId) @@ -2704,21 +2712,7 @@ void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId) lookDistance = -1; } - std::ostringstream ss; - ss << "You see " << creature->getDescription(lookDistance); - - if (player->isAccessPlayer()) { - ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << ']'; - - if (creature->getMaxMana() > 0) { - ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << ']'; - } - - ss << '.' << std::endl; - ss << "Position: [X: " << creaturePos.x << "] [Y: " << creaturePos.y << "] [Z: " << creaturePos.getZ() << "]."; - } - - player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); + g_events->eventPlayerOnLookInBattleList(player, creature, lookDistance); } void Game::playerCancelAttackAndFollow(uint32_t playerId) @@ -2847,6 +2841,10 @@ void Game::playerTurn(uint32_t playerId, Direction dir) return; } + if (!g_events->eventPlayerOnTurn(player, dir)) { + return; + } + player->resetIdleTime(); internalCreatureTurn(player, dir); } @@ -2897,6 +2895,10 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, player->resetIdleTime(); + if (playerSaySpell(player, type, text)) { + return; + } + uint32_t muteTime = player->isMuted(); if (muteTime > 0) { std::ostringstream ss; @@ -2905,14 +2907,6 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, return; } - if (playerSayCommand(player, text)) { - return; - } - - if (playerSaySpell(player, type, text)) { - return; - } - if (!text.empty() && text.front() == '/' && player->isAccessPlayer()) { return; } @@ -2966,23 +2960,6 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, } } -bool Game::playerSayCommand(Player* player, const std::string& text) -{ - if (text.empty()) { - return false; - } - - char firstCharacter = text.front(); - for (char commandTag : commandTags) { - if (commandTag == firstCharacter) { - if (commands.exeCommand(*player, text)) { - return true; - } - } - } - return false; -} - bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& text) { std::string words = text; @@ -3234,6 +3211,10 @@ void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta) void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit) { + if (!g_events->eventCreatureOnChangeOutfit(creature, outfit)) { + return; + } + creature->setCurrentOutfit(outfit); if (creature->isInvisible()) { @@ -3403,6 +3384,17 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage return false; } + if (damage.origin != ORIGIN_NONE) { + const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); + if (!events.empty()) { + for (CreatureEvent* creatureEvent : events) { + creatureEvent->executeHealthChange(target, attacker, damage); + } + damage.origin = ORIGIN_NONE; + return combatChangeHealth(attacker, target, damage); + } + } + int32_t realHealthChange = target->getHealth(); target->gainHealth(attacker, damage.value); realHealthChange = target->getHealth() - realHealthChange; @@ -3410,7 +3402,8 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (realHealthChange > 0 && !target->isInGhostMode()) { addMagicEffect(targetPos, CONST_ME_MAGIC_BLUE); } - } else { + } + else { if (Monster* monster = target->getMonster()) { // makes monsters aggressive when damaged // basically stands for UNDERATTACK stance under CipSoft servers @@ -3431,7 +3424,8 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage Player* attackerPlayer; if (attacker) { attackerPlayer = attacker->getPlayer(); - } else { + } + else { attackerPlayer = nullptr; } damage.value = std::abs(damage.value); @@ -3440,25 +3434,39 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (healthChange == 0) { return true; } - + Player* targetPlayer = target->getPlayer(); SpectatorVec list; if (target->hasCondition(CONDITION_MANASHIELD) && damage.type != COMBAT_UNDEFINEDDAMAGE) { - int32_t manaDamage = std::min(target->getMana(), healthChange); + int32_t manaDamage = std::min(targetPlayer->getMana(), healthChange); if (manaDamage != 0) { - target->drainMana(attacker, manaDamage); + if (damage.origin != ORIGIN_NONE) { + const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); + if (!events.empty()) { + for (CreatureEvent* creatureEvent : events) { + creatureEvent->executeManaChange(target, attacker, damage); + } + healthChange = damage.value; + if (healthChange == 0) { + return true; + } + manaDamage = std::min(targetPlayer->getMana(), healthChange); + } + } + targetPlayer->drainMana(attacker, manaDamage); map.getSpectators(list, targetPos, true, true); addMagicEffect(list, targetPos, CONST_ME_LOSEENERGY); std::string damageString = std::to_string(manaDamage); - Player* targetPlayer = target->getPlayer(); if (targetPlayer) { std::stringstream ss; if (!attacker) { ss << "You lose " << damageString << " mana."; - } else if (targetPlayer == attackerPlayer) { + } + else if (targetPlayer == attackerPlayer) { ss << "You lose " << damageString << " mana due to your own attack."; - } else { + } + else { ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.'; } targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str()); @@ -3489,19 +3497,11 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage realDamage = damage.value; if (realDamage == 0) { return true; - } else if (realDamage >= targetHealth) { - for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { - if (!creatureEvent->executeOnPrepareDeath(target, attacker)) { - return false; - } - } } - target->drainHealth(attacker, realDamage); if (list.empty()) { map.getSpectators(list, targetPos, true, true); } - addCreatureHealth(list, target); TextColor_t color = TEXTCOLOR_NONE; uint8_t hitEffect; @@ -3514,15 +3514,15 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (color != TEXTCOLOR_NONE) { std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint"); - - Player* targetPlayer = target->getPlayer(); if (targetPlayer) { std::stringstream ss; if (!attacker) { ss << "You lose " << damageString << "."; - } else if (targetPlayer == attackerPlayer) { + } + else if (targetPlayer == attackerPlayer) { ss << "You lose " << damageString << " due to your own attack."; - } else { + } + else { ss << "You lose " << damageString << " due to an attack by " << attacker->getNameDescription() << '.'; } targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str()); @@ -3534,16 +3534,43 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage tmpPlayer->sendAnimatedText(targetPos, color, realDamageStr); } } + + if (realDamage >= targetHealth) { + for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { + if (!creatureEvent->executeOnPrepareDeath(target, attacker)) { + return false; + } + } + } + + target->drainHealth(attacker, realDamage); + addCreatureHealth(list, target); } return true; } -bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange) +bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& damage) { + Player* targetPlayer = target->getPlayer(); + if (!targetPlayer) { + return true; + } + int32_t manaChange = damage.value; if (manaChange > 0) { - target->changeMana(manaChange); - } else { + if (damage.origin != ORIGIN_NONE) { + const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); + if (!events.empty()) { + for (CreatureEvent* creatureEvent : events) { + creatureEvent->executeManaChange(target, attacker, damage); + } + damage.origin = ORIGIN_NONE; + return combatChangeMana(attacker, target, damage); + } + } + targetPlayer->changeMana(manaChange); + } + else { const Position& targetPos = target->getPosition(); if (!target->isAttackable()) { if (!target->isInGhostMode()) { @@ -3555,11 +3582,12 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh Player* attackerPlayer; if (attacker) { attackerPlayer = attacker->getPlayer(); - } else { + } + else { attackerPlayer = nullptr; } - int32_t manaLoss = std::min(target->getMana(), -manaChange); + int32_t manaLoss = std::min(targetPlayer->getMana(), -manaChange); BlockType_t blockType = target->blockHit(attacker, COMBAT_MANADRAIN, manaLoss); if (blockType != BLOCK_NONE) { addMagicEffect(targetPos, CONST_ME_POFF); @@ -3570,11 +3598,20 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh return true; } - target->drainMana(attacker, manaLoss); + if (damage.origin != ORIGIN_NONE) { + const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); + if (!events.empty()) { + for (CreatureEvent* creatureEvent : events) { + creatureEvent->executeManaChange(target, attacker, damage); + } + damage.origin = ORIGIN_NONE; + return combatChangeMana(attacker, target, damage); + } + } + + targetPlayer->drainMana(attacker, manaLoss); std::string damageString = std::to_string(manaLoss); - - Player* targetPlayer = target->getPlayer(); SpectatorVec list; map.getSpectators(list, targetPos, false, true); @@ -3582,9 +3619,11 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh std::stringstream ss; if (!attacker) { ss << "You lose " << damageString << " mana."; - } else if (targetPlayer == attackerPlayer) { + } + else if (targetPlayer == attackerPlayer) { ss << "You lose " << damageString << " mana due to your own attack."; - } else { + } + else { ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.'; } targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str()); @@ -3814,23 +3853,9 @@ void Game::getWorldLightInfo(LightInfo& lightInfo) const lightInfo.color = 0xD7; } -void Game::addCommandTag(char tag) -{ - for (char commandTag : commandTags) { - if (commandTag == tag) { - return; - } - } - commandTags.push_back(tag); -} - -void Game::resetCommandTag() -{ - commandTags.clear(); -} - void Game::shutdown() { + saveGameState(); std::cout << "Shutting down..." << std::flush; g_scheduler.shutdown(); @@ -4324,22 +4349,8 @@ void Game::playerReportBug(uint32_t playerId, const std::string& message) return; } - if (player->getAccountType() == ACCOUNT_TYPE_NORMAL) { - return; - } - - std::string fileName = "data/reports/" + player->getName() + " report.txt"; - FILE* file = fopen(fileName.c_str(), "a"); - if (!file) { - player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster."); - return; - } - - const Position& playerPosition = player->getPosition(); - fprintf(file, "------------------------------\nName: %s [Player Position: %u, %u, %u]\nComment: %s\n", player->getName().c_str(), playerPosition.x, playerPosition.y, playerPosition.z, message.c_str()); - fclose(file); - - player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " + g_config.getString(ConfigManager::SERVER_NAME) + "."); + const Position& position = player->getPosition(); + g_events->eventPlayerOnReportBug(player, message, position); } void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment) @@ -4534,3 +4545,64 @@ void Game::removeBedSleeper(uint32_t guid) bedSleepersMap.erase(it); } } + +bool Game::reload(ReloadTypes_t reloadType) +{ + switch (reloadType) { + case RELOAD_TYPE_ACTIONS: return g_actions->reload(); + case RELOAD_TYPE_CHAT: return g_chat->load(); + case RELOAD_TYPE_CONFIG: return g_config.reload(); + case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload(); + case RELOAD_TYPE_EVENTS: return g_events->load(); + case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload(); + case RELOAD_TYPE_ITEMS: return Item::items.reload(); + case RELOAD_TYPE_MONSTERS: return g_monsters.reload(); + case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload(); + case RELOAD_TYPE_NPCS: { + Npcs::reload(); + return true; + } + case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup(); + + case RELOAD_TYPE_SPELLS: { + if (!g_spells->reload()) { + std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; + std::terminate(); + } + else if (!g_monsters.reload()) { + std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl; + std::terminate(); + } + return true; + } + + case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload(); + + default: { + if (!g_spells->reload()) { + std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; + std::terminate(); + return false; + } + else if (!g_monsters.reload()) { + std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl; + std::terminate(); + return false; + } + + g_actions->reload(); + g_config.reload(); + g_creatureEvents->reload(); + g_monsters.reload(); + g_moveEvents->reload(); + Npcs::reload(); + raids.reload() && raids.startup(); + g_talkActions->reload(); + Item::items.reload(); + g_globalEvents->reload(); + g_events->load(); + g_chat->load(); + return true; + } + } +} diff --git a/src/game.h b/src/game.h index 20d80e2..a484818 100644 --- a/src/game.h +++ b/src/game.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -22,7 +22,6 @@ #include "account.h" #include "combat.h" -#include "commands.h" #include "groups.h" #include "map.h" #include "position.h" @@ -436,7 +435,7 @@ class Game void combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect); bool combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage); - bool combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange); + bool combatChangeMana(Creature* attacker, Creature* target, CombatDamage& damage); //animation help functions void addCreatureHealth(const Creature* target); @@ -449,9 +448,6 @@ class Game static void addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t color, const std::string& text); void addMonsterSayText(const Position& pos, const std::string& text); - void addCommandTag(char tag); - void resetCommandTag(); - void startDecay(Item* item); int32_t getLightHour() const { return lightHour; @@ -488,13 +484,13 @@ class Game BedItem* getBedBySleeper(uint32_t guid) const; void setBedSleeper(BedItem* bed, uint32_t guid); void removeBedSleeper(uint32_t guid); + bool reload(ReloadTypes_t reloadType); Groups groups; Map map; Raids raids; protected: - bool playerSayCommand(Player* player, const std::string& text); bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text); void playerWhisper(Player* player, const std::string& text); bool playerYell(Player* player, const std::string& text); @@ -518,7 +514,6 @@ class Game std::vector ToReleaseCreatures; std::vector ToReleaseItems; - std::vector commandTags; size_t lastBucket = 0; @@ -532,8 +527,6 @@ class Game std::map bedSleepersMap; - Commands commands; - static constexpr int32_t LIGHT_LEVEL_DAY = 250; static constexpr int32_t LIGHT_LEVEL_NIGHT = 40; static constexpr int32_t SUNSET = 1305; diff --git a/src/globalevent.cpp b/src/globalevent.cpp index 53463a8..96c9d71 100644 --- a/src/globalevent.cpp +++ b/src/globalevent.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/globalevent.h b/src/globalevent.h index 1232ddf..b4ba1cb 100644 --- a/src/globalevent.h +++ b/src/globalevent.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/groups.cpp b/src/groups.cpp index f56956c..8b4460d 100644 --- a/src/groups.cpp +++ b/src/groups.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/groups.h b/src/groups.h index 2215678..e16adfb 100644 --- a/src/groups.h +++ b/src/groups.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/guild.cpp b/src/guild.cpp index 34db634..e6bed21 100644 --- a/src/guild.cpp +++ b/src/guild.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/guild.h b/src/guild.h index 1aeefe9..3085ec9 100644 --- a/src/guild.h +++ b/src/guild.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/house.cpp b/src/house.cpp index 88614f9..29f375c 100644 --- a/src/house.cpp +++ b/src/house.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -82,7 +82,7 @@ void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* play setAccessList(SUBOWNER_LIST, ""); setAccessList(GUEST_LIST, ""); - for (Door* door : doorList) { + for (Door* door : doorSet) { door->setAccessList(""); } @@ -116,7 +116,7 @@ void House::updateDoorDescription() const } } - for (const auto& it : doorList) { + for (const auto& it : doorSet) { it->setSpecialDescription(ss.str()); } } @@ -277,17 +277,17 @@ bool House::isInvited(const Player* player) void House::addDoor(Door* door) { door->incrementReferenceCounter(); - doorList.push_back(door); + doorSet.insert(door); door->setHouse(this); updateDoorDescription(); } void House::removeDoor(Door* door) { - auto it = std::find(doorList.begin(), doorList.end(), door); - if (it != doorList.end()) { + auto it = doorSet.find(door); + if (it != doorSet.end()) { door->decrementReferenceCounter(); - doorList.erase(it); + doorSet.erase(it); } } @@ -299,7 +299,7 @@ void House::addBed(BedItem* bed) Door* House::getDoorByNumber(uint32_t doorId) const { - for (Door* door : doorList) { + for (Door* door : doorSet) { if (door->getDoorId() == doorId) { return door; } @@ -309,7 +309,7 @@ Door* House::getDoorByNumber(uint32_t doorId) const Door* House::getDoorByPosition(const Position& pos) { - for (Door* door : doorList) { + for (Door* door : doorSet) { if (door->getPosition() == pos) { return door; } diff --git a/src/house.h b/src/house.h index 2b126c3..3d9b543 100644 --- a/src/house.h +++ b/src/house.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -21,6 +21,7 @@ #define FS_HOUSE_H_EB9732E7771A438F9CD0EFA8CB4C58C4 #include +#include #include "container.h" #include "housetile.h" @@ -212,10 +213,11 @@ class House return houseTiles; } - const std::list& getDoors() const { - return doorList; + const std::set& getDoors() const { + return doorSet; } + void addBed(BedItem* bed); const HouseBedItemList& getBeds() const { return bedsList; @@ -234,7 +236,7 @@ class House Container transfer_container{ITEM_LOCKER1}; HouseTileList houseTiles; - std::list doorList; + std::set doorSet; HouseBedItemList bedsList; std::string houseName; diff --git a/src/housetile.cpp b/src/housetile.cpp index d206b48..f3fccf5 100644 --- a/src/housetile.cpp +++ b/src/housetile.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/housetile.h b/src/housetile.h index ba55d43..17ef634 100644 --- a/src/housetile.h +++ b/src/housetile.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/ioguild.cpp b/src/ioguild.cpp index 69675dc..b4ef9b5 100644 --- a/src/ioguild.cpp +++ b/src/ioguild.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/ioguild.h b/src/ioguild.h index d956c77..e5f058c 100644 --- a/src/ioguild.h +++ b/src/ioguild.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/iologindata.cpp b/src/iologindata.cpp index a2e5708..4fb9fbc 100644 --- a/src/iologindata.cpp +++ b/src/iologindata.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/iologindata.h b/src/iologindata.h index 9b58fdf..d207507 100644 --- a/src/iologindata.h +++ b/src/iologindata.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/iomap.cpp b/src/iomap.cpp index 3f6f426..197bf97 100644 --- a/src/iomap.cpp +++ b/src/iomap.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/iomap.h b/src/iomap.h index 9386141..5062e98 100644 --- a/src/iomap.h +++ b/src/iomap.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/iomapserialize.cpp b/src/iomapserialize.cpp index 0e83463..a6a5b9d 100644 --- a/src/iomapserialize.cpp +++ b/src/iomapserialize.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/iomapserialize.h b/src/iomapserialize.h index c7b269b..1a87c28 100644 --- a/src/iomapserialize.h +++ b/src/iomapserialize.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/item.cpp b/src/item.cpp index cb69843..acf2f1b 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/item.h b/src/item.h index 45e1e58..fc92915 100644 --- a/src/item.h +++ b/src/item.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/items.cpp b/src/items.cpp index dcb1463..767ada3 100644 --- a/src/items.cpp +++ b/src/items.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/items.h b/src/items.h index af2f76e..6411e99 100644 --- a/src/items.h +++ b/src/items.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/lockfree.h b/src/lockfree.h index 0b9860c..7c8cc7b 100644 --- a/src/lockfree.h +++ b/src/lockfree.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/luascript.cpp b/src/luascript.cpp index 231d2b5..8099d24 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -676,6 +676,18 @@ void LuaScriptInterface::setCreatureMetatable(lua_State* L, int32_t index, const } // Get +CombatDamage LuaScriptInterface::getCombatDamage(lua_State* L) +{ + CombatDamage damage; + damage.value = getNumber(L, -4); + damage.type = getNumber(L, -3); + damage.min = getNumber(L, -2); + damage.max = getNumber(L, -1); + + lua_pop(L, 4); + return damage; +} + std::string LuaScriptInterface::getString(lua_State* L, int32_t arg) { size_t len; @@ -839,6 +851,16 @@ void LuaScriptInterface::pushBoolean(lua_State* L, bool value) lua_pushboolean(L, value ? 1 : 0); } +void LuaScriptInterface::pushCombatDamage(lua_State* L, const CombatDamage& damage) +{ + lua_pushnumber(L, damage.value); + lua_pushnumber(L, damage.type); + lua_pushnumber(L, damage.min); + lua_pushnumber(L, damage.max); + lua_pushnumber(L, damage.origin); +} + + void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, int32_t stackpos/* = 0*/) { lua_createtable(L, 0, 4); @@ -1465,6 +1487,13 @@ void LuaScriptInterface::registerFunctions() registerEnum(SLOTP_DEPOT) registerEnum(SLOTP_TWO_HAND) + // Use with combat functions + registerEnum(ORIGIN_NONE) + registerEnum(ORIGIN_CONDITION) + registerEnum(ORIGIN_SPELL) + registerEnum(ORIGIN_MELEE) + registerEnum(ORIGIN_RANGED) + // Use with house:getAccessList, house:setAccessList registerEnum(GUEST_LIST) registerEnum(SUBOWNER_LIST) @@ -1533,6 +1562,31 @@ void LuaScriptInterface::registerFunctions() registerEnum(RETURNVALUE_CANONLYUSEONESHIELD) registerEnum(RETURNVALUE_NOPARTYMEMBERSINRANGE) registerEnum(RETURNVALUE_YOUARENOTTHEOWNER) + registerEnum(RETURNVALUE_TRADEPLAYERFARAWAY) + registerEnum(RETURNVALUE_YOUDONTOWNTHISHOUSE) + registerEnum(RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE) + registerEnum(RETURNVALUE_TRADEPLAYERHIGHESTBIDDER) + registerEnum(RETURNVALUE_YOUCANNOTTRADETHISHOUSE) + + registerEnum(RELOAD_TYPE_ALL) + registerEnum(RELOAD_TYPE_ACTIONS) + registerEnum(RELOAD_TYPE_CHAT) + registerEnum(RELOAD_TYPE_COMMANDS) + registerEnum(RELOAD_TYPE_CONFIG) + registerEnum(RELOAD_TYPE_CREATURESCRIPTS) + registerEnum(RELOAD_TYPE_EVENTS) + registerEnum(RELOAD_TYPE_GLOBAL) + registerEnum(RELOAD_TYPE_GLOBALEVENTS) + registerEnum(RELOAD_TYPE_ITEMS) + registerEnum(RELOAD_TYPE_MONSTERS) + registerEnum(RELOAD_TYPE_MOUNTS) + registerEnum(RELOAD_TYPE_MOVEMENTS) + registerEnum(RELOAD_TYPE_NPCS) + registerEnum(RELOAD_TYPE_QUESTS) + registerEnum(RELOAD_TYPE_RAIDS) + registerEnum(RELOAD_TYPE_SPELLS) + registerEnum(RELOAD_TYPE_TALKACTIONS) + registerEnum(RELOAD_TYPE_WEAPONS) // _G registerGlobalVariable("INDEX_WHEREEVER", INDEX_WHEREEVER); @@ -1608,6 +1662,8 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::MAX_PACKETS_PER_SECOND) registerEnumIn("configKeys", ConfigManager::NEWBIE_TOWN) registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD) + registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT) + registerEnumIn("configKeys", ConfigManager::DROP_ITEMS) // os registerMethod("os", "mtime", LuaScriptInterface::luaSystemTime); @@ -1646,6 +1702,8 @@ void LuaScriptInterface::registerFunctions() registerMethod("Game", "startRaid", LuaScriptInterface::luaGameStartRaid); + registerMethod("Game", "reload", LuaScriptInterface::luaGameReload); + // Variant registerClass("Variant", "", LuaScriptInterface::luaVariantCreate); @@ -1853,10 +1911,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Creature", "getMaxHealth", LuaScriptInterface::luaCreatureGetMaxHealth); registerMethod("Creature", "setMaxHealth", LuaScriptInterface::luaCreatureSetMaxHealth); - registerMethod("Creature", "getMana", LuaScriptInterface::luaCreatureGetMana); - registerMethod("Creature", "addMana", LuaScriptInterface::luaCreatureAddMana); - registerMethod("Creature", "getMaxMana", LuaScriptInterface::luaCreatureGetMaxMana); - registerMethod("Creature", "getSkull", LuaScriptInterface::luaCreatureGetSkull); registerMethod("Creature", "setSkull", LuaScriptInterface::luaCreatureSetSkull); @@ -1914,6 +1968,9 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getMagicLevel", LuaScriptInterface::luaPlayerGetMagicLevel); registerMethod("Player", "getBaseMagicLevel", LuaScriptInterface::luaPlayerGetBaseMagicLevel); + registerMethod("Player", "getMana", LuaScriptInterface::luaPlayerGetMana); + registerMethod("Player", "addMana", LuaScriptInterface::luaPlayerAddMana); + registerMethod("Player", "getMaxMana", LuaScriptInterface::luaPlayerGetMaxMana); registerMethod("Player", "setMaxMana", LuaScriptInterface::luaPlayerSetMaxMana); registerMethod("Player", "getManaSpent", LuaScriptInterface::luaPlayerGetManaSpent); registerMethod("Player", "addManaSpent", LuaScriptInterface::luaPlayerAddManaSpent); @@ -2122,6 +2179,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("House", "getOwnerGuid", LuaScriptInterface::luaHouseGetOwnerGuid); registerMethod("House", "setOwnerGuid", LuaScriptInterface::luaHouseSetOwnerGuid); + registerMethod("House", "startTrade", LuaScriptInterface::luaHouseStartTrade); registerMethod("House", "getBeds", LuaScriptInterface::luaHouseGetBeds); registerMethod("House", "getBedCount", LuaScriptInterface::luaHouseGetBedCount); @@ -2196,6 +2254,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Combat", "setArea", LuaScriptInterface::luaCombatSetArea); registerMethod("Combat", "setCondition", LuaScriptInterface::luaCombatSetCondition); registerMethod("Combat", "setCallback", LuaScriptInterface::luaCombatSetCallback); + registerMethod("Combat", "setOrigin", LuaScriptInterface::luaCombatSetOrigin); registerMethod("Combat", "execute", LuaScriptInterface::luaCombatExecute); @@ -2794,7 +2853,7 @@ int LuaScriptInterface::luaCreateCombatArea(lua_State* L) int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) { - //doAreaCombatHealth(cid, type, pos, area, min, max, effect) + //doAreaCombatHealth(cid, type, pos, area, min, max, effect[, origin = ORIGIN_SPELL]) Creature* creature = getCreature(L, 1); if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -2812,6 +2871,7 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) params.impactEffect = getNumber(L, 7); CombatDamage damage; + damage.origin = getNumber(L, 8, ORIGIN_SPELL); damage.type = combatType; damage.value = normal_random(getNumber(L, 6), getNumber(L, 5)); @@ -2826,7 +2886,7 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) { - //doTargetCombatHealth(cid, target, type, min, max, effect) + //doTargetCombatHealth(cid, target, type, min, max, effect[, origin = ORIGIN_SPELL]) Creature* creature = getCreature(L, 1); if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -2848,6 +2908,7 @@ int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) params.impactEffect = getNumber(L, 6); CombatDamage damage; + damage.origin = getNumber(L, 7, ORIGIN_SPELL); damage.type = combatType; damage.value = normal_random(getNumber(L, 4), getNumber(L, 5)); @@ -2858,7 +2919,7 @@ int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L) { - //doAreaCombatMana(cid, pos, area, min, max, effect) + //doAreaCombatMana(cid, pos, area, min, max, effect[, origin = ORIGIN_SPELL]) Creature* creature = getCreature(L, 1); if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -2873,6 +2934,7 @@ int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L) params.impactEffect = getNumber(L, 6); CombatDamage damage; + damage.origin = getNumber(L, 7, ORIGIN_SPELL); damage.type = COMBAT_MANADRAIN; damage.value = normal_random(getNumber(L, 4), getNumber(L, 5)); @@ -2888,7 +2950,7 @@ int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L) int LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) { - //doTargetCombatMana(cid, target, min, max, effect) + //doTargetCombatMana(cid, target, min, max, effect[, origin = ORIGIN_SPELL) Creature* creature = getCreature(L, 1); if (!creature && (!isNumber(L, 1) || getNumber(L, 1) != 0)) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -2907,6 +2969,7 @@ int LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) params.impactEffect = getNumber(L, 5); CombatDamage damage; + damage.origin = getNumber(L, 6, ORIGIN_SPELL); damage.type = COMBAT_MANADRAIN; damage.value = normal_random(getNumber(L, 3), getNumber(L, 4)); @@ -4144,6 +4207,25 @@ int LuaScriptInterface::luaGameStartRaid(lua_State* L) return 1; } +int LuaScriptInterface::luaGameReload(lua_State* L) +{ + // Game.reload(reloadType) + ReloadTypes_t reloadType = getNumber(L, 1); + if (!reloadType) { + lua_pushnil(L); + return 1; + } + + if (reloadType == RELOAD_TYPE_GLOBAL) { + pushBoolean(L, g_luaEnvironment.loadFile("data/global.lua") == 0); + } + else { + pushBoolean(L, g_game.reload(reloadType)); + } + lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0); + return 1; +} + // Variant int LuaScriptInterface::luaVariantCreate(lua_State* L) { @@ -6575,50 +6657,6 @@ int LuaScriptInterface::luaCreatureSetMaxHealth(lua_State* L) return 1; } -int LuaScriptInterface::luaCreatureGetMana(lua_State* L) -{ - // creature:getMana() - const Creature* creature = getUserdata(L, 1); - if (creature) { - lua_pushnumber(L, creature->getMana()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreatureAddMana(lua_State* L) -{ - // creature:addMana(manaChange[, animationOnLoss = false]) - Creature* creature = getUserdata(L, 1); - if (!creature) { - lua_pushnil(L); - return 1; - } - - int32_t manaChange = getNumber(L, 2); - bool animationOnLoss = getBoolean(L, 3, false); - if (!animationOnLoss && manaChange < 0) { - creature->changeMana(manaChange); - } else { - g_game.combatChangeMana(nullptr, creature, manaChange); - } - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaCreatureGetMaxMana(lua_State* L) -{ - // creature:getMaxMana() - const Creature* creature = getUserdata(L, 1); - if (creature) { - lua_pushnumber(L, creature->getMaxMana()); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaCreatureGetSkull(lua_State* L) { // creature:getSkull() @@ -7191,15 +7229,15 @@ int LuaScriptInterface::luaPlayerGetExperience(lua_State* L) int LuaScriptInterface::luaPlayerAddExperience(lua_State* L) { - // player:addExperience(experience[, sendText = false[, applyStages = true]) + // player:addExperience(experience[, sendText = false]) Player* player = getUserdata(L, 1); if (player) { int64_t experience = getNumber(L, 2); bool sendText = getBoolean(L, 3, false); - bool applyStages = getBoolean(L, 4, true); - player->addExperience(experience, sendText, applyStages); + player->addExperience(nullptr, experience, sendText); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -7255,6 +7293,56 @@ int LuaScriptInterface::luaPlayerGetBaseMagicLevel(lua_State* L) return 1; } +int LuaScriptInterface::luaPlayerGetMana(lua_State* L) +{ + // player:getMana() + const Player* player = getUserdata(L, 1); + if (player) { + lua_pushnumber(L, player->getMana()); + } + else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaPlayerAddMana(lua_State* L) +{ + // player:addMana(manaChange[, animationOnLoss = false]) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + int32_t manaChange = getNumber(L, 2); + bool animationOnLoss = getBoolean(L, 3, false); + if (!animationOnLoss && manaChange < 0) { + player->changeMana(manaChange); + } + else { + CombatDamage damage; + damage.value = manaChange; + damage.origin = ORIGIN_NONE; + g_game.combatChangeMana(nullptr, player, damage); + } + pushBoolean(L, true); + return 1; +} + +int LuaScriptInterface::luaPlayerGetMaxMana(lua_State* L) +{ + // player:getMaxMana() + const Player* player = getUserdata(L, 1); + if (player) { + lua_pushnumber(L, player->getMaxMana()); + } + else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaPlayerSetMaxMana(lua_State* L) { // player:setMaxMana(maxMana) @@ -7723,12 +7811,20 @@ int LuaScriptInterface::luaPlayerSetBankBalance(lua_State* L) { // player:setBankBalance(bankBalance) Player* player = getUserdata(L, 1); - if (player) { - player->setBankBalance(getNumber(L, 2)); - pushBoolean(L, true); - } else { + if (!player) { lua_pushnil(L); + return 1; } + + int64_t balance = getNumber(L, 2); + if (balance < 0) { + reportErrorFunc("Invalid bank balance value."); + lua_pushnil(L); + return 1; + } + + player->setBankBalance(balance); + pushBoolean(L, true); return 1; } @@ -9473,6 +9569,53 @@ int LuaScriptInterface::luaHouseSetOwnerGuid(lua_State* L) return 1; } +int LuaScriptInterface::luaHouseStartTrade(lua_State* L) +{ + // house:startTrade(player, tradePartner) + House* house = getUserdata(L, 1); + Player* player = getUserdata(L, 2); + Player* tradePartner = getUserdata(L, 3); + + if (!player || !tradePartner || !house) { + lua_pushnil(L); + return 1; + } + + if (!Position::areInRange<2, 2, 0>(tradePartner->getPosition(), player->getPosition())) { + lua_pushnumber(L, RETURNVALUE_TRADEPLAYERFARAWAY); + return 1; + } + + if (house->getOwner() != player->getGUID()) { + lua_pushnumber(L, RETURNVALUE_YOUDONTOWNTHISHOUSE); + return 1; + } + + if (g_game.map.houses.getHouseByPlayerId(tradePartner->getGUID())) { + lua_pushnumber(L, RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE); + return 1; + } + + if (IOLoginData::hasBiddedOnHouse(tradePartner->getGUID())) { + lua_pushnumber(L, RETURNVALUE_TRADEPLAYERHIGHESTBIDDER); + return 1; + } + + Item* transferItem = house->getTransferItem(); + if (!transferItem) { + lua_pushnumber(L, RETURNVALUE_YOUCANNOTTRADETHISHOUSE); + return 1; + } + + transferItem->getParent()->setParent(player); + if (!g_game.internalStartTrade(player, tradePartner, transferItem)) { + house->resetTransferItem(); + } + + lua_pushnumber(L, RETURNVALUE_NOERROR); + return 1; +} + int LuaScriptInterface::luaHouseGetBeds(lua_State* L) { // house:getBeds() @@ -10225,6 +10368,20 @@ int LuaScriptInterface::luaCombatSetCallback(lua_State* L) return 1; } +int LuaScriptInterface::luaCombatSetOrigin(lua_State* L) +{ + // combat:setOrigin(origin) + Combat* combat = getUserdata(L, 1); + if (combat) { + combat->setOrigin(getNumber(L, 2)); + pushBoolean(L, true); + } + else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaCombatExecute(lua_State* L) { // combat:execute(creature, variant) @@ -11222,7 +11379,7 @@ int LuaScriptInterface::luaPartyShareExperience(lua_State* L) uint64_t experience = getNumber(L, 2); Party* party = getUserdata(L, 1); if (party) { - party->shareExperience(experience); + party->shareExperience(experience, nullptr); pushBoolean(L, true); } else { lua_pushnil(L); diff --git a/src/luascript.h b/src/luascript.h index 4e937e7..8b90a48 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -320,6 +320,7 @@ class LuaScriptInterface } static std::string getString(lua_State* L, int32_t arg); + static CombatDamage getCombatDamage(lua_State* L); static Position getPosition(lua_State* L, int32_t arg, int32_t& stackpos); static Position getPosition(lua_State* L, int32_t arg); static Outfit_t getOutfit(lua_State* L, int32_t arg); @@ -368,6 +369,7 @@ class LuaScriptInterface // Push static void pushBoolean(lua_State* L, bool value); + static void pushCombatDamage(lua_State* L, const CombatDamage& damage); static void pushPosition(lua_State* L, const Position& position, int32_t stackpos = 0); static void pushOutfit(lua_State* L, const Outfit_t& outfit); @@ -549,6 +551,8 @@ class LuaScriptInterface static int luaGameStartRaid(lua_State* L); + static int luaGameReload(lua_State* L); + // Variant static int luaVariantCreate(lua_State* L); @@ -749,10 +753,6 @@ class LuaScriptInterface static int luaCreatureGetMaxHealth(lua_State* L); static int luaCreatureSetMaxHealth(lua_State* L); - static int luaCreatureGetMana(lua_State* L); - static int luaCreatureAddMana(lua_State* L); - static int luaCreatureGetMaxMana(lua_State* L); - static int luaCreatureGetSkull(lua_State* L); static int luaCreatureSetSkull(lua_State* L); @@ -809,6 +809,9 @@ class LuaScriptInterface static int luaPlayerGetMagicLevel(lua_State* L); static int luaPlayerGetBaseMagicLevel(lua_State* L); + static int luaPlayerGetMana(lua_State* L); + static int luaPlayerAddMana(lua_State* L); + static int luaPlayerGetMaxMana(lua_State* L); static int luaPlayerSetMaxMana(lua_State* L); static int luaPlayerGetManaSpent(lua_State* L); static int luaPlayerAddManaSpent(lua_State* L); @@ -1011,6 +1014,7 @@ class LuaScriptInterface static int luaHouseGetOwnerGuid(lua_State* L); static int luaHouseSetOwnerGuid(lua_State* L); + static int luaHouseStartTrade(lua_State* L); static int luaHouseGetBeds(lua_State* L); static int luaHouseGetBedCount(lua_State* L); @@ -1082,6 +1086,7 @@ class LuaScriptInterface static int luaCombatSetArea(lua_State* L); static int luaCombatSetCondition(lua_State* L); static int luaCombatSetCallback(lua_State* L); + static int luaCombatSetOrigin(lua_State* L); static int luaCombatExecute(lua_State* L); diff --git a/src/mailbox.cpp b/src/mailbox.cpp index 23754b1..8b21a45 100644 --- a/src/mailbox.cpp +++ b/src/mailbox.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/mailbox.h b/src/mailbox.h index 16ce6fb..084dc26 100644 --- a/src/mailbox.h +++ b/src/mailbox.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/map.cpp b/src/map.cpp index 9e9f0f3..94bdeef 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/map.h b/src/map.h index 67a26d2..3e8df8d 100644 --- a/src/map.h +++ b/src/map.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/monster.cpp b/src/monster.cpp index 001a1ff..9c65630 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -744,23 +744,12 @@ void Monster::setIdle(bool idle) void Monster::updateIdleStatus() { - bool idle = true; + bool idle = false; - if (!isSummon()) { - if (!targetList.empty()) { - // visible target - idle = false; - } else { - for (Condition* condition : conditions) { - if (condition->getType() >= CONDITION_ENERGY && condition->getType() <= CONDITION_ENERGY) { - // monsters with aggressive conditions never become idle - idle = false; - break; - } - } + if (conditions.empty()) { + if (!isSummon() && targetList.empty()) { + idle = true; } - } else { - idle = false; } setIdle(idle); @@ -887,7 +876,7 @@ void Monster::doAttacking(uint32_t) for (spellBlock_t& spellBlock : mType->info.attackSpells) { if (spellBlock.range != 0 && std::max(Position::getDistanceX(myPos, targetPos), Position::getDistanceY(myPos, targetPos)) <= spellBlock.range) { - if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) { + if (normal_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) { updateLookDirection(); minCombatValue = spellBlock.minCombatValue; @@ -988,7 +977,7 @@ void Monster::onThinkTarget(uint32_t interval) void Monster::onThinkDefense(uint32_t) { for (const spellBlock_t& spellBlock : mType->info.defenseSpells) { - if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) { + if (normal_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) { minCombatValue = spellBlock.minCombatValue; maxCombatValue = spellBlock.maxCombatValue; spellBlock.spell->castSpell(this, this); @@ -1012,7 +1001,7 @@ void Monster::onThinkDefense(uint32_t) continue; } - if (uniform_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) { + if (normal_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) { Monster* summon = Monster::createMonster(summonBlock.name); if (summon) { const Position& summonPos = getPosition(); @@ -1038,16 +1027,15 @@ void Monster::onThinkYell(uint32_t) } int32_t randomResult = rand(); - if (randomResult == 50 * (randomResult / 50)) { - if (!mType->info.voiceVector.empty()) { - uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1); - const voiceBlock_t& vb = mType->info.voiceVector[index]; - - if (vb.yellText) { - g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, vb.text, false); - } else { - g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, vb.text, false); - } + if (rand() == 50 * (randomResult / 50)) { + int32_t totalVoices = mType->info.voiceVector.size(); + const voiceBlock_t& voice = mType->info.voiceVector[rand() % totalVoices + 1]; + + if (voice.yellText) { + g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false); + } + else { + g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, voice.text, false); } } } @@ -1172,7 +1160,7 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags) if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) { if (OTSYS_TIME() >= nextDanceStepRound) { updateLookDirection(); - nextDanceStepRound = OTSYS_TIME() + 200 + getStepDuration(); + nextDanceStepRound = OTSYS_TIME() + getStepDuration(); //choose a random direction result = getRandomStep(getPosition(), direction); @@ -1235,6 +1223,7 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags) egibleToDance = false; earliestWakeUpTime = OTSYS_TIME() + 1000; earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration(); + earliestAttackTime += 200; } } } @@ -1276,6 +1265,7 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags) egibleToDance = false; earliestWakeUpTime = OTSYS_TIME() + 1000; earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration(); + earliestAttackTime += 200; } } } @@ -2002,40 +1992,71 @@ void Monster::updateLookDirection() //look EAST/WEST if (offsetx < 0) { newDir = DIRECTION_WEST; - } else { + } + else { newDir = DIRECTION_EAST; } - } else if (dx < dy) { + } + else if (dx < dy) { //look NORTH/SOUTH if (offsety < 0) { newDir = DIRECTION_NORTH; - } else { + } + else { newDir = DIRECTION_SOUTH; } - } else { + } + else { Direction dir = getDirection(); if (offsetx < 0 && offsety < 0) { - if (dir == DIRECTION_SOUTH || dir == DIRECTION_NORTH) { + if (offsetx == -1 && offsety == -1) { + if (dir == DIRECTION_NORTH) { + newDir = DIRECTION_WEST; + } + } + if (dir == DIRECTION_SOUTH) { newDir = DIRECTION_WEST; - } else if (dir == DIRECTION_EAST) { + } + else if (dir == DIRECTION_EAST) { newDir = DIRECTION_NORTH; } - } else if (offsetx < 0 && offsety > 0) { - if (dir == DIRECTION_NORTH || dir == DIRECTION_SOUTH) { + } + else if (offsetx < 0 && offsety > 0) { + if (offsetx == -1 && offsety == 1) { + if (dir == DIRECTION_SOUTH) { + newDir = DIRECTION_WEST; + } + } + if (dir == DIRECTION_NORTH) { newDir = DIRECTION_WEST; - } else if (dir == DIRECTION_EAST) { + } + else if (dir == DIRECTION_EAST) { newDir = DIRECTION_SOUTH; } - } else if (offsetx > 0 && offsety < 0) { - if (dir == DIRECTION_SOUTH || dir == DIRECTION_NORTH) { + } + else if (offsetx > 0 && offsety < 0) { + if (offsetx == 1 && offsety == -1) { + if (dir == DIRECTION_NORTH) { + newDir = DIRECTION_EAST; + } + } + if (dir == DIRECTION_SOUTH) { newDir = DIRECTION_EAST; - } else if (dir == DIRECTION_WEST) { + } + else if (dir == DIRECTION_WEST) { newDir = DIRECTION_NORTH; } - } else { - if (dir == DIRECTION_NORTH || dir == DIRECTION_SOUTH) { + } + else { + if (offsetx == 1 && offsety == 1) { + if (dir == DIRECTION_SOUTH) { + newDir = DIRECTION_EAST; + } + } + if (dir == DIRECTION_NORTH) { newDir = DIRECTION_EAST; - } else if (dir == DIRECTION_WEST) { + } + else if (dir == DIRECTION_WEST) { newDir = DIRECTION_SOUTH; } } diff --git a/src/monster.h b/src/monster.h index 0e04f9e..8447df7 100644 --- a/src/monster.h +++ b/src/monster.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/monsters.cpp b/src/monsters.cpp index 08bf423..bf46bd1 100644 --- a/src/monsters.cpp +++ b/src/monsters.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -372,6 +372,18 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE); combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1); combat->setParam(COMBAT_PARAM_BLOCKSHIELD, 1); + uint32_t tD = this->getMonsterType(description)->info.targetDistance; + if (tD == 1) { + if (sb.range > 1) { + combat->setOrigin(ORIGIN_RANGED); + } + else { + combat->setOrigin(ORIGIN_MELEE); + } + } + else if (tD > 1 && sb.range > 1) { + combat->setOrigin(ORIGIN_RANGED); + } } else if (tmpName == "bleed") { combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE); } else if (tmpName == "poison" || tmpName == "earth") { diff --git a/src/monsters.h b/src/monsters.h index 671ffe8..23906ab 100644 --- a/src/monsters.h +++ b/src/monsters.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/movement.cpp b/src/movement.cpp index 9c0848c..184a44a 100644 --- a/src/movement.cpp +++ b/src/movement.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/movement.h b/src/movement.h index cb24693..d8a67e4 100644 --- a/src/movement.h +++ b/src/movement.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/networkmessage.cpp b/src/networkmessage.cpp index 12c0fdb..7f02803 100644 --- a/src/networkmessage.cpp +++ b/src/networkmessage.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/networkmessage.h b/src/networkmessage.h index e64fa30..7d11153 100644 --- a/src/networkmessage.h +++ b/src/networkmessage.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/npc.cpp b/src/npc.cpp index cb2cf7b..ab92d7b 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/npc.h b/src/npc.h index 737e143..acf95ec 100644 --- a/src/npc.h +++ b/src/npc.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/otpch.cpp b/src/otpch.cpp index 323a370..5db3123 100644 --- a/src/otpch.cpp +++ b/src/otpch.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/otpch.h b/src/otpch.h index e1de8c9..a2ff7c1 100644 --- a/src/otpch.h +++ b/src/otpch.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/otserv.cpp b/src/otserv.cpp index bf202d6..c6ee693 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/outputmessage.cpp b/src/outputmessage.cpp index 75c1200..0d3d597 100644 --- a/src/outputmessage.cpp +++ b/src/outputmessage.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/outputmessage.h b/src/outputmessage.h index 52526c5..01897ff 100644 --- a/src/outputmessage.h +++ b/src/outputmessage.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/party.cpp b/src/party.cpp index e778572..9ac0bed 100644 --- a/src/party.cpp +++ b/src/party.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -22,9 +22,11 @@ #include "party.h" #include "game.h" #include "configmanager.h" +#include "events.h" extern Game g_game; extern ConfigManager g_config; +extern Events* g_events; Party::Party(Player* leader) : leader(leader) { @@ -33,6 +35,11 @@ Party::Party(Player* leader) : leader(leader) void Party::disband() { + if (!g_events->eventPartyOnDisband(this)) { + return; + } + + Player* currentLeader = leader; leader = nullptr; @@ -79,6 +86,10 @@ bool Party::leaveParty(Player* player) return false; } + if (!g_events->eventPartyOnLeave(this, player)) { + return false; + } + bool missingLeader = false; if (leader == player) { if (!memberList.empty()) { @@ -171,6 +182,10 @@ bool Party::passPartyLeadership(Player* player) bool Party::joinParty(Player& player) { + if (!g_events->eventPartyOnJoin(this, &player)) { + return false; + } + auto it = std::find(inviteList.begin(), inviteList.end(), &player); if (it == inviteList.end()) { return false; @@ -380,13 +395,15 @@ bool Party::setSharedExperience(Player* player, bool sharedExpActive) return true; } -void Party::shareExperience(uint64_t experience) +void Party::shareExperience(uint64_t experience, Creature* source/* = nullptr*/) { - uint64_t shareExperience = static_cast(std::ceil((static_cast(experience) * (extraExpRate + 1)) / (memberList.size() + 1))); + uint64_t shareExperience = experience; + g_events->eventPartyOnShareExperience(this, shareExperience); + for (Player* member : memberList) { - member->onGainSharedExperience(shareExperience); + member->onGainSharedExperience(shareExperience, source); } - leader->onGainSharedExperience(shareExperience); + leader->onGainSharedExperience(shareExperience, source); } bool Party::canUseSharedExperience(const Player* player) const diff --git a/src/party.h b/src/party.h index baccc56..58c72ec 100644 --- a/src/party.h +++ b/src/party.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -66,7 +66,7 @@ class Party return memberList.empty() && inviteList.empty(); } - void shareExperience(uint64_t experience); + void shareExperience(uint64_t experience, Creature* source/* = nullptr*/); bool setSharedExperience(Player* player, bool sharedExpActive); bool isSharedExperienceActive() const { return sharedExpActive; diff --git a/src/player.cpp b/src/player.cpp index 4cca9d8..57b15bf 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -26,6 +26,7 @@ #include "combat.h" #include "configmanager.h" #include "creatureevent.h" +#include "events.h" #include "game.h" #include "iologindata.h" #include "monster.h" @@ -38,6 +39,7 @@ extern Chat* g_chat; extern Vocations g_vocations; extern MoveEvents* g_moveEvents; extern CreatureEvents* g_creatureEvents; +extern Events* g_events; MuteCountMap Player::muteCountMap; @@ -263,8 +265,6 @@ int32_t Player::getDefense() default: break; } - - defenseSkill = getSkillLevel(defenseSkill); } if (shield) { @@ -344,12 +344,7 @@ void Player::addSkillAdvance(skills_t skill, uint64_t count) return; } - if (skill == SKILL_MAGLEVEL) { - count *= g_config.getNumber(g_config.RATE_MAGIC); - } else { - count *= g_config.getNumber(g_config.RATE_SKILL); - } - + g_events->eventPlayerOnGainSkillTries(this, skill, count); if (count == 0) { return; } @@ -411,7 +406,7 @@ void Player::setVarStats(stats_t stat, int32_t modifier) case STAT_MAXMANAPOINTS: { if (getMana() > getMaxMana()) { - Creature::changeMana(getMaxMana() - getMana()); + changeMana(getMaxMana() - getMana()); } break; } @@ -1224,7 +1219,13 @@ void Player::drainHealth(Creature* attacker, int32_t damage) void Player::drainMana(Creature* attacker, int32_t manaLoss) { - Creature::drainMana(attacker, manaLoss); + onAttacked(); + changeMana(-manaLoss); + + if (attacker) { + addDamagePoints(attacker, manaLoss); + } + sendStats(); } @@ -1241,8 +1242,7 @@ void Player::addManaSpent(uint64_t amount) return; } - amount *= g_config.getNumber(g_config.RATE_MAGIC); - + g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, amount); if (amount == 0) { return; } @@ -1286,10 +1286,11 @@ void Player::addManaSpent(uint64_t amount) } } -void Player::addExperience(uint64_t exp, bool sendText/* = false*/, bool applyStages/* = true*/) +void Player::addExperience(Creature* source, uint64_t exp, bool sendText/* = false*/) { uint64_t currLevelExp = Player::getExpForLevel(level); uint64_t nextLevelExp = Player::getExpForLevel(level + 1); + uint64_t rawExp = exp; if (currLevelExp >= nextLevelExp) { //player has reached max level levelPercent = 0; @@ -1297,17 +1298,7 @@ void Player::addExperience(uint64_t exp, bool sendText/* = false*/, bool applySt return; } - if (getSoul() < getVocation()->getSoulMax() && exp >= level) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000, 0); - condition->setParam(CONDITION_PARAM_SOULGAIN, 1); - condition->setParam(CONDITION_PARAM_SOULTICKS, vocation->getSoulGainTicks() * 1000); - addCondition(condition); - } - - if (applyStages) { - exp *= g_game.getExperienceStage(level); - } - + g_events->eventPlayerOnGainExperience(this, source, exp, rawExp); if (exp == 0) { return; } @@ -1641,6 +1632,7 @@ void Player::death(Creature* lastHitCreature) //Level loss uint64_t expLoss = static_cast(experience * deathLossPercent); + g_events->eventPlayerOnLoseExperience(this, expLoss); if (expLoss != 0) { uint32_t oldLevel = level; @@ -1732,7 +1724,9 @@ void Player::death(Creature* lastHitCreature) loginPosition = getTemplePosition(); // Restart first items - addStorageValue(30017, 1); + lastLoginSaved = 0; + lastLogout = 0; + // Restart items for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; slot++) @@ -2299,7 +2293,7 @@ Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** de n--; } - if (g_config.getBoolean(ConfigManager::QUERY_PLAYER_CONTAINERS)) { + if (!g_config.getBoolean(ConfigManager::DROP_ITEMS)) { for (Item* tmpContainerItem : tmpContainer->getItemList()) { if (Container* subContainer = tmpContainerItem->getContainer()) { containers.push_back(subContainer); @@ -2328,7 +2322,7 @@ Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** de return tmpContainer; } - if (g_config.getBoolean(ConfigManager::QUERY_PLAYER_CONTAINERS)) { + if (!g_config.getBoolean(ConfigManager::DROP_ITEMS)) { if (Container* subContainer = tmpItem->getContainer()) { containers.push_back(subContainer); } @@ -2716,10 +2710,10 @@ void Player::internalAddThing(uint32_t index, Thing* thing) uint32_t Player::checkPlayerKilling() { time_t today = std::time(nullptr); - uint32_t lastDay = 0; - uint32_t lastWeek = 0; - uint32_t lastMonth = 0; - uint64_t egibleMurders = 0; + int32_t lastDay = 0; + int32_t lastWeek = 0; + int32_t lastMonth = 0; + int64_t egibleMurders = 0; time_t dayTimestamp = today - (24 * 60 * 60); time_t weekTimestamp = today - (7 * 24 * 60 * 60); @@ -3133,13 +3127,13 @@ bool Player::onKilledCreature(Creature* target, bool lastHit/* = true*/) return unjustified; } -void Player::gainExperience(uint64_t gainExp) +void Player::gainExperience(uint64_t gainExp, Creature* source) { if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0) { return; } - addExperience(gainExp, true); + addExperience(source, gainExp, true); } void Player::onGainExperience(uint64_t gainExp, Creature* target) @@ -3149,18 +3143,18 @@ void Player::onGainExperience(uint64_t gainExp, Creature* target) } if (target && !target->getPlayer() && party && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) { - party->shareExperience(gainExp); + party->shareExperience(gainExp, target); //We will get a share of the experience through the sharing mechanism return; } Creature::onGainExperience(gainExp, target); - gainExperience(gainExp); + gainExperience(gainExp, target); } -void Player::onGainSharedExperience(uint64_t gainExp) +void Player::onGainSharedExperience(uint64_t gainExp, Creature* source) { - gainExperience(gainExp); + gainExperience(gainExp, source); } bool Player::isImmune(CombatType_t type) const @@ -3207,7 +3201,12 @@ void Player::changeHealth(int32_t healthChange, bool sendHealthChange/* = true*/ void Player::changeMana(int32_t manaChange) { if (!hasFlag(PlayerFlag_HasInfiniteMana)) { - Creature::changeMana(manaChange); + if (manaChange > 0) { + mana += std::min(manaChange, getMaxMana() - mana); + } + else { + mana = std::max(0, mana + manaChange); + } } sendStats(); @@ -3322,6 +3321,18 @@ void Player::addAttacked(const Player* attacked) attackedSet.insert(attacked->id); } +void Player::removeAttacked(const Player* attacked) +{ + if (!attacked || attacked == this) { + return; + } + + auto it = attackedSet.find(attacked->guid); + if (it != attackedSet.end()) { + attackedSet.erase(it); + } +} + void Player::clearAttacked() { attackedSet.clear(); diff --git a/src/player.h b/src/player.h index 40c05e1..16e0f3b 100644 --- a/src/player.h +++ b/src/player.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -382,6 +382,11 @@ class Player final : public Creature, public Cylinder int32_t getMaxHealth() const final { return std::max(1, healthMax + varStats[STAT_MAXHITPOINTS]); } + + uint32_t getMana() const { + return mana; + } + uint32_t getMaxMana() const { return std::max(0, manaMax + varStats[STAT_MAXMANAPOINTS]); } @@ -466,7 +471,7 @@ class Player final : public Creature, public Cylinder static bool lastHitIsPlayer(Creature* lastHitCreature); void changeHealth(int32_t healthChange, bool sendHealthChange = true) final; - void changeMana(int32_t manaChange) final; + void changeMana(int32_t manaChange); void changeSoul(int32_t soulChange); bool isPzLocked() const { @@ -501,7 +506,7 @@ class Player final : public Creature, public Cylinder void getShieldAndWeapon(const Item*& shield, const Item*& weapon) const; void drainHealth(Creature* attacker, int32_t damage) final; - void drainMana(Creature* attacker, int32_t manaLoss) final; + void drainMana(Creature* attacker, int32_t manaLoss); void addManaSpent(uint64_t amount); void addSkillAdvance(skills_t skill, uint64_t count); @@ -524,7 +529,7 @@ class Player final : public Creature, public Cylinder void onTargetCreatureGainHealth(Creature* target, int32_t points) final; bool onKilledCreature(Creature* target, bool lastHit = true) final; void onGainExperience(uint64_t gainExp, Creature* target) final; - void onGainSharedExperience(uint64_t gainExp); + void onGainSharedExperience(uint64_t gainExp, Creature* source); void onAttackedCreatureBlockHit(BlockType_t blockType) final; void onBlockHit() final; void onChangeZone(ZoneType_t zone) final; @@ -541,6 +546,7 @@ class Player final : public Creature, public Cylinder bool hasAttacked(const Player* attacked) const; void addAttacked(const Player* attacked); + void removeAttacked(const Player* attacked); void clearAttacked(); void addUnjustifiedDead(const Player* attacked); void sendCreatureSkull(const Creature* creature) const { @@ -903,8 +909,8 @@ class Player final : public Creature, public Cylinder void checkTradeState(const Item* item); bool hasCapacity(const Item* item, uint32_t count) const; - void gainExperience(uint64_t exp); - void addExperience(uint64_t exp, bool sendText = false, bool applyStages = true); + void gainExperience(uint64_t gainExp, Creature* source); + void addExperience(Creature* source, uint64_t exp, bool sendText = false); void removeExperience(uint64_t exp); void updateInventoryWeight(); @@ -1016,6 +1022,7 @@ class Player final : public Creature, public Cylinder uint32_t guid = 0; uint32_t windowTextId = 0; uint32_t editListId = 0; + uint32_t mana = 0; uint32_t manaMax = 0; int32_t varSkills[SKILL_LAST + 1] = {}; int32_t varStats[STAT_LAST + 1] = {}; diff --git a/src/position.cpp b/src/position.cpp index 5dd6ff0..a972899 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/position.h b/src/position.h index 257f7e5..abd83ad 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocol.cpp b/src/protocol.cpp index ec871e7..34f027c 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocol.h b/src/protocol.h index 8063956..d2a46df 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index b6f604f..6142df6 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -1786,7 +1786,7 @@ void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bo msg.addByte(std::ceil((static_cast(creature->getHealth()) / std::max(creature->getMaxHealth(), 1)) * 100)); msg.addByte(creature->getDirection()); - if (!creature->isInvisible() || !otherPlayer && player->canSeeInvisibility()) { + if (!creature->isInGhostMode() && !creature->isInvisible()) { AddOutfit(msg, creature->getCurrentOutfit()); } else { static Outfit_t outfit; diff --git a/src/protocolgame.h b/src/protocolgame.h index 85ac0c2..b5e7a8d 100644 --- a/src/protocolgame.h +++ b/src/protocolgame.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index a12f107..be9a125 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocollogin.h b/src/protocollogin.h index 4a31331..17004f6 100644 --- a/src/protocollogin.h +++ b/src/protocollogin.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocolstatus.cpp b/src/protocolstatus.cpp index bd6057d..d29b19e 100644 --- a/src/protocolstatus.cpp +++ b/src/protocolstatus.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/protocolstatus.h b/src/protocolstatus.h index a7e499d..c9fef5a 100644 --- a/src/protocolstatus.h +++ b/src/protocolstatus.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/pugicast.h b/src/pugicast.h index b13158c..456a2c0 100644 --- a/src/pugicast.h +++ b/src/pugicast.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/raids.cpp b/src/raids.cpp index b54d8d1..6fd4f1f 100644 --- a/src/raids.cpp +++ b/src/raids.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/raids.h b/src/raids.h index 2239107..3c97d1d 100644 --- a/src/raids.h +++ b/src/raids.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/rsa.cpp b/src/rsa.cpp index 86c425b..affcad4 100644 --- a/src/rsa.cpp +++ b/src/rsa.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/rsa.h b/src/rsa.h index 74a9850..e75335b 100644 --- a/src/rsa.h +++ b/src/rsa.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/scheduler.cpp b/src/scheduler.cpp index d5daf69..28fe462 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/scheduler.h b/src/scheduler.h index c3c04aa..cc8a839 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/script.cpp b/src/script.cpp index 2e0e3cb..40e955e 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator -* Copyright (C) 2017 Alejandro Mujica +* Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -59,7 +59,7 @@ void ScriptReader::nextToken() if (this->RecursionDepth == -1) { - error("ScriptReader::nextToken: Kein Skript zum Lesen ge÷ffnet.\n"); + error("ScriptReader::nextToken: Kein Skript zum Lesen geffnet.\n"); LABEL_30: this->Token = ENDOFFILE; return; @@ -156,7 +156,7 @@ LABEL_3: { if (fclose(this->File[v7])) { - printf("ScriptReader::close: Fehler %d beim Schlie¯en der Datei.\n", RecursionDepth); + printf("ScriptReader::close: Fehler %d beim Schlieen der Datei.\n", RecursionDepth); } --this->RecursionDepth; } diff --git a/src/script.h b/src/script.h index d686132..60d646a 100644 --- a/src/script.h +++ b/src/script.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator -* Copyright (C) 2017 Alejandro Mujica +* Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/scriptmanager.cpp b/src/scriptmanager.cpp index 99c24e3..f372962 100644 --- a/src/scriptmanager.cpp +++ b/src/scriptmanager.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -27,10 +27,12 @@ #include "spells.h" #include "movement.h" #include "globalevent.h" +#include "events.h" Actions* g_actions = nullptr; CreatureEvents* g_creatureEvents = nullptr; Chat* g_chat = nullptr; +Events* g_events = nullptr; GlobalEvents* g_globalEvents = nullptr; Spells* g_spells = nullptr; TalkActions* g_talkActions = nullptr; @@ -40,6 +42,7 @@ extern LuaEnvironment g_luaEnvironment; ScriptingManager::~ScriptingManager() { + delete g_events; delete g_spells; delete g_actions; delete g_talkActions; @@ -93,5 +96,12 @@ bool ScriptingManager::loadScriptSystems() return false; } + g_events = new Events(); + if (!g_events->load()) { + std::cout << "> ERROR: Unable to load events!" << std::endl; + return false; + } + + return true; } diff --git a/src/scriptmanager.h b/src/scriptmanager.h index 0c6fd3f..fc5da53 100644 --- a/src/scriptmanager.h +++ b/src/scriptmanager.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/server.cpp b/src/server.cpp index 80c72ab..1923c74 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/server.h b/src/server.h index a662f1c..f6ff611 100644 --- a/src/server.h +++ b/src/server.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/spawn.cpp b/src/spawn.cpp index 6a9e22a..8b08492 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/spawn.h b/src/spawn.h index bcacfce..aece74e 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/spells.cpp b/src/spells.cpp index 4592a24..a79f0b8 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -212,7 +212,7 @@ InstantSpell* Spells::getInstantSpell(const std::string& words) if (words.length() > resultWords.length()) { size_t spellLen = resultWords.length(); size_t paramLen = words.length() - spellLen; - if (paramLen < 2 || (words[spellLen] != ' ' || words[spellLen + 1] != '"')) { + if (paramLen < 2 || words[spellLen] != ' ') { return nullptr; } } @@ -687,31 +687,33 @@ bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos) return false; } - const Creature* topVisibleCreature = tile->getTopCreature(); - if (blockingCreature && topVisibleCreature) { + const Creature* visibleCreature = tile->getTopCreature(); + if (blockingCreature && visibleCreature) { player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; - } else if (blockingSolid && tile->hasProperty(CONST_PROP_BLOCKPROJECTILE) && tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID)) { + } + else if (blockingSolid && tile->hasFlag(TILESTATE_BLOCKSOLID)) { player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } - if (needTarget && !topVisibleCreature) { + if (needTarget && !visibleCreature) { player->sendCancelMessage(RETURNVALUE_CANONLYUSETHISRUNEONCREATURES); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } - if (aggressive && needTarget && topVisibleCreature && player->hasSecureMode()) { - const Player* targetPlayer = topVisibleCreature->getPlayer(); + if (aggressive && needTarget && visibleCreature && player->hasSecureMode()) { + const Player* targetPlayer = visibleCreature->getPlayer(); if (targetPlayer && targetPlayer != player && player->getSkullClient(targetPlayer) == SKULL_NONE && !Combat::isInPvpZone(player, targetPlayer)) { player->sendCancelMessage(RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } } + return true; } diff --git a/src/spells.h b/src/spells.h index 10a7a35..24ddab3 100644 --- a/src/spells.h +++ b/src/spells.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/talkaction.cpp b/src/talkaction.cpp index 5474c1a..8e2a0b7 100644 --- a/src/talkaction.cpp +++ b/src/talkaction.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -36,9 +36,6 @@ TalkActions::~TalkActions() void TalkActions::clear() { - for (TalkAction* talkAction : talkActions) { - delete talkAction; - } talkActions.clear(); scriptInterface.reInitState(); @@ -64,15 +61,17 @@ Event* TalkActions::getEvent(const std::string& nodeName) bool TalkActions::registerEvent(Event* event, const pugi::xml_node&) { - talkActions.push_front(static_cast(event)); // event is guaranteed to be a TalkAction + auto talkAction = std::unique_ptr(static_cast(event)); // event is guaranteed to be a TalkAction + talkActions.push_front(std::move(*talkAction)); + return true; } TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type, const std::string& words) const { size_t wordsLength = words.length(); - for (TalkAction* talkAction : talkActions) { - const std::string& talkactionWords = talkAction->getWords(); + for (const TalkAction& talkAction : talkActions) { + const std::string& talkactionWords = talkAction.getWords(); size_t talkactionLength = talkactionWords.length(); if (wordsLength < talkactionLength || strncasecmp(words.c_str(), talkactionWords.c_str(), talkactionLength) != 0) { continue; @@ -86,7 +85,7 @@ TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type } trim_left(param, ' '); - char separator = talkAction->getSeparator(); + char separator = talkAction.getSeparator(); if (separator != ' ') { if (!param.empty()) { if (param.front() != separator) { @@ -98,7 +97,7 @@ TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type } } - if (talkAction->executeSay(player, param, type)) { + if (talkAction.executeSay(player, param, type)) { return TALKACTION_CONTINUE; } else { return TALKACTION_BREAK; diff --git a/src/talkaction.h b/src/talkaction.h index a9fdd62..765f336 100644 --- a/src/talkaction.h +++ b/src/talkaction.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -51,8 +51,7 @@ class TalkActions : public BaseEvents bool registerEvent(Event* event, const pugi::xml_node& node) final; void clear() final; - // TODO: Store TalkAction objects directly in the list instead of using pointers - std::forward_list talkActions; + std::forward_list talkActions; LuaScriptInterface scriptInterface; }; diff --git a/src/tasks.cpp b/src/tasks.cpp index 3626cf4..1ed727f 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/tasks.h b/src/tasks.h index ad08653..4ea459c 100644 --- a/src/tasks.h +++ b/src/tasks.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/teleport.cpp b/src/teleport.cpp index 92a2a23..562fd0b 100644 --- a/src/teleport.cpp +++ b/src/teleport.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/teleport.h b/src/teleport.h index fd6cd2e..410b2ab 100644 --- a/src/teleport.h +++ b/src/teleport.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/thing.cpp b/src/thing.cpp index c053b29..21e72f6 100644 --- a/src/thing.cpp +++ b/src/thing.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/thing.h b/src/thing.h index ee9304e..b23be2b 100644 --- a/src/thing.h +++ b/src/thing.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/thread_holder_base.h b/src/thread_holder_base.h index 97dd31d..91280db 100644 --- a/src/thread_holder_base.h +++ b/src/thread_holder_base.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/tile.cpp b/src/tile.cpp index 14da480..5b2afbb 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -100,6 +100,25 @@ bool Tile::hasHeight(uint32_t n) const return false; } +int32_t Tile::getHeight() { + int32_t height = 0; + if (ground) { + if (ground->hasProperty(CONST_PROP_HASHEIGHT)) { + ++height; + } + } + + if (const TileItemVector* items = getItemList()) { + for (ItemVector::const_iterator it = items->begin(); it != items->end(); ++it) { + if ((*it)->hasProperty(CONST_PROP_HASHEIGHT)) { + ++height; + } + } + } + + return std::min(height, 4); +} + size_t Tile::getCreatureCount() const { if (const CreatureVector* creatures = getCreatures()) { @@ -1459,22 +1478,16 @@ bool Tile::isMoveableBlocking() const return !ground || hasFlag(TILESTATE_BLOCKSOLID); } -Item* Tile::getUseItem() const +Item* Tile::getUseItem(int32_t index) const { const TileItemVector* items = getItemList(); if (!items || items->size() == 0) { return ground; } - for (Item* item : boost::adaptors::reverse(*items)) { - if (Item::items[item->getID()].forceUse) { - return item; - } + if (Thing* thing = getThing(index)) { + return thing->getItem(); } - Item* item = items->getTopDownItem(); - if (!item) { - item = items->getTopTopItem(); - } - return item; + return nullptr; } diff --git a/src/tile.h b/src/tile.h index 0f7f2a9..99f3ffd 100644 --- a/src/tile.h +++ b/src/tile.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -220,6 +220,7 @@ class Tile : public Cylinder } bool hasHeight(uint32_t n) const; + int32_t getHeight(); std::string getDescription(int32_t lookDistance) const final; @@ -265,7 +266,7 @@ class Tile : public Cylinder return false; } - Item* getUseItem() const; + Item* getUseItem(int32_t index) const; Item* getGround() const { return ground; diff --git a/src/tools.cpp b/src/tools.cpp index b2b299b..14e0320 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 @@ -1112,6 +1112,21 @@ const char* getReturnMessage(ReturnValue value) case RETURNVALUE_YOUARENOTTHEOWNER: return "You are not the owner."; + case RETURNVALUE_TRADEPLAYERFARAWAY: + return "Trade player is too far away."; + + case RETURNVALUE_YOUDONTOWNTHISHOUSE: + return "You don't own this house."; + + case RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE: + return "Trade player already owns a house."; + + case RETURNVALUE_TRADEPLAYERHIGHESTBIDDER: + return "Trade player is currently the highest bidder of an auctioned house."; + + case RETURNVALUE_YOUCANNOTTRADETHISHOUSE: + return "You can not trade this house."; + default: // RETURNVALUE_NOTPOSSIBLE, etc return "Sorry, not possible."; } diff --git a/src/tools.h b/src/tools.h index c4cd4fe..1edc868 100644 --- a/src/tools.h +++ b/src/tools.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/town.h b/src/town.h index a64939b..9e50720 100644 --- a/src/town.h +++ b/src/town.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/vocation.cpp b/src/vocation.cpp index 5ac8f36..98a9973 100644 --- a/src/vocation.cpp +++ b/src/vocation.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/vocation.h b/src/vocation.h index ad28e12..b6bc48e 100644 --- a/src/vocation.h +++ b/src/vocation.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/waitlist.cpp b/src/waitlist.cpp index 20acc80..2743629 100644 --- a/src/waitlist.cpp +++ b/src/waitlist.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/waitlist.h b/src/waitlist.h index 00e7ebf..77dd2c5 100644 --- a/src/waitlist.h +++ b/src/waitlist.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/wildcardtree.cpp b/src/wildcardtree.cpp index 6a9cac2..fa5eef8 100644 --- a/src/wildcardtree.cpp +++ b/src/wildcardtree.cpp @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/src/wildcardtree.h b/src/wildcardtree.h index 730ed27..2d16981 100644 --- a/src/wildcardtree.h +++ b/src/wildcardtree.h @@ -1,6 +1,6 @@ /** * Tibia GIMUD Server - a free and open-source MMORPG server emulator - * Copyright (C) 2017 Alejandro Mujica + * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 diff --git a/vc14/theforgottenserver.vcxproj b/vc14/theforgottenserver.vcxproj index c601ab1..8d24fba 100644 --- a/vc14/theforgottenserver.vcxproj +++ b/vc14/theforgottenserver.vcxproj @@ -149,7 +149,6 @@ - @@ -161,6 +160,7 @@ + @@ -225,7 +225,6 @@ - @@ -239,6 +238,7 @@ +