diff --git a/config.lua.dist b/config.lua.dist
index 447a90647c2..9d1ed2fa681 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-canary/scripts/actions/other/large_sea_shell.lua b/data-canary/scripts/actions/other/large_sea_shell.lua
deleted file mode 100644
index b396bd70611..00000000000
--- a/data-canary/scripts/actions/other/large_sea_shell.lua
+++ /dev/null
@@ -1,28 +0,0 @@
-local largeSeaShell = Action()
-
-function largeSeaShell.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if player:getStorageValue(Storage.DelayLargeSeaShell) <= os.time() then
- local chance = math.random(100)
- local msg = ""
- if chance <= 16 then
- doTargetCombatHealth(0, player, COMBAT_PHYSICALDAMAGE, -200, -200, CONST_ME_NONE)
- msg = "Ouch! You squeezed your fingers."
- elseif chance > 16 and chance <= 64 then
- Game.createItem(math.random(281, 282), 1, player:getPosition())
- msg = "You found a beautiful pearl."
- else
- msg = "Nothing is inside."
- end
- player:say(msg, TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
- item:transform(198)
- item:decay()
- player:setStorageValue(Storage.DelayLargeSeaShell, os.time() + 20 * 60 * 60)
- item:getPosition():sendMagicEffect(CONST_ME_BUBBLES)
- else
- player:say("You have already opened a shell today.", TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
- end
- return true
-end
-
-largeSeaShell:id(197)
-largeSeaShell:register()
diff --git a/data-canary/scripts/weapons/scripted_weapons.lua b/data-canary/scripts/weapons/scripted_weapons.lua
deleted file mode 100644
index 93e677d43cd..00000000000
--- a/data-canary/scripts/weapons/scripted_weapons.lua
+++ /dev/null
@@ -1,101 +0,0 @@
-local burstArea = createCombatArea({
- { 1, 1, 1 },
- { 1, 3, 1 },
- { 1, 1, 1 },
-})
-
-local burstCombat = Combat()
-burstCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-burstCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONAREA)
-burstCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_BURSTARROW)
-burstCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-burstCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-burstCombat:setParameter(COMBAT_PARAM_IMPACTSOUND, SOUND_EFFECT_TYPE_BURST_ARROW_EFFECT)
-burstCombat:setParameter(COMBAT_PARAM_CASTSOUND, SOUND_EFFECT_TYPE_DIST_ATK_BOW)
-burstCombat:setArea(burstArea)
-
-local burstarrow = Weapon(WEAPON_AMMO)
-burstarrow.onUseWeapon = function(player, variant)
- if player:getSkull() == SKULL_BLACK then
- return false
- end
-
- return burstCombat:execute(player, variant)
-end
-
-burstarrow:id(3449)
-burstarrow:action("removecount")
-burstarrow:register()
-
-local poisonCombat = Combat()
-poisonCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-poisonCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_POISONARROW)
-poisonCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-poisonCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-
-local poisonarrow = Weapon(WEAPON_AMMO)
-poisonarrow.onUseWeapon = function(player, variant)
- if not poisonCombat:execute(player, variant) then
- return false
- end
-
- player:addDamageCondition(Creature(variant:getNumber()), CONDITION_POISON, DAMAGELIST_LOGARITHMIC_DAMAGE, 3)
- return true
-end
-
-poisonarrow:id(3448)
-poisonarrow:action("removecount")
-poisonarrow:register()
-
-local viperCombat = Combat()
-viperCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-viperCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_GREENSTAR)
-viperCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-viperCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-
-local viperstar = Weapon(WEAPON_DISTANCE)
-viperstar.onUseWeapon = function(player, variant)
- if not viperCombat:execute(player, variant) then
- return false
- end
-
- if math.random(1, 100) <= 90 then
- return false
- end
-
- player:addDamageCondition(Creature(variant:getNumber()), CONDITION_POISON, DAMAGELIST_LOGARITHMIC_DAMAGE, 2)
- return true
-end
-
-viperstar:id(7366)
-viperstar:breakChance(9)
-viperstar:register()
-
-local diamondArea = createCombatArea({
- { 0, 1, 1, 1, 0 },
- { 1, 1, 1, 1, 1 },
- { 1, 1, 3, 1, 1 },
- { 1, 1, 1, 1, 1 },
- { 0, 1, 1, 1, 0 },
-})
-
-local diamondCombat = Combat()
-diamondCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-diamondCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT)
-diamondCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_DIAMONDARROW)
-diamondCombat:setParameter(COMBAT_PARAM_IMPACTSOUND, SOUND_EFFECT_TYPE_DIAMOND_ARROW_EFFECT)
-diamondCombat:setParameter(COMBAT_PARAM_CASTSOUND, SOUND_EFFECT_TYPE_DIST_ATK_BOW)
-diamondCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-diamondCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-diamondCombat:setArea(diamondArea)
-
-local diamondarrow = Weapon(WEAPON_AMMO)
-diamondarrow.onUseWeapon = function(player, variant)
- return diamondCombat:execute(player, variant)
-end
-
-diamondarrow:id(ITEM_OLD_DIAMOND_ARROW)
-diamondarrow:action("removecount")
-diamondarrow:level(150)
-diamondarrow:wieldUnproperly(true)
-diamondarrow:register()
diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua
index 1052efb7bd6..031c8fb2026 100644
--- a/data-otservbr-global/lib/others/load.lua
+++ b/data-otservbr-global/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-otservbr-global/scripts/creaturescripts/customs/vip.lua b/data-otservbr-global/scripts/creaturescripts/customs/vip.lua
deleted file mode 100644
index 0ae99c00f2d..00000000000
--- a/data-otservbr-global/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-otservbr-global/scripts/creaturescripts/others/forge_kill.lua b/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua
deleted file mode 100644
index 3e454d84d14..00000000000
--- a/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua
+++ /dev/null
@@ -1,12 +0,0 @@
-local forgeKill = CreatureEvent("ForgeSystemMonster")
-
-function forgeKill.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
- local targetMonster = creature:getMonster()
- if not targetMonster then
- return true
- end
-
- return ForgeMonster:onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
-end
-
-forgeKill:register()
diff --git a/data/items/items.xml b/data/items/items.xml
index e7e73d9753b..9fdc89bbd8f 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 c63276d9444..0fab2c3cb5e 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 5c7073ad64e..281b8b1d466 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-otservbr-global/lib/others/vip_system.lua b/data/libs/systems/vip.lua
similarity index 61%
rename from data-otservbr-global/lib/others/vip_system.lua
rename to data/libs/systems/vip.lua
index 5a393157c8c..9e76fd8ad80 100644
--- a/data-otservbr-global/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-otservbr-global/scripts/actions/other/hirelinglamp.lua b/data/scripts/actions/items/hireling_lamp.lua
similarity index 93%
rename from data-otservbr-global/scripts/actions/other/hirelinglamp.lua
rename to data/scripts/actions/items/hireling_lamp.lua
index 8117b5366bc..0ffed1fa744 100644
--- a/data-otservbr-global/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 00000000000..c6d767146ec
--- /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-otservbr-global/scripts/globalevents/others/bosslever_death.lua b/data/scripts/creaturescripts/monster/boss_lever_death.lua
similarity index 99%
rename from data-otservbr-global/scripts/globalevents/others/bosslever_death.lua
rename to data/scripts/creaturescripts/monster/boss_lever_death.lua
index 9538fe15eb7..a2c58d1e43b 100644
--- a/data-otservbr-global/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-canary/scripts/creaturescripts/forge_kill.lua b/data/scripts/creaturescripts/monster/forge_kill.lua
similarity index 100%
rename from data-canary/scripts/creaturescripts/forge_kill.lua
rename to data/scripts/creaturescripts/monster/forge_kill.lua
diff --git a/data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
similarity index 99%
rename from data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua
rename to data/scripts/creaturescripts/player/adventure_blessing_login.lua
index df04052716f..8ebf88a72e9 100644
--- a/data-otservbr-global/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 34d139a047d..fbe4f9e63b6 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 20644582325..09d240ece4d 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-otservbr-global/scripts/weapons/dawnport_weapon.lua b/data/scripts/weapons/dawnport_weapons.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/dawnport_weapon.lua
rename to data/scripts/weapons/dawnport_weapons.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/burst_arrow.lua b/data/scripts/weapons/scripts/burst_arrow.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/burst_arrow.lua
rename to data/scripts/weapons/scripts/burst_arrow.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data/scripts/weapons/scripts/diamond_arrow.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
rename to data/scripts/weapons/scripts/diamond_arrow.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/poison_arrow.lua b/data/scripts/weapons/scripts/poison_arrow.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/poison_arrow.lua
rename to data/scripts/weapons/scripts/poison_arrow.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/viper_star.lua b/data/scripts/weapons/scripts/viper_star.lua
similarity index 100%
rename from data-otservbr-global/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 22043f42525..0e53c97546e 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 e1345bbac4b..35d2cf3b1be 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 ef5912c8052..968b90d8cae 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 58bebfb9f2f..c0761704db4 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 621e3593816..899a89549d9 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 0fdbc96f4d6..4858b3ab264 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 7f3b642e97d..70e81061c9c 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 1c575ff00d0..3effd52f829 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 1f821379bad..eed7e776ee0 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": [