diff --git a/config.lua.dist b/config.lua.dist
index ea65eef4e..536c78c3b 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -511,6 +511,7 @@ bossDefaultTimeToFightAgain = 20 * 60 * 60 -- 20 hours
bossDefaultTimeToDefeat = 20 * 60 -- 20 minutes
-- Monsters
+defaultRespawnTime = 60
deSpawnRange = 2
deSpawnRadius = 50
diff --git a/data-otxserver/lib/others/load.lua b/data-otxserver/lib/others/load.lua
index 1052efb7b..031c8fb20 100644
--- a/data-otxserver/lib/others/load.lua
+++ b/data-otxserver/lib/others/load.lua
@@ -1,2 +1 @@
dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua")
-dofile(DATA_DIRECTORY .. "/lib/others/vip_system.lua")
diff --git a/data-otxserver/scripts/creaturescripts/customs/vip.lua b/data-otxserver/scripts/creaturescripts/customs/vip.lua
deleted file mode 100644
index 0ae99c00f..000000000
--- a/data-otxserver/scripts/creaturescripts/customs/vip.lua
+++ /dev/null
@@ -1,20 +0,0 @@
-local playerLogin = CreatureEvent("VipLogin")
-
-function playerLogin.onLogin(player)
- if configManager.getBoolean(configKeys.VIP_SYSTEM_ENABLED) then
- local wasVip = player:kv():scoped("account"):get("vip-system") or false
- if wasVip and not player:isVip() then
- player:onRemoveVip()
- end
- if not wasVip and player:isVip() then
- player:onAddVip(player:getVipDays())
- end
-
- if player:isVip() then
- CheckPremiumAndPrint(player, MESSAGE_LOGIN)
- end
- end
- return true
-end
-
-playerLogin:register()
diff --git a/data-otxserver/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua b/data-otxserver/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua
new file mode 100644
index 000000000..a5cc9a123
--- /dev/null
+++ b/data-otxserver/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua
@@ -0,0 +1,24 @@
+local promotionScrolls = {
+ { oldScroll = "wheel.scroll.abridged", newScroll = "abridged" },
+ { oldScroll = "wheel.scroll.basic", newScroll = "basic" },
+ { oldScroll = "wheel.scroll.revised", newScroll = "revised" },
+ { oldScroll = "wheel.scroll.extended", newScroll = "extended" },
+ { oldScroll = "wheel.scroll.advanced", newScroll = "advanced" },
+}
+
+local function migrate(player)
+ for _, scrollTable in ipairs(promotionScrolls) do
+ local oldStorage = player:getStorageValueByName(scrollTable.oldScroll)
+ if oldStorage > 0 then
+ player:kv():scoped("wheel-of-destiny"):scoped("scrolls"):set(scrollTable.newScroll, true)
+ end
+ end
+end
+
+local migration = Migration("20241715984279_move_wheel_scrolls_from_storagename_to_kv")
+
+function migration:onExecute()
+ self:forEachPlayer(migrate)
+end
+
+migration:register()
diff --git a/data/items/items.xml b/data/items/items.xml
index e7e73d975..9fdc89bbd 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -75820,5 +75820,295 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/libs/functions/bosslever.lua b/data/libs/functions/boss_lever.lua
similarity index 100%
rename from data/libs/functions/bosslever.lua
rename to data/libs/functions/boss_lever.lua
diff --git a/data/libs/functions/load.lua b/data/libs/functions/load.lua
index c63276d94..0fab2c3cb 100644
--- a/data/libs/functions/load.lua
+++ b/data/libs/functions/load.lua
@@ -1,14 +1,15 @@
-- Load core functions
dofile(CORE_DIRECTORY .. "/libs/functions/bit.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/bitwise_flags.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/boss_lever.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/combat.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/constants.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/container.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/creature.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/fs.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/game.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/item.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/itemtype.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/lever.lua")
@@ -20,14 +21,13 @@ dofile(CORE_DIRECTORY .. "/libs/functions/player.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/position.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/pronouns.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/quests.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/queue.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/revscriptsys.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/set.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/spawn.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/spectators.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/bosslever.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/string.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/tables.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/teleport.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/tile.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/vocation.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/set.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/queue.lua")
diff --git a/data/libs/systems/load.lua b/data/libs/systems/load.lua
index 5c7073ad6..281b8b1d4 100644
--- a/data/libs/systems/load.lua
+++ b/data/libs/systems/load.lua
@@ -9,4 +9,5 @@ dofile(CORE_DIRECTORY .. "/libs/systems/hazard.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/hireling.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/raids.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/reward_boss.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/vip.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/zones.lua")
diff --git a/data-otxserver/lib/others/vip_system.lua b/data/libs/systems/vip.lua
similarity index 61%
rename from data-otxserver/lib/others/vip_system.lua
rename to data/libs/systems/vip.lua
index 5a393157c..9e76fd8ad 100644
--- a/data-otxserver/lib/others/vip_system.lua
+++ b/data/libs/systems/vip.lua
@@ -1,16 +1,10 @@
local config = {
- activationMessage = "You have received %s VIP days.",
- activationMessageType = MESSAGE_EVENT_ADVANCE,
-
- expirationMessage = "Your VIP days ran out.",
- expirationMessageType = MESSAGE_ADMINISTRATOR,
-
outfits = {},
mounts = {},
}
function Player.onRemoveVip(self)
- self:sendTextMessage(config.expirationMessageType, config.expirationMessage)
+ self:sendTextMessage(MESSAGE_ADMINISTRATOR, "Your VIP status has expired. All VIP benefits have been removed.")
for _, outfit in ipairs(config.outfits) do
self:removeOutfit(outfit)
@@ -21,13 +15,14 @@ function Player.onRemoveVip(self)
end
local playerOutfit = self:getOutfit()
- if table.contains(config.outfits, self:getOutfit().lookType) then
+ if table.contains(config.outfits, playerOutfit.lookType) then
if self:getSex() == PLAYERSEX_FEMALE then
playerOutfit.lookType = 136
else
playerOutfit.lookType = 128
end
playerOutfit.lookAddons = 0
+
self:setOutfit(playerOutfit)
end
@@ -36,7 +31,7 @@ end
function Player.onAddVip(self, days, silent)
if not silent then
- self:sendTextMessage(config.activationMessageType, string.format(config.activationMessage, days))
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have been granted %s days of VIP status.", days))
end
for _, outfit in ipairs(config.outfits) do
@@ -52,16 +47,15 @@ end
function CheckPremiumAndPrint(player, msgType)
if player:getVipDays() == 0xFFFF then
- player:sendTextMessage(msgType, "You have infinite amount of VIP days left.")
+ player:sendTextMessage(msgType, "You have an unlimited VIP status.")
return true
end
local playerVipTime = player:getVipTime()
if playerVipTime < os.time() then
- local msg = "You do not have VIP on your account."
- player:sendTextMessage(msgType, msg)
+ player:sendTextMessage(msgType, "Your VIP status is currently inactive.")
return true
end
- player:sendTextMessage(msgType, string.format("You have %s of VIP time left.", getFormattedTimeRemaining(playerVipTime)))
+ player:sendTextMessage(msgType, string.format("You have %s of VIP time remaining.", getFormattedTimeRemaining(playerVipTime)))
end
diff --git a/data-otxserver/scripts/actions/other/hirelinglamp.lua b/data/scripts/actions/items/hireling_lamp.lua
similarity index 93%
rename from data-otxserver/scripts/actions/other/hirelinglamp.lua
rename to data/scripts/actions/items/hireling_lamp.lua
index 8117b5366..0ffed1fa7 100644
--- a/data-otxserver/scripts/actions/other/hirelinglamp.lua
+++ b/data/scripts/actions/items/hireling_lamp.lua
@@ -2,8 +2,9 @@ local hirelingLamp = Action()
function hirelingLamp.onUse(player, item, fromPosition, target, toPosition, isHotkey)
local spawnPosition = player:getPosition()
- local hireling_id = item:getCustomAttribute("Hireling")
+ local hirelingId = item:getCustomAttribute("Hireling")
local house = spawnPosition and spawnPosition:getTile() and spawnPosition:getTile():getHouse() or nil
+
if not house then
player:getPosition():sendMagicEffect(CONST_ME_POFF)
player:sendTextMessage(MESSAGE_FAILURE, "You may use this only inside a house.")
@@ -22,10 +23,10 @@ function hirelingLamp.onUse(player, item, fromPosition, target, toPosition, isHo
return false
end
- local hireling = getHirelingById(hireling_id)
+ local hireling = getHirelingById(hirelingId)
if not hireling then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error creating the hireling and it has been deleted, please, contact server admin.")
- logger.warn("[hirelingLamp.onUse] Player {} is using hireling with id {} not exist in the database", player:getName(), hireling_id)
+ logger.warn("[hirelingLamp.onUse] Player {} is using hireling with id {} not exist in the database", player:getName(), hirelingId)
logger.error("Deleted the lamp")
item:remove(1)
return true
diff --git a/data/scripts/actions/objects/large_seashell.lua b/data/scripts/actions/objects/large_seashell.lua
new file mode 100644
index 000000000..c6d767146
--- /dev/null
+++ b/data/scripts/actions/objects/large_seashell.lua
@@ -0,0 +1,30 @@
+local largeSeashell = Action()
+
+function largeSeashell.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ if player:hasExhaustion("delay-large-seashell") then
+ player:say("You have already opened a shell today.", TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
+ return true
+ end
+
+ local chance = math.random(100)
+ local message = "Nothing is inside."
+
+ if chance <= 16 then
+ doTargetCombatHealth(0, player, COMBAT_PHYSICALDAMAGE, -200, -200, CONST_ME_NONE)
+ message = "Ouch! You squeezed your fingers."
+ elseif chance > 16 and chance <= 64 then
+ Game.createItem(math.random(281, 282), 1, player:getPosition())
+ message = "You found a beautiful pearl."
+ player:addAchievementProgress("Shell Seeker", 100)
+ end
+
+ player:setExhaustion("delay-large-seashell", 20 * 60 * 60)
+ player:say(message, TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
+ item:transform(198)
+ item:decay()
+ item:getPosition():sendMagicEffect(CONST_ME_BUBBLES)
+ return true
+end
+
+largeSeashell:id(197)
+largeSeashell:register()
diff --git a/data-otxserver/scripts/globalevents/others/bosslever_death.lua b/data/scripts/creaturescripts/monster/boss_lever_death.lua
similarity index 99%
rename from data-otxserver/scripts/globalevents/others/bosslever_death.lua
rename to data/scripts/creaturescripts/monster/boss_lever_death.lua
index 9538fe15e..a2c58d1e4 100644
--- a/data-otxserver/scripts/globalevents/others/bosslever_death.lua
+++ b/data/scripts/creaturescripts/monster/boss_lever_death.lua
@@ -1,22 +1,28 @@
local onBossDeath = CreatureEvent("BossLeverOnDeath")
+
function onBossDeath.onDeath(creature)
if not creature then
return true
end
+
local name = creature:getName()
local key = "boss." .. toKey(name)
local zone = Zone(key)
+
if not zone then
return true
end
+
local bossLever = BossLever[name]
if not bossLever then
return true
end
+
if bossLever.timeoutEvent then
stopEvent(bossLever.timeoutEvent)
bossLever.timeoutEvent = nil
end
+
if bossLever.timeAfterKill > 0 then
zone:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The " .. name .. " has been defeated. You have " .. bossLever.timeAfterKill .. " seconds to leave the room.")
bossLever.timeoutEvent = addEvent(function(zn)
@@ -26,4 +32,5 @@ function onBossDeath.onDeath(creature)
end
return true
end
+
onBossDeath:register()
diff --git a/data-otxserver/scripts/creaturescripts/others/forge_kill.lua b/data/scripts/creaturescripts/monster/forge_kill.lua
similarity index 100%
rename from data-otxserver/scripts/creaturescripts/others/forge_kill.lua
rename to data/scripts/creaturescripts/monster/forge_kill.lua
diff --git a/data-otxserver/scripts/creaturescripts/others/adventure_blessing_login.lua b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
similarity index 99%
rename from data-otxserver/scripts/creaturescripts/others/adventure_blessing_login.lua
rename to data/scripts/creaturescripts/player/adventure_blessing_login.lua
index df0405271..8ebf88a72 100644
--- a/data-otxserver/scripts/creaturescripts/others/adventure_blessing_login.lua
+++ b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
@@ -1,6 +1,7 @@
dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
local adventurerBlessingLogin = CreatureEvent("AdventurerBlessingLogin")
+
function adventurerBlessingLogin.onLogin(player)
return Blessings.doAdventurerBlessing(player)
end
diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua
index 34d139a04..fbe4f9e63 100644
--- a/data/scripts/creaturescripts/player/login.lua
+++ b/data/scripts/creaturescripts/player/login.lua
@@ -49,10 +49,8 @@ function playerLoginGlobal.onLogin(player)
end
-- Boosted
- player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, "Today's boosted creature: " .. Game.getBoostedCreature() .. " \
- Boosted creatures yield more experience points, carry more loot than usual and respawn at a faster rate.")
- player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, "Today's boosted boss: " .. Game.getBoostedBoss() .. " \
- Boosted bosses contain more loot and count more kills for your Bosstiary.")
+ player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Today's boosted creature: %s.\nBoosted creatures yield more experience points, carry more loot than usual, and respawn at a faster rate.", Game.getBoostedCreature()))
+ player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Today's boosted boss: %s.\nBoosted bosses contain more loot and count more kills for your Bosstiary.", Game.getBoostedBoss()))
-- Rewards
local rewards = #player:getRewardList()
@@ -118,6 +116,24 @@ function playerLoginGlobal.onLogin(player)
player:setStaminaXpBoost(player:getFinalBonusStamina() * 100)
player:getFinalLowLevelBonus()
+ -- Updates the player's VIP status and executes corresponding actions if applicable.
+ if configManager.getBoolean(configKeys.VIP_SYSTEM_ENABLED) then
+ local isVipNow = player:isVip()
+ local wasVip = player:kv():scoped("account"):get("vip-system") or false
+
+ if wasVip ~= isVipNow then
+ if wasVip then
+ player:onRemoveVip()
+ else
+ player:onAddVip(player:getVipDays())
+ end
+ end
+
+ if isVipNow then
+ CheckPremiumAndPrint(player, MESSAGE_LOGIN)
+ end
+ end
+
-- Set Ghost Mode
if player:getGroup():getId() >= GROUP_TYPE_GAMEMASTER then
player:setGhostMode(true)
diff --git a/data/scripts/talkactions/god/add_skill.lua b/data/scripts/talkactions/god/add_skill.lua
index 206445823..09d240ece 100644
--- a/data/scripts/talkactions/god/add_skill.lua
+++ b/data/scripts/talkactions/god/add_skill.lua
@@ -16,11 +16,6 @@ local function getSkillId(skillName)
end
end
-local function getExpForLevel(level)
- level = level - 1
- return ((50 * level * level * level) - (150 * level * level) + (400 * level)) / 3
-end
-
local addSkill = TalkAction("/addskill")
function addSkill.onSay(player, words, param)
@@ -54,7 +49,7 @@ function addSkill.onSay(player, words, param)
local ch = split[2]:sub(1, 1)
if ch == "l" or ch == "e" then
targetLevel = target:getLevel() + count
- targetExp = getExpForLevel(targetLevel)
+ targetExp = Game.getExperienceForLevel(targetLevel)
addExp = targetExp - target:getExperience()
target:addExperience(addExp, false)
elseif ch == "m" then
diff --git a/data-otxserver/scripts/weapons/dawnport_weapon.lua b/data/scripts/weapons/dawnport_weapons.lua
similarity index 100%
rename from data-otxserver/scripts/weapons/dawnport_weapon.lua
rename to data/scripts/weapons/dawnport_weapons.lua
diff --git a/data-otxserver/scripts/weapons/scripts/burst_arrow.lua b/data/scripts/weapons/scripts/burst_arrow.lua
similarity index 100%
rename from data-otxserver/scripts/weapons/scripts/burst_arrow.lua
rename to data/scripts/weapons/scripts/burst_arrow.lua
diff --git a/data-otxserver/scripts/weapons/scripts/diamond_arrow.lua b/data/scripts/weapons/scripts/diamond_arrow.lua
similarity index 100%
rename from data-otxserver/scripts/weapons/scripts/diamond_arrow.lua
rename to data/scripts/weapons/scripts/diamond_arrow.lua
diff --git a/data-otxserver/scripts/weapons/scripts/poison_arrow.lua b/data/scripts/weapons/scripts/poison_arrow.lua
similarity index 100%
rename from data-otxserver/scripts/weapons/scripts/poison_arrow.lua
rename to data/scripts/weapons/scripts/poison_arrow.lua
diff --git a/data-otxserver/scripts/weapons/scripts/viper_star.lua b/data/scripts/weapons/scripts/viper_star.lua
similarity index 100%
rename from data-otxserver/scripts/weapons/scripts/viper_star.lua
rename to data/scripts/weapons/scripts/viper_star.lua
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 22043f425..0e53c9754 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -50,6 +50,7 @@ enum ConfigKey_t : uint16_t {
DATA_DIRECTORY,
DAY_KILLS_TO_RED,
DEATH_LOSE_PERCENT,
+ DEFAULT_RESPAWN_TIME,
DEFAULT_DESPAWNRADIUS,
DEFAULT_DESPAWNRANGE,
DEFAULT_PRIORITY,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index e1345bbac..35d2cf3b1 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -225,6 +225,7 @@ bool ConfigManager::load() {
loadIntConfig(L, CRITICALCHANCE, "criticalChance", 10);
loadIntConfig(L, DAY_KILLS_TO_RED, "dayKillsToRedSkull", 3);
loadIntConfig(L, DEATH_LOSE_PERCENT, "deathLosePercent", -1);
+ loadIntConfig(L, DEFAULT_RESPAWN_TIME, "defaultRespawnTime", 60);
loadIntConfig(L, DEFAULT_DESPAWNRADIUS, "deSpawnRadius", 50);
loadIntConfig(L, DEFAULT_DESPAWNRANGE, "deSpawnRange", 2);
loadIntConfig(L, DEPOTCHEST, "depotChest", 4);
diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp
index ef5912c80..968b90d8c 100644
--- a/src/creatures/monsters/spawns/spawn_monster.cpp
+++ b/src/creatures/monsters/spawns/spawn_monster.cpp
@@ -94,7 +94,15 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) {
weight = pugi::cast(weightAttribute.value());
}
- spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, pugi::cast(childMonsterNode.attribute("spawntime").value()) * 1000, weight);
+ uint32_t scheduleInterval = g_configManager().getNumber(DEFAULT_RESPAWN_TIME, __FUNCTION__);
+
+ try {
+ scheduleInterval = pugi::cast(childMonsterNode.attribute("spawntime").value());
+ } catch (...) {
+ g_logger().warn("Failed to add schedule interval to monster: {}, interval: {}. Setting to default respawn time: {}", nameAttribute.value(), childMonsterNode.attribute("spawntime").value(), scheduleInterval);
+ }
+
+ spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, scheduleInterval * 1000, weight);
}
}
}
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index 58bebfb9f..c0761704d 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -230,9 +230,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
void addList() override;
void removePlayer(bool displayEffect, bool forced = true);
- static uint64_t getExpForLevel(int32_t lv) {
- lv--;
- return ((50ULL * lv * lv * lv) - (150ULL * lv * lv) + (400ULL * lv)) / 3ULL;
+ static uint64_t getExpForLevel(const uint32_t level) {
+ return (((level - 6ULL) * level + 17ULL) * level - 12ULL) / 6ULL * 100ULL;
}
uint16_t getStaminaMinutes() const {
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 621e35938..899a89549 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -5165,7 +5165,7 @@ void Game::playerBuyItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint
return;
}
- if (inBackpacks) {
+ if (inBackpacks || it.isContainer()) {
uint32_t maxContainer = static_cast(g_configManager().getNumber(MAX_CONTAINER, __FUNCTION__));
auto backpack = player->getInventoryItem(CONST_SLOT_BACKPACK);
auto mainBackpack = backpack ? backpack->getContainer() : nullptr;
diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp
index 0fdbc96f4..4858b3ab2 100644
--- a/src/lua/functions/core/game/game_functions.cpp
+++ b/src/lua/functions/core/game/game_functions.cpp
@@ -191,6 +191,17 @@ int GameFunctions::luaGameloadMapChunk(lua_State* L) {
return 0;
}
+int GameFunctions::luaGameGetExperienceForLevel(lua_State* L) {
+ // Game.getExperienceForLevel(level)
+ const uint32_t level = getNumber(L, 1);
+ if (level == 0) {
+ reportErrorFunc("Level must be greater than 0.");
+ } else {
+ lua_pushnumber(L, Player::getExpForLevel(level));
+ }
+ return 1;
+}
+
int GameFunctions::luaGameGetMonsterCount(lua_State* L) {
// Game.getMonsterCount()
lua_pushnumber(L, g_game().getMonstersOnline());
diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp
index 7f3b642e9..70e81061c 100644
--- a/src/lua/functions/core/game/game_functions.hpp
+++ b/src/lua/functions/core/game/game_functions.hpp
@@ -28,6 +28,7 @@ class GameFunctions final : LuaScriptInterface {
registerMethod(L, "Game", "loadMap", GameFunctions::luaGameLoadMap);
registerMethod(L, "Game", "loadMapChunk", GameFunctions::luaGameloadMapChunk);
+ registerMethod(L, "Game", "getExperienceForLevel", GameFunctions::luaGameGetExperienceForLevel);
registerMethod(L, "Game", "getMonsterCount", GameFunctions::luaGameGetMonsterCount);
registerMethod(L, "Game", "getPlayerCount", GameFunctions::luaGameGetPlayerCount);
registerMethod(L, "Game", "getNpcCount", GameFunctions::luaGameGetNpcCount);
@@ -103,6 +104,7 @@ class GameFunctions final : LuaScriptInterface {
static int luaGameLoadMap(lua_State* L);
static int luaGameloadMapChunk(lua_State* L);
+ static int luaGameGetExperienceForLevel(lua_State* L);
static int luaGameGetMonsterCount(lua_State* L);
static int luaGameGetPlayerCount(lua_State* L);
static int luaGameGetNpcCount(lua_State* L);
diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp
index 1c575ff00..3effd52f8 100644
--- a/src/server/network/connection/connection.cpp
+++ b/src/server/network/connection/connection.cpp
@@ -105,7 +105,7 @@ void Connection::accept(Protocol_ptr protocolPtr) {
void Connection::acceptInternal(bool toggleParseHeader) {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
- readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); });
+ readTimer.async_wait([self = std::weak_ptr(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); });
try {
asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this(), toggleParseHeader](const std::error_code &error, std::size_t N) {
@@ -147,7 +147,7 @@ void Connection::parseProxyIdentification(const std::error_code &error) {
connectionState = CONNECTION_STATE_READINGS;
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
- readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); });
+ readTimer.async_wait([self = std::weak_ptr(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); });
// Read the remainder of proxy identification
asio::async_read(socket, asio::buffer(msg.getBuffer(), remainder), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseProxyIdentification(error); });
@@ -208,7 +208,7 @@ void Connection::parseHeader(const std::error_code &error) {
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
- readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); });
+ readTimer.async_wait([self = std::weak_ptr(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); });
// Read packet content
msg.setLength(size + HEADER_LENGTH);
@@ -275,7 +275,7 @@ void Connection::parsePacket(const std::error_code &error) {
try {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
- readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); });
+ readTimer.async_wait([self = std::weak_ptr(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); });
if (!skipReadingNextPacket) {
// Wait to the next packet
@@ -289,7 +289,7 @@ void Connection::parsePacket(const std::error_code &error) {
void Connection::resumeWork() {
readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT));
- readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); });
+ readTimer.async_wait([self = std::weak_ptr(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); });
try {
asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseHeader(error); });
@@ -358,7 +358,7 @@ uint32_t Connection::getIP() {
void Connection::internalSend(const OutputMessage_ptr &outputMessage) {
writeTimer.expires_from_now(std::chrono::seconds(CONNECTION_WRITE_TIMEOUT));
- readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); });
+ writeTimer.async_wait([self = std::weak_ptr(shared_from_this())](const std::error_code &error) { Connection::handleTimeout(self, error); });
try {
asio::async_write(socket, asio::buffer(outputMessage->getOutputBuffer(), outputMessage->getLength()), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->onWriteOperation(error); });
diff --git a/vcpkg.json b/vcpkg.json
index 1f821379b..eed7e776e 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -18,6 +18,11 @@
"spdlog",
"zlib",
"bshoshany-thread-pool",
+ {
+ "name": "opentelemetry-cpp",
+ "default-features": true,
+ "features": ["otlp-http", "prometheus"]
+ },
{
"name": "libmariadb",
"features": [