From 98c4a10b5d063ce3bb9976affa46a527a780a8f8 Mon Sep 17 00:00:00 2001
From: Majesty <32709570+majestyotbr@users.noreply.github.com>
Date: Tue, 9 Jul 2024 15:06:33 -0300
Subject: [PATCH 01/15] docs: update and simplify readme infos (#2743)
---
README.md | 90 +++++++++++++------------------------------------------
1 file changed, 20 insertions(+), 70 deletions(-)
diff --git a/README.md b/README.md
index c7cb88e7b32..a5d3aa20797 100644
--- a/README.md
+++ b/README.md
@@ -1,93 +1,43 @@
# OpenTibiaBR - Canary
[![Discord Channel](https://img.shields.io/discord/528117503952551936.svg?style=flat-square&logo=discord)](https://discord.gg/gvTj5sh9Mp)
-[![GitHub issues](https://img.shields.io/github/issues/opentibiabr/canary)](https://github.com/opentibiabr/canary/issues)
-[![GitHub pull request](https://img.shields.io/github/issues-pr/opentibiabr/canary)](https://github.com/opentibiabr/canary/pulls)
-[![Contributors](https://img.shields.io/github/contributors/opentibiabr/canary.svg?style=flat-square)](https://github.com/opentibiabr/canary/graphs/contributors)
-[![GitHub](https://img.shields.io/github/license/opentibiabr/canary)](https://github.com/opentibiabr/canary/blob/master/LICENSE)
-
-![GitHub repo size](https://img.shields.io/github/repo-size/opentibiabr/canary)
-
-[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=opentibiabr_canary&metric=alert_status)](https://sonarcloud.io/dashboard?id=opentibiabr_canary)
-
-## Builds
-
[![Build - Ubuntu](https://github.com/opentibiabr/canary/actions/workflows/build-ubuntu.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-ubuntu.yml)
[![Build - Windows - CMake](https://github.com/opentibiabr/canary/actions/workflows/build-windows-cmake.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-windows-cmake.yml)
[![Build - Windows - Solution](https://github.com/opentibiabr/canary/actions/workflows/build-windows-solution.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-windows-solution.yml)
+[![Build - Docker](https://github.com/opentibiabr/canary/actions/workflows/build-docker.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-docker.yml)
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=opentibiabr_canary&metric=alert_status)](https://sonarcloud.io/dashboard?id=opentibiabr_canary)
+![GitHub repo size](https://img.shields.io/github/repo-size/opentibiabr/canary)
+[![GitHub](https://img.shields.io/github/license/opentibiabr/canary)](https://github.com/opentibiabr/canary/blob/main/LICENSE)
-## Docker
-
-`docker pull opentibiabr/canary:latest`
-[![Automation](https://img.shields.io/docker/cloud/automated/opentibiabr/canary)](https://hub.docker.com/r/opentibiabr/canary)
-[![Image Size](https://img.shields.io/docker/image-size/opentibiabr/canary)](https://hub.docker.com/r/opentibiabr/canary/tags?page=1&ordering=last_updated)
-![Pulls](https://img.shields.io/docker/pulls/opentibiabr/canary)
-[![Build](https://img.shields.io/docker/cloud/build/opentibiabr/canary)](https://hub.docker.com/r/opentibiabr/canary/builds)
-
-## Project
-
-OpenTibiaBR - Canary is a free and open-source MMORPG server emulator written in C++.
-
-It is a fork of the [OTServBR-Global](https://github.com/opentibiabr/otservbr-global) project. You can see the
-repository history in the [releases](https://github.com/opentibiabr/otservbr-global/releases/).
-
-This project was created with the intention of being a base as clean as possible, to work as an MMORPG engine and not
-necessarily linked to Tibia Global, although it will also work. The OpenTibiaBR - Global was adapted to work with the
-source of the Canary, so that it will be the first repository to use this engine.
-
-To connect to the server and to take a stable experience, you can
-use [mehah's otclient](https://github.com/mehah/otclient)
+OpenTibiaBR - Canary is a free and open-source MMORPG server emulator written in C++. It is a fork of the [OTServBR-Global](https://github.com/opentibiabr/otservbr-global) project. To connect to the server and to take a stable experience, you can use [mehah's otclient](https://github.com/mehah/otclient)
or [tibia client](https://github.com/dudantas/tibia-client/releases/latest) and if you want to edit something, check
-our [customized tools](https://docs.opentibiabr.com/opentibiabr/downloads/tools).
-
-If you want edit the map, use the [own remere's map editor](https://github.com/opentibiabr/remeres-map-editor/).
+our [customized tools](https://docs.opentibiabr.com/opentibiabr/downloads/tools). If you want to edit the map, use our own [remere's map editor](https://github.com/opentibiabr/remeres-map-editor/).
-You are subject to our code of conduct, read
-at [this link](https://github.com/opentibiabr/canary/blob/master/CODE_OF_CONDUCT.md).
-
-### Getting **Started**
+## Getting Started
* [Gitbook](https://docs.opentibiabr.com/opentibiabr/projects/canary).
* [Wiki](https://github.com/opentibiabr/canary/wiki).
-### Issues
+## Support
+
+If you need help, please visit our [discord](https://discord.gg/gvTj5sh9Mp). Our issue tracker is not a support forum, and using it as one will result in your issue being closed.
-We use the [issue tracker on GitHub](https://github.com/opentibiabr/canary/issues). Keep in mind that everyone who is
-watching the repository gets notified by e-mail when there is an activity, so be thoughtful and avoid writing comments
-that aren't meant for an issue (e.g. "+1"). If you'd like for an issue to be fixed faster, you should either fix it
-yourself and submit a pull request, or place a bounty on the issue.
+## Contributing
-### Pull requests
+Here are some ways you can contribute:
-Before [creating a pull request](https://github.com/opentibiabr/canary/pulls) please keep in mind:
+* [Issue Tracker](https://github.com/opentibiabr/canary/issues/new/choose).
+* [Pull Request](https://github.com/opentibiabr/canary/pulls).
-* Do not send Pull Request changing the map, as we can't review the changes it's better to use
- our [Discord](https://discord.gg/gvTj5sh9Mp) to talk about or send the map changes to the responsible for updating it.
-* Focus on fixing only one thing, mixing too much things on the same Pull Request make it harder to review, harder to
- test and if we need to revert the change it will remove other things together.
-* Follow the project indentation, if your editor support you can use the [editorconfig](https://editorconfig.org/) to
- automatic configure the indentation.
-* There are people that doesn't play the game on the official server, so explain your changes to help understand what
- are you changing and why.
-* Avoid opening a Pull Request to just update one line of an xml file.
+You are subject to our code of conduct, read at [this link](https://github.com/opentibiabr/canary/blob/main/CODE_OF_CONDUCT.md).
-### Special Thanks
+## Special Thanks
-* our partners
-* our crew (majesty, gpedro, eduardo dantas, foot)
-* [our contributors](https://github.com/opentibiabr/canary/graphs/contributors)
-* [fear lucien](https://github.com/FearLucien)
-* [cjaker](https://github.com/Cjaker)
-* [slavidodo](https://github.com/slavidodo)
-* [mignari and our awesome tools](https://github.com/ottools)
-* [mattyx14/otxserver](https://github.com/mattyx14/otxserver) and contributors
-* [otland/forgottenserver](https://github.com/otland/forgottenserver) and contributors
-* [saiyansking/optimized_forgottenserver](https://github.com/SaiyansKing/optimized_forgottenserver) and contributors
-* if we forget someone, we apologize by forgot you. but you know, **forgot**tenserver.
+- Our contributors ([Canary](https://github.com/opentibiabr/canary/graphs/contributors) | [OTServBR-Global](https://github.com/opentibiabr/otservbr-global/graphs/contributors)).
-### **Sponsors**
+## Sponsors
-See our [donate page](https://docs.opentibiabr.com/home/donate)
+See our [donate page](https://docs.opentibiabr.com/home/donate).
## Project supported by JetBrains
@@ -98,6 +48,6 @@ other open-source initiatives.
-### Partners
+## Partners
[![Supported by OTServ Brasil](https://raw.githubusercontent.com/otbr/otserv-brasil/main/otbr.png)](https://forums.otserv.com.br)
From ff686d7472aebbb71f53fa9ab78c63231525f89c Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Thu, 11 Jul 2024 06:15:12 -0300
Subject: [PATCH 02/15] fix: duplicate code in the combat and onPrepareDeath
event (#2749)
Resolves #2748
Resolves #2747
---
src/game/game.cpp | 25 +++++++++----------------
1 file changed, 9 insertions(+), 16 deletions(-)
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 7ef6b7a9038..702c46f3f2d 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -6935,14 +6935,6 @@ int32_t Game::applyHealthChange(CombatDamage &damage, std::shared_ptr
}
}
}
-
- if (damage.primary.value >= targetHealth) {
- damage.primary.value = targetHealth;
- damage.secondary.value = 0;
- } else if (damage.secondary.value) {
- damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value);
- }
-
return targetHealth;
}
@@ -7300,19 +7292,12 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt
}
}
- auto targetHealth = applyHealthChange(damage, target);
- if (damage.primary.value >= targetHealth) {
- damage.primary.value = targetHealth;
- damage.secondary.value = 0;
- } else if (damage.secondary.value) {
- damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value);
- }
-
// Apply Custom PvP Damage (must be placed here to avoid recursive calls)
if (attackerPlayer && targetPlayer) {
applyPvPDamage(damage, attackerPlayer, targetPlayer);
}
+ auto targetHealth = target->getHealth();
realDamage = damage.primary.value + damage.secondary.value;
if (realDamage == 0) {
return true;
@@ -7324,6 +7309,14 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt
}
}
+ targetHealth = applyHealthChange(damage, target);
+ if (damage.primary.value >= targetHealth) {
+ damage.primary.value = targetHealth;
+ damage.secondary.value = 0;
+ } else if (damage.secondary.value) {
+ damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value);
+ }
+
target->drainHealth(attacker, realDamage);
if (realDamage > 0 && targetMonster) {
if (attackerPlayer && attackerPlayer->getPlayer()) {
From b799436bb172f0bb0bd37ca5c896cead1bd4732f Mon Sep 17 00:00:00 2001
From: Renato Machado
Date: Thu, 11 Jul 2024 06:16:10 -0300
Subject: [PATCH 03/15] fix: prevent crash in pathfinder async (#2742)
---
src/creatures/creature.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp
index c68ab4f351b..e3474ab2436 100644
--- a/src/creatures/creature.cpp
+++ b/src/creatures/creature.cpp
@@ -1050,6 +1050,10 @@ void Creature::goToFollowCreature_async(std::function &&onComplete) {
pathfinderRunning.store(true);
g_dispatcher().asyncEvent([self = getCreature()] {
+ if (!self || self->isRemoved()) {
+ return;
+ }
+
self->goToFollowCreature();
self->pathfinderRunning.store(false);
});
From 3f12a8542ad62ec12367c8ff3205b4404fa2a881 Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Thu, 11 Jul 2024 06:20:34 -0300
Subject: [PATCH 04/15] feat: new global event OnSave (#2025)
---
data/libs/functions/revscriptsys.lua | 4 ++++
src/game/game.cpp | 5 ++++-
src/lua/functions/core/game/global_functions.cpp | 2 ++
src/lua/functions/events/global_event_functions.cpp | 2 ++
src/lua/functions/events/global_event_functions.hpp | 1 +
src/lua/global/globalevent.cpp | 13 ++++++++++++-
src/lua/global/globalevent.hpp | 2 ++
src/lua/lua_definitions.hpp | 1 +
src/server/signals.cpp | 2 ++
9 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/data/libs/functions/revscriptsys.lua b/data/libs/functions/revscriptsys.lua
index 84b8275e3ce..515522d6443 100644
--- a/data/libs/functions/revscriptsys.lua
+++ b/data/libs/functions/revscriptsys.lua
@@ -233,6 +233,10 @@ do
self:type("periodchange")
self:onPeriodChange(value)
return
+ elseif key == "onSave" then
+ self:type("save")
+ self:onSave(value)
+ return
end
rawset(self, key, value)
end
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 702c46f3f2d..2348d384569 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -593,7 +593,8 @@ void Game::setGameState(GameState_t newState) {
}
case GAME_STATE_SHUTDOWN: {
- g_globalEvents().execute(GLOBALEVENT_SHUTDOWN);
+ g_globalEvents().save();
+ g_globalEvents().shutdown();
// kick all players that are still online
auto it = players.begin();
@@ -611,6 +612,8 @@ void Game::setGameState(GameState_t newState) {
}
case GAME_STATE_CLOSED: {
+ g_globalEvents().save();
+
/* kick all players without the CanAlwaysLogin flag */
auto it = players.begin();
while (it != players.end()) {
diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp
index c8a857cfffa..bed0ff27026 100644
--- a/src/lua/functions/core/game/global_functions.cpp
+++ b/src/lua/functions/core/game/global_functions.cpp
@@ -16,6 +16,7 @@
#include "lua/functions/core/game/global_functions.hpp"
#include "lua/scripts/lua_environment.hpp"
#include "lua/scripts/script_environment.hpp"
+#include "lua/global/globalevent.hpp"
#include "server/network/protocol/protocolstatus.hpp"
#include "creatures/players/wheel/player_wheel.hpp"
#include "lua/global/lua_timer_event_descr.hpp"
@@ -706,6 +707,7 @@ int GlobalFunctions::luaStopEvent(lua_State* L) {
}
int GlobalFunctions::luaSaveServer(lua_State* L) {
+ g_globalEvents().save();
g_saveManager().scheduleAll();
pushBoolean(L, true);
return 1;
diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp
index ea6a5f7df03..129a466e97c 100644
--- a/src/lua/functions/events/global_event_functions.cpp
+++ b/src/lua/functions/events/global_event_functions.cpp
@@ -40,6 +40,8 @@ int GlobalEventFunctions::luaGlobalEventType(lua_State* L) {
global->setEventType(GLOBALEVENT_PERIODCHANGE);
} else if (tmpStr == "onthink") {
global->setEventType(GLOBALEVENT_ON_THINK);
+ } else if (tmpStr == "save") {
+ global->setEventType(GLOBALEVENT_SAVE);
} else {
g_logger().error("[GlobalEventFunctions::luaGlobalEventType] - "
"Invalid type for global event: {}");
diff --git a/src/lua/functions/events/global_event_functions.hpp b/src/lua/functions/events/global_event_functions.hpp
index 6c988a61ca1..e04072a24e4 100644
--- a/src/lua/functions/events/global_event_functions.hpp
+++ b/src/lua/functions/events/global_event_functions.hpp
@@ -25,6 +25,7 @@ class GlobalEventFunctions final : LuaScriptInterface {
registerMethod(L, "GlobalEvent", "onShutdown", GlobalEventFunctions::luaGlobalEventOnCallback);
registerMethod(L, "GlobalEvent", "onRecord", GlobalEventFunctions::luaGlobalEventOnCallback);
registerMethod(L, "GlobalEvent", "onPeriodChange", GlobalEventFunctions::luaGlobalEventOnCallback);
+ registerMethod(L, "GlobalEvent", "onSave", GlobalEventFunctions::luaGlobalEventOnCallback);
}
private:
diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp
index 04420431def..32fa1a01ab1 100644
--- a/src/lua/global/globalevent.cpp
+++ b/src/lua/global/globalevent.cpp
@@ -66,6 +66,14 @@ void GlobalEvents::startup() const {
execute(GLOBALEVENT_STARTUP);
}
+void GlobalEvents::shutdown() const {
+ execute(GLOBALEVENT_SHUTDOWN);
+}
+
+void GlobalEvents::save() const {
+ execute(GLOBALEVENT_SAVE);
+}
+
void GlobalEvents::timer() {
time_t now = time(nullptr);
@@ -165,7 +173,8 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) {
case GLOBALEVENT_PERIODCHANGE:
case GLOBALEVENT_STARTUP:
case GLOBALEVENT_SHUTDOWN:
- case GLOBALEVENT_RECORD: {
+ case GLOBALEVENT_RECORD:
+ case GLOBALEVENT_SAVE: {
GlobalEventMap retMap;
for (const auto &it : serverMap) {
if (it.second->getEventType() == type) {
@@ -196,6 +205,8 @@ std::string GlobalEvent::getScriptTypeName() const {
return "onPeriodChange";
case GLOBALEVENT_ON_THINK:
return "onThink";
+ case GLOBALEVENT_SAVE:
+ return "onSave";
default:
g_logger().error("[GlobalEvent::getScriptTypeName] - Invalid event type");
return std::string();
diff --git a/src/lua/global/globalevent.hpp b/src/lua/global/globalevent.hpp
index 004c6cb5f8c..128a743b3fb 100644
--- a/src/lua/global/globalevent.hpp
+++ b/src/lua/global/globalevent.hpp
@@ -29,6 +29,8 @@ class GlobalEvents final : public Scripts {
}
void startup() const;
+ void shutdown() const;
+ void save() const;
void timer();
void think();
diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp
index 083871e237b..81add18e516 100644
--- a/src/lua/lua_definitions.hpp
+++ b/src/lua/lua_definitions.hpp
@@ -108,6 +108,7 @@ enum GlobalEvent_t {
GLOBALEVENT_RECORD,
GLOBALEVENT_PERIODCHANGE,
GLOBALEVENT_ON_THINK,
+ GLOBALEVENT_SAVE,
};
enum ModuleType_t {
diff --git a/src/server/signals.cpp b/src/server/signals.cpp
index c85b21312eb..626a918f677 100644
--- a/src/server/signals.cpp
+++ b/src/server/signals.cpp
@@ -15,6 +15,7 @@
#include "lib/thread/thread_pool.hpp"
#include "lua/creature/events.hpp"
#include "lua/scripts/lua_environment.hpp"
+#include "lua/global/globalevent.hpp"
#include "server/signals.hpp"
Signals::Signals(asio::io_service &service) :
@@ -92,6 +93,7 @@ void Signals::sigtermHandler() {
void Signals::sigusr1Handler() {
// Dispatcher thread
g_logger().info("SIGUSR1 received, saving the game state...");
+ g_globalEvents().save();
g_saveManager().scheduleAll();
}
From 284897fa6c079fafb7228161175fc837e1625508 Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Thu, 11 Jul 2024 06:23:11 -0300
Subject: [PATCH 05/15] fix: register event AdvancedLevel and FamiliarAdvance
(#2734)
---
.../scripts/creaturescripts/others/login_events.lua | 3 ---
data/scripts/creaturescripts/familiar/on_login.lua | 1 +
data/scripts/creaturescripts/player/login.lua | 1 +
3 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/data-otservbr-global/scripts/creaturescripts/others/login_events.lua b/data-otservbr-global/scripts/creaturescripts/others/login_events.lua
index f88095e4756..2223f095f04 100644
--- a/data-otservbr-global/scripts/creaturescripts/others/login_events.lua
+++ b/data-otservbr-global/scripts/creaturescripts/others/login_events.lua
@@ -1,10 +1,7 @@
local loginEvents = CreatureEvent("LoginEvents")
function loginEvents.onLogin(player)
local events = {
- "AdvanceSave",
"RookgaardAdvance",
- "FamiliarLogin",
- "FamiliarAdvance",
--Quests
--Cults Of Tibia Quest
"HealthPillar",
diff --git a/data/scripts/creaturescripts/familiar/on_login.lua b/data/scripts/creaturescripts/familiar/on_login.lua
index e264040dbf9..dd4a1928e77 100644
--- a/data/scripts/creaturescripts/familiar/on_login.lua
+++ b/data/scripts/creaturescripts/familiar/on_login.lua
@@ -5,6 +5,7 @@ function familiarOnLogin.onLogin(player)
return false
end
+ player:registerEvent("FamiliarAdvance")
local vocation = FAMILIAR_ID[player:getVocation():getBaseId()]
local familiarName
diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua
index fbe4f9e63b6..41676066975 100644
--- a/data/scripts/creaturescripts/player/login.lua
+++ b/data/scripts/creaturescripts/player/login.lua
@@ -173,6 +173,7 @@ function playerLoginGlobal.onLogin(player)
player:registerEvent("PlayerDeath")
player:registerEvent("DropLoot")
player:registerEvent("BossParticipation")
+ player:registerEvent("UpdatePlayerOnAdvancedLevel")
return true
end
From 4920394c12c930ba413bd69a542d172766d5b573 Mon Sep 17 00:00:00 2001
From: Renato Machado
Date: Thu, 11 Jul 2024 06:35:50 -0300
Subject: [PATCH 06/15] perf: std::forward_list to std::vector (#2731)
---
src/creatures/combat/combat.cpp | 29 ++++++++++---------
src/creatures/combat/combat.hpp | 8 ++---
.../monsters/spawns/spawn_monster.cpp | 3 +-
.../monsters/spawns/spawn_monster.hpp | 13 +++++----
src/creatures/npcs/spawns/spawn_npc.cpp | 2 +-
src/creatures/npcs/spawns/spawn_npc.hpp | 4 +--
src/creatures/players/player.cpp | 20 +++++++------
src/creatures/players/player.hpp | 10 +++----
src/io/functions/iologindata_load_player.cpp | 4 +--
src/io/iologindata.cpp | 25 ++++++++--------
src/io/iologindata.hpp | 4 +--
src/io/iomapserialize.cpp | 8 +++--
.../functions/core/game/global_functions.cpp | 4 +--
.../creatures/monster/monster_functions.cpp | 2 +-
src/server/network/protocol/protocolgame.cpp | 2 +-
15 files changed, 73 insertions(+), 65 deletions(-)
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 87f7500d0a9..91ff712de4c 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -108,7 +108,7 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr creature, std::sh
return damage;
}
-void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::forward_list> &list) {
+void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::vector> &list) {
if (targetPos.z >= MAP_MAX_LAYERS) {
return;
}
@@ -116,7 +116,7 @@ void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos,
if (area) {
area->getList(centerPos, targetPos, list);
} else {
- list.push_front(g_game().map.getOrCreateTile(targetPos));
+ list.emplace_back(g_game().map.getOrCreateTile(targetPos));
}
}
@@ -1119,7 +1119,7 @@ bool Combat::doCombat(std::shared_ptr caster, const Position &position
}
void Combat::CombatFunc(std::shared_ptr caster, const Position &origin, const Position &pos, const std::unique_ptr &area, const CombatParams ¶ms, CombatFunction func, CombatDamage* data) {
- std::forward_list> tileList;
+ std::vector> tileList;
if (caster) {
getCombatArea(caster->getPosition(), pos, area, tileList);
@@ -1827,26 +1827,29 @@ AreaCombat::AreaCombat(const AreaCombat &rhs) {
}
}
-void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::forward_list> &list) const {
+void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const {
const std::unique_ptr &area = getArea(centerPos, targetPos);
if (!area) {
return;
}
- uint32_t centerY, centerX;
+ uint32_t centerY;
+ uint32_t centerX;
area->getCenter(centerY, centerX);
+ const uint32_t rows = area->getRows();
+ const uint32_t cols = area->getCols();
+ list.reserve(rows * cols);
+
Position tmpPos(targetPos.x - centerX, targetPos.y - centerY, targetPos.z);
- uint32_t cols = area->getCols();
- for (uint32_t y = 0, rows = area->getRows(); y < rows; ++y) {
- for (uint32_t x = 0; x < cols; ++x) {
- if (area->getValue(y, x) != 0 && g_game().isSightClear(targetPos, tmpPos, true)) {
- list.push_front(g_game().map.getOrCreateTile(tmpPos));
+ for (uint32_t y = 0; y < rows; ++y, ++tmpPos.y, tmpPos.x -= cols) {
+ for (uint32_t x = 0; x < cols; ++x, ++tmpPos.x) {
+ if (area->getValue(y, x) != 0) {
+ if (g_game().isSightClear(targetPos, tmpPos, true)) {
+ list.emplace_back(g_game().map.getOrCreateTile(tmpPos));
+ }
}
- tmpPos.x++;
}
- tmpPos.x -= cols;
- tmpPos.y++;
}
}
diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp
index 93b02502f25..304a709135d 100644
--- a/src/creatures/combat/combat.hpp
+++ b/src/creatures/combat/combat.hpp
@@ -84,7 +84,7 @@ class ChainPickerCallback final : public CallBack {
};
struct CombatParams {
- std::forward_list> conditionList;
+ std::vector> conditionList;
std::unique_ptr valueCallback;
std::unique_ptr tileCallback;
@@ -218,7 +218,7 @@ class AreaCombat {
// non-assignable
AreaCombat &operator=(const AreaCombat &) = delete;
- void getList(const Position ¢erPos, const Position &targetPos, std::forward_list> &list) const;
+ void getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const;
void setupArea(const std::list &list, uint32_t rows);
void setupArea(int32_t length, int32_t spread);
@@ -290,7 +290,7 @@ class Combat {
static void doCombatDispel(std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms);
static void doCombatDispel(std::shared_ptr caster, const Position &position, const std::unique_ptr &area, const CombatParams ¶ms);
- static void getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::forward_list> &list);
+ static void getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::vector> &list);
static bool isInPvpZone(std::shared_ptr attacker, std::shared_ptr target);
static bool isProtected(std::shared_ptr attacker, std::shared_ptr target);
@@ -320,7 +320,7 @@ class Combat {
return area != nullptr;
}
void addCondition(const std::shared_ptr condition) {
- params.conditionList.emplace_front(condition);
+ params.conditionList.emplace_back(condition);
}
void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb);
void postCombatEffects(std::shared_ptr caster, const Position &origin, const Position &pos) const {
diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp
index 968b90d8cae..ca8f4584775 100644
--- a/src/creatures/monsters/spawns/spawn_monster.cpp
+++ b/src/creatures/monsters/spawns/spawn_monster.cpp
@@ -61,8 +61,7 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) {
continue;
}
- spawnMonsterList.emplace_front(centerPos, radius);
- SpawnMonster &spawnMonster = spawnMonsterList.front();
+ SpawnMonster &spawnMonster = spawnMonsterList.emplace_back(centerPos, radius);
for (auto childMonsterNode : spawnMonsterNode.children()) {
if (strcasecmp(childMonsterNode.name(), "monster") == 0) {
diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp
index 35b856d23d4..23a18cf3201 100644
--- a/src/creatures/monsters/spawns/spawn_monster.hpp
+++ b/src/creatures/monsters/spawns/spawn_monster.hpp
@@ -32,6 +32,11 @@ class SpawnMonster {
centerPos(initPos), radius(initRadius) { }
~SpawnMonster();
+ SpawnMonster(SpawnMonster &&) { }
+ SpawnMonster &operator=(SpawnMonster &&) {
+ return *this;
+ }
+
// non-copyable
SpawnMonster(const SpawnMonster &) = delete;
SpawnMonster &operator=(const SpawnMonster &) = delete;
@@ -83,10 +88,6 @@ class SpawnsMonster {
bool loadFromXML(const std::string &filemonstername);
void startup();
void clear();
- SpawnMonster &addSpawnMonster(const Position &pos, int32_t radius) {
- spawnMonsterList.emplace_front(pos, radius);
- return spawnMonsterList.front();
- }
bool isStarted() const {
return started;
@@ -94,12 +95,12 @@ class SpawnsMonster {
bool isLoaded() const {
return loaded;
}
- std::forward_list &getspawnMonsterList() {
+ std::vector &getspawnMonsterList() {
return spawnMonsterList;
}
private:
- std::forward_list spawnMonsterList;
+ std::vector spawnMonsterList;
std::string filemonstername;
bool loaded = false;
bool started = false;
diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp
index a8be411dc6c..694822c2386 100644
--- a/src/creatures/npcs/spawns/spawn_npc.cpp
+++ b/src/creatures/npcs/spawns/spawn_npc.cpp
@@ -57,7 +57,7 @@ bool SpawnsNpc::loadFromXml(const std::string &fileNpcName) {
continue;
}
- const auto &spawnNpc = spawnNpcList.emplace_front(std::make_shared(centerPos, radius));
+ const auto &spawnNpc = spawnNpcList.emplace_back(std::make_shared(centerPos, radius));
for (auto childNode : spawnNode.children()) {
if (strcasecmp(childNode.name(), "npc") == 0) {
diff --git a/src/creatures/npcs/spawns/spawn_npc.hpp b/src/creatures/npcs/spawns/spawn_npc.hpp
index 49eb3bc6f2b..b0d0ab8622a 100644
--- a/src/creatures/npcs/spawns/spawn_npc.hpp
+++ b/src/creatures/npcs/spawns/spawn_npc.hpp
@@ -94,12 +94,12 @@ class SpawnsNpc {
return fileName = std::move(setName);
}
- std::forward_list> &getSpawnNpcList() {
+ std::vector> &getSpawnNpcList() {
return spawnNpcList;
}
private:
- std::forward_list> spawnNpcList;
+ std::vector> spawnNpcList;
std::string fileName;
bool loaded = false;
bool started = false;
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 273079873e8..2660c039110 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -5289,12 +5289,12 @@ double Player::getLostPercent() const {
void Player::learnInstantSpell(const std::string &spellName) {
if (!hasLearnedInstantSpell(spellName)) {
- learnedInstantSpellList.push_front(spellName);
+ learnedInstantSpellList.emplace_back(spellName);
}
}
void Player::forgetInstantSpell(const std::string &spellName) {
- learnedInstantSpellList.remove(spellName);
+ std::erase(learnedInstantSpellList, spellName);
}
bool Player::hasLearnedInstantSpell(const std::string &spellName) const {
@@ -5700,12 +5700,12 @@ bool Player::addPartyInvitation(std::shared_ptr newParty) {
return false;
}
- invitePartyList.push_front(newParty);
+ invitePartyList.emplace_back(newParty);
return true;
}
void Player::removePartyInvitation(std::shared_ptr remParty) {
- invitePartyList.remove(remParty);
+ std::erase(invitePartyList, remParty);
}
void Player::clearPartyInvitations() {
@@ -6124,7 +6124,7 @@ bool Player::hasModalWindowOpen(uint32_t modalWindowId) const {
}
void Player::onModalWindowHandled(uint32_t modalWindowId) {
- modalWindows.remove(modalWindowId);
+ std::erase(modalWindows, modalWindowId);
}
void Player::sendModalWindow(const ModalWindow &modalWindow) {
@@ -6132,7 +6132,7 @@ void Player::sendModalWindow(const ModalWindow &modalWindow) {
return;
}
- modalWindows.push_front(modalWindow.id);
+ modalWindows.emplace_back(modalWindow.id);
client->sendModalWindow(modalWindow);
}
@@ -6251,8 +6251,10 @@ size_t Player::getMaxDepotItems() const {
return g_configManager().getNumber(FREE_DEPOT_LIMIT, __FUNCTION__);
}
-std::forward_list> Player::getMuteConditions() const {
- std::forward_list> muteConditions;
+std::vector> Player::getMuteConditions() const {
+ std::vector> muteConditions;
+ muteConditions.reserve(conditions.size());
+
for (const std::shared_ptr &condition : conditions) {
if (condition->getTicks() <= 0) {
continue;
@@ -6263,7 +6265,7 @@ std::forward_list> Player::getMuteConditions() const
continue;
}
- muteConditions.push_front(condition);
+ muteConditions.emplace_back(condition);
}
return muteConditions;
}
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index 2c765850f10..bfaad14cf76 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -2664,7 +2664,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
static uint32_t playerFirstID;
static uint32_t playerLastID;
- std::forward_list> getMuteConditions() const;
+ std::vector> getMuteConditions() const;
void checkTradeState(std::shared_ptr- item);
bool hasCapacity(std::shared_ptr
- item, uint32_t count) const;
@@ -2760,11 +2760,11 @@ class Player final : public Creature, public Cylinder, public Bankable {
GuildWarVector guildWarVector;
- std::forward_list> invitePartyList;
- std::forward_list modalWindows;
- std::forward_list learnedInstantSpellList;
+ std::vector> invitePartyList;
+ std::vector modalWindows;
+ std::vector learnedInstantSpellList;
// TODO: This variable is only temporarily used when logging in, get rid of it somehow.
- std::forward_list> storedConditionList;
+ std::vector> storedConditionList;
std::unordered_set> m_bestiaryMonsterTracker;
std::unordered_set> m_bosstiaryMonsterTracker;
diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp
index 8406cf11010..764d4641469 100644
--- a/src/io/functions/iologindata_load_player.cpp
+++ b/src/io/functions/iologindata_load_player.cpp
@@ -235,7 +235,7 @@ void IOLoginDataLoad::loadPlayerConditions(std::shared_ptr player, DBRes
auto condition = Condition::createCondition(propStream);
while (condition) {
if (condition->unserialize(propStream)) {
- player->storedConditionList.push_front(condition);
+ player->storedConditionList.emplace_back(condition);
}
condition = Condition::createCondition(propStream);
}
@@ -465,7 +465,7 @@ void IOLoginDataLoad::loadPlayerInstantSpellList(std::shared_ptr player,
query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID();
if ((result = db.storeQuery(query.str()))) {
do {
- player->learnedInstantSpellList.emplace_front(result->getString("name"));
+ player->learnedInstantSpellList.emplace_back(result->getString("name"));
} while (result->next());
}
}
diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp
index 122ade42c72..132cead6412 100644
--- a/src/io/iologindata.cpp
+++ b/src/io/iologindata.cpp
@@ -349,15 +349,14 @@ bool IOLoginData::hasBiddedOnHouse(uint32_t guid) {
return db.storeQuery(query.str()).get() != nullptr;
}
-std::forward_list IOLoginData::getVIPEntries(uint32_t accountId) {
- std::forward_list entries;
-
+std::vector IOLoginData::getVIPEntries(uint32_t accountId) {
std::string query = fmt::format("SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = {}", accountId);
+ std::vector entries;
- DBResult_ptr result = Database::getInstance().storeQuery(query);
- if (result) {
+ if (const auto &result = Database::getInstance().storeQuery(query)) {
+ entries.reserve(result->countResults());
do {
- entries.emplace_front(
+ entries.emplace_back(
result->getNumber("player_id"),
result->getString("name"),
result->getString("description"),
@@ -366,6 +365,7 @@ std::forward_list IOLoginData::getVIPEntries(uint32_t accountId) {
);
} while (result->next());
}
+
return entries;
}
@@ -388,15 +388,16 @@ void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) {
g_database().executeQuery(query);
}
-std::forward_list IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) {
- std::forward_list entries;
-
+std::vector IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) {
std::string query = fmt::format("SELECT `id`, `name`, `customizable` FROM `account_vipgroups` WHERE `account_id` = {}", accountId);
- DBResult_ptr result = g_database().storeQuery(query);
- if (result) {
+ std::vector entries;
+
+ if (const auto &result = g_database().storeQuery(query)) {
+ entries.reserve(result->countResults());
+
do {
- entries.emplace_front(
+ entries.emplace_back(
result->getNumber("id"),
result->getString("name"),
result->getNumber("customizable") == 0 ? false : true
diff --git a/src/io/iologindata.hpp b/src/io/iologindata.hpp
index be414739837..1451cf89778 100644
--- a/src/io/iologindata.hpp
+++ b/src/io/iologindata.hpp
@@ -31,12 +31,12 @@ class IOLoginData {
static void increaseBankBalance(uint32_t guid, uint64_t bankBalance);
static bool hasBiddedOnHouse(uint32_t guid);
- static std::forward_list getVIPEntries(uint32_t accountId);
+ static std::vector getVIPEntries(uint32_t accountId);
static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify);
static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify);
static void removeVIPEntry(uint32_t accountId, uint32_t guid);
- static std::forward_list getVIPGroupEntries(uint32_t accountId, uint32_t guid);
+ static std::vector getVIPGroupEntries(uint32_t accountId, uint32_t guid);
static void addVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable);
static void editVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable);
static void removeVIPGroupEntry(uint8_t groupId, uint32_t accountId);
diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp
index 475a210a760..e2a367c4ec4 100644
--- a/src/io/iomapserialize.cpp
+++ b/src/io/iomapserialize.cpp
@@ -241,19 +241,21 @@ void IOMapSerialize::saveTile(PropWriteStream &stream, std::shared_ptr til
return;
}
- std::forward_list> items;
+ std::vector> items;
+ items.reserve(32);
+
uint16_t count = 0;
for (auto &item : *tileItems) {
if (item->getID() == ITEM_BATHTUB_FILLED_NOTMOVABLE) {
std::shared_ptr
- tub = Item::CreateItem(ITEM_BATHTUB_FILLED);
- items.push_front(tub);
+ items.emplace_back(tub);
++count;
continue;
} else if (!item->isSavedToHouses()) {
continue;
}
- items.push_front(item);
+ items.emplace_back(item);
++count;
}
diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp
index bed0ff27026..6b5175b06cf 100644
--- a/src/lua/functions/core/game/global_functions.cpp
+++ b/src/lua/functions/core/game/global_functions.cpp
@@ -444,7 +444,7 @@ int GlobalFunctions::luaDoAreaCombatCondition(lua_State* L) {
if (area || areaId == 0) {
CombatParams params;
params.impactEffect = getNumber(L, 5);
- params.conditionList.emplace_front(condition);
+ params.conditionList.emplace_back(condition);
Combat::doCombatCondition(creature, getPosition(L, 2), area, params);
pushBoolean(L, true);
} else {
@@ -479,7 +479,7 @@ int GlobalFunctions::luaDoTargetCombatCondition(lua_State* L) {
CombatParams params;
params.impactEffect = getNumber(L, 4);
- params.conditionList.emplace_front(condition->clone());
+ params.conditionList.emplace_back(condition->clone());
Combat::doCombatCondition(creature, target, params);
pushBoolean(L, true);
return 1;
diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp
index 273f6af5c99..3ad40a55c72 100644
--- a/src/lua/functions/creatures/monster/monster_functions.cpp
+++ b/src/lua/functions/creatures/monster/monster_functions.cpp
@@ -363,7 +363,7 @@ int MonsterFunctions::luaMonsterSetSpawnPosition(lua_State* L) {
const Position &pos = monster->getPosition();
monster->setMasterPos(pos);
- g_game().map.spawnsMonster.getspawnMonsterList().emplace_front(pos, 5);
+ g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5);
SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().front();
uint32_t interval = getNumber(L, 2, 90) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * eventschedule));
spawnMonster.addMonster(monster->mType->typeName, pos, DIRECTION_NORTH, static_cast(interval));
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index 3ac22ac6b77..f3a4bcef3ac 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -6695,7 +6695,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos
sendVIPGroups();
- const std::forward_list &vipEntries = IOLoginData::getVIPEntries(player->getAccountId());
+ const auto &vipEntries = IOLoginData::getVIPEntries(player->getAccountId());
if (player->isAccessPlayer()) {
for (const VIPEntry &entry : vipEntries) {
From 418aeb02cfc279914f2aacf2d0c58d3591200368 Mon Sep 17 00:00:00 2001
From: Renato Machado
Date: Thu, 11 Jul 2024 06:39:09 -0300
Subject: [PATCH 07/15] perf: optimized isSightClear and checkSightLine (#2732)
---
src/map/map.cpp | 169 +++++++++++++++++++++++++++++++++++++-----------
src/map/map.hpp | 2 +-
2 files changed, 134 insertions(+), 37 deletions(-)
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 82629aeae05..7406ecffbdf 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -457,62 +457,159 @@ bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool
return isSightClear(fromPos, toPos, false);
}
-bool Map::checkSightLine(const Position &fromPos, const Position &toPos) {
- if (fromPos == toPos) {
+bool Map::checkSightLine(Position start, Position destination) {
+ if (start.x == destination.x && start.y == destination.y) {
return true;
}
- Position start(fromPos.z > toPos.z ? toPos : fromPos);
- Position destination(fromPos.z > toPos.z ? fromPos : toPos);
+ int32_t distanceX = Position::getDistanceX(start, destination);
+ int32_t distanceY = Position::getDistanceY(start, destination);
- const int8_t mx = start.x < destination.x ? 1 : start.x == destination.x ? 0
- : -1;
- const int8_t my = start.y < destination.y ? 1 : start.y == destination.y ? 0
- : -1;
+ if (start.y == destination.y) {
+ // Horizontal line
+ const uint16_t delta = start.x < destination.x ? 0x0001 : 0xFFFF;
+ while (--distanceX > 0) {
+ start.x += delta;
- int32_t A = Position::getOffsetY(destination, start);
- int32_t B = Position::getOffsetX(start, destination);
- int32_t C = -(A * destination.x + B * destination.y);
+ const auto &tile = getTile(start.x, start.y, start.z);
+ if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) {
+ return false;
+ }
+ }
+ } else if (start.x == destination.x) {
+ // Vertical line
+ const uint16_t delta = start.y < destination.y ? 0x0001 : 0xFFFF;
+ while (--distanceY > 0) {
+ start.y += delta;
+
+ const auto &tile = getTile(start.x, start.y, start.z);
+ if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) {
+ return false;
+ }
+ }
+ } else {
+ // Xiaolin Wu's line algorithm - https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
+ // based on Michael Abrash's implementation - https://www.amazon.com/gp/product/1576101746/102-5103244-8168911
+ uint16_t eAdj;
+ uint16_t eAcc = 0;
+ uint16_t deltaX = 0x0001;
+ uint16_t deltaY = 0x0001;
+
+ if (distanceY > distanceX) {
+ eAdj = (static_cast(distanceX) << 16) / static_cast(distanceY);
+
+ if (start.y > destination.y) {
+ std::swap(start.x, destination.x);
+ std::swap(start.y, destination.y);
+ }
+ if (start.x > destination.x) {
+ deltaX = 0xFFFF;
+ eAcc -= eAdj;
+ }
- while (start.x != destination.x || start.y != destination.y) {
- int32_t move_hor = std::abs(A * (start.x + mx) + B * (start.y) + C);
- int32_t move_ver = std::abs(A * (start.x) + B * (start.y + my) + C);
- int32_t move_cross = std::abs(A * (start.x + mx) + B * (start.y + my) + C);
+ while (--distanceY > 0) {
+ uint16_t xIncrease = 0;
+ const uint16_t eAccTemp = eAcc;
+ eAcc += eAdj;
+ if (eAcc <= eAccTemp) {
+ xIncrease = deltaX;
+ }
- if (start.y != destination.y && (start.x == destination.x || move_hor > move_ver || move_hor > move_cross)) {
- start.y += my;
- }
+ const auto &tile = getTile(start.x + xIncrease, start.y + deltaY, start.z);
+ if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) {
+ if (Position::areInRange<1, 1>(start, destination)) {
+ return true;
+ }
+ return false;
+ }
- if (start.x != destination.x && (start.y == destination.y || move_ver > move_hor || move_ver > move_cross)) {
- start.x += mx;
- }
+ start.x += xIncrease;
+ start.y += deltaY;
+ }
+ } else {
+ eAdj = (static_cast(distanceY) << 16) / static_cast(distanceX);
- const std::shared_ptr tile = getTile(start.x, start.y, start.z);
- if (tile && tile->hasProperty(CONST_PROP_BLOCKPROJECTILE)) {
- return false;
- }
- }
+ if (start.x > destination.x) {
+ std::swap(start.x, destination.x);
+ std::swap(start.y, destination.y);
+ }
+ if (start.y > destination.y) {
+ deltaY = 0xFFFF;
+ eAcc -= eAdj;
+ }
- // now we need to perform a jump between floors to see if everything is clear (literally)
- while (start.z != destination.z) {
- const std::shared_ptr tile = getTile(start.x, start.y, start.z);
- if (tile && tile->getThingCount() > 0) {
- return false;
- }
+ while (--distanceX > 0) {
+ uint16_t yIncrease = 0;
+ const uint16_t eAccTemp = eAcc;
+ eAcc += eAdj;
+ if (eAcc <= eAccTemp) {
+ yIncrease = deltaY;
+ }
- start.z++;
- }
+ const auto &tile = getTile(start.x + deltaX, start.y + yIncrease, start.z);
+ if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) {
+ if (Position::areInRange<1, 1>(start, destination)) {
+ return true;
+ }
+ return false;
+ }
+ start.x += deltaX;
+ start.y += yIncrease;
+ }
+ }
+ }
return true;
}
bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) {
+ // Check if this sight line should be even possible
if (floorCheck && fromPos.z != toPos.z) {
return false;
}
- // Cast two converging rays and see if either yields a result.
- return checkSightLine(fromPos, toPos) || checkSightLine(toPos, fromPos);
+ // Check if we even need to perform line checking
+ if (fromPos.z == toPos.z && (Position::areInRange<1, 1>(fromPos, toPos) || (!floorCheck && fromPos.z == 0))) {
+ return true;
+ }
+
+ // We can only throw one floor up
+ if (fromPos.z > toPos.z && Position::getDistanceZ(fromPos, toPos) > 1) {
+ return false;
+ }
+
+ // Perform check for current floor
+ const bool sightClear = checkSightLine(fromPos, toPos);
+ if (floorCheck || (fromPos.z == toPos.z && sightClear)) {
+ return sightClear;
+ }
+
+ uint8_t startZ;
+ if (sightClear && (fromPos.z < toPos.z || fromPos.z == toPos.z)) {
+ startZ = fromPos.z;
+ } else {
+ // Check if we can throw above obstacle
+ const auto &tile = getTile(fromPos.x, fromPos.y, fromPos.z - 1);
+ if ((tile && (tile->getGround() || tile->hasFlag(TILESTATE_BLOCKPROJECTILE))) || !checkSightLine(Position(fromPos.x, fromPos.y, fromPos.z - 1), Position(toPos.x, toPos.y, toPos.z - 1))) {
+ return false;
+ }
+
+ // We can throw above obstacle
+ if (fromPos.z > toPos.z) {
+ return true;
+ }
+
+ startZ = fromPos.z - 1;
+ }
+
+ // now we need to perform a jump between floors to see if everything is clear (literally)
+ for (; startZ != toPos.z; ++startZ) {
+ const auto &tile = getTile(toPos.x, toPos.y, startZ);
+ if (tile && (tile->getGround() || tile->hasFlag(TILESTATE_BLOCKPROJECTILE))) {
+ return false;
+ }
+ }
+ return true;
}
std::shared_ptr Map::canWalkTo(const std::shared_ptr &creature, const Position &pos) {
diff --git a/src/map/map.hpp b/src/map/map.hpp
index e57328e12b3..bae07445156 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -119,7 +119,7 @@ class Map : public MapCache {
* \returns The result if there is no obstacles
*/
bool isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck);
- bool checkSightLine(const Position &fromPos, const Position &toPos);
+ bool checkSightLine(Position start, Position destination);
std::shared_ptr canWalkTo(const std::shared_ptr &creature, const Position &pos);
From 762eaea5450076e57ca909a19570beb6befca9d0 Mon Sep 17 00:00:00 2001
From: Renato Machado
Date: Thu, 11 Jul 2024 14:48:08 -0300
Subject: [PATCH 08/15] fix: spawn monster (#2750)
---
.../monsters/spawns/spawn_monster.hpp | 24 +++++++++++++++----
.../creatures/monster/monster_functions.cpp | 3 +--
2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp
index 23a18cf3201..81a08973cb7 100644
--- a/src/creatures/monsters/spawns/spawn_monster.hpp
+++ b/src/creatures/monsters/spawns/spawn_monster.hpp
@@ -32,15 +32,29 @@ class SpawnMonster {
centerPos(initPos), radius(initRadius) { }
~SpawnMonster();
- SpawnMonster(SpawnMonster &&) { }
- SpawnMonster &operator=(SpawnMonster &&) {
- return *this;
- }
-
// non-copyable
SpawnMonster(const SpawnMonster &) = delete;
SpawnMonster &operator=(const SpawnMonster &) = delete;
+ // moveable
+ SpawnMonster(SpawnMonster &&rhs) noexcept :
+ spawnMonsterMap(std::move(rhs.spawnMonsterMap)),
+ spawnedMonsterMap(std::move(rhs.spawnedMonsterMap)),
+ checkSpawnMonsterEvent(rhs.checkSpawnMonsterEvent), centerPos(rhs.centerPos), radius(rhs.radius), interval(rhs.interval) { }
+
+ SpawnMonster &operator=(SpawnMonster &&rhs) noexcept {
+ if (this != &rhs) {
+ spawnMonsterMap = std::move(rhs.spawnMonsterMap);
+ spawnedMonsterMap = std::move(rhs.spawnedMonsterMap);
+
+ checkSpawnMonsterEvent = rhs.checkSpawnMonsterEvent;
+ centerPos = rhs.centerPos;
+ radius = rhs.radius;
+ interval = rhs.interval;
+ }
+ return *this;
+ }
+
bool addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t interval, uint32_t weight = 1);
void removeMonster(std::shared_ptr monster);
void removeMonsters();
diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp
index 3ad40a55c72..5578477d6ba 100644
--- a/src/lua/functions/creatures/monster/monster_functions.cpp
+++ b/src/lua/functions/creatures/monster/monster_functions.cpp
@@ -363,8 +363,7 @@ int MonsterFunctions::luaMonsterSetSpawnPosition(lua_State* L) {
const Position &pos = monster->getPosition();
monster->setMasterPos(pos);
- g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5);
- SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().front();
+ SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5);
uint32_t interval = getNumber(L, 2, 90) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * eventschedule));
spawnMonster.addMonster(monster->mType->typeName, pos, DIRECTION_NORTH, static_cast(interval));
spawnMonster.startSpawnMonsterCheck();
From c053dfd7d1b99021002c65634a9796c8d18aafb0 Mon Sep 17 00:00:00 2001
From: AlphaRage
Date: Mon, 15 Jul 2024 13:11:24 +0200
Subject: [PATCH 09/15] Fix: change class name "Iks" to "Undead" (#2751)
---
data-otservbr-global/monster/undeads/iks_ahpututu.lua | 2 +-
data-otservbr-global/monster/undeads/iks_aucar.lua | 2 +-
data-otservbr-global/monster/undeads/iks_chuka.lua | 2 +-
data-otservbr-global/monster/undeads/iks_churrascan.lua | 2 +-
data-otservbr-global/monster/undeads/iks_pututu.lua | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/data-otservbr-global/monster/undeads/iks_ahpututu.lua b/data-otservbr-global/monster/undeads/iks_ahpututu.lua
index 06e8d061228..d1f44cd61d3 100644
--- a/data-otservbr-global/monster/undeads/iks_ahpututu.lua
+++ b/data-otservbr-global/monster/undeads/iks_ahpututu.lua
@@ -15,7 +15,7 @@ monster.outfit = {
monster.raceId = 2349
monster.Bestiary = {
- class = "Iks",
+ class = "Undead",
race = BESTY_RACE_UNDEAD,
toKill = 5,
FirstUnlock = 1,
diff --git a/data-otservbr-global/monster/undeads/iks_aucar.lua b/data-otservbr-global/monster/undeads/iks_aucar.lua
index 26bfc867d9b..efd711a01bd 100644
--- a/data-otservbr-global/monster/undeads/iks_aucar.lua
+++ b/data-otservbr-global/monster/undeads/iks_aucar.lua
@@ -15,7 +15,7 @@ monster.outfit = {
monster.raceId = 2344
monster.Bestiary = {
- class = "Iks",
+ class = "Undead",
race = BESTY_RACE_UNDEAD,
toKill = 1000,
FirstUnlock = 50,
diff --git a/data-otservbr-global/monster/undeads/iks_chuka.lua b/data-otservbr-global/monster/undeads/iks_chuka.lua
index 85f0cd77c3f..514011c9bd8 100644
--- a/data-otservbr-global/monster/undeads/iks_chuka.lua
+++ b/data-otservbr-global/monster/undeads/iks_chuka.lua
@@ -15,7 +15,7 @@ monster.outfit = {
monster.raceId = 2345
monster.Bestiary = {
- class = "Iks",
+ class = "Undead",
race = BESTY_RACE_UNDEAD,
toKill = 1000,
FirstUnlock = 50,
diff --git a/data-otservbr-global/monster/undeads/iks_churrascan.lua b/data-otservbr-global/monster/undeads/iks_churrascan.lua
index 704e40a53fa..fd44b36f620 100644
--- a/data-otservbr-global/monster/undeads/iks_churrascan.lua
+++ b/data-otservbr-global/monster/undeads/iks_churrascan.lua
@@ -15,7 +15,7 @@ monster.outfit = {
monster.raceId = 2350
monster.Bestiary = {
- class = "Iks",
+ class = "Undead",
race = BESTY_RACE_UNDEAD,
toKill = 1000,
FirstUnlock = 50,
diff --git a/data-otservbr-global/monster/undeads/iks_pututu.lua b/data-otservbr-global/monster/undeads/iks_pututu.lua
index 460aa7eb511..bbf16a43d16 100644
--- a/data-otservbr-global/monster/undeads/iks_pututu.lua
+++ b/data-otservbr-global/monster/undeads/iks_pututu.lua
@@ -15,7 +15,7 @@ monster.outfit = {
monster.raceId = 2343
monster.Bestiary = {
- class = "Iks",
+ class = "Undead",
race = BESTY_RACE_UNDEAD,
toKill = 1000,
FirstUnlock = 50,
From 1ff589f7c70aebbdefe16e33c8437386f402b788 Mon Sep 17 00:00:00 2001
From: Karin
Date: Thu, 18 Jul 2024 08:29:53 -0300
Subject: [PATCH 10/15] fix: NPCs purchase items without capacity check (#2753)
Resolves #2737
---
src/game/game.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 2348d384569..40b82d6b47e 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -2362,7 +2362,7 @@ std::tuple Game::addItemBatch(const std::shared
if (item->getContainer()) {
containersCreated++;
}
- totalAdded++;
+ totalAdded += item->getItemCount();
}
ret = returnError;
From f153224edb5043e712127b9826643b7aceaf64cf Mon Sep 17 00:00:00 2001
From: Elson Costa
Date: Thu, 18 Jul 2024 08:56:09 -0300
Subject: [PATCH 11/15] fix: config to avoid the support outfit crashes on
login (#2526)
---
config.lua.dist | 2 +
data/scripts/creaturescripts/player/login.lua | 8 +++
src/config/config_enums.hpp | 1 +
src/config/configmanager.cpp | 1 +
src/creatures/creature.hpp | 4 ++
src/creatures/players/player.cpp | 4 ++
src/game/game.cpp | 7 ++-
src/server/network/protocol/protocolgame.cpp | 55 ++++++++++---------
8 files changed, 56 insertions(+), 26 deletions(-)
diff --git a/config.lua.dist b/config.lua.dist
index 9d1ed2fa681..6823be5e0b1 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -254,6 +254,7 @@ onlyPremiumAccount = false
-- NOTE: startStreakLevel will make a reward streak level for new players who never logged in
-- NOTE: if showLootsInBestiary is true, will cause all loots to be shown in the bestiary even if the player has not reached the required number of kills
-- NOTE: minTownIdToBankTransfer blocks towns less than defined from receiving money transfers
+-- NOTE: enableSupportOutfit enable GODS and GMS to select support outfit (gamemaster, customer support or community manager)
stashMoving = false
depotChest = 4
autoLoot = false
@@ -273,6 +274,7 @@ enablePlayerPutItemInAmmoSlot = false
startStreakLevel = 0
showLootsInBestiary = false
minTownIdToBankTransfer = 3
+enableSupportOutfit = true
-- Teleport summon
-- Set to true will never remove the summon
diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua
index 41676066975..5f9f0e695f4 100644
--- a/data/scripts/creaturescripts/player/login.lua
+++ b/data/scripts/creaturescripts/player/login.lua
@@ -169,6 +169,14 @@ function playerLoginGlobal.onLogin(player)
onMovementRemoveProtection(playerId, player:getPosition(), 10)
end
+ -- Change support outfit to a normal outfit to open customize character without crashes
+ local playerOutfit = player:getOutfit()
+ if table.contains({ 75, 266, 302 }, playerOutfit.lookType) then
+ playerOutfit.lookType = 136
+ playerOutfit.lookAddons = 0
+ player:setOutfit(playerOutfit)
+ end
+
player:initializeLoyaltySystem()
player:registerEvent("PlayerDeath")
player:registerEvent("DropLoot")
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 0e53c97546e..b1d18b5a32c 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -63,6 +63,7 @@ enum ConfigKey_t : uint16_t {
DISCORD_WEBHOOK_URL,
EMOTE_SPELLS,
ENABLE_PLAYER_PUT_ITEM_IN_AMMO_SLOT,
+ ENABLE_SUPPORT_OUTFIT,
EX_ACTIONS_DELAY_INTERVAL,
EXP_FROM_PLAYERS_LEVEL_RANGE,
EXPERIENCE_FROM_PLAYERS,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 35d2cf3b1be..5ade621dff8 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -97,6 +97,7 @@ bool ConfigManager::load() {
loadBoolConfig(L, DISCORD_SEND_FOOTER, "discordSendFooter", true);
loadBoolConfig(L, EMOTE_SPELLS, "emoteSpells", false);
loadBoolConfig(L, ENABLE_PLAYER_PUT_ITEM_IN_AMMO_SLOT, "enablePlayerPutItemInAmmoSlot", false);
+ loadBoolConfig(L, ENABLE_SUPPORT_OUTFIT, "enableSupportOutfit", true);
loadBoolConfig(L, EXPERIENCE_FROM_PLAYERS, "experienceByKillingPlayers", false);
loadBoolConfig(L, FORCE_MONSTERTYPE_LOAD, "forceMonsterTypesOnLoad", true);
loadBoolConfig(L, FREE_PREMIUM, "freePremium", false);
diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp
index 670d5f3d688..edf2fa55b74 100644
--- a/src/creatures/creature.hpp
+++ b/src/creatures/creature.hpp
@@ -281,6 +281,10 @@ class Creature : virtual public Thing, public SharedObject {
const Outfit_t getDefaultOutfit() const {
return defaultOutfit;
}
+ bool isWearingSupportOutfit() const {
+ auto outfit = currentOutfit.lookType;
+ return outfit == 75 || outfit == 266 || outfit == 302;
+ }
bool isInvisible() const;
ZoneType_t getZoneType() {
if (getTile()) {
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 2660c039110..a6db39f587d 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -5826,6 +5826,10 @@ bool Player::toggleMount(bool mount) {
return false;
}
+ if (isWearingSupportOutfit()) {
+ return false;
+ }
+
if (mount) {
if (isMounted()) {
return false;
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 40b82d6b47e..949da71c0a3 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -4266,7 +4266,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos
}
const auto mount = mounts.getMountByClientID(outfit.lookMount);
- if (!mount || !player->hasMount(mount)) {
+ if (!mount || !player->hasMount(mount) || player->isWearingSupportOutfit()) {
outfit.lookMount = 0;
}
@@ -5963,6 +5963,11 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun
return;
}
+ if (player->isWearingSupportOutfit()) {
+ outfit.lookMount = 0;
+ isMountRandomized = 0;
+ }
+
player->setRandomMount(isMountRandomized);
if (isMountRandomized && outfit.lookMount != 0 && player->hasAnyMount()) {
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index f3a4bcef3ac..46ccb14db77 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -3203,15 +3203,22 @@ void ProtocolGame::sendCreatureOutfit(std::shared_ptr creature, const
return;
}
+ Outfit_t newOutfit = outfit;
+ if (player->isWearingSupportOutfit()) {
+ player->setCurrentMount(0);
+ newOutfit.lookMount = 0;
+ }
+
NetworkMessage msg;
msg.addByte(0x8E);
msg.add(creature->getID());
- AddOutfit(msg, outfit);
- if (!oldProtocol && outfit.lookMount != 0) {
- msg.addByte(outfit.lookMountHead);
- msg.addByte(outfit.lookMountBody);
- msg.addByte(outfit.lookMountLegs);
- msg.addByte(outfit.lookMountFeet);
+ AddOutfit(msg, newOutfit);
+
+ if (!oldProtocol && newOutfit.lookMount != 0) {
+ msg.addByte(newOutfit.lookMountHead);
+ msg.addByte(newOutfit.lookMountBody);
+ msg.addByte(newOutfit.lookMountLegs);
+ msg.addByte(newOutfit.lookMountFeet);
}
writeToOutputBuffer(msg);
}
@@ -6949,15 +6956,23 @@ void ProtocolGame::sendOutfitWindow() {
NetworkMessage msg;
msg.addByte(0xC8);
- if (oldProtocol) {
- Outfit_t currentOutfit = player->getDefaultOutfit();
+ Outfit_t currentOutfit = player->getDefaultOutfit();
+ auto isSupportOutfit = player->isWearingSupportOutfit();
+ bool mounted = false;
+
+ if (!isSupportOutfit) {
const auto currentMount = g_game().mounts.getMountByID(player->getLastMount());
if (currentMount) {
+ mounted = (currentOutfit.lookMount == currentMount->clientId);
currentOutfit.lookMount = currentMount->clientId;
}
+ } else {
+ currentOutfit.lookMount = 0;
+ }
- AddOutfit(msg, currentOutfit);
+ AddOutfit(msg, currentOutfit);
+ if (oldProtocol) {
std::vector protocolOutfits;
const auto outfits = Outfits::getInstance().getOutfits(player->getSex());
protocolOutfits.reserve(outfits.size());
@@ -6998,20 +7013,10 @@ void ProtocolGame::sendOutfitWindow() {
return;
}
- bool mounted = false;
- Outfit_t currentOutfit = player->getDefaultOutfit();
- const auto currentMount = g_game().mounts.getMountByID(player->getLastMount());
- if (currentMount) {
- mounted = (currentOutfit.lookMount == currentMount->clientId);
- currentOutfit.lookMount = currentMount->clientId;
- }
-
- AddOutfit(msg, currentOutfit);
-
- msg.addByte(currentOutfit.lookMountHead);
- msg.addByte(currentOutfit.lookMountBody);
- msg.addByte(currentOutfit.lookMountLegs);
- msg.addByte(currentOutfit.lookMountFeet);
+ msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountHead);
+ msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountBody);
+ msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountLegs);
+ msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountFeet);
msg.add(currentOutfit.lookFamiliarsType);
auto startOutfits = msg.getBufferPosition();
@@ -7020,7 +7025,7 @@ void ProtocolGame::sendOutfitWindow() {
uint16_t outfitSize = 0;
msg.skipBytes(2);
- if (player->isAccessPlayer()) {
+ if (player->isAccessPlayer() && g_configManager().getBoolean(ENABLE_SUPPORT_OUTFIT, __FUNCTION__)) {
msg.add(75);
msg.addString("Gamemaster", "ProtocolGame::sendOutfitWindow - Gamemaster");
msg.addByte(0);
@@ -7144,7 +7149,7 @@ void ProtocolGame::sendOutfitWindow() {
msg.addByte(mounted ? 0x01 : 0x00);
// Version 12.81 - Random mount 'bool'
- msg.addByte(player->isRandomMounted() ? 0x01 : 0x00);
+ msg.addByte(isSupportOutfit ? 0x00 : (player->isRandomMounted() ? 0x01 : 0x00));
writeToOutputBuffer(msg);
}
From 7a1d6241818e1720a26324f29a679923787a5dca Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Thu, 18 Jul 2024 09:09:23 -0300
Subject: [PATCH 12/15] fix: prey oldprotocol (#2757)
---
src/server/network/protocol/protocolgame.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index 46ccb14db77..cf274ce58a9 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -4259,10 +4259,12 @@ void ProtocolGame::sendBasicData() {
msg.addByte(player->getVocation()->getClientId());
// Prey window
- if (player->getVocation()->getId() == 0 && player->getGroup()->id < GROUP_TYPE_GAMEMASTER) {
- msg.addByte(0);
- } else {
- msg.addByte(1); // has reached Main (allow player to open Prey window)
+ if (!oldProtocol) {
+ if (player->getVocation()->getId() == 0 && player->getGroup()->id < GROUP_TYPE_GAMEMASTER) {
+ msg.addByte(0);
+ } else {
+ msg.addByte(1); // has reached Main (allow player to open Prey window)
+ }
}
// Filter only valid ids
From 72b51f01cde2803785cd3b7820d5213e2cb82dfe Mon Sep 17 00:00:00 2001
From: AlphaRage
Date: Thu, 18 Jul 2024 16:50:21 +0200
Subject: [PATCH 13/15] fix: quest trigger "symbol" for Children of the
Revolution Mission 4 to the stairs (#2756)
---
.../movements/quests/children_of_the_revolution/symbol.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua b/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua
index b6b0bfe6d88..ac12a11b0c1 100644
--- a/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua
+++ b/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua
@@ -16,5 +16,5 @@ function symbol.onStepIn(creature, item, position, fromPosition)
end
symbol:type("stepin")
-symbol:position({ x = 33349, y = 31123, z = 5 })
+symbol:position({ x = 33357, y = 31123, z = 5 })
symbol:register()
From 07df814b185e04d2be1c212a0d0ba1cd922336a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?=
Date: Thu, 18 Jul 2024 12:41:55 -0300
Subject: [PATCH 14/15] feat: missing configuration warning (#2698)
When a configuration is missing in config.lua, you will get a warning in console.
You can ignore the warning and, this way, the default value will be used.
With the warning, you dont need to conflict your config.lua with configmanager.
---
config.lua.dist | 6 ++++++
src/config/config_enums.hpp | 6 ------
src/config/configmanager.cpp | 20 +++++++++++++-------
src/config/configmanager.hpp | 2 ++
4 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/config.lua.dist b/config.lua.dist
index 6823be5e0b1..89bd3e68fc3 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -239,6 +239,7 @@ onlyPremiumAccount = false
-- Customs
-- NOTE: stashMoving = true, stow an container inside your stash
+-- NOTE: stashItemCount, the maximum items quantity in stash
-- NOTE: depotChest, the non-stackable items will be moved to the selected depot chest(I - XVIII).
-- NOTE: autoBank = true, the dropped coins from monsters will be automatically deposited to your bank account.
-- NOTE: toggleGoldPouchAllowAnything will allow players to move items or gold to gold pouch
@@ -256,6 +257,7 @@ onlyPremiumAccount = false
-- NOTE: minTownIdToBankTransfer blocks towns less than defined from receiving money transfers
-- NOTE: enableSupportOutfit enable GODS and GMS to select support outfit (gamemaster, customer support or community manager)
stashMoving = false
+stashItemCount = 5000
depotChest = 4
autoLoot = false
autoBank = false
@@ -504,6 +506,9 @@ rateMonsterHealth = 1.0
rateMonsterAttack = 1.0
rateMonsterDefense = 1.0
+-- Npc rates
+rateNpcHealth = 1.0
+
-- Boss rates
rateBossHealth = 1.0
rateBossAttack = 1.0
@@ -541,6 +546,7 @@ location = "South America"
-- Leave empty if you wish to disable.
discordWebhookURL = ""
discordSendFooter = true
+discordWebhookDelayMs = 1000
-- Vip System (Get more info in: https://github.com/opentibiabr/canary/pull/1063)
-- NOTE: set vipSystemEnabled to true to enable the vip system functionalities (this overrides premium checks)
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index b1d18b5a32c..bf480505014 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -14,7 +14,6 @@ enum ConfigKey_t : uint16_t {
ACTIONS_DELAY_INTERVAL,
ADVENTURERSBLESSING_LEVEL,
AIMBOT_HOTKEY_ENABLED,
- ALLOW_BLOCK_SPAWN,
ALLOW_CHANGEOUTFIT,
ALLOW_RELOAD,
AUGMENT_INCREASED_DAMAGE_PERCENT,
@@ -68,7 +67,6 @@ enum ConfigKey_t : uint16_t {
EXP_FROM_PLAYERS_LEVEL_RANGE,
EXPERIENCE_FROM_PLAYERS,
FAMILIAR_TIME,
- FORCE_MONSTERTYPE_LOAD,
FORGE_AMOUNT_MULTIPLIER,
FORGE_BASE_SUCCESS_RATE,
FORGE_BONUS_SUCCESS_RATE,
@@ -136,8 +134,6 @@ enum ConfigKey_t : uint16_t {
M_CONST,
MAINTAIN_MODE_MESSAGE,
MAP_AUTHOR,
- MAP_CUSTOM_AUTHOR,
- MAP_CUSTOM_NAME,
MAP_DOWNLOAD_URL,
MAP_NAME,
MARKET_OFFER_DURATION,
@@ -222,8 +218,6 @@ enum ConfigKey_t : uint16_t {
RATE_MONSTER_ATTACK,
RATE_MONSTER_DEFENSE,
RATE_MONSTER_HEALTH,
- RATE_NPC_ATTACK,
- RATE_NPC_DEFENSE,
RATE_NPC_HEALTH,
RATE_OFFLINE_TRAINING_SPEED,
RATE_SKILL,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 5ade621dff8..6390d0699b3 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -72,8 +72,6 @@ bool ConfigManager::load() {
loadStringConfig(L, IP, "ip", "127.0.0.1");
loadStringConfig(L, MAINTAIN_MODE_MESSAGE, "maintainModeMessage", "");
loadStringConfig(L, MAP_AUTHOR, "mapAuthor", "Eduardo Dantas");
- loadStringConfig(L, MAP_CUSTOM_AUTHOR, "mapCustomAuthor", "OTServBR");
- loadStringConfig(L, MAP_CUSTOM_NAME, "mapCustomName", "");
loadStringConfig(L, MAP_DOWNLOAD_URL, "mapDownloadUrl", "");
loadStringConfig(L, MAP_NAME, "mapName", "canary");
loadStringConfig(L, MYSQL_DB, "mysqlDatabase", "canary");
@@ -84,7 +82,6 @@ bool ConfigManager::load() {
}
loadBoolConfig(L, AIMBOT_HOTKEY_ENABLED, "hotkeyAimbotEnabled", true);
- loadBoolConfig(L, ALLOW_BLOCK_SPAWN, "allowBlockSpawn", true);
loadBoolConfig(L, ALLOW_CHANGEOUTFIT, "allowChangeOutfit", true);
loadBoolConfig(L, ALLOW_RELOAD, "allowReload", false);
loadBoolConfig(L, AUTOBANK, "autoBank", false);
@@ -99,7 +96,6 @@ bool ConfigManager::load() {
loadBoolConfig(L, ENABLE_PLAYER_PUT_ITEM_IN_AMMO_SLOT, "enablePlayerPutItemInAmmoSlot", false);
loadBoolConfig(L, ENABLE_SUPPORT_OUTFIT, "enableSupportOutfit", true);
loadBoolConfig(L, EXPERIENCE_FROM_PLAYERS, "experienceByKillingPlayers", false);
- loadBoolConfig(L, FORCE_MONSTERTYPE_LOAD, "forceMonsterTypesOnLoad", true);
loadBoolConfig(L, FREE_PREMIUM, "freePremium", false);
loadBoolConfig(L, GLOBAL_SERVER_SAVE_CLEAN_MAP, "globalServerSaveCleanMap", false);
loadBoolConfig(L, GLOBAL_SERVER_SAVE_CLOSE, "globalServerSaveClose", false);
@@ -194,8 +190,6 @@ bool ConfigManager::load() {
loadFloatConfig(L, RATE_MONSTER_ATTACK, "rateMonsterAttack", 1.0);
loadFloatConfig(L, RATE_MONSTER_DEFENSE, "rateMonsterDefense", 1.0);
loadFloatConfig(L, RATE_MONSTER_HEALTH, "rateMonsterHealth", 1.0);
- loadFloatConfig(L, RATE_NPC_ATTACK, "rateNpcAttack", 1.0);
- loadFloatConfig(L, RATE_NPC_DEFENSE, "rateNpcDefense", 1.0);
loadFloatConfig(L, RATE_NPC_HEALTH, "rateNpcHealth", 1.0);
loadFloatConfig(L, RATE_OFFLINE_TRAINING_SPEED, "rateOfflineTrainingSpeed", 1.0);
loadFloatConfig(L, RATE_SOUL_REGEN_SPEED, "rateSoulRegenSpeed", 1.0);
@@ -236,7 +230,7 @@ bool ConfigManager::load() {
loadIntConfig(L, FAMILIAR_TIME, "familiarTime", 30);
loadIntConfig(L, FORGE_BASE_SUCCESS_RATE, "forgeBaseSuccessRate", 50);
loadIntConfig(L, FORGE_BONUS_SUCCESS_RATE, "forgeBonusSuccessRate", 15);
- loadIntConfig(L, FORGE_CONVERGENCE_FUSION_DUST_COST, "forgeConvergenceFusionCost", 130);
+ loadIntConfig(L, FORGE_CONVERGENCE_FUSION_DUST_COST, "forgeConvergenceFusionDustCost", 130);
loadIntConfig(L, FORGE_CONVERGENCE_TRANSFER_DUST_COST, "forgeConvergenceTransferCost", 160);
loadIntConfig(L, FORGE_CORE_COST, "forgeCoreCost", 50);
loadIntConfig(L, FORGE_COST_ONE_SLIVER, "forgeCostOneSliver", 20);
@@ -384,11 +378,17 @@ bool ConfigManager::reload() {
return result;
}
+void ConfigManager::missingConfigWarning(const char* identifier) {
+ g_logger().warn("[{}]: Missing configuration for identifier: {}", __FUNCTION__, identifier);
+}
+
std::string ConfigManager::loadStringConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const std::string &defaultValue) {
std::string value = defaultValue;
lua_getglobal(L, identifier);
if (lua_isstring(L, -1)) {
value = lua_tostring(L, -1);
+ } else {
+ missingConfigWarning(identifier);
}
configs[key] = value;
lua_pop(L, 1);
@@ -400,6 +400,8 @@ int32_t ConfigManager::loadIntConfig(lua_State* L, const ConfigKey_t &key, const
lua_getglobal(L, identifier);
if (lua_isnumber(L, -1)) {
value = static_cast(lua_tointeger(L, -1));
+ } else {
+ missingConfigWarning(identifier);
}
configs[key] = value;
lua_pop(L, 1);
@@ -411,6 +413,8 @@ bool ConfigManager::loadBoolConfig(lua_State* L, const ConfigKey_t &key, const c
lua_getglobal(L, identifier);
if (lua_isboolean(L, -1)) {
value = static_cast(lua_toboolean(L, -1));
+ } else {
+ missingConfigWarning(identifier);
}
configs[key] = value;
lua_pop(L, 1);
@@ -422,6 +426,8 @@ float ConfigManager::loadFloatConfig(lua_State* L, const ConfigKey_t &key, const
lua_getglobal(L, identifier);
if (lua_isnumber(L, -1)) {
value = static_cast(lua_tonumber(L, -1));
+ } else {
+ missingConfigWarning(identifier);
}
configs[key] = value;
lua_pop(L, 1);
diff --git a/src/config/configmanager.hpp b/src/config/configmanager.hpp
index 406ff69891e..2a0c0861955 100644
--- a/src/config/configmanager.hpp
+++ b/src/config/configmanager.hpp
@@ -26,6 +26,8 @@ class ConfigManager {
bool load();
bool reload();
+ void missingConfigWarning(const char* identifier);
+
const std::string &setConfigFileLua(const std::string &what) {
configFileLua = { what };
return configFileLua;
From 165a90800174e5461c6803f1df78bc85740e8405 Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Thu, 18 Jul 2024 16:51:04 -0300
Subject: [PATCH 15/15] improve: refactor sendSaleItemList (#2665)
---
data-otservbr-global/npc/obi.lua | 2 +-
data-otservbr-global/npc/ramina.lua | 2 +-
data-otservbr-global/npc/sessek.lua | 8 ++--
src/creatures/players/wheel/player_wheel.cpp | 2 +-
src/server/network/protocol/protocolgame.cpp | 49 +++++++++-----------
src/server/server_definitions.hpp | 3 +-
6 files changed, 31 insertions(+), 35 deletions(-)
diff --git a/data-otservbr-global/npc/obi.lua b/data-otservbr-global/npc/obi.lua
index d63995619da..8694accfa0c 100644
--- a/data-otservbr-global/npc/obi.lua
+++ b/data-otservbr-global/npc/obi.lua
@@ -141,7 +141,7 @@ npcConfig.shop = {
{ itemName = "hatchet", clientId = 3276, sell = 25 },
{ itemName = "katana", clientId = 3300, sell = 35 },
{ itemName = "mace", clientId = 3286, sell = 30 },
- { itemName = "machete", clientId = 3308, sell = 30 },
+ { itemName = "machete", clientId = 3308, sell = 6 },
{ itemName = "rapier", clientId = 3272, buy = 15, sell = 5 },
{ itemName = "sabre", clientId = 3273, buy = 25, sell = 12 },
{ itemName = "scythe", clientId = 3453, buy = 12, sell = 3 },
diff --git a/data-otservbr-global/npc/ramina.lua b/data-otservbr-global/npc/ramina.lua
index b115ca4b6f9..78972d2a78f 100644
--- a/data-otservbr-global/npc/ramina.lua
+++ b/data-otservbr-global/npc/ramina.lua
@@ -65,7 +65,7 @@ npcConfig.shop = {
{ itemName = "orange", clientId = 3586, buy = 12 },
{ itemName = "peas", clientId = 11683, buy = 5 },
{ itemName = "vial of fruit juice", clientId = 2874, buy = 10, count = 14 },
- { itemName = "vial of water", clientId = 2874, buy = 2, count = 1 },
+ { itemName = "vial of water", clientId = 2874, buy = 6, count = 1 },
}
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
diff --git a/data-otservbr-global/npc/sessek.lua b/data-otservbr-global/npc/sessek.lua
index ee7f56a2536..8a8d24ca532 100644
--- a/data-otservbr-global/npc/sessek.lua
+++ b/data-otservbr-global/npc/sessek.lua
@@ -55,11 +55,11 @@ npcConfig.shop = {
{ itemName = "peas", clientId = 11683, buy = 3 },
{ itemName = "pineapple", clientId = 11459, buy = 14 },
{ itemName = "roll", clientId = 3601, buy = 2 },
- { itemName = "vial of coconut milk", clientId = 2874, buy = 2, count = 15 },
+ { itemName = "vial of coconut milk", clientId = 2874, buy = 6, count = 15 },
{ itemName = "vial of fruit juice", clientId = 2874, buy = 6, count = 14 },
- { itemName = "vial of tea", clientId = 2874, buy = 3, count = 17 },
- { itemName = "vial of water", clientId = 2874, buy = 2, count = 1 },
- { itemName = "vial of wine", clientId = 2874, buy = 3, count = 2 },
+ { itemName = "vial of tea", clientId = 2874, buy = 6, count = 17 },
+ { itemName = "vial of water", clientId = 2874, buy = 6, count = 1 },
+ { itemName = "vial of wine", clientId = 2874, buy = 6, count = 2 },
}
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp
index 4c7a3dc52e0..2a3e1366430 100644
--- a/src/creatures/players/wheel/player_wheel.cpp
+++ b/src/creatures/players/wheel/player_wheel.cpp
@@ -1031,7 +1031,7 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con
// TODO: read items from inventory
auto voc = m_player.getVocation();
m_player.client->sendResourceBalance(RESOURCE_BANK, m_player.getBankBalance());
- m_player.client->sendResourceBalance(RESOURCE_INVENTORY, m_player.getMoney());
+ m_player.client->sendResourceBalance(RESOURCE_INVENTORY_MONEY, m_player.getMoney());
m_player.client->sendResourceBalance(RESOURCE_LESSER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Lesser)));
m_player.client->sendResourceBalance(RESOURCE_REGULAR_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Regular)));
m_player.client->sendResourceBalance(RESOURCE_GREATER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Greater)));
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index cf274ce58a9..c67cee710a8 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -4804,7 +4804,7 @@ void ProtocolGame::sendGameNews() {
void ProtocolGame::sendResourcesBalance(uint64_t money /*= 0*/, uint64_t bank /*= 0*/, uint64_t preyCards /*= 0*/, uint64_t taskHunting /*= 0*/, uint64_t forgeDust /*= 0*/, uint64_t forgeSliver /*= 0*/, uint64_t forgeCores /*= 0*/) {
sendResourceBalance(RESOURCE_BANK, bank);
- sendResourceBalance(RESOURCE_INVENTORY, money);
+ sendResourceBalance(RESOURCE_INVENTORY_MONEY, money);
sendResourceBalance(RESOURCE_PREY_CARDS, preyCards);
sendResourceBalance(RESOURCE_TASK_HUNTING, taskHunting);
sendResourceBalance(RESOURCE_FORGE_DUST, forgeDust);
@@ -4825,40 +4825,35 @@ void ProtocolGame::sendResourceBalance(Resource_t resourceType, uint64_t value)
}
void ProtocolGame::sendSaleItemList(const std::vector &shopVector, const std::map &inventoryMap) {
- // Since we already have full inventory map we shouldn't call getMoney here - it is simply wasting cpu power
- uint64_t playerMoney = 0;
- auto it = inventoryMap.find(ITEM_CRYSTAL_COIN);
- if (it != inventoryMap.end()) {
- playerMoney += static_cast(it->second) * 10000;
- }
- it = inventoryMap.find(ITEM_PLATINUM_COIN);
- if (it != inventoryMap.end()) {
- playerMoney += static_cast(it->second) * 100;
- }
- it = inventoryMap.find(ITEM_GOLD_COIN);
- if (it != inventoryMap.end()) {
- playerMoney += static_cast(it->second);
- }
+ sendResourceBalance(RESOURCE_BANK, player->getBankBalance());
- NetworkMessage msg;
- msg.addByte(0xEE);
- msg.addByte(0x00);
- msg.add(player->getBankBalance());
uint16_t currency = player->getShopOwner() ? player->getShopOwner()->getCurrency() : static_cast(ITEM_GOLD_COIN);
- msg.addByte(0xEE);
if (currency == ITEM_GOLD_COIN) {
- msg.addByte(0x01);
- msg.add(playerMoney);
+ // Since we already have full inventory map we shouldn't call getMoney here - it is simply wasting cpu power
+ uint64_t playerMoney = 0;
+ auto it = inventoryMap.find(ITEM_CRYSTAL_COIN);
+ if (it != inventoryMap.end()) {
+ playerMoney += static_cast(it->second) * 10000;
+ }
+ it = inventoryMap.find(ITEM_PLATINUM_COIN);
+ if (it != inventoryMap.end()) {
+ playerMoney += static_cast(it->second) * 100;
+ }
+ it = inventoryMap.find(ITEM_GOLD_COIN);
+ if (it != inventoryMap.end()) {
+ playerMoney += static_cast(it->second);
+ }
+ sendResourceBalance(RESOURCE_INVENTORY_MONEY, playerMoney);
} else {
- msg.addByte(oldProtocol ? 0x01 : 0x02);
- uint64_t newCurrency = 0;
+ uint64_t customCurrencyValue = 0;
auto search = inventoryMap.find(currency);
if (search != inventoryMap.end()) {
- newCurrency += static_cast(search->second);
+ customCurrencyValue += static_cast(search->second);
}
- msg.add(newCurrency);
+ sendResourceBalance(oldProtocol ? RESOURCE_INVENTORY_MONEY : RESOURCE_INVENTORY_CURRENCY_CUSTOM, customCurrencyValue);
}
+ NetworkMessage msg;
msg.addByte(0x7B);
if (oldProtocol) {
@@ -4874,7 +4869,7 @@ void ProtocolGame::sendSaleItemList(const std::vector &shopVector, co
continue;
}
- it = inventoryMap.find(shopBlock.itemId);
+ auto it = inventoryMap.find(shopBlock.itemId);
if (it != inventoryMap.end()) {
msg.add(shopBlock.itemId);
if (oldProtocol) {
diff --git a/src/server/server_definitions.hpp b/src/server/server_definitions.hpp
index 1d5ed5b628e..b957292b579 100644
--- a/src/server/server_definitions.hpp
+++ b/src/server/server_definitions.hpp
@@ -55,7 +55,8 @@ enum SessionEndInformations : uint8_t {
enum Resource_t : uint8_t {
RESOURCE_BANK = 0x00,
- RESOURCE_INVENTORY = 0x01,
+ RESOURCE_INVENTORY_MONEY = 0x01,
+ RESOURCE_INVENTORY_CURRENCY_CUSTOM = 0x02,
RESOURCE_PREY_CARDS = 0x0A,
RESOURCE_TASK_HUNTING = 0x32,
RESOURCE_FORGE_DUST = 0x46,