From e4759c78831fa3ec70b7b23fdb9ec3ae0f2f3fec Mon Sep 17 00:00:00 2001 From: Luan Colombo <94877887+luancolombo@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:26:36 +0000 Subject: [PATCH 1/9] fix: buy house (#1903) Wrong enum type. --- data/scripts/talkactions/player/buy_house.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/scripts/talkactions/player/buy_house.lua b/data/scripts/talkactions/player/buy_house.lua index 90b33aef735..ee0ce8da5ee 100644 --- a/data/scripts/talkactions/player/buy_house.lua +++ b/data/scripts/talkactions/player/buy_house.lua @@ -1,7 +1,7 @@ local buyHouse = TalkAction("!buyhouse") function buyHouse.onSay(player, words, param) - local housePrice = configManager.getNumber(configKeys.HOUSE_PRICE) + local housePrice = configManager.getNumber(configKeys.HOUSE_PRICE_PER_SQM) if housePrice == -1 then return true end From cdc68ceb837fea943651c704b847620fd583505e Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Fri, 24 Nov 2023 22:18:30 -0800 Subject: [PATCH 2/9] fix: adjust target reach assumption immediately after setting (#1902) This is a subtle bug, but it prevents the `challenge` spell from working most of the time. This is what occurs: 1. Monster attacks someone, it's their only option for a target when they do, so whether they can reach it, it sets it; it keeps trying to set it until it works. 2. Someone challenges it; it sets its target to the new person (which runs the code above) 3. `onThink` runs, `hasFollowPath` is still `false` because we haven't had time to run the path finding algo yet; we think it's unreachable and switch away to the other target (with we already had a path for) Solution: by "assuming" we have a path, we let the pathfinding algo run, in the meantime, `onThink` assumes everything is OK. --- src/creatures/creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index b0f0b446384..e9b674c54f5 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -1064,7 +1064,7 @@ bool Creature::setFollowCreature(std::shared_ptr creature) { onWalkAborted(); } - hasFollowPath = false; + hasFollowPath = true; forceUpdateFollowPath = false; m_followCreature = creature; isUpdatingPath = true; From 9b093c7b374321afba0c20299066bdad9bf4fb62 Mon Sep 17 00:00:00 2001 From: sebbesiren <35768829+sebbesiren@users.noreply.github.com> Date: Sat, 25 Nov 2023 07:58:02 +0100 Subject: [PATCH 3/9] fix: angry demon (#1894) Angry demons were accidentally super strong. --- .../quests/annihilator/angry_demon.lua | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/data-otservbr-global/monster/quests/annihilator/angry_demon.lua b/data-otservbr-global/monster/quests/annihilator/angry_demon.lua index 84200b6232b..3d9d9a4fe80 100644 --- a/data-otservbr-global/monster/quests/annihilator/angry_demon.lua +++ b/data-otservbr-global/monster/quests/annihilator/angry_demon.lua @@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Angry Demon") local monster = {} monster.description = "an angry demon" -monster.experience = 30000 +monster.experience = 6000 monster.outfit = { lookType = 35, lookHead = 0, @@ -13,8 +13,8 @@ monster.outfit = { lookMount = 0, } -monster.health = 60000 -monster.maxHealth = 60000 +monster.health = 8200 +monster.maxHealth = 8200 monster.race = "fire" monster.corpse = 5995 monster.speed = 128 @@ -57,7 +57,7 @@ monster.light = { monster.summon = { maxSummons = 1, summons = { - { name = "massive fire elemental", chance = 25, interval = 2000, count = 1 }, + { name = "fire elemental", chance = 10, interval = 2000, count = 1 }, }, } @@ -108,19 +108,12 @@ monster.loot = { } monster.attacks = { - -- {name ="melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -520}, - -- {name ="combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -120, range = 7, target = false}, - -- {name ="combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -150, maxDamage = -250, range = 7, radius = 7, shootEffect = CONST_ANI_FIRE, effect = CONST_ME_FIREAREA, target = true}, - -- {name ="firefield", interval = 2000, chance = 10, range = 7, radius = 1, shootEffect = CONST_ANI_FIRE, target = true}, - -- {name ="combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = -300, maxDamage = -490, length = 8, spread = 3, effect = CONST_ME_PURPLEENERGY, target = false}, - -- {name ="combat", interval = 2000, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -210, maxDamage = -300, range = 1, shootEffect = CONST_ANI_ENERGY, target = false}, - -- {name ="speed", interval = 2000, chance = 15, speedChange = -700, radius = 1, effect = CONST_ME_MAGIC_RED, target = true, duration = 30000} - { name = "melee", interval = 2000, chance = 500, minDamage = 0, maxDamage = -1940 }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -150, range = 7, target = false }, - { name = "combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -650, maxDamage = -900, range = 7, radius = 7, shootEffect = CONST_ANI_FIRE, effect = CONST_ME_FIREAREA, target = true }, + { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -520 }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -120, range = 7, target = false }, + { name = "combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -150, maxDamage = -250, range = 7, radius = 7, shootEffect = CONST_ANI_FIRE, effect = CONST_ME_FIREAREA, target = true }, { name = "firefield", interval = 2000, chance = 10, range = 7, radius = 1, shootEffect = CONST_ANI_FIRE, target = true }, - { name = "combat", interval = 2000, chance = 20, type = COMBAT_LIFEDRAIN, minDamage = -300, maxDamage = -800, length = 8, spread = 0, effect = CONST_ME_PURPLEENERGY, target = false }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -1050, maxDamage = -1500, range = 1, shootEffect = CONST_ANI_ENERGY, target = true }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = -300, maxDamage = -490, length = 8, spread = 3, effect = CONST_ME_PURPLEENERGY, target = false }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -210, maxDamage = -300, range = 1, shootEffect = CONST_ANI_ENERGY, target = false }, { name = "speed", interval = 2000, chance = 15, speedChange = -700, radius = 1, effect = CONST_ME_MAGIC_RED, target = true, duration = 30000 }, } From 278d8570d74d8cc11979c5e625064825826d77e2 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 25 Nov 2023 12:24:20 -0300 Subject: [PATCH 4/9] fix: cask and kegs (#1905) --- data-otservbr-global/scripts/actions/other/cask_kegs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/actions/other/cask_kegs.lua b/data-otservbr-global/scripts/actions/other/cask_kegs.lua index f15ea2fd0e7..872d30d613c 100644 --- a/data-otservbr-global/scripts/actions/other/cask_kegs.lua +++ b/data-otservbr-global/scripts/actions/other/cask_kegs.lua @@ -39,7 +39,7 @@ function flasks.onUse(player, item, fromPosition, target, toPosition, isHotkey) return false end - local charges = item:getCharges() + local charges = target:getCharges() local itemCount = item:getCount() local recharged = itemCount From 63de45627c7821f376e1cff083640eb070b2593c Mon Sep 17 00:00:00 2001 From: ArtDev <104323065+artdev-br@users.noreply.github.com> Date: Sat, 25 Nov 2023 14:41:56 -0300 Subject: [PATCH 5/9] fix: monsters bestiary class (#1908) When opening the bestiary window in the Tibia client, the Hybrids category appeared instead of Humanoid. And besides, there were only four monsters inside. --- data-otservbr-global/monster/reptiles/boar_man.lua | 2 +- data-otservbr-global/monster/reptiles/crape_man.lua | 2 +- data-otservbr-global/monster/reptiles/liodile.lua | 2 +- data-otservbr-global/monster/reptiles/rhindeer.lua | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/monster/reptiles/boar_man.lua b/data-otservbr-global/monster/reptiles/boar_man.lua index b2568e7ce2a..53ec742ca22 100644 --- a/data-otservbr-global/monster/reptiles/boar_man.lua +++ b/data-otservbr-global/monster/reptiles/boar_man.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2339 monster.Bestiary = { - class = "Hybrids", + class = "Humanoid", race = BESTY_RACE_HUMANOID, toKill = 2500, FirstUnlock = 100, diff --git a/data-otservbr-global/monster/reptiles/crape_man.lua b/data-otservbr-global/monster/reptiles/crape_man.lua index 031dbf2ca48..99d2db01f34 100644 --- a/data-otservbr-global/monster/reptiles/crape_man.lua +++ b/data-otservbr-global/monster/reptiles/crape_man.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2337 monster.Bestiary = { - class = "Hybrids", + class = "Humanoid", race = BESTY_RACE_HUMANOID, toKill = 2500, FirstUnlock = 100, diff --git a/data-otservbr-global/monster/reptiles/liodile.lua b/data-otservbr-global/monster/reptiles/liodile.lua index 77676a87a6b..d7f79621679 100644 --- a/data-otservbr-global/monster/reptiles/liodile.lua +++ b/data-otservbr-global/monster/reptiles/liodile.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2338 monster.Bestiary = { - class = "Hybrids", + class = "Humanoid", race = BESTY_RACE_HUMANOID, toKill = 2500, FirstUnlock = 100, diff --git a/data-otservbr-global/monster/reptiles/rhindeer.lua b/data-otservbr-global/monster/reptiles/rhindeer.lua index d9de46b857c..743d9fb492a 100644 --- a/data-otservbr-global/monster/reptiles/rhindeer.lua +++ b/data-otservbr-global/monster/reptiles/rhindeer.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2342 monster.Bestiary = { - class = "Hybrids", + class = "Humanoid", race = BESTY_RACE_HUMANOID, toKill = 2500, FirstUnlock = 100, From 1a8e2956bb88fbf9d8a9b75065597fd0ae3d78fa Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 25 Nov 2023 14:44:51 -0300 Subject: [PATCH 6/9] fix: kick yourself from house (#1906) Resolves #1891 --- data-otservbr-global/scripts/spells/house/kick.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data-otservbr-global/scripts/spells/house/kick.lua b/data-otservbr-global/scripts/spells/house/kick.lua index be7c7517a35..b4b583c1007 100644 --- a/data-otservbr-global/scripts/spells/house/kick.lua +++ b/data-otservbr-global/scripts/spells/house/kick.lua @@ -4,6 +4,13 @@ function spell.onCastSpell(player, variant) local targetPlayer = Player(variant:getString()) or player local guest = targetPlayer:getTile():getHouse() local owner = player:getTile():getHouse() + -- Owner kick yourself from house + if targetPlayer == player then + player:getPosition():sendMagicEffect(CONST_ME_POFF) + player:teleportTo(owner:getExitPosition()) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + return true + end if not owner or not guest or not guest:kickPlayer(player, targetPlayer) then player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) player:getPosition():sendMagicEffect(CONST_ME_POFF) From 34329159cfab86f134f10b0c092de66416688eb3 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 25 Nov 2023 16:37:41 -0300 Subject: [PATCH 7/9] improve: '/pos' command parsing for multiple formats (#1912) Refactored the '/pos' command in the game's scripting to support multiple input formats more robustly. The command now accurately interprets positions from table format '{x = ..., y = ..., z = ...}', the 'Position(..., ..., ...)' format, and the standard 'x, y, z' coordinate format. The update includes improved error handling and clearer user feedback for invalid inputs. Resolves #1846 --- data/scripts/talkactions/gm/position.lua | 48 ++++++++++++++++++------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/data/scripts/talkactions/gm/position.lua b/data/scripts/talkactions/gm/position.lua index f68b4210996..1869b109c5f 100644 --- a/data/scripts/talkactions/gm/position.lua +++ b/data/scripts/talkactions/gm/position.lua @@ -1,22 +1,46 @@ local position = TalkAction("/pos", "!pos") +local function extractCoordinates(input) + local patterns = { + -- table format + "{%s*x%s*=%s*(%d+)%s*,%s*y%s*=%s*(%d+)%s*,%s*z%s*=%s*(%d+)%s*}", + -- Position format + "Position%s*%((%d+)%s*,%s*(%d+)%s*,%s*(%d+)%s*%)", + -- x, y, z format + "(%d+)%s*,%s*(%d+)%s*,%s*(%d+)", + } + + for _, pattern in ipairs(patterns) do + local x, y, z = string.match(input, pattern) + if x and y and z then + return tonumber(x), tonumber(y), tonumber(z) + end + end +end + function position.onSay(player, words, param) -- create log logCommand(player, words, param) - local param = string.gsub(param, "%s+", "") - local tile = load("return " .. param)() - local split = param:split(",") - if type(tile) == "table" and tile.x and tile.y and tile.z then - player:teleportTo(Position(tile.x, tile.y, tile.z)) - elseif split and param ~= "" then - player:teleportTo(Position(split[1], split[2], split[3])) - elseif param == "" then - local playerPosition = player:getPosition() - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your current position is: \z - " .. playerPosition.x .. ", " .. playerPosition.y .. ", " .. playerPosition.z .. ".") + if param == "" then + local pos = player:getPosition() + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your current position is: " .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. ".") + return + end + + local x, y, z = extractCoordinates(param) + if x and y and z then + local teleportPosition = Position(x, y, z) + local tile = Tile(teleportPosition) + if not tile then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Invalid tile or position. Send a valid position.") + return + end + + player:teleportTo(teleportPosition) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Invalid position format. Use one of the following formats: \n/pos {x = ..., y = ..., z = ...}\n/pos Position(..., ..., ...)\n/pos x, y, z.") end - return true end position:separator(" ") From 8100402932d70b31f8e4ad7484d5fa84f2a8a74b Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Sat, 25 Nov 2023 12:27:10 -0800 Subject: [PATCH 8/9] fix: critical hit damage calculation (#1913) Resolves #1893 Resolves #1888 --- src/creatures/combat/combat.cpp | 9 ++++----- src/game/game.cpp | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index f5ec13f3473..151e5458a85 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -2024,13 +2024,12 @@ void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptrgetPlayer(); auto monster = caster->getMonster(); if (player) { chance = player->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE); - multiplier = player->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE); - + bonus = player->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE); if (target) { uint16_t playerCharmRaceid = player->parseRacebyCharm(CHARM_LOW, false, 0); if (playerCharmRaceid != 0) { @@ -2048,8 +2047,8 @@ void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptrcritChance(); } - multiplier += damage.criticalDamage; - multiplier = 1 + multiplier / 100; + bonus += damage.criticalDamage; + double multiplier = 1.0 + static_cast(bonus) / 100; chance += (uint16_t)damage.criticalChance; if (chance != 0 && uniform_random(1, 100) <= chance) { diff --git a/src/game/game.cpp b/src/game/game.cpp index b6733395831..e9cb108942e 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -6625,10 +6625,8 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt } } + std::string attackMsg = fmt::format("{} attack", damage.critical ? "critical " : " "); std::stringstream ss; - ss << (damage.critical ? "critical " : " ") << "attack"; - std::string attackMsg = ss.str(); - ss.str({}); if (target->hasCondition(CONDITION_MANASHIELD) && damage.primary.type != COMBAT_UNDEFINEDDAMAGE) { int32_t manaDamage = std::min(target->getMana(), healthChange); @@ -6915,17 +6913,19 @@ void Game::buildMessageAsSpectator( ) const { if (spectatorMessage.empty()) { ss.str({}); + auto attackMsg = damage.critical ? "critical " : ""; + auto article = damage.critical ? "a" : "an"; ss << ucfirst(target->getNameDescription()) << " loses " << damageString; if (attacker) { ss << " due to "; if (attacker == target) { if (targetPlayer) { - ss << targetPlayer->getPossessivePronoun() << " own attack"; + ss << targetPlayer->getPossessivePronoun() << " own " << attackMsg << "attack"; } else { - ss << "its own attack"; + ss << "its own " << attackMsg << "attack"; } } else { - ss << "an attack by " << attacker->getNameDescription(); + ss << article << " " << attackMsg << "attack by " << attacker->getNameDescription(); } } ss << '.'; @@ -6945,13 +6945,15 @@ void Game::buildMessageAsTarget( const std::string &damageString ) const { ss.str({}); + auto attackMsg = damage.critical ? "critical " : ""; + auto article = damage.critical ? "a" : "an"; ss << "You lose " << damageString; if (!attacker) { ss << '.'; } else if (targetPlayer == attackerPlayer) { - ss << " due to your own attack."; + ss << " due to your own " << attackMsg << "attack."; } else { - ss << " due to an attack by " << attacker->getNameDescription() << '.'; + ss << " due to " << article << " " << attackMsg << "attack by " << attacker->getNameDescription() << '.'; } if (damage.extension) { ss << " " << damage.exString; @@ -6965,7 +6967,7 @@ void Game::buildMessageAsAttacker( std::stringstream &ss, const std::string &damageString ) const { ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString << " due to your attack."; + ss << ucfirst(target->getNameDescription()) << " loses " << damageString << " due to your " << (damage.critical ? "critical " : " ") << "attack."; if (damage.extension) { ss << " " << damage.exString; } From 33d62b4482961cff62f149e480277e7c99d7bebb Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Sat, 25 Nov 2023 22:32:08 -0300 Subject: [PATCH 9/9] fix: NextUseConcoctionTime _G table (#1916) --- data/global.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/global.lua b/data/global.lua index 04a8ff78fe9..4caec8c69f9 100644 --- a/data/global.lua +++ b/data/global.lua @@ -100,8 +100,8 @@ if not _G.NextUseXpStamina then _G.NextUseXpStamina = {} end -if not _G._G.NextUseConcoctionTime then - _G._G.NextUseConcoctionTime = {} +if not _G.NextUseConcoctionTime then + _G.NextUseConcoctionTime = {} end -- Delay potion